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 } }