Backtesting
MZpack strategies process the market tick by tick — their logic runs off NinjaTrader's tick stream (OnMarketData), not off finished bars. Even a strategy whose logic runs on bar close is driven by the first tick of the next bar. Because of this, a meaningful historical backtest needs tick-level data, and the way you supply that data depends on what the strategy consumes.
There are two paths:
- Strategy Analyzer + Tick Replay — for order flow strategies that need only historical trades and best Bid/Ask prices.
- Market Replay (Playback) — for strategies that need data Tick Replay cannot reconstruct: best Bid/Ask volumes or the full order book (Level 2 / DOM), neither of which NinjaTrader stores historically.
Choosing your path
| Strategy uses… | Data required | Backtest path |
|---|---|---|
| Footprint, delta, volume profile, big trades | Historical trades + best Bid/Ask prices | Strategy Analyzer + Tick Replay |
| Iceberg (Hard/Soft), DOM pressure/support, Smart/Predatory trades | Best Bid/Ask volumes (full Level 1 quote sizes) | Market Replay (Playback) or live |
| mzMarketDepth | Full order book (Level 2 / DOM) | Market Replay (Playback) or live |
What separates the paths is what Tick Replay can reconstruct. Tick Replay replays historical trades and best bid/ask prices — enough for footprint, delta, volume profile, and big trades. It does not reconstruct the volumes resting at the best bid/ask, nor the full order book. So features that need bid/ask volumes (iceberg Hard/Soft, DOM pressure/support, Smart/Predatory trades) or the full depth (mzMarketDepth) can only be exercised against Market Replay or live data. See Order Flow — Data Levels for the distinction.
mzBigTrade's Tape iceberg algorithm is the exception: it derives hidden volume from each trade's fill composition instead of from bid/ask volumes or the order book, so it also works on historical bars under Tick Replay — including in timestamps-only reconstruction (see below).
Data is only half the picture. How MZpack aggregates ticks into trades also differs between historical and live data unless you configure it — see Tape reconstruction, which is what makes big trades in a backtest match the ones you see live.
Prerequisites: Tick Replay
Both paths that use historical data depend on Tick Replay, which reconstructs the tick-by-tick record (including historical Bid/Ask) that order flow calculations require. Enabling it is a two-step switch — both are required:
- Global — turn on
Tools ▸ Options ▸ Market Data ▸ Show Tick Replay. This only makes the Tick Replay option available. - Per data series — turn on Tick Replay in the chart's
Data Seriesproperties (and, for a backtest, on the Strategy Analyzer's data series). This actually enables historical Bid/Ask processing for that series.
Without Tick Replay, order flow indicators cannot reconstruct historical data — they display an on-chart warning ("Enable 'Tick Replay' option from DataSeries properties…") and produce no historical plots, which in a backtest means no signals and no trades. Tick Replay is resource-intensive, so keep Days to load as small as your test allows (typically 1–14).
Path 1 — Strategy Analyzer + Tick Replay
This is the primary path for order flow strategies.
1. Enable the Backtesting parameter
MZpack strategies are not active in the historical state by default, but the Strategy Analyzer runs in the historical state only. The strategy's Backtesting parameter is what lets it run on historical data — without it the strategy stays idle in the Analyzer and produces no trades. Enable it one of two ways:
- From the UI — turn on the Backtesting parameter (MZpack category) in the strategy settings window.
- From code — set
EnableBacktesting= trueinState.SetDefaults:
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
// Required for backtesting in Strategy Analyzer
EnableBacktesting = true;
}
}
Either way, MZpack switches its indicator models to ModelIncrementRefresh.HistoricalRealtime and sets WorkingStateHistorical = true so the order flow models compute on historical bars. You don't set those properties manually.
Enabling backtesting increases historical loading time for mzFootprint and mzVolumeProfile, because their load-time optimizations must be disabled to calculate all values on historical bars. Don't enable it for normal chart trading.
2. Run the backtest
- Open the NinjaTrader Strategy Analyzer.
- Add your strategy and select the instrument and date range. Keep the range short at first — Tick Replay backtests are heavy.
- Make sure Tick Replay is enabled on the Analyzer's data series (see Prerequisites above).
- Confirm the Calculation mode matches your data feed, then run.
3. Match the calculation mode to your data
The calculation mode decides how each trade is classified as buy or sell — and how accurate your historical delta will be:
| Mode | When to use |
|---|---|
| BidAsk | Futures with Tick Replay enabled — most accurate |
| UpDownTick | Forex, crypto, stocks, or any feed without historical Bid/Ask |
| Hybrid | Markets where historical Bid/Ask is unavailable but live is (UpDownTick for history, BidAsk live) |
See Order Flow — Trade Classification and Indicators Overview — Calculation Modes for full details.
For volume profile / footprint work, the two must agree: Tick accuracy requires Tick Replay on; Minute accuracy (used to speed up very long ranges) requires Tick Replay off. A mismatch triggers a warning and suppresses historical output.
4. Tape reconstruction: matching historical to live
An exchange reports a single large market order as a burst of individual fills. MZpack's order flow core aggregates those ticks back into one reconstructed trade — and mzBigTrade's filters (trade volume, aggression, iceberg) all run against that aggregated trade, not against the raw ticks. So the aggregation rule decides which big trades exist in the first place.
In the default mode (Reconstruct tape: enable), a reconstructed trade is closed by any of three events: a newer timestamp, a change of aggressor side, or a Level 1 best bid/ask event. That third trigger is the problem for backtesting. A live feed and NinjaTrader's historical Tick Replay do not deliver the same Level 1 event stream, so the same ticks aggregate into different trades with different volumes in the Strategy Analyzer than they would live. A strategy that fires on "trade volume ≥ 100" will therefore see big trades in the backtest that it would never have seen live — and miss ones it would have caught.
Enable Reconstruct tape: timestamps only (group Orderflow). Trade boundaries then depend only on timestamp and aggressor side — the two inputs that are identical in historical and live data — which gives a 100% match between historical and live big trades. Trades sharing a timestamp are merged; Level 1 events are ignored.
What timestamps-only mode changes:
| Feature | Under timestamps-only |
|---|---|
| Big trade volume, aggression, delta | Reconstructed identically in history and live |
| Iceberg — Tape algorithm | Works (reads the trade's fill composition, not the order book) |
| Iceberg — Hard / Soft | Disabled — they read bid/ask volumes, which don't exist on historical data anyway |
| DOM pressure / DOM support | Disabled — same reason |
| Smart / Predatory trades | Unavailable historically regardless of this setting |
In the Strategy Analyzer this costs you nothing: everything the setting disables is real-time-only to begin with, and the one iceberg algorithm that does work on historical bars — Tape — keeps working.
Reconstruct tape: timestamps only is not a backtest-only switch — it changes reconstruction on live data as well. That is exactly what makes the match possible, so leave it on when trading live if you want the chart to reproduce the backtest. Turning it off live re-introduces Level 1 boundaries and your live big trades will no longer correspond to the backtested ones.
Note also that Tape iceberg volume is derived from the trade's fill composition, so its values differ between timestamps-only and the default mode — consistent between history and live, but not comparable across the two modes.
Path 2 — Market Replay (Playback)
Use Market Replay when the strategy relies on data Tick Replay cannot reconstruct — the volumes at the best bid/ask (Hard/Soft iceberg algorithms, DOM pressure/support, Smart/Predatory trade detection) or the full order book (mzMarketDepth). These work only on live or Market Replay data, because NinjaTrader stores neither historically. (The exception is mzBigTrade's Tape algorithm, which works historically under Tick Replay.)
At a high level, backtesting against Market Replay means:
- Download Market Replay data for the instrument and dates via
Tools ▸ Historical Data ▸ Load(Market Replay). - Connect the built-in Playback connection (
Connections ▸ Playback Connection). - Load the strategy on a chart and drive the session forward with the Playback controller. Fills run against NinjaTrader's
Playback101simulation account.
For the full mechanics of downloading replay data and using the Playback connection, see NinjaTrader's own documentation: Playback Connection.
The Strategy Analyzer runs on historical data only, so these strategies cannot be backtested there — Market Replay (or live) is the only way to exercise bid/ask-volume and order-book logic.
Troubleshooting
If a backtest returns no trades or the order flow looks empty, the cause is almost always the data underneath it:
- Footprint shows no data or all zeros — usually Tick Replay off, no tick data for the instrument, or a calculation-mode mismatch. See Troubleshooting — Footprint shows no data.
- Missing or corrupted
.ncdtick data (OneDrive / cloud folders) — a synced NinjaTrader data folder can leave tick files incomplete, so backtests return no trades or unreliable results. See Troubleshooting — File access errors.