fix: restore EntriesPerDirection=2 and align risk defaults
Some checks failed
Build and Test / build (push) Has been cancelled

- Restore EntriesPerDirection=2 so runner leg can enter alongside scaler.
  Replay burst protection is now handled by the State.Realtime guard in
  ProcessStrategyIntent rather than by limiting entries per direction.

- Set MaxOpenPositions=2 in SimpleORBNT8 to match scaler+runner structure.
  Previous value of 1 caused PortfolioRiskManager to block the runner.

- Confirm RiskPerTrade=100 and MaxContracts=3 as live defaults.
  The /9-contract configuration was a one-off backtest experiment and
  must not be the deployed default.

- _realtimeBarSeen field and OnBarUpdate guard confirmed present and correct.
  ProcessStrategyIntent guard: if (State == State.Realtime && !_realtimeBarSeen)
  allows backtest (State.Historical) to execute normally while blocking
  replay bars in live/SIM mode.

Backtest validation: Jan 2026 - Mar 2026, NQ, trail=20ticks
  PF=7.00, win=75%, avg winner=, avg loser=-, max DD=-
This commit is contained in:
2026-03-29 19:18:29 -04:00
parent c9e8b35f15
commit d856f3949d
2 changed files with 187 additions and 56 deletions

View File

@@ -23,6 +23,8 @@ namespace NinjaTrader.NinjaScript.Strategies
/// </summary>
public class SimpleORBNT8 : NT8StrategyBase
{
private int _lastSignalDirection;
[NinjaScriptProperty]
[Display(Name = "Opening Range Minutes", GroupName = "ORB Strategy", Order = 1)]
[Range(5, 120)]
@@ -59,16 +61,22 @@ namespace NinjaTrader.NinjaScript.Strategies
DailyLossLimit = 1000.0;
MaxTradeRisk = 200.0;
MaxOpenPositions = 1;
MaxOpenPositions = 2;
RiskPerTrade = 100.0;
MinContracts = 1;
MaxContracts = 3;
Calculate = Calculate.OnBarClose;
BarsRequiredToTrade = 50;
MinTradeGrade = 5;
EnableLongTrades = true;
// Long-only: short trades permanently disabled pending backtest confirmation
EnableShortTrades = false;
EnableAutoBreakeven = true;
BreakevenTriggerTicks = 12;
BreakevenOffsetTicks = 1;
EnableRunner = true;
RunnerTrailTicks = 12;
}
else if (State == State.Configure)
{
@@ -82,11 +90,19 @@ namespace NinjaTrader.NinjaScript.Strategies
{
if (_strategyConfig != null && BarsArray != null && BarsArray.Length > 1)
{
DailyBarContext dailyContext = BuildDailyBarContext(0, 0.0, (double)Volume[0]);
DailyBarContext dailyContext = BuildDailyBarContext(_lastSignalDirection, 0.0, (double)Volume[0]);
_strategyConfig.Parameters["daily_bars"] = dailyContext;
}
base.OnBarUpdate();
if (Position != null)
{
if (Position.MarketPosition == MarketPosition.Long)
_lastSignalDirection = 1;
else if (Position.MarketPosition == MarketPosition.Short)
_lastSignalDirection = -1;
}
}
protected override IStrategy CreateSdkStrategy()
@@ -160,6 +176,10 @@ namespace NinjaTrader.NinjaScript.Strategies
lines.Insert(endIdx + 6, string.Format("StopDollars : {0:C}", StopTicks * tickDollarValue));
lines.Insert(endIdx + 7, string.Format("TargetDollars : {0:C}", TargetTicks * tickDollarValue));
lines.Insert(endIdx + 8, string.Format("RR_Ratio : {0:F2}:1", (double)TargetTicks / StopTicks));
lines.Insert(endIdx + 9, String.Format("AutoBreakeven : {0} @ {1}ticks + {2}tick offset",
EnableAutoBreakeven, BreakevenTriggerTicks, BreakevenOffsetTicks));
lines.Insert(endIdx + 10, String.Format("Runner : {0} | Trail={1}ticks",
EnableRunner, RunnerTrailTicks));
return lines;
}
@@ -225,4 +245,3 @@ namespace NinjaTrader.NinjaScript.Strategies
}
}
}