
因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例
引言
我们将逐步介绍如何将深度学习应用于三个先进的EA。这三个EA是之前作为文章发布的。
这些文章发布在 www.mql5.com 上
- 纳什博弈论与隐马尔可夫滤模型在交易中的应用
- 以下是关于因果网络分析(Causality Network Analysis,CNA)和向量自回归(Vector Auto-Regression,VAR)模型在市场事件预测中的应用实例
- 随机优化与最优控制示例
如果您还没有阅读过这些文章,我会非常简要地解释一下它们的内容,但我强烈建议您阅读这些文章。
纳什博弈论
纳什均衡被认为是博弈论的基本组成部分之一。
纳什均衡是博弈论中的一个概念,假设每个参与者都知晓其他参与者的均衡策略,且没有任何一个参与者仅通过改变自身的策略就能获得更多的收益。
在纳什均衡中,每个参与者的策略在给定其他所有参与者策略的情况下都是最优的。一个博弈可能有多个纳什均衡,也可能一个都没有。
因果网络分析
因果网络分析(CNA)是一种用于理解和建模系统中变量之间复杂因果关系的方法。当应用于金融市场时,它可以帮助识别不同的市场事件和因素如何相互影响,从而可能带来更准确的预测。
该机器人使用快速因果推断。
快速因果推断是一种将快速统计推断与因果分析相结合的概念。它旨在快速从数据中得出因果结论,平衡速度和因果理解。这种方法适用于需要快速、数据驱动决策的场景,同时还需要了解因果关系。其应用场景可能包括实时市场分析、快速商业决策以及快速流行病学研究。该方法在分析深度上有所取舍,以提升速度,使其适合于时间敏感的环境,其中理解因果联系至关重要。
随机优化
随机建模和控制优化是数学技术,帮助在不确定性条件下解决问题。它们在金融、工程、人工智能以及许多其他领域都有应用。
随机建模用于描述具有随机元素的系统,例如股票市场的价格波动或餐厅的排队情况。它基于随机变量、概率分布和随机过程。蒙特卡洛和马尔可夫链等方法可以模拟这些过程并预测其行为。
管理优化帮助您找到系统管理的最佳解决方案。它用于自动化和改进各种流程的运行,从驾驶汽车到运营化工厂。基本方法包括线性二次控制器、模型预测控制和强化学习。
深度学习
应用于交易的深度学习使用人工神经网络分析和预测金融市场趋势。以下是简要概述:
- 数据分析:深度学习模型处理大量金融数据,包括价格历史、交易量、经济指标,甚至新闻情绪。
- 模式识别:这些模型可以识别市场数据中复杂的模式和关系,这些模式和关系可能对人类交易者或传统分析方法并不明显。
- 预测:基于识别出的模式,深度学习模型尝试预测未来的市场走势、价格趋势或最佳交易策略。
- 自动化交易:一些系统使用深度学习进行自主交易决策,根据模型的预测执行交易。
- 风险管理:深度学习可以通过同时分析多个风险因素,更有效地评估和管理交易风险。
- 适应性:这些模型可以持续学习并适应不断变化的市场条件,随着时间的推移可能提高其性能。
- 高频交易:深度学习在高频交易场景中特别有用,其中毫秒级的决策至关重要。
虽然深度学习在交易中提供了强大的能力,但需要注意的是,市场是复杂且不可预测的。这些模型并非万无一失,仍然需要谨慎的监督和风险管理。
为什么我们需要一个带有深度学习的 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 脚本还会生成一些图表并将其保存(这是另一种判断模型是否过拟合或欠拟合的方法)。
图表应该看起来像这样:
该脚本还会保存一个 .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
设置
同原始EA(CNA_Final_v4)比较:
带深度学习和不带深度学习的因果网络分析(CNA)比较
- 盈利能力:不使用深度学习的EA的盈利能力显著更高,但同时也承担了更高的风险,表现为更大的回撤。
- 风险:使用深度学习的 EA 回撤较小,其交易方式似乎更为保守。
- 交易频率:不使用深度学习的 EA 交易频率更高(1292 次交易对比 58 次)。
- 胜率:使用深度学习的 EA 胜率略高,但两者都低于 50%。
- 策略倾向:不使用深度学习的 EA 更倾向于做空交易,而使用深度学习的 EA 则更为平衡。
- 一致性:使用深度学习的 EA 似乎更具有一致性,连续盈利或亏损的次数更少。
总结:不使用深度学习的 EA 似乎更具盈利能力,但风险也更高;而使用深度学习的 EA 更为保守,回报较低,但风险也较低。选择哪一种取决于交易者的风险承受能力和投资目标。如果倾向于更稳定、保守的交易方式,可能会更倾向于选择使用深度学习的 EA;而不使用深度学习的 EA 可能更适合那些愿意承担更高风险以换取潜在更高回报的交易者。
SMOC_DL
不带深度学习的SMOC
对比SMOC_DL和SMOC:
- 盈利能力:策略 2 是盈利的(4.32),而策略 1 是亏损的(-7.33)。
- 风险调整后的回报:策略 2 的夏普比率(1.69)为正数,表明其风险调整后的回报优于策略 1 的负夏普比率(-3.77)。
- 胜率:策略 2 的胜率(59.38%)高于策略 1(54%)。
- 交易次数:策略 2 执行的交易次数(64 次)多于策略 1(50 次),这可能表明策略 2 识别出了更多的交易机会。
- 盈利因子:策略 2 的盈利因子高于 1(1.13),而策略 1 低于 1(0.74),这表明策略 2 在生成利润方面相对亏损更为有效。
- 回撤:两种策略的回撤都很低(账户的 0.01%),表明风险管理良好。
- 交易分布:两种策略都倾向于做空交易,但策略 2 在做空和做多交易之间的分布更为平衡。
总体而言,策略 2 在大多数方面都表现更优,包括盈利能力、风险调整后的回报、胜率和盈利因子。它还执行了更多的交易,这可能表明它识别出了更多的交易机会。两种策略都表现出良好的风险管理,回撤较低。根据这些数据,策略 2 将是两者之间的首选。尝试其他时间框架是使用深度学习的一个合适方法。
NASH_DL
现在我们来看看不使用深度学习的情况:
总体来看,非深度学习策略在总盈利和风险调整回报率方面表现更优,尽管其最大回撤较高。该策略交易更为活跃,在多头头寸管理上似乎更有效率。深度学习策略相对保守,回撤较小但整体收益也较低。
但需注意,这一比较仅基于单一回测周期。要得出更具说服力的结论,应在不同时间段和市场环境下测试这些策略。
结论
本研究探讨了将深度学习(DL)模型整合至三种先进EA的实践:因果网络分析(CNA)、随机优化与最优控制(SMOC)以及纳什博弈论策略。实施过程包括使用Python创建ONNX模型,并将其整合至现有MQL5脚本。
整合效果在不同策略中呈现差异化表现:
- 在CNA策略中,DL版本展现出更保守的交易风格,收益降低但风险同步缩减。尽管盈利能力减弱,但其交易一致性更佳,展现出更均衡的交易决策。
- 对于SMOC策略,DL版本显著超越非DL版本。其盈利能力更优,胜率更高,风险调整后回报更突出,表明DL模型有效提升了策略的决策质量。
- 在纳什博弈论策略中,非DL版本整体表现更佳,总盈利和风险调整回报更高,尽管承受了更大的最大回撤。DL版本交易更为保守,但整体收益水平较低。
这些结果凸显了深度学习整合于交易策略的潜力,同时也警示需谨慎实施与验证。DL的有效性因底层策略与市场环境而异。
必须强调,本研究发现基于特定回测周期与市场条件。为获得更具普适性的结论,需在不同时间框架与市场情境下进行扩展测试。
综上所述,尽管深度学习能增强交易策略,但其整合过程需谨慎对待并充分验证。不同策略间的差异化表现表明,DL在交易中的有效性并非普适,其效能高度依赖具体策略与市场环境。未来研究可探索针对不同时间框架与市场条件优化DL模型,以期提升其在各类交易场景中的表现。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15819
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。


