Cálculo de EMA Asimétrica y Optimización Bayesiana con Python

Gráfico de Tesla con EMA Asimétrica
Hoy nos sumergimos en el mundo del trading algorítmico con un enfoque único en la Media Móvil Exponencial (EMA), conocido como EMA asimétrica. Vamos a darle un toque especial por medio de la optimización bayesiana, utilizando los regresores Extra Trees.
El Problema: Encontrar la Estrategia de Trading Perfecta
Imagina que estás en un casino, pero en lugar de fichas, estás apostando en los precios de las acciones. Quieres saber cuándo comprar y cuándo vender para maximizar tus ganancias. Suena fácil, ¿verdad? ¡Error! Si fuera tan simple, todos serían ricos. Pero no temas, porque hoy vamos a construir un algoritmo que podría darnos una ventaja.
Introducción a la EMA Asimétrica
La EMA tradicional suaviza las fluctuaciones de precios al dar más peso a los precios recientes. Pero ¿y si pudiéramos hacerla aún más inteligente? Aquí es donde entra la EMA Asimétrica (AsyEma), que utiliza una fórmula diseñada para reaccionar de manera diferente según si los precios están subiendo o bajando:
AsyEma = α × max(Close,AsyEma) + (1−α) ×min(Close,AsyEma)
Este pequeño ajuste permite que nuestra media móvil se adapte mejor a las condiciones del mercado.
Visualización de Diferentes EMAs Asimétricas y la EMA Estándar para Tesla: Un Análisis Comparativo
En el mundo del trading algorítmico, contar con un conjunto de herramientas versátil es fundamental. Hoy exploraremos cómo distintos valores de alpha en nuestra EMA Asimétrica (AsyEma) afectan el análisis de los precios de las acciones. Para hacerlo más interesante, compararemos estos resultados con una Media Móvil Exponencial (EMA) estándar.
Usaremos los datos de las acciones de Tesla de los últimos dos años para demostrarlo. Nuestro objetivo es visualizar cómo la variación de alpha influye en la sensibilidad de la AsyEma en comparación con una EMA tradicional. Este análisis comparativo puede proporcionar información valiosa sobre las tendencias del mercado y posibles estrategias de trading.
¡Vamos a sumergirnos en el código que hace esto posible!
import yfinance as yf import numpy as np import matplotlib.pyplot as plt # Descargar datos de Tesla desde el 01-01-2022 hasta el 31-12-2024 datos = yf.download('TSLA', start='2022-01-01', end='2024-12-31') precios_cierre = datos['Close'].values # Función para calcular la EMA asimétrica (AsyEma) def calcular_AsyEma(precios, alpha): AsyEma = np.zeros_like(precios) AsyEma[0] = precios[0] for i in range(1, len(precios)): AsyEma[i] = alpha * max(precios[i], AsyEma[i-1]) + (1 - alpha) * min(precios[i], AsyEma[i-1]) return AsyEma # Función para calcular la EMA estándar def calcular_EMA_Estandar(precios, alpha): ema = np.zeros_like(precios) ema[0] = precios[0] factor = 2 / (1 + alpha) for i in range(1, len(precios)): ema[i] = (precios[i] - ema[i-1]) * factor + ema[i-1] return ema # Definir diferentes valores de alpha para la AsyEma alphas = [0.1, 0.2, 0.3] # Calcular las AsyEma y la EMA estándar AsyEmas = {alpha: calcular_AsyEma(precios_cierre, alpha) for alpha in alphas} EMA_Estandar = calcular_EMA_Estandar(precios_cierre, 0.1) # Graficar los resultados plt.figure(figsize=(14, 8)) # Graficar los precios de cierre plt.plot(datos.index, precios_cierre, label='Precio de Cierre', color='blue') # Graficar las AsyEma for alpha, AsyEma in AsyEmas.items(): plt.plot(datos.index, AsyEma, label=f'AsyEma (alpha={alpha})') # Graficar la EMA estándar plt.plot(datos.index, EMA_Estandar, label='EMA Estándar (alpha=0.1)', linestyle='--') # Agregar etiquetas y leyenda plt.xlabel('Fecha') plt.ylabel('Precio') plt.title('Precios de Tesla con Diferentes AsyEma y EMA Estándar') plt.legend() # Mostrar la gráfica plt.show()
Explicación del Código
Obtención de Datos
Primero, descargamos los datos de las acciones de Tesla de los últimos dos años utilizando yfinance.
data = yf.download('TSLA', start='2022-01-01', end='2024-12-31') close_prices = data['Close'].values
Cálculo de la EMA Asimétrica
Definimos una función para calcular la EMA Asimétrica utilizando la fórmula dada:
AsyEma = α × max(Close,AsyEma) + (1−α) ×min(Close,AsyEma)
def calcular_AsyEma(precios, alpha): AsyEma = np.zeros_like(precios) AsyEma[0] = precios[0] for i in range(1, len(precios)): AsyEma[i] = alpha * max(precios[i], AsyEma[i-1]) + (1 - alpha) * min(precios[i], AsyEma[i-1]) return AsyEma
Cálculo de la EMA Estándar
También definimos una función para calcular la EMA estándar para compararla:
EMAt=Preciot×Multiplicador+EMAt−1×(1−Multiplicador)
Donde:
Multiplicador = 2/(N+1)
y N es el período (en este caso, N=1/α).
def calcular_EMA_Estandar(precios, alpha): ema = np.zeros_like(precios) ema[0] = precios[0] factor = 2 / (1 + alpha) for i in range(1, len(precios)): ema[i] = (precios[i] - ema[i-1]) * factor + ema[i-1] return ema
Graficado de los Resultados
Finalmente, representamos gráficamente los precios de cierre, las diferentes AsyEma y la EMA estándar para una comparación visual.
plt.figure(figsize=(14, 8)) plt.plot(datos.index, precios_cierre, label='Precio de Cierre', color='blue') for alpha, AsyEma in AsyEmas.items(): plt.plot(datos.index, AsyEma, label=f'AsyEma (alpha={alpha})') plt.plot(datos.index, EMA_Estandar, label='EMA Estándar (alpha=0.1)', linestyle='--') plt.xlabel('Fecha') plt.ylabel('Precio') plt.title('Precios de Tesla con Diferentes AsyEma y EMA Estándar') plt.legend() plt.show()

Gráfico de Tesla con EMA Asimétrica. Cómo la AsyEma aproxima las “resistencias” en diferentes escalas de tiempo
Al comparar diferentes EMAs Asimétricas con una EMA estándar, obtenemos información valiosa sobre cómo la variación de alpha afecta la sensibilidad de nuestras medias móviles. Esto puede ser crucial para desarrollar estrategias de trading más adaptativas y dinámicas.
Recuerda, la belleza del trading algorítmico radica en su flexibilidad y precisión. Con herramientas como la EMA Asimétrica y la Optimización Bayesiana, puedes navegar los mercados con confianza y precisión.
Construcción del Agente de Trading
Ahora, vamos a crear nuestro agente de trading. Este agente utilizará la AsyEma para decidir cuándo comprar o vender. Aquí está la regla:
- Comprar: Cuando ∣AsyEma−Close∣<δ|
- Vender: En caso contrario
Aquí, α controla cuán sensible es la AsyEma, y δ determina cuán sensible es nuestro agente a los cambios de precio. Nuestro objetivo es encontrar los mejores valores para α y δ que maximicen las ganancias.
¿Cómo encontramos estos números importantes? Aquí entra la optimización bayesiana.
Optimización Bayesiana
La optimización bayesiana es como tener un asistente súper inteligente que aprende de experiencias pasadas para tomar mejores decisiones. En lugar de probar combinaciones aleatorias de α y δ, usamos modelos como los Regresores Extra Trees para predecir dónde podrían estar los mejores parámetros. Es como jugar al ajedrez contra un gran maestro: cada movimiento es estratégico.
Veamos cómo funciona esto en la práctica. Usaremos Python y algunas bibliotecas poderosas para automatizar este proceso:
import yfinance as yf import numpy as np from skopt import Optimizer from skopt.space import Real from skopt.learning import ExtraTreesRegressor import matplotlib.pyplot as plt # Descargar datos de Apple desde el 01-01-2021 hasta el 31-12-2022 datos = yf.download('AAPL', start='2021-01-01', end='2022-12-31') precios_cierre = datos['Close'].values # Función para calcular la EMA asimétrica (AsyEma) def calcular_AsyEma(precios, alpha): AsyEma = np.zeros_like(precios) AsyEma[0] = precios[0] for i in range(1, len(precios)): AsyEma[i] = alpha * max(precios[i], AsyEma[i-1]) + (1 - alpha) * min(precios[i], AsyEma[i-1]) return AsyEma # Función para evaluar el rendimiento del agente def evaluar_agente(alpha, delta, precios_cierre): try: AsyEma = calcular_AsyEma(precios_cierre, alpha) posicion = 0 # 0: sin posición, 1: largo capital = 100000 # Capital inicial acciones = 0 for i in range(len(precios_cierre)): if abs(AsyEma[i] - precios_cierre[i]) < delta: if posicion == 0: # Comprar acciones = capital // precios_cierre[i] capital -= acciones * precios_cierre[i] posicion = 1 else: if posicion == 1: # Vender capital += acciones * precios_cierre[i] acciones = 0 posicion = 0 # Venta final si aún hay una posición abierta capital += acciones * precios_cierre[-1] rendimiento = -(capital - 100000) # Rendimiento neto negativo para minimizar # Verificar que el rendimiento sea un escalar if isinstance(rendimiento, np.ndarray): rendimiento = rendimiento.item() # Convertir a escalar si es un array if not isinstance(rendimiento, (int, float, np.number)): raise ValueError(f"El rendimiento no es un escalar: {rendimiento}") return rendimiento except Exception as e: print(f"Error durante la evaluación del agente con alpha={alpha}, delta={delta}: {e}") return float('inf') # Retorna infinito para evitar considerar esta combinación # Definir el espacio de búsqueda espacio = [Real(0.01, 0.5, name='alpha'), Real(0.1, 10, name='delta')] # Inicializar el optimizador con ExtraTreesRegressor optimizador = Optimizer(espacio, base_estimator=ExtraTreesRegressor(n_estimators=100, random_state=42)) # Ejecutar la optimización bayesiana n_iteraciones = 100 valores_objetivo = [] for i in range(n_iteraciones): siguiente_x = optimizador.ask() valor_f = evaluar_agente(siguiente_x[0], siguiente_x[1], precios_cierre) valores_objetivo.append(-valor_f) optimizador.tell(siguiente_x, valor_f) # Extraer los mejores parámetros y el rendimiento correspondiente resultado = optimizador.get_result() mejor_alpha = resultado.x[0] mejor_delta = resultado.x[1] mejor_rendimiento = -resultado.fun print(f"Mejor alpha: {mejor_alpha:.4f}") print(f"Mejor delta: {mejor_delta:.4f}") print(f"Mejor rendimiento: {mejor_rendimiento:.2f}")
Resultados de los datos:
- Mejor alpha: 0.0152
- Mejor delta: 3.1365
- Mejor rendimiento: 58622.03
De acuerdo a los resultados del backtesting, se produce un desempeño del 58%.
El Rol de la Diferencia Entre AsyEma y Close
La clave está en la diferencia entre el precio de cierre (Close) y nuestro radar especial (AsyEma). Si esta diferencia es menor que delta, significa que el mercado se comporta de manera “normal” en relación con nuestra media ponderada. Es como si el mercado estuviera tranquilo y estable, por lo que es un buen momento para comprar.
Por otro lado, si la diferencia es mayor que delta, indica un movimiento significativo en el mercado que podría señalar volatilidad o cambios importantes. En este caso, es mejor vender y proteger nuestras ganancias.
¿Por Qué Usar Delta?
Delta es nuestro límite de tolerancia. Es como una frontera invisible que nos dice cuándo debemos actuar. Imagina que estás jugando al ping-pong. Delta es la distancia óptima entre tú y la mesa. Si estás demasiado cerca, no tienes espacio para moverte; si estás demasiado lejos, no puedes golpear la pelota. Delta nos da ese equilibrio perfecto.
En términos técnicos, delta nos ayuda a:
- Reducir el Ruido: El mercado está lleno de ruido y fluctuaciones aleatorias. Delta ayuda a filtrar estas pequeñas oscilaciones y enfocarnos en los movimientos significativos.
- Identificar Tendencias: Cuando la diferencia entre AsyEma y Close excede delta, podría ser una señal de que el mercado está cambiando de dirección. Esto nos permite reaccionar rápidamente.
- Gestionar el Riesgo: Al establecer un valor apropiado para delta, podemos reducir el riesgo de pérdidas repentinas y proteger nuestras inversiones.
Ejemplo Práctico
Imagina que tienes dos series temporales:
- Serie A: Precios fluctuando ligeramente alrededor de su valor medio.
- Serie B: Precios mostrando grandes oscilaciones y cambios abruptos.
Si establecemos delta a un valor bajo, digamos 0.5, la Serie A podría activar con frecuencia nuestras reglas de compra/venta, creando muchas transacciones, pero pocas ganancias reales. Por el contrario, la Serie B podría requerir un delta más alto, digamos 5, para evitar reaccionar a cada pequeña oscilación.
Visualización con Sombras
Ahora, volvamos a la visualización. Coloreamos de verde las zonas donde la diferencia es menor que delta, lo que indica un buen momento para comprar. Las zonas rojas representan períodos de alta volatilidad, donde la diferencia es mayor que delta, lo que sugiere que debemos considerar vender.
# Calcular AsyEma con los mejores parámetros AsyEma_mejor = calcular_AsyEma(precios_cierre, mejor_alpha) # Identificar zonas donde |AsyEma - Cierre| > delta y donde ocurren compras zonas_alta_diferencia = np.abs(AsyEma_mejor - precios_cierre) > mejor_delta zonas_compra = np.zeros_like(precios_cierre, dtype=bool) posicion = 0 for i in range(len(precios_cierre)): if abs(AsyEma_mejor[i] - precios_cierre[i]) < mejor_delta: if posicion == 0: zonas_compra[i] = True posicion = 1 else: if posicion == 1: posicion = 0 # Graficar el resultado plt.figure(figsize=(14, 8)) plt.plot(datos.index, precios_cierre, label='Precio de Cierre', color='blue') plt.plot(datos.index, AsyEma_mejor, label='AsyEma', color='orange') # Agregar sombreado para zonas de alta diferencia (rojo transparente) for i in range(len(zonas_alta_diferencia)): if zonas_alta_diferencia[i]: plt.axvspan(datos.index[i], datos.index[min(i+1, len(datos.index)-1)], color='red', alpha=0.1) # Agregar sombreado para zonas de compra (verde transparente) for i in range(len(zonas_compra)): if zonas_compra[i]: plt.axvspan(datos.index[i], datos.index[min(i+1, len(datos.index)-1)], color='green', alpha=0.1) plt.xlabel('Fecha') plt.ylabel('Precio') plt.title('Acción con AsyEma y Zonas donde |AsyEma - Cierre| > delta y Zonas de Compra') plt.legend() # Agregar copyright del algoritmo plt.text(0.95, 0.01, '© Di Cecco Antonio', fontsize=10, color='gray', ha='right', va='bottom', transform=plt.gca().transAxes) plt.show()
Conclusión
Al comparar diferentes EMAs asimétricas con una EMA estándar, obtenemos información valiosa sobre cómo la variación de alfa impacta la sensibilidad de nuestras medias móviles. Esto puede ser crucial para desarrollar estrategias de trading más adaptativas y receptivas.
Recuerda que la belleza del trading algorítmico radica en su flexibilidad y precisión. Con herramientas como la EMA asimétrica y la optimización bayesiana, puedes navegar los mercados con confianza y precisión.