
Filtrado de estacionalidad y período de tiempo para modelos de Deep Learning ONNX con Python para EA
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:- 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.
- 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.
- 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.
- 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.
- 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
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
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
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
Conclusión sobre el filtrado
Así que, sí. Es cómodo utilizar el filtrado.
Código utilizado y modelos:
- ONNX.eurusd.H1.120.Prediction_FILTERED.mq5
- ONNX.eurusd.H1.120.Prediction_FILTERED_v2.mq5 ONNX.eurusd.H1.120.Prediction_NOT_FILTERED.mq5
- onnx_LSTM_simple_EURUSD_filtered.py onnx_LSTM_simple_EURUSD_not_filtered.py
- Download all data from a symbol EURUSD_LSTM_120_1h_not_filtered.onnx EURUSD_LSTM_120_1h_filtered_v1.onnx EURUSD_LSTM_120_1h_filtered_v2.onnx
¿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:
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)
Si lo comparamos con el modelo filtrado,
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)
15 min sin filtro
30 min con filtro (a partir de ahora usaré siempre filtro)
LSTM.30m.EURUSD.120.0.94.onnx LTSM_simple_30m_filtrado.py ONNX.eurusd.H1.120.30m_eurusd.mq5
1 hora
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)
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
12 H con periodo de 30 min
Periodo de 6h 30 min
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
30 min con periodos NextDay de 2h
30 min con periodos NextDay de 1h
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.
5 minutos con 1 h variable NextDay5 minutos con 30 min NextDay var.
5 minutos con 30 min variable NextDay
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); } }
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





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso