Enhanced ORB Strategy Optimization Guide
Exact Start / Step / End values for the NinjaTrader 8 Strategy Analyzer (Optimize tab), the order to optimize them in, and how to avoid curve-fitting.
HOW THE SYSTEM WORKS
- Builds an Opening Range from OrStartTime to OrEndTime, marking the high and low of that window.
- Trades a separate, independently configurable window from TradeStartTime to TradeEndTime. No trade can occur until the OR period has closed AND the trade window is open.
- Entries fire on a closed-bar cross: long on CrossAbove(Close, OR High), short on CrossBelow(Close, OR Low). The signal is evaluated against the bar's close, not an intrabar wick.
- Two optional entry filters (minimum breakout size, volume vs. 20-period average) sit in front of every signal.
- Two independent stop-loss modes (fixed-tick, OR-level) plus a profit target, breakeven, and trailing stop handle exits.
This strategy is fully deterministic: pure price/volume/time logic, no machine learning, no trained model, and no randomness anywhere in the code. The same settings on the same data produce the identical result every single run. A single backtest is trustworthy as-is - there is no reproducibility check to perform and no non-determinism warning needed anywhere in this guide.
UNITS: TICKS
All *Ticks inputs (StopLossTicks, TargetTicks, etc.) are in ticks, the instrument's own minimum price increment (e.g. 0.25 on ES = 1 tick). NinjaTrader has no 4-digit/5-digit broker ambiguity - a tick is a tick for the instrument you're testing. Just confirm your chart's instrument and TickSize before typing the ranges below; scale them up for wider-ranging instruments (e.g. NQ, CL) and down for tighter ones.
STRATEGY ANALYZER SETUP
- Use real historical tick or minute data for the instrument/session you intend to trade. Garbage data = garbage results.
- Period: 1-2+ years of the instrument's regular session. Hold back the most recent ~25-30% for out-of-sample validation, or use the Strategy Analyzer's Walk-Forward Optimization feature to automate the split.
- Optimizer: NinjaTrader 8 Strategy Analyzer, Optimize tab. Use the Genetic optimization method once a table's grid gets large; use Exhaustive/Brute Force for small grids (e.g. a single bool).
- Fitness/sort: Profit Factor or a custom criterion that weighs drawdown - don't sort by Net Profit alone (see READING RESULTS).
- Bools optimize as Start 0, Step 1, End 1 (tests both false/off and true/on).
TIME PARAMETERS (set/test manually, do NOT numeric grid-sweep)
OrStartTime / OrEndTime : the Opening Range window TradeStartTime / TradeEndTime : the trade window (must start at or after OrEndTime)
These are DateTime/time-of-day properties, not plain integers - the Strategy Analyzer's Optimize tab does not sweep DateTime inputs in a useful way. Instead, test a handful of sensible combinations by hand, e.g. a 15/30/45-minute OR window measured from the session open, with the trade window starting exactly at OrEndTime and running to your session's usual close. Compare a few full runs and lock the winner before touching anything else.
OPTIMIZATION TABLES (Start / Step / End)
GROUP A: CORE - OR WINDOW + TRADE WINDOW (set first, manually)
| Input | Default | Note |
|---|---|---|
| OrStartTime | 09:30 | session open; test manually, not a sweep |
| OrEndTime | 10:00 | try OR windows of 15 / 30 / 45 min from open |
| TradeStartTime | 10:00 | keep equal to OrEndTime unless testing a delay |
| TradeEndTime | 15:45 | usual pre-close cutoff; test a couple of values |
(No Start/Step/End table here - hand-pick a few combinations, run each as a full backtest, and lock the best before moving to Group B.)
GROUP B: STOP LOSS
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| UseStopLoss | 0 | 1 | 1 | bool, default true: enables the fixed-tick stop (Pass 1 assumes this is true; see below) |
| StopLossTicks | 20 | 20 | 200 | fixed-tick stop (default 80) |
| UseORLevelStops | 0 | 1 | 1 | bool: fixed-tick vs OR-level stop |
| ORStopBufferTicks | 0 | 2 | 20 | only if UseORLevelStops = true |
Run this as TWO SEPARATE optimization passes, not one combined grid: Pass 1 - UseORLevelStops forced FALSE, UseStopLoss forced TRUE: sweep StopLossTicks alone. (Leaving UseStopLoss false here would mean the position has NO stop loss at all, since UseORLevelStops is also false in this pass - don't test that combination unless you deliberately want a no-stop run.) Pass 2 - UseORLevelStops forced TRUE: sweep ORStopBufferTicks alone (StopLossTicks and UseStopLoss are irrelevant to this pass).
The strategy's own entry logic picks the OR-level stop first whenever UseORLevelStops is true (see the code's "IMPORTANT: UseORLevelStops works independently of UseStopLoss" comment) - StopLossTicks is not even read for stop placement in that case, so optimizing both stop inputs in the same grid wastes iterations on a combination that never resolves the way the grid implies. Pick a winner from each pass, then carry forward whichever stop MODE (fixed-tick or OR-level) produced the better, more stable result into Group C.
GROUP C: PROFIT TARGET
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| TargetTicks | 40 | 20 | 300 | profit target (default 120) |
Optimize Group B and Group C TOGETHER (in whichever stop-mode pass you're running) - stop distance and target distance interact directly through the risk:reward ratio and win rate, so sweeping TargetTicks against a single fixed StopLossTicks (or ORStopBufferTicks) value will miss the real edge. Run the full StopLossTicks x TargetTicks grid (or ORStopBufferTicks x TargetTicks grid) for each stop-mode pass.
GROUP D: BREAKEVEN (default ON)
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| BreakEvenTriggerTicks | 20 | 10 | 100 | default 40 |
| BreakEvenOffsetTicks | 0 | 5 | 30 | default 10 |
(UseBreakEven defaults to true in this strategy. See GROUP E below for why you should not enable Group E at the same time.)
GROUP E: TRAILING STOP (optional, OFF by default)
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| TrailingStopTriggerTicks | 20 | 10 | 150 | default 60 |
| TrailingStopOffsetTicks | 10 | 10 | 100 | default 30 |
CRITICAL: Never enable UseBreakEven and UseTrailingStop together, and never optimize Group D and Group E in the same pass. With the defaults, breakeven's trigger (40 ticks) is LOWER than trailing's trigger (60 ticks), so breakeven fires first and moves the stop via SetStopLoss() while the trailing-stop logic is independently placing its own initial protective stop order every bar until ITS trigger is reached - two stop-management paths acting on the same position before trailing ever activates. The strategy's own code has an explicit comment warning about this conflict. Also note: whenever UseTrailingStop is true, the entry-time stop selection (fixed-tick or OR-level, Group B) is skipped entirely - the trailing-stop system manages its own initial stop using StopLossTicks regardless of UseORLevelStops. If you want the OR-level stop mode from Group B, keep UseTrailingStop off. Pick ONE of Group D or Group E, never both.
GROUP F: TRADE MANAGEMENT
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| MaxTradesPerDay | 1 | 1 | 3 | default 1 |
| RequireClosedCandleBreakout | 0 | 1 | 1 | bool, default true |
GROUP G: ENTRY FILTERS (one at a time)
| Input | Start | Step | End | Note |
|---|---|---|---|---|
| UseMinimumBreakoutSize | 0 | 1 | 1 | bool, default true |
| MinimumBreakoutTicks | 2 | 2 | 20 | only if filter on (default 5) |
| UseVolumeFilter | 0 | 1 | 1 | bool, default true |
| VolumeMultiplier | 0.8 | 0.2 | 2.0 | only if filter on (default 1.2) |
SET, DON'T OPTIMIZE
ContractQty : your own position-size choice, not an edge parameter. Set it to what you'll actually trade live; optimizing it only rescales P&L, it doesn't change the edge.
DO NOT TOUCH (purely cosmetic, no effect on trading logic)
ShowORBox, ORBoxOpacity, ShowORLines, ORLineWidth, ORBoxColorSerialized / ORBoxColorSerializable, ORLineColorSerialized / ORLineColorSerializable.
PHASES (order; lock each before the next)
- Pick the OR window and trade window (Group A) - manual, a few sensible combinations (e.g. 15/30/45-minute OR windows from the session open). Lock it before proceeding.
- Optimize Group B + C TOGETHER, run as two separate stop-mode passes (fixed-tick StopLossTicks x TargetTicks, then OR-level ORStopBufferTicks x TargetTicks). Compare the two passes and lock whichever stop mode + target combination wins.
- Group F (trade management / frequency) - MaxTradesPerDay, RequireClosedCandleBreakout.
- Group G (entry filters) - one filter at a time: first UseMinimumBreakoutSize/MinimumBreakoutTicks alone, then UseVolumeFilter/VolumeMultiplier alone, keeping the other at its locked/default state while you test each.
- Choose ONE of Group D (breakeven) or Group E (trailing stop) as a final optional overlay on top of the already-locked settings from steps 1-4. Never enable both. Optimize it last, against the locked OR window, trade window, stop mode, target, trade management, and filters.
READING RESULTS / VALIDATION
- Don't sort by Net Profit alone. In the Strategy Analyzer's optimization results grid, weigh:
- Profit Factor > 1.2 (>1.5 is strong)
- Max Drawdown - can you stomach it live?
- Sharpe Ratio - rewards smoother, more consistent equity
- Total trades - 100+ over the test period is ideal; under 30 is noise, don't trust it
- CLUSTER TEST: the best row should be surrounded by other good rows in the grid. If its neighbors are losers, it's a curve-fit - discard it and pick from a broad, stable plateau of good results instead of an isolated spike.
- OUT-OF-SAMPLE VALIDATION: use the Strategy Analyzer's Walk-Forward Optimization feature, or do it manually - optimize on an older slice of data, then run the locked winner UNCHANGED on a held-out, more recent slice. Keep the setting only if it holds up with similar character on the unseen data; if it collapses, it was curve-fit - go back and pick a broader, coarser setting from the cluster.
- Repeat per instrument and per session, since OR window, filters, and stop/target behavior are all session- and instrument-dependent.
QUICK-START CHECKLIST
- OR window and trade window chosen and fixed (Group A, manual)
- Stop mode chosen: fixed-tick XOR OR-level stop (never both)
- Group B/C (stop + target) locked from a stable cluster, per stop-mode pass
- Group F (trade management) tuned
- Group G entry filters tuned ONE AT A TIME
- Only ONE of breakeven (Group D) or trailing stop (Group E) enabled, if either - never both together
- Picked from a STABLE CLUSTER, not an isolated spike
- Healthy trade count (100+ ideal), acceptable Max Drawdown, Profit Factor > 1.2, reasonable Sharpe Ratio
- Validated with Walk-Forward Optimization / out-of-sample data - it held up unchanged
- Repeated per instrument / session
- Demo-confirmed before going live