English Русский Español Deutsch 日本語 Português
preview
因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例

因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例

MetaTrader 5示例 |
171 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

引言

我们将逐步介绍如何将深度学习应用于三个先进的EA。这三个EA是之前作为文章发布的。 

这些文章发布在 www.mql5.com 上

  1. 纳什博弈论与隐马尔可夫滤模型在交易中的应用
  2. 以下是关于因果网络分析(Causality Network Analysis,CNA)和向量自回归(Vector Auto-Regression,VAR)模型在市场事件预测中的应用实例
  3. 随机优化与最优控制示例

如果您还没有阅读过这些文章,我会非常简要地解释一下它们的内容,但我强烈建议您阅读这些文章。



纳什博弈论

纳什均衡被认为是博弈论的基本组成部分之一。 

纳什均衡是博弈论中的一个概念,假设每个参与者都知晓其他参与者的均衡策略,且没有任何一个参与者仅通过改变自身的策略就能获得更多的收益。

在纳什均衡中,每个参与者的策略在给定其他所有参与者策略的情况下都是最优的。一个博弈可能有多个纳什均衡,也可能一个都没有。



因果网络分析

因果网络分析(CNA)是一种用于理解和建模系统中变量之间复杂因果关系的方法。当应用于金融市场时,它可以帮助识别不同的市场事件和因素如何相互影响,从而可能带来更准确的预测。

该机器人使用快速因果推断。

快速因果推断是一种将快速统计推断与因果分析相结合的概念。它旨在快速从数据中得出因果结论,平衡速度和因果理解。这种方法适用于需要快速、数据驱动决策的场景,同时还需要了解因果关系。其应用场景可能包括实时市场分析、快速商业决策以及快速流行病学研究。该方法在分析深度上有所取舍,以提升速度,使其适合于时间敏感的环境,其中理解因果联系至关重要。



随机优化

随机建模和控制优化是数学技术,帮助在不确定性条件下解决问题。它们在金融、工程、人工智能以及许多其他领域都有应用。

随机建模用于描述具有随机元素的系统,例如股票市场的价格波动或餐厅的排队情况。它基于随机变量、概率分布和随机过程。蒙特卡洛和马尔可夫链等方法可以模拟这些过程并预测其行为。

管理优化帮助您找到系统管理的最佳解决方案。它用于自动化和改进各种流程的运行,从驾驶汽车到运营化工厂。基本方法包括线性二次控制器、模型预测控制和强化学习。



深度学习

应用于交易的深度学习使用人工神经网络分析和预测金融市场趋势。以下是简要概述:

  1. 数据分析:深度学习模型处理大量金融数据,包括价格历史、交易量、经济指标,甚至新闻情绪。
  2. 模式识别:这些模型可以识别市场数据中复杂的模式和关系,这些模式和关系可能对人类交易者或传统分析方法并不明显。
  3. 预测:基于识别出的模式,深度学习模型尝试预测未来的市场走势、价格趋势或最佳交易策略。
  4. 自动化交易:一些系统使用深度学习进行自主交易决策,根据模型的预测执行交易。
  5. 风险管理:深度学习可以通过同时分析多个风险因素,更有效地评估和管理交易风险。
  6. 适应性:这些模型可以持续学习并适应不断变化的市场条件,随着时间的推移可能提高其性能。
  7. 高频交易:深度学习在高频交易场景中特别有用,其中毫秒级的决策至关重要。

虽然深度学习在交易中提供了强大的能力,但需要注意的是,市场是复杂且不可预测的。这些模型并非万无一失,仍然需要谨慎的监督和风险管理。


为什么我们需要一个带有深度学习的 EA?

我们将使用 Python 创建一个深度学习模型,并生成一个 ONNX 模型,然后将该模型添加到每个 EA 中,并比较有无深度学习的结果。

我们将使用 ONNX 模型,因为它们可以在 Python 中轻松创建,并且是不同生态系统之间的桥梁,促进了模型开发和部署的灵活性和效率。Python 是创建 ONNX 模型或快速回测策略的快速方式。对于本文,我们将使用 Python 3.11.9。

我们将在这几个 EA 中使用深度学习,因为之前文章中的结果还可以改进,我们将为 EA 添加条件,只有当深度学习模型也同意时,EA 才执行交易。



 Python 脚本

我们将使用这个 .py 脚本,该脚本在 ONNX 中创建一个深度学习模型,并且包含用于检查模型是否正确完成的指标。深度学习在执行不当时会存在问题,因为它可能会欠拟合或过拟合,从而导致错误的预测。

#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') 


这个 .py 脚本的输出看起来类似于这样:

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
________________________________________________________________

这个模型可能过拟合了,根据这些结果来看,这可能处于良好预测与过拟合的临界点。

RMSE         : 0.005155711558172936
MSE          : 2.6581361671078003e-05
R2 score     : 0.9915315181564703

主要指标是这三个:R²:给出了测试结果中良好预测的百分比。RMSE(均方根误差) 和 MSE(均方误差):这些是误差指标。如果您想了解更多关于这些指标的信息,可以在网上搜索。

由于我们将对同一货币对(EURUSD)进行操作,因此我们只需要创建一个深度学习模型,并将在 2024 年对其进行测试。

我们选择了 1 天的时间周期,以避免过度消耗计算机资源。

该 Python 脚本还会生成一些图表并将其保存(这是另一种判断模型是否过拟合或欠拟合的方法)。

图表应该看起来像这样:

误差 图1 图2 训练 测试

该脚本还会保存一个 .txt 文件,其中按终端显示的顺序保存了前面提到的指标。

要使用此脚本,您必须首先安装 Python 3.11.9,并使用以下命令安装这些库:pip install:

tensorflow==2.12.0, keras==2.12.0,  tf2onnx==1.16.0, MetaTrader5, pandas, numpy, scikit-learn, tf2onnx, keras

我强烈推荐使用 Visual Studio Code,您可以从微软商店下载。

它可能会要求您安装

Microsoft Visual C++ 2015 - 2022 Redistributable

我已经附上了 .py 脚本,只需在 VSC 中打开并运行它。

如果您想更改任何内容(例如其他货币对、获取数据的时间范围或时间周期),只需更改以下几行代码:

symbol = "EURUSD"
optional = "D1_2024"

货币对的含义是显而易见的。我建议将 optional 改为您使用的数据的时间范围和数据的最后日期。

eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)

在这行代码中,您可以更改时间框架,例如 H1(1小时时间框架)。

现在我们已经拥有了深度学习的 ONNX 模型,可以将其添加到之前文章中提到的 EA 中。

我将附上 DL ONNX 模型(2024)。


我们将为 EA 添加什么内容?

CNA 修改

这部分非常基础,我会非常详细地逐步介绍。

首先,在输入参数的部分,我们需要添加以下几行代码:

#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

请记得将此行代码中的 ONNX 模型路径修改为您生成的模型路径(Python 脚本会将模型保存在 MQL5/Files/... 中)。

#resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]

在OnInit()函数中你需要添加如下代码:

//--- 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);
     }

在哪里?任何地方都可以......这无关紧要(只要是在函数返回前)。

在OnDeinit()函数中你也必须添加如下代码:

if(ExtHandle!=INVALID_HANDLE)
     {
      OnnxRelease(ExtHandle);
      ExtHandle=INVALID_HANDLE;
     }

在哪里?任何地方都可以,这用于释放ONNX模型。

在OnTick()函数中你必须添加如下代码:

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();

在哪里?在一开始,这一点非常重要,因为函数 PredictPrice() 的结果必须添加到执行订单的逻辑中。

在 OnTick() 函数中,您还必须修改生成订单的函数。在这种情况下(CNA_DL),我们将修改原始的代码行:

int signal = GenerateSignal(symbol, prediction);

这行:

int signal = GenerateSignal(symbol, prediction, ExtPredictedClass);

ExtPredictedClass 是我们之前添加的 PredictedPrice() 函数的结果。

现在,我们还需要修改 GenerateSignal() 函数的逻辑,使其使用 ExtPredictedClass:

我们需要更改该函数的声明:

int GenerateSignal(string symbol, double prediction, int dlsgnl)

并在此处将新变量添加到生成订单的逻辑中:

从:

       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;

    到:

         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;

      最后需要修改的部分是这个。

      我们需要将此函数添加到脚本中。添加到哪里?任何地方,例如在最后。

      //+------------------------------------------------------------------+
      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();
        }

      这两个函数对于预测至关重要,一个用于生成预测,另一个用于获取最低和最高收盘价。


      SMOC 的修改

      其他部分保持不变,修改内容与之前相同,只有以下几点不同:

      在输入参数中,您不需要初始化 dlsignal 或添加 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

      在OnTick()中,我们将得到:

      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);
           }
        }

      在 ManageOpenOrder() 函数中,我们将进行以下修改:

      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);
           }
        }

      在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());
           }
        }

      现在我们来修改纳什均衡脚本。


      纳什均衡的修改

      所有修改内容都与之前相同,只有 OnTick() 函数有所不同,它将如下所示:

      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();
           }
        }



      结果
      CNA_DL

      设置

      设置CNA_DL

      CNA_DL 图形

      CNA_dl 回测

      同原始EA(CNA_Final_v4)比较:

      CNA_Final_v4 图表

      CNA_Final_v4 回测


      带深度学习和不带深度学习的因果网络分析(CNA)比较

      1. 盈利能力:不使用深度学习的EA的盈利能力显著更高,但同时也承担了更高的风险,表现为更大的回撤。
      2. 风险:使用深度学习的 EA 回撤较小,其交易方式似乎更为保守。
      3. 交易频率:不使用深度学习的 EA 交易频率更高(1292 次交易对比 58 次)。
      4. 胜率:使用深度学习的 EA 胜率略高,但两者都低于 50%。
      5. 策略倾向:不使用深度学习的 EA 更倾向于做空交易,而使用深度学习的 EA 则更为平衡。
      6. 一致性:使用深度学习的 EA 似乎更具有一致性,连续盈利或亏损的次数更少。

      总结:不使用深度学习的 EA 似乎更具盈利能力,但风险也更高;而使用深度学习的 EA 更为保守,回报较低,但风险也较低。选择哪一种取决于交易者的风险承受能力和投资目标。如果倾向于更稳定、保守的交易方式,可能会更倾向于选择使用深度学习的 EA;而不使用深度学习的 EA 可能更适合那些愿意承担更高风险以换取潜在更高回报的交易者。

      SMOC_DL

      设置SMOC_DL

      SMOC_DL图表

      SMOC_DL 回测


      不带深度学习的SMOC

      SMOC 回测

      SMOC 图表

      对比SMOC_DL和SMOC:

      1. 盈利能力:策略 2 是盈利的(4.32),而策略 1 是亏损的(-7.33)。
      2. 风险调整后的回报:策略 2 的夏普比率(1.69)为正数,表明其风险调整后的回报优于策略 1 的负夏普比率(-3.77)。
      3. 胜率:策略 2 的胜率(59.38%)高于策略 1(54%)。
      4. 交易次数:策略 2 执行的交易次数(64 次)多于策略 1(50 次),这可能表明策略 2 识别出了更多的交易机会。
      5. 盈利因子:策略 2 的盈利因子高于 1(1.13),而策略 1 低于 1(0.74),这表明策略 2 在生成利润方面相对亏损更为有效。
      6. 回撤:两种策略的回撤都很低(账户的 0.01%),表明风险管理良好。
      7. 交易分布:两种策略都倾向于做空交易,但策略 2 在做空和做多交易之间的分布更为平衡。

      总体而言,策略 2 在大多数方面都表现更优,包括盈利能力、风险调整后的回报、胜率和盈利因子。它还执行了更多的交易,这可能表明它识别出了更多的交易机会。两种策略都表现出良好的风险管理,回撤较低。根据这些数据,策略 2 将是两者之间的首选。尝试其他时间框架是使用深度学习的一个合适方法。

      NASH_DL

      纳什均衡设置

      纳什均衡深度学习

      Nash DL 回测

      现在我们来看看不使用深度学习的情况:

      Nash

      Nash 回测

      总体来看,非深度学习策略在总盈利和风险调整回报率方面表现更优,尽管其最大回撤较高。该策略交易更为活跃,在多头头寸管理上似乎更有效率。深度学习策略相对保守,回撤较小但整体收益也较低。

      但需注意,这一比较仅基于单一回测周期。要得出更具说服力的结论,应在不同时间段和市场环境下测试这些策略。


      结论

      本研究探讨了将深度学习(DL)模型整合至三种先进EA的实践:因果网络分析(CNA)、随机优化与最优控制(SMOC)以及纳什博弈论策略。实施过程包括使用Python创建ONNX模型,并将其整合至现有MQL5脚本。

      整合效果在不同策略中呈现差异化表现:

      1. 在CNA策略中,DL版本展现出更保守的交易风格,收益降低但风险同步缩减。尽管盈利能力减弱,但其交易一致性更佳,展现出更均衡的交易决策。
      2. 对于SMOC策略,DL版本显著超越非DL版本。其盈利能力更优,胜率更高,风险调整后回报更突出,表明DL模型有效提升了策略的决策质量。
      3. 在纳什博弈论策略中,非DL版本整体表现更佳,总盈利和风险调整回报更高,尽管承受了更大的最大回撤。DL版本交易更为保守,但整体收益水平较低。

      这些结果凸显了深度学习整合于交易策略的潜力,同时也警示需谨慎实施与验证。DL的有效性因底层策略与市场环境而异。

      必须强调,本研究发现基于特定回测周期与市场条件。为获得更具普适性的结论,需在不同时间框架与市场情境下进行扩展测试。

      综上所述,尽管深度学习能增强交易策略,但其整合过程需谨慎对待并充分验证。不同策略间的差异化表现表明,DL在交易中的有效性并非普适,其效能高度依赖具体策略与市场环境。未来研究可探索针对不同时间框架与市场条件优化DL模型,以期提升其在各类交易场景中的表现。

      本文由MetaQuotes Ltd译自英文
      原文地址: https://www.mql5.com/en/articles/15819

      附加的文件 |
      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)
      将 MQL5 与数据处理包集成 (第 2 部分):机器学习和预测分析 将 MQL5 与数据处理包集成 (第 2 部分):机器学习和预测分析
      在我们关于将 MQL5 与数据处理包集成的系列文章中,我们深入研究了机器学习和预测分析的强大组合。我们将探索如何将 MQL5 与流行的机器学习库无缝连接,以便为金融市场提供复杂的预测模型。
      您应当知道的 MQL5 向导技术(第 31 部分):选择损失函数 您应当知道的 MQL5 向导技术(第 31 部分):选择损失函数
      损失函数是机器学习算法的关键量值,即量化给定参数集相比预期目标的性能来为训练过程提供反馈。我们在 MQL5 自定义向导类中探索该函数的各种格式。
      如何在MQL5的EA中实现自优化 如何在MQL5的EA中实现自优化
      MQL5中EA自优化的分步指南。我们将涵盖稳健的优化逻辑、参数选择的最佳实践,以及如何通过回测重构策略。此外,还将讨论诸如分步优化等高级方法,以增强您的交易方法。
      从基础到中级:IF ELSE 从基础到中级:IF ELSE
      在本文中,我们将讨论如何使用 IF 操作符及其伴随者 ELSE。这个语句是所有编程语言中最为重要且最有意义的语句。然而,尽管它易于使用,但如果我们没有使用它的经验以及与之相关的概念,它有时会令人困惑。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。