作为技术分析工具的 MTF 指标·综合运用
🧩

作为技术分析工具的 MTF 指标·综合运用

(3/3)·当M1下单没空切H4看趋势,MTF组合指标如何替你守住三重滤网不漏信号

含代码示例 第 3/3 篇
在M1到M15刷单时还切去H1看方向,多数人会漏掉关键反转点。精神紧张下提前平仓或错失盈利信号,往往就源于较高周期信息不在手边。MTF综合指标不是装饰,是把多周期数据压进当前图表的刚需。

EA 输入参数与全局变量拆解

下面这段 MT5 专家顾问头部声明,把多指标共振策略的可调旋钮一次性摊开了。手数固定 0.1,止盈 150 点、止损 60 点,水平信号阈值 20 点,跟踪止损 25 点、偏移 5 点——这几个数字直接决定了仓位风险轮廓,外汇与贵金属杠杆环境下回撤可能放大,需自行压力测试。 信号层用了 MA(21)、MACD(12,26)、Stochastic(5,3) 三组默认值,周期偏日内波段。注意 InpTFreg 输入的是枚举值 2,对应更低一级周期做信号确认,和主图不是同一帧。 全局里 ExtTimeOut=10 限制了交易指令最小间隔为 10 秒,barsCalculated=3 意味着至少 3 根已成型 K 线才参与计算,避免实时毛刺。复制下面代码到 MQ5 文件头部即可直接编译验证。

MQL5 / C++
class=class="str">"cmt">//|                                                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string s0="class=class="str">"cmt">//--- 输入参数 手数+跟踪 ---//";
class="kw">input class="type">class="kw">double InpLots            =class="num">0.1; class=class="str">"cmt">// 手数
class="kw">input class="type">int    InpTakeProfit      =class="num">150; class=class="str">"cmt">// 获利 (in pips)
class="kw">input class="type">int    InpStopLoss        =class="num">60;  class=class="str">"cmt">// 止损 (in pips)
class="kw">input class="type">int    InpLevel_S         =class="num">20;  class=class="str">"cmt">// 水平信号
class="kw">input compTF InpTFreg           =class="num">2;
class="kw">input class="type">class="kw">string s1="class=class="str">"cmt">//--- MA 输入参数 ---//";
class="kw">input class="type">int                Signal_MA_PeriodMA       =class="num">21;                                                class=class="str">"cmt">// 平均周期数
class="kw">input class="type">class="kw">string s2="class=class="str">"cmt">//--- MACD 输入参数 ---//";
class="kw">input class="type">int                Signal_MACD_PeriodFast   =class="num">12;                                                class=class="str">"cmt">// 快速 EMA 周期数
class="kw">input class="type">int                Signal_MACD_PeriodSlow   =class="num">26;                                                class=class="str">"cmt">// 慢速 EMA 周期数
class="kw">input class="type">class="kw">string s3="class=class="str">"cmt">//--- Stochastic 输入参数 ---//";
class="kw">input class="type">int                Signal_Stoch_PeriodK     =class="num">5;                                                 class=class="str">"cmt">//  K-period
class="kw">input class="type">int                Signal_Stoch_PeriodD     =class="num">3;                                                 class=class="str">"cmt">//  D-period
class="kw">input class="type">class="kw">string s4="class=class="str">"cmt">//--- 跟踪止损输入参数  ---//";
class=class="str">"cmt">//--- 用于跟踪止损的输入参数
class="kw">input class="type">int    InpTrailingStop  =class="num">25;  class=class="str">"cmt">// 跟踪止损水平 (in pips)
class="kw">input class="type">int    InpOffset        =class="num">5;   class=class="str">"cmt">// 与价格之间的距离 (in pips)
class=class="str">"cmt">//---
class="type">int ExtTimeOut=class="num">10; class=class="str">"cmt">// 交易操作之间的时间间隔
class="type">int barsCalculated=class="num">3;
class="type">class="kw">datetime t=class="num">0;
class="type">class="kw">datetime time[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| AC 示例专家类                                                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSampleExpert
  {
class="kw">protected:

「EA 类里的核心变量与句柄声明」

写 MT5 专家顾问时,把交易对象、品种信息和指标句柄集中挂在类成员里,后面每个回调函数都能直接复用,不用反复传参。下面这段声明来自一个多指标共振的样例 EA,覆盖了报价精度、账户、持仓、订单以及 MACD / MA / Stochastic 三类指标的句柄与缓冲区。 double m_adjusted_point 存的是 3 位或 5 位报价的小数位修正值,欧美类品种常为 5 位、日元类常为 3 位,下单时算止损距离要靠它。CTrade、CSymbolInfo、CPositionInfo、CAccountInfo、COrderInfo 五个标准库对象一次性实例化,省去在 OnTick 里反复构造的开销。 指标部分用 mas_tf[3] 存三个时间框架,handle_MACD / handle_MA / handle_Stochastic 分别接指标句柄,对应的 macd_buff / ma_buff / stoch_buff 动态数组用来 CopyBuffer 取数。close/open/low/high 四个数组直接拉 K 线数据,macd_ind_0、macd_ind_1 取 MACD 最近两根值做金叉死叉判断,stoch_ind_0/1 同理。 跟踪止损与止盈止损也提前留了成员:m_traling_stop、m_take_profit、m_stop_losse(原文拼写如此)。公开方法只留了构造、析构、Init、Deinit,逻辑全塞进类内,开 MT5 新建 EA 时照这个结构挂成员,编译能直接过。外汇和贵金属杠杆高,这类 EA 实盘前务必在策略测试器跑历史数据验证。

MQL5 / C++
  class="type">class="kw">double              m_adjusted_point;                class=class="str">"cmt">// class="num">3 或者 class="num">5 位小数值
  CTrade              m_trade;                          class=class="str">"cmt">// 交易对象
  CSymbolInfo         m_symbol;                         class=class="str">"cmt">// 交易品种信息
  CPositionInfo       m_position;                       class=class="str">"cmt">// 交易仓位
  CAccountInfo        m_account;                        class=class="str">"cmt">// 账户信息
  COrderInfo          m_order;                          class=class="str">"cmt">// 订单信息
  class=class="str">"cmt">//--- 指标
  ENUM_TIMEFRAMES     mas_tf[class="num">3];                        class=class="str">"cmt">// 时间框架的数组
  class="type">int                 handle_MACD;                      class=class="str">"cmt">// MACD 指标句柄
  class="type">int                 handle_MA;                        class=class="str">"cmt">// MA 指标句柄
  class="type">int                 handle_Stochastic;                class=class="str">"cmt">// Stochastic 指标句柄
  class=class="str">"cmt">//--- 指标缓冲区
  class="type">class="kw">double              macd_buff[];                      class=class="str">"cmt">// MACD 指标主缓冲区
  class="type">class="kw">double              ma_buff[];                        class=class="str">"cmt">// MA 指标主缓冲区
  class="type">class="kw">double              stoch_buff[];                     class=class="str">"cmt">// Stochastic 指标主缓冲区
  class=class="str">"cmt">//---
  class="type">class="kw">double              close[];
  class="type">class="kw">double              open[];
  class="type">class="kw">double              low[];
  class="type">class="kw">double              high[];
  class=class="str">"cmt">//--- 要处理的指标数据
  class="type">class="kw">double              macd_ind_0;
  class="type">class="kw">double              macd_ind_1;
  class="type">class="kw">double              ma_ind;
  class="type">class="kw">double              stoch_ind_0;
  class="type">class="kw">double              stoch_ind_1;
  class="type">int                 level_stoch;
  class=class="str">"cmt">//--- 要处理的跟踪止损数据
  class="type">class="kw">double              m_traling_stop;
  class="type">class="kw">double              m_take_profit;
  class="type">class="kw">double              m_stop_losse;
class="kw">public:
                      CSampleExpert(class="type">void);
                      ~CSampleExpert(class="type">void);
  class="type">bool                Init(class="type">void);
  class="type">void                Deinit(class="type">void);

◍ EA 类骨架与构造函数初始化

这段声明把策略引擎的核心方法拆成了公开与保护两层。公开侧只留一个 Processing(void) 作为外部调用入口,其余建仓、改单、删单逻辑全部收在 protected 里,避免被外部误触。 从方法名能直接读出行为边界:LongOpened 与 ShortOpened 负责检查买卖触发条件,OpenBuyStop / OpenSellStop 挂止损单,OrderModifyBuyStop / OrderModifySellStop 做追踪改单,DellBuyStop / DellSellStop 清掉挂单。注意原文 DellSellStop 的注释写成了「删除止损卖出订单」,而 DellBuyStop 也标成「删除止损卖出订单」,这大概率是拷贝注释时的笔误,实际应按函数名区分买卖。 构造函数把关键句柄一次性置为 INVALID_HANDLE,MACD、Stochastic、MA 三大指标句柄若初始化失败就会保持这个值,后续 InitIndicators 里必须判空。m_adjusted_point 与各类 ind_0 / ind_1 缓冲区初值给 0,意味着首根 K 线前不假设任何指标偏移量。 [CODE] bool Processing(void); protected: bool InitCheckParameters(const int digits_adjust); bool Copy(void); bool InitIndicators(void); bool LongModified(void); bool ShortModified(void); bool LongOpened(void); // 检查买入条件 bool ShortOpened(void); // 检查卖出条件 bool OpenSellStop(void); // 设置止损卖出订单 bool OpenBuyStop(void); // 设置止损买入订单 bool OrderModifySellStop(void); // 修改止损卖出订单 bool OrderModifyBuyStop(void); // 修改止损买入订单 bool DellSellStop(void); // 删除止损卖出订单 bool DellBuyStop(void); // 删除止损卖出订单 }; //--- 全局的专家对象 CSampleExpert ExtExpert; CSampleExpert::CSampleExpert(void) : m_adjusted_point(0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(0), macd_ind_1(0), stoch_ind_0(0), stoch_ind_1(0), [/CODE] 逐行拆解:第1行公开方法 Processing 是 EA 每 tick 的主循环入口;protected 块里 InitCheckParameters 用 digits_adjust 校准小数点位差;Copy 负责把指标缓冲拷进本地数组;InitIndicators 申请指标句柄;Long/ShortModified 处理已持仓的追踪;Long/ShortOpened 是开仓信号判断;Open*Stop 与 OrderModify*Stop 管理挂单生命周期;Dell*Stop 做清理。全局对象 ExtExpert 在编译期即实例化,构造函数列表初始化把所有句柄与缓冲置零或无效值,杜绝野指针。 开 MT5 新建 EA 时,照这个分层把订单操作锁在 protected 内,能少踩很多外部重复调用的坑。外汇与贵金属杠杆高,这类自动挂单逻辑在滑点扩大时可能频繁改单失败,需实盘前用策略测试器跑至少 3 个月 tick 数据验证。

MQL5 / C++
class="type">bool              Processing(class="type">void);
class="kw">protected:
  class="type">bool              InitCheckParameters(class="kw">const class="type">int digits_adjust);
  class="type">bool              Copy(class="type">void);
  class="type">bool              InitIndicators(class="type">void);
  class="type">bool              LongModified(class="type">void);
  class="type">bool              ShortModified(class="type">void);
  class="type">bool              LongOpened(class="type">void);       class=class="str">"cmt">// 检查买入条件
  class="type">bool              ShortOpened(class="type">void);      class=class="str">"cmt">// 检查卖出条件
  class="type">bool              OpenSellStop(class="type">void);     class=class="str">"cmt">// 设置止损卖出订单
  class="type">bool              OpenBuyStop(class="type">void);      class=class="str">"cmt">// 设置止损买入订单
  class="type">bool              OrderModifySellStop(class="type">void); class=class="str">"cmt">// 修改止损卖出订单
  class="type">bool              OrderModifyBuyStop(class="type">void);  class=class="str">"cmt">// 修改止损买入订单
  class="type">bool              DellSellStop(class="type">void);     class=class="str">"cmt">// 删除止损卖出订单
  class="type">bool              DellBuyStop(class="type">void);      class=class="str">"cmt">// 删除止损卖出订单
  };
class=class="str">"cmt">//--- 全局的专家对象
CSampleExpert ExtExpert;
CSampleExpert::CSampleExpert(class="type">void) : m_adjusted_point(class="num">0),
                     handle_MACD(INVALID_HANDLE),
                     handle_Stochastic(INVALID_HANDLE),
                     handle_MA(INVALID_HANDLE),
                     macd_ind_0(class="num">0),
                     macd_ind_1(class="num">0),
                     stoch_ind_0(class="num">0),
                     stoch_ind_1(class="num">0),

EA 初始化里的点位换算与多周期映射

这段 Init 函数干的事很直接:把外部输入参数落成可交易的内部变量,并顺手把报价精度抹平。MT5 里 3 位或 5 位小数品种(如 XAUUSD 的 5 位报价)和 2 位、4 位品种的点值差 10 倍,代码用 digits_adjust 做修正——3/5 位时取 10,否则取 1,再乘 SymbolPoint 得到 m_adjusted_point。 止损、止盈、 trailing stop 都不是直接读输入值,而是各自乘上 m_adjusted_point。比如 InpTrailingStop 设为 30,在 5 位黄金上实际距离是 30×0.00001×10 = 0.00300,即 300 点。偏差也按 3×digits_adjust 设,5 位品种就是 30 点容差。 周期映射由 InpTFreg 控制,switch 里 0~3 对应四组 M1/M5/M15 到 H1/H4/D1 的三周期组合,写进 mas_tf 数组。改这个输入就能让均线逻辑在短中长三档周期间切换,不用动核心代码。 最后两步 InitCheckParameters 和 InitIndicators 任一返回 false,Init 就整体失败,EA 不会继续跑。开 MT5 加载后若面板不反应,先查这两个函数打印的日志。

MQL5 / C++
class="type">bool CSampleExpert::Init(class="type">void)
  {
class=class="str">"cmt">//--- 初始化一般信息
   m_symbol.Name(Symbol());                     class=class="str">"cmt">// 交易品种
   m_trade.SetExpertMagicNumber(EXPERT_MAGIC);  class=class="str">"cmt">// 幻数
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(Symbol());
class=class="str">"cmt">//--- 为3位或5位小数做调整
   class="type">int digits_adjust=class="num">1;
   if(m_symbol.Digits()==class="num">3 || m_symbol.Digits()==class="num">5)
      digits_adjust=class="num">10;
   m_adjusted_point=m_symbol.Point()*digits_adjust;
class=class="str">"cmt">//--- 设置用于交易的默认偏差 
   m_traling_stop     =InpTrailingStop*m_adjusted_point;
   m_take_profit      =InpTakeProfit*m_adjusted_point;
   m_stop_losse       =InpStopLoss*m_adjusted_point;
class=class="str">"cmt">//--- 设置交易默认偏差的点数
   m_trade.SetDeviationInPoints(class="num">3*digits_adjust);
class=class="str">"cmt">//---
   class="type">int x=InpTFreg;
   class="kw">switch(x)
     {
      case class="num">0: {mas_tf[class="num">0]=PERIOD_M1;mas_tf[class="num">1]=PERIOD_M5;mas_tf[class="num">2]=PERIOD_M15;}
      break;
      case class="num">1: {mas_tf[class="num">0]=PERIOD_M5;mas_tf[class="num">1]=PERIOD_M15;mas_tf[class="num">2]=PERIOD_H1;}
      break;
      case class="num">2: {mas_tf[class="num">0]=PERIOD_M15;mas_tf[class="num">1]=PERIOD_H1;mas_tf[class="num">2]=PERIOD_H4;}
      break;
      case class="num">3: {mas_tf[class="num">0]=PERIOD_H1;mas_tf[class="num">1]=PERIOD_H4;mas_tf[class="num">2]=PERIOD_D1;}
      break;
     }
class=class="str">"cmt">//---
   if(!InitCheckParameters(digits_adjust))
      class="kw">return(class="kw">false);
   if(!InitIndicators())
      class="kw">return(class="kw">false);
class=class="str">"cmt">//--- 结果
   class="kw">return(true);
  }

「参数校验与多周期指标句柄的初始化落点」

EA 启动阶段先卡死参数边界,比进场逻辑更该被盯紧。InitCheckParameters 里用 m_symbol.StopsLevel() 做底线:获利和跟踪止损乘上 digits_adjust 后若还小于平台止损级,直接返回 false 并 printf 报错,这意味着你的 InpTakeProfit 在 5 位数账号上填 10 点,若券商 StopsLevel 是 20,EA 连初始化都过不了。 手数检查分两道:先比对 LotsMin() 与 LotsMax() 的硬区间,再用 MathAbs(InpLots/LotsStep() - MathRound(...)) > 1e-10 验证是否踩中步长网格。很多市价单莫名被拒,根因就在手数不是 LotsStep 的整数倍,这套判定能提前拦掉。 InpTakeProfit <= InpTrailingStop 只打 warning 不阻断,属于逻辑隐患提醒而非致命错——实盘里跟踪止损吞掉获利的概率会显著抬升,建议手动改成 return(false)。外汇与贵金属杠杆高,参数越界可能瞬间放大回撤,校验层必须硬。 指标句柄走懒加载:handle_MACD / handle_MA / handle_Stochastic 仅在等于 INVALID_HANDLE 时才用 iCustom 去挂 MTF 系列自定义指标,路径如 "MTF\\Oscillators\\MTF_MACD",时间帧取 mas_tf[2]。任何句柄创建失败就 printf 并返回 false,EA 不会带着空指标硬跑。开 MT5 把这段贴进 CSampleExpert 类,改 mas_tf 数组就能验证多周期信号是否按预期挂载。

MQL5 / C++
class="type">bool CSampleExpert::InitCheckParameters(class="kw">const class="type">int digits_adjust)
  {
class=class="str">"cmt">//--- 检查源数据
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     {
       printf("获利必须大于 %d",m_symbol.StopsLevel());
       class="kw">return(class="kw">false);
     }
   if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel())
     {
       printf("跟踪止损必须大于 %d",m_symbol.StopsLevel());
       class="kw">return(class="kw">false);
     }
class=class="str">"cmt">//--- 检查手数值
   if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax())
     {
       printf("手数必须在范围 %f 和 %f 之间",m_symbol.LotsMin(),m_symbol.LotsMax());
       class="kw">return(class="kw">false);
     }
   if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>class="num">1.0E-10)
     {
       printf("数量没有满足手数步长 %f",m_symbol.LotsStep());
       class="kw">return(class="kw">false);
     }
class=class="str">"cmt">//--- warning
   if(InpTakeProfit<=InpTrailingStop)
     printf("警告:跟踪止损必须小于获利");
class=class="str">"cmt">//--- 结果
   class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 指标的初始化                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSampleExpert::InitIndicators(class="type">void)
  {
class=class="str">"cmt">//--- 创建 MACD 指标
   if(handle_MACD==INVALID_HANDLE)
     {
       class=class="str">"cmt">//---
       handle_MACD=iCustom(NULL,class="num">0,"MTF\\Oscillators\\MTF_MACD",mas_tf[class="num">2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow);
       class=class="str">"cmt">//---
       if(handle_MACD==INVALID_HANDLE)
         {
          printf("当创建 MACD 指标时出现错误。");
          class="kw">return(class="kw">false);
         }
     }
class=class="str">"cmt">//--- 创建 MA 指标
   if(handle_MA==INVALID_HANDLE)
     {
       class=class="str">"cmt">//---
       handle_MA=iCustom(NULL,class="num">0,"MTF\\Trend\\MA_MultiTF",mas_tf[class="num">2],Signal_MA_PeriodMA,class="num">0,MODE_EMA,PRICE_CLOSE);
       class=class="str">"cmt">//---
       if(handle_MA==INVALID_HANDLE)
         {
          printf("当创建 MA 指标时出现错误");
          class="kw">return(class="kw">false);
         }
     }
class=class="str">"cmt">//--- 创建 Stochastic 指标
   if(handle_Stochastic==INVALID_HANDLE)
     {
       class=class="str">"cmt">//---
       handle_Stochastic=iCustom(NULL,class="num">0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[class="num">1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD);
       class=class="str">"cmt">//---
       if(handle_Stochastic==INVALID_HANDLE)
         {

◍ 用双向函数把追踪止损写进EA

CSampleExpert 里把跟踪止损拆成了 LongModified 与 ShortModified 两个成员函数,逻辑对称但方向反着写:多单看 Bid 与开仓价的差值,空单看开仓价与 Ask 的差值,都要求超过 m_adjusted_point*InpTrailingStop 才触发。 以多单为例,触发后先用 NormalizeDouble 按品种小数位把止损算到 Bid - m_traling_stop,再判断原止损小于新值或原为 0.0 才允许改。m_trade.PositionModify(Symbol(),sl,tp) 真正下单修改,成功就打印日志,失败则把 ResultComment 和 SL/TP 数值一并输出,方便在 MT5 终端查错。 空单那段几乎镜像:sl = Ask + m_traling_stop,且只有当持仓止损大于新止损或原止损为 0 时才改,避免把止损往亏损方向拉。外汇与贵金属杠杆高,追踪参数 InpTrailingStop 设太小可能频繁改单触发点差损耗,倾向先在策略测试器跑几组不同点数再上实盘。

MQL5 / C++
class="type">bool CSampleExpert::LongModified(class="type">void)
  {
  class="type">bool res=class="kw">false;
class=class="str">"cmt">//--- 检查跟踪止损
  if(InpTrailingStop>class="num">0)
    {
      if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop)
        {
         class="type">class="kw">double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits());
         class="type">class="kw">double tp=m_position.TakeProfit();
         if(m_position.StopLoss()<sl || m_position.StopLoss()==class="num">0.0)
           {
            class=class="str">"cmt">//--- 修改仓位
            if(m_trade.PositionModify(Symbol(),sl,tp))
               printf("将修改 %s 的买入仓位",Symbol());
            else
              {
               printf("修改 %s 仓位时出错: &class="macro">#x27;%s&class="macro">#x27;",Symbol(),m_trade.ResultComment());
               printf("修改参数 : SL=%f,TP=%f",sl,tp);
              }
            class=class="str">"cmt">//--- 已经修改,并且需要退出EA交易
            res=true;
           }
        }
    }
class=class="str">"cmt">//--- 结果
   class="kw">return(res);
  }

class="type">bool CSampleExpert::ShortModified(class="type">void)
  {
  class="type">bool  res=class="kw">false;
class=class="str">"cmt">//--- 检查跟踪止损
  if(InpTrailingStop>class="num">0)
    {
      if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop))
        {
         class="type">class="kw">double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits());
         class="type">class="kw">double tp=m_position.TakeProfit();
         if(m_position.StopLoss()>sl || m_position.StopLoss()==class="num">0.0)
           {
            class=class="str">"cmt">//--- 修改仓位
            if(m_trade.PositionModify(Symbol(),sl,tp))
               printf("将要修改 %s 的卖出仓位",Symbol());
            else
              {

用 BuyStop 挂单承接多头触发

当 LongOpened() 返回 true,说明随机指标上穿阈值、MACD 柱走高且均线位于前一根 K 线的高低点下方,此时倾向在更高价位埋 BuyStop 而不是市价追。 代码里先置 res=true 表示已处理,再填充 MqlTradeRequest 结构体。request.deviation 写死为 5 点,意味着成交允许偏离挂单价最多 5 个点,外汇与贵金属跳空时可能直接吃不到单。 request.volume 取外部参数 InpLots,request.magic 用 EXPERT_MAGIC 区分本 EA 订单;offset 由 InpOffset 控制挂单与市价距离,调小它挂单更贴近现价、被触发的概率更高但假突破也更多。 实际验证时把 InpOffset 从默认改到 10~20 点,回看 EURUSD 的 M15 上 BuyStop 触发率与止损频次,再决定阈值。

MQL5 / C++
class="type">bool CSampleExpert::OpenBuyStop(class="type">void)
  {
   class="type">bool res=class="kw">false;
   class="type">class="kw">double tp=class="num">0,sl=class="num">0;
class=class="str">"cmt">//---
   if(LongOpened())
     {
      res=true;
      class=class="str">"cmt">//--- 声明和初始化交易请求和结果
      class="type">MqlTradeRequest request={class="num">0};
      class="type">MqlTradeResult   result={class="num">0};
      class=class="str">"cmt">//--- 设置挂单的参数
      request.action   =TRADE_ACTION_PENDING;                      class=class="str">"cmt">// 交易操作类型
      request.symbol   =Symbol();                                  class=class="str">"cmt">// 交易品种
      request.deviation=class="num">5;                                          class=class="str">"cmt">// 允许的价格偏差
      request.volume   =InpLots;                                    class=class="str">"cmt">// 交易手数
      request.magic    =EXPERT_MAGIC;                               class=class="str">"cmt">// 订单幻数
      class="type">class="kw">double offset=InpOffset;                                      class=class="str">"cmt">// 当前价格距离订单的点差

「挂单触发价与止损止盈的规范化拼接」

在 MT5 里挂 BUY_STOP 时,触发价不能直接拿前一根 K 线高点硬加,必须先用 SymbolInfoDouble(_Symbol,SYMBOL_POINT) 取点值、用 SymbolInfoInteger(_Symbol,SYMBOL_DIGITS) 取报价精度。否则在 XAUUSD 这种 digits=3 或 EURUSD digits=5 的品种上,价格尾差可能让 OrderSend 直接返回 4756(无效价格)报错。 下面这段把 high[1] 加偏移后做了 NormalizeDouble,再顺手把 tp/sl 一起规范化写进 request 结构。注意 sl/tp 用的是 _Digits 而非局部 digits 变量,两者在多数品种一致,但跨品种 EA 里混用可能埋雷。 double price; // 订单触发价格 double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // 点的大小 int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // 小数点位数 (digits) //--- 操作类型 request.type=ORDER_TYPE_BUY_STOP; // 订单类型 price=high[1]+offset*m_adjusted_point; // 开启价格 request.price=NormalizeDouble(price,digits); // 规范化的开启价格 tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); // 把新的获利值加到结构中 //--- 发送请求 if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // 如果无法发送请求,输出错误代码 //--- 关于操作的信息 printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- 退出 EA } //--- 结果 return(res); } 卖出侧的条件判断另写了一个 ShortOpened:当 stoch_ind_1 大于 (100-InpLevel_S) 且 stoch_ind_0 下穿该阈值,同时 macd_ind_1 小于 macd_ind_0、均线 ma_ind 高于前一根收盘价与开盘价时,返回 true。外汇与贵金属杠杆高,这类共振信号只是提高触发概率,实盘仍可能连续止损,建议先在策略测试器跑 2020—2023 年数据验证。 OpenSellStop 函数开头只声明了 tp/sl 初值 0,真正的 SELL_STOP 挂单价算法和 BUY_STOP 对称,改 request.type 为 ORDER_TYPE_SELL_STOP 并把 price 锚在 low[1] 下方即可,开 MT5 把这段补完就能跑。

MQL5 / C++
class="type">class="kw">double price;                      class=class="str">"cmt">// 订单触发价格
class="type">class="kw">double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);      class=class="str">"cmt">// 点的大小
class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);     class=class="str">"cmt">// 小数点位数 (digits)
class=class="str">"cmt">//--- 操作类型
request.type=ORDER_TYPE_BUY_STOP;                  class=class="str">"cmt">// 订单类型
price=high[class="num">1]+offset*m_adjusted_point;                 class=class="str">"cmt">// 开启价格
request.price=NormalizeDouble(price,digits);               class=class="str">"cmt">// 规范化的开启价格
tp=price+m_take_profit;
sl=price-m_stop_losse;
request.sl      =NormalizeDouble(sl,_Digits);          class=class="str">"cmt">// 把新的止损值加到结构中
request.tp      =NormalizeDouble(tp,_Digits);          class=class="str">"cmt">// 把新的获利值加到结构中
class=class="str">"cmt">//--- 发送请求
if(!OrderSend(request,result))
  {res=class="kw">false;printf("OrderSend error %d",GetLastError());}     class=class="str">"cmt">// 如果无法发送请求,输出错误代码
class=class="str">"cmt">//--- 关于操作的信息
printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
class=class="str">"cmt">//--- 退出 EA
  }
class=class="str">"cmt">//--- 结果
  class="kw">return(res);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 检查建立卖出仓位的条件                                                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSampleExpert::ShortOpened(class="type">void)
  {
   class="type">bool res=class="kw">false;
   level_stoch=class="num">100-InpLevel_S;
class=class="str">"cmt">//--- 检查是否可以建立卖出仓位 (SELL) 
   if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[class="num">1] && ma_ind>open[class="num">1])
     {
      class=class="str">"cmt">//--- 退出 EA
      res=true;
     }
class=class="str">"cmt">//--- 结果
   class="kw">return(res);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 开启一个止损卖出挂单                                                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSampleExpert::OpenSellStop(class="type">void)
  {
   class="type">bool res=class="kw">false;
   class="type">class="kw">double tp=class="num">0,sl=class="num">0;

◍ 空单持仓后如何挂 Sell Stop 追空

当 ShortOpened() 返回 true,说明当前已持有空单,这时程序会尝试在更低价位挂一张 SELL_STOP pending 单,用来在破位下行时自动加仓或接力。外汇与贵金属杠杆高,这类追空挂单若遇反向跳空可能扩大浮亏,实盘前务必在 MT5 策略测试器里跑一遍。 下面这段逻辑先把交易请求和结果结构体清零,再逐项填参数:action 设为 TRADE_ACTION_PENDING 表示发挂单;symbol 取当前品种;deviation 给 5 点允许偏差;volume 用外部输入手数 InpLots;magic 用专家顾问幻数做订单标记。 挂单位置由前一根低点 low[1] 减去 offset 个 adjusted_point 算出,offset 就是 InpOffset 控制的距离。订单类型写死 ORDER_TYPE_SELL_STOP,价格经 NormalizeDouble 按品种小数位规范化,避免报价精度不符被拒。 止损止盈以挂单价格为中心:tp = price - m_take_profit,sl = price + m_stop_loss,同样规范化后写回 request。最后 OrderSend 发出请求,失败则 res 置 false 并打印 GetLastError 代码,方便你直接查 MT5 终端的报错原因。

MQL5 / C++
if(ShortOpened())
  {
   res=true;
   class=class="str">"cmt">//--- 声明和初始化交易请求和结果
   class="type">MqlTradeRequest request={class="num">0};
   class="type">MqlTradeResult   result={class="num">0};
   class=class="str">"cmt">//--- 设置挂单的参数
   request.action   =TRADE_ACTION_PENDING;                   class=class="str">"cmt">// 交易操作类型
   request.symbol   =Symbol();                               class=class="str">"cmt">// 交易品种
   request.deviation=class="num">5;                                      class=class="str">"cmt">// 允许的价格偏差
   request.volume=InpLots;                                   class=class="str">"cmt">// 交易手数
   request.magic=EXPERT_MAGIC;                               class=class="str">"cmt">// 订单幻数
   class="type">int offset=InpOffset;                                     class=class="str">"cmt">// 当前价格距离挂单的点数距离
   class="type">class="kw">double price;                                             class=class="str">"cmt">// 订单触发价格
   class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">// 小数点位数 (digits)
   request.type=ORDER_TYPE_SELL_STOP;                        class=class="str">"cmt">// 订单类型
   price=low[class="num">1]-offset*m_adjusted_point;                     class=class="str">"cmt">// 开启价格
   request.price=NormalizeDouble(price,digits);              class=class="str">"cmt">// 规范化的开启价格
   tp=price-m_take_profit;
   sl=price+m_stop_losse;
   request.sl      =NormalizeDouble(sl,_Digits);             class=class="str">"cmt">// 把新的止损值加到结构中
   request.tp      =NormalizeDouble(tp,_Digits);             class=class="str">"cmt">// 把新的获利值加到结构中
   class=class="str">"cmt">//--- 发送请求
   if(!OrderSend(request,result))
     {res=class="kw">false;printf("OrderSend error %d",GetLastError());} class=class="str">"cmt">// 如果无法发送请求,输出错误代码
   class=class="str">"cmt">//--- 关于操作的信息

Processing 函数里的数据同步与持仓巡检

EA 主循环里真正干活的 Processing() 先不做任何交易判断,而是把行情和指标缓冲刷到本地。m_symbol.RefreshRates() 失败就直接返 false,避免用过期报价算信号;之后对 Stochastic、MACD、MA 三个句柄都做 BarsCalculated 小于 barsCalculated 或 CopyBuffer 拿不到足量大棒的拦截,任一不满足即退出。 数据落袋后,代码把缓冲下标 1 和 0 分别赋给 macd_ind_0/1、ma_ind、stoch_ind_0/1,相当于用 [1] 存上一根、[0] 存最新一根,后续逻辑只碰这几个变量,省掉反复查数组的开销。这里 barsCalculated 若设成 3,就要求至少 3 根历史棒完成计算才放行。 持仓侧先 m_position.Select(Symbol()) 并尝试 PositionsTotal()>0 且跟踪止损开关 m_traling_stop 非零,才进多空分支调用 LongModified() 或 ShortModified(),改单成功直接 return(true) 交还控制权。 挂单处理另起 for(int i=0;i<OrdersTotal();i++) 循环,逐个选定挂单取 ticket,为后面按单号改止损或激活条件单做准备。外汇与贵金属杠杆高,这类自动改仓逻辑在滑点扩大时可能失效,实盘前务必在 MT5 策略测试器用历史数据跑通。

MQL5 / C++
class="type">bool CSampleExpert::Processing(class="type">void)
  {
  if(!m_symbol.RefreshRates())
    class="kw">return(class="kw">false);
  if(BarsCalculated(handle_Stochastic)<barsCalculated)
    class="kw">return(class="kw">false);
  if(CopyBuffer(handle_Stochastic,class="num">0,class="num">0,barsCalculated,stoch_buff)!=barsCalculated)
    class="kw">return(class="kw">false);
  if(BarsCalculated(handle_MACD)<barsCalculated)
    class="kw">return(class="kw">false);
  if(CopyBuffer(handle_MACD,class="num">0,class="num">0,barsCalculated,macd_buff)!=barsCalculated)
    class="kw">return(class="kw">false);
  if(BarsCalculated(handle_MA)<barsCalculated)
    class="kw">return(class="kw">false);
  if(CopyBuffer(handle_MA,class="num">0,class="num">0,barsCalculated,ma_buff)!=barsCalculated)
    class="kw">return(class="kw">false);
  if(!Copy())class="kw">return(class="kw">false);
  macd_ind_0   = macd_buff[class="num">1];
  macd_ind_1   = macd_buff[class="num">0];
  ma_ind       = ma_buff[class="num">1];
  stoch_ind_0  = stoch_buff[class="num">1];
  stoch_ind_1  = stoch_buff[class="num">0];
  class="type">bool bord=class="kw">false,sord=class="kw">false;
  class="type">class="kw">ulong ticket;
  if(m_position.Select(Symbol()) && PositionsTotal()>class="num">0 && m_traling_stop!=class="num">0)
    {
    if(m_position.PositionType()==POSITION_TYPE_BUY)
      {
      bord=true;
      if(LongModified())
        class="kw">return(true);
      }
    else
      {
      sord=true;
      if(ShortModified())
        class="kw">return(true);
      }
    }
  for(class="type">int i=class="num">0;i<OrdersTotal();i++)
    {

「挂单遍历与止损单动态重挂」

EA 在每根 K 线刷新时先扫一遍终端所有挂单,用 OrderGetTicket(i) 按索引取出单号,再判断类型。若是 BUY_STOP 就置 bord=true 并调 OrderModifyBuyStop(),SELL_STOP 则置 sord=true 并调 OrderModifySellStop(),这一步决定已有挂单要不要跟着价格挪位置。 若全场扫完 sord 仍为 false,说明没有止损卖单,就 OpenSellStop() 新建;bord 为 false 则 OpenBuyStop() 新建,建完立刻 return(true) 中止本次处理。两个标志都已有就 return(false),不重复刷单。 修改函数里先抓 SYMBOL_DIGITS 决定报价精度,offset 来自外部参数 InpOffset,代表挂单价离现价的点数距离。下面这段是 SellStop 修改函数的头部,重点在按 OrdersTotal() 倒序遍历、用 OrderGetInteger(ORDER_MAGIC) 核对幻数,避免动到手动单或其他 EA 的单。 外汇与贵金属杠杆高,挂单被滑点吞掉或重复触发都可能发生,任何重挂逻辑都建议在策略测试器用真实点差回测确认。

MQL5 / C++
ticket=OrderGetTicket(i);
class=class="str">"cmt">// 处理止损买入订单
if(m_order.OrderType()==ORDER_TYPE_BUY_STOP)
  {
   class=class="str">"cmt">// 设置标志,指出有止损买入订单
   bord=true;
   class=class="str">"cmt">//--- 正确进入市场是很重要的,必要时移动订单 
   if(bord)OrderModifyBuyStop(); class=class="str">"cmt">// 修改 BUYSTOP 订单
   }
class=class="str">"cmt">// 处理止损卖出订单
if(m_order.OrderType()==ORDER_TYPE_SELL_STOP)
  {
   class=class="str">"cmt">// 设置标志指出有止损卖出订单
   sord=true;
   class=class="str">"cmt">//--- 正确进入市场是很重要的,必要时移动订单 
   if(sord)OrderModifySellStop(); class=class="str">"cmt">// 修改 SELLSTOP 订单
   }
  }
class=class="str">"cmt">//--- 如果没有订单,就进行设置 --------------------------------+
  if(!sord)if(OpenSellStop()){sord=true;class="kw">return(true);}
  if(!bord)if(OpenBuyStop()){bord=true;class="kw">return(true);}
class=class="str">"cmt">//--- 不进行仓位处理而退出
  class="kw">return(class="kw">false);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|修改之前设置的止损卖出挂单的参数                                                                                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSampleExpert::OrderModifySellStop(class="type">void)
  {
  class="type">bool res=true;
  class="type">class="kw">ulong ticket;
  class="type">class="kw">double tp=class="num">0,sl=class="num">0;
  class="type">class="kw">double offset=InpOffset;                                                                       class=class="str">"cmt">// 当前价格距离订单的点差
  class="type">class="kw">double price;                                                                                  class=class="str">"cmt">// 订单触发价格
  class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);                                      class=class="str">"cmt">// 小数点位数 (digits)
                                                                                                 class=class="str">"cmt">// 声明并初始化交易请求和结果
  class="type">MqlTradeRequest request={class="num">0};
  class="type">MqlTradeResult   result={class="num">0};
  class="type">int total=OrdersTotal(); class=class="str">"cmt">// 挂单的数量
class=class="str">"cmt">//--- 迭代所有设置的挂单
  for(class="type">int i=total-class="num">1; i>=class="num">0; i--)
   {
   class=class="str">"cmt">// 选择每个挂单并取得它的单号
   ticket=OrderGetTicket(i);
   class=class="str">"cmt">// 处理止损卖出订单
   if(m_order.OrderType()==ORDER_TYPE_SELL_STOP)
     {
      class="type">class="kw">ulong   magic=OrderGetInteger(ORDER_MAGIC);                                                class=class="str">"cmt">// 订单幻数

◍ 挂单改价时的双低点过滤

在遍历持仓或挂单时,先用幻数 EXPERT_MAGIC 筛出本 EA 自己下的单,避免误改其他策略的仓位。这一步是后续所有改单逻辑的前提,没匹配到就直接跳过。 价格计算以 low[1] 为基准,减去 offset*m_adjusted_point 得到新的挂单触发价。注意这里要求 price 必须大于原开仓价 m_order.PriceOpen(),且 low[1] > low[2],也就是最近一根 K 线低点高于前一根——双条件同时满足才允许下移挂单,倾向过滤掉弱势延续的假突破。 请求结构里 action 设为 TRADE_ACTION_MODIFY,填入 OrderGetTicket(i) 取到的单号与 Symbol() 当前品种,deviation 用 InpOffset 控制重价容差。SL/TP 按 price±固定点数重算后 NormalizeDouble 到 _Digits 精度,最后 OrderSend 发出;若返回失败则 printf 打错误码并置 res=true 标记未处理完。 实盘里把 offset 和 m_stop_losse / m_take_profit 调小一点,能在 5 分钟图上让改单触发更密集,但外汇与贵金属杠杆高,频繁改单可能放大滑点风险,建议先开 MT5 策略测试器跑一遍观察 retcode 分布。

MQL5 / C++
if(magic==EXPERT_MAGIC)
  {
   price=low[class="num">1]-offset*m_adjusted_point;                      class=class="str">"cmt">// 开启价格
   if(price>m_order.PriceOpen()) class=class="str">"cmt">// 检查条件
   if(low[class="num">1]>low[class="num">2]) class=class="str">"cmt">// 检查条件
     {
      request.action=TRADE_ACTION_MODIFY;                     class=class="str">"cmt">// 交易操作类型
      request.order = OrderGetTicket(i);                      class=class="str">"cmt">// 订单单号
      request.symbol   =Symbol();                             class=class="str">"cmt">// 交易品种
      request.deviation=InpOffset;                            class=class="str">"cmt">// 允许的价格偏移
      price=low[class="num">1]-offset*m_adjusted_point;                   class=class="str">"cmt">// 开启价格
      request.price=NormalizeDouble(price,digits);            class=class="str">"cmt">// 规范化的开启价格
      tp=price-m_take_profit;
      sl=price+m_stop_losse;
      request.sl       =NormalizeDouble(sl,_Digits);          class=class="str">"cmt">// 把新的止损值加到结构中
      request.tp       =NormalizeDouble(tp,_Digits);          class=class="str">"cmt">// 把新的获利值加到结构中
      class=class="str">"cmt">//--- 发送请求
      if(!OrderSend(request,result))
        {
         class=class="str">"cmt">// 设置标志指出止损卖出订单没有被删除
         res=true; printf("OrderSend 错误 %d",GetLastError());
        }  class=class="str">"cmt">// 如果无法发送请求,输出错误代码
      class=class="str">"cmt">//--- 关于操作的信息   
      printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
     }
   }
class="kw">return(res);

动态重挂 BuyStop 的触发价

在 EA 运行期间,已挂出的止损买入单若一直不触发,价格结构可能已经偏移,原挂单价就容易悬在远离当前波动区的位置。这段逻辑做的就是按最新 K 线高点加偏移,把老挂单的触发价往下拉到更贴近市场的位置。 核心判断只有一句:当计算出的新价 price 小于原挂单开盘价 m_order.PriceOpen() 时才改单,避免把单子往远离市场的方向推。外汇与贵金属杠杆高,重挂操作若 offset 设太小,可能在快速行情中连续触发修改请求,增加服务器拒绝概率。 循环从 OrdersTotal()-1 倒序遍历,只挑 ORDER_TYPE_BUY_STOP 且幻数等于 EXPERT_MAGIC 的订单处理,防止动到手动单或其他 EA 的单。下面代码拆开看,每一行都在填 MqlTradeRequest 的修改字段。

MQL5 / C++
class="type">bool CSampleExpert::OrderModifyBuyStop(class="type">void)
  {
   class="type">bool res=true;
   class="type">class="kw">ulong ticket;
   class="type">class="kw">double tp=class="num">0,sl=class="num">0;
   class="type">class="kw">double offset=InpOffset;                                           class=class="str">"cmt">// 当前价格距离订单的点差
   class="type">class="kw">double price;                                                      class=class="str">"cmt">// 订单触发价格
   class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);          class=class="str">"cmt">// 小数点位数 (digits)
                                                                      class=class="str">"cmt">//--- 声明和初始化交易请求和结果
   class="type">MqlTradeRequest request={class="num">0};
   class="type">MqlTradeResult   result={class="num">0};
   class="type">int total=OrdersTotal(); class=class="str">"cmt">// 挂单的数量
class=class="str">"cmt">//--- 迭代所有设置的挂单
   for(class="type">int i=total-class="num">1; i>=class="num">0; i--)
     {
      class=class="str">"cmt">// 选择每个挂单并取得它的单号
      ticket=OrderGetTicket(i);
      class=class="str">"cmt">// 处理止损买入订单
      if(m_order.OrderType()==ORDER_TYPE_BUY_STOP)
        {
         class="type">class="kw">ulong   magic=OrderGetInteger(ORDER_MAGIC);                  class=class="str">"cmt">// 订单幻数
         class=class="str">"cmt">//--- 如果幻数匹配
         if(magic==EXPERT_MAGIC)
           {
            price=high[class="num">1]+offset*m_adjusted_point;                    class=class="str">"cmt">// 开仓价格
            if(price<m_order.PriceOpen()) class=class="str">"cmt">// check the conditions
              {
               request.action=TRADE_ACTION_MODIFY;                    class=class="str">"cmt">// 交易操作类型
               request.symbol   =Symbol();                            class=class="str">"cmt">// 交易品种
               request.action=TRADE_ACTION_MODIFY;                    class=class="str">"cmt">// 交易操作类型
               request.order = OrderGetTicket(i);                     class=class="str">"cmt">// 订单单号

「下单请求的结构填充与发送」

挂单逻辑走到实际出手这一步,核心是把 MQL5 的 MqlTradeRequest 结构逐项填实,再交给 OrderSend 去跑。下面这段截取自一个止损买入挂单的改写函数,可以看到价格、止损、止盈都先用 NormalizeDouble 按品种精度规整,避免跨品种时点值位数不对导致 reject。 request.symbol 直接取 Symbol() 锁定当前图表品种;deviation 用外部输入 InpOffset 控制重价容忍,滑点大的贵金属盘口这个值设太小容易吃不到单。price 用 NormalizeDouble(price,digits) 规范后,tp、sl 以 price 为锚加减类成员变量 m_take_profit / m_stop_losse 算出,再各自 NormalizeDouble 到 _Digits 精度写回 request。 发送环节若 OrderSend 返回 false,代码把 res 置 true 标记“挂单未删”,并用 printf 打出 GetLastError 的错误码——这一步在 MT5 策略测试器日志里排错最直接,比如 10030 就是价格偏离超限。成功与否都会 printf 出 retcode、deal、order 三个 uint64 字段,方便对照交易池回执。 OnInit 里只做 ExtExpert.Init() 成败判断,失败返 INIT_FAILED 阻止 EA 加载;OnTick 用 static datetime limit_time 做节流,Bars(Symbol(),Period())>barsCalculated 才进处理,避免每 tick 都重算。外汇与贵金属杠杆高,挂单未成交前也占用保证金敞口,验证时建议先开模拟盘跑这套节流逻辑。

MQL5 / C++
   request.symbol   =Symbol();                                           class=class="str">"cmt">// 交易品种
   request.deviation=InpOffset;                                            class=class="str">"cmt">// 允许的价格偏移
   class=class="str">"cmt">//--- set the price level, take profit and stop loss 
   request.price=NormalizeDouble(price,digits);                            class=class="str">"cmt">// 规范化的开启价格
   tp=price+m_take_profit;
   sl=price-m_stop_losse;
   request.sl       =NormalizeDouble(sl,_Digits);                          class=class="str">"cmt">// 把新的止损值加到结构中
   request.tp       =NormalizeDouble(tp,_Digits);                          class=class="str">"cmt">// 把新的获利值加到结构中
   class=class="str">"cmt">//--- 发送请求
   if(!OrderSend(request,result))
     {
      class=class="str">"cmt">// 设置标志指出止损买入挂单没有被删除
      res=true;
      printf("OrderSend 错误 %d",GetLastError());
     } class=class="str">"cmt">// 如果无法发送请求,输出错误代码
   class=class="str">"cmt">//--- 关于操作的信息  
   printf("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   }
     }
   }
 }
 class="kw">return(res);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA交易初始化函数                                                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit(class="type">void)
  {
class=class="str">"cmt">//--- 创建所有所需的对象
   if(!ExtExpert.Init())
      class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- ok
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 分时处理函数                                                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick(class="type">void)
  {
   class="kw">static class="type">class="kw">datetime limit_time=class="num">0; class=class="str">"cmt">// 上次的处理时间 + 超时
class=class="str">"cmt">//--- 不处理直到超时
   if(TimeCurrent()>=limit_time)
     {
      class=class="str">"cmt">//--- 检查数据
      if(Bars(Symbol(),Period())>barsCalculated)
       {

◍ 跨周期抓取三根 K 线的复制逻辑

EA 在 Copy 方法里固定拉取最近 3 根柱形的 OHLC 数据,copied 写死为 3,意味着你只拿到当前与前面两根的快照,回看深度到此为止。 数组先用 ArrayResize 按 copied 撑开,再统一 ArraySetAsSeries(...,true) 把索引方向翻成时序倒序,这样 high[0] 永远是最新一根最高价,写条件判断时不用反向数下标。 注意最高价和最低价取自 mas_tf[0] 的周期,而开盘、收盘却来自 mas_tf[2]——两套时间框架混用,若这两个下标指向不同周期,你的信号其实是跨周期拼接的,外汇与贵金属这种高波动品种下错位可能直接引发误触发。 四段 CopyHigh / CopyLow / CopyClose / CopyOpen 任一返回小于 0 就打印错误并 return false,实盘里若频繁看到“未能复制”日志,优先查 mas_tf 下标越界或对应图表周期没打开。

MQL5 / C++
class="type">bool CSampleExpert::Copy(class="type">void)
  {
class=class="str">"cmt">//--- 要复制的数量 
   class="type">int copied=class="num">3;
class=class="str">"cmt">//---
   ArrayResize(high,copied);
   ArrayResize(low,copied);
   ArrayResize(close,copied);
   ArrayResize(open,copied);
class=class="str">"cmt">//+------- 设置数组索引方向 ---------------------+
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(open,true);
class=class="str">"cmt">//--- 复制柱形价格 
   if(CopyHigh(NULL,mas_tf[class="num">0],class="num">0,copied,high)<class="num">0)
     {printf("未能复制最高价",GetLastError());class="kw">return(class="kw">false);}
class=class="str">"cmt">//---
   if(CopyLow(NULL,mas_tf[class="num">0],class="num">0,copied,low)<class="num">0)
     {printf("未能复制最低价",GetLastError());class="kw">return(class="kw">false);}
class=class="str">"cmt">//---
   if(CopyClose(NULL,mas_tf[class="num">2],class="num">0,copied,close)<class="num">0)
     {printf("未能复制收盘价",GetLastError());class="kw">return(class="kw">false);}
class=class="str">"cmt">//---
   if(CopyOpen(NULL,mas_tf[class="num">2],class="num">0,copied,open)<class="num">0)
     {printf("未能复制开盘价",GetLastError());class="kw">return(class="kw">false);}
class=class="str">"cmt">//---
   class="kw">return(true);
  }

多周期指标的延伸空间

MQL5 原生并没有给软件模块作者开放直接生成多时间框架(MTF)算法的接口,但这类指标在实战里仍有价值:同一品种在不同 TF 上同步读市场状态,能压低策略测试器的空转、让平滑表现更干净。 附件里那批 MTF 指标已经铺开了路——趋势类有 MA_MultiPeriod、MA_MultiTF、MTF_BB 等 6 个,振荡类覆盖 MTF_RSI、MTF_MACD、MTF_Stochastic 等 8 个,全部走 MQL5\Indicators\MTF\ 路径,复制进 MT5 就能挂。 示例代码难免有坑。有用户反馈 Triple Screen Trading System 1.0 编译报 'cannot convert 0 to enum ENUM_TRADE_REQUEST_ACTIONS',社区给的解法是把那几个 0 直接删掉即可过编;另有人在 M1 切到 M5 时碰见 MA_MultiTF 报 'Not All data of ExtHandle1 is calculated (-1 bars). Error4806',先挂个别的指标再回看往往就正常。 外汇与贵金属杠杆高、滑点狠,MTF 只是把视角叠厚,不替你过滤风险。拿下面这段日志当线索,去 MT5 终端把对应指标重跑一遍,比看结论更实在。

MQL5 / C++
class="type">MqlTradeRequest request = {};
class="type">MqlTradeResult  result  = {};
class="num">2023.11.class="num">04 class="num">07:class="num">31:class="num">15.541 MA_MultiTF(EURUSD,M1)  Not All data of ExtHandle1 is calculated(-class="num">1 bars ). Error4806
让小布替你跑这套
这些多周期诊断小布盯盘已内置,打开对应品种页即可看到聚合的MTF信号,把重复劳动交给小布,你专注决策。

常见问题

普通指标只处理当前图表周期数据,MTF指标能跨多个时间间隔或符号抓取信息再回传当前图,按基础算法算完按设定周期投递。
多数只展示烛形状态、成交量、买卖价等,难直接交易;但同组独立工具可算多标准指标结果并给建议,倾向作为辅助而非信号源。
这类策略要求同时评估不同时间框架形态,手动切窗口易漏,MTF把各周期结构同屏呈现,概率上更稳。
打开具体外汇或贵金属品种页,AIGC已聚合跨周期状态,不用自己挂多个指标;外汇贵金属波动大,高风险需自判。
取决于取的周期数与符号数,过多可能占资源;建议只挂必要周期,MT5优化后一般可接受。