All posts · MQL4 · MQL5 · Expert Advisors
OnNewBar: Run Your Trading Bot Once per Candle in MQL4 and MQL5
MetaTrader has no OnNewBar event. A small, reusable pattern to run your EA's logic once per candle in MQL4 and MQL5, and the mistakes that cause repeat signals.
· 4 min read · Updated
Whether you're building a scalping bot or a long-term swing strategy, running your logic once per new candle is fundamental. If you rely on OnTick() alone, your logic can fire many times inside a single candle. Usually you want the strategy to react once, right after a new bar opens.
MetaTrader doesn't have an OnNewBar() event, so you build one. This post gives you:
- A minimal, reusable
OnNewBar()pattern - Complete examples in MQL4 and MQL5
- The common mistakes, and how to test it
Why new-candle detection matters
In MetaTrader 4 and 5, an EA's main entry point is OnTick(), which runs every time a new price tick arrives. In an active market that can be many times per second.
If your strategy checks its conditions on every tick without checking for a new bar, you're likely to:
- Open more trades than the strategy intends
- Repeat the same signal several times in one candle
- Act on a candle that hasn't finished forming
So for most bar-based strategies, you want the logic to run once per candle, at the open of each new bar.
The idea: remember the last bar's time
Every bar has an opening time. Store the opening time of the last bar you processed. When the chart shows a different time, a new bar has started.
MQL4 version
Step by step:
OnTick()stays the main loop.- On every tick, read the opening time of the last closed bar (index 1).
- If it differs from the stored
lastBarTime, a new bar has just formed: store it and runOnNewBar().
datetime lastBarTime = 0;
int OnInit()
{
lastBarTime = iTime(Symbol(), PERIOD_CURRENT, 1);
return INIT_SUCCEEDED;
}
void OnTick()
{
datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 1); // last closed candle
if(currentBarTime == 0)
return; // history not loaded yet
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
OnNewBar();
}
// Other per-tick logic here (if needed)
}
void OnNewBar()
{
Print("New bar detected at: ", TimeToString(TimeCurrent(), TIME_SECONDS));
// Your strategy logic goes here
}Setting lastBarTime in OnInit() means the EA waits for the next new bar instead of trading immediately when you attach it. Leave it at 0 if you'd rather it run once at startup.
MQL5 version
The pattern is the same. This version reads the bar time with CopyTime(), which copies bar opening times into an array:
datetime lastBarTime = 0;
int OnInit()
{
lastBarTime = GetBarTime(1); // time of the last closed candle
return INIT_SUCCEEDED;
}
void OnTick()
{
datetime currentBarTime = GetBarTime(1);
if(currentBarTime == 0)
return; // history not loaded yet
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
OnNewBar();
}
}
void OnNewBar()
{
Print("New bar detected at: ", TimeToString(TimeCurrent(), TIME_SECONDS));
// Your strategy logic goes here
}
datetime GetBarTime(int shift)
{
datetime time[];
if(CopyTime(_Symbol, _Period, shift, 1, time) == 1)
return time[0];
return 0;
}MQL5 also has iTime(_Symbol, _Period, shift), so the MQL4 version's iTime() call works almost unchanged. CopyTime() is the better fit when you need several bar times at once. For everything else that changes between the two languages, see converting MQL4 to MQL5.
Common mistakes to avoid
Reading signals from the bar that just opened
Bar 0 is the candle that's forming right now. At the moment a new bar is detected it holds a tick or two of data, and its close, high and low keep changing. Detecting the new bar with bar 0's time or bar 1's time works equally well, since both change at the same moment. But calculate your signals from bar 1, the candle that just closed, because its values are final.
Using data before it's loaded
Right after the EA starts, or after switching timeframes, the history may not be ready. iTime() then returns 0 and CopyTime() copies nothing. Check for that, as both examples do, so a missing value isn't mistaken for a new bar.
Running several symbols or timeframes with one stored time
Each symbol and timeframe opens its bars at its own times. If one EA watches several, keep a separate lastBarTime for each and read each one's own bar time.
How to test new-candle detection
It's a manual test, but it's quick:
- Run your EA in the Strategy Tester.
- Turn on visual mode.
- Add
Print("Tick received at: ", TimeToString(TimeCurrent(), TIME_SECONDS));toOnTick(). - Watch the journal.
You should see many ticks per candle, and exactly one New bar detected line at the start of each new candle.
Optional add-ons
Run logic only on specific timeframes
if(Period() == PERIOD_M5)
OnNewBar_M5();Handy if one EA carries logic for several timeframes.
Read the closed bar's prices
double open = iOpen(Symbol(), PERIOD_CURRENT, 1);
double close = iClose(Symbol(), PERIOD_CURRENT, 1);Read these inside OnNewBar() and you're always working with a finished candle.
Read indicators once per bar, not once per tick
If your signals are bar-based, read indicator values inside OnNewBar():
double ma = iMA(Symbol(), PERIOD_CURRENT, 14, 0, MODE_SMA, PRICE_CLOSE, 1);One read per bar is lighter than reading the same value on every tick.
Don't use Sleep() for candle timing
Some developers try to Sleep() for a candle's duration, or count time with TimeCurrent(). That breaks easily:
- Candle durations aren't regular in practice (weekend opens, holidays, gaps in quotes)
- While the EA sleeps it doesn't react to ticks, and indicators can't call
Sleep()at all - It gets complicated fast with several symbols or timeframes
Comparing bar times is simpler and behaves the same with any broker.
Summary
The OnNewBar pattern belongs in every MQL toolbox. It keeps your logic clean, prevents duplicate signals and makes your bot's behavior easier to reason about.
- Store the last closed candle's opening time.
- Detect a change with
iTime()(MQL4 and MQL5) orCopyTime()(MQL5). - Run your strategy logic once per candle, using closed-bar values.
- Keep heavy work out of
OnTick().
MQL4 skeleton
datetime lastBarTime = 0;
void OnTick()
{
datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 1);
if(currentBarTime != 0 && currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
OnNewBar();
}
}MQL5 skeleton
datetime lastBarTime = 0;
datetime GetBarTime(int shift)
{
datetime time[];
if(CopyTime(_Symbol, _Period, shift, 1, time) == 1)
return time[0];
return 0;
}
void OnTick()
{
datetime currentBarTime = GetBarTime(1);
if(currentBarTime != 0 && currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
OnNewBar();
}
}Build a bot from your idea
Describe your trading idea and the AI writes MT4 or MT5 code for you to review and test, with a plain-English explanation and setup steps. Your first bot is free.