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:

  1. OnTick() stays the main loop.
  2. On every tick, read the opening time of the last closed bar (index 1).
  3. If it differs from the stored lastBarTime, a new bar has just formed: store it and run OnNewBar().
MQL4
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:

MQL5
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:

  1. Run your EA in the Strategy Tester.
  2. Turn on visual mode.
  3. Add Print("Tick received at: ", TimeToString(TimeCurrent(), TIME_SECONDS)); to OnTick().
  4. 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

MQL4
if(Period() == PERIOD_M5)
   OnNewBar_M5();

Handy if one EA carries logic for several timeframes.

Read the closed bar's prices

MQL4
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():

MQL4
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) or CopyTime() (MQL5).
  • Run your strategy logic once per candle, using closed-bar values.
  • Keep heavy work out of OnTick().

MQL4 skeleton

MQL4
datetime lastBarTime = 0;

void OnTick()
{
   datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 1);
   if(currentBarTime != 0 && currentBarTime != lastBarTime)
   {
      lastBarTime = currentBarTime;
      OnNewBar();
   }
}

MQL5 skeleton

MQL5
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.

More from the blog