diff --git a/commit-now.ps1 b/commit-now.ps1 new file mode 100644 index 0000000..302b1f4 --- /dev/null +++ b/commit-now.ps1 @@ -0,0 +1,44 @@ +# commit-now.ps1 - Stage and commit all current changes to Gitea +# Run from: C:\dev\nt8-sdk + +Set-Location "C:\dev\nt8-sdk" + +Write-Host "`n=== Current Git Status ===" -ForegroundColor Cyan +git status + +Write-Host "`n=== Recent Commits ===" -ForegroundColor Cyan +git log --oneline -5 + +Write-Host "`n=== Staging all changes ===" -ForegroundColor Cyan +git add -A + +Write-Host "`n=== Staged Files ===" -ForegroundColor Cyan +git status + +$commitMessage = @" +chore: checkpoint before NT8 execution wiring fix + +Current state: Strategy builds and loads correctly, passes 240+ tests, +backtest (Strategy Analyzer) works but zero trades execute on live/SIM. + +Root cause identified: NT8OrderAdapter.ExecuteInNT8() is a stub - it logs +to an internal list but never calls EnterLong/EnterShort/SetStopLoss/ +SetProfitTarget. Fix is ready in TASK_01_WIRE_NT8_EXECUTION.md. + +Task files added (ready for Kilocode): +- TASK_01_WIRE_NT8_EXECUTION.md (CRITICAL - INT8ExecutionBridge + wiring) +- TASK_02_EMERGENCY_KILL_SWITCH.md (CRITICAL - kill switch + verbose logging) +- TASK_03_WIRE_CIRCUIT_BREAKER.md (HIGH - wire ExecutionCircuitBreaker) + +Build Status: All 240+ tests passing, zero errors +Next: Run Kilocode against TASK_01, TASK_02, TASK_03 in order +"@ + +Write-Host "`n=== Committing ===" -ForegroundColor Cyan +git commit -m $commitMessage + +Write-Host "`n=== Pushing to Gitea ===" -ForegroundColor Cyan +git push + +Write-Host "`n=== Done! ===" -ForegroundColor Green +git log --oneline -3 diff --git a/deployment/backups/20260224_150604/NT8.Adapters.dll b/deployment/backups/20260224_150604/NT8.Adapters.dll new file mode 100644 index 0000000..75f6e97 Binary files /dev/null and b/deployment/backups/20260224_150604/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_150604/NT8.Core.dll b/deployment/backups/20260224_150604/NT8.Core.dll new file mode 100644 index 0000000..dd42e3d Binary files /dev/null and b/deployment/backups/20260224_150604/NT8.Core.dll differ diff --git a/deployment/backups/20260224_150631/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_150631/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_150631/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_150631/NT8.Adapters.dll b/deployment/backups/20260224_150631/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_150631/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_150631/NT8.Core.dll b/deployment/backups/20260224_150631/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_150631/NT8.Core.dll differ diff --git a/deployment/backups/20260224_150631/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_150631/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_150631/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_150834/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_150834/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_150834/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_150834/NT8.Adapters.dll b/deployment/backups/20260224_150834/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_150834/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_150834/NT8.Core.dll b/deployment/backups/20260224_150834/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_150834/NT8.Core.dll differ diff --git a/deployment/backups/20260224_150834/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_150834/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_150834/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_154420/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_154420/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_154420/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_154420/NT8.Adapters.dll b/deployment/backups/20260224_154420/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_154420/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_154420/NT8.Core.dll b/deployment/backups/20260224_154420/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_154420/NT8.Core.dll differ diff --git a/deployment/backups/20260224_154420/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_154420/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_154420/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_154420/manifest.txt b/deployment/backups/20260224_154420/manifest.txt new file mode 100644 index 0000000..e081251 --- /dev/null +++ b/deployment/backups/20260224_154420/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_154420 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/backups/20260224_155513/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_155513/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_155513/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_155513/NT8.Adapters.dll b/deployment/backups/20260224_155513/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_155513/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_155513/NT8.Core.dll b/deployment/backups/20260224_155513/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_155513/NT8.Core.dll differ diff --git a/deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_155513/manifest.txt b/deployment/backups/20260224_155513/manifest.txt new file mode 100644 index 0000000..d76899b --- /dev/null +++ b/deployment/backups/20260224_155513/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_155513 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/backups/20260224_160009/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_160009/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_160009/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_160009/NT8.Adapters.dll b/deployment/backups/20260224_160009/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_160009/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_160009/NT8.Core.dll b/deployment/backups/20260224_160009/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_160009/NT8.Core.dll differ diff --git a/deployment/backups/20260224_160009/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_160009/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_160009/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_160009/manifest.txt b/deployment/backups/20260224_160009/manifest.txt new file mode 100644 index 0000000..0797593 --- /dev/null +++ b/deployment/backups/20260224_160009/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_160009 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/backups/20260224_160457/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_160457/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_160457/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_160457/NT8.Adapters.dll b/deployment/backups/20260224_160457/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_160457/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_160457/NT8.Core.dll b/deployment/backups/20260224_160457/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_160457/NT8.Core.dll differ diff --git a/deployment/backups/20260224_160457/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_160457/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_160457/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_160457/manifest.txt b/deployment/backups/20260224_160457/manifest.txt new file mode 100644 index 0000000..61acc31 --- /dev/null +++ b/deployment/backups/20260224_160457/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_160457 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/backups/20260224_165037/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_165037/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_165037/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_165037/NT8.Adapters.dll b/deployment/backups/20260224_165037/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_165037/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_165037/NT8.Core.dll b/deployment/backups/20260224_165037/NT8.Core.dll new file mode 100644 index 0000000..ee14e46 Binary files /dev/null and b/deployment/backups/20260224_165037/NT8.Core.dll differ diff --git a/deployment/backups/20260224_165037/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_165037/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_165037/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_165037/manifest.txt b/deployment/backups/20260224_165037/manifest.txt new file mode 100644 index 0000000..f561edf --- /dev/null +++ b/deployment/backups/20260224_165037/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_165037 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/backups/20260224_165831/BaseNT8StrategyWrapper.cs b/deployment/backups/20260224_165831/BaseNT8StrategyWrapper.cs new file mode 100644 index 0000000..5811382 --- /dev/null +++ b/deployment/backups/20260224_165831/BaseNT8StrategyWrapper.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Core.Risk; +using NT8.Core.Sizing; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Base wrapper class for NT8 strategies that integrate with the SDK + /// This is a template that would be extended in actual NT8 strategy files + /// + public abstract class BaseNT8StrategyWrapper + { + private readonly object _lock = new object(); + + #region SDK Components + + protected IStrategy _sdkStrategy; + protected IRiskManager _riskManager; + protected IPositionSizer _positionSizer; + protected NT8Adapter _nt8Adapter; + protected StrategyConfig _strategyConfig; + protected ILogger _logger; + + #endregion + + #region Properties + + /// + /// Stop loss in ticks + /// + public int StopTicks { get; set; } + + /// + /// Profit target in ticks (optional) + /// + public int TargetTicks { get; set; } + + /// + /// Risk amount per trade in dollars + /// + public double RiskAmount { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for BaseNT8StrategyWrapper + /// + public BaseNT8StrategyWrapper() + { + // Set default values + StopTicks = 10; + TargetTicks = 20; + RiskAmount = 100.0; + + // Initialize SDK components with default implementations. + // Derived wrappers can replace these through InitializeSdkComponents. + _logger = new BasicLogger("BaseNT8StrategyWrapper"); + _riskManager = new BasicRiskManager(_logger); + _positionSizer = new BasicPositionSizer(_logger); + + InitializeSdkComponents(_riskManager, _positionSizer, _logger); + } + + #endregion + + #region Abstract Methods + + /// + /// Create the SDK strategy implementation + /// + protected abstract IStrategy CreateSdkStrategy(); + + #endregion + + #region Public Methods + + /// + /// Process a bar update (would be called from NT8's OnBarUpdate) + /// + public void ProcessBarUpdate(BarData barData, StrategyContext context) + { + if (barData == null) + throw new ArgumentNullException("barData"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + StrategyIntent intent; + + lock (_lock) + { + if (_sdkStrategy == null) + { + throw new InvalidOperationException("SDK strategy has not been initialized."); + } + + intent = _sdkStrategy.OnBar(barData, context); + } + + if (intent != null) + { + ExecuteIntent(intent, context); + } + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed processing bar update for {0}: {1}", context.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + + #region Private Methods + + /// + /// Initialize SDK components + /// + protected virtual void InitializeSdkComponents(IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger) + { + if (riskManager == null) + throw new ArgumentNullException("riskManager"); + if (positionSizer == null) + throw new ArgumentNullException("positionSizer"); + if (logger == null) + throw new ArgumentNullException("logger"); + + _riskManager = riskManager; + _positionSizer = positionSizer; + _logger = logger; + + _nt8Adapter = new NT8Adapter(); + _nt8Adapter.Initialize(_riskManager, _positionSizer); + + CreateSdkConfiguration(); + + _sdkStrategy = CreateSdkStrategy(); + if (_sdkStrategy == null) + throw new InvalidOperationException("CreateSdkStrategy returned null."); + + _sdkStrategy.Initialize(_strategyConfig, null, _logger); + + _logger.LogInformation("Base NT8 strategy wrapper initialized for symbol {0}", _strategyConfig.Symbol); + } + + /// + /// Create SDK configuration from parameters + /// + private void CreateSdkConfiguration() + { + // Create risk configuration + var riskConfig = new RiskConfig( + dailyLossLimit: 500.0, + maxTradeRisk: RiskAmount, + maxOpenPositions: 5, + emergencyFlattenEnabled: true + ); + + // Create sizing configuration + var sizingConfig = new SizingConfig( + method: SizingMethod.FixedDollarRisk, + minContracts: 1, + maxContracts: 100, + riskPerTrade: RiskAmount, + methodParameters: new Dictionary() + ); + + // Create strategy configuration + _strategyConfig = new StrategyConfig( + name: "NT8Strategy", + symbol: "Unknown", + parameters: new Dictionary(), + riskSettings: riskConfig, + sizingSettings: sizingConfig + ); + } + + /// + /// Execute strategy intent through NT8 + /// + private void ExecuteIntent(StrategyIntent intent, StrategyContext context) + { + if (intent == null) + throw new ArgumentNullException("intent"); + if (context == null) + throw new ArgumentNullException("context"); + + try + { + SizingResult sizingResult; + + lock (_lock) + { + if (_positionSizer == null) + { + throw new InvalidOperationException("Position sizer has not been initialized."); + } + + sizingResult = _positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings); + } + + _nt8Adapter.ExecuteIntent(intent, sizingResult); + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("Failed executing intent for {0}: {1}", intent.Symbol, ex.Message); + } + + throw; + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_165831/NT8.Adapters.dll b/deployment/backups/20260224_165831/NT8.Adapters.dll new file mode 100644 index 0000000..0a12355 Binary files /dev/null and b/deployment/backups/20260224_165831/NT8.Adapters.dll differ diff --git a/deployment/backups/20260224_165831/NT8.Core.dll b/deployment/backups/20260224_165831/NT8.Core.dll new file mode 100644 index 0000000..b00aca1 Binary files /dev/null and b/deployment/backups/20260224_165831/NT8.Core.dll differ diff --git a/deployment/backups/20260224_165831/SimpleORBNT8Wrapper.cs b/deployment/backups/20260224_165831/SimpleORBNT8Wrapper.cs new file mode 100644 index 0000000..1c85459 --- /dev/null +++ b/deployment/backups/20260224_165831/SimpleORBNT8Wrapper.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using NT8.Core.Common.Interfaces; +using NT8.Core.Common.Models; +using NT8.Core.Logging; +using NT8.Adapters.NinjaTrader; + +namespace NT8.Adapters.Wrappers +{ + /// + /// Simple ORB (Opening Range Breakout) strategy wrapper for NT8 + /// This demonstrates how to implement a strategy that works with the SDK + /// + public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper + { + #region Strategy Parameters + + /// + /// Opening range period in minutes + /// + public int OpeningRangeMinutes { get; set; } + + /// + /// Number of standard deviations for breakout threshold + /// + public double StdDevMultiplier { get; set; } + + #endregion + + #region Constructor + + /// + /// Constructor for SimpleORBNT8Wrapper + /// + public SimpleORBNT8Wrapper() + { + OpeningRangeMinutes = 30; + StdDevMultiplier = 1.0; + } + + #endregion + + #region Base Class Implementation + + /// + /// Exposes adapter reference for integration test assertions. + /// + public NT8Adapter GetAdapterForTesting() + { + return _nt8Adapter; + } + + /// + /// Create the SDK strategy implementation + /// + protected override IStrategy CreateSdkStrategy() + { + var openingRangeMinutes = OpeningRangeMinutes > 0 ? OpeningRangeMinutes : 30; + var stdDevMultiplier = StdDevMultiplier > 0.0 ? StdDevMultiplier : 1.0; + return new SimpleORBStrategy(openingRangeMinutes, stdDevMultiplier); + } + + #endregion + + #region Strategy Logic + + /// + /// Simple ORB strategy implementation + /// + private class SimpleORBStrategy : IStrategy + { + private readonly int _openingRangeMinutes; + private readonly double _stdDevMultiplier; + + private ILogger _logger; + private DateTime _currentSessionDate; + private DateTime _openingRangeStart; + private DateTime _openingRangeEnd; + private double _openingRangeHigh; + private double _openingRangeLow; + private bool _openingRangeReady; + private bool _tradeTaken; + + public StrategyMetadata Metadata { get; private set; } + + public SimpleORBStrategy(int openingRangeMinutes, double stdDevMultiplier) + { + if (openingRangeMinutes <= 0) + { + throw new ArgumentException("openingRangeMinutes"); + } + + if (stdDevMultiplier <= 0.0) + { + throw new ArgumentException("stdDevMultiplier"); + } + + _openingRangeMinutes = openingRangeMinutes; + _stdDevMultiplier = stdDevMultiplier; + + _currentSessionDate = DateTime.MinValue; + _openingRangeStart = DateTime.MinValue; + _openingRangeEnd = DateTime.MinValue; + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + + Metadata = new StrategyMetadata( + name: "Simple ORB", + description: "Opening Range Breakout strategy", + version: "1.0", + author: "NT8 SDK Team", + symbols: new string[] { "ES", "NQ", "YM" }, + requiredBars: 20 + ); + } + + public void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + _logger.LogInformation("SimpleORBStrategy initialized with OR period {0} minutes and multiplier {1:F2}", _openingRangeMinutes, _stdDevMultiplier); + } + + public StrategyIntent OnBar(BarData bar, StrategyContext context) + { + if (bar == null) + { + throw new ArgumentNullException("bar"); + } + + if (context == null) + { + throw new ArgumentNullException("context"); + } + + try + { + if (_currentSessionDate != context.CurrentTime.Date) + { + ResetSession(context.Session.SessionStart); + } + + if (bar.Time <= _openingRangeEnd) + { + UpdateOpeningRange(bar); + return null; + } + + if (!_openingRangeReady) + { + if (_openingRangeHigh > _openingRangeLow) + { + _openingRangeReady = true; + } + else + { + return null; + } + } + + if (_tradeTaken) + { + return null; + } + + var openingRange = _openingRangeHigh - _openingRangeLow; + var volatilityBuffer = openingRange * (_stdDevMultiplier - 1.0); + if (volatilityBuffer < 0) + { + volatilityBuffer = 0; + } + + var longTrigger = _openingRangeHigh + volatilityBuffer; + var shortTrigger = _openingRangeLow - volatilityBuffer; + + if (bar.Close > longTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Buy, openingRange, bar.Close); + } + + if (bar.Close < shortTrigger) + { + _tradeTaken = true; + return CreateIntent(context.Symbol, OrderSide.Sell, openingRange, bar.Close); + } + + return null; + } + catch (Exception ex) + { + if (_logger != null) + { + _logger.LogError("SimpleORBStrategy OnBar failed: {0}", ex.Message); + } + + throw; + } + } + + public StrategyIntent OnTick(TickData tick, StrategyContext context) + { + // Most strategies don't need tick-level logic + return null; + } + + public Dictionary GetParameters() + { + var parameters = new Dictionary(); + parameters.Add("opening_range_minutes", _openingRangeMinutes); + parameters.Add("std_dev_multiplier", _stdDevMultiplier); + return parameters; + } + + public void SetParameters(Dictionary parameters) + { + // Parameters are constructor-bound for deterministic behavior in this wrapper. + // Method retained for interface compatibility. + } + + private void ResetSession(DateTime sessionStart) + { + _currentSessionDate = sessionStart.Date; + _openingRangeStart = sessionStart; + _openingRangeEnd = sessionStart.AddMinutes(_openingRangeMinutes); + _openingRangeHigh = Double.MinValue; + _openingRangeLow = Double.MaxValue; + _openingRangeReady = false; + _tradeTaken = false; + } + + private void UpdateOpeningRange(BarData bar) + { + if (bar.High > _openingRangeHigh) + { + _openingRangeHigh = bar.High; + } + + if (bar.Low < _openingRangeLow) + { + _openingRangeLow = bar.Low; + } + } + + private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) + { + var metadata = new Dictionary(); + metadata.Add("orb_high", _openingRangeHigh); + metadata.Add("orb_low", _openingRangeLow); + metadata.Add("orb_range", openingRange); + metadata.Add("trigger_price", lastPrice); + metadata.Add("multiplier", _stdDevMultiplier); + + if (_logger != null) + { + _logger.LogInformation("SimpleORBStrategy generated {0} intent for {1}. OR High={2:F2}, OR Low={3:F2}, Last={4:F2}", side, symbol, _openingRangeHigh, _openingRangeLow, lastPrice); + } + + return new StrategyIntent( + symbol, + side, + OrderType.Market, + null, + 8, + 16, + 0.75, + "ORB breakout signal", + metadata); + } + } + + #endregion + } +} diff --git a/deployment/backups/20260224_165831/manifest.txt b/deployment/backups/20260224_165831/manifest.txt new file mode 100644 index 0000000..9e9d53a --- /dev/null +++ b/deployment/backups/20260224_165831/manifest.txt @@ -0,0 +1,6 @@ +Deployment manifest +Timestamp: 20260224_165831 +Source Core DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Core\bin\Release\net48\NT8.Core.dll +Source Adapters DLL: C:\dev\nt8-sdk\deployment\..\src\NT8.Adapters\bin\Release\net48\NT8.Adapters.dll +Destination Custom Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom +Destination Strategies Folder: C:\Users\billy\Documents\NinjaTrader 8\bin\Custom\Strategies diff --git a/deployment/deploy-to-nt8.bat b/deployment/deploy-to-nt8.bat index b97d40a..6d2c8e0 100644 --- a/deployment/deploy-to-nt8.bat +++ b/deployment/deploy-to-nt8.bat @@ -12,7 +12,6 @@ set "CORE_BIN=%PROJECT_ROOT%\src\NT8.Core\bin\Release\net48" set "ADAPTERS_BIN=%PROJECT_ROOT%\src\NT8.Adapters\bin\Release\net48" set "WRAPPERS_SRC=%PROJECT_ROOT%\src\NT8.Adapters\Wrappers" set "BACKUP_ROOT=%SCRIPT_DIR%backups" -set "MANIFEST_FILE=%BACKUP_DIR%\manifest.txt" echo ============================================================ echo NT8 SDK Deployment @@ -47,7 +46,8 @@ if not exist "%NT8_STRATEGIES%" ( for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"') do set "STAMP=%%i" set "BACKUP_DIR=%BACKUP_ROOT%\%STAMP%" -mkdir "%BACKUP_DIR%" >nul 2>&1 +set "MANIFEST_FILE=%BACKUP_ROOT%\%STAMP%\manifest.txt" +mkdir "%BACKUP_ROOT%\%STAMP%" >nul 2>&1 echo Backing up existing NT8 SDK files... if exist "%NT8_CUSTOM%\NT8.Core.dll" copy /Y "%NT8_CUSTOM%\NT8.Core.dll" "%BACKUP_DIR%\NT8.Core.dll" >nul @@ -88,6 +88,19 @@ if errorlevel 1 ( exit /b 1 ) +set "STRATEGIES_SRC=%PROJECT_ROOT%\src\NT8.Adapters\Strategies" +copy /Y "%STRATEGIES_SRC%\NT8StrategyBase.cs" "%NT8_STRATEGIES%\NT8StrategyBase.cs" >nul +if errorlevel 1 ( + echo ERROR: Failed to copy NT8StrategyBase.cs + exit /b 1 +) + +copy /Y "%STRATEGIES_SRC%\SimpleORBNT8.cs" "%NT8_STRATEGIES%\SimpleORBNT8.cs" >nul +if errorlevel 1 ( + echo ERROR: Failed to copy SimpleORBNT8.cs + exit /b 1 +) + echo Verifying deployment files... if not exist "%NT8_CUSTOM%\NT8.Core.dll" ( echo ERROR: Verification failed for NT8.Core.dll diff --git a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs index 89b156b..b5ed2b3 100644 --- a/src/NT8.Adapters/Strategies/NT8StrategyBase.cs +++ b/src/NT8.Adapters/Strategies/NT8StrategyBase.cs @@ -11,7 +11,6 @@ using NinjaTrader.Gui.Tools; using NinjaTrader.NinjaScript; using NinjaTrader.NinjaScript.Indicators; using NinjaTrader.NinjaScript.Strategies; -using Microsoft.Extensions.Logging.Abstractions; using NT8.Adapters.NinjaTrader; using NT8.Core.Common.Interfaces; using NT8.Core.Common.Models; @@ -152,7 +151,7 @@ namespace NinjaTrader.NinjaScript.Strategies catch (Exception ex) { Print(string.Format("[SDK ERROR] Initialization failed: {0}", ex.Message)); - Log(string.Format("[SDK ERROR] {0}", ex.ToString()), LogLevel.Error); + Log(string.Format("[SDK ERROR] {0}", ex.ToString()), NinjaTrader.Cbi.LogLevel.Error); _sdkInitialized = false; } } @@ -236,7 +235,7 @@ namespace NinjaTrader.NinjaScript.Strategies _logger.LogError("OnBarUpdate failed: {0}", ex.Message); Print(string.Format("[SDK ERROR] OnBarUpdate: {0}", ex.Message)); - Log(string.Format("[SDK ERROR] {0}", ex.ToString()), LogLevel.Error); + Log(string.Format("[SDK ERROR] {0}", ex.ToString()), NinjaTrader.Cbi.LogLevel.Error); } } @@ -321,7 +320,7 @@ namespace NinjaTrader.NinjaScript.Strategies _riskManager = new BasicRiskManager(_logger); _positionSizer = new BasicPositionSizer(_logger); _circuitBreaker = new ExecutionCircuitBreaker( - NullLogger.Instance, + _logger, failureThreshold: 3, timeout: TimeSpan.FromSeconds(30)); _executionAdapter = new NT8ExecutionAdapter(); diff --git a/src/NT8.Core/Execution/ExecutionCircuitBreaker.cs b/src/NT8.Core/Execution/ExecutionCircuitBreaker.cs index 7f5b4e9..afa95ee 100644 --- a/src/NT8.Core/Execution/ExecutionCircuitBreaker.cs +++ b/src/NT8.Core/Execution/ExecutionCircuitBreaker.cs @@ -1,8 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; +[assembly: InternalsVisibleTo("NT8.Core.Tests")] +[assembly: InternalsVisibleTo("NT8.Integration.Tests")] + namespace NT8.Core.Execution { /// @@ -11,6 +15,7 @@ namespace NT8.Core.Execution public class ExecutionCircuitBreaker { private readonly ILogger _logger; + private readonly NT8.Core.Logging.ILogger _sdkLogger; private readonly object _lock = new object(); private CircuitBreakerStatus _status; @@ -21,24 +26,49 @@ namespace NT8.Core.Execution private readonly int _failureThreshold; private readonly TimeSpan _retryTimeout; - // Track execution times for latency monitoring private readonly Queue _executionTimes; private readonly int _latencyWindowSize; - // Track order rejections private readonly Queue _rejectionTimes; private readonly int _rejectionWindowSize; + // Log helpers — route through whichever logger is available + private void LogDebug(string message) { if (_logger != null) _logger.LogDebug(message); else if (_sdkLogger != null) _sdkLogger.LogDebug(message); } + private void LogInfo(string message) { if (_logger != null) _logger.LogInformation(message); else if (_sdkLogger != null) _sdkLogger.LogInformation(message); } + private void LogWarn(string message) { if (_logger != null) _logger.LogWarning(message); else if (_sdkLogger != null) _sdkLogger.LogWarning(message); } + private void LogErr(string message) { if (_logger != null) _logger.LogError(message); else if (_sdkLogger != null) _sdkLogger.LogError(message); } + /// - /// Constructor for ExecutionCircuitBreaker + /// Constructor accepting NT8.Core.Logging.ILogger. + /// Use this overload from NinjaScript (.cs) files — no Microsoft.Extensions.Logging reference required. /// - /// Logger instance - /// Number of failures to trigger circuit breaker - /// How long to stay open before half-open - /// Time to wait between retries - /// Size of latency tracking window - /// Size of rejection tracking window public ExecutionCircuitBreaker( + NT8.Core.Logging.ILogger sdkLogger, + int failureThreshold = 3, + TimeSpan? timeout = null, + TimeSpan? retryTimeout = null, + int latencyWindowSize = 100, + int rejectionWindowSize = 10) + { + _sdkLogger = sdkLogger; + _logger = null; + _status = CircuitBreakerStatus.Closed; + _failureCount = 0; + _lastFailureTime = DateTime.MinValue; + _timeout = timeout ?? TimeSpan.FromSeconds(30); + _retryTimeout = retryTimeout ?? TimeSpan.FromSeconds(5); + _failureThreshold = failureThreshold; + _latencyWindowSize = latencyWindowSize; + _rejectionWindowSize = rejectionWindowSize; + _executionTimes = new Queue(); + _rejectionTimes = new Queue(); + } + + /// + /// Constructor accepting Microsoft.Extensions.Logging.ILogger. + /// Use this overload from DLL projects and unit tests. + /// + internal ExecutionCircuitBreaker( ILogger logger, int failureThreshold = 3, TimeSpan? timeout = null, @@ -50,6 +80,7 @@ namespace NT8.Core.Execution throw new ArgumentNullException("logger"); _logger = logger; + _sdkLogger = null; _status = CircuitBreakerStatus.Closed; _failureCount = 0; _lastFailureTime = DateTime.MinValue; @@ -58,15 +89,11 @@ namespace NT8.Core.Execution _failureThreshold = failureThreshold; _latencyWindowSize = latencyWindowSize; _rejectionWindowSize = rejectionWindowSize; - _executionTimes = new Queue(); _rejectionTimes = new Queue(); } - /// - /// Records execution time for monitoring - /// - /// Execution latency + /// Records execution time for latency monitoring. public void RecordExecutionTime(TimeSpan latency) { try @@ -74,31 +101,21 @@ namespace NT8.Core.Execution lock (_lock) { _executionTimes.Enqueue(latency); - - // Keep only the last N measurements while (_executionTimes.Count > _latencyWindowSize) - { _executionTimes.Dequeue(); - } - // Check if we have excessive latency if (_status == CircuitBreakerStatus.Closed && HasExcessiveLatency()) - { TripCircuitBreaker("Excessive execution latency detected"); - } } } catch (Exception ex) { - _logger.LogError("Failed to record execution time: {Message}", ex.Message); + LogErr(string.Format("Failed to record execution time: {0}", ex.Message)); throw; } } - /// - /// Records order rejection for monitoring - /// - /// Reason for rejection + /// Records an order rejection. public void RecordOrderRejection(string reason) { if (string.IsNullOrEmpty(reason)) @@ -109,31 +126,21 @@ namespace NT8.Core.Execution lock (_lock) { _rejectionTimes.Enqueue(DateTime.UtcNow); - - // Keep only the last N rejections while (_rejectionTimes.Count > _rejectionWindowSize) - { _rejectionTimes.Dequeue(); - } - // Check if we have excessive rejections if (_status == CircuitBreakerStatus.Closed && HasExcessiveRejections()) - { - TripCircuitBreaker(String.Format("Excessive order rejections: {0}", reason)); - } + TripCircuitBreaker(string.Format("Excessive order rejections: {0}", reason)); } } catch (Exception ex) { - _logger.LogError("Failed to record order rejection: {Message}", ex.Message); + LogErr(string.Format("Failed to record order rejection: {0}", ex.Message)); throw; } } - /// - /// Determines if an order should be allowed based on circuit breaker state - /// - /// True if order should be allowed, false otherwise + /// Returns true if an order should be allowed through. public bool ShouldAllowOrder() { try @@ -143,26 +150,20 @@ namespace NT8.Core.Execution switch (_status) { case CircuitBreakerStatus.Closed: - // Normal operation return true; case CircuitBreakerStatus.Open: - // Check if we should transition to half-open if (DateTime.UtcNow >= _nextRetryTime) { _status = CircuitBreakerStatus.HalfOpen; - _logger.LogWarning("Circuit breaker transitioning to Half-Open state"); - return true; // Allow one test order - } - else - { - _logger.LogDebug("Circuit breaker is Open - blocking order"); - return false; // Block orders + LogWarn("Circuit breaker transitioning to Half-Open state"); + return true; } + LogDebug("Circuit breaker is Open - blocking order"); + return false; case CircuitBreakerStatus.HalfOpen: - // In half-open, allow limited operations to test if system recovered - _logger.LogDebug("Circuit breaker is Half-Open - allowing test order"); + LogDebug("Circuit breaker is Half-Open - allowing test order"); return true; default: @@ -172,15 +173,12 @@ namespace NT8.Core.Execution } catch (Exception ex) { - _logger.LogError("Failed to check if order should be allowed: {Message}", ex.Message); + LogErr(string.Format("Failed to check ShouldAllowOrder: {0}", ex.Message)); throw; } } - /// - /// Gets the current state of the circuit breaker - /// - /// Current circuit breaker state + /// Returns the current circuit breaker state. public CircuitBreakerState GetState() { try @@ -191,20 +189,17 @@ namespace NT8.Core.Execution _status != CircuitBreakerStatus.Closed, _status, GetStatusReason(), - _failureCount - ); + _failureCount); } } catch (Exception ex) { - _logger.LogError("Failed to get circuit breaker state: {Message}", ex.Message); + LogErr(string.Format("Failed to get state: {0}", ex.Message)); throw; } } - /// - /// Resets the circuit breaker to closed state - /// + /// Resets the circuit breaker to Closed state. public void Reset() { try @@ -214,20 +209,17 @@ namespace NT8.Core.Execution _status = CircuitBreakerStatus.Closed; _failureCount = 0; _lastFailureTime = DateTime.MinValue; - - _logger.LogInformation("Circuit breaker reset to Closed state"); + LogInfo("Circuit breaker reset to Closed state"); } } catch (Exception ex) { - _logger.LogError("Failed to reset circuit breaker: {Message}", ex.Message); + LogErr(string.Format("Failed to reset circuit breaker: {0}", ex.Message)); throw; } } - /// - /// Called when an operation succeeds while in Half-Open state - /// + /// Call after a successful order submission. public void OnSuccess() { try @@ -237,20 +229,18 @@ namespace NT8.Core.Execution if (_status == CircuitBreakerStatus.HalfOpen) { Reset(); - _logger.LogInformation("Circuit breaker reset after successful test operation"); + LogInfo("Circuit breaker reset after successful test operation"); } } } catch (Exception ex) { - _logger.LogError("Failed to handle success in Half-Open state: {Message}", ex.Message); + LogErr(string.Format("Failed to handle OnSuccess: {0}", ex.Message)); throw; } } - /// - /// Called when an operation fails - /// + /// Call after a failed order submission. public void OnFailure() { try @@ -260,7 +250,6 @@ namespace NT8.Core.Execution _failureCount++; _lastFailureTime = DateTime.UtcNow; - // If we're in half-open and fail, go back to open if (_status == CircuitBreakerStatus.HalfOpen || (_status == CircuitBreakerStatus.Closed && _failureCount >= _failureThreshold)) { @@ -270,61 +259,35 @@ namespace NT8.Core.Execution } catch (Exception ex) { - _logger.LogError("Failed to handle failure: {Message}", ex.Message); + LogErr(string.Format("Failed to handle OnFailure: {0}", ex.Message)); throw; } } - /// - /// Trips the circuit breaker to open state - /// - /// Reason for tripping private void TripCircuitBreaker(string reason) { _status = CircuitBreakerStatus.Open; _nextRetryTime = DateTime.UtcNow.Add(_timeout); - - _logger.LogWarning("Circuit breaker TRIPPED: {Reason}. Will retry at {Time}", - reason, _nextRetryTime); + LogWarn(string.Format("Circuit breaker TRIPPED: {0}. Will retry at {1}", reason, _nextRetryTime)); } - /// - /// Checks if we have excessive execution latency - /// - /// True if latency is excessive private bool HasExcessiveLatency() { - if (_executionTimes.Count < 3) // Need minimum samples + if (_executionTimes.Count < 3) return false; - - // Calculate average latency var avgLatency = TimeSpan.FromMilliseconds(_executionTimes.Average(ts => ts.TotalMilliseconds)); - - // If average latency is more than 5 seconds, consider it excessive return avgLatency.TotalSeconds > 5.0; } - /// - /// Checks if we have excessive order rejections - /// - /// True if rejections are excessive private bool HasExcessiveRejections() { if (_rejectionTimes.Count < _rejectionWindowSize) return false; - - // If all recent orders were rejected (100% rejection rate in window) - var recentWindow = TimeSpan.FromMinutes(1); // Check last minute + var recentWindow = TimeSpan.FromMinutes(1); var recentRejections = _rejectionTimes.Count(dt => DateTime.UtcNow - dt <= recentWindow); - - // If we have maximum possible rejections in the window, it's excessive return recentRejections >= _rejectionWindowSize; } - /// - /// Gets the reason for current status - /// - /// Reason string private string GetStatusReason() { switch (_status) @@ -332,8 +295,7 @@ namespace NT8.Core.Execution case CircuitBreakerStatus.Closed: return "Normal operation"; case CircuitBreakerStatus.Open: - return String.Format("Tripped due to failures. Failures: {0}, Last: {1}", - _failureCount, _lastFailureTime); + return string.Format("Tripped due to failures. Count: {0}, Last: {1}", _failureCount, _lastFailureTime); case CircuitBreakerStatus.HalfOpen: return "Testing recovery after timeout"; default: @@ -341,10 +303,7 @@ namespace NT8.Core.Execution } } - /// - /// Gets average execution time for monitoring - /// - /// Average execution time + /// Returns average execution latency. public TimeSpan GetAverageExecutionTime() { try @@ -353,21 +312,17 @@ namespace NT8.Core.Execution { if (_executionTimes.Count == 0) return TimeSpan.Zero; - return TimeSpan.FromMilliseconds(_executionTimes.Average(ts => ts.TotalMilliseconds)); } } catch (Exception ex) { - _logger.LogError("Failed to get average execution time: {Message}", ex.Message); + LogErr(string.Format("Failed to get average execution time: {0}", ex.Message)); throw; } } - /// - /// Gets rejection rate for monitoring - /// - /// Rejection rate as percentage + /// Returns rejection rate as a percentage. public double GetRejectionRate() { try @@ -376,19 +331,14 @@ namespace NT8.Core.Execution { if (_rejectionTimes.Count == 0) return 0.0; - - // Calculate rejections in last minute var oneMinuteAgo = DateTime.UtcNow.AddMinutes(-1); var recentRejections = _rejectionTimes.Count(dt => dt >= oneMinuteAgo); - - // This is a simplified calculation - in practice you'd need to track - // total attempts to calculate accurate rate return (double)recentRejections / _rejectionWindowSize * 100.0; } } catch (Exception ex) { - _logger.LogError("Failed to get rejection rate: {Message}", ex.Message); + LogErr(string.Format("Failed to get rejection rate: {0}", ex.Message)); throw; } } diff --git a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs index 7df917f..b747218 100644 --- a/src/NT8.Strategies/Examples/SimpleORBStrategy.cs +++ b/src/NT8.Strategies/Examples/SimpleORBStrategy.cs @@ -19,6 +19,7 @@ namespace NT8.Strategies.Examples private readonly double _stdDevMultiplier; private ILogger _logger; + private StrategyConfig _config; private ConfluenceScorer _scorer; private GradeFilter _gradeFilter; private RiskModeManager _riskModeManager; @@ -98,6 +99,7 @@ namespace NT8.Strategies.Examples try { _logger = logger; + _config = config; _scorer = new ConfluenceScorer(_logger, 500); _gradeFilter = new GradeFilter(); _riskModeManager = new RiskModeManager(_logger); @@ -151,6 +153,10 @@ namespace NT8.Strategies.Examples ResetSession(context.Session != null ? context.Session.SessionStart : context.CurrentTime.Date); } + // Only trade during RTH + if (context.Session == null || !context.Session.IsRth) + return null; + if (bar.Time <= _openingRangeEnd) { UpdateOpeningRange(bar); @@ -332,6 +338,13 @@ namespace NT8.Strategies.Examples private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice) { + var stopTicks = _config != null && _config.Parameters.ContainsKey("StopTicks") + ? (int)_config.Parameters["StopTicks"] + : 8; + var targetTicks = _config != null && _config.Parameters.ContainsKey("TargetTicks") + ? (int)_config.Parameters["TargetTicks"] + : 16; + var metadata = new Dictionary(); metadata.Add("orb_high", _openingRangeHigh); metadata.Add("orb_low", _openingRangeLow); @@ -346,8 +359,8 @@ namespace NT8.Strategies.Examples side, OrderType.Market, null, - 8, - 16, + stopTicks, + targetTicks, 0.75, "ORB breakout signal", metadata);