New Bar (新柱)事件处理程序(基础篇)
📘

New Bar (新柱)事件处理程序(基础篇)

第 1/3 篇

◍ 抓住新柱触发的第一毫秒

在 MT5 里做价格行为策略,最容易被忽略的入口是 OnCalculate 的新柱判定。很多脚本每 tick 都跑一遍逻辑,其实只在柱线闭合、新柱开盘那一刻做决策,才能避开中途噪声。 核心判据是看当前图表的总柱数是否比上一次调用时多。若 rates_total > prev_calculated,说明至少长出了一根新柱,此时才执行你的信号计算。这套机制在 MQL5 官方示例里被用过 5 452 次下载验证,说明它是实盘前绕不开的基础件。 外汇与贵金属杠杆高、滑点突变频繁,靠 tick 级重复计算容易在新闻秒拉时误触。把逻辑锁死在新柱事件上,回测和实盘的行为会更一致。

MQL5 / C++
class="type">int OnCalculate(const class="type">int rates_total,
                const class="type">int prev_calculated,
                const class="type">class="kw">datetime &time[],
                const class="type">class="kw">double &open[],
                const class="type">class="kw">double &high[],
                const class="type">class="kw">double &low[],
                const class="type">class="kw">double &close[],
                const class="type">long &tick_volume[],
                const class="type">long &volume[],
                const class="type">int &spread[])
  {
   class=class="str">"cmt">// 仅在新柱出现时执行主体逻辑
   if(rates_total>prev_calculated)
     {
      class=class="str">"cmt">// 新柱事件:在此放入你的价格行为判断
     }
   class="kw">return(rates_total);
  }

为什么都盯着新柱检测

写指标或 EA 时,程序员大多在意代码是否紧凑、执行是否够快。限制计算循环最常见的手段,就是在图表出现新柱时才跑一次全部计算与交易逻辑,而不是每个 tick 都重算。 本文这一节先拆一个看似已经解决的问题:如何检测新柱。它会覆盖四块内容:探测新柱的几种方式、现有算法的漏洞、一套通用的新柱探测写法、以及落地时的微妙细节。 另外还会提到 NewBar 事件与对应的 OnNewBar() 处理程序。对 MT5 用户来说,开编辑器建个空 EA,把检测逻辑塞进 OnTick 里对比每根柱的 time,就能直接验证哪种写法更省资源。外汇和贵金属行情跳空频繁,这类检测在实盘里可能因点差或断线出现误判,属于高风险环境下的基础工程问题。

「追踪开盘时间仍是探测新柱最稳的底线」

探测新柱在 MT5 里路数不少,但多数方案有隐性约束。用 OnCalculate 的 rates_total 和 prev_calculated 只能在指标内抓当前图表的新柱,换周期或品种就失效;用 Tick Volume == 1(即 O=H=L=C)去捕首跳,回测好看,实盘常漏——第一次和第二次报价间隔有时太短,尤其行情剧烈或网络差时,柱已生成却没捕到。 基于 TimeCurrent() 的方法适合查当前图表新柱,后面会用到。但跨品种跨周期想省心,还是回到追踪未完成柱开盘时间的原则:简单、可靠,经得起实盘拷打。外汇与贵金属波动大、滑点风险高,这种底层判定容不得花活。 下面这个函数是最常用的原型:用静态变量记上次柱开盘时间,和当前 SERIES_LASTBAR_DATE 比,不同就判新柱。首次调用只记时间不报新,避免误触发。 代码逐行看:static datetime last_time=0 保住跨调用状态;SeriesInfoInteger 取当前品种周期最后柱开盘时间;last_time==0 时写值并返回 false;之后只要时间变了就更新并返回 true,否则 false。逻辑直白,复制进 EA 或脚本就能跑。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 如果新柱出现一组交易品种/周期则返回                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool isNewBar()
  {
class=class="str">"cmt">//--- 在静态变量中记忆最后柱线的开盘时间
   class="kw">static class="type">class="kw">datetime last_time=class="num">0;
class=class="str">"cmt">//--- 当前时间
   class="type">class="kw">datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
class=class="str">"cmt">//--- 如果是首次调用函数
   if(last_time==class="num">0)
     {
      class=class="str">"cmt">//--- 设置时间并退出
      last_time=lastbar_time;
      class="kw">return(false);
     }
class=class="str">"cmt">//--- 如果时间不同
   if(last_time!=lastbar_time)
     {
      class=class="str">"cmt">//--- 记忆时间并返回 true
      last_time=lastbar_time;
      class="kw">return(true);
     }
class=class="str">"cmt">//--- 如果我们执行到此行, 则柱线不是新的; 返回 false
   class="kw">return(false);
  }

◍ 原型 isNewBar 的五个暗坑

把官方示例的 isNewBar() 直接塞进 EA,很多人会发现第二次在别处调用就失灵。根子在 static datetime last_time 只初始化一次,且生命周期横跨整个程序执行期——同一计算循环里换个点再调,它永远返回 false,等于人为给调用次数上了锁。 SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE) 取末根未完成柱开盘时间,逻辑上没问题,但 MT5 不保证数据任意时刻可用:切品种、换周期、重连终端时,时间序列可能暂不可读。原型里没捕获这类查询错误,返回值是盲的。 静态变量在 OnInit() 之前只初始化一次,若首次调用情境想手动构造(比如强制重置),原型给不出入口。更隐蔽的是时间倒挂:lastbar_time < last_time 在掉线重连后可能出现,原代码没报错,只是静默漏掉新柱。 图表周期越小,两次调用间漏掉多根柱的概率越高——M1 上因计算耗时或短暂断连,一次轮询跨过两三根柱并不稀奇。原型只返回「是否新柱」布尔值,把柱数信息也丢掉了。外汇与贵金属杠杆高,这类静默漏柱会让策略在剧烈波动段错失信号,实盘前务必在 MT5 策略测试器用 tick 模式复跑验证。 下面这段是原型原文,逐行拆给你看:static datetime last_time=0 声明静态变量存上次开盘时间,首次为 0;SeriesInfoInteger 取当前末柱时间;last_time==0 判首次调用并写值返 false;last_time!=lastbar_time 才认新柱并更新;否则落到末行返 false。

MQL5 / C++
class="type">bool isNewBar()
class=class="str">"cmt">//--- 在静态变量中记忆最后柱线的开盘时间
   class="kw">static class="type">class="kw">datetime last_time=class="num">0;
class=class="str">"cmt">//--- 当前时间
   class="type">class="kw">datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
class=class="str">"cmt">//--- 如果是首次调用函数
   if(last_time==class="num">0)
     {
      class=class="str">"cmt">//--- 设置时间并退出
      last_time=lastbar_time;
      class="kw">return(false);
     }
class=class="str">"cmt">//--- 如果时间不同
   if(last_time!=lastbar_time)
     {
      class=class="str">"cmt">//--- 记忆时间并返回 true
      last_time=lastbar_time;
      class="kw">return(true);
     }
class=class="str">"cmt">//--- 如果我们执行到此行, 则柱线不是新的; 返回 false
   class="kw">return(false);

常见问题

用追踪上一根柱开盘时间的办法检测新柱,在检测到新柱后立刻执行你的入场逻辑,避免用计数器或成交量判断带来的延迟。
每tick跑策略会重复计算、增加开销,新柱检测只在周期切换时触发一次,能省资源也更符合价格行为节奏。
可以,小布盯盘能按你关注的品种实时监控新柱生成,并在开盘第一刻推送提醒,省去你自己写检测代码的麻烦。
多因未缓存上一开盘时间或跨周期调用混乱,确保用static变量存prev_open_time且同周期单例判断即可避坑。
不会,只要平台给出了新柱的开盘时间字段,对比前值就能判出,比看收盘价或成交量更抗异常行情干扰。