An MT5 closed-bar EA is not a wall clock: validate its OnTick decision boundary

Learn why a finished M15 candle is not processed at an exact minute, how shift 0 differs from shift 1, and how to test one decision per completed bar in an MT5 EA.

BamaUp Editorial ·
Vector illustration marking the open, high, low and close of a candlestick; the figure does not display a tested trading signal
Wikimedia Commons | Probe-meteo.com, “Candlestick chart scheme 01-en”, CC BY-SA 3.0 Unported, unmodified; schematic artwork, not trading performance.

A completed bar is a data boundary, not a scheduled callback

A common EA specification says, “Evaluate the signal when the M15 candle closes.” That sounds precise, but MetaTrader 5 does not promise to call your Expert Advisor at 15:00:00 simply because a fifteen-minute boundary has arrived. OnTick runs when a new quote event is delivered for the chart symbol. The official MQL5 documentation also states that NewTick events are processed serially: if one is already queued or running, another is not added to that application queue [1].

This changes the engineering question. Do you need to evaluate a completed bar once, or do you require a guaranteed sub-second wall-clock decision? Those are different contracts. A closed-bar strategy can make a consistent decision about completed data while still reacting later than the nominal bar boundary. It should measure that delay rather than quietly calling every decision “on time.”

Do not read shift zero as a final candle

In the CopyRates function, start position zero refers to the current bar [2]. That bar is still forming, so its high, low, close and volume may change with incoming data. Shift one conventionally refers to the preceding bar after a newer bar has appeared. If your rules depend on final candle values, reading shift zero and labeling it “closed” introduces look-ahead-like inconsistencies into the research notes.

For a robust specification, say which symbol and timeframe the EA examines, how many completed bars it needs and what it does when they are unavailable. The opening time returned by iTime for a given shift identifies the requested bar, and a zero return indicates an error [3]. CopyRates also has array-ordering rules: inspect its documentation before indexing a destination array, rather than guessing which physical element is newest.

Worked example: an M15 boundary with no immediate quote

Consider a deliberately hypothetical M15 strategy. The 14:45 bar is due to finish at 15:00 according to the chart timeline, yet the first quote for the next interval arrives at 15:00:07. If the EA relies on OnTick, it cannot react to that boundary before an event arrives. At 15:00:07 it can identify the new interval, read the completed 14:45 bar and record the detection timestamp. This is an illustration of event timing, not a measured broker delay or a BamaUp trading result.

Now suppose several quotes arrive while the handler is still busy. You cannot assume that every quote will generate a separate callback [1]. The design should therefore detect a change in the last-completed-bar identity, perform at most one scheduled evaluation for that identity and leave a reasoned record if data were missing. A missing bar during a market closure must not be invented solely to keep an imaginary minute-by-minute schedule.

One evaluation per bar requires state and a restart policy

A practical design records the open time of the most recently evaluated completed bar for each symbol/timeframe pair. When a new quote arrives, compare the actual completed-bar identity with that record. If unchanged, skip the duplicate evaluation. If changed, verify that the historical series is ready, obtain the required completed bars, evaluate the fixed rule and save an audit record. This is an engineering workflow, not a drop-in trading algorithm.

A restart creates a separate question: should the EA resume a missed bar, skip it or reconcile an already submitted order? Decide that policy before testing, and keep signal evaluation distinct from order placement. A repeated signal decision should not automatically cause a repeated trade request. Historical data synchronization can be inspected using SeriesInfoInteger and SERIES_SYNCHRONIZED [4], but a synchronized series alone does not prove that a broker executed an order or that all execution conditions were favorable.

Acceptance tests that expose timing assumptions

Create a demo-only test log with the chart symbol, selected timeframe, expected completed-bar opening time, observed callback time, copied-bar count, series-readiness result and evaluation outcome. Test three scenarios: a normal first tick after the boundary, an interval with sparse quotes and an EA restart between bars. Add one burst-of-ticks run to check that callbacks and state changes do not accidentally create duplicate evaluations. Keep every failed scenario in the evidence folder.

Compare historical testing with a separate forward demo observation, but do not treat either as a live-fill guarantee. Tick availability, server timestamps, holiday sessions, chart-symbol selection and the EA’s own processing workload may change what is observed. OnTimer can be evaluated for periodic housekeeping, yet timers do not manufacture missing market quotes. Start with the free Auto-Trading Foundations lesson or the EA testing checklist if you need a simple structure for this experiment. No paid purchase or funded account is necessary for the exercise.

Article references

1. MQL5 Reference — OnTick event handling2. MQL5 Reference — CopyRates timeseries access3. MQL5 Reference — iTime bar opening time4. MQL5 Reference — SeriesInfoInteger and SERIES_SYNCHRONIZED

Hypothetical examples are not BamaUp trading performance.

Support