English Русский Deutsch 日本語 Português
preview
Filtrado de estacionalidad y período de tiempo para modelos de Deep Learning ONNX con Python para EA

Filtrado de estacionalidad y período de tiempo para modelos de Deep Learning ONNX con Python para EA

MetaTrader 5Asesores Expertos | 26 agosto 2024, 11:37
54 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introducción

Después de leer el artículo Benefiting from Forex Market Seasonality (Beneficiarse de la estacionalidad del mercado Forex), decidí escribir otro documento para comparar el rendimiento de un EA (Asesor Experto) con y sin la incorporación de estacionalidad, para ver si puede proporcionar alguna ventaja. 

Ya sabía que los mercados estaban influidos por factores estacionales. Esto quedó claro cuando supe que Mark Zuckerberg financió Facebook con dinero de un inversor. Este inversor había invertido previamente el dinero de su Bar Mitzvah en acciones petroleras, prediciendo una subida debido a los huracanes que se esperaban en el Caribe. Había analizado las previsiones meteorológicas que indicaban que se avecinaban condiciones meteorológicas adversas durante ese periodo.

Estoy muy orgulloso e interesado en escribir este artículo, que pretende explotar la idea de que el mercado y la estacionalidad son buenos compañeros. Una buena aproximación para hacer esto realidad sería fusionar ambos EAs en uno solo, pero ya tenemos un artículo al respecto, aquí tienes el enlace: Ejemplo de un conjunto de modelos ONNX en MQL5.

En primer lugar, compararemos modelos con y sin filtrado utilizando un EA, para ver cómo afecta o no el filtrado de datos, y, después de esto, discutiremos la estacionalidad con un gráfico, para finalizar con un caso de estudio real para febrero de 2024, con y sin estacionalidad. En la última parte del artículo (que me parece muy interesante), hablaremos de otras aproximaciones al EA que ya tenemos del artículo: Uso de modelos ONNX en MQL5, y veremos si podemos beneficiarnos de afinar esos EAs y modelos ONNX. Le diré ahora mismo que la respuesta es sí, podemos.

Para que esto suceda, primero descargaremos los datos (todos los ticks) con este script: Download all data from a symbol. Sólo tenemos que añadir el script al gráfico del símbolo que necesitemos estudiar, y, en algún tiempo (menos de una hora) tendremos descargados todos los ticks históricos de ese símbolo en nuestra carpeta Archivos.

Cuando tengamos todos los ticks descargados, trabajaremos con ese csv y extraeremos sólo los datos que necesitemos o queramos (en este caso, los periodos de enero de 2015 a febrero a 2023).


Estacionalidad

La estacionalidad en el comercio consiste en detectar los altibajos regulares de los precios de los activos que se producen de forma previsible a lo largo del año. Es como reconocer que ciertas acciones tienden a ir mejor en ciertos momentos que en otros. Desmenucemos un poco esta idea.

Comprender la estacionalidad en el comercio:

  • Definición: Estacionalidad significa observar cómo los precios tienden a fluctuar siguiendo un patrón recurrente en función de la época del año. Puede estar vinculada a estaciones concretas (como verano o invierno), a temporadas comerciales (como las vacaciones) o incluso a meses específicos.
  • Ejemplos: Los inversores inteligentes no pierden de vista estos patrones porque suelen ser fiables y rentables. He aquí algunos ejemplos:
    • Estacionalidad meteorológica: Al igual que el clima afecta a las temporadas agrícolas, también influye en aspectos como los precios de las materias primas y los valores relacionados. Por ejemplo, una empresa que venda artículos de playa puede registrar un aumento de las ventas en verano, pero un descenso en los meses más fríos, lo que afectaría a sus acciones.
    • Estacionalidad navideña: Los valores minoristas suelen repuntar durante el frenesí de las compras navideñas. Las empresas que prosperan con las ventas navideñas, como las tiendas de regalos, tienden a brillar en estas fechas.
    • Estacionalidad de los beneficios trimestrales: Las empresas que cotizan en bolsa publican sus resultados trimestralmente, y la cotización de sus acciones puede reaccionar de forma previsible durante estas temporadas.
    • Estacionalidad fiscal: Los acontecimientos relacionados con la fiscalidad pueden agitar el mercado, especialmente para los sectores vinculados a las finanzas.
    • Ciclos naturales: Industrias como el turismo o la energía tienen sus propios patrones de demanda estacional, como las vacaciones de verano o las necesidades de calefacción en invierno.

Estrategias de negociación basadas en la estacionalidad:

  • Los operadores pueden aprovechar la estacionalidad de varias maneras:
    • Identificación de patrones estacionales: Profundizar en los datos del pasado para detectar tendencias que se repiten en determinadas épocas del año.
    • Cronometrar las operaciones: Entrar y salir de posiciones en función de estas tendencias estacionales.
    • Gestión del riesgo: ajuste del riesgo asumido en periodos de volatilidad.
    • Rotación sectorial: Cambiar las inversiones entre sectores que tienden a obtener mejores resultados en diferentes épocas del año.


Filtrado de datos

Utilizaremos el filtro de paso bajo. Según Wikipedia:

Un filtro de paso bajo es un filtro que deja pasar señales con una frecuencia inferior a una frecuencia de corte seleccionada y atenúa las señales con frecuencias superiores a la frecuencia de corte. La respuesta en frecuencia exacta del filtro depende de su diseño. A veces se denomina filtro de corte de agudos o filtro de corte de agudos en aplicaciones de audio. Un filtro de paso bajo es el complemento de un filtro de paso alto.

¿Por qué elegimos filtros de paso bajo en lugar de paso alto en la negociación algorítmica? En la negociación algorítmica, la preferencia por los filtros de paso bajo se debe a varias ventajas fundamentales:
  1. Suavizado de señales: los filtros de paso bajo suavizan eficazmente los movimientos ruidosos de los precios, destacando las tendencias a largo plazo frente a las fluctuaciones a corto plazo.
  2. Reducción del ruido de alta frecuencia: Ayudan a atenuar el ruido de alta frecuencia, que puede no aportar información significativa para las estrategias de negociación.
  3. Menores costes de transacción: Al centrarse en las tendencias a más largo plazo, los filtros de paso bajo pueden dar lugar a menos operaciones y más estratégicas, reduciendo potencialmente los gastos de transacción.
  4. Mejor gestión del riesgo: Los filtros de paso bajo contribuyen a una estrategia de negociación más estable y predecible, reduciendo el impacto de las fluctuaciones del mercado a corto plazo.
  5. Alineación con el horizonte de inversión: Se adaptan bien a las estrategias con horizontes de inversión a más largo plazo, captando eficazmente las tendencias a lo largo de periodos prolongados.

Personalmente, utilizo un filtro de paso bajo para filtrar las frecuencias altas. No tiene mucho sentido utilizar aquí un filtro de paso alto.

Esto es lo que usaremos (nota: terminé cambiando los parámetros order y cutoff_frequency en la última parte del artículo a 0.1 cutoff y order igual a 1, porque terminaron dando mejores resultados. Además, los .py correctos para filtrar son los de la última parte del artículo (ahí no sólo usé mejores parámetros, también usé minmaxscaler para ajustar e invertir).

# Low-pass filter parameters
cutoff_frequency = 0.01  # Cutoff frequency as a proportion of the Nyquist frequency
order = 4

# Apply the low-pass filter
def butter_lowpass_filter(data, cutoff, fs, order):
    nyquist = 0.5 * fs
    normal_cutoff = cutoff / nyquist
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    print("Filter coefficients - b:", b)
    print("Filter coefficients - a:", a)
    y = lfilter(b, a, data)
    return y

filtered_data_low = butter_lowpass_filter(df2['close'], cutoff_frequency, fs=1, order=order)

Utilizaremos «onnx_LSTM_simple_filtered.py» y «onnx_LSTM_simple_not_filtered.py» para crear los modelos ONNX y compararlos.

Nota: He utilizado el modelo v1 y el modelo v2 que tienen diferentes parámetros de filtro de paso bajo.

Aquí están los resultados:

Utilizaremos las mismas entradas para los EAs

inputs

inputs2

El periodo de tiempo a estudiar, será del primero de febrero al primero de marzo.

Para el modelo no filtrado:

RMSE         : 0.0010798043714784716
MSE          : 1.165977480664017e-06
R2 score     : 0.8799146678247277

Not filtered


Filtrado v1

# Parámetros del filtro pasa bajo

cutoff_frequency = 0.01  # Frecuencia de corte en proporción a la frecuencia de Nyquist

order = 4


RMSE         : 0.0010228999869332884
MSE          : 1.0463243832681218e-06
R2 score     : 0.8922378749062259


filtered v1


Filtrado v2

cutoff_frequency = 0.1  # Frecuencia de corte en proporción a la frecuencia de Nyquist

order = 2

RMSE         : 0.0010899163515744447
MSE          : 1.1879176534293484e-06
R2 score     : 0.8775550550819025

filtered v2


Conclusión sobre el filtrado

Sólo da mejores resultados si se utilizan los parámetros correctos.

Así que, sí. Es cómodo utilizar el filtrado.

Código utilizado y modelos:


¿Los símbolos son estacionales?

Para esta parte, primero lo veremos en un gráfico, obtendremos los datos de febrero desde 2015 hasta 2023 y sumaremos los datos para ver cómo se mueve en torno a esas semanas.

Esto es lo que podemos ver de ese periodo:

Sum of febrary

Conclusión:

Podemos ver que tiene algunas tendencias, o al menos no vemos una línea horizontal negra (línea de suma). Hay espacios entre cada línea, porque el símbolo cotiza a lo largo del año y sus precios fluctúan. Por eso, en la siguiente parte del artículo, vamos a concatenar todos los símbolos por años para febrero y por eso necesitamos usar un filtro, para que no pase la alta frecuencia de, por ejemplo, la última fecha de 2022 a la primera de febrero de 2023 y cuando la IA se entrena de, por ejemplo, el cierre de un viernes y la apertura de un lunes, entonces, no estudia esos cambios y busca un dato más suavizado.

Guiones y datos utilizados:


¿Están correlacionados estos datos del símbolo?

La autocorrelación es una característica de los datos que muestra el grado de similitud entre valores en intervalos de tiempo sucesivos.

Un valor cercano a 1 indica que existe una gran correlación positiva.

A continuación se muestran los resultados que hemos obtenido con autocorrelation.py

[1.         0.99736147 0.99472432 0.99206626 0.98937664 0.98671649
 0.98405706 0.98144222 0.9787753  0.97615525 0.97356318 0.97099777
 0.96848029 0.96602671 0.96360361 0.96113539 0.95865344 0.95615626
 0.95362417 0.95108177 0.94854957 0.94599045 0.94346076 0.94091564
 0.93837742 0.93583734 0.9332909  0.93074655 0.92826504 0.92579028
 0.92330505 0.92084645 0.91834403 0.91581296 0.91328091 0.91076099
 0.90826447]


Creación de un modelo ONNX para las temporadas de febrero

Para esta tarea, sólo tenemos que concatenar todos los datos en un único csv y hacer un modelo con él.

Usaremos el concat seasonal.py creado para hacer un único csv, que se añade al zip seasonal_feb_contac. Con el onnx_LSTM_..._seasonals.py entrenaremos y crearemos el modelo.

Guiones y datos utilizados (todos se adjuntan a continuación):


Resultados de las pruebas del modelo estacional y comparación con el modelo filtrado de 120 días (1h)

Modelo estacional

RMSE         : 0.013137568368684325
MSE          : 0.00017259570264185493
R2 score     : 0.7166764010650979

Aunque no son asombrosos, los resultados parecen buenos en general (no tiene muchos resultados negativos de Sharpe)

Sharpe

sharpe2

optimización estacional


Si lo comparamos con el modelo filtrado,

Sharpe filtered

Sharpe filtered 2d

optimización filtrada


Lo que me ha parecido curioso, es que el número de valores de Sharpe negativos ocupa la mitad de la tabla de la optimización para el modelo filtrado, mientras que los negativos para el modelo estacional ocupan alrededor de una quinta parte de la tabla. Esto es notable, porque incluso teniendo un r2 más bajo, parece ser un modelo robusto que da rendimientos rentables.

También podría haber probado el EA sin SL y TP, pero encuentro una mejor práctica usarlos siempre en los EAs.


Código y modelo ONNX utilizado (todo ello adjunto a continuación):

ONNX.eurusd.H1.120.Prediction_seasonal.mq5 EURUSD_LSTM_270_1h_filtered_seasonal0.72.onnx onnx_LSTM_simple_EURUSD_concat_seasonals.py



¿Qué periodo de tiempo utilizar?

Para esta parte del artículo, he afinado el filtro para tener mejores resultados para EURUSD

cutoff_frequency = 0.1  # Frecuencia de corte en proporción a la frecuencia de Nyquist
order = 1

También he modificado el filtro:

from sklearn.preprocessing import MinMaxScaler
from scipy.signal import butter, lfilter

# Parámetros del filtro pasa bajo
cutoff_frequency = 0.1  # Frecuencia de corte en proporción a la frecuencia de Nyquist
order = 1

# Aplicar filtro pasa bajo
def butter_lowpass_filter(data, cutoff, fs, order):
    nyquist = 0.5 * fs
    normal_cutoff = cutoff / nyquist
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    print("Coeficientes del filtro - b:", b)
    print("Coeficientes del filtro - a:", a)
    y = lfilter(b, a, data)
    return y

scaled = MinMaxScaler(feature_range=(0,1))

valores_df1 = df.filter(['close']).values
valores_df1 = pd.DataFrame(valores_df1)
x_features1 = valores_df1[[0]]
valores_df2 = x_features1.values


data_escalada = scaled.fit_transform(valores_df2)

print(data_escalada)

filtered_data_low = butter_lowpass_filter(data_escalada, cutoff_frequency, fs=1, order=order)
print("filtered_data_low",filtered_data_low)

filtered_data_low_unscaled = scaled.inverse_transform(filtered_data_low)

El pedido debe redondearse a un precio entero.

Esta estrategia se prueba durante un mes para el intervalo de 1 hora, concretamente en febrero de 2024. Para el intervalo de 30 minutos, las pruebas se realizan del 1 al 15 de febrero, y así sucesivamente.

Para el intervalo de 15 minutos, realicé pruebas con y sin filtros. Los resultados indican que utilizar un filtro con ajuste fino da mejores resultados (al menos en general).

15 min con filtro (Sharpe)

pruebas 15 min con filtro


tester 2d 15 min

prueba 15min con filtro


LTSM_simple_15m_filtrado.py LSTM.15m.EURUSD.120.0.98.onnx ONNX.eurusd.H1.120.15m_eurusd.mq5


15 min sin filtro

15 min sin pruebas de filtro

2d 15 min sin filtro

pruebas 15 min con filtro.

LTSM_simple_15m_filtrado_sin.py LSTM.15m.EURUSD.120.0.98.onnx ONNX.eurusd.H1.120.15m_eurusd.mq5


30 min con filtro (a partir de ahora usaré siempre filtro)

tester 30 min

tester 30 min 2d

testing 30 min


LSTM.30m.EURUSD.120.0.94.onnx
LTSM_simple_30m_filtrado.py ONNX.eurusd.H1.120.30m_eurusd.mq5


1 hora

1 hour tests

1 hour tests heatmap

1 hour tests


1 hour files.zip (815.64 KB)


2 horas

Como no puedo utilizar un período de dos días, he utilizado un período de un día (para el valor de los próximos días en el EA)

tester 2h

heat map 2h

tests 2h

graph 2h

Backtesting 2h


2h.zip

Como no puedo cargar más de 32 archivos, los siguientes archivos se cargarán todos en una carpeta.


Conclusión

Parece que con esta estrategia, a medida que aumenta el periodo, los resultados parecen más sólidos para todos los TP y SL.


Variable NextDay

He utilizado la estrategia de este artículo: Uso de modelos ONNX en MQL5, pero como he tenido buenos resultados con la estrategia de 2horas, y en esa he usado el periodo de 1Dia. Estudiaremos otros valores para la variable NextDays

   if(TimeCurrent()>=ExtNextDay)
     {
      GetMinMax();
      //--- set next day time
      ExtNextDay=TimeCurrent();
      ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1);
      ExtNextDay+=PeriodSeconds(PERIOD_D1);
     }
void GetMinMax(void)
  {
   vectorf close;
   close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE);
   ExtMin=close.Min();
   ExtMax=close.Max();
  }

Ahora procederemos a estudiar EURUSD con periodo 30min con datos filtrados, y diferentes periodos NextDay (Usaremos 1D, 12h y 6h) y discutiremos los resultados.

1D con periodo de 30 min

1D 30min

1D heatmap 30min

1D tests


12 H con periodo de 30 min

12h 30 min tests

12h heatmap

12h tests 30 min


Periodo de 6h 30 min

Periodo de 6h 30 min

6h heatmap 30 min period

h6 30 min tests


Los resultados parecen mejores cuando se ajusta la variable NextDay. Veamos cómo evoluciona en los periodos inferiores del NextDay.

30 min con periodos NextDay de 4h

4h periods

4h period heatmaps

tests 4h periods


30 min con periodos NextDay de 2h

30 min 2 h periods

30 min 2h nextday periods heatmap

2h period tests


30 min con periodos NextDay de 1h

h1 periods 30 min

h1 heatmap 30 min

tests 1h 30 min


Parece que para periodos de al menos 30 minutos, alrededor de 8 a 12 compases de 30 minutos dan mejores resultados.


Como este «juego» es para ganar más dinero, y una forma de hacerlo es tener más operaciones ganadoras y una estrategia sólida. Veamos si podemos usar este inicio para ganar con periodos de 5 minutos, así que probaremos este strat para 5 minutos y 1 h y 30 min para la variable NextDay.

Inputs

5 minutos con 1 h variable NextDay5 minutos con 30 min NextDay var.

tester 1 h (5 min period)

heatmap 1h (5 min period)

results test 1h (5 min)



5 minutos con 30 min variable NextDay

tester 30 min (5 min)

heatmap 30 min (5 min)

testing 30 min (5 min)

Archivos utilizados: Last files.zip

Otros periodos de tiempo, parecen ser más fiables, pero, si quieres otro EA, tienes más para usar.


Por último, podemos hacer ajustes a la estrategia, que podrían acabar dando mejores resultados o resultados más sólidos, como establecer un límite de tiempo o de barras, por ejemplo, en periodos de 1 h, y utilizando un 12h para el día siguiente, podríamos establecer que una vez realizada la orden, ésta debe permanecer abierta no más de 12 horas.

   if(ExtPredictedClass>=0)
     {
      if(PositionSelect(_Symbol))
         CheckForClose();
      else
        {
         CheckForOpen();
         started_time1 = rates[0].time;
         string started_time1_str = TimeToString(started_time1);
int total = PositionsTotal();
   if(total>0)
     {
      datetime started_time2 = rates[0].time;
      string started_time2_str = TimeToString(started_time2);
      int segundos = started_time2 - started_time1;
      Print("Tiempo 1: ", started_time1_str);
      Print("Tiempo 2: ", started_time2_str);
      Print("Diferencia en segundos: ", segundos);
      if((segundos >= days*PeriodSeconds(PERIOD_H12)) && segundos != 0)
        {
         Print("closing position----------------");
         ExtTrade.PositionClose(_Symbol,3);

        }
     }

tester

heatmap

tests

graph


ONNX.eurusd.120.1h_H12_eurusd.v3.mq5
 (9.23 KB)


Conclusión

Hemos visto la estacionalidad, el filtrado y la comparación de modelos, EAs y parámetros, tratando de lograr mejores resultados con un ajuste fino. Espero que haya disfrutado leyendo este artículo tanto como yo haciéndolo. 


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14424

Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Características del Wizard MQL5 que debe conocer (Parte 13): DBSCAN para la clase experta de señales Características del Wizard MQL5 que debe conocer (Parte 13): DBSCAN para la clase experta de señales
El agrupamiento basado en densidad para aplicaciones con ruido (DBSCAN) es una forma no supervisada de agrupar datos que apenas requiere parámetros de entrada, salvo solo 2, lo cual, en comparación con otros enfoques como k-means, es una ventaja. Profundizamos en cómo esto podría ser constructivo para probar y eventualmente operar con Asesores Expertos montados por Wizard MQL5.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Desarrollamos un Asesor Experto multidivisas (Parte 4): Órdenes pendientes virtuales y guardado del estado Desarrollamos un Asesor Experto multidivisas (Parte 4): Órdenes pendientes virtuales y guardado del estado
Tras empezar a desarrollar un EA multidivisa, ya hemos obtenido algunos resultados y hemos conseguido realizar varias iteraciones de mejora del código. Sin embargo, nuestro EA fue incapaz de trabajar con órdenes pendientes y reanudar la operación después del reinicio del terminal. Añadamos estas características.