Data study

MT5 Strategy Tester accuracy: real ticks vs synthetic, and why backtests disagree

13 min read

If you have ever run the same Expert Advisor through the MetaTrader 5 Strategy Tester twice, on two different price histories, and watched the equity curve change shape, you have met the most common reason a backtest “differs”: the tester can only replay the price path it is handed. We checked how much that matters by running 100 strategies through both RoboticEA’s engine and the MT5 Strategy Tester, first on a tick stream built from bars, then on 24,596,614 real ticks.

The short answer

Backtests disagree mostly because they were fed different price paths, not because one tool is wrong. In our harness the bar-derived stream held 273,200 ticks for the test window; the real stream held 24,596,614, 90 times as many. Switching to real ticks moved the median gap between engine and tester from 8.97 pp to 6.07 pp of account return and cut the directional bias from +4.92 pp to +1.59 pp. The counter-intuitive part: exact trade-count matches fell from 43 of 97 strategies to 5. Scope: one instrument, one timeframe, 2023-02-01 to 2023-12-31.

The short version

Why your MT5 backtest differs from someone else’s

A Strategy Tester run has three inputs that people rarely write down: the price path the tester replays inside each bar, the costs charged on it, and the account model that books the trades. Change any one and a strategy that looked identical on a chart will not produce the same trades. This post is about the first, because it is the one that is invisible in the report and the one we could measure cleanly.

The tester offers modelling modes that differ in where the ticks come from. “Every tick” builds them from bar data; “Every tick based on real ticks” replays recorded ticks. We measured a bar-derived stream that our own tooling wrote and a real-tick stream, so treat what follows as evidence on bar-derived versus real, not as a measurement of the tester’s built-in generator, which we did not test. The tester did exactly what it was asked in both cases, and that is the point: MetaTrader 5 is a faithful replay engine, and a replay is only as informative as the tape.

8.97 pp → 6.07 pp

median gap in total return between the RoboticEA engine and the MT5 Strategy Tester, bar-derived ticks then real ticks

97 strategies present in both runs, gap = engine minus MT5 in percentage points of return. Within 5 pp of each other: 34% of strategies before, 46% after. The MT5 result itself moved by a median 7.75 pp between the two tick models.

What the tester is fed

Four points per bar, or 24,596,614 ticks

The first run gave MT5 a custom EURUSD symbol whose only history was bars. To let the tester run in its tick-based mode, our importer wrote four points per five-minute bar — open, high, low, close, in that order, a quarter of the bar apart — with the ask a fixed two points above the bid. That is 48 points per hourly bar, and the tester’s journal confirmed it: 273,200 ticks over 5,695 bars, or 47.97 per bar. The second run replayed real Dukascopy ticks, loaded into a custom symbol, with the spread each tick actually carried.

Real EURUSD does not look like that. In the same window a typical five-minute bar held 283 ticks (mean 360), and only 0.2% of bars held four or fewer. An hourly bar held a median 3,461. The count also swings through the day, from about 1,151 ticks in the 21:00 UTC hour to about 10,074 at 14:00 — and the spread swings with it, averaging 1.38 pips at 21:00 against a median of 0.30 pips overall. A fixed spread cannot represent that, and a flat 48 points a bar cannot represent any of it.

Real ticks per H1 bar by hour of dayMean number of real EURUSD ticks in an H1 bar for each UTC hour, from about 1,151 to about 10,074. The bar-derived stream has a flat 48 points per H1 bar.06,00012,00000:00 UTC: 2,735 ticks per H1 bar0001:00 UTC: 3,454 ticks per H1 bar02:00 UTC: 2,758 ticks per H1 bar03:00 UTC: 2,190 ticks per H1 bar0304:00 UTC: 1,908 ticks per H1 bar05:00 UTC: 2,598 ticks per H1 bar06:00 UTC: 4,141 ticks per H1 bar0607:00 UTC: 6,040 ticks per H1 bar08:00 UTC: 6,027 ticks per H1 bar09:00 UTC: 5,046 ticks per H1 bar0910:00 UTC: 4,277 ticks per H1 bar11:00 UTC: 4,324 ticks per H1 bar12:00 UTC: 7,502 ticks per H1 bar1213:00 UTC: 9,319 ticks per H1 bar14:00 UTC: 10,074 ticks per H1 bar15:00 UTC: 7,737 ticks per H1 bar1516:00 UTC: 5,133 ticks per H1 bar17:00 UTC: 3,939 ticks per H1 bar18:00 UTC: 4,077 ticks per H1 bar1819:00 UTC: 3,797 ticks per H1 bar20:00 UTC: 2,463 ticks per H1 bar21:00 UTC: 1,151 ticks per H1 bar2122:00 UTC: 1,487 ticks per H1 bar23:00 UTC: 1,442 ticks per H1 bar23dashed line: synthetic stream, 48 ticks in every hour10,074UTC hour of day
Mean real ticks in an H1 bar by UTC hour, EURUSD, 2023-02-01 to 2023-12-31 (5,695 bars). The dashed line at 48 is the flat count a four-points-per-M5-bar stream gives every H1 bar, in every hour.
UTC hourTicks / H1 barMean spread (pips)UTC hourTicks / H1 barMean spread (pips)
00:002,7350.3412:007,5020.30
01:003,4540.3313:009,3190.30
02:002,7580.3414:0010,0740.30
03:002,1900.3415:007,7370.30
04:001,9080.3316:005,1330.30
05:002,5980.3517:003,9390.30
06:004,1410.3118:004,0770.32
07:006,0400.3019:003,7970.33
08:006,0270.3020:002,4630.39
09:005,0460.3021:001,1511.38
10:004,2770.3022:001,4871.17
11:004,3240.2923:001,4420.47
The numbers behind the chart, plus the mean quoted spread in each hour. Real EURUSD ticks, 2023-02-01 to 2023-12-31. The synthetic stream is 48 points per H1 bar and a fixed two-point spread in every hour.

For a concrete week, take Monday 6 to Friday 10 March 2023: 671,703 real ticks, where a four-point stream would offer 5,660. Everything a strategy does between the four points — whether a stop was touched before a target, what a fill cost — is decided by the stream, not by the strategy.

RoboticEA's trade replay: a candlestick chart with an open long position, its entry line and a take-profit line, above a live profit and loss strip
The same candles look identical whichever stream built them. What differs is the path inside each candle, which is where stops fill and where a backtest quietly diverges.

The test

100 strategies, two tick models, one price file

  • Same strategies, both tools. 100 strategies from RoboticEA’s library (presets, composed strategies and stateful sizing and basket families), each run in the C++ engine and exported as an Expert Advisor and run in the MT5 Strategy Tester.
  • Same window and account. EURUSD H1, 2023-02-01 to 2023-12-31, $10,000 deposit, leverage 1:100, seed 42. Swap and commission were zero on both sides so a cost difference could not hide a fill difference.
  • Same tick file in the real-tick run. Both legs consumed one file of 27,553,506 Dukascopy records. 24,581,968 fall inside the window; MT5 reported 24,596,614, a 0.06% difference. Each side then builds its own candles from those ticks, as a live terminal does.
  • Every row is on disk. The figures on this page are recomputed from the raw run records by a script, and the published /parity-study table is the same 100 rows, gap for gap.

On real ticks, the median absolute gap in total return was 6.06 pp across the 98 strategies that traded on at least one side (2 took no trades in either tool). The headline figure of 6.07 pp above is the same data on the 97 strategies that also ran on the bar-derived stream, no-trade rows included; a slightly different row set, not a different result. The spread of outcomes matters more than the median, so here are the deciles, then the same strategies on the bar-derived stream beside them.

Absolute return gap by decile, bar-derived versus real ticksDeciles of the absolute gap between engine and MT5 total return, 95 strategies paired across the two runs.bar-derived ticksreal ticksP10: bar-derived ticks 1.0 pp, real ticks 0.5 ppP101.0 pp0.5 ppP20: bar-derived ticks 2.8 pp, real ticks 1.3 ppP202.8 pp1.3 ppP30: bar-derived ticks 4.4 pp, real ticks 2.3 ppP304.4 pp2.3 ppP40: bar-derived ticks 6.0 pp, real ticks 3.7 ppP406.0 pp3.7 ppP50: bar-derived ticks 9.1 pp, real ticks 6.1 ppP509.1 pp6.1 ppP60: bar-derived ticks 10.9 pp, real ticks 7.4 ppP6010.9 pp7.4 ppP70: bar-derived ticks 15.9 pp, real ticks 11.4 ppP7015.9 pp11.4 ppP80: bar-derived ticks 19.5 pp, real ticks 16.1 ppP8019.5 pp16.1 ppP90: bar-derived ticks 29.9 pp, real ticks 21.5 ppP9029.9 pp21.5 pppercentage points of total return (the lower the closer)
Absolute gap between engine and MT5 total return, in percentage points, at each decile of the 95 paired strategies that traded on at least one side. Read a row as: this share of strategies agreed to within this many points or better. Grey is the bar-derived run, blue the real-tick run.
WithinBar-derived ticksReal ticks
1 pp11%17%
2 pp18%27%
3 pp22%37%
5 pp33%45%
10 pp54%66%
20 pp81%88%
Share of the 95 paired strategies whose engine and MT5 total returns agree to within N percentage points, and the decile values that feed the chart (P10, P20, P30, P40, P50, P60, P70, P80, P90: 1.0 vs 0.5; 2.8 vs 1.3; 4.4 vs 2.3; 6.0 vs 3.7; 9.1 vs 6.1; 10.9 vs 7.4; 15.9 vs 11.4; 19.5 vs 16.1; 29.9 vs 21.5).

The engine and the tester also agree on which strategies did well: across the 98 strategies the correlation between the two total returns is 0.92, and they agree on whether the strategy finished up or down for 89 of them.

Engine return against MT5 return, one dot per strategy100 strategies on the same real ticks. Dots on the diagonal agree exactly; the Pearson correlation is 0.919.-100%-100%-50%-50%0%0%50%50%100%100%150%150%200%200%250%250%300%300%preset_amber_bollinger_reversion_quick: MT5 +38.3%, engine +31.9% (gap −6.38 pp)preset_amber_cog_cycle: MT5 −28.1%, engine −27.3% (gap +0.76 pp)preset_amber_cvd_order_flow_runner: MT5 −16.2%, engine −17.4% (gap −1.16 pp)preset_amber_hurst_imprint: MT5 −3.5%, engine −4.6% (gap −1.12 pp)preset_x25_mean_reversion_williams_cci_double_extreme_eurusd_rr2: MT5 −41.7%, engine −14.5% (gap +27.16 pp)preset_apex_keltner_breakout_scalp: MT5 −1.7%, engine −4.0% (gap −2.26 pp)preset_apex_macd_trend_glide: MT5 +6.3%, engine +8.9% (gap +2.57 pp)preset_aurora_cci_divergence_trend: MT5 +30.8%, engine +30.9% (gap +0.09 pp)preset_aurora_cci_hunter_quick: MT5 −11.1%, engine −5.0% (gap +6.05 pp)preset_aurora_fracdiff_thrust_runner: MT5 +0.1%, engine +12.7% (gap +12.60 pp)preset_aurora_hurst_sentinel: MT5 −15.7%, engine −13.2% (gap +2.57 pp)preset_x25_statistical_sg3_er_donchian_breakout_eurusd_rr1: MT5 −14.1%, engine −13.6% (gap +0.46 pp)preset_x25_oscillator_stoch_kd_cross_regime_eurusd_rr2: MT5 +39.4%, engine +39.3% (gap −0.08 pp)preset_x25_volatility_bollinger_bandwidth_squeeze_break_eurusd_rr2: MT5 −4.4%, engine +37.8% (gap +42.28 pp)preset_ironclad_nbar_high_killzone_quick: MT5 +14.2%, engine +12.2% (gap −2.00 pp)v2_adaptive_sizing_adapt_001: MT5 −59.9%, engine −66.6% (gap −6.73 pp)v2_protective_stop_protect_001: MT5 +59.3%, engine +70.5% (gap +11.18 pp)v2_window_reduce_window_001: MT5 −8.0%, engine −5.1% (gap +2.91 pp)v2_grid_basket_grid_007: MT5 −8.1%, engine −9.6% (gap −1.51 pp)v2_adaptive_kelly_ak_full_kelly_01_rsi: MT5 +3.9%, engine +5.4% (gap +1.42 pp)v2_anti_martingale_pyramid_am_geo_01: MT5 +7.0%, engine +23.8% (gap +16.83 pp)v2_multistep_setups_msetup_squeeze_01: MT5 −6.8%, engine −4.4% (gap +2.39 pp)v2_orderflow_micro_a1_streak_01: MT5 +26.8%, engine +6.9% (gap −19.92 pp)v2_regime_switch_rs_statsizer_hurst_a: MT5 +34.3%, engine +47.3% (gap +12.98 pp)x25w2_starved_si_amihud_illiquid_bb_fade_eurusd_h1_rr2: MT5 −41.3%, engine −34.0% (gap +7.26 pp)new_msliq_amihud_deep_ppo: MT5 −14.6%, engine −14.1% (gap +0.51 pp)new_lane3_herfindahl_concentration_adaccel_thrust: MT5 +0.0%, engine +0.0% (gap +0.00 pp)new_lane5_adaptive_ribbon_kama_vidya_tema_stack_adx: MT5 +1.6%, engine +3.6% (gap +1.99 pp)g10_r1_donch_10__adx25_macd_s3_rr2_sig: MT5 +34.0%, engine +42.5% (gap +8.47 pp)g10_r3_0001_Cband: MT5 +10.6%, engine +2.0% (gap −8.66 pp)g10_r3_0085_Ettm: MT5 +100.4%, engine +73.0% (gap −27.34 pp)g10_r3_0245_Hskew: MT5 +79.1%, engine +54.0% (gap −25.04 pp)g10_r3_0299_Aexp: MT5 +80.1%, engine +60.9% (gap −19.17 pp)g10_r3_0306_Bsq: MT5 +0.2%, engine −6.4% (gap −6.54 pp)g10_r7_a_aroonBreak14_adxGt22p14_rr2trail: MT5 −0.1%, engine −6.1% (gap −6.07 pp)g10_r7_c_emaX20_adxGt22p14_rr2: MT5 +21.3%, engine +22.9% (gap +1.59 pp)g10_r7_e_hurst_rs100_donch20_bb20_rr3: MT5 +26.9%, engine +25.9% (gap −1.00 pp)g15_l1_dc20__adx14_20__rr2: MT5 +10.6%, engine +4.5% (gap −6.08 pp)g15_l2_bk_bb_ao__rr15: MT5 +7.8%, engine +16.2% (gap +8.39 pp)g15_l3_dual_atr14_adx20_bollinger50m2_rr_be1_5: MT5 −1.4%, engine −5.1% (gap −3.66 pp)g15_l5_amihud20__emacross__bollinger__betime: MT5 −8.9%, engine −0.5% (gap +8.42 pp)preset_amber_connors_reversion_trend: MT5 +3.6%, engine +6.3% (gap +2.75 pp)preset_amber_cybercycle_wave_scalp: MT5 −4.2%, engine −17.2% (gap −13.01 pp)preset_amber_garman_klass_current_trend: MT5 −28.4%, engine −7.6% (gap +20.82 pp)preset_amber_hurst_imprint_scalp: MT5 +1.4%, engine +1.3% (gap −0.13 pp)preset_x25_mean_reversion_ulcer_gated_rsi2_dip_eurusd_rr3: MT5 +12.7%, engine +14.0% (gap +1.36 pp)preset_apex_nbar_high_eject_runner: MT5 −17.0%, engine −3.8% (gap +13.22 pp)preset_crimson_adx_trend_surfer_marathon: MT5 −2.3%, engine +0.8% (gap +3.07 pp)preset_cinder_cci_divergence_marathon: MT5 +30.3%, engine +38.1% (gap +7.79 pp)preset_aurora_fractal_signal_quick: MT5 +36.3%, engine +14.3% (gap −21.93 pp)preset_cinder_fracdiff_impulse_trend: MT5 +41.7%, engine +53.8% (gap +12.18 pp)preset_x25_regime_efficiency_ratio_supertrend_rider_eurusd_rr1: MT5 −10.9%, engine +1.0% (gap +11.98 pp)preset_x25_statistical_sg3_er_donchian_breakout_eurusd_rr15: MT5 −26.5%, engine −24.4% (gap +2.07 pp)preset_x25_oscillator_stoch_kd_cross_regime_eurusd_rr15: MT5 −12.2%, engine −10.0% (gap +2.22 pp)preset_x25_volatility_garman_klass_contraction_supertrend_eurusd_rr1: MT5 +10.2%, engine +14.8% (gap +4.63 pp)preset_juniper_nbar_high_session_play_trend: MT5 −5.8%, engine −5.7% (gap +0.15 pp)v2_adaptive_sizing_adapt_002: MT5 −45.5%, engine −29.6% (gap +15.91 pp)v2_protective_stop_protect_002: MT5 +178.9%, engine +269.2% (gap +90.31 pp)v2_window_reduce_window_002: MT5 +12.5%, engine +15.7% (gap +3.14 pp)v2_grid_basket_grid_011: MT5 −12.5%, engine −13.9% (gap −1.41 pp)v2_adaptive_kelly_ak_full_kelly_02_ema: MT5 +0.6%, engine +1.1% (gap +0.53 pp)v2_anti_martingale_pyramid_am_geo_02: MT5 +42.6%, engine +89.8% (gap +47.22 pp)v2_multistep_setups_msetup_squeeze_02: MT5 −10.1%, engine −10.0% (gap +0.12 pp)v2_orderflow_micro_a1_streak_02: MT5 +8.6%, engine +4.8% (gap −3.82 pp)v2_regime_switch_rs_statsizer_hurst_b: MT5 +3.8%, engine +14.2% (gap +10.40 pp)x25w2_starved_si_pin_bar_donchian_reversal_eurusd_h1_rr3: MT5 +4.3%, engine +3.5% (gap −0.72 pp)new_msliq_amihud_deep_vidya: MT5 +63.6%, engine +109.7% (gap +46.07 pp)new_lane3_vp_poc_break_herfindahl_adaccel: MT5 +0.0%, engine +0.0% (gap +0.00 pp)new_lane5_donchian_breakout_efficiency_kama_slope: MT5 −18.0%, engine −18.8% (gap −0.83 pp)g10_r1_donch_10__adx25_none_s2_rr2: MT5 +15.4%, engine +4.0% (gap −11.42 pp)g10_r3_0002_Cband: MT5 −3.0%, engine −9.7% (gap −6.66 pp)g10_r3_0086_Ettm: MT5 +5.2%, engine +3.6% (gap −1.56 pp)g10_r3_0246_Hskew: MT5 +79.5%, engine +61.9% (gap −17.63 pp)g10_r3_0300_Aexp: MT5 +72.8%, engine +54.7% (gap −18.05 pp)g10_r3_0307_Bsq: MT5 +6.1%, engine +4.4% (gap −1.71 pp)g10_r7_a_aroonBreak14_adxGt22p20_rr2: MT5 −4.8%, engine −1.0% (gap +3.79 pp)g10_r7_c_emaX20_adxGt22p14_rv_rr3: MT5 +2.7%, engine +10.0% (gap +7.22 pp)g10_r7_e_hurst_rs100_donch20_bb30_rr2trail: MT5 −8.0%, engine −8.8% (gap −0.75 pp)g15_l1_dc20__adx14_20__rr3: MT5 +17.1%, engine +23.0% (gap +5.84 pp)g15_l2_bk_bb_cmo14__betime: MT5 −2.4%, engine +4.4% (gap +6.83 pp)g15_l3_dual_atr14_adx20_bollinger50m2_rr_be2: MT5 −1.3%, engine −6.2% (gap −4.88 pp)g15_l5_amihud20__emacross__bollinger__rr2: MT5 +22.9%, engine +34.3% (gap +11.32 pp)preset_amber_rsi_reversion: MT5 −11.6%, engine +7.4% (gap +19.02 pp)preset_amber_sinewave_oscillator_quick: MT5 +3.4%, engine −19.0% (gap −22.45 pp)preset_amber_vwap_z_flow_trend: MT5 +4.3%, engine +17.0% (gap +12.65 pp)preset_amber_rsi_imprint_quick: MT5 −7.8%, engine −4.2% (gap +3.63 pp)preset_x25_mean_reversion_ulcer_gated_rsi2_dip_eurusd_rr1: MT5 −11.4%, engine +7.2% (gap +18.63 pp)preset_apex_nbar_high_eject_trend: MT5 +8.9%, engine +8.5% (gap −0.39 pp)preset_x25_trend_hilbert_trendline_price_cross_eurusd_rr1: MT5 −21.2%, engine −36.2% (gap −15.00 pp)preset_drift_connors_split_marathon: MT5 +61.0%, engine +93.2% (gap +32.13 pp)preset_basalt_connors_hunter_marathon: MT5 −17.2%, engine −9.6% (gap +7.63 pp)preset_glacier_fracdiff_momentum_scalp: MT5 −37.8%, engine −31.7% (gap +6.18 pp)preset_x25_regime_fractal_dimension_keltner_fade_eurusd_rr1: MT5 −0.0%, engine −0.1% (gap −0.04 pp)preset_x25_statistical_sg8_hurst_supertrend_ride_eurusd_rr1: MT5 −1.2%, engine +5.7% (gap +6.93 pp)v2_oscillator_confluence_quad_all_meanrev_01: MT5 +8.1%, engine +12.1% (gap +4.08 pp)preset_x25_volatility_parkinson_expansion_rsi_reversal_eurusd_rr15: MT5 +6.5%, engine +24.9% (gap +18.43 pp)preset_kraken_nbar_high_session_play_runner: MT5 −4.0%, engine −3.9% (gap +0.07 pp)v2_adaptive_sizing_adapt_003: MT5 +49.3%, engine +50.4% (gap +1.07 pp)v2_protective_stop_protect_003: MT5 −5.4%, engine −7.7% (gap −2.32 pp)v2_window_reduce_window_003: MT5 +12.1%, engine +17.1% (gap +5.03 pp)MT5 Strategy Tester returnengine return
Total return over the window: RoboticEA engine (vertical) against the MetaTrader 5 Strategy Tester (horizontal), same ticks, 100 strategies. A dot on the diagonal is a perfect match. Hollow rings are stateful strategies (grids, sizing that reacts to prior trades), which compound one different fill into a bigger gap.

6.06 pp

median absolute return gap, real ticks

47%

of strategies within 5 pp of the tester

67%

within 10 pp

90.31 pp

the single worst gap

Read that as agreement of the same order of magnitude, not identity. The worst gaps are concentrated in strategies whose result compounds: the median gap was 3.65 pp where the MT5 return was under 20% in size (66 strategies) and 12.58 pp where it was 20% or more (32). Stateful strategies, whose position size reacts to earlier trades, had a lower median gap (3.14 pp against 6.18 pp for the rest) but the fattest tail.

StrategyEngineMT5GapTrades (engine / MT5)
Protective Stop 002+269.2%+178.9%+90.31 pp124 / 115
Anti-Martingale 002+89.8%+42.6%+47.22 pp136 / 128
Quartz Deepening-Liquidity VIDYA Cross+109.7%+63.6%+46.07 pp82 / 79
Cinder Bandwidth Squeeze (Trend)+37.8%−4.4%+42.28 pp114 / 111
Drift Connors Split (Marathon)+93.2%+61.0%+32.13 pp166 / 174
Cinder TTM Squeeze MACD histogram Release (K1.5 BB20)+73.0%+100.4%−27.34 pp144 / 141
The 6 widest gaps on real ticks, with the reason we can measure from the run record. This is the tail of the distribution, printed rather than omitted.

The reasons attached to those rows in the run record are mundane: a stateful sizing strategy where one different fill is amplified, a volume-reading strategy, or a large compounded return that turns a small fill difference into many points. One row has no cause we could measure, and we say so rather than inventing one.

Test a strategy the way you will run it

Build a strategy, see its per-year record and drawdown, then export readable MQL5 and run it in your own MetaTrader 5 tester. Free tier, no card.

Try it free

One cause, three symptoms

Three symptoms that turned out to be the same thing

Before the real-tick run we had three separate anomalies on the books, each with its own theory. All three moved when only the tick stream changed.

1. Stops that filled at the wrong price

In a four-point stream, a price between the open and the high has nothing to fill against, so the tester fills such a stop at the bar's extreme instead. Across the paired strategies the median bias (engine minus MT5) fell from +4.92 pp to +1.59 pp, the engine was the higher of the two on 67 strategies before and 60 after, and 55 of 97 strategies were closer to the tester on real ticks. The clearest single case, preset x25 oscillator stoch kd cross regime eurusd rr2: engine +69.0% against MT5 +9.3% on the four-point stream (+59.64 pp), and +39.3% against +39.4% on real ticks (−0.08 pp).

2. A tick-volume series that was a constant

Volume-reading indicators use the tester’s tick count. A four-points-per-bar stream makes that count 48 for every hourly bar, so a volume indicator sees a flat line. The strategies that read volume (11 of them traded on at least one side) had a median gap of 18.35 pp against 7.26 pp for the rest on the bar-derived stream, a penalty of 11.09 pp. On real ticks it was 8.42 pp against 5.95 pp, a penalty of 2.47 pp: 78% of it gone, not all of it. What remains is plausibly a different volume series (recorded size in our engine, tick count in the tester); we have not confirmed that.

3. Grid strategies that made impossible money

On the bar-derived stream, 10 grid strategies returned between +190% and +5508% in the MT5 tester while the engine, on the same strategies, reported between −44% and +32%. A grid adds to a position as price moves against it and closes the basket on a small retracement. One plausible mechanism is that a stream visiting the high before the low inside every bar hands it a tidy retracement after every excursion; we saw the effect, and did not isolate that mechanism.

MT5 return on a bar-derived tick stream, grid strategies10 grid strategies, MT5 return between +190% and +5508% on a four-point-per-bar stream, against an engine result that is modest for every one.ENGINE, SAME STRATEGYv2_grid_basket_grid_003: MT5 +565.7%, engine −17.8%grid_003+566%−17.8%v2_grid_basket_grid_004: MT5 +4963.2%, engine +32.2%grid_004+4963%+32.2%v2_grid_basket_grid_005: MT5 +866.6%, engine −6.0%grid_005+867%−6.0%v2_grid_basket_grid_007: MT5 +265.5%, engine −2.6%grid_007+265%−2.6%v2_grid_basket_grid_008: MT5 +1097.1%, engine −23.1%grid_008+1097%−23.1%v2_grid_basket_grid_009: MT5 +2195.2%, engine −25.5%grid_009+2195%−25.5%v2_grid_basket_grid_010: MT5 +190.0%, engine −13.6%grid_010+190%−13.6%v2_grid_basket_grid_011: MT5 +275.8%, engine −6.5%grid_011+276%−6.5%v2_grid_basket_grid_012: MT5 +329.3%, engine −26.2%grid_012+329%−26.2%v2_grid_basket_grid_014: MT5 +5508.4%, engine −44.0%grid_014+5508%−44.0%log scale
MetaTrader 5 return of 10 grid strategies when the tester replayed a four-points-per-M5-bar stream, on a LOG axis (bar length = log of 1 + return) because the range spans 29x. The engine's result for the same strategy is printed at the right.
Grid strategyMT5, bar-derivedEngineEngine minus MT5
grid_003+565.73%−17.84%−583.57 pp
grid_004+4963.23%+32.17%−4931.06 pp
grid_005+866.60%−5.96%−872.56 pp
grid_007+265.47%−2.59%−268.06 pp
grid_008+1097.07%−23.12%−1120.19 pp
grid_009+2195.16%−25.50%−2220.66 pp
grid_010+189.96%−13.56%−203.52 pp
grid_011+275.75%−6.46%−282.21 pp
grid_012+329.32%−26.17%−355.49 pp
grid_014+5508.42%−43.97%−5552.39 pp
MetaTrader 5 return on the four-point stream against the engine, for every grid strategy where both runs completed the full window.

Only two of those grids have a real-tick MT5 report on disk. Both fall to ordinary losses:

Grid strategyMT5, real ticksEngine, real ticksGapTrades (engine / MT5)
grid_007−8.1%−9.6%−1.51 pp530 / 475
grid_011−12.5%−13.9%−1.41 pp576 / 510
From the tester's own HTML reports and the engine record. Baskets book each leg as its own deal in MT5, so both sides are counted per leg.

Two strategies are not a distribution, and we are not claiming one. What they show is direction: the absurd figures were a property of the stream, and they disappear when the stream is real.

The result we did not expect

Exact trade counts fell while the money got closer

On the bar-derived stream, the engine and the tester took exactly the same number of trades on 43 of 97 strategies. On real ticks that dropped to 5. If you judge agreement by trade count, the real-tick run looks much worse. If you judge it by money, it is better. Both are true, and they are the same fact.

Share of strategies within N trades of the testerCumulative share of paired strategies whose engine and MT5 trade counts differ by at most N.bar-derived ticksreal ticksexact: bar-derived ticks 43%, real ticks 3%exact43%3%±1: bar-derived ticks 67%, real ticks 11%±167%11%±2: bar-derived ticks 75%, real ticks 21%±275%21%±5: bar-derived ticks 86%, real ticks 50%±586%50%±10: bar-derived ticks 92%, real ticks 81%±1092%81%share of strategies
Share of the 95 paired strategies whose engine and MT5 trade counts differ by at most N trades. On the bar-derived stream almost all agree to within a trade or two; on real ticks agreement is looser, even as the return gap shrinks.
Trade-count gapBar-derived ticksReal ticks
exact43%3%
within 167%11%
within 275%21%
within 586%50%
within 1092%81%
within 2094%96%
Same numbers as the chart. "Exact" is 43% against 3% here because this table counts only strategies that traded on at least one side; the headline of 43 against 5 also counts the 2 that took no trades in either tool.

The mechanism is simple. When both tools replay the same coarse path, they evaluate every signal on the same bar closes and reach the same decisions by construction — and the same coarseness is what mis-fills the stops, so the two agree with each other and both are wrong about the money. On real ticks, each tool builds its own candles from a very large tick set (the two counts differ by 0.06%), a marginal signal can land on either side of its threshold, and the trade lists drift apart while the fills become realistic. Even the price a candle is built from matters: building the engine’s bars from the mid rather than the bid, which is what the tester charts, took exact matches from 5 to 9 of 100 while making the money gap worse (6.84 pp against 5.95 pp).

There is also a residual we cannot explain from the records: on real ticks the engine took more trades than the tester on 88 of the 95 strategies where the counts differed. The direction is consistent, so it is probably systematic, and we have not found its cause.

43 → 5

strategies with an exact trade-count match, bar-derived then real ticks

Out of 97. Meanwhile the median return gap fell from 8.97 pp to 6.07 pp. Agreement on trade count was never the goal: a real account experiences fills and money, not a tally of trades.

Honest limits

What this does and does not prove

  • One instrument, one timeframe, one window. Every row is EURUSD H1, 2023-02-01 to 2023-12-31, one seed, one $10,000 account, on a curated 100-strategy set. Nothing here speaks to gold, indices, other timeframes or other years.
  • Agreement with MT5 is not proof of live fills. It shows two simulators agree when fed identical ticks. Both legs used Dukascopy ticks; your broker’s feed, spreads and slippage differ, and a live account will differ again.
  • About 6 pp is the honest number, not one point. Only 47% of strategies were within 5 pp of the tester. Use the engine to rank and stress strategies, and the tester to confirm before you trust a number.
  • One suspect row. preset_x25_mean_reversion_williams_cci_double_extreme_eurusd_rr2 shows the widest trade-count gap. An earlier audit found that row contaminated by tester-container reuse, so the MT5 side of it is probably a harness artefact. It was not re-run for this post; leaving it out moves the median to 6.05 pp.
  • The volume set is a classification, not a proof. The strategies flagged as volume-reading come from an earlier audit’s list rather than a fresh read of every spec.
  • Earlier boards looked tighter. Boards we published earlier showed smaller gaps under a different, bar-fed set-up. We could not reconcile them with the run records that remain, so we do not quote them.

Practical

A checklist for trusting a backtest

  • Read the tester journal before the report. It states how many ticks and bars were generated. A count near 48 per hourly bar means the run replayed roughly four points per five-minute bar, not recorded ticks.
  • Choose the real-ticks model when you want fills to mean something. In our harness the tester’s real-ticks mode on a custom symbol that held only bars produced no trades at all, so if a run comes back empty, check the tick history before the strategy.
  • Charge the spread the instrument really had. On the ticks we used it averaged 0.34 pips, 1.30 at the 99th percentile, and 1.38 in the 21:00 UTC hour. A constant will flatter or punish a strategy depending on when it trades.
  • Set swap, commission and account type deliberately. We zeroed swap and commission on both sides to isolate the tick effect. Baskets and grids are booked per leg on a hedging account, so compare their returns, not their trade counts.
  • Be careful with volume indicators. They read tick counts, which are only meaningful when the ticks are real.
  • Run it twice and treat the spread as your error bar. The same MT5 strategy moved a median 7.75 pp between the two tick models (the largest move was 121.6 pp). A single tester run is one draw.

RoboticEA is built around that loop: backtest with a per-year record and the drawdown beside every return, export readable MQL5, then confirm in your own MetaTrader 5. See how the workflow fits together, the full parity study table, and start free.

FAQ

MT5 tester accuracy, answered

Why does my MT5 backtest differ from another run of the same EA?

Most often because the two runs replayed different price paths. The tester replays whatever tick history it is given, and the path inside each bar decides whether a stop or a target is hit first and at what price. In our test the same MetaTrader 5 strategies moved by a median 7.75 percentage points of total return between a tick stream built from bars and a real-tick stream, with the largest move 121.6 points. Costs (spread, swap, commission) and the account model (hedging or netting) are the other two inputs worth checking.

What is the difference between synthetic ticks and real ticks in the MT5 Strategy Tester?

Synthetic ticks are manufactured from bars; real ticks are recorded quotes. In our first run the only price history was bars, so our importer wrote four points per five-minute bar (open, high, low, close), which gave the tester 273,200 ticks for the window. The real-tick run replayed 24,596,614, about 90 times as many. A real five-minute bar held a median 283 ticks and a real hourly bar a median 3,461, against 48 for the synthetic stream. We measured a bar-derived stream and a real one; we did not test the tester's own built-in tick generator.

How accurate is the MT5 Strategy Tester on real ticks?

Accurate enough to rank and stress strategies, not accurate enough to treat one run as exact. Running 97 strategies through both the MT5 tester and RoboticEA's engine on the same real ticks, the median gap in total return was 6.07 percentage points, down from 8.97 on a bar-derived stream, and 46% of strategies landed within 5 points of each other. That is agreement between two simulators on one instrument (EURUSD, H1, 2023-02-01 to 2023-12-31), not proof of live fills.

Why does my grid or martingale EA show huge returns in the tester?

One documented cause is a tick stream that is too coarse. On the four-points-per-bar stream, 10 grid strategies returned between +190% and +5508% in the MT5 tester, while the engine, on the same strategies, reported between -44% and +32%. Two of them have a real-tick MT5 report, and there the same strategies returned -8.13% and -12.54%. Two strategies are not a distribution, but the direction is clear: an implausible grid result is worth re-running on real ticks before you believe it.

Why is tick volume (iVolume) constant in my backtest?

If the tester replays four synthetic points per five-minute bar, every hourly bar has exactly 48 of them, so tick volume is flat. We measured 47.97 ticks per hourly bar in the tester journal. On strategies that read volume, the median gap to the engine was 18.35 percentage points against 7.26 for the rest, a penalty of 11.09; on real ticks the penalty fell to 2.47. Real ticks make the count meaningful.

Why did exact trade-count matches fall when we switched to real ticks?

Because agreement on trade count and agreement on money are different things. On the bar-derived stream both tools walked the same coarse path and reached the same decisions by construction: 43 of 97 strategies took exactly the same number of trades, but the money gap was larger (8.97 percentage points median). On real ticks each tool builds its own candles from millions of ticks, marginal signals flip, and exact matches fell to 5 while the median money gap fell to 6.07.

Does matching MT5 mean my backtest will match live trading?

No. Matching the tester shows two simulators agree when fed identical ticks; it does not model your broker's feed, spreads, slippage or requotes. Even on identical real ticks the gap was not zero: a median 6.06 percentage points across the 98 strategies that traded, with 67% within 10 points and a worst case of 90.31. Our test covered one instrument, one timeframe and one window, with both sides using the same Dukascopy ticks. Treat a backtest as evidence to test further, not a forecast.

Risk disclaimer

Every figure on this page is the output of a historical simulation, not a live trading record. Past performance does not predict future results. Backtests contain no information about the future and a strategy that was profitable over a historical window can lose money from the day it is deployed. Trading leveraged instruments carries a substantial risk of loss. Nothing here is investment advice or a recommendation to trade. RoboticEA is research and engineering software: it does not execute trades, hold funds or manage accounts. See our Terms §4 for the full statement.