How to Detect New Bars and Avoid EA Timing Bugs

目次

1. What Is mql5 itime?

In MQL5, itime is a function that gets the start time of a bar from a specified currency pair, timeframe, and bar position.
Its return value is a datetime type, and in EAs (Expert Advisors) and indicators, it is a basic function for time-based control.

The key point is that itime handles “time,” not “price.”
For this reason, it is often used for tasks such as the following.

  • Detecting a new bar (candlestick)
  • Controlling processing at fixed time intervals
  • Checking time during logging and debugging

1.1 Basic Function of itime

itime gets a time value by specifying the following three pieces of information.

  • Symbol (example: EURUSD)
  • Timeframe (example: 1-minute chart, 1-hour chart)
  • Bar position (index)

For example, you can use it as follows.

datetime t = itime(_Symbol, PERIOD_M5, 0);

This code gets the “start time” of:

  • The currency pair of the current chart (_Symbol)
  • The 5-minute timeframe (PERIOD_M5)
  • The latest bar (index=0)

The important point here is the meaning of index.

  • 0: the current bar that is still forming
  • 1: the most recently closed bar
  • 2 and later: older bars

In other words, itime is a function for “referencing data on the time axis,”
and it has a structure that lets you look back into past bars like an array.


1.2 When Do You Use It?

In real-world development, itime is almost always used for the following purposes.

Detecting a New Bar

  • One of the most important processes in an EA
  • Prevents multiple entries within the same bar

Optimizing Processing

  • Instead of running on every tick, or every price update
  • Run processing only when a new bar is confirmed to reduce load

Debugging and Logging

Print("Current bar time: ", itime(_Symbol, PERIOD_M1, 0));

1.3 Difference from CopyTime (Important)

A common source of confusion for beginners is the difference between itime and CopyTime.

FunctionFeature
itimeGets the time of a single bar
CopyTimeGets multiple bar times as an array

How to Choose

  • Need only one value -> itime
  • Need to process multiple values together -> CopyTime

For example, CopyTime is suitable for loop processing and historical data analysis,
while itime is best for one-time retrieval such as new bar detection.


■ Common Pitfalls (Important)

Here are the points beginners most often get wrong.

Misunderstanding index

  • 0 is not a “closed bar”
  • -> Because it is still forming, using it in trading logic can cause bugs

Data Not Loaded

  • When there is no history for the symbol or timeframe
  • -> You may not be able to get the correct value

Confusing It with CopyTime

  • Using itime when array processing is needed
  • -> This can make the logic inefficient or break it entirely

■ Practical Notes

  • Time is based on server time
  • The timezone may differ depending on the broker (environment-dependent)
  • In multi-timeframe logic, pay attention to time consistency

2. How to Use itime (With Code)

itime is a simple function, but if you do not understand the arguments and how to handle index correctly, it can cause incorrect behavior.
This section organizes the essentials so you can use it in practical development as quickly as possible.


2.1 Basic Syntax

datetime itime(
   string symbol,
   ENUM_TIMEFRAMES timeframe,
   int index
);

Return Value

  • datetime (date and time)
  • If the value cannot be obtained, 0 may be returned (important)

2.2 Meaning of Each Argument

symbol (Currency Pair)

  • Examples: "EURUSD", _Symbol
  • Normally, use _Symbol for the current chart

timeframe

  • Examples: PERIOD_M1, PERIOD_M5, PERIOD_H1
  • ENUM_TIMEFRAMES, which is the timeframe constant type

index (Bar Position)

  • 0: current bar that is still forming
  • 1: most recently closed bar
  • 2 or higher: older bars

2.3 Shortest Basic Samples

Get the Time of the Latest Bar

datetime current_bar_time = itime(_Symbol, PERIOD_M5, 0);

Most Recently Closed Bar

datetime last_closed_bar = itime(_Symbol, PERIOD_M5, 1);

2.4 Basic Patterns Used in Practice

1. Output the Time to the Log

Print("Current bar time: ", TimeToString(itime(_Symbol, PERIOD_M1, 0)));

* TimeToString: converts datetime to a string


2. Get the Value Safely (Important)

datetime t = itime(_Symbol, PERIOD_M5, 0);

if(t == 0)
{
   Print("Data may not have been retrieved");
   return;
}

2.5 Practical Techniques for Better Reproducibility

Be Aware of Data Loading

if(Bars(_Symbol, PERIOD_M5) < 10)
{
   Print("Not enough bars");
   return;
}

* Bars: the number of bars already available


Specify the Symbol Explicitly

datetime t = itime("EURUSD", PERIOD_H1, 1);

* Important for multi-currency-pair EAs


■ Common Pitfalls

Incorrect Use of index

  • Using index=0 as a closed bar
    -> The value can change because the bar is still forming

Wrong timeframe

  • Specifying a timeframe different from the one you intended
    -> The logic runs at the wrong timing

Wrong symbol

  • Accidentally referencing a different currency pair

■ Common Failures

1. Repeating the same process on every tick

-> Without control using itime, the process can run endlessly

2. Ignoring retrieval failure (0)

-> Conditions may never become true

3. Confusing it with CopyTime

-> Loop processing becomes inefficient


■ Practical Notes

  • itime is lightweight, but overusing it creates waste
  • Avoid calling it repeatedly inside loops
  • If needed, retrieve it once and reuse the value

3. How to Detect a New Bar (Essential EA Logic)

The most important use of itime is detecting whether a new bar (candlestick) has been generated.
This logic directly affects EA quality by preventing unnecessary entries and reducing processing load.


3.1 Why New Bar Detection Is Necessary

An MQL5 EA runs every time OnTick() is called, meaning on each tick or price update.
However, it can be called dozens or hundreds of times within a single candlestick, so leaving it as-is can cause the following problems.

  • Multiple entries under the same conditions
  • More unnecessary calculations and lower performance
  • Lower reproducibility in the logic, creating gaps between backtests and live trading

To prevent this, you need to process only when a new bar has been confirmed.

MQL5 new bar detection using itime function, showing OnTick execution flow, time comparison logic, and trade execution trigger on chart

3.2 Basic Logic (Most Important)

datetime last_bar_time = 0;

void OnTick()
{
   datetime current_time = itime(_Symbol, PERIOD_M5, 0);

   if(current_time != last_bar_time)
   {
      last_bar_time = current_time;

      // Run only on a new bar
      Print("A new bar has been generated");
   }
}

3.3 How the Logic Works

State Management Concept

VariableMeaning
current_timeTime of the current bar
last_bar_timeBar time processed last time

Decision Logic

  • Same -> same bar, so do nothing
  • Different -> new bar, so run the process

This “time comparison” is the simplest and most reproducible method.


3.4 Enhanced Practical Version (Recommended)

datetime last_bar_time = 0;

void OnTick()
{
   datetime current_time = itime(_Symbol, PERIOD_M5, 0);

   // Data retrieval check
   if(current_time == 0)
      return;

   // Initialization on first run
   if(last_bar_time == 0)
   {
      last_bar_time = current_time;
      return;
   }

   // New bar detection
   if(current_time != last_bar_time)
   {
      last_bar_time = current_time;

      // Write trading logic here
      Print("New bar confirmed: start processing");
   }
}

3.5 Common Implementation Patterns

1. Entry Control

if(current_time != last_bar_time)
{
   last_bar_time = current_time;

   // Condition check
   if(/* trading condition */)
   {
      // Entry process
   }
}

2. Optimizing Indicator Calculation

if(current_time != last_bar_time)
{
   last_bar_time = current_time;

   // Run heavy calculations only here
}

■ Common Pitfalls (Important)

Misunderstanding the Meaning of Using index=0

  • The bar is not closed, but its “time” is fixed
    -> This is not a problem for new bar detection

Forgetting to Initialize last_bar_time

  • This can cause a false detection on the first run

Not Using a Global Variable

  • If it is local, it resets every time

■ Common Failures

1. Entering on every tick

-> No new bar control

2. Comparing with index=1

-> The logic runs one bar late, causing delayed entries

3. Timeframe mismatch

-> The EA runs at a timing different from what you expected


■ Practical Notes

  • In multi-timeframe logic, each timeframe needs its own time management
  • In VPS environments or during low-liquidity periods, ticks may not arrive
    -> Strictly speaking, “new bar confirmed” means “detected when a tick arrives”

■ Practical Conclusion

  • itime x comparison is the simplest and most powerful approach
  • EA quality is largely determined by this logic
  • It is essential when you care about reproducibility and backtest consistency

4. Applying itime to Multi-Timeframe and Multi-Currency Logic

itime is useful not only on a single chart but also for control across multiple timeframes (MTF) and multiple currency pairs (multi-symbol logic).
However, this area is also where bugs, false detections, and loss of reproducibility often occur, so it is important to understand the structure clearly.


4.1 Multi-Timeframe (MTF) Basics

For example, assume the following logic.

  • Entry: 5-minute timeframe (M5)
  • Filter: 1-hour timeframe (H1)

In this case, you need to manage the bar generation timing of each timeframe separately.


4.2 Basic MTF Implementation

datetime last_m5_time = 0;
datetime last_h1_time = 0;

void OnTick()
{
   datetime m5_time = itime(_Symbol, PERIOD_M5, 0);
   datetime h1_time = itime(_Symbol, PERIOD_H1, 0);

   if(m5_time == 0 || h1_time == 0)
      return;

   // M5 new bar
   if(m5_time != last_m5_time)
   {
      last_m5_time = m5_time;

      // M5-based processing
      Print("M5 new bar");
   }

   // H1 new bar
   if(h1_time != last_h1_time)
   {
      last_h1_time = h1_time;

      // H1-based processing
      Print("H1 new bar");
   }
}

4.3 MTF Design Points (Important)

Separate State by Timeframe

  • Do not share last_bar_time
  • -> Manage it separately for each timeframe

Higher Timeframes Update Less Often

  • H1 updates only once per hour
  • -> Pay attention to the timing of condition evaluation

Understand Timing Misalignment

  • M5 and H1 do not update at the same time
  • -> Design the logic with clear separation

■ Common Pitfalls (MTF)

Sharing Variables

-> Using the same variable for every timeframe causes bugs

Timing Dependency

-> Checking H1 conditions every M5 bar is inefficient


■ Common Failures (MTF)

1. Evaluating higher timeframes on every tick

-> Wasteful and likely to create bugs

2. Assuming synchronization

-> In reality, the timing can differ



4.4 Multiple Currency Pairs (Multi-Symbol)

When handling multiple currency pairs, specify the symbol argument explicitly.


Basic Multi-Symbol Implementation

string symbols[] = {"EURUSD", "USDJPY"};

datetime last_times[2];

void OnTick()
{
   for(int i = 0; i < ArraySize(symbols); i++)
   {
      datetime t = itime(symbols[i], PERIOD_M5, 0);

      if(t == 0)
         continue;

      if(t != last_times[i])
      {
         last_times[i] = t;

         Print(symbols[i], " new bar");
      }
   }
}

4.5 Multi-Symbol Design Points

Manage with Arrays

  • Keep state for each symbol

Account for Missing Data

  • History may not be available on the first run

Need for SymbolSelect (Environment-Dependent)

SymbolSelect("EURUSD", true);

■ Common Pitfalls (Multi-Symbol)

No Data

-> itime returns 0

Array Index Mistake

-> Data from the wrong currency pair is referenced


■ Common Failures (Multi-Symbol)

1. Preparing only one last_time variable

-> All currency pairs can behave incorrectly

2. Forgetting SymbolSelect

-> Data cannot be retrieved


■ Practical Notes

  • Some symbols may be unavailable depending on the broker
  • Updates do not occur if ticks are not delivered
  • In VPS environments, network latency can affect behavior

■ Practical Conclusion

ItemImportance
MTFMedium to high
Multi-symbolHigh, with implementation complexity
  • itime works well for scalable design
  • However, state management is everything

5. Notes, Environment Dependencies, and Bug Prevention When Using itime

The syntax of itime itself is simple, but in practice, failures usually come from how you handle the assumptions around the function, not from the function call itself.
The four points that especially trouble beginners are bar closing, missing history, the time reference, and tick arrival conditions.
This section explains the notes you need for stable operation with itime.


5.1 Do Not Misunderstand the Difference Between index=0 and index=1

The most common mistake is assuming that index=0 is a “closed bar.”

  • index=0: the bar currently being formed
  • index=1: the most recently closed bar

In other words, if you want to use confirmed close-based information for trade conditions, you should generally use index=1.
On the other hand, new bar detection itself works correctly with a time comparison using index=0.
The reason is that even though the “price” of the current bar changes, the “bar start time” is fixed.

Typical Mistake

double close_price = iClose(_Symbol, PERIOD_M5, 0);

If you use the price of bar 0 for condition judgment like this, the logic depends on changes inside an unclosed bar.
As a result, behavior can easily differ between backtesting and live operation.

Safer Way to Think About It

  • New bar detection -> itime(..., 0)
  • Closed-bar-based judgment -> iClose(..., 1) or CopyRates(..., 1, ...)

5.2 Assume Data May Not Be Loaded or History May Be Insufficient

If enough data for the target symbol or timeframe has not been loaded, itime may not return the value you expect.
In that case, you may see a returned value of 0, or related processing may fail to work correctly.

Minimum Defensive Code

datetime bar_time = itime(_Symbol, PERIOD_H1, 0);

if(bar_time == 0)
{
   Print("Could not get the bar time. History may not have been loaded.");
   return;
}

Also Check the Number of Bars

if(Bars(_Symbol, PERIOD_H1) < 2)
{
   Print("Processing stopped because there are not enough bars.");
   return;
}

Especially in multi-timeframe or multi-symbol logic,
it is common for history to be missing for timeframes or currency pairs that are not shown on the current chart.


5.3 Treat Time as Market Data Time, Not Local Time

itime returns the time of a bar.
In practice, it is safest to treat it as a time based on chart data or server-side time series data.
A common point of confusion is the difference from the PC’s local time and TimeCurrent().

Types of Time That Are Easy to Confuse

  • itime: bar start time
  • TimeCurrent(): current time equivalent to the current server time
  • PC clock: local time on the user’s computer

These three do not always match exactly.
Therefore, you need to treat “what time it is now” and “what time this bar started” as different things.

Notes

  • The server time reference can differ by broker
  • Some environments show daylight saving time effects
  • Differences are especially likely when comparing multiple accounts or brokers

Therefore, in EA design,
it is important to unify the time reference used for comparisons.


5.4 A New Bar Is Detected When a Tick Arrives, Not Exactly When the Time Comes

This is an important point that is easy to overlook.
Because an MQL5 EA runs in OnTick(), a new bar is detected only when the next tick arrives, not at the theoretical moment when the bar starts on the chart.

For example, even if a new M5 bar starts at 12:00:00, if the first tick during a low-liquidity period arrives at 12:00:08, the EA recognizes the new bar at 12:00:08.

What This Can Cause

  • The EA may appear to react late late at night, early in the morning, or on minor currency pairs
  • The live trading feel may differ from the backtest
  • It can lead to the misunderstanding that the EA “does not run exactly at 00 seconds”

Practical Judgment

  • For normal discretionary-trading support and EA operation, this is often not a problem
  • If strict second-level control is required, the design itself should be reconsidered

Common Pitfalls

Assuming itime Is Broken

In many cases, the real cause is missing history, an unselected symbol, or too few bars.

Confusing New Bar Detection with Closed-Bar Judgment

It becomes easier to organize the logic if you separate roles: bar 0 for time detection and bar 1 for confirmed conditions.

Ignoring Environment Differences

If you do not account for broker and time-setting differences, the logic may not reproduce in another environment.


Common Failures

1. Assuming price information from itime(..., 0) is also confirmed

The time is fixed, but the price is not confirmed.

2. Not checking for 0 when retrieval fails

If you compare it as-is, it can cause false detections at startup.

3. Not accounting for situations where no tick arrives

This can lead to the mistaken belief that the EA reacts instantly based only on time.


Practical Prevention Methods

  • Do not process if the return value of itime is 0
  • Return early if there are not enough bars
  • Separate new bar detection from trade judgment
  • Unify the time reference around bar time
  • Assume detection can be delayed during low-liquidity periods

6. Choosing Between itime and Related Functions (CopyTime, TimeCurrent, and Others)

itime is powerful on its own, but in practice, accuracy and efficiency improve greatly when it is combined with other time-related functions.
This section explains the following functions, which are often confused.

  • itime
  • CopyTime
  • TimeCurrent
  • iTime (watch for naming differences)

6.1 Difference Between itime and CopyTime

The function most often confused with itime is CopyTime.

ItemitimeCopyTime
TargetSingle barMultiple bars
Return valuedatetimeArray
PerformanceLightweightSlightly heavier
Use caseNew bar detection and one-time retrievalBacktesting and analysis

itime (One-Time Retrieval)

datetime t = itime(_Symbol, PERIOD_M5, 0);
  • Use it when you need only one value
  • Best for new bar detection

CopyTime (Multiple Values)

datetime times[];
CopyTime(_Symbol, PERIOD_M5, 0, 10, times);
  • Gets the times of the latest 10 bars together
  • Suitable for array processing and analysis

■ Practical Judgment (Important)

  • Real-time control -> itime
  • Historical analysis and loop processing -> CopyTime

6.2 Difference Between itime and TimeCurrent

These two functions have completely different purposes.

ItemitimeTimeCurrent
MeaningBar start timeCurrent time
ReferenceChart dataServer time
Use caseCandlestick controlTime-based judgment

Example of TimeCurrent

datetime now = TimeCurrent();

■ Common Misuse

Incorrect Example

if(TimeCurrent() != last_time)
{
   // Mistaken as a new bar
}

This cannot be used for new bar detection.
The reason is that TimeCurrent() simply advances as the current time changes.


■ Correct Role Separation

  • New bar detection -> itime
  • Time conditions, such as running only during London hours -> TimeCurrent

6.3 Difference from iTime (Important)

This is a point that often confuses developers with MQL4 experience.

  • MQL4 -> iTime
  • MQL5 -> itime

Differences

ItemiTimeitime
EnvironmentMQL4MQL5
SyntaxAlmost the sameSimilar
NoteNot recommended in MQL5Standard

■ Practical Judgment

  • In MQL5, using itime is the correct choice
  • iTime usually appears only when migrating older code

6.4 Best Strategy for Choosing Functions

If you are unsure in practice, use the following rules to make a consistent decision.

By Pattern

New Bar Detection

-> itime

Historical Data Analysis

-> CopyTime

Time-Based Control, such as running only at 9:00

-> TimeCurrent

■ Common Pitfalls

Confusing itime and CopyTime

-> Using an array even though you only need one value is inefficient

Trying to Detect Bars with TimeCurrent

-> The purpose is fundamentally different

Using MQL4 Knowledge As-Is

-> Continuing to use iTime


■ Common Failures

1. Calling CopyTime on every tick

-> Unnecessarily heavy

2. Overusing itime inside loops

-> Performance gets worse

3. Mixing time references

-> The logic breaks down


■ Practical Best Practices

  • Use itime as the center of real-time processing
  • Use CopyTime to retrieve analysis data in batches
  • Use TimeCurrent to support time conditions
  • Separate the roles of each function

■ Conclusion from a Design Perspective

itime is the “trigger”
CopyTime is “data retrieval”
TimeCurrent is the “current time”

Just keeping this role separation clear
greatly improves EA stability and reproducibility.

7. FAQ (Frequently Asked Questions and Solutions)

Q1. Why does itime return 0?

Answer:
The main cause is that data has not been retrieved yet.

  • The history for the target symbol or timeframe has not been loaded
  • SymbolSelect has not been used in multi-symbol logic
  • There are not enough bars

Fix:

  • Check the number of bars with Bars()
  • Stop processing if itime(...) == 0
  • If needed, display the chart or trigger history loading

Q2. Should I use index=0 or index=1?

Answer:
Use them based on the purpose.

  • New bar detection -> index=0
  • Trading logic based on a closed bar -> index=1

Note:
The price of index=0 is not confirmed, so as a rule, do not use it for entry decisions.


Q3. A new bar came, but it was not detected. Why?

Answer:
OnTick() only runs when a tick arrives,
so a new bar start does not mean immediate detection.

Fix:

  • Understand that detection waits until a tick arrives
  • Design with possible delay during low-liquidity periods

Q4. Should I use itime or CopyTime?

Answer:
Choose based on the goal.

  • One-time retrieval, such as new bar detection -> itime
  • Processing multiple data points -> CopyTime

Note:
Overusing CopyTime in a real-time EA can reduce performance.


Q5. Multi-timeframe logic does not work correctly. Why?

Answer:
You are likely not managing state separately for each timeframe.

Fix:

  • Separate last_bar_time by timeframe
  • Avoid reusing the same variable
  • Assume that higher timeframes update less often

Q6. Why can’t I get itime in multi-symbol logic?

Answer:
The target symbol’s data may not have been loaded.

Fix:

  • Run SymbolSelect(symbol, true)
  • Add a check for itime == 0
  • Use arrays to separate state by symbol

Q7. Can TimeCurrent() detect a new bar?

Answer:
No. It has a different purpose.

  • TimeCurrent() -> current time
  • itime -> bar start time

Conclusion:
Always use itime for new bar detection.


Q8. Why does behavior differ between backtesting and live operation?

Answer:
The following causes are common.

  • Using an unclosed bar (index=0) in the logic
  • Different tick arrival timing
  • Different broker server time

Fix:

  • Build logic around closed bars (index=1)
  • Limit processing with new bar detection
  • Unify the time reference

■ Practical Summary (Fastest Understanding)

  • New bar detection -> compare time with itime(..., 0)
  • Trade judgment -> use a closed bar (index=1)
  • Error prevention -> check for 0 and confirm the number of bars
  • Expansion -> MTF and multi-symbol logic require separated state

If you follow these four points, you can prevent most bugs related to itime.