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