diff --git a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs index 96532c0..120bd4e 100644 --- a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs +++ b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs @@ -97,7 +97,7 @@ namespace NinjaTrader.NinjaScript.Strategies public bool EnableVerboseLogging { get; set; } [NinjaScriptProperty] - [Display(Name = "Min Trade Grade (0=F,1=D,2=C,3=B,4=A,5=A+)", GroupName = "Confluence", Order = 1)] + [Display(Name = "Min Trade Grade (1=F,2=D,3=C,4=B,5=A,6=A+)", GroupName = "Confluence", Order = 1)] [Range(0, 6)] public int MinTradeGrade { get; set; } diff --git a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs index 97feea5..f34857d 100644 --- a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs +++ b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs @@ -157,9 +157,18 @@ namespace NT8.Strategies.Examples ? context.Session.SessionStart : context.CurrentTime.Date.AddHours(9.5); - if (thisSessionStart != _openingRangeStart || _currentSessionDate == DateTime.MinValue) + // Validate session start is a legitimate RTH boundary (08:00-10:30 ET). + // Rejects spurious ETH-derived session boundaries that can reset _tradeTaken mid-session. + TimeSpan sessionStartTime = thisSessionStart.TimeOfDay; + bool isValidRthSessionStart = sessionStartTime >= new TimeSpan(8, 0, 0) + && sessionStartTime <= new TimeSpan(10, 30, 0); + + if (isValidRthSessionStart) { - ResetSession(thisSessionStart); + if (thisSessionStart != _openingRangeStart || _currentSessionDate == DateTime.MinValue) + { + ResetSession(thisSessionStart); + } } // Only trade during RTH @@ -329,21 +338,48 @@ namespace NT8.Strategies.Examples var avwap = _avwapCalculator.GetCurrentValue(); var avwapSlope = _avwapCalculator.GetSlope(10); - var bars = new List(); - bars.Add(bar); - var valueArea = _volumeProfileAnalyzer.CalculateValueArea(bars); - if (context.CustomData == null) context.CustomData = new Dictionary(); + // Use pre-calculated intraday average from daily bar context when available. + // Fall back to current bar volume only if daily context is not yet populated. + double avgVol = (double)bar.Volume; + double normalAtr = bar.High - bar.Low; + + if (_config != null && _config.Parameters != null && _config.Parameters.ContainsKey("daily_bars")) + { + var src = _config.Parameters["daily_bars"]; + if (src is DailyBarContext) + { + DailyBarContext dc = (DailyBarContext)src; + + if (dc.AvgIntradayBarVolume > 0.0) + avgVol = dc.AvgIntradayBarVolume; + + // Use 10-day average daily range as the volatility baseline. + if (dc.Count >= 5 && dc.Highs != null && dc.Lows != null) + { + double sumRanges = 0.0; + int lookback = Math.Min(10, dc.Count - 1); + int start = dc.Count - 1 - lookback; + int end = dc.Count - 2; + for (int i = start; i <= end; i++) + sumRanges += dc.Highs[i] - dc.Lows[i]; + + if (lookback > 0) + normalAtr = sumRanges / lookback; + } + } + } + context.CustomData["current_bar"] = bar; context.CustomData["avwap"] = avwap; context.CustomData["avwap_slope"] = avwapSlope; context.CustomData["trend_confirm"] = avwapSlope > 0.0 ? 1.0 : 0.0; context.CustomData["current_atr"] = Math.Max(0.01, bar.High - bar.Low); - context.CustomData["normal_atr"] = Math.Max(0.01, valueArea.ValueAreaHigh - valueArea.ValueAreaLow); + context.CustomData["normal_atr"] = Math.Max(0.01, normalAtr); context.CustomData["recent_execution_quality"] = 0.8; - context.CustomData["avg_volume"] = (double)bar.Volume; + context.CustomData["avg_volume"] = avgVol; } private void ResetSession(DateTime sessionStart)