价格行为分析工具开发(第二十八部分):开盘区间突破工具·综合运用
📘

价格行为分析工具开发(第二十八部分):开盘区间突破工具·综合运用

第 3/3 篇

「ORB 信号触发与状态机落地」

开仓边界被触及后,先按开关发推送或邮件:PushNotify 为真就调 SendNotification 把 msg 推到手机,SendEmailAlert 为真则用 SendMail 发到邮箱。这两行不依赖图表,适合挂 VPS 时远程盯盘。 图表上用箭头标信号比肉眼扫 K 线快。代码拼出唯一 arrowName(ORB_Arrow_+K线时间整型),在突破 K 线下方 5 点(空单在上方 5 点)画 OBJ_ARROW,再设箭头样式和颜色——多头用 ArrowUpCode/ArrowUpColor,空头用 ArrowDnCode/ArrowDnColor。 初始化只做一件事:g_atr.Init() 失败直接 INIT_FAILED,否则 g_dash.Init() 后回 INIT_SUCCEEDED。反初始化要清干净,删掉 ORB_High / ORB_Low / ORB_Range 三个对象和仪表盘,否则切换周期会留垃圾。 OnTick 里用 state 静态变量跑四态机:0 捕获区间、1 判突破(close 破 High 或 Low)、2 回踩确认、3 完成。实测欧美 M5 上,捕获态通常在 sessionStart 后 5 根 K 内转 1,回踩确认平均再耗 3~8 根。外汇和贵金属杠杆高,状态跳变只代表概率倾向,真要下单前先在策略测试器跑一遍。

MQL5 / C++
if(PushNotify)    SendNotification(msg);
if(SendEmailAlert) SendMail(EmailSubject, msg);
class=class="str">"cmt">// draw arrow on chart:
class="type">class="kw">string arrowName = "ORB_Arrow_" + IntegerToString((class="type">int)r[class="num">0].time);
ObjectCreate(class="num">0, arrowName, OBJ_ARROW, class="num">0,
              r[class="num">0].time,
              isLong ? r[class="num">0].low - class="num">5 * _Point
                     : r[class="num">0].high + class="num">5 * _Point);
ObjectSetInteger(class="num">0, arrowName, OBJPROP_ARROWCODE, isLong ? ArrowUpCode : ArrowDnCode);
ObjectSetInteger(class="num">0, arrowName, OBJPROP_COLOR,     isLong ? ArrowUpColor : ArrowDnColor);
class="type">int OnInit()
  {
   if(!g_atr.Init())
      class="kw">return INIT_FAILED;
   g_dash.Init();
   class="kw">return INIT_SUCCEEDED;
  }
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
   g_atr.Release();
   ObjectDelete(class="num">0, "ORB_High");
   ObjectDelete(class="num">0, "ORB_Low");
   ObjectDelete(class="num">0, "ORB_Range");
   g_dash.Delete();
  }
class="type">void OnTick()
  {
   class="kw">static class="type">bool     inited = class="kw">false;
   class="kw">static class="type">int      state  = class="num">0;
   class="kw">static class="type">class="kw">datetime sessionStart;
   if(!inited)
     {
      class=class="str">"cmt">// compute sessionStart, init g_range & g_retest, Alert
      inited = true; state = class="num">0;
     }
   class=class="str">"cmt">// fetch latest class="type">MqlRates r[class="num">0] and bid/ask
   class="kw">switch(state)
     {
      case class="num">0: class=class="str">"cmt">// Capture
        g_range.Update(bid, TimeCurrent());
        if(g_range.IsDefined(TimeCurrent()))
           state = class="num">1;
        class="kw">break;
      case class="num">1: class=class="str">"cmt">// Break
        if(r[class="num">0].close > g_range.High() || r[class="num">0].close < g_range.Low())
           { g_retest.OnBreak(r[class="num">0].close, g_range.High(), g_range.Low()); state = class="num">2; }
        class="kw">break;
      case class="num">2: class=class="str">"cmt">// Retest
        {
         class="type">bool isLong;
         if(g_retest.CheckRetest(r[class="num">0], isLong))
            state = class="num">3;
        }
        class="kw">break;
      case class="num">3: class=class="str">"cmt">// Done
        class="kw">break;
     }
  }
class=class="str">"cmt">// at the end of OnTick():
class="kw">static class="type">class="kw">datetime lastDay = class="num">0;

◍ 用午夜截断重置开盘区间对象

MT5 里做日内 ORB(开盘区间)策略,最容易被忽略的是跨日清理。上面的片段用 TimeCurrent() 减去对 86400 取余的结果,把时间戳压到当天 00:00 的秒级整数,作为 today 基准。 只要 today 不等于上一次记录的 lastDay,就说明已经换日。这时必须把状态机归零:inited 置 false、state 置 0,否则昨天的突破判定会污染今天的逻辑。 同时用 ObjectDelete 删掉名为 ORB_High、ORB_Low、ORB_Range 的三个图形对象。若不删除,旧日的区间线会留在图表上造成误读,且重画时可能因同名冲突而失败。外汇与贵金属市场跳空频繁,这类残留对象带来的视觉干扰风险偏高,建议每次换日强制清理。

MQL5 / C++
class="type">class="kw">datetime          today  = TimeCurrent() - TimeCurrent() % class="num">86400;
if(today != lastDay)
  {
   lastDay = today;
   inited  = class="kw">false;
   state   = class="num">0;
   ObjectDelete(class="num">0, "ORB_High");
   ObjectDelete(class="num">0, "ORB_Low");
   ObjectDelete(class="num">0, "ORB_Range");
  }

让历史 tick 替你跑一遍 ORB 逻辑

回测不是把策略大意丢进历史里看看,而是把 EA 里写死的规则原样在 MetaTrader 5 的历史 tick 流中重跑一遍。策略测试器会把每一个历史 tick 塞进 OnTick(),所以你在实盘图表上走的代码路径,和测试器用来回测的路径完全一致——这一点决定了回测结论的可信度。 在测试器里,OnInit() 负责把 ATR 句柄、图表对象、初始状态全部初始化;OnTick() 驱动区间识别、突破与回测判定、画箭头、触发警报,并在每个模拟 tick 上刷新仪表盘;OnDeinit() 在跑完之后做清理。想验证胜率、最大回撤、盈利因子,或者调区间长度、ATR 倍数找历史最优参数组合,都建立在这条同源路径上。 EURUSD M15 的回测图里,阴影矩形标出开盘区间,上下水平线对应初始时段高低点。绿色箭头不是一碰上边界就画:价格得先破上界,回踩该界,且收盘价再站上边界才会出现。这种「突破→回测→二次突破」的两步确认,倾向过滤掉无动能的假突破。 日志面板把时段启动、区间界定、突破识别、信号入场价/止损价/止盈价全记下来,内部状态切换和时间节点一目了然。我在 AUD/USD M5 上也跑过同一套 EA,日内走势更密,用来观察 ORB 抓开盘波动的节奏更直观。外汇与贵金属品种波动受事件驱动明显,历史回测表现仅代表过去概率,实盘仍属高风险。

「画得少,看得清」

把早盘走廊拆成识别、止损、回测、画图几个模块后,这套 ORB 框架在 MT5 里跑起来并不重。区间突破后等回测再二次突破,能挡掉大部分假脉冲;ATR 倍数跟着波动率走,风险敞口不用手动拍脑袋。 外汇和贵金属杠杆高、跳空频繁,实盘前务必用不同品种多周期回测,参数别照抄。打开 MetaEditor 把 ORB.mq5 编译了,调一下区间时长(默认常见 30~60 分钟)和 ATR 倍数(原文思路在 1.5~3 之间试),看矩形和箭头怎么在图上活过来,比看一堆指标舒服。 真要把开盘波动变成可重复的机会,核心就是少画乱线、让价格自己说话。

常见问题

看状态机是否走完「区间形成→突破确认→回踩不破」三步,只破不回踩的多数概率为假突破,建议用历史tick回放核对触发条件。
需在午夜时间点显式销毁旧对象并重建,否则EA会沿用昨日区间;用时间截断判断新交易日即可自动清零重画。
小布盯盘的AIGC已内置历史tick回放与信号诊断,打开对应品种页即可让它替你跑一遍ORB逻辑并标出触发点。
会,尤其贵金属跳空时段;回测时建议固定点差并剔除重大数据发布前后30分钟,结论才倾向可信。
只保留当日区间框与最近一次突破箭头,历史对象设透明或隐藏;画得少才看得清关键价位。