价格行为分析工具开发(第二十八部分):开盘区间突破工具·进阶篇
📊

价格行为分析工具开发(第二十八部分):开盘区间突破工具·进阶篇

(2/3)· 从闪崩教训到状态机实现,手把手拆开 ORB 策略的 EA 骨架与回测验证

新手友好 第 2/3 篇
很多交易者看到价格刺穿开盘区间就立刻追单,结果被瞬时噪音扫损。2010 年闪崩证明,未确认的突破在流动性反转时会把人困在不利价位。先把边界当路标、等回踩再确认,才能避开这类坑。

◍ 开盘区间怎么被EA框出来

这套EA把开盘区间突破做成了全自动流程。每个时段一开始先记开盘价,随后在 RangeMinutes(默认 15 分钟)窗口里不断刷新最高、最低,窗口一关就用水平线加矩形把这段早期波动圈出来。 波动率不够它不下单。ATR 过滤器带可配周期和乘数,只有真实波幅过了阈值,突破信号才被放行,避免在窄幅垃圾行情里频繁假突破。 区间定型后走两步确认:价格先收在区间外,再回撤碰边界、随后再度收在外侧,这才算回测成立。此时 EA 在入场 K 线画箭头,按 ATR 算止损和目标,并推屏幕、邮件或推送警报。 仪表盘盯着重置节点——下一时段或午夜自动清状态、ATR 值和区间宽度,不用人手干预。外汇与贵金属杠杆高,信号失效时回撤可能快速扩大,验证前先用策略测试器跑历史时段。

「把ORB策略拆进MT5的类与状态机」

写MT5的EA,第一道关是文件头。#property strict不是摆设,它强制编译器走现代MQL5规则:禁止隐式类型转换、要求函数原型显式声明,能在编译期揪出不少隐蔽笔误。链接和描述字段则会直接显示在EA属性框里,省掉额外UI代码做说明。 输入参数这块,MQL5的input变量自动进属性对话框,ENUM_TIMEFRAMES这类枚举直接渲染成下拉菜单。想限制箭头字符,可以用自定义枚举ArrowSymbol{UP=233,DOWN=234}锁死选项,比让用户填任意Wingdings码更稳。 核心逻辑建议封装成类。CRangeCapture管开盘区间,提供Init()、Update()、IsDefined();注意MQL5类不自动调构造,必须手写Init()。hi/lo用double吃满64位浮点精度,外汇报价差个0.00001都算得清。CATRModule把iATR句柄缓存起来,Value()每tick只CopyBuffer一次,Release()里调IndicatorRelease()防指标泄漏——漏写这步,终端指标标签页会堆满僵尸句柄。 信号判定用CRetestSignal三个私有布尔值搞定突破-回踩-再突破,MqlRates用引用传参省拷贝。CDashboard用OBJ_LABEL做左上角面板,高频tick下可缓存上一次字符串,仅内容变了才ObjectSetString(),这点开销优化在实盘EA里很值钱。 OnInit()初始化失败就返INIT_FAILED,EA绝不以残态启动;OnDeinit()配对释放句柄和对象。OnTick()用static变量跨tick保持状态,switch(state)把策略切成捕捉、确认、回踩、结束四段——这是MQL5无协程下的标准写法。午夜重置用now-now%86400比hour==0靠谱,能躲时区与夏令时的差一天bug。 下面这段头文件与输入声明,直接拷进MetaEditor能验证枚举下拉与strict报错行为:

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 ORB Breakout EA|
class=class="str">"cmt">//|                                         Copyright class="num">2025, MetaQuotes Ltd.|
class=class="str">"cmt">//|                         [MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright   "Copyright class="num">2025, MetaQuotes Ltd."
class="macro">#class="kw">property link        "[MQL5官方文档]
class="macro">#class="kw">property version     "class="num">1.0"
class="macro">#class="kw">property description "Opening‑Range Breakout with ATR confirmation"
class="macro">#class="kw">property strict
class=class="str">"cmt">//--- session & range
input class="type">int             SessionIndex    = class="num">0;            class=class="str">"cmt">// class="num">0 = first session
input class="type">int             RangeMinutes    = class="num">15;           class=class="str">"cmt">// minutes to capture range
class=class="str">"cmt">//--- ATR filter
input ENUM_TIMEFRAMES TF              = PERIOD_M1;    class=class="str">"cmt">// ATR timeframe

用类把开盘区间和ATR封装起来

ORB 策略的核心是先抓一段固定时间的极值,再用 ATR 给突破加一层波动过滤。下面这段把两块逻辑拆成两个类,主程序只要调接口,不用管内部细节。 CRangeCapture 负责在 RangeMinutes 分钟内持续刷新最高价和最低价。Init 用开盘时刻和首根价格定基准,Update 只在时间窗内更新 hi/lo,IsDefined 用于判断窗口是否已结束,High/Low 直接吐出区间边界。 CATRModule 单独持有 ATR 指标句柄。Init 里 iATR(_Symbol, TF, ATRPeriod) 建句柄,TF 是外部传的周期;Value 取最新 ATR 乘以 ATRMultiplier(默认 1.5),作为突破缓冲宽度;Release 在退出时释放句柄避免泄漏。 默认参数里 ATRPeriod=14、ATRMultiplier=1.5,箭头用 wingdings 233/234,邮件和推送默认关。外汇和贵金属波动大、杠杆高,实盘前务必在 MT5 策略测试器里用历史数据验一遍类的行为。 箭头和提醒参数先按默认跑,能让你专注看区间突破本身。

MQL5 / C++
input class="type">int         ATRPeriod      = class="num">14;          class=class="str">"cmt">// ATR look‑back
input class="type">class="kw">double      ATRMultiplier  = class="num">1.5;          class=class="str">"cmt">// ATR * multiplier
class=class="str">"cmt">//--- arrows & alerts
input class="type">int         ArrowUpCode    = class="num">233;          class=class="str">"cmt">// wingdings ↑
input class="type">int         ArrowDnCode    = class="num">234;          class=class="str">"cmt">// wingdings ↓
input class="type">class="kw">color       ArrowUpColor   = clrLime;      class=class="str">"cmt">// class="type">long arrow
input class="type">class="kw">color       ArrowDnColor   = clrRed;       class=class="str">"cmt">// class="type">class="kw">short arrow
input class="type">bool        SendEmailAlert = class="kw">false;        class=class="str">"cmt">// email on signal
input class="type">bool        PushNotify     = class="kw">false;        class=class="str">"cmt">// push on signal
input class="type">class="kw">string      EmailSubject   = "ORB Signal"; class=class="str">"cmt">// mail subject
class CRangeCapture
  {
class="kw">private:
   class="type">class="kw">datetime startTime;
   class="type">class="kw">double   hi, lo;
class="kw">public:
   class="type">void Init(class="type">class="kw">datetime t, class="type">class="kw">double price)
     { startTime = t; hi = lo = price; }
   class="type">void Update(class="type">class="kw">double price, class="type">class="kw">datetime now)
     { if(now < startTime + RangeMinutes*class="num">60)
         { hi = MathMax(hi, price); lo = MathMin(lo, price); } }
   class="type">bool IsDefined(class="type">class="kw">datetime now) class="kw">const
     { class="kw">return(now >= startTime + RangeMinutes*class="num">60); }
   class="type">class="kw">double High() class="kw">const { class="kw">return hi; }
   class="type">class="kw">double Low()  class="kw">const { class="kw">return lo; }
  };
class="kw">static CRangeCapture g_range;
class CATRModule
  {
class="kw">private:
   class="type">int handle;
class="kw">public:
   class="type">bool Init()
     {
       handle = iATR(_Symbol, TF, ATRPeriod);
       class="kw">return(handle != INVALID_HANDLE);
     }
   class="type">class="kw">double Value() class="kw">const
     {
       class="type">class="kw">double buf[];
       if(handle != INVALID_HANDLE && CopyBuffer(handle, class="num">0, class="num">0, class="num">1, buf) == class="num">1)
         class="kw">return buf[class="num">0] * ATRMultiplier;
       class="kw">return class="num">0.0;
     }
   class="type">void Release()
     {
       if(handle != INVALID_HANDLE)
         IndicatorRelease(handle);
     }
  };

◍ 回测确认与图表信息层的代码骨架

把突破和回踩逻辑封装成类之后,真正下单前还差两件事:确认回踩有效性、把信号可视化。下面这段静态对象定义和类结构可以直接贴进 MT5 的 EA 里跑,验证 ORB 策略的信号触发节奏。 CRetestSignal 类用 breakLong / breakShort / retested 三个布尔量记录状态。OnBreak 在收盘价超出区间高低点时置位,CheckRetest 则分两步:第一次触碰区间边界只标记 retested,不返信号;等收盘价重新站上(或跌破)边界才返回 true,避免假突破噪音。 CDashboard 类在左上角创建名为 ORB_Info 的 OBJ_LABEL,XDISTANCE 10、YDISTANCE 30 像素定位。Update 方法用 ObjectSetString 刷新文本,Delete 在退出时清理对象,防止图表残留。 信号触发后,StringFormat 把方向、入场价、止损、止盈拼成一行,例如 "LONG Signal @1.08542 SL=1.08410 TP=1.08790",再调 Alert 弹出。外汇与贵金属波动剧烈,这类信号仅代表概率倾向,实盘前务必用策略测试器按历史数据回测。

MQL5 / C++
class="kw">static CATRModule g_atr;
class CRetestSignal
  {
class="kw">private:
   class="type">bool breakLong, breakShort, retested;
class="kw">public:
   class="type">void Reset()
     { breakLong = breakShort = retested = class="kw">false; }
   class="type">void OnBreak(class="type">class="kw">double close, class="type">class="kw">double h, class="type">class="kw">double l)
     { breakLong = (close > h); breakShort = (close < l); retested = class="kw">false; }
   class="type">bool CheckRetest(class="kw">const class="type">MqlRates &r, class="type">bool &isLong)
     {
      if(breakLong)
        {
         if(!retested && r.low <= g_range.High())
           { retested = true; isLong = true; class="kw">return class="kw">false; }
         if(retested && r.close > g_range.High())
           { isLong = true; class="kw">return true; }
        }
      else if(breakShort)
        {
         if(!retested && r.high >= g_range.Low())
           { retested = true; isLong = class="kw">false; class="kw">return class="kw">false; }
         if(retested && r.close < g_range.Low())
           { isLong = class="kw">false; class="kw">return true; }
        }
      class="kw">return class="kw">false;
     }
  };
class="kw">static CRetestSignal g_retest;
class CDashboard
  {
class="kw">private:
   class="type">class="kw">string name;
class="kw">public:
   class="type">void Init()
     {
      name = "ORB_Info";
      if(ObjectFind(class="num">0, name) < class="num">0)
        {
         ObjectCreate(class="num">0, name, OBJ_LABEL, class="num">0, class="num">0, class="num">0);
         ObjectSetInteger(class="num">0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
         ObjectSetInteger(class="num">0, name, OBJPROP_XDISTANCE, class="num">10);
         ObjectSetInteger(class="num">0, name, OBJPROP_YDISTANCE, class="num">30);
        }
     }
   class="type">void Update(class="kw">const class="type">class="kw">string &txt)
     { ObjectSetString(class="num">0, name, OBJPROP_TEXT, txt); }
   class="type">void Delete()
     { if(ObjectFind(class="num">0, name) >= class="num">0) ObjectDelete(class="num">0, name); }
  };
class="kw">static CDashboard g_dash;
class="kw">static CTrade trade;
class=class="str">"cmt">// ... inside your retest-confirmation block:
class="type">class="kw">string msg = StringFormat("%s Signal @%.5f SL=%.5f TP=%.5f",
                          isLong ? "LONG" : "SHORT",
                          r[class="num">0].close, sl, tp);
Alert(msg);
把区间边界交给小布画
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时开盘区间阴影与突破标签,你只管判断回踩质量。

常见问题

取决于品种早盘波动节律,外汇主流时段常用 15 分钟,期货可拉长到 30 分钟;建议在回测里对比两者胜率再定。
ATR 随波动放大自动放宽止损,避免固定点数在重大数据后被轻易扫掉,也便于跨品种复用同一套逻辑。
价格突破后未回踩边界或回踩后未再次突破,倾向视为噪音,概率上不入场比硬做更省手续费。
可以,小布在品种页标出开盘区间并提示回踩突破,但外汇贵金属高风险,最终下单仍由你确认。
不清零会带入下一时段旧状态,导致误判区间或重复触发,重置保证每天从干净边界开始。