MQL5 新柱检测:从静态函数到 CisNewBar 类
一、为什么需要检测新柱
指标重算、信号触发、交易动作通常只在柱收盘/开盘时发生一次。如果每 tick 都跑完整计算,既浪费 CPU 又会让信号在柱内反复触发。检测“新柱出现”是限制计算循环、让逻辑只在新柱执行一次的标准手段。
二、早期静态函数的做法与缺陷
最常见的原型是用静态变量记住上一根柱的开盘时间,与当前 SERIES_LASTBAR_DATE 比较:
bool isNewBar()
{
static datetime last_time=0;
datetime t=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
if(last_time==0){ last_time=t; return(false); }
if(last_time!=t){ last_time=t; return(true); }
return(false);
}
它的问题很多:静态变量在 OnInit 前只初始化一次,同一循环里多次调用除首次外都返回 false;硬编码 Symbol()/Period() 不能监控别的品种/周期;数据未就绪时 SeriesInfoInteger 可能返回 0 或拒绝;若两次调用间跨了多根柱只报 true 不知几根;时间倒退也无报错。
三、CisNewBar 类的完整设计
用面向对象去掉静态限制:每个实例用自己的 m_lastbar_time 跨调用保持状态,支持 SetSymbol/SetPeriod 监控任意品种周期,并通过 m_new_bars/m_retcode/m_comment 暴露更多信息。核心判断:
bool CisNewBar::isNewBar(datetime newbar_time)
{
m_new_bars=0; m_retcode=0;
if(m_lastbar_time>newbar_time){ m_retcode=-1; return(false); } // 同步错误
if(m_lastbar_time==0){ m_lastbar_time=newbar_time; return(false); }
if(m_lastbar_time<newbar_time){ m_new_bars=1; m_lastbar_time=newbar_time; return(true); }
return(false);
}
int CisNewBar::isNewBar() // 无参重载,内部取 SERIES_LASTBAR_DATE
{
datetime nt; ResetLastError();
if(!SeriesInfoInteger(m_symbol,m_period,SERIES_LASTBAR_DATE,nt))
{ m_retcode=GetLastError(); return(0); }
if(!isNewBar(nt)) return(0);
m_new_bars=Bars(m_symbol,m_period,m_lastbar_time,nt)-1;
return(m_new_bars);
}
四、在 EA / 指标里调用
全局声明实例 current_chart; 在 OnTick 里直接用无参重载即可。想要新柱事件化处理,可用 TimeCurrent() 算出当前柱开盘时间再调带参重载,封装成 OnNewBar():
#include <Lib CisNewBar.mqh>
CisNewBar current_chart;
void OnTick()
{
if(current_chart.isNewBar()>0)
PrintFormat("新柱线: %s", TimeToString(TimeCurrent(),TIME_SECONDS));
}
五、相比静态函数的优势
- 多周期/多品种:SetSymbol/SetPeriod 或传任意 datetime,可同时监控 EURUSD M1、GBPUSD M1 等。
- 无调用次数限制:实例变量替代静态变量,多处调用各自维护状态。
- 错误捕获与同步检测:m_retcode/m_comment 暴露运行错误,时间倒退能被捕获。
- 返回新柱数量:断网后也能知道漏了几根,便于指标重算。
- 支持非标准图(Renko/Kagi):传入自定义开盘时间即可。
六、使用注意事项
七、静态函数的缺陷
早期用「记录上一根时间、比较当前时间」的静态函数判断新柱,缺陷明显:多周期下不同周期要各自记状态、重初始化(改周期/重加载)会丢状态、函数无封装易被多处误改。一旦状态错,整 EA 时序全乱。
更隐蔽的是:静态变量在回测与实盘行为可能不一致,导致回测过的逻辑实盘失效。这正是要封装成类的原因。
八、CisNewBar 类与跨周期用法
把新柱检测封装成 CisNewBar 类:构造时记录周期,每次调用 IsNewBar() 内部比对最后时间并返回布尔,状态自管理。多周期就每个周期一个实例,互不干扰,重初始化也能在类里正确重置。
class CisNewBar
{
ENUM_TIMEFRAMES tf; datetime last;
public:
bool IsNewBar()
{
datetime now = iTime(_Symbol, tf, 0);
if(now != last) { last = now; return true; }
return false;
}
};
九、新柱触发交易的最佳实践
把「只在新柱做一次」的逻辑(如重新算信号、下当根单)放到 if(IsNewBar()) 块里,避免每 tick 重复计算与重复发单。但注意:新柱内仍可能多次 OnTick,确认逻辑幂等,别依赖「只来一次」的假设。
回测时新柱检测和实盘一致,是策略可迁移的基础。用类封装后,回测实盘行为统一,省掉大量「回测好实盘崩」的坑。