chore: Improve wrapper thread safety and logging
- Add thread-safe locking to BaseNT8StrategyWrapper - Add BasicLogger initialization - Improve null checking and error handling - Minor adapter enhancements
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NT8.Core.Common.Models;
|
||||
using NT8.Core.Risk;
|
||||
using NT8.Core.Sizing;
|
||||
@@ -10,16 +11,43 @@ namespace NT8.Adapters.NinjaTrader
|
||||
/// </summary>
|
||||
public class NT8OrderAdapter
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private IRiskManager _riskManager;
|
||||
private IPositionSizer _positionSizer;
|
||||
private readonly List<NT8OrderExecutionRecord> _executionHistory;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for NT8OrderAdapter.
|
||||
/// </summary>
|
||||
public NT8OrderAdapter()
|
||||
{
|
||||
_executionHistory = new List<NT8OrderExecutionRecord>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the order adapter with required components
|
||||
/// </summary>
|
||||
public void Initialize(IRiskManager riskManager, IPositionSizer positionSizer)
|
||||
{
|
||||
_riskManager = riskManager;
|
||||
_positionSizer = positionSizer;
|
||||
if (riskManager == null)
|
||||
{
|
||||
throw new ArgumentNullException("riskManager");
|
||||
}
|
||||
|
||||
if (positionSizer == null)
|
||||
{
|
||||
throw new ArgumentNullException("positionSizer");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_riskManager = riskManager;
|
||||
_positionSizer = positionSizer;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -27,31 +55,70 @@ namespace NT8.Adapters.NinjaTrader
|
||||
/// </summary>
|
||||
public void ExecuteIntent(StrategyIntent intent, StrategyContext context, StrategyConfig config)
|
||||
{
|
||||
if (intent == null)
|
||||
{
|
||||
throw new ArgumentNullException("intent");
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
throw new ArgumentNullException("config");
|
||||
}
|
||||
|
||||
if (_riskManager == null || _positionSizer == null)
|
||||
{
|
||||
throw new InvalidOperationException("Adapter not initialized. Call Initialize() first.");
|
||||
}
|
||||
|
||||
// Validate the intent through risk management
|
||||
var riskDecision = _riskManager.ValidateOrder(intent, context, config.RiskSettings);
|
||||
if (!riskDecision.Allow)
|
||||
try
|
||||
{
|
||||
// Log rejection and return
|
||||
// In a real implementation, we would use a proper logging system
|
||||
return;
|
||||
}
|
||||
// Validate the intent through risk management
|
||||
var riskDecision = _riskManager.ValidateOrder(intent, context, config.RiskSettings);
|
||||
if (!riskDecision.Allow)
|
||||
{
|
||||
// Risk rejected the order flow.
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate position size
|
||||
var sizingResult = _positionSizer.CalculateSize(intent, context, config.SizingSettings);
|
||||
if (sizingResult.Contracts <= 0)
|
||||
// Calculate position size
|
||||
var sizingResult = _positionSizer.CalculateSize(intent, context, config.SizingSettings);
|
||||
if (sizingResult.Contracts <= 0)
|
||||
{
|
||||
// No tradable size produced.
|
||||
return;
|
||||
}
|
||||
|
||||
// In a real implementation, this would call NT8's order execution methods.
|
||||
ExecuteInNT8(intent, sizingResult);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Log that no position size was calculated
|
||||
return;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// In a real implementation, this would call NT8's order execution methods
|
||||
// For now, we'll just log what would be executed
|
||||
ExecuteInNT8(intent, sizingResult);
|
||||
/// <summary>
|
||||
/// Gets a snapshot of executions submitted through this adapter.
|
||||
/// </summary>
|
||||
/// <returns>Execution history snapshot.</returns>
|
||||
public IList<NT8OrderExecutionRecord> GetExecutionHistory()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return new List<NT8OrderExecutionRecord>(_executionHistory);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,10 +126,32 @@ namespace NT8.Adapters.NinjaTrader
|
||||
/// </summary>
|
||||
private void ExecuteInNT8(StrategyIntent intent, SizingResult sizing)
|
||||
{
|
||||
if (intent == null)
|
||||
{
|
||||
throw new ArgumentNullException("intent");
|
||||
}
|
||||
|
||||
if (sizing == null)
|
||||
{
|
||||
throw new ArgumentNullException("sizing");
|
||||
}
|
||||
|
||||
// This is where the actual NT8 order execution would happen
|
||||
// In a real implementation, this would call NT8's EnterLong/EnterShort methods
|
||||
// along with SetStopLoss, SetProfitTarget, etc.
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_executionHistory.Add(new NT8OrderExecutionRecord(
|
||||
intent.Symbol,
|
||||
intent.Side,
|
||||
intent.EntryType,
|
||||
sizing.Contracts,
|
||||
intent.StopTicks,
|
||||
intent.TargetTicks,
|
||||
DateTime.UtcNow));
|
||||
}
|
||||
|
||||
// Example of what this might look like in NT8:
|
||||
/*
|
||||
if (intent.Side == OrderSide.Buy)
|
||||
@@ -91,11 +180,22 @@ namespace NT8.Adapters.NinjaTrader
|
||||
/// </summary>
|
||||
public void OnOrderUpdate(string orderId, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, string orderState, DateTime time, string errorCode, string nativeError)
|
||||
{
|
||||
// Pass order updates to risk manager for tracking
|
||||
if (_riskManager != null)
|
||||
if (string.IsNullOrWhiteSpace(orderId))
|
||||
{
|
||||
// In a real implementation, we would convert NT8 order data to SDK format
|
||||
// and pass it to the risk manager
|
||||
throw new ArgumentException("orderId");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Pass order updates to risk manager for tracking.
|
||||
if (_riskManager != null)
|
||||
{
|
||||
// In a real implementation, convert NT8 order data to SDK models.
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +204,83 @@ namespace NT8.Adapters.NinjaTrader
|
||||
/// </summary>
|
||||
public void OnExecutionUpdate(string executionId, string orderId, double price, int quantity, string marketPosition, DateTime time)
|
||||
{
|
||||
// Pass execution updates to risk manager for P&L tracking
|
||||
if (_riskManager != null)
|
||||
if (string.IsNullOrWhiteSpace(executionId))
|
||||
{
|
||||
// In a real implementation, we would convert NT8 execution data to SDK format
|
||||
// and pass it to the risk manager
|
||||
throw new ArgumentException("executionId");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(orderId))
|
||||
{
|
||||
throw new ArgumentException("orderId");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Pass execution updates to risk manager for P&L tracking.
|
||||
if (_riskManager != null)
|
||||
{
|
||||
// In a real implementation, convert NT8 execution data to SDK models.
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution record captured by NT8OrderAdapter for diagnostics and tests.
|
||||
/// </summary>
|
||||
public class NT8OrderExecutionRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Trading symbol.
|
||||
/// </summary>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order side.
|
||||
/// </summary>
|
||||
public OrderSide Side { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry order type.
|
||||
/// </summary>
|
||||
public OrderType EntryType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executed contract quantity.
|
||||
/// </summary>
|
||||
public int Contracts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stop-loss distance in ticks.
|
||||
/// </summary>
|
||||
public int StopTicks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Profit target distance in ticks.
|
||||
/// </summary>
|
||||
public int? TargetTicks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the execution was recorded.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for NT8OrderExecutionRecord.
|
||||
/// </summary>
|
||||
public NT8OrderExecutionRecord(string symbol, OrderSide side, OrderType entryType, int contracts, int stopTicks, int? targetTicks, DateTime timestamp)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Side = side;
|
||||
EntryType = entryType;
|
||||
Contracts = contracts;
|
||||
StopTicks = stopTicks;
|
||||
TargetTicks = targetTicks;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user