English Русский Deutsch 日本語
preview
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)

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)

MetaTrader 5Ejemplos | 29 abril 2025, 08:02
136 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

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

  1. Aplicación de la Teoría de Juegos de Nash con filtrado HMM en el trading
  2. 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
  3. 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:

  1. 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.
  2. 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.
  3. 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.
  4. Comercio automatizado: algunos sistemas utilizan aprendizaje profundo para tomar decisiones comerciales autónomas y ejecutar operaciones según las predicciones del modelo.
  5. 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.
  6. Adaptabilidad: Estos modelos pueden aprender continuamente y adaptarse a las condiciones cambiantes del mercado, mejorando potencialmente su desempeño con el tiempo.
  7. 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í:

Errores Gráfico 1 Gráfico 2 Entrenamiento Pruebas

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:

      Configuración CNA_DL

      Gráfico CNA_DL

      Prueba retrospectiva de CNA_dl

      En comparación con el EA original (CNA_Final_v4):

      Gráfico CNA_Final_v4

      Pruebas retrospectivas de CNA_Final_v4


      Comparación de CNA con y sin aprendizaje profundo

      1. Rentabilidad: El EA sin aprendizaje profundo es significativamente más rentable, pero también asume más riesgos con mayores caídas.
      2. Riesgo: El EA de aprendizaje profundo tiene menores contracciones y parece más conservador en su enfoque.
      3. Frecuencia de negociación: el EA sin aprendizaje profundo realiza operaciones con mucha más frecuencia (1292 frente a 58 operaciones).
      4. Tasa de victorias: el EA de aprendizaje profundo tiene una tasa de victorias ligeramente mejor, pero ambas están por debajo del 50%.
      5. 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.
      6. 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

      Configuración SMOC_DL

      Gráfico SMOC_DL

      Prueba retrospectiva de SMOC_DL

      Y ahora SMOC sin Deep Learning

      Prueba retrospectiva de SMOC

      Gráfico SMOC


      Comparación SMOC_DL vs SMOC:

      1. Rentabilidad: La estrategia 2 es rentable (4,32), mientras que la estrategia 1 no es rentable (-7,33).
      2. 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).
      3. Tasa de victorias: la estrategia 2 tiene una tasa de victorias más alta (59,38 %) en comparación con la estrategia 1 (54 %).
      4. 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.
      5. 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.
      6. Drawdown: Ambas estrategias tienen drawdowns bajos similares (0,01% de la cuenta), lo que indica una buena gestión del riesgo.
      7. 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

      Configuración de Nash

      Aprendizaje profundo de Nash

      Prueba retrospectiva de Nash DL

      Y ahora sin Deep Learning:

      Nash

      Pruebas retrospectivas de Nash

      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:

      1. 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.
      2. 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.
      3. 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

      Archivos adjuntos |
      EURUSD_D1_2024.onnx (884.41 KB)
      CNA_DL.mq5 (199.34 KB)
      CNA_Final_v4.mq5 (179.13 KB)
      SMOC_DL.mq5 (49.01 KB)
      SMOC_final.mq5 (40.29 KB)
      NASH_v4.mq5 (125.94 KB)
      NASH_v4_DL.mq5 (136.65 KB)
      Cómo implementar la optimización automática en los asesores expertos de MQL5 Cómo implementar la optimización automática en los asesores expertos de MQL5
      Guía paso a paso para la optimización automática en MQL5 para Asesores Expertos. Cubriremos la lógica de optimización robusta, las mejores prácticas para la selección de parámetros y cómo reconstruir estrategias con pruebas retrospectivas. Además, se discutirán métodos de nivel superior, como la optimización del avance, para mejorar su enfoque comercial.
      Características del Wizard MQL5 que debe conocer (Parte 38): Bandas de Bollinger Características del Wizard MQL5 que debe conocer (Parte 38): Bandas de Bollinger
      Las bandas de Bollinger son un indicador de envolvente muy común utilizado por muchos traders para colocar y cerrar operaciones manualmente. Examinamos este indicador considerando las diferentes señales posibles que genera, y vemos cómo se podrían poner en uso en un Asesor Experto montado por un asistente.
      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.
      Creación de un asesor experto integrado de MQL5 y Telegram (Parte 6): Añadir botones interactivos en línea Creación de un asesor experto integrado de MQL5 y Telegram (Parte 6): Añadir botones interactivos en línea
      En este artículo, integramos botones interactivos en línea en un Asesor Experto MQL5, permitiendo el control en tiempo real a través de Telegram. Cada pulsación de botón desencadena acciones específicas y envía respuestas al usuario. También modularizamos las funciones para manejar los mensajes de Telegram y las consultas de devolución de llamada de forma eficiente.