1、本篇笔记也是在MQL5网上看到大佬的笔记作的修改
2、利用Tensorflow训练,MQL5运行。
3、模型训练的方式,已经在另一篇文章记录//+------------------------------------------------------------------+ //| TimelessAI.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Timeless" #property link "https://www.mql5.com" #property version "1.00" #include <Trade\Trade.mqh> input double InpLots = 1.0; // Lots amount to open position input bool InpUseStops = true; // Use stops in trading input int InpTakeProfit = 500; // TakeProfit level input int InpStopLoss = 500; // StopLoss level #resource "model.eurusd.H1.120.onnx" as uchar ExtModel[] //SAMPLE_SIZE: 定义每次预测使用的历史数据的大小(120个时间点)。 #define SAMPLE_SIZE 120 //ExtHandle: 保存ONNX模型的句柄。 //ExtPredictedClass: 保存模型预测的结果(价格上升、持平或下降)。 //ExtNextBar、ExtNextDay: 用于管理图表的下一根K线和日期。 //ExtMin、ExtMax: 保存价格的最小值和最大值,用于归一化。 //ExtTrade: 用于交易操作的CTrade对象。 long ExtHandle = INVALID_HANDLE; int ExtPredictedClass = -1; datetime ExtNextBar = 0; datetime ExtNextDay = 0; float ExtMin = 0.0; float ExtMax = 0.0; CTrade ExtTrade; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2 //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- if(_Symbol != "EURUSD" || _Period != PERIOD_H1) { Print("model must work with EURUSD,H1"); return(INIT_FAILED); } //--- 加载ONNX模型并获取句柄ExtHandle ExtHandle = OnnxCreateFromBuffer(ExtModel, ONNX_DEFAULT); if(ExtHandle == INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ", GetLastError()); return(INIT_FAILED); } // 由于并非输入张量中定义的所有大小,我们必须显式设置它们 // 第一个索引-批量大小,第二个索引-系列大小,第三个索引-序列号(仅关闭) // 设置ONNX模型的输入和输出维度。输入维度为{1, SAMPLE_SIZE, 1}, // 表示批量大小为1,输入时间序列长度为120,特征数为1(收盘价)。 const long input_shape[] = {1, SAMPLE_SIZE, 1}; if(!OnnxSetInputShape(ExtHandle, ONNX_DEFAULT, input_shape)) { Print("OnnxSetInputShape error ", GetLastError()); return(INIT_FAILED); } //---由于并非输出张量中定义的所有大小,我们必须显式设置它们 //---第一个索引-批大小,必须与输入张量的批大小匹配 //---第二个指数-预测价格的数量(我们只预测收盘价) //输出维度为{1, 1},表示模型输出1个预测值。 const long output_shape[] = {1, 1}; if(!OnnxSetOutputShape(ExtHandle, 0, output_shape)) { Print("OnnxSetOutputShape error ", GetLastError()); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 返回当前服务器的时间(以秒为单位), //ExtNextDay 是存储下一个新的一天的时间戳(通常基于午夜)。 //这行代码判断当前时间是否超过或等于下一天的时间点。 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; //如果当前时间已经超过了 ExtNextBar,则更新 ExtNextBar 为下一个 K 线的时间: //PeriodSeconds() 获取当前图表周期的秒数(例如,PeriodSeconds() 如果是 M1 则返回 60 秒,M5 则返回 300 秒等)。 //通过对当前时间取模并加上周期秒数,调整 ExtNextBar 为下一个完整的 K 线开始时间。 ExtNextBar = TimeCurrent(); ExtNextBar -= ExtNextBar % PeriodSeconds(); ExtNextBar += PeriodSeconds(); float close = (float)iClose(_Symbol, _Period, 0); // 如果 ExtMin(当天最低价)大于当前的收盘价 close,则更新 ExtMin 为当前的 close。这行代码在每次 OnTick 被调用时检查并更新当天的最低价 if(ExtMin > close) ExtMin = close; if(ExtMax < close) ExtMax = close; //--- predict next price PredictPrice(); //--- check trading according to prediction if(ExtPredictedClass >= 0) if(PositionSelect(_Symbol)) CheckForClose(); else CheckForOpen(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Get minimal and maximal Close for last 120 days | //+------------------------------------------------------------------+ //该函数获取过去120天(SAMPLE_SIZE)的日线收盘价,并计算其中的最小值和最大值,用于后续的归一化处理。 void GetMinMax(void) { //这里定义了一个名为 close 的变量,类型为 vectorf。 //vectorf 是 MQL5 中的一个数据结构,用来存储浮动的数据序列,在这里用于存储一段时间内的收盘价数据。 //vectorf 是一个一维数组,通常用于存储单一类型的数值数据,如收盘价、开盘价等。 //MqlRates 是一个结构体,表示单根K线的数据,包含了开盘价、最高价、最低价、收盘价等多个字段。 vectorf close; //获取最120根K线的收盘价 close.CopyRates(_Symbol, PERIOD_D1, COPY_RATES_CLOSE, 0, SAMPLE_SIZE); //将过去120根K线的收盘价的最低价放在ExtMin里 ExtMin = close.Min(); //将过去120根K线的收盘价的最高价放在ExtMax里 ExtMax = close.Max(); } //+------------------------------------------------------------------+ //| Predict next price | //+------------------------------------------------------------------+ //该函数用于获取预测的价格方向: // 首先从图表获取过去120个时间点的收盘价,并进行归一化处理(使用最小值和最大值)。 // 然后将归一化的价格数据输入到ONNX模型中,运行推理得到预测值。 // 根据预测值与当前收盘价的差异,确定预测的价格趋势: // PRICE_SAME:表示预测价格与当前价格相同。 // PRICE_UP:表示预测价格上涨。 // PRICE_DOWN:表示预测价格下跌。 void PredictPrice(void) { //这行代码定义了一个静态的 vectorf 类型变量 output_data,大小为 1。vectorf 用于存储浮动类型的数值数据, //这里用来存储预测的结果。 static vectorf output_data(1); // vector to get result //这行定义了另一个静态的 vectorf 类型变量 x_norm,大小为 SAMPLE_SIZE。 //它用于存储经过归一化处理的价格数据。 static vectorf x_norm(SAMPLE_SIZE); // vector for prices normalize //检查是否可以进行归一化。如果 ExtMin (最小值)大于或等于 ExtMax (最大值),则无法归一化。 //在这种情况下,ExtPredictedClass 被设置为 -1(表示错误状态),并且函数直接返回,不进行进一步操作。 if(ExtMin >= ExtMax) { ExtPredictedClass = -1; return; } //使用 CopyRates 函数从图表请求历史数据。具体参数: //_Symbol 表示当前交易品种(例如 EURUSD)。 //_Period 表示时间周期(例如 D1)。 //COPY_RATES_CLOSE 表示只复制收盘价数据。 //1 表示从最近的时间点开始请求数据。 //SAMPLE_SIZE 表示请求的数据条数。 //如果请求失败,ExtPredictedClass 被设置为 -1,表示发生错误,然后函数返回。 if(!x_norm.CopyRates(_Symbol, _Period, COPY_RATES_CLOSE, 1, SAMPLE_SIZE)) { ExtPredictedClass = -1; return; } //--- //该行获取最后一根 K 线的收盘价。因为数据被存储在 x_norm 中,x_norm[SAMPLE_SIZE - 1] 表示数组中的最后一个元素,即最近的收盘价。 float last_close = x_norm[SAMPLE_SIZE - 1]; //这两行代码将价格数据进行归一化处理: //x_norm -= ExtMin;:将数据中的每个元素减去最小值 ExtMin,以确保数据从 0 开始。 //x_norm /= (ExtMax - ExtMin);:然后将数据除以 (ExtMax - ExtMin),使数据范围变为 0 到 1。这样就完成了归一化。 x_norm -= ExtMin; x_norm /= (ExtMax - ExtMin); //OnnxRun 是一个函数,用于通过 ONNX 模型进行推理。具体参数: //ExtHandle 是 ONNX 模型的句柄。 //ONNX_NO_CONVERSION 表示不进行数据类型转换。 //x_norm 是归一化后的价格数据,作为输入传入。 //output_data 用于存储推理的输出结果。 //如果推理失败,ExtPredictedClass 被设置为 -1,表示错误状态,函数直接返回。 if(!OnnxRun(ExtHandle, ONNX_NO_CONVERSION, x_norm, output_data)) { ExtPredictedClass = -1; return; } //这行代码将预测的输出值从 0 到 1 的范围反归一化到实际价格范围。 // output_data[0] 是 ONNX 模型输出的归一化预测结果。 // (ExtMax - ExtMin) 将归一化结果还原为实际的价格范围。 // + ExtMin 是为了将结果偏移回到正确的价格区间。 float predicted = output_data[0] * (ExtMax - ExtMin) + ExtMin; //计算预测的价格(predicted)与最后的收盘价(last_close)之间的差异delta。 //如果差异为负,表示预测价格低于当前收盘价;如果差异为正,表示预测价格高于当前收盘价。 float delta = last_close - predicted; //如果 delta 的绝对值非常小(即价格变化几乎为 0),那么预测的价格与当前收盘价几乎相同。 //此时,ExtPredictedClass 被设置为 PRICE_SAME,表示价格保持不变。 if(fabs(delta) <= 0.003) ExtPredictedClass = PRICE_SAME; else { if(delta < 0) ExtPredictedClass = PRICE_UP; else ExtPredictedClass = PRICE_DOWN; } } //+------------------------------------------------------------------+ //| Check for open position conditions | //+------------------------------------------------------------------+ //根据预测的价格趋势(上涨或下跌),判断建仓信号: //如果预测价格上涨(PRICE_UP),则选择买入。 //如果预测价格下跌(PRICE_DOWN),则选择卖出。 //根据输入参数设置止损和止盈,并在允许交易的情况下建仓。 void CheckForOpen(void) { //定义一个变量 signal,类型为 ENUM_ORDER_TYPE,用于表示订单类型(买单或卖单)。初始化为 WRONG_VALUE,表示没有有效的信号。 ENUM_ORDER_TYPE signal = WRONG_VALUE; //--- check signals if(ExtPredictedClass == PRICE_DOWN) signal = ORDER_TYPE_SELL; // sell condition else { if(ExtPredictedClass == PRICE_UP) signal = ORDER_TYPE_BUY; // buy condition } //检查 signal 是否有效(即不等于 WRONG_VALUE),并且检查终端是否允许交易(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 返回是否可以进行交易)。 if(signal != WRONG_VALUE && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { double price, sl = 0, tp = 0; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if(signal == ORDER_TYPE_SELL) { price = bid; // 如果启用了止损止盈(InpUseStops),则根据当前卖价 (bid) 和止损、止盈的参数进行计算: // sl = NormalizeDouble(bid + InpStopLoss * _Point, _Digits);:计算止损价格(卖价加上止损距离),并将其规范化为适合当前交易品种的价格精度。 // tp = NormalizeDouble(ask - InpTakeProfit * _Point, _Digits);:计算止盈价格(买价减去止盈距离),并规范化为适合的精度。 if(InpUseStops) { sl = NormalizeDouble(bid + InpStopLoss * _Point, _Digits); tp = NormalizeDouble(ask - InpTakeProfit * _Point, _Digits); } } else { price = ask; if(InpUseStops) { sl = NormalizeDouble(ask - InpStopLoss * _Point, _Digits); tp = NormalizeDouble(bid + InpTakeProfit * _Point, _Digits); } } ExtTrade.PositionOpen(_Symbol, signal, InpLots, price, sl, tp); } } //+------------------------------------------------------------------+ //| 平仓条件判断 | //| 如果设置了止损和止盈,函数会直接返回。 | //| 如果没有设置止损和止盈,检查当前持仓的类型: | //| 如果是买入仓位且预测价格下跌,则平仓并反向开仓。 | //| 如果是卖出仓位且预测价格上涨,则平仓并反向开仓。 | //+------------------------------------------------------------------+ void CheckForClose(void) { //这行代码检查是否启用了止损(InpUseStops)。 //如果启用了止损,函数将直接返回,不会继续执行其他的平仓操作。这意味着止损机制优先于其他信号。 if(InpUseStops) return; //定义一个布尔变量 bsignal,初始化为 false。这个变量用来表示是否存在需要平仓的信号。 bool bsignal = false; //使用 PositionGetInteger(POSITION_TYPE) 获取当前持仓的类型,返回值存储在 type 变量中: // POSITION_TYPE_BUY 表示当前持仓为买单。 // POSITION_TYPE_SELL 表示当前持仓为卖单。 long type = PositionGetInteger(POSITION_TYPE); //如果当前持仓为买单 (POSITION_TYPE_BUY),并且 ExtPredictedClass 为 PRICE_DOWN(即预测价格将下跌),则将 bsignal 设置为 true,表示需要平仓。 if(type == POSITION_TYPE_BUY && ExtPredictedClass == PRICE_DOWN) bsignal = true; //如果当前持仓为卖单 (POSITION_TYPE_SELL),并且 ExtPredictedClass 为 PRICE_UP(即预测价格将上涨),则将 bsignal 设置为 true,表示需要平仓 if(type == POSITION_TYPE_SELL && ExtPredictedClass == PRICE_UP) bsignal = true; //如果 bsignal 为 true(即需要平仓)并且终端允许交易(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 返回 true),则执行平仓操作。 if(bsignal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { //调用 ExtTrade.PositionClose 函数平掉当前持仓,_Symbol 表示当前的交易品种,3 表示平仓的魔术数字,通常用于标识特定的订单。 ExtTrade.PositionClose(_Symbol, 3); //--- open opposite CheckForOpen(); } } //+------------------------------------------------------------------+
MQL5运行TensorFlow训练出来的模型
| Title | MQL5运行TensorFlow训练出来的模型 |
|---|---|
| Framework | MQL5 |
| User | wy8817399@vip.qq.com |
| Id | 64 |
| Created | 3/6/26, 7:47 PM |
| Modified | 3/6/26, 7:47 PM |
| Published | Yes |
Content