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

@@ -20,6 +20,7 @@ namespace NT8.Core.Risk
private double _dailyPnL;
private double _maxDrawdown;
private bool _tradingHalted;
private double _configuredDailyLossLimit;
private DateTime _lastUpdate = DateTime.UtcNow;
private readonly Dictionary<string, double> _symbolExposure = new Dictionary<string, double>();
@@ -27,6 +28,15 @@ namespace NT8.Core.Risk
{
if (logger == null) throw new ArgumentNullException("logger");
_logger = logger;
_configuredDailyLossLimit = 1000.0;
}
public BasicRiskManager(ILogger logger, double dailyLossLimit)
{
if (logger == null) throw new ArgumentNullException("logger");
if (dailyLossLimit <= 0.0) throw new ArgumentException("dailyLossLimit must be positive", "dailyLossLimit");
_logger = logger;
_configuredDailyLossLimit = dailyLossLimit;
}
public RiskDecision ValidateOrder(StrategyIntent intent, StrategyContext context, RiskConfig config)
@@ -216,10 +226,8 @@ namespace NT8.Core.Risk
private void CheckEmergencyConditions(double dayPnL)
{
// Emergency halt if daily loss exceeds 90% of limit
// Using a default limit of 1000 as this method doesn't have access to config
// In Phase 1, this should be improved to use the actual config value
if (dayPnL <= -(1000 * 0.9) && !_tradingHalted)
// Emergency halt if daily loss exceeds 90% of configured limit
if (dayPnL <= -(_configuredDailyLossLimit * 0.9) && !_tradingHalted)
{
_tradingHalted = true;
_logger.LogCritical("Emergency halt triggered at 90% of daily loss limit: {0:C}", dayPnL);