Fix stop/target ordering, P&L wiring, dynamic targets, confluence wiring
Some checks failed
Build and Test / build (push) Has been cancelled

- Move SetStopLoss/SetProfitTarget before EnterLong/EnterShort in
  SubmitOrderToNT8(). NT8 requires stop/target pre-registered to the
  signal name before entry fires — calling after caused silent fallback
  to strategy defaults in backtest, nullifying all dynamic target scaling.
  Add [dynamic] tag to SUBMIT log line for observability.

- Wire OnPositionUpdate() in NT8StrategyBase to call
  _riskManager.OnPnLUpdate() and PortfolioRiskManager.ReportPnL() on
  every position change. Daily loss limit checks were evaluating against
   PnL permanently — safety net now live.

- Fix BasicRiskManager.CheckEmergencyConditions() hardcoded 1000 limit.
  Add dailyLossLimit constructor overload; emergency halt now fires at
  90% of the configured DailyLossLimit, not a hardcoded .

- Add AVWAP slope hard veto in SimpleORBStrategy.OnBar(). Trend=0.00
  (AVWAP working against trade direction) can no longer pass grade
  filter. Pre-scoring veto prevents high gap/volume scores from masking
  a directionally broken setup.

- Raise default MinTradeGrade from 4 (B) to 5 (A) in both
  SimpleORBStrategy and SimpleORBNT8 defaults.

- Scale TargetTicks dynamically in CreateIntent() based on ORB/ATR
  ratio (0.75x-1.75x of base target). Tight ORBs get extended targets;
  wide ORBs get tightened. Hard floor at stopTicks+4.

- Add NarrowRange, OrbRangeVsAtr, GapDirectionAlignment,
  BreakoutVolumeStrength, PriorDayCloseStrength default weights in
  ConfluenceScorer constructor. All 10 factors now registered.

- Fix session_open_price tracking for GapDirectionAlignment factor.
  TodayOpen in DailyBarContext now reflects first RTH bar open, not
  the breakout bar open.

- Log all 10 factor scores at signal acceptance for observability.
This commit is contained in:
2026-03-23 22:43:21 -04:00
parent ae8ac05017
commit c9e8b35f15
4 changed files with 183 additions and 16 deletions

View File

@@ -426,6 +426,46 @@ namespace NinjaTrader.NinjaScript.Strategies
_executionAdapter.ProcessExecution(orderId, executionId, price, quantity, time);
}
protected override void OnPositionUpdate(
Position position,
double averagePrice,
int quantity,
MarketPosition marketPosition)
{
if (!_sdkInitialized || _riskManager == null)
return;
try
{
double dayPnL = 0.0;
if (Account != null)
{
try
{
dayPnL = Account.Get(AccountItem.GainLoss, Currency.UsDollar);
}
catch
{
dayPnL = 0.0;
}
}
_riskManager.OnPnLUpdate(dayPnL, dayPnL);
PortfolioRiskManager.Instance.ReportPnL(Name, dayPnL);
if (EnableVerboseLogging)
Print(string.Format("[SDK] P&L update: DayPnL={0:C} Position={1} Qty={2}",
dayPnL, marketPosition, quantity));
FileLog(string.Format("PNL_UPDATE DayPnL={0:C} Position={1} Qty={2}",
dayPnL, marketPosition, quantity));
}
catch (Exception ex)
{
Print(string.Format("[SDK] OnPositionUpdate error: {0}", ex.Message));
}
}
/// <summary>
/// Handles broker connection status changes. Halts new orders on disconnect,
/// logs reconnect, and resets the connection flag when restored.
@@ -571,7 +611,7 @@ namespace NinjaTrader.NinjaScript.Strategies
_riskConfig,
_sizingConfig);
_riskManager = new BasicRiskManager(_logger);
_riskManager = new BasicRiskManager(_logger, DailyLossLimit);
_positionSizer = new BasicPositionSizer(_logger);
_circuitBreaker = new ExecutionCircuitBreaker(
_logger,
@@ -842,15 +882,25 @@ namespace NinjaTrader.NinjaScript.Strategies
FileLog(string.Format("SIGNAL {0} | Grade={1} | Score={2}", intent.Side, grade, score));
if (!string.IsNullOrEmpty(factors))
FileLog(string.Format(" Factors: {0}", factors));
FileLog(string.Format("SUBMIT {0} {1} @ Market | Stop={2} Target={3}",
FileLog(string.Format("SUBMIT {0} {1} @ Market | Stop={2} Target={3}{4}",
intent.Side,
request.Quantity,
intent.StopTicks,
intent.TargetTicks));
intent.TargetTicks,
intent.Metadata != null && intent.Metadata.ContainsKey("dynamic_target_ticks") ? " [dynamic]" : ""));
}
_executionAdapter.SubmitOrder(request, orderName);
// Register stop and target BEFORE submitting the entry order.
// NT8 requires stop/target to be pre-registered to the signal name
// so they are applied correctly in both backtest and live/SIM modes.
if (intent.StopTicks > 0)
SetStopLoss(orderName, CalculationMode.Ticks, (int)intent.StopTicks, false);
if (intent.TargetTicks.HasValue && intent.TargetTicks.Value > 0)
SetProfitTarget(orderName, CalculationMode.Ticks, (int)intent.TargetTicks.Value);
if (request.Side == OmsOrderSide.Buy)
{
if (request.Type == OmsOrderType.Market)
@@ -870,12 +920,6 @@ namespace NinjaTrader.NinjaScript.Strategies
EnterShortStopMarket(request.Quantity, (double)request.StopPrice.Value, orderName);
}
if (intent.StopTicks > 0)
SetStopLoss(orderName, CalculationMode.Ticks, (int)intent.StopTicks, false);
if (intent.TargetTicks.HasValue && intent.TargetTicks.Value > 0)
SetProfitTarget(orderName, CalculationMode.Ticks, (int)intent.TargetTicks.Value);
if (_circuitBreaker != null)
_circuitBreaker.OnSuccess();
}