
Ejemplo de Análisis de Redes de Causalidad (CNA), Control Óptimo de Modelos Estocásticos (SMOC) y la Teoría de Juegos de Nash con Aprendizaje Profundo (Deep Learning)
Introducción
Vamos a recorrer el proceso de agregar Deep Learning a tres asesores expertos comerciales avanzados, estos tres expertos son EA avanzados que se publicaron como artículos.
Estos tres artículos están publicados aquí en www.mql5.com
- Aplicación de la Teoría de Juegos de Nash con filtrado HMM en el trading
- Ejemplo de análisis de redes de causalidad (Causality Network Analysis, CNA) y modelo de autorregresión vectorial para la predicción de eventos de mercado
- Ejemplo de optimización estocástica y control óptimo
Si no has leído estos artículos, te explicaré muy brevemente qué son, pero te recomiendo encarecidamente que los leas.
Teoría de Juegos de Nash
El equilibrio de Nash se considera uno de los componentes básicos de la teoría de juegos.
El equilibrio de Nash es un concepto de la teoría de juegos en el que se supone que cada jugador conoce las estrategias de equilibrio de los demás jugadores y ningún jugador tiene nada que ganar cambiando únicamente su propia estrategia.
En un equilibrio de Nash, la estrategia de cada jugador es óptima dadas las estrategias de todos los demás jugadores. Un juego puede tener múltiples equilibrios de Nash o ninguno.
Análisis de redes de causalidad
El análisis de redes de causalidad (Causality Network Analysis, CNA) es un método utilizado para comprender y modelar relaciones causales complejas entre variables en un sistema. Cuando se aplica a los mercados financieros, puede ayudar a identificar cómo los diferentes eventos y factores del mercado se influyen entre sí, lo que potencialmente conduce a predicciones más precisas.
El bot utiliza inferencia de causalidad rápida.
La inferencia casual rápida es un concepto que combina la inferencia estadística rápida con el análisis causal. Su objetivo es extraer conclusiones causales rápidamente a partir de los datos, equilibrando la velocidad y la comprensión causal. Este enfoque es útil en escenarios que requieren decisiones rápidas basadas en datos y que también necesitan conocimientos sobre las relaciones causa-efecto. Las aplicaciones pueden incluir análisis de mercado en tiempo real, toma de decisiones comerciales rápidas y estudios epidemiológicos rápidos. El método sacrifica cierta profundidad de análisis por velocidad, lo que lo hace adecuado para entornos sensibles al tiempo donde comprender los vínculos causales es crucial.
Optimización estocástica
El modelado estocástico y la optimización del control son técnicas matemáticas que ayudan a resolver problemas en condiciones de incertidumbre. Encuentran aplicaciones en finanzas, ingeniería, inteligencia artificial y muchas otras áreas.
El modelado estocástico se utiliza para describir sistemas con elementos de aleatoriedad, como los movimientos de precios en el mercado de valores o una cola en un restaurante. Se basa en variables aleatorias, distribuciones de probabilidad y procesos estocásticos. Métodos como Monte Carlo y cadenas de Markov pueden modelar estos procesos y predecir su comportamiento.
La optimización de la gestión le ayuda a encontrar las mejores soluciones para la gestión de sistemas. Se utiliza para automatizar y mejorar el funcionamiento de diversos procesos, desde la conducción de automóviles hasta la operación de plantas químicas. Los métodos básicos incluyen el controlador cuadrático lineal, el control predictivo de modelos y el aprendizaje de refuerzo.
Aprendizaje profundo (Deep Learning, DP)
El aprendizaje profundo aplicado al trading utiliza redes neuronales artificiales para analizar y predecir las tendencias del mercado financiero. He aquí una breve descripción general:
- Análisis de datos: Los modelos de aprendizaje profundo procesan grandes cantidades de datos financieros, incluidos historiales de precios, volúmenes comerciales, indicadores económicos e incluso opiniones sobre las noticias.
- Reconocimiento de patrones: estos modelos pueden identificar patrones y relaciones complejos en los datos del mercado que pueden no ser evidentes para los operadores humanos o los métodos de análisis tradicionales.
- Predicción: basándose en los patrones identificados, los modelos de aprendizaje profundo intentan predecir movimientos futuros del mercado, tendencias de precios o estrategias comerciales óptimas.
- Comercio automatizado: algunos sistemas utilizan aprendizaje profundo para tomar decisiones comerciales autónomas y ejecutar operaciones según las predicciones del modelo.
- Gestión de riesgos: el aprendizaje profundo se puede utilizar para evaluar y gestionar los riesgos comerciales de forma más eficaz analizando múltiples factores de riesgo simultáneamente.
- Adaptabilidad: Estos modelos pueden aprender continuamente y adaptarse a las condiciones cambiantes del mercado, mejorando potencialmente su desempeño con el tiempo.
- Trading de alta frecuencia: el aprendizaje profundo puede ser particularmente útil en escenarios de trading de alta frecuencia, donde las decisiones tomadas en fracciones de segundo son cruciales.
Si bien el aprendizaje profundo en el trading ofrece capacidades poderosas, es importante tener en cuenta que los mercados son complejos e impredecibles. Estos modelos no son infalibles y aun así requieren una cuidadosa supervisión y gestión de riesgos.
¿Por qué deberíamos tener un EA con Deep Learning?
Haremos un modelo de Deep Learning con Python, creando un modelo ONNX, y agregaremos ese modelo a cada uno de los EA, y compararemos resultados con y sin Deep Learning.
Se utilizarán modelos ONNX porque se pueden realizar fácilmente en Python y son un puente para diferentes ecosistemas, promoviendo la flexibilidad y la eficiencia en el desarrollo e implementación de modelos. Python es una forma rápida de crear modelos ONNX o realizar pruebas retrospectivas rápidamente como una estrategia. Para este artículo, utilizaremos Python 3.11.9.
Usaremos Deep Learning con este EA porque los resultados anteriores de los artículos podrían ser mejores, agregaremos condiciones para que el EA ejecute operaciones o no si los modelos de Deep Learning también lo indican.
El script de Python
Usaremos este script .py, este script crea un modelo DL en ONNX, y también tiene métricas para ver si el modelo está hecho correctamente, DL tiene problemas cuando se hace incorrectamente porque puede estar subajustado o sobreajustado, lo que lleva a predicciones incorrectas.
#python 3.11.9, tensorflow==2.12.0, keras==2.12.0, tf2onnx==1.16.0 # python libraries import MetaTrader5 as mt5 import tensorflow as tf import numpy as np import pandas as pd import tf2onnx #import tensorflow as tf #import tf2onnx import keras print(f"TensorFlow version: {tf.__version__}") print(f"Keras version: {keras.__version__}") print(f"tf2onnx version: {tf2onnx.__version__}") # input parameters inp_history_size = 120 sample_size = inp_history_size*3*20 symbol = "EURUSD" optional = "D1_2024" inp_model_name = str(symbol)+"_"+str(optional)+".onnx" if not mt5.initialize(): print("initialize() failed, error code =",mt5.last_error()) quit() # we will save generated onnx-file near the our script to use as resource from sys import argv data_path=argv[0] last_index=data_path.rfind("\\")+1 data_path=data_path[0:last_index] print("data path to save onnx model",data_path) # and save to MQL5\Files folder to use as file terminal_info=mt5.terminal_info() file_path=terminal_info.data_path+"\\MQL5\\Files\\" print("file path to save onnx model",file_path) # set start and end dates for history data from datetime import timedelta, datetime #end_date = datetime.now() end_date = datetime(2024, 1, 1, 0) start_date = end_date - timedelta(days=inp_history_size*20) # print start and end dates print("data start date =",start_date) print("data end date =",end_date) # get rates eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size) # create dataframe df = pd.DataFrame(eurusd_rates) # get close prices only data = df.filter(['close']).values # scale data from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data) # training size is 80% of the data training_size = int(len(scaled_data)*0.80) print("Training_size:",training_size) train_data_initial = scaled_data[0:training_size,:] test_data_initial = scaled_data[training_size:,:1] # split a univariate sequence into samples def split_sequence(sequence, n_steps): X, y = list(), list() for i in range(len(sequence)): # find the end of this pattern end_ix = i + n_steps # check if we are beyond the sequence if end_ix > len(sequence)-1: break # gather input and output parts of the pattern seq_x, seq_y = sequence[i:end_ix], sequence[end_ix] X.append(seq_x) y.append(seq_y) return np.array(X), np.array(y) # split into samples time_step = inp_history_size x_train, y_train = split_sequence(train_data_initial, time_step) x_test, y_test = split_sequence(test_data_initial, time_step) # reshape input to be [samples, time steps, features] which is required for LSTM x_train =x_train.reshape(x_train.shape[0],x_train.shape[1],1) x_test = x_test.reshape(x_test.shape[0],x_test.shape[1],1) # define model from keras.models import Sequential from keras.layers import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM from keras.metrics import RootMeanSquaredError as rmse from tensorflow.keras import callbacks model = Sequential() model.add(Conv1D(filters=256, kernel_size=2, strides=1, padding='same', activation='relu', input_shape=(inp_history_size,1))) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100, return_sequences = True)) model.add(Dropout(0.3)) model.add(LSTM(100, return_sequences = False)) model.add(Dropout(0.3)) model.add(Dense(units=1, activation = 'sigmoid')) model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()]) # Set up early stopping early_stopping = callbacks.EarlyStopping( monitor='val_loss', patience=20, restore_best_weights=True, ) # model training for 300 epochs history = model.fit(x_train, y_train, epochs = 300 , validation_data = (x_test,y_test), batch_size=32, callbacks=[early_stopping], verbose=2) # evaluate training data train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = 32) print(f"train_loss={train_loss:.3f}") print(f"train_rmse={train_rmse:.3f}") # evaluate testing data test_loss, test_rmse = model.evaluate(x_test,y_test, batch_size = 32) print(f"test_loss={test_loss:.3f}") print(f"test_rmse={test_rmse:.3f}") # Define a function that represents your model @tf.function(input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)]) def model_function(x): return model(x) output_path = data_path+inp_model_name # Convert the model to ONNX onnx_model, _ = tf2onnx.convert.from_function( model_function, input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)], opset=13, output_path=output_path ) print(f"Saved ONNX model to {output_path}") # save model to ONNX output_path = data_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") output_path = file_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") #prediction using testing data test_predict = model.predict(x_test) print(test_predict) print("longitud total de la prediccion: ", len(test_predict)) print("longitud total del sample: ", sample_size) plot_y_test = np.array(y_test).reshape(-1, 1) # Selecciona solo el último elemento de cada muestra de prueba plot_y_train = y_train.reshape(-1,1) train_predict = model.predict(x_train) #print(plot_y_test) #calculate metrics from sklearn import metrics from sklearn.metrics import r2_score #transform data to real values value1=scaler.inverse_transform(plot_y_test) #print(value1) # Escala las predicciones inversas al transformarlas a la escala original value2 = scaler.inverse_transform(test_predict.reshape(-1, 1)) #print(value2) #calc score score = np.sqrt(metrics.mean_squared_error(value1,value2)) print("RMSE : {}".format(score)) print("MSE :", metrics.mean_squared_error(value1,value2)) print("R2 score :",metrics.r2_score(value1,value2)) #sumarize model model.summary() #Print error value11=pd.DataFrame(value1) value22=pd.DataFrame(value2) #print(value11) #print(value22) value111=value11.iloc[:,:] value222=value22.iloc[:,:] print("longitud salida (tandas de 1 hora): ",len(value111) ) print("en horas son " + str((len(value111))*60*24)+ " minutos") print("en horas son " + str(((len(value111)))*60*24/60)+ " horas") print("en horas son " + str(((len(value111)))*60*24/60/24)+ " dias") # Calculate error error = value111 - value222 import matplotlib.pyplot as plt # Plot error plt.figure(figsize=(7, 6)) plt.scatter(range(len(error)), error, color='blue', label='Error') plt.axhline(y=0, color='red', linestyle='--', linewidth=1) # Línea horizontal en y=0 plt.title('Error de Predicción ' + str(symbol)) plt.xlabel('Índice de la muestra') plt.ylabel('Error') plt.legend() plt.grid(True) plt.savefig(str(symbol)+str(optional)+'.png') rmse_ = format(score) mse_ = metrics.mean_squared_error(value1,value2) r2_ = metrics.r2_score(value1,value2) resultados= [rmse_,mse_,r2_] # Abre un archivo en modo escritura with open(str(symbol)+str(optional)+"results.txt", "w") as archivo: # Escribe cada resultado en una línea separada for resultado in resultados: archivo.write(str(resultado) + "\n") # finish mt5.shutdown() #show iteration-rmse graph for training and validation plt.figure(figsize = (7,10)) plt.plot(history.history['root_mean_squared_error'],label='Training RMSE',color='b') plt.plot(history.history['val_root_mean_squared_error'],label='Validation-RMSE',color='g') plt.xlabel("Iteration") plt.ylabel("RMSE") plt.title("RMSE" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+'1.png') #show iteration-loss graph for training and validation plt.figure(figsize = (7,10)) plt.plot(history.history['loss'],label='Training Loss',color='b') plt.plot(history.history['val_loss'],label='Validation-loss',color='g') plt.xlabel("Iteration") plt.ylabel("Loss") plt.title("LOSS" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+'2.png') #show actual vs predicted (training) graph plt.figure(figsize=(7,10)) plt.plot(scaler.inverse_transform(plot_y_train),color = 'b', label = 'Original') plt.plot(scaler.inverse_transform(train_predict),color='red', label = 'Predicted') plt.title("Prediction Graph Using Training Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+'3.png') #show actual vs predicted (testing) graph plt.figure(figsize=(7,10)) plt.plot(scaler.inverse_transform(plot_y_test),color = 'b', label = 'Original') plt.plot(scaler.inverse_transform(test_predict),color='g', label = 'Predicted') plt.title("Prediction Graph Using Testing Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+'4.png')
La salida que da este script .py se parece a esto:
177/177 - 51s - loss: 2.4565e-04 - root_mean_squared_error: 0.0157 - val_loss: 4.8860e-05 - val_root_mean_squared_error: 0.0070 - 51s/epoch - 291ms/step 177/177 [==============================] - 15s 82ms/step - loss: 1.0720e-04 - root_mean_squared_error: 0.0104 train_loss=0.000 train_rmse=0.010 42/42 [==============================] - 3s 74ms/step - loss: 4.4485e-05 - root_mean_squared_error: 0.0067 test_loss=0.000 test_rmse=0.007 Saved ONNX model to c:\XXXXXXXXXXXXXXX\EURUSD_D1_2024.onnx saved model to c:\XXXXXXXXXXXXXXXX\EURUSD_D1_2024.onnx saved model to C:\XXXXXXXXXX\MQL5\Files\EURUSD_D1_2024.onnx 42/42 [==============================] - 5s 80ms/step [[0.40353602] [0.39568084] [0.39963558] ... [0.35914657] [0.3671721 ] [0.3618655 ]] longitud total de la prediccion: 1320 longitud total del sample: 7200 177/177 [==============================] - 13s 72ms/step RMSE : 0.005155711558172936 MSE : 2.6581361671078003e-05 R2 score : 0.9915315181564703 Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv1d (Conv1D) (None, 120, 256) 768 max_pooling1d (MaxPooling1D (None, 60, 256) 0 ) lstm (LSTM) (None, 60, 100) 142800 dropout (Dropout) (None, 60, 100) 0 lstm_1 (LSTM) (None, 100) 80400 dropout_1 (Dropout) (None, 100) 0 dense (Dense) (None, 1) 101 ================================================================= Total params: 224,069 Trainable params: 224,069 Non-trainable params: 0 ________________________________________________________________
Este modelo podría estar sobreajustado, debido a estos resultados, podría estar en el límite de una buena predicción y estar sobreajustado:
RMSE : 0.005155711558172936 MSE : 2.6581361671078003e-05 R2 score : 0.9915315181564703
Las métricas principales son estas tres, R2, da un porcentaje de resultados probados de buenas predicciones, y RMSE, MSE son los errores, si quieres saber más sobre esto puedes buscarlo en Google.
Como vamos a hacer esto para el mismo símbolo (EURUSD), solo tenemos que hacer un modelo DL y lo probaremos para 2024.
Hemos seleccionado el periodo de tiempo de 1 día, para no consumir demasiado de/con nuestros ordenadores.
El script py también muestra algunos gráficos que se guardan (es otra forma de ver si los modelos están sobreajustados o subajustados).
Los gráficos deberían verse así:
El script también guarda un .txt donde se guardan las métricas anteriores en el mismo orden en que aparecen en la terminal.
Para utilizar este script, primero debe instalar Python 3.11.9 e instalar estas bibliotecas con; pip install:
tensorflow==2.12.0, keras==2.12.0, tf2onnx==1.16.0, MetaTrader5, pandas, numpy, scikit-learn, tf2onnx, keras
Recomiendo encarecidamente utilizar Visual Studio Code, que puedes descargar desde la Tienda de Microsoft.
Es posible que te pida que lo instales
Microsoft Visual C++ 2015 - 2022 Redistributable
He adjuntado el script .py, simplemente ábralo en VSC y ejecútelo.
Si desea cambiar algo (otro símbolo, período de donde obtener los datos o período de tiempo), cambie solo estas líneas:
symbol = "EURUSD" optional = "D1_2024"
El símbolo es obvio, recomiendo cambiar de forma opcional el periodo de tiempo utilizado y la última fecha de los datos.
eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)
En esta línea es donde se cambia el marco temporal, por ejemplo H1 (marco temporal de 1 hora).
Ahora que tenemos el modelo Deep Learning ONNX, podemos agregarlo a los EA que ya tenemos de los otros artículos.
Adjuntaré el modelo DL ONNX (2024).
¿Qué añadiremos a los EA?
Modificaciones de la CNA
Esto es básico, voy a explicar este proceso muy lentamente.
1º, al principio, donde están los parámetros de entrada, tenemos que agregar estas líneas:
#include <Trade\Trade.mqh> #resource "/Files/EURUSD_D1.onnx" as uchar ExtModel[] #define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; CTrade ExtTrade; int dlsignal=-1; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2
Recuerda modificar esta línea con el modelo ONNX que creaste (los scripts de py guardan en MQL5/Files/ ... el modelo).
#resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]
En OnInit() debes agregar esto:
//--- create a model from static buffer ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT); if(ExtHandle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (only Close) const long input_shape[] = {1,SAMPLE_SIZE,1}; if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape)) { Print("OnnxSetInputShape error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the output tensor we must set them explicitly //--- first index - batch size, must match the batch size of the input tensor //--- second index - number of predicted prices (we only predict Close) const long output_shape[] = {1,1}; if(!OnnxSetOutputShape(ExtHandle,0,output_shape)) { Print("OnnxSetOutputShape error ",GetLastError()); return(INIT_FAILED); }
¿Dónde? En cualquier lugar... no importa (justo antes del retorno de la función).
En la función OnDeinit, también debes agregar esto:
if(ExtHandle!=INVALID_HANDLE) { OnnxRelease(ExtHandle); ExtHandle=INVALID_HANDLE; }
¿Dónde? En cualquier lugar, esto libera el modelo ONNX.
En la función OnTick debes agregar esto:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice();
¿Dónde? Al principio, esto es importante porque el resultado de la función PredictPrice() debe agregarse a la lógica del orden de ejecución.
En la función OnTick() también se debe modificar la función que realiza las órdenes, en este caso (CNA_DL), cambiaremos la línea original:
int signal = GenerateSignal(symbol, prediction);
A esta línea:
int signal = GenerateSignal(symbol, prediction, ExtPredictedClass);
ExtPredictedClass es el resultado de la función PredictedPrice() que agregamos antes.
Ahora, también tenemos que modificar la lógica de GenerateSignal(), para que utilice ExtPredictedClass:
Tendremos que cambiar el encabezado de esa función:
int GenerateSignal(string symbol, double prediction, int dlsgnl)
y agregamos la nueva variable a la lógica para realizar pedidos, en este caso aquí:
De esto:bool buy_condition = prediction > 0.00001 && rsi < 30 && trend_strong && volatility_ok && fastMA > slowMA; bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA;A esto:
bool buy_condition = prediction > 0.00001 && rsi < 30 && trend_strong && volatility_ok && fastMA > slowMA && dlsgnl==PRICE_UP; bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA && dlsgnl==PRICE_DOWN;
La última parte que necesitamos modificar es esta.
Tenemos que agregar estas funciones al script, ¿donde? En cualquier lugar, al final, por ejemplo.
//+------------------------------------------------------------------+ void PredictPrice(void) { static vectorf output_data(1); // vector to get result static vectorf x_norm(SAMPLE_SIZE); // vector for prices normalize //--- check for normalization possibility if(ExtMin>=ExtMax) { Print("ExtMin>=ExtMax"); ExtPredictedClass=-1; return; } //--- request last bars if(!x_norm.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,SAMPLE_SIZE)) { Print("CopyRates ",x_norm.Size()); ExtPredictedClass=-1; return; } float last_close=x_norm[SAMPLE_SIZE-1]; //--- normalize prices x_norm-=ExtMin; x_norm/=(ExtMax-ExtMin); //--- run the inference if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,x_norm,output_data)) { Print("OnnxRun"); ExtPredictedClass=-1; return; } //--- denormalize the price from the output value float predicted=output_data[0]*(ExtMax-ExtMin)+ExtMin; //--- classify predicted price movement float delta=last_close-predicted; if(fabs(delta)<=0.00001) ExtPredictedClass=PRICE_SAME; else { if(delta<0) ExtPredictedClass=PRICE_UP; else ExtPredictedClass=PRICE_DOWN; } } void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); }
Estas dos funciones son claves para las predicciones, una hace las predicciones y la otra acerca el mínimo y el máximo.
Modificaciones en SMOC
Todo sigue igual y los cambios son iguales a los anteriores excepto estos:
En los parámetros de entrada no inicialice dlsignal ni agregue CTrade:
#resource "/Files/EURUSD_D1.onnx" as uchar ExtModel[] #define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2
En OnTick(), tendremos esto:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice(); static datetime lastBarTime = 0; datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0); // Only process on new bar if(currentBarTime == lastBarTime) return; lastBarTime = currentBarTime; double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_LAST); int decision = OptimalControl(currentPrice); string logMessage = StringFormat("New bar: Time=%s, Price=%f, Decision=%d", TimeToString(currentBarTime), currentPrice, decision); LogMessage(logMessage); // Manage open order if exists if(orderTicket != 0) { ManageOpenOrder(decision, ExtPredictedClass); } if(orderTicket == 0 && decision != 0 && IsTrendFavorable(decision) && IsLongTermTrendFavorable(decision) && (ExtPredictedClass==PRICE_DOWN || ExtPredictedClass==PRICE_UP)) { ExecuteTrade(decision, ExtPredictedClass); } }
En ManageOpenOrder() haremos este cambio:
void ManageOpenOrder(int decision, int dlsignal) { int barsOpen = iBars(Symbol(), PERIOD_CURRENT) - iBarShift(Symbol(), PERIOD_CURRENT, orderOpenTime); LogMessage(StringFormat("Bars open: %d for order ticket: %d", barsOpen, orderTicket)); if(barsOpen >= maxBarsOpen || (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && decision == -1 && dlsignal == PRICE_DOWN) || (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && decision == 1 && dlsignal == PRICE_UP)) { CloseOrder(orderTicket); orderTicket = 0; orderOpenTime = 0; } else { UpdateSLTP(orderTicket, decision); } }
En ExecuteTrade():
void ExecuteTrade(int decision, int dlsignal) { MqlTradeRequest request = {}; MqlTradeResult result = {}; double price; int decision1; if(decision == 1 && dlsignal == PRICE_UP) { price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); request.type = ORDER_TYPE_BUY; decision1 = 1; } else if(decision == -1 && dlsignal == PRICE_DOWN) { price = SymbolInfoDouble(Symbol(), SYMBOL_BID); request.type = ORDER_TYPE_SELL; decision1 = -1; } double sl = CalculateDynamicSL(price, decision1); double tp = CalculateDynamicTP(price, decision1); double adjustedLotSize = AdjustLotSizeForDrawdown(); request.volume = adjustedLotSize; request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); //request.volume = lotSize; //request.type = (decision == 1 && dlsignal == PRICE_UP) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; request.price = price; request.sl = sl; request.tp = tp; request.deviation = 10; request.magic = 123456; // Magic number to identify orders from this EA request.comment = (decision == 1) ? "Buy Order" : "Sell Order"; if(OrderSend(request, result)) { Print((decision1 == 1 ? "Buy" : "Sell"), " Order Executed Successfully. Ticket: ", result.order); orderOpenTime = iTime(Symbol(), PERIOD_CURRENT, 0); orderTicket = result.order; } else { Print("Error executing Order: ", GetLastError()); } }
Y ahora las modificaciones en el script de acciones de Nash.
Modificaciones en Nash Equity
Todos los cambios son los mismos, excepto la función OnTick(), que será así:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice(); if(!IsNewBar()) return; // Solo procesar en nueva barra MarketRegime hmmRegime = NOT_PRESENT; MarketRegime logLikelihoodRegime = NOT_PRESENT; DetectMarketRegime(hmmRegime, logLikelihoodRegime); // Calcular señales para cada estrategia CalculateStrategySignals(_Symbol, TimeCurrent(), hmmRegime, logLikelihoodRegime); // Verificar si la estrategia de Nash Equilibrium ha generado una señal if(strategies[3].enabled && strategies[3].signal != 0) { if(strategies[3].signal > 0 && ExtPredictedClass==PRICE_UP) { OpenBuyOrder(strategies[3].name); } else if(strategies[3].signal < 0 && ExtPredictedClass==PRICE_DOWN) { OpenSellOrder(strategies[3].name); } } // Actualizar stops de seguimiento si es necesario if(UseTrailingStop) { UpdateTrailingStops(); } }
Resultados
CNA_DL
Ajustes:
En comparación con el EA original (CNA_Final_v4):
Comparación de CNA con y sin aprendizaje profundo
- Rentabilidad: El EA sin aprendizaje profundo es significativamente más rentable, pero también asume más riesgos con mayores caídas.
- Riesgo: El EA de aprendizaje profundo tiene menores contracciones y parece más conservador en su enfoque.
- Frecuencia de negociación: el EA sin aprendizaje profundo realiza operaciones con mucha más frecuencia (1292 frente a 58 operaciones).
- Tasa de victorias: el EA de aprendizaje profundo tiene una tasa de victorias ligeramente mejor, pero ambas están por debajo del 50%.
- Sesgo de estrategia: el EA sin aprendizaje profundo muestra una fuerte preferencia por las operaciones cortas, mientras que el EA de aprendizaje profundo es equilibrado.
- Coherencia: el EA de aprendizaje profundo parece más consistente con series más pequeñas de victorias/derrotas consecutivas.
En conclusión, el EA sin aprendizaje profundo parece ser más rentable pero más riesgoso, mientras que el EA con aprendizaje profundo es más conservador, con retornos más bajos pero también menor riesgo. La elección entre ellos dependerá de la tolerancia al riesgo del comerciante y de sus objetivos de inversión. El EA de aprendizaje profundo puede ser preferible para un trading más estable y conservador, mientras que el EA sin aprendizaje profundo puede elegirse para obtener retornos potencialmente más altos a costa de un mayor riesgo.
SMOC_DL
Y ahora SMOC sin Deep Learning
Comparación SMOC_DL vs SMOC:
- Rentabilidad: La estrategia 2 es rentable (4,32), mientras que la estrategia 1 no es rentable (-7,33).
- Rendimientos ajustados al riesgo: la estrategia 2 tiene un índice de Sharpe positivo (1,69), lo que indica mejores rendimientos ajustados al riesgo en comparación con el índice de Sharpe negativo de la estrategia 1 (-3,77).
- Tasa de victorias: la estrategia 2 tiene una tasa de victorias más alta (59,38 %) en comparación con la estrategia 1 (54 %).
- Número de operaciones: la estrategia 2 ejecuta más operaciones (64) que la estrategia 1 (50), lo que potencialmente indica que se identificaron más oportunidades.
- Factor de beneficio: la estrategia 2 tiene un factor de beneficio superior a 1 (1,13), mientras que la estrategia 1 está por debajo de 1 (0,74), lo que sugiere que la estrategia 2 es más eficaz para generar beneficios en relación con las pérdidas.
- Drawdown: Ambas estrategias tienen drawdowns bajos similares (0,01% de la cuenta), lo que indica una buena gestión del riesgo.
- Distribución comercial: Ambas estrategias tienen preferencia por las operaciones cortas, pero la Estrategia 2 tiene una distribución más equilibrada entre operaciones cortas y largas.
En general, la estrategia 2 parece ser superior en la mayoría de los aspectos, incluida la rentabilidad, la rentabilidad ajustada al riesgo, la tasa de ganancias y el factor de beneficio. También se requieren más transacciones, lo que potencialmente indica una mejor identificación de oportunidades. Ambas estrategias demuestran una buena gestión del riesgo con bajas reducciones de riesgo. Con base en estos datos, la estrategia 2 sería la opción preferida entre las dos. Un enfoque adecuado para utilizar DL sería probar otros marcos temporales.
NASH_DL
Y ahora sin Deep Learning:
En general, la estrategia no DL parece haber obtenido mejores resultados en términos de rentabilidad total y retorno ajustado al riesgo, a pesar de tener una caída máxima mayor. Se negoció de manera más activa y pareció más efectivo con posiciones largas. La estrategia DL fue más conservadora, con menores caídas pero también retornos generales más bajos.
Sin embargo, es importante tener en cuenta que esta comparación se basa en un solo período de prueba retrospectiva. Para sacar conclusiones más sólidas, sería mejor probar estas estrategias en múltiples períodos de tiempo y condiciones de mercado.
Conclusión
Este estudio exploró la integración de modelos de aprendizaje profundo (DL) en tres asesores expertos (EA) comerciales avanzados: análisis de red de causalidad (CNA), optimización estocástica y control óptimo (SMOC) y teoría de juegos de Nash. El proceso implicó crear modelos ONNX utilizando Python e incorporarlos a scripts MQL5 existentes.
Los resultados de esta integración fueron mixtos en las diferentes estrategias:
- Para la estrategia CNA, la versión DL mostró un trading más conservador con menores retornos pero también menor riesgo en comparación con la versión no DL. Si bien fue menos rentable, demostró una mejor consistencia y un enfoque más equilibrado hacia el trading.
- En el caso de SMOC, la versión DL superó significativamente a su contraparte no DL. Mostró una mejor rentabilidad, mayores tasas de ganancia y retornos ajustados al riesgo superiores, lo que sugiere que el modelo DL mejoró efectivamente el proceso de toma de decisiones de la estrategia.
- Según el enfoque de la teoría de juegos de Nash, la versión sin DL tuvo un mejor desempeño en general, con mayor rentabilidad total y retornos ajustados al riesgo, a pesar de tener una caída máxima mayor. La versión DL se negociaba de forma más conservadora pero obtuvo rendimientos generales más bajos.
Estos resultados resaltan el potencial de integrar el aprendizaje profundo en las estrategias comerciales, pero también subrayan la importancia de una implementación y pruebas cuidadosas. La efectividad de la integración de DL varía según la estrategia subyacente y las condiciones del mercado.
Es fundamental tener en cuenta que estos hallazgos se basan en períodos de pruebas retrospectivas y condiciones del mercado específicos. Para sacar conclusiones más sólidas, sería necesario realizar más pruebas en distintos períodos de tiempo y escenarios de mercado.
En conclusión, si bien el aprendizaje profundo puede mejorar las estrategias comerciales, su integración debe abordarse con cautela y con pruebas exhaustivas. Los resultados mixtos en las diferentes estrategias sugieren que la efectividad del DL en el trading no es universal y depende en gran medida de la estrategia específica y del contexto del mercado. Las investigaciones futuras podrían explorar la optimización de estos modelos DL para diferentes marcos temporales y condiciones de mercado para mejorar potencialmente su desempeño en varios escenarios comerciales.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15819





- 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