重构经典策略(第九部分):多时间框架分析(第二部分)·综合运用
⏱️

重构经典策略(第九部分):多时间框架分析(第二部分)·综合运用

(3/3)·月线与小时图出现双低误差区,但残差无规律分布,最优周期远非直觉所能判定

案例拆解 第 3/3 篇
很多人凭感觉挑时间框架,以为预测越近越准,结果在错的周期上反复吃亏。本文用 AI 模型残差在 11 个周期上的实测分布,戳破这条经验主义老规矩。连老手争了多年的“哪个周期最好用”,在数据面前都得先打问号。

EA 骨架:先把 ONNX 和指标句柄立起来

把深度学习模型塞进 MT5 做日内决策,第一步不是算信号,而是把资源句柄和状态变量在 OnInit 里一次性挂好。这段初始化逻辑直接绑定 EURUSD 日线,布林带周期 30、RSI 周期 14、EMA 周期 20,三个指标全部用 PRICE_CLOSE 计算——意味着模型输入偏重收盘价结构,对盘中毛刺不敏感。 OnTick 的主循环非常薄:先 update_market_data 刷行情,再 model_predict 取模型输出,最后 display_stats 打印状态。真正下单只在 PositionsTotal()==0 时触发,且严格依赖 model_state 的符号——等于 1 才查多头条件,等于 -1 才查空头条件,模型不表态就空仓。 持仓管理给了两条退出路径:user_close_type 切到 MA_CLOSE 就走均线死叉/金叉逻辑,否则交给 ai_close_positions 用模型自己判断离场。外汇与贵金属杠杆高,这种双退出机制能降低单一模型误判的暴露,但回测前务必在策略测试器里确认 load_onnx_model 返回的模型版本与指标周期匹配。

MQL5 / C++
vectorf model_output = vectorf::Zeros(class="num">1);
class="type">class="kw">double  bid,ask;
class="type">int     ma_hanlder;
class="type">class="kw">double  ma_buffer[];
class="type">int     bb_hanlder;
class="type">class="kw">double  bb_mid_buffer[];
class="type">class="kw">double  bb_high_buffer[];
class="type">class="kw">double  bb_low_buffer[];
class="type">int     rsi_hanlder;
class="type">class="kw">double  rsi_buffer[];
class="type">int     system_state = class="num">0,model_state=class="num">0;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Load our ONNX function
   if(!load_onnx_model())
     {
       class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//--- Load our technical indicators
   bb_hanlder  = iBands("EURUSD",PERIOD_D1,class="num">30,class="num">0,class="num">1,PRICE_CLOSE);
   rsi_hanlder = iRSI("EURUSD",PERIOD_D1,class="num">14,PRICE_CLOSE);
   ma_hanlder  = iMA("EURUSD",PERIOD_D1,class="num">20,class="num">0,MODE_EMA,PRICE_CLOSE);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//--- Release the resources we don&class="macro">#x27;t need
   release_resources();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//--- Update market data
   update_market_data();
class=class="str">"cmt">//--- Fetch a prediction from our model
   model_predict();
class=class="str">"cmt">//--- Display stats
   display_stats();
class=class="str">"cmt">//--- Find a position
   if(PositionsTotal() == class="num">0)
     {
       if(model_state == class="num">1)
         check_bullish_setup();
       else
         if(model_state == -class="num">1)
           check_bearish_setup();
     }
class=class="str">"cmt">//--- Manage the position we have
   else
     {
       class=class="str">"cmt">//--- How should we close our positions?
       if(user_close_type == MA_CLOSE)
         {
          ma_close_positions();
         }
       else
         {
          ai_close_positions();
         }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+

「EURUSD 日线多空触发与 AI 反转平仓」

下面这段逻辑直接在 MT5 的 EA 里跑,用日线(PERIOD_D1)收盘价做判定,品种写死 EURUSD,外汇品种波动大、杠杆高,实盘前务必先开模拟账户验证。 AI 反转平仓函数 ai_close_positions 只做一件事:当内部状态 system_state 与模型输出 model_state 不一致,弹 Alert 并平掉 EURUSD 仓位。这属于兜底逻辑,模型判势和持仓状态冲突时优先离场,避免扛单。 均线平仓函数 ma_close_positions 分两种情况:system_state==1 表示持多,若日线收盘价跌破 ma_buffer[0] 则平多;system_state==-1 表示持空,若日线收盘价上破 ma_buffer[0] 则平空。这里 ma_buffer[0] 是当日均线值,价格回破均线倾向视为趋势减弱。 空头建仓 check_bearish_setup:日线收盘价低于布林下轨 bb_low_buffer[0],且 RSI 低于 50,且收盘价低于均线,才 Trade.Sell(0.3,...) 并置 system_state=-1。多头 check_bullish_setup 对称:收盘价高于布林上轨、RSI 大于 50、收盘价高于均线才买。三重过滤把假突破概率压下来,但 EURUSD 日线一根 bar 走 80~120 点很常见,止损必须另设。 让小布替你跑这套 把代码贴进 MT5 策略测试器,品种选 EURUSD 日线,重点看 ma_buffer / bb_low_buffer / rsi_buffer 三个数组有没有正确赋值,否则 PositionClose 永远不会触发。

MQL5 / C++
class="type">void ai_close_positions(class="type">void)
  {
   if(system_state != model_state)
     {
       Alert("Reversal detected by our AI system,closing open positions");
       Trade.PositionClose("EURUSD");
     }
  }
class="type">void ma_close_positions(class="type">void)
  {
class=class="str">"cmt">//--- Is our buy position possibly weakening?
   if(system_state == class="num">1)
     {
       if(iClose("EURUSD",PERIOD_D1,class="num">0) < ma_buffer[class="num">0])
         Trade.PositionClose("EURUSD");
     }
class=class="str">"cmt">//--- Is our sell position possibly weakening?
   if(system_state == -class="num">1)
     {
       if(iClose("EURUSD",PERIOD_D1,class="num">0) > ma_buffer[class="num">0])
         Trade.PositionClose("EURUSD");
     }
  }
class="type">void check_bearish_setup(class="type">void)
  {
   if(iClose("EURUSD",PERIOD_D1,class="num">0) < bb_low_buffer[class="num">0])
     {
       if(class="num">50 > rsi_buffer[class="num">0])
         {
           if(iClose("EURUSD",PERIOD_D1,class="num">0) < ma_buffer[class="num">0])
             {
               Trade.Sell(class="num">0.3,"EURUSD",bid,class="num">0,class="num">0,"EURUSD MTF AI");
               system_state = -class="num">1;
             }
         }
     }
  }
class="type">void check_bullish_setup(class="type">void)
  {
   if(iClose("EURUSD",PERIOD_D1,class="num">0) > bb_high_buffer[class="num">0])
     {
       if(class="num">50 < rsi_buffer[class="num">0])
         {
           if(iClose("EURUSD",PERIOD_D1,class="num">0) > ma_buffer[class="num">0])
             {

◍ 把模型信号接进下单与资源回收

这段逻辑把 ONNX 模型的输出直接转成交易动作与状态机标记。当预测值低于月线收盘价时,model_state 置为 1,上层据此用 Trade.Buy(0.3,"EURUSD",ask,0,0,"EURUSD MTF AI") 以市价 ask 开 0.3 手多单,并把 system_state 切到 1,说明信号已被消费。 display_stats 仅用 Comment 把 model_output[0] 打印到图表左上角,方便肉眼核对预测值,不干涉任何仓位。model_predict 则从 EURUSD 月线(PERIOD_MN1)取 1 根收盘价喂给 OnnxRun,再按输出与输入大小关系写 model_state,预测值高于输入则置 -1,低于则置 1,等于的情况留空。 release_resources 在退出前释放 onnx_model 与三个指标句柄(ma/rsi/bb),最后调 ExpertRemove 卸载 EA。update_market_data 每次刷新 bid、ask 并各取 1 根缓冲到数组:MA 主缓冲、RSI 主线、布林中轨/上轨/下轨分别来自 bb_hanlder 的 0/1/2 号缓冲。外汇与贵金属杠杆高,模型信号仅作概率参考,实盘前请在 MT5 策略测试器用历史数据验证这套复制逻辑是否丢帧。

MQL5 / C++
Trade.Buy(class="num">0.3,"EURUSD",ask,class="num">0,class="num">0,"EURUSD MTF AI");
   system_state = class="num">1;
   }
  }
  }
 }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display account stats                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void display_stats(class="type">void)
  {
   Comment("Forecast: ",model_output[class="num">0]);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Fetch a prediction from our model                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void model_predict(class="type">void)
  {
class=class="str">"cmt">//--- Get inputs
   model_input.CopyRates("EURUSD",PERIOD_MN1,COPY_RATES_CLOSE,class="num">0,class="num">1);
class=class="str">"cmt">//--- Fetch a prediction from our model
   OnnxRun(onnx_model,ONNX_DEFAULT,model_input,model_output);
class=class="str">"cmt">//--- Store the model&class="macro">#x27;s prediction as a flag
   if(model_output[class="num">0] > model_input[class="num">0])
     {
      model_state = -class="num">1;
     }
   else
     if(model_output[class="num">0] < model_input[class="num">0])
       {
        model_state = class="num">1;
       }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Release the resources we don&class="macro">#x27;t need                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void release_resources(class="type">void)
  {
   OnnxRelease(onnx_model);
   IndicatorRelease(ma_hanlder);
   IndicatorRelease(rsi_hanlder);
   IndicatorRelease(bb_hanlder);
   ExpertRemove();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update our market data                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void update_market_data(class="type">void)
  {
class=class="str">"cmt">//--- Update all our technical data
   bid = SymbolInfoDouble("EURUSD",SYMBOL_BID);
   ask = SymbolInfoDouble("EURUSD",SYMBOL_ASK);
   CopyBuffer(ma_hanlder,class="num">0,class="num">0,class="num">1,ma_buffer);
   CopyBuffer(rsi_hanlder,class="num">0,class="num">0,class="num">1,rsi_buffer);
   CopyBuffer(bb_hanlder,class="num">0,class="num">0,class="num">1,bb_mid_buffer);
   CopyBuffer(bb_hanlder,class="num">1,class="num">0,class="num">1,bb_high_buffer);
   CopyBuffer(bb_hanlder,class="num">2,class="num">0,class="num">1,bb_low_buffer);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

把训练好的模型塞进 MT5 内存

在 MQL5 里跑 AI 推理,第一步不是画信号线,而是把 ONNX 二进制缓冲加载成可用句柄。OnnxCreateFromBuffer 吃的是内存里的 onnx_buffer,第二个参数用 ONNX_DEFAULT 即可,返回 INVALID_HANDLE 就说明模型本身损坏或版本不兼容。 加载成功后必须显式声明输入输出维度。上面这段写死成 {1,1},意思是单次喂 1 个特征、吐 1 个值——如果你的网络是 30 根 K 线的收盘价序列,这里就得改成 {1,30},否则 OnnxSetInputShape 会直接返回 false。 别把 {1,1} 当通用模板抄。shape 设错不会让 EA 崩,但后续推理结果会是垃圾值,且 MT5 只在图表左上角 Comment 一行提示,日志里不报红,排查起来很隐蔽。外汇与贵金属杠杆高,模型加载异常时任何信号都不可信,先确认返回 true 再谈交易。

MQL5 / C++
class="type">bool load_onnx_model(class="type">void)
  {
class=class="str">"cmt">//--- Create the ONNX model from our buffer
   onnx_model = OnnxCreateFromBuffer(onnx_buffer,ONNX_DEFAULT);
class=class="str">"cmt">//--- Validate the model
   if(onnx_model == INVALID_HANDLE)
     {
     class=class="str">"cmt">//--- Give feedback
       Comment("Failed to create the ONNX model");
     class=class="str">"cmt">//--- We failed to create the model
       class="kw">return(false);
     }
class=class="str">"cmt">//--- Specify the I/O shapes
   class="type">ulong input_shape[] = {class="num">1,class="num">1};
   class="type">ulong output_shape[] = {class="num">1,class="num">1};
class=class="str">"cmt">//--- Validate the I/O shapes
   if(!(OnnxSetInputShape(onnx_model,class="num">0,input_shape)) || !(OnnxSetOutputShape(onnx_model,class="num">0,output_shape)))
     {
     class=class="str">"cmt">//--- Give feedback
       Comment("We failed to define the correct input shapes");
     class=class="str">"cmt">//--- We failed to define the correct I/O shape
       class="kw">return(false);
     }
   class="kw">return(true);
  }

「把工具请下神坛」

回测里月线与小时图在欧元兑美元上跑出了相对最稳的框架,但这只覆盖单一货币对,不能外推到全市场。附件里的 EURUSD_MTF_AI.mq5 和对应的 .onnx 权重只是验证思路的载体,不是圣杯。 真正该做的,是自己在 MT5 里换几个非欧系货币对复跑,看误差指标会不会垮。时间框架组合搜索、损失函数微调这些活儿,得靠你本地算力接着挖。 外汇和贵金属杠杆高、跳空频繁,任何历史最优框架遇到流动性断裂都可能失效。把脚本当探针用,别当预言机。

让小布替你跑这套残差扫描
把 11 个时间框架的误差分布诊断交给小布盯盘,AIGC 已内置对应品种的模型残差视图,打开欧元兑美元页就能直接比对,你只管读信号做决策。

常见问题

月度数据 400 根约覆盖 33 年,多数市场没这么长历史,用它做公平测试的容量天花板可避免其他周期因数据过多产生偏差。
矛盾价格走势常在比开仓周期更短的图上先出现,趋势减弱的裂缝从底向上传递,高级别图形随后才反映。
可以,小布盯盘的品种页内置了 AIGC 残差分布视图,覆盖主要外汇周期,省去你自己从终端导数据跑模型的重复劳动。
不代表无意义,只说明单看误差水平不能定最优周期,还需跨市场验证分布稳定性,否则结论可能随行情结构漂移。
可把月度当情绪背景、H1 找具体设置,但需先确认该分布在当前市场未畸变,再结合更低周期疲软信号过滤入场。