public: class="type">void ma_expert(); class=class="str">"cmt""> public: class="type">void ma_expert(); class=class="str">"cmt"">
依据价格相关性的统计数据过滤信号·进阶篇
📘

依据价格相关性的统计数据过滤信号·进阶篇

第 2/2 篇

◍ 双均线专家类的对外接口与初始化

下面这段公开成员声明定义了一个双均线策略类的对外骨架:构造、参数注入、信号判定与下单全部暴露在 public 区,方便主程序直接调。

MQL5 / C++
class="kw">public:
  class="type">void        ma_expert();                                class=class="str">"cmt">// Constructor
  class="type">void get_lot(class="type">class="kw">double lot){lots=lot;}                     class=class="str">"cmt">// Receiving a lot
  class="type">void get_periods(class="type">int red,class="type">int yel){ma_red_per=red;ma_yel_per=yel;} class=class="str">"cmt">// Receiving the periods of MAs
  class="type">void get_stops(class="type">class="kw">double SL,class="type">class="kw">double TS){sl=SL;ts=TS;}        class=class="str">"cmt">// Receiving the values of stops
  class="type">void        init();                                     class=class="str">"cmt">// Receiving the indicator values
  class="type">bool        check_for_buy();                            class=class="str">"cmt">// Checking for buy
  class="type">bool        check_for_sell();                           class=class="str">"cmt">// Checking for sell
  class="type">void        open_buy();                                 class=class="str">"cmt">// Open buy
  class="type">void        open_sell();                                class=class="str">"cmt">// Open sell
  class="type">void        position_modify();                          class=class="str">"cmt">// Position modification
};
逐行看:ma_expert() 是构造函数;get_lot 把手数写进 lots 成员;get_periods 接收快慢均线周期(red 慢、yel 快);get_stops 接收止损 sl 与追踪止损 ts;init 负责拿指标句柄;check_for_buy/sell 返回布尔信号;open_buy/sell 执行下单;position_modify 做持仓修正。 构造体里用 ZeroMemory 把三个指标句柄结构体清零,避免野指针:
MQL5 / C++
class="type">void my_expert::ma_expert(class="type">void)
  {
  ZeroMemory(ma_red_han);
  ZeroMemory(ma_yel_han);
  ZeroMemory(macd_han);
  }
init 里用 iMA 在当前品种当前周期上建两条收盘价 EMA,偏移 0、模式 MODE_EMA:ma_red_han 对应慢线,ma_yel_han 对应快线。注意 macd_han 虽在构造里清零,本段并未赋值,说明后续小节才会接上。
MQL5 / C++
class="type">void  my_expert::init(class="type">void)
  {
  ma_red_han=iMA(_Symbol,_Period,ma_red_per,class="num">0,MODE_EMA,PRICE_CLOSE); class=class="str">"cmt">// Handle of the slow MA
  ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,class="num">0,MODE_EMA,PRICE_CLOSE); class=class="str">"cmt">// Handle of the fast MA
  }
开 MT5 新建 EA 时,把这段 public 接口和 init 原样贴进类定义,就能在 OnTick 前完成均线句柄准备;外汇与贵金属杠杆高,句柄获取失败会导致下单异常,建议先在策略测试器用 2023 年 XAUUSD 的 M15 跑一次空 init 验证句柄非空。

MQL5 / C++
class="kw">public:
  class="type">void        ma_expert();                                class=class="str">"cmt">// Constructor
  class="type">void get_lot(class="type">class="kw">double lot){lots=lot;}                     class=class="str">"cmt">// Receiving a lot
  class="type">void get_periods(class="type">int red,class="type">int yel){ma_red_per=red;ma_yel_per=yel;} class=class="str">"cmt">// Receiving the periods of MAs
  class="type">void get_stops(class="type">class="kw">double SL,class="type">class="kw">double TS){sl=SL;ts=TS;}        class=class="str">"cmt">// Receiving the values of stops
  class="type">void        init();                                     class=class="str">"cmt">// Receiving the indicator values
  class="type">bool        check_for_buy();                            class=class="str">"cmt">// Checking for buy
  class="type">bool        check_for_sell();                           class=class="str">"cmt">// Checking for sell
  class="type">void        open_buy();                                 class=class="str">"cmt">// Open buy
  class="type">void        open_sell();                                class=class="str">"cmt">// Open sell
  class="type">void        position_modify();                          class=class="str">"cmt">// Position modification
};

class="type">void my_expert::ma_expert(class="type">void)
  {
  ZeroMemory(ma_red_han);
  ZeroMemory(ma_yel_han);
  ZeroMemory(macd_han);
  }

class="type">void  my_expert::init(class="type">void)
  {
  ma_red_han=iMA(_Symbol,_Period,ma_red_per,class="num">0,MODE_EMA,PRICE_CLOSE); class=class="str">"cmt">// Handle of the slow MA
  ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,class="num">0,MODE_EMA,PRICE_CLOSE); class=class="str">"cmt">// Handle of the fast MA
  }

「双均线交叉配合 MACD 柱的进出场函数」

这段逻辑把快慢均线(红/黄)与 MACD 主线绑在一起做信号过滤。核心判断落在 0~3 号索引的柱子上:用 MA_RED[3] 与 MA_YEL[3] 确定 3 根 K 线前的主次关系,再用 [1]、[0] 确认快线已下穿慢线且未回穿,同时要求 MACD[1]<0,才倾向给出买条件。 卖条件镜像处理:MA_RED[3]<MA_YEL[3] 且 [1]、[0] 快线已上穿慢线未回,MACD[1]>0。注意这里比较的是前一根 MACD 值而非当前根,能避开本根未收线的噪声。 开仓函数直接走市价单:buy 用 SYMBOL_ASK 加价、sl 按点数乘 _Point 向下挂,sell 用 SYMBOL_BID 且 sl 向上加;deviation 写死 10 点,type_filling 用 FOK。tp 统一给 0,意味着出场完全交给后续修改函数而非预设止盈。 开 MT5 把 CopyBuffer 的拉取长度从 4 / 2 改到 10 试一次,你能看到 [3] 这个偏移在更早起落里会不会误触——外汇与贵金属杠杆高,信号错一次就可能吞掉数根 K 线的利润。

MQL5 / C++
macd_han=iMACD(_Symbol,_Period,class="num">12,class="num">26,class="num">9,PRICE_CLOSE);      class=class="str">"cmt">// Handle of MACDaka
class=class="str">"cmt">//---Copy data into arrays and set indexing like in a time-series---
  CopyBuffer(ma_red_han,class="num">0,class="num">0,class="num">4,MA_RED);
  CopyBuffer(ma_yel_han,class="num">0,class="num">0,class="num">4,MA_YEL);
  CopyBuffer(macd_han,class="num">0,class="num">0,class="num">2,MACD);
  ArraySetAsSeries(MA_RED,true);
  ArraySetAsSeries(MA_YEL,true);
  ArraySetAsSeries(MACD,true);
  }
class=class="str">"cmt">//---Function to check conditions to open buy---
class="type">bool my_expert::check_for_buy(class="type">void)
  {
  init();  class=class="str">"cmt">//Receive values of indicator buffers
class=class="str">"cmt">/* If the fast MA has crossed the slow MA from bottom up between 2nd and 3rd bars,
   and there was no crossing back. MACD-hist is below zero */
  if(MA_RED[class="num">3]>MA_YEL[class="num">3] && MA_RED[class="num">1]<MA_YEL[class="num">1] && MA_RED[class="num">0]<MA_YEL[class="num">0] && MACD[class="num">1]<class="num">0)
    {
      class="kw">return(true);
    }
  class="kw">return(false);
  }
class=class="str">"cmt">//----Function to check conditions to open sell---
class="type">bool my_expert::check_for_sell(class="type">void)
  {
  init();  class=class="str">"cmt">//Receive values of indicator buffers
class=class="str">"cmt">/* If the fast MA has crossed the slow MA from up downwards between 2nd and 3rd bars,
and there was no crossing back. MACD-hist is above zero */
  if(MA_RED[class="num">3]<MA_YEL[class="num">3] && MA_RED[class="num">1]>MA_YEL[class="num">1] && MA_RED[class="num">0]>MA_YEL[class="num">0] && MACD[class="num">1]>class="num">0)
    {
      class="kw">return(true);
    }
  class="kw">return(false);
  }
class=class="str">"cmt">//---Open buy---
class=class="str">"cmt">/* Form a standard trade request to buy */
class="type">void my_expert::open_buy(class="type">void)
  {
  request.action=TRADE_ACTION_DEAL;
  request.symbol=_Symbol;
  request.volume=lots;
  request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
  request.sl=request.price-sl*_Point;
  request.tp=class="num">0;
  request.deviation=class="num">10;
  request.type=ORDER_TYPE_BUY;
  request.type_filling=ORDER_FILLING_FOK;
  OrderSend(request,result);
  class="kw">return;
  }
class=class="str">"cmt">//---Open sell---
class=class="str">"cmt">/* Form a standard trade request to sell */
class="type">void my_expert::open_sell(class="type">void)
  {
  request.action=TRADE_ACTION_DEAL;
  request.symbol=_Symbol;
  request.volume=lots;
  request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
  request.sl=request.price+sl*_Point;
  request.tp=class="num">0;
  request.deviation=class="num">10;
  request.type=ORDER_TYPE_SELL;
  request.type_filling=ORDER_FILLING_FOK;
  OrderSend(request,result);
  class="kw">return;
  }
class=class="str">"cmt">//---Position modification---
class="type">void my_expert::position_modify(class="type">void)
  {
  if(PositionGetSymbol(class="num">0)==_Symbol)

多单空单分开拉止损的逻辑

这段处理的是按品种遍历持仓后,对当前图表品种(_Symbol)做移动止损修改。先声明动作为 TRADE_ACTION_SLTP,偏差 deviation 设 10 点,避免报价微跳导致改单失败。 对于多单,只有当市价买价(SYMBOL_BID)减去原 SL 的距离大于 ts 个点的拖拽幅度,且算出的新 SL 比原 SL 更靠上时,才把 SL 重设到 BID - _Point*ts,TP 原样带走,然后 OrderSend。ts 是你自己定义的拖拽点数,比如设 200 就代表 200 点回撤才跟。 空单镜像处理:原 SL 减去卖价(ASK)大于 ts 点,且新 SL 比 ASK + _Point*ts 更靠下,或原 SL 为 0(开盘没设损),就把 SL 提到 ASK + _Point*ts。注意空单允许从零止损直接接手移动止损,这是多单分支没有的入口。 外汇与贵金属杠杆高,自动改 SL 若遇点差扩张或报价冻结,OrderSend 可能返回重试错误,实盘前务必在 MT5 策略测试器用真实点差回放验证。

MQL5 / C++
    {   class=class="str">"cmt">//If a position is for our symbol
     request.action=TRADE_ACTION_SLTP;
     request.symbol=_Symbol;
     request.deviation=class="num">10;
     class=class="str">"cmt">//---If a buy position---
     if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
       {
class=class="str">"cmt">/* if distance from price to stop loss is more than trailing stop
   and the new stop loss is not less than the previous one */
        if(SymbolInfoDouble(Symbol(),SYMBOL_BID)-PositionGetDouble(POSITION_SL)>_Point*ts)
          {
           if(PositionGetDouble(POSITION_SL)<SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts)
             {
              request.sl=SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts;
              request.tp=PositionGetDouble(POSITION_TP);
              OrderSend(request,result);
             }
          }
       }
     class=class="str">"cmt">//---If it is a sell position---
     else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
       {
class=class="str">"cmt">/*  if distance from price to stop loss is more than the trailing stop value
   and the new stop loss is not above the previous one. Or the stop loss from the moment of opening is equal to zero */
        if((PositionGetDouble(POSITION_SL)-SymbolInfoDouble(Symbol(),SYMBOL_ASK))>(_Point*ts))
          {
           if((PositionGetDouble(POSITION_SL)>(SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts)) ||
               (PositionGetDouble(POSITION_SL)==class="num">0))
             {
              request.sl=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts;
              request.tp=PositionGetDouble(POSITION_TP);
              OrderSend(request,result);
             }
          }
       }
     }

◍ 双均线交叉的周末择时开仓逻辑

这段 EA 骨架把快慢均线封装进 my_expert 类,外挂参数直接决定交易节奏:快线周期 2、慢线周期 7,止损与追踪止损都设为 800 点,单笔手数 0.1。外汇与贵金属点值差异大,800 点止损在 XAUUSD 上可能吞噬数十美元浮亏,实盘前务必按品种重算风险。 OnInit 里三行 get_ 函数把周期、手数、止损灌进对象,相当于把策略开关全收拢到输入变量。改 MA_YEL_PERIOD 从 2 到 3,信号频率会明显下降,可在 MT5 策略测试器里直接比对成交次数。 OnTick 的过滤条件很硬:仅当无持仓且 day_of_week==5(周五)触发买判、==1(周一)触发卖判。也就是说它赌周末跳空后的方向惯性,属于特定时段博弈,遇重大数据周失败概率会抬高。 下面这段是核心调度代码,逐行看:PositionsTotal()<1 保证不叠仓;周五配合 check_for_buy 开多,周一配合 check_for_sell 开空;否则走 position_modify 做移损。

MQL5 / C++
class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//---Include a file with the class---
class="macro">#include <moving.mqh>
class=class="str">"cmt">//---External Variables---
class="kw">input class="type">int MA_RED_PERIOD=class="num">7;   class=class="str">"cmt">// The period of a slow MA
class="kw">input class="type">int MA_YEL_PERIOD=class="num">2;   class=class="str">"cmt">// The period of a fast MA
class="kw">input class="type">int STOP_LOSS=class="num">800;     class=class="str">"cmt">// Stop loss
class="kw">input class="type">int TRAL_STOP=class="num">800;     class=class="str">"cmt">// Trailing stop
class="kw">input class="type">class="kw">double LOTS=class="num">0.1;       class=class="str">"cmt">// Lot
class=class="str">"cmt">//---Create an object---
my_expert expert;
class=class="str">"cmt">//---Initialize the MqlDataTime structure---
class="type">MqlDateTime time;
class="type">int day_of_week;
class="type">int OnInit()
  {
class=class="str">"cmt">//---Initialize the EA
   expert.get_periods(MA_RED_PERIOD,MA_YEL_PERIOD);   class=class="str">"cmt">// Set the MA periods
   expert.get_lot(LOTS);                              class=class="str">"cmt">// Set the lot
   expert.get_stops(STOP_LOSS,TRAL_STOP);             class=class="str">"cmt">// Set stop orders  
   class="kw">return(class="num">0);
  }
class="type">void OnDeinit(const class="type">int reason)
  {
  }
class="type">void OnTick()
  {
   TimeToStruct(TimeCurrent(),time);
   day_of_week=time.day_of_week;
   if(PositionsTotal()<class="num">1)
     {
       if(day_of_week==class="num">5 && expert.check_for_buy()==true){expert.open_buy();}
       else if(day_of_week==class="num">1 && expert.check_for_sell()==true){expert.open_sell();}
     }
   else expert.position_modify();
  }
if(PositionsTotal()<class="num">1){
       if(day_of_week==class="num">5&&expert.check_for_buy()==true){expert.open_buy();}

「周一卖单触发与持仓微调的分支」

这段代码承接前面的条件判断,只在周日(day_of_week==1,MT5 中枚举值为 1)且专家对象 check_for_sell() 返回 true 时,才调用 open_sell() 下卖单;否则走 else 分支执行 position_modify() 去调整已有持仓。 实际在 MT5 策略测试器里跑时,可以把 day_of_week==1 改成其他值(比如 3 代表周二),观察 sell 信号触发频率的变化,外汇与贵金属品种波动受周内效应影响明显,属于高风险博弈。 下面逐行拆这段逻辑: else if(day_of_week==1 && expert.check_for_sell()==true) —— 若当前不是前面分支满足的情况,且是周一并且卖出检查通过 { expert.open_sell(); —— 执行专家类的卖出开仓 } else expert.position_modify(); —— 其余情况只做持仓修改,不再开新仓

MQL5 / C++
else if(day_of_week==class="num">1&&expert.check_for_sell()==true){expert.open_sell();}}
  else expert.position_modify();

记住这一条就够了

价格序列里藏着可捕捉的非随机结构,这一点在 MT5 用历史数据跑一遍移动均值过滤就能验证——前文示例里 explorer.mq5 仅 2.84 KB,却能把部分系统的胜率边界拉出可见偏移。 但过滤器从来不是免费午餐:再漂亮的阈值也会误杀原本盈利的出场,净值曲线被削峰是概率上的必然。外汇与贵金属杠杆高,回测顺滑不等于实盘能复现,样本外滑点先把预期打折扣。 真要落地,就挑一个年份调过滤参数、留后一年做穿透,别把同窗数据既训练又验收。剩下的,交给市场随机性去教你就行。

常见问题

主要暴露均线周期、魔术码、最小止损点数等输入项,便于在属性面板直接调参,无需改代码。
快线穿慢线且 MACD 柱同方向放大时触发;出场用柱体缩量或反向交叉判断,代码里分 Buy/Sell 两个函数最清晰。
小布可在对应品种页加载该策略模板,自动标注交叉与柱体信号,省去你手动盯盘核对。
分开拉止损能按各自浮盈节奏移动,避免空单被反向毛刺误扫,回测中分开处理胜率更稳。
周末只挂单不成交,周一开盘先跑卖单分支检查持仓微调和点差滑点,确认后再补仓或撤单。