Tick Data Cannot Repair an Unstated Fill Rule

“Use tick data” is a useful suggestion only after the trader and developer agree what the simulation should do with it. More observations do not choose the entry timing, the side of the quote, the cost model or the priority between a stop and a target. Those decisions still belong in the test contract.
For a trader planning a prop challenge, the distinction matters. A test can process detailed historical data while applying an execution assumption that differs from the written strategy. The resulting number is still a number from that simulation. It is not a record of a broker filling the trader's orders.
This article uses a small event fixture to separate data from execution rules. It adds no new strategy result. The measured counts cited later come from the existing frozen Supertrend result, with its stated costs.
First name the mode and the data source
MetaTrader 5's official testing guide distinguishes “Every tick” from “Every tick based on real ticks.” The former simulates ticks. The latter uses historical ticks accumulated by brokers. Record the selected mode, broker history, symbol and period beside an exported result. Do not reduce that description to “realistic backtest.”
The word “real” describes the historical input in that mode. It does not mean the simulator's fills are observed executions of this strategy. Quote history and a broker execution receipt are different evidence. Neither a mode label nor a high model-quality percentage supplies the missing order contract.
The same discipline applies outside MT5. If an input file contains only hourly Bid candles, say so. Ask-side prices and costs then require explicit modeling. A detailed result table cannot recover information that the input file never contained.
The candle does not tell you which level came first
Consider an already open position with a stop and a target. A historical candle's high reaches the target and its low reaches the stop. The candle states that both levels were inside its range. It does not, by itself, state their chronological order.
The fixture below names three possible input cases without assigning a new measured return:
position: open, with fixed stop and target
case A: ordered events reach the stop before the target
case B: ordered events reach the target before the stop
case C: only one OHLC candle is available, and both levels lie inside it
Cases A and B can have the same candle high and low. If the simulation receives only that candle, it cannot distinguish the paths. Selecting the favorable exit would add an assumption. Selecting the adverse exit is also an assumption, although it may be a deliberately conservative one. The important point is to choose and record the policy before looking at the resulting percentage.
When ordered historical ticks are available, the event sequence may resolve that particular ambiguity. It still needs the correct quote side, timestamps and execution policy. A stop triggered on Ask cannot be justified solely by an unrelated Bid event.
Keep the ambiguous outcome visible
A useful implementation boundary is a resolver that returns a reason, rather than quietly treating every candle as a known path. The following is pseudocode for that boundary, not a claimed production engine:
resolve_exit(input_events, stop, target, policy):
if input_events have an ordered usable quote sequence:
return first eligible crossing, with its event reference
stop_touched = candle_range_contains(stop)
target_touched = candle_range_contains(target)
if stop_touched and target_touched:
return policy.ambiguous_exit, reason="both levels in one candle"
if stop_touched:
return stop, reason="stop only"
if target_touched:
return target, reason="target only"
return no_exit, reason="neither level reached"
The output should retain the input bar or event reference and the selected policy. If the policy changes, the manifest changes and the result receives a new run identity. Do not overwrite the prior count and continue quoting the earlier headline.
This resolver also needs an explicit gap rule. A price can move past a stop between two observations. “First crossing” alone does not decide whether the modeled fill occurs at the stop level or at a later available price. Record that decision separately. Otherwise, the code can look deterministic while the economic assumption remains hidden.
A test matrix for the boundary
| Input fixture | Expected observation | Failure to catch |
|---|---|---|
| Ordered usable quotes hit stop first | Stop outcome references the first eligible event | Sorting by price instead of event time |
| Ordered usable quotes hit target first | Target outcome references the first eligible event | Applying a candle fallback despite available order |
| One candle reaches both levels | Outcome has an explicit ambiguous-policy reason | Silently choosing the favorable result |
| One candle reaches neither | Position remains open under the frozen rule | Inventing an exit for a complete-looking report |
| Gap beyond the level | The documented gap policy controls the modeled fill | Assuming the requested level was available |
| A duplicate event arrives | The existing exit is not applied twice | Turning replay into a second completed trade |
These are proposed acceptance tests for a resolver. They are not results of a new numerical experiment. The complete engine must also test its entry timing, open-position state, costs and quantity rules.
The recorded Supertrend example
The frozen Supertrend simulation used official Dukascopy H1 Bid candles for EURUSD and EURJPY, across its full stated period. It reports 984 completed trades and a measured win rate of 40.04% in this simulation under stated costs. Its result records one pooled ambiguous bar. The frozen policy uses the adverse exit first when both stop and target are inside a bar.
That single count belongs beside the policy. It is not a reason to claim that every fill is known. It is also not permission to replace the data and carry the same percentage forward without rerunning the unchanged rules.
The cost contract names a fixed spread of 1.0 pip for EURUSD and 1.2 pips for EURJPY, plus $7 per standard lot round trip. EURJPY commission conversion uses a fixed USDJPY value of 140. These are stated model assumptions, not observed variable spreads or commission receipts from live orders.
Three things to freeze before requesting more data
First, write down the eligible event: which quote side, timestamp and closed-bar condition can trigger the decision. Second, write down the exit policy: ordered crossing, gap handling and the fallback when only a candle is available. Third, keep costs and units alongside that policy so a data change does not silently change the fee calculation too.
A developer can then say exactly what additional history would resolve. A trader can see what remains assumed. That is a better delivery contract than promising that tick data will make a backtest “accurate.”
The limitations remain even with a clean fixture: historical quotes do not guarantee later liquidity, latency, acceptance or execution. A daily prop limit may also require intraday equity and a specified reset time that this strategy test does not prove. Treat a historical result as a bounded simulation under its stated costs and rules.
Sources: MetaTrader 5's official testing-mode guide and the canonical Supertrend case with rules and results. No new performance measurement or challenge-pass claim is made here.
