将 MQL5 与数据处理包集成(第 4 部分):大数据处理·综合运用
📘

将 MQL5 与数据处理包集成(第 4 部分):大数据处理·综合运用

第 3/3 篇

「把多指标塞进特征数组交给模型」

EA 在 OnTick 里先把技术面量化为数值,再统一丢给预测函数。下面这段 MT5 代码就是典型的「指标采集 → 特征组装 → 信号分发」骨架,直接挂 EURUSD 或 XAUUSD 的 M5 周期就能跑通逻辑。 代码先取了 MA20、MA50 两条均线(SMA、收盘价),RSI 周期固定 14,MACD 用 12/26/9 经典参数。注意 MACD 必须通过 iMACD 拿句柄,再用 CopyBuffer 分别拷主图、信号线、柱线三个缓冲区,任何一次拷贝返回 ≤0 都会在日志里报警,但不会中断后续执行。 五个特征按固定顺序进数组:features[0]~[4] 分别是 MA20、MA50、RSI、MACD、SignalLine。GetPrediction 返回 1 触发 MBuy(),返回 -1 触发 MSell(),中间值不动作。外汇与贵金属杠杆高,这类信号仅代表模型当时的概率倾向,实盘前务必在策略测试器用至少 3 个月 tick 数据回验。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick(){
   class=class="str">"cmt">// Calculate indicators
   class="type">class="kw">double MA20 = iMA(_Symbol, PERIOD_CURRENT, class="num">20, class="num">0, MODE_SMA, PRICE_CLOSE);
   class="type">class="kw">double MA50 = iMA(_Symbol, PERIOD_CURRENT, class="num">50, class="num">0, MODE_SMA, PRICE_CLOSE);
   class="type">class="kw">double RSI = iRSI(_Symbol, PERIOD_CURRENT, class="num">14, PRICE_CLOSE);
   class=class="str">"cmt">// Declare arrays to hold MACD data
   class="type">class="kw">double MACD_Buffer[class="num">1], SignalLine_Buffer[class="num">1], Hist_Buffer[class="num">1];
   class=class="str">"cmt">// Get MACD handle
   class="type">int macd_handle = iMACD(NULL, class="num">0, class="num">12, class="num">26, class="num">9, PRICE_CLOSE);
   if (macd_handle != INVALID_HANDLE) {
      class=class="str">"cmt">// Copy the most recent MACD values into buffers
      if (CopyBuffer(macd_handle, class="num">0, class="num">0, class="num">1, MACD_Buffer) <= class="num">0) Print("Failed to copy MACD");
      if (CopyBuffer(macd_handle, class="num">1, class="num">0, class="num">1, SignalLine_Buffer) <= class="num">0) Print("Failed to copy Signal Line");
      if (CopyBuffer(macd_handle, class="num">2, class="num">0, class="num">1, Hist_Buffer) <= class="num">0) Print("Failed to copy Histogram");
   }

   class=class="str">"cmt">// Assign the values from the buffers
   class="type">class="kw">double MACD = MACD_Buffer[class="num">0];
   class="type">class="kw">double SignalLine = SignalLine_Buffer[class="num">0];
   class=class="str">"cmt">// Assign features
   class="type">class="kw">double features[class="num">5];
   features[class="num">0] = MA20;
   features[class="num">1] = MA50;
   features[class="num">2] = RSI;
   features[class="num">3] = MACD;
   features[class="num">4] = SignalLine;
   class=class="str">"cmt">// Get prediction
   class="type">class="kw">double signal = GetPrediction(features);
   if (signal == class="num">1){
      MBuy();   class=class="str">"cmt">// Adjust lot size
   }
   else if (signal == -class="num">1){
      MSell();
   }
}
逐行拆解重点:第 4–6 行用 iMA / iRSI 直接拿标量,但 MACD 不行,它返回的是句柄而非数值,所以第 10 行拿句柄后,12–14 行用 CopyBuffer 把最新一根的三种值拷进长度为 1 的数组。features 数组的索引顺序要和你的模型训练时完全一致,否则 GetPrediction 输出了错误方向的信号概率会明显偏高。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick(){
   class=class="str">"cmt">// Calculate indicators
   class="type">class="kw">double MA20 = iMA(_Symbol, PERIOD_CURRENT, class="num">20, class="num">0, MODE_SMA, PRICE_CLOSE);
   class="type">class="kw">double MA50 = iMA(_Symbol, PERIOD_CURRENT, class="num">50, class="num">0, MODE_SMA, PRICE_CLOSE);
   class="type">class="kw">double RSI = iRSI(_Symbol, PERIOD_CURRENT, class="num">14, PRICE_CLOSE);
   class=class="str">"cmt">// Declare arrays to hold MACD data
   class="type">class="kw">double MACD_Buffer[class="num">1], SignalLine_Buffer[class="num">1], Hist_Buffer[class="num">1];
   class=class="str">"cmt">// Get MACD handle
   class="type">int macd_handle = iMACD(NULL, class="num">0, class="num">12, class="num">26, class="num">9, PRICE_CLOSE);
   if (macd_handle != INVALID_HANDLE) {
      class=class="str">"cmt">// Copy the most recent MACD values into buffers
      if (CopyBuffer(macd_handle, class="num">0, class="num">0, class="num">1, MACD_Buffer) <= class="num">0) Print("Failed to copy MACD");
      if (CopyBuffer(macd_handle, class="num">1, class="num">0, class="num">1, SignalLine_Buffer) <= class="num">0) Print("Failed to copy Signal Line");
      if (CopyBuffer(macd_handle, class="num">2, class="num">0, class="num">1, Hist_Buffer) <= class="num">0) Print("Failed to copy Histogram");
   }

   class=class="str">"cmt">// Assign the values from the buffers
   class="type">class="kw">double MACD = MACD_Buffer[class="num">0];
   class="type">class="kw">double SignalLine = SignalLine_Buffer[class="num">0];
   class=class="str">"cmt">// Assign features
   class="type">class="kw">double features[class="num">5];
   features[class="num">0] = MA20;
   features[class="num">1] = MA50;
   features[class="num">2] = RSI;
   features[class="num">3] = MACD;
   features[class="num">4] = SignalLine;
   class=class="str">"cmt">// Get prediction
   class="type">class="kw">double signal = GetPrediction(features);
   if (signal == class="num">1){
      MBuy();   class=class="str">"cmt">// Adjust lot size
   }
   else if (signal == -class="num">1){
      MSell();
   }
}

◍ 用 Flask 把 BTC 模型预测挂成接口

想把离线训好的机器学习模型用进 BTC 决策,不一定非要嵌进 MT5。起一个轻量 web API,让盯盘工具 POST 一段行情特征就能拿回预测,是更灵活的接法。 下面这段 Python 用了 Flask 框架,加载一个名为 btc_trading_model.pkl 的 joblib 模型文件,在本地 5000 端口暴露 /predict 接口,只接受 POST 的 JSON 行情数据并返回预测列表。

MQL5 / C++
from flask class="kw">import Flask, request, jsonify
class="kw">import joblib
class="kw">import pandas as pd
# Load the model
model = joblib.load(&class="macro">#x27;btc_trading_model.pkl&class="macro">#x27;)
app = Flask(__name__)
@app.route(&class="macro">#x27;/predict&class="macro">#x27;, methods=[&class="macro">#x27;POST&class="macro">#x27;])
def predict():
    data = request.json
    df = pd.DataFrame(data)
    prediction = model.predict(df)
    class="kw">return jsonify(prediction.tolist())
app.run(port=class="num">5000)
逐行看:前 3 行引入 Flask、joblib 和 pandas,并注释标明加载模型;第 5 行用 joblib.load 把本地 pkl 模型读进内存。app 实例化后,用 @app.route 装饰器把 /predict 绑定到 predict 函数,仅收 POST。 函数体内,request.json 拿到前端传来的 JSON,pd.DataFrame 转成模型要的二维表,model.predict 出结果,jsonify(prediction.tolist()) 把 numpy 数组变回普通列表返给调用方。最后 app.run(port=5000) 在 5000 端口起服务。 实机上你可以用 curl 向 http://localhost:5000/predict 发一条 BTC 的 OHLCV 特征 JSON 验证返回结构。加密货币与外汇、贵金属一样属高风险品种,模型输出只作概率参考,别直接当下单指令。

MQL5 / C++
from flask class="kw">import Flask, request, jsonify
class="kw">import joblib
class="kw">import pandas as pd
# Load the model
model = joblib.load(&class="macro">#x27;btc_trading_model.pkl&class="macro">#x27;)
app = Flask(__name__)
@app.route(&class="macro">#x27;/predict&class="macro">#x27;, methods=[&class="macro">#x27;POST&class="macro">#x27;])
def predict():
    data = request.json
    df = pd.DataFrame(data)
    prediction = model.predict(df)
    class="kw">return jsonify(prediction.tolist())
app.run(port=class="num">5000)

一点提醒

把历史 BTC/USD 的 H1 数据清洗后抽出的 MA、RSI、MACD 特征喂给模型,训出的信号预测器通过 Flask API 对外服务,MT5 里的 EA 只负责抓实时指标、发请求、按回包下单——这条链路在 5.08 KB 的 BTC-Big-DataH.mq5 里已经跑通。 对外汇与贵金属来说,这类 ML+指标混合方案只是把人工判断换成概率输出,市场突变时模型滞后不可避免,杠杆品种爆仓风险始终存在。 真要落地,先拿原文附的 162.02 KB BTCUSD_H1.csv 在本地复现 notebook,再只开模拟账户接 EA,别一上来就实盘扛单。

常见问题

在拼接特征数组时严格按 bar 索引偏移,所有指标只用已闭合 K 线的值;回测前用肉眼抽查前几根 bar 的特征是否含当日收盘价。
把模型权重常驻内存不要每次加载,接口只收标准化后的特征向量;用 gunicorn 开多 worker 比单线程快数倍。
可以。小布能按你给的指标清单自动算特征重要性并圈出冗余项,你只需把品种页打开看诊断结论。
用 walk-forward 切分,看样本外准确率掉多少;掉超 15% 就删掉相关性高于 0.9 的成对指标。
不能。外汇贵金属高风险,概率只作倾向参考,仓位按账户净值和止损距离反推更安全。