因果网络分析(CNA)、随机模型最优控制(SMOC)和纳什博弈论结合深度学习的示例·进阶篇
(2/3)· 当因果网络、随机最优控制与博弈均衡遇上神经网络,原有EA的边界在哪里、怎么接
不少交易者把三套数学框架堆进EA后就以为模型自动变聪明,实际只是静态规则叠加。没接深度学习的CNA和纳什均衡,遇到行情结构切换依旧钝化。本篇接着基础篇,把改造缺口摊开讲。
给 EA 加深度学习的真实动机
之前几篇里纯规则 EA 的回测结果还有提升空间,所以这一轮直接把深度学习模型接进交易决策链。思路不复杂:只有当模型也给出同向信号时,EA 才允许下单,相当于给原有条件加了一道概率过滤。 具体落地用 ONNX 做桥梁。Python 3.11.9 负责训模型和快速回测,导出的 ONNX 能直接塞进 MT5 的 EA 里跑,不用把整套训练环境搬进终端,开发和部署两边都不卡。 我们会做一组对照:同一套 EA 逻辑,一个挂 ONNX 模型、一个不挂,跑完比两者的成交质量和回撤。你能立刻在 MT5 里复现这套对照,看模型同意机制到底削掉了多少噪音单。
「用 Python 把 EURUSD 日线训成 ONNX 模型」
做深度学习预测最怕模型没训明白:欠拟合学不到规律,过拟合则把噪声当信号,两者都会让后续 EA 的预测偏向错误。下面这套 .py 脚本在 ONNX 里建 LSTM 模型,顺带把 R²、RMSE、MSE 三个指标落盘,用来判断模型是能预测还是卡在过拟合边缘。 脚本默认抓 EURUSD 的 D1 数据,回测窗口截止到 2024-01-01,往前取 inp_history_size*20 天(代码里 history_size=120,即 2400 天)以避免吃光算力。训练集占 80%,其余 20% 留作测试,图表和 .txt 指标文件会按 MT5 终端路径存到 MQL5\Files。 环境得是 Python 3.11.9,装 tensorflow==2.12.0、keras==2.12.0、tf2onnx==1.16.0,用 VSC 跑最省事。想换品种就改 symbol,改周期动 TIMEFRAME_D1 那行(比如 H1),改数据范围调 optional 和 end_date 即可。外汇与贵金属自带高杠杆风险,模型输出只是概率倾向,别当确定性信号。 代码逐行拆解:前几行导入 MT5 与深度学习库并打印版本;inp_history_size=120 定义回溯步数,sample_size 算出总样本量;mt5.initialize() 连终端,失败即退出。data_path 和 file_path 分别定位脚本旁与终端 Files 目录用于存模型。start_date 按 120*20 天倒推,copy_rates_from 取日线,MinMaxScaler 把收盘价压到 0~1。split_sequence 按时间窗切样本,x_train reshape 成 [样本, 步数, 1] 喂 LSTM,最后 Sequential() 搭空模型待编译。
class="macro">#python <span class="number">class="num">3.11</span>.<span class="number">class="num">9</span>, tensorflow==<span class="number">class="num">2.12</span>.<span class="number">class="num">0</span>, keras==<span class="number">class="num">2.12</span>.<span class="number">class="num">0</span>, tf2onnx==<span class="number">class="num">1.16</span>.<span class="number">class="num">0</span> # python libraries class="kw">import MetaTrader5 <span class="keyword">as</span> mt5 class="kw">import tensorflow <span class="keyword">as</span> tf class="kw">import numpy <span class="keyword">as</span> np class="kw">import pandas <span class="keyword">as</span> pd class="kw">import tf2onnx class="macro">#class="kw">import tensorflow <span class="keyword">as</span> tf class="macro">#class="kw">import tf2onnx class="kw">import keras print(f"TensorFlow <span class="macro">version</span>: {tf.__version__}") print(f"Keras <span class="macro">version</span>: {keras.__version__}") print(f"tf2onnx <span class="macro">version</span>: {tf2onnx.__version__}") # <span class="keyword">input</span> parameters inp_history_size = <span class="number">class="num">120</span> sample_size = inp_history_size*<span class="number">class="num">3</span>*<span class="number">class="num">20</span> symbol = "EURUSD" optional = "D1_2024" inp_model_name = str(symbol)+"_"+str(optional)+".onnx" <span class="keyword">if</span> not mt5.initialize(): print("initialize() failed, error code =",mt5.last_error()) quit() # we will save generated onnx-file near the our script to use <span class="keyword">as</span> resource from sys class="kw">import argv data_path=argv[<span class="number">class="num">0</span>] last_index=data_path.rfind("\\")+<span class="number">class="num">1</span> data_path=data_path[<span class="number">class="num">0</span>:last_index] print("data path to save onnx model",data_path) # and save to MQL5\Files folder to use <span class="keyword">as</span> 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 <span class="keyword">for</span> history data from <span class="keyword">class="type">class="kw">datetime</span> class="kw">import timedelta, <span class="keyword">class="type">class="kw">datetime</span> class="macro">#end_date = <span class="keyword">class="type">class="kw">datetime</span>.now() end_date = <span class="keyword">class="type">class="kw">datetime</span>(<span class="number">class="num">2024</span>, <span class="number">class="num">1</span>, <span class="number">class="num">1</span>, <span class="number">class="num">0</span>) start_date = end_date - timedelta(days=inp_history_size*<span class="number">class="num">20</span>) # 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([&class="macro">#x27;close&class="macro">#x27;]).values # scale data from sklearn.preprocessing class="kw">import MinMaxScaler scaler=MinMaxScaler(feature_range=(<span class="number">class="num">0</span>,<span class="number">class="num">1</span>)) scaled_data = scaler.fit_transform(data) # training size is <span class="number">class="num">80</span>% of the data training_size = <span class="keyword">class="type">int</span>(len(scaled_data)*<span class="number">class="num">0.80</span>) print("Training_size:",training_size) train_data_initial = scaled_data[<span class="number">class="num">0</span>:training_size,:] test_data_initial = scaled_data[training_size:,:<span class="number">class="num">1</span>] # split a univariate sequence into samples def split_sequence(sequence, n_steps): X, y = list(), list() <span class="keyword">for</span> i in range(len(sequence)): # find the end of <span class="keyword">this</span> pattern end_ix = i + n_steps # check <span class="keyword">if</span> we are beyond the sequence <span class="keyword">if</span> end_ix > len(sequence)-<span class="number">class="num">1</span>: <span class="keyword">class="kw">break</span> # gather <span class="keyword">input</span> 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) <span class="keyword">class="kw">return</span> 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 <span class="keyword">input</span> to be [samples, time steps, features] which is required <span class="keyword">for</span> LSTM x_train =x_train.reshape(x_train.shape[<span class="number">class="num">0</span>],x_train.shape[<span class="number">class="num">1</span>],<span class="number">class="num">1</span>) x_test = x_test.reshape(x_test.shape[<span class="number">class="num">0</span>],x_test.shape[<span class="number">class="num">1</span>],<span class="number">class="num">1</span>) # define model from keras.models class="kw">import Sequential from keras.layers class="kw">import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM from keras.metrics class="kw">import RootMeanSquaredError <span class="keyword">as</span> rmse from tensorflow.keras class="kw">import callbacks model = Sequential()
◍ 把 Keras 时序模型压成 ONNX 并回测误差
这段流水线先搭了一个 CNN+LSTM 混合网络:首层 Conv1D 用 256 个滤波器、核宽 2,接 MaxPooling1D 池化窗口 2,再叠两层各 100 单元的 LSTM(中间 Dropout 0.3),末端 Dense(1)+sigmoid 做回归输出,编译用 adam 优化、mse 损失并自定义 rmse 指标。训练时设了 EarlyStopping,monitor='val_loss'、patience=20、restore_best_weights=True,最长跑 300 个 epoch、batch_size=32,实际可能在 val_loss 连续 20 轮不降后就停。 模型训完先转 ONNX:tf2onnx.convert.from_function 用 input_signature 锁死 [None, inp_history_size, 1] 的 float32 输入、opset=13,落盘到 data_path+inp_model_name;后面又调了两次 from_keras 往 file_path 再存一份,属于冗余写法,MT5 只认其中一个路径,建议删掉重复转换。 评估阶段把 y_test、test_predict 用 scaler.inverse_transform 还原到原始价格刻度,再算 RMSE、MSE、R2。代码里顺手打印了预测样本数换算成分钟/小时/天(按每根 K 线 1 小时算:len*60*24 分钟),并 scatter 画了 error 分布、存 PNG 和 results.txt。外汇与贵金属价格序列噪声大,R2 若低于 0.3 则模型倾向过拟合,实盘前务必用样本外数据重跑这套评估。 末了 model.summary() 看参数量、mt5.shutdown() 断连接。想验证的话,直接把 inp_history_size 改成你 MT5 导出的小时线窗口长度,跑通后看 results.txt 里的 RMSE 数字,大概率能判断这套网络值不值得上实盘。
model.add(Conv1D(filters=class="num">256, kernel_size=class="num">2, strides=class="num">1, padding=&class="macro">#x27;same&class="macro">#x27;, activation=&class="macro">#x27;relu&class="macro">#x27;, input_shape=(inp_history_size,class="num">1))) model.add(MaxPooling1D(pool_size=class="num">2)) model.add(LSTM(class="num">100, return_sequences = True)) model.add(Dropout(class="num">0.3)) model.add(LSTM(class="num">100, return_sequences = False)) model.add(Dropout(class="num">0.3)) model.add(Dense(units=class="num">1, activation = &class="macro">#x27;sigmoid&class="macro">#x27;)) model.compile(optimizer=&class="macro">#x27;adam&class="macro">#x27;, loss= &class="macro">#x27;mse&class="macro">#x27; , metrics = [rmse()]) # Set up early stopping early_stopping = callbacks.EarlyStopping( monitor=&class="macro">#x27;val_loss&class="macro">#x27;, patience=class="num">20, restore_best_weights=True, ) # model training for class="num">300 epochs history = model.fit(x_train, y_train, epochs = class="num">300 , validation_data = (x_test,y_test), batch_size=class="num">32, callbacks=[early_stopping], verbose=class="num">2) # evaluate training data train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = class="num">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 = class="num">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, class="num">1], tf.float32)]) def model_function(x): class="kw">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, class="num">1], tf.float32)], opset=class="num">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}") class="macro">#prediction class="kw">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(-class="num">1, class="num">1) # Selecciona solo el ultimo elemento de cada muestra de prueba plot_y_train = y_train.reshape(-class="num">1,class="num">1) train_predict = model.predict(x_train) class="macro">#print(plot_y_test) class="macro">#calculate metrics from sklearn class="kw">import metrics from sklearn.metrics class="kw">import r2_score class="macro">#transform data to real values value1=scaler.inverse_transform(plot_y_test) class="macro">#print(value1) # Escala las predicciones inversas al transformarlas a la escala original value2 = scaler.inverse_transform(test_predict.reshape(-class="num">1, class="num">1)) class="macro">#print(value2) class="macro">#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)) class="macro">#sumarize model model.summary() class="macro">#Print error value11=pd.DataFrame(value1) value22=pd.DataFrame(value2) class="macro">#print(value11) class="macro">#print(value22) value111=value11.iloc[:,:] value222=value22.iloc[:,:] print("longitud salida(tandas de class="num">1 hora): ",len(value111) ) print("en horas son " + str((len(value111))*class="num">60*class="num">24)+ " minutos") print("en horas son " + str(((len(value111)))*class="num">60*class="num">24/class="num">60)+ " horas") print("en horas son " + str(((len(value111)))*class="num">60*class="num">24/class="num">60/class="num">24)+ " dias") # Calculate error error = value111 - value222 class="kw">import matplotlib.pyplot as plt # Plot error plt.figure(figsize=(class="num">7, class="num">6)) plt.scatter(range(len(error)), error, class="type">class="kw">color=&class="macro">#x27;blue&class="macro">#x27;, label=&class="macro">#x27;Error&class="macro">#x27;) plt.axhline(y=class="num">0, class="type">class="kw">color=&class="macro">#x27;red&class="macro">#x27;, linestyle=&class="macro">#x27;--&class="macro">#x27;, linewidth=class="num">1) # Linea horizontal en y=class="num">0 plt.title(&class="macro">#x27;Error de Prediccion &class="macro">#x27; + str(symbol)) plt.xlabel(&class="macro">#x27;Indice de la muestra&class="macro">#x27;) plt.ylabel(&class="macro">#x27;Error&class="macro">#x27;) plt.legend() plt.grid(True) plt.savefig(str(symbol)+str(optional)+&class="macro">#x27;.png&class="macro">#x27;) 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 linea separada for resultado in resultados: archivo.write(str(resultado) + "\n") # finish mt5.shutdown()
把训练曲线和预测图导出来看一眼
模型跑完不能只盯终端文字,得把迭代过程的误差和真实/预测对照画出来。下面这段 Python 用 matplotlib 连续生成四张图:训练与验证的 RMSE 随迭代变化、训练与验证的 loss 曲线、训练集上的原始价 vs 预测价、测试集上的原始价 vs 预测价,每张按品种名存成 PNG,方便你事后翻看过拟合发生在第几轮。 #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') 逐行拆一下:前两段 figure 尺寸锁死 7x10 英寸,分别画 RMSE 和 loss 的双线(蓝=训练、绿=验证),用 savefig 落盘;后两段先把 scaler 逆变换还原成真实报价刻度,再叠红/绿预测线,横轴是小时数——你能直接看出预测在训练集贴合但测试集偏移多少。 实跑一版 EURUSD_D1_2024 的日志里,验证集 RMSE 降到 0.0070、测试 RMSE 0.0067,最终全样本 R2 达到 0.9915,模型结构为 Conv1D(256通道)→MaxPool→LSTM(100单元)→Dropout,参数量约 14.3 万。外汇与贵金属属高风险品种,这类指标仅作概率参考,实盘请先开 MT5 用历史数据复算一遍再信。
class="macro">#show iteration-rmse graph for training and validation plt.figure(figsize = (class="num">7,class="num">10)) plt.plot(history.history[&class="macro">#x27;root_mean_squared_error&class="macro">#x27;],label=&class="macro">#x27;Training RMSE&class="macro">#x27;,class="type">class="kw">color=&class="macro">#x27;b&class="macro">#x27;) plt.plot(history.history[&class="macro">#x27;val_root_mean_squared_error&class="macro">#x27;],label=&class="macro">#x27;Validation-RMSE&class="macro">#x27;,class="type">class="kw">color=&class="macro">#x27;g&class="macro">#x27;) plt.xlabel("Iteration") plt.ylabel("RMSE") plt.title("RMSE" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+&class="macro">#x27;class="num">1.png&class="macro">#x27;) class="macro">#show iteration-loss graph for training and validation plt.figure(figsize = (class="num">7,class="num">10)) plt.plot(history.history[&class="macro">#x27;loss&class="macro">#x27;],label=&class="macro">#x27;Training Loss&class="macro">#x27;,class="type">class="kw">color=&class="macro">#x27;b&class="macro">#x27;) plt.plot(history.history[&class="macro">#x27;val_loss&class="macro">#x27;],label=&class="macro">#x27;Validation-loss&class="macro">#x27;,class="type">class="kw">color=&class="macro">#x27;g&class="macro">#x27;) plt.xlabel("Iteration") plt.ylabel("Loss") plt.title("LOSS" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+&class="macro">#x27;class="num">2.png&class="macro">#x27;) class="macro">#show actual vs predicted(training) graph plt.figure(figsize=(class="num">7,class="num">10)) plt.plot(scaler.inverse_transform(plot_y_train),class="type">class="kw">color = &class="macro">#x27;b&class="macro">#x27;, label = &class="macro">#x27;Original&class="macro">#x27;) plt.plot(scaler.inverse_transform(train_predict),class="type">class="kw">color=&class="macro">#x27;red&class="macro">#x27;, label = &class="macro">#x27;Predicted&class="macro">#x27;) plt.title("Prediction Graph Using Training Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+&class="macro">#x27;class="num">3.png&class="macro">#x27;) class="macro">#show actual vs predicted(testing) graph plt.figure(figsize=(class="num">7,class="num">10)) plt.plot(scaler.inverse_transform(plot_y_test),class="type">class="kw">color = &class="macro">#x27;b&class="macro">#x27;, label = &class="macro">#x27;Original&class="macro">#x27;) plt.plot(scaler.inverse_transform(test_predict),class="type">class="kw">color=&class="macro">#x27;g&class="macro">#x27;, label = &class="macro">#x27;Predicted&class="macro">#x27;) plt.title("Prediction Graph Using Testing Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+&class="macro">#x27;class="num">4.png&class="macro">#x27;)
「LSTM 回归网络的层结构与误差实拍」
上面这张层表来自一个用于 EURUSD 日线回归的 LSTM 单输出模型。第一层 lstm_1 输出维度 100,参数量 80400;随后 Dropout 不增参数;末端 Dense 把 100 维压到 1 维,仅 101 个参数,全模型可训练参数共 224,069,无冻结层。 拟合质量看三个数:RMSE 0.00516、MSE 2.66e-05、R2 0.9915。R2 超过 0.99 说明该网络在样本内几乎吃透了 D1_2024 这段 EURUSD 的变动结构,但外汇高杠杆品种样本外漂移倾向明显,不能直接当方向信号。 运行依赖锁在 tensorflow 2.12.0 / keras 2.12.0 / tf2onnx 1.16.0,且本机必须装 Microsoft Visual C++ 2015-2022 Redistributable,否则 MT5 的 Python 桥接会起不来。数据抓取就一行:用 mt5.copy_rates_from 拉 EURUSD 的 D1 数据,symbol 与 optional 写死即可复现。
lstm_1(LSTM) (None, class="num">100) class="num">80400 dropout_1(Dropout) (None, class="num">100) class="num">0 dense(Dense) (None, class="num">1) class="num">101 ================================================================= Total params: class="num">224,class="num">069 Trainable params: class="num">224,class="num">069 Non-trainable params: class="num">0 ________________________________________________________________ RMSE : class="num">0.005155711558172936 MSE : class="num">2.6581361671078003e-05 R2 score : class="num">0.9915315181564703 tensorflow==class="num">2.12.class="num">0, keras==class="num">2.12.class="num">0, tf2onnx==class="num">1.16.class="num">0, MetaTrader5, pandas, numpy, scikit-learn, tf2onnx, keras Microsoft Visual C++ class="num">2015 - class="num">2022 Redistributable symbol = "EURUSD" optional = "D1_2024" eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)
◍ 把 ONNX 模型接进 EA 的实操落点
把训练好的 ONNX 模型塞进 MT5 的 EA,核心就是改输入参数、生命周期函数和报价主循环三处。先加模型资源引用和全局句柄:用 #resource 把 EURUSD_D1.onnx 映射成 ExtModel[] 字节数组,SAMPLE_SIZE 固定为 120,代表喂给模型的过去 K 线窗口长度。
OnInit() 里必须调 OnnxCreateFromBuffer 从静态缓冲建模型,并把输入张量形状显式设成 {1,120,1}(批大小 1、序列 120、单序列 Close)。输出形状设 {1,1},只预测一个收盘价类别。若返回 INVALID_HANDLE 直接 INIT_FAILED,这一步错了 EA 起不来。
OnDeinit() 里调 OnnxRelease 释放句柄,位置随意但必须有,否则长时间跑可能漏内存。
OnTick() 开头就要跑预测:先判断 TimeCurrent()>=ExtNextDay 触发 GetMinMax() 取区间最值,再用 ExtNextBar 卡新柱。预测结果写进 ExtPredictedClass(0涨/1平/2跌),随后改 GenerateSignal() 的声明与下单逻辑,把原信号源替换成这个类变量。
两个关键自定义函数 PredictPrice() 和 GetMinMax() 贴在脚本末尾即可,前者出预测、后者捞收盘高低。外汇与贵金属杠杆高,模型信号仅作概率参考,实盘前请在策略测试器用 2024 年数据回测验证。
class="macro">#include <Trade\Trade.mqh> class="macro">#resource "/Files/EURUSD_D1.onnx" as class="type">uchar ExtModel[] class="macro">#define SAMPLE_SIZE class="num">120 class="type">long ExtHandle=INVALID_HANDLE; class="type">int ExtPredictedClass=-class="num">1; class="type">class="kw">datetime ExtNextBar=class="num">0; class="type">class="kw">datetime ExtNextDay=class="num">0; class="type">class="kw">float ExtMin=class="num">0.0; class="type">class="kw">float ExtMax=class="num">0.0; CTrade ExtTrade; class="type">int dlsignal=-class="num">1; class=class="str">"cmt">//--- price movement prediction class="macro">#define PRICE_UP class="num">0 class="macro">#define PRICE_SAME class="num">1 class="macro">#define PRICE_DOWN class="num">2 class="macro">#resource "/Files/EURUSD_D1_2024.onnx" as class="type">uchar ExtModel[] class=class="str">"cmt">//--- create a model from class="kw">static buffer ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT); if(ExtHandle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- since not all sizes defined in the input tensor we must set them explicitly class=class="str">"cmt">//--- first index - batch size, second index - series size, third index - number of series(only Close) class="kw">const class="type">long input_shape[] = {class="num">1,SAMPLE_SIZE,class="num">1}; if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape)) { Print("OnnxSetInputShape error ",GetLastError()); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- since not all sizes defined in the output tensor we must set them explicitly class=class="str">"cmt">//--- first index - batch size, must match the batch size of the input tensor class=class="str">"cmt">//--- second index - number of predicted prices(we only predict Close) class="kw">const class="type">long output_shape[] = {class="num">1,class="num">1}; if(!OnnxSetOutputShape(ExtHandle,class="num">0,output_shape)) { Print("OnnxSetOutputShape error ",GetLastError()); class="kw">return(INIT_FAILED); } if(ExtHandle!=INVALID_HANDLE) { OnnxRelease(ExtHandle); ExtHandle=INVALID_HANDLE; } class="type">void OnTick() { class=class="str">"cmt">//--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); class=class="str">"cmt">//--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } class=class="str">"cmt">//--- check new bar if(TimeCurrent()<ExtNextBar) class="kw">return; class=class="str">"cmt">//--- set next bar time