//+------------------------------------------------------------------+ //| NewBar.mqh | //| AnimateDread | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "AnimateDread" #property link "https://www.mql5.com" #ifndef WARRIOR_SYSTEM_NEWBAR_MQH #define WARRIOR_SYSTEM_NEWBAR_MQH //+------------------------------------------------------------------+ //| "HAS A NEW BAR OPENED SINCE I LAST ASKED?" | //| | //| A CLASS, not the free function this file used to hold, and the | //| reason is the one piece of state involved: the last bar time. A | //| free function has to keep that in a `static`, which makes it ONE | //| watermark shared by every caller - so the first caller each tick | //| consumes the transition and every other caller is told "no new | //| bar" for a bar that just opened. That is silent and direction- | //| dependent, which is the worst shape a bug can have. One instance | //| per consumer, each with its own watermark, and the question is | //| answerable by as many callers as want to ask it. | //| | //| FIRST CALL COUNTS AS NEW. A gate built on this runs the moment | //| the EA attaches rather than sitting idle until the next bar | //| closes - on H4 that would be up to four hours of a live EA | //| deliberately doing nothing. | //+------------------------------------------------------------------+ class CNewBar { private: datetime m_lastTime; bool m_seen; // false until the first successful read public: CNewBar(void) : m_lastTime(0), m_seen(false) {} //--- Reset the watermark, so the next call reports a new bar again. void Reset(void) { m_lastTime = 0; m_seen = false; } //--- The bar time this instance last accepted (0 = never read one). datetime LastBarTime(void) const { return m_lastTime; } //--- True exactly once per opened bar of `symbol`/`period` (defaults: the chart's own). bool IsNewBar(const string symbol = NULL, const ENUM_TIMEFRAMES period = PERIOD_CURRENT) { string sym = (symbol == NULL || symbol == "") ? _Symbol : symbol; datetime cTime[]; ArraySetAsSeries(cTime, true); //--- CopyTime can fail or come back empty (history not yet synced right after attach, or a gap //--- on an illiquid symbol). Indexing cTime[0] unguarded would raise "array out of range" and //--- abort the whole tick. Answering "not a new bar" is the safe direction to fail: the caller //--- skips this tick's bar-boundary work and asks again on the next one. if(CopyTime(sym, period, 0, 1, cTime) <= 0 || ArraySize(cTime) == 0) return false; if(!m_seen) { m_seen = true; m_lastTime = cTime[0]; return true; // first observation - see the header note } if(cTime[0] <= m_lastTime) return false; m_lastTime = cTime[0]; return true; } }; #endif // WARRIOR_SYSTEM_NEWBAR_MQH