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

0
280
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

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.


TagsPython
Raul Canessa

Leave a reply