Implement NinjaTrader 8 adapter for integration
Some checks failed
Build and Test / build (push) Has been cancelled

This commit is contained in:
Billy Valentine
2025-09-09 17:19:14 -04:00
parent 92f3732b3d
commit 63200fe9b4
7 changed files with 671 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
using System;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Interface for NinjaTrader 8 integration adapter
/// </summary>
public interface INT8Adapter
{
/// <summary>
/// Initialize the adapter with required components
/// </summary>
void Initialize(IRiskManager riskManager, IPositionSizer positionSizer);
/// <summary>
/// Convert NT8 bar data to SDK format
/// </summary>
BarData ConvertToSdkBar(string symbol, DateTime time, double open, double high, double low, double close, long volume, int barSize);
/// <summary>
/// Convert NT8 account data to SDK format
/// </summary>
AccountInfo ConvertToSdkAccount(double equity, double buyingPower, double dailyPnL, double maxDrawdown, DateTime lastUpdate);
/// <summary>
/// Convert NT8 position data to SDK format
/// </summary>
Position ConvertToSdkPosition(string symbol, int quantity, double averagePrice, double unrealizedPnL, double realizedPnL, DateTime lastUpdate);
/// <summary>
/// Execute strategy intent through NT8
/// </summary>
void ExecuteIntent(StrategyIntent intent, SizingResult sizing);
/// <summary>
/// Handle order updates from NT8
/// </summary>
void OnOrderUpdate(string orderId, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, string orderState, DateTime time, string errorCode, string nativeError);
/// <summary>
/// Handle execution updates from NT8
/// </summary>
void OnExecutionUpdate(string executionId, string orderId, double price, int quantity, string marketPosition, DateTime time);
}
}

View File

@@ -0,0 +1,92 @@
using System;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
using NT8.Core.Logging;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Main NT8 adapter implementation that integrates all components
/// </summary>
public class NT8Adapter : INT8Adapter
{
private readonly NT8DataAdapter _dataAdapter;
private readonly NT8OrderAdapter _orderAdapter;
private readonly NT8LoggingAdapter _loggingAdapter;
private IRiskManager _riskManager;
private IPositionSizer _positionSizer;
/// <summary>
/// Constructor for NT8Adapter
/// </summary>
public NT8Adapter()
{
_dataAdapter = new NT8DataAdapter();
_orderAdapter = new NT8OrderAdapter();
_loggingAdapter = new NT8LoggingAdapter();
}
/// <summary>
/// Initialize the adapter with required components
/// </summary>
public void Initialize(IRiskManager riskManager, IPositionSizer positionSizer)
{
_riskManager = riskManager;
_positionSizer = positionSizer;
_orderAdapter.Initialize(riskManager, positionSizer);
}
/// <summary>
/// Convert NT8 bar data to SDK format
/// </summary>
public BarData ConvertToSdkBar(string symbol, DateTime time, double open, double high, double low, double close, long volume, int barSizeMinutes)
{
return _dataAdapter.ConvertToSdkBar(symbol, time, open, high, low, close, volume, barSizeMinutes);
}
/// <summary>
/// Convert NT8 account data to SDK format
/// </summary>
public AccountInfo ConvertToSdkAccount(double equity, double buyingPower, double dailyPnL, double maxDrawdown, DateTime lastUpdate)
{
return _dataAdapter.ConvertToSdkAccount(equity, buyingPower, dailyPnL, maxDrawdown, lastUpdate);
}
/// <summary>
/// Convert NT8 position data to SDK format
/// </summary>
public Position ConvertToSdkPosition(string symbol, int quantity, double averagePrice, double unrealizedPnL, double realizedPnL, DateTime lastUpdate)
{
return _dataAdapter.ConvertToSdkPosition(symbol, quantity, averagePrice, unrealizedPnL, realizedPnL, lastUpdate);
}
/// <summary>
/// Execute strategy intent through NT8
/// </summary>
public void ExecuteIntent(StrategyIntent intent, SizingResult sizing)
{
// In a full implementation, this would execute the order through NT8
// For now, we'll just log what would be executed
_loggingAdapter.LogInformation("Executing intent: {0} {1} contracts at {2} ticks stop",
intent.Side, sizing.Contracts, intent.StopTicks);
}
/// <summary>
/// Handle order updates from NT8
/// </summary>
public void OnOrderUpdate(string orderId, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, string orderState, DateTime time, string errorCode, string nativeError)
{
_orderAdapter.OnOrderUpdate(orderId, limitPrice, stopPrice, quantity, filled, averageFillPrice, orderState, time, errorCode, nativeError);
}
/// <summary>
/// Handle execution updates from NT8
/// </summary>
public void OnExecutionUpdate(string executionId, string orderId, double price, int quantity, string marketPosition, DateTime time)
{
_orderAdapter.OnExecutionUpdate(executionId, orderId, price, quantity, marketPosition, time);
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using NT8.Core.Common.Models;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Data adapter for converting between NT8 and SDK formats
/// </summary>
public class NT8DataAdapter
{
/// <summary>
/// Convert NT8 bar data to SDK format
/// </summary>
public BarData ConvertToSdkBar(string symbol, DateTime time, double open, double high, double low, double close, long volume, int barSizeMinutes)
{
return new BarData(symbol, time, open, high, low, close, volume, TimeSpan.FromMinutes(barSizeMinutes));
}
/// <summary>
/// Convert NT8 account data to SDK format
/// </summary>
public AccountInfo ConvertToSdkAccount(double equity, double buyingPower, double dailyPnL, double maxDrawdown, DateTime lastUpdate)
{
return new AccountInfo(equity, buyingPower, dailyPnL, maxDrawdown, lastUpdate);
}
/// <summary>
/// Convert NT8 position data to SDK format
/// </summary>
public Position ConvertToSdkPosition(string symbol, int quantity, double averagePrice, double unrealizedPnL, double realizedPnL, DateTime lastUpdate)
{
return new Position(symbol, quantity, averagePrice, unrealizedPnL, realizedPnL, lastUpdate);
}
/// <summary>
/// Convert NT8 market session data to SDK format
/// </summary>
public MarketSession ConvertToSdkSession(DateTime sessionStart, DateTime sessionEnd, bool isRth, string sessionName)
{
return new MarketSession(sessionStart, sessionEnd, isRth, sessionName);
}
/// <summary>
/// Convert NT8 strategy context to SDK format
/// </summary>
public StrategyContext ConvertToSdkContext(string symbol, DateTime currentTime, Position currentPosition, AccountInfo account, MarketSession session, System.Collections.Generic.Dictionary<string, object> customData)
{
return new StrategyContext(symbol, currentTime, currentPosition, account, session, customData);
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using NT8.Core.Logging;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Logging adapter for integrating with NT8's logging system
/// </summary>
public class NT8LoggingAdapter : ILogger
{
/// <summary>
/// Log debug message
/// </summary>
public void LogDebug(string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
// For now, we'll just use Console.WriteLine as a placeholder
Console.WriteLine("[DEBUG] " + FormatMessage(message, args));
}
/// <summary>
/// Log information message
/// </summary>
public void LogInformation(string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
Console.WriteLine("[INFO] " + FormatMessage(message, args));
}
/// <summary>
/// Log warning message
/// </summary>
public void LogWarning(string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
Console.WriteLine("[WARN] " + FormatMessage(message, args));
}
/// <summary>
/// Log error message
/// </summary>
public void LogError(string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
Console.WriteLine("[ERROR] " + FormatMessage(message, args));
}
/// <summary>
/// Log critical message
/// </summary>
public void LogCritical(string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
Console.WriteLine("[CRITICAL] " + FormatMessage(message, args));
}
/// <summary>
/// Log error with exception
/// </summary>
public void LogError(Exception ex, string message, params object[] args)
{
// In a real implementation, this would call NT8's logging system
Console.WriteLine("[ERROR] " + FormatMessage(message, args) + " - Exception: " + ex.ToString());
}
/// <summary>
/// Format message with arguments
/// </summary>
private string FormatMessage(string message, object[] args)
{
if (args == null || args.Length == 0)
{
return message;
}
try
{
return string.Format(message, args);
}
catch
{
// If formatting fails, return the original message
return message;
}
}
}
}

View File

@@ -0,0 +1,115 @@
using System;
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
namespace NT8.Adapters.NinjaTrader
{
/// <summary>
/// Order adapter for executing trades through NT8
/// </summary>
public class NT8OrderAdapter
{
private IRiskManager _riskManager;
private IPositionSizer _positionSizer;
/// <summary>
/// Initialize the order adapter with required components
/// </summary>
public void Initialize(IRiskManager riskManager, IPositionSizer positionSizer)
{
_riskManager = riskManager;
_positionSizer = positionSizer;
}
/// <summary>
/// Execute strategy intent through NT8 order management
/// </summary>
public void ExecuteIntent(StrategyIntent intent, StrategyContext context, StrategyConfig 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)
{
// Log rejection and return
// In a real implementation, we would use a proper logging system
return;
}
// Calculate position size
var sizingResult = _positionSizer.CalculateSize(intent, context, config.SizingSettings);
if (sizingResult.Contracts <= 0)
{
// Log that no position size was calculated
return;
}
// 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>
/// Execute the order in NT8 (placeholder implementation)
/// </summary>
private void ExecuteInNT8(StrategyIntent intent, SizingResult 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.
// Example of what this might look like in NT8:
/*
if (intent.Side == OrderSide.Buy)
{
EnterLong(sizing.Contracts, "SDK_Entry");
SetStopLoss("SDK_Entry", CalculationMode.Ticks, intent.StopTicks);
if (intent.TargetTicks.HasValue)
{
SetProfitTarget("SDK_Entry", CalculationMode.Ticks, intent.TargetTicks.Value);
}
}
else if (intent.Side == OrderSide.Sell)
{
EnterShort(sizing.Contracts, "SDK_Entry");
SetStopLoss("SDK_Entry", CalculationMode.Ticks, intent.StopTicks);
if (intent.TargetTicks.HasValue)
{
SetProfitTarget("SDK_Entry", CalculationMode.Ticks, intent.TargetTicks.Value);
}
}
*/
}
/// <summary>
/// Handle order updates from NT8
/// </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)
{
// In a real implementation, we would convert NT8 order data to SDK format
// and pass it to the risk manager
}
}
/// <summary>
/// Handle execution updates from NT8
/// </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)
{
// In a real implementation, we would convert NT8 execution data to SDK format
// and pass it to the risk manager
}
}
}
}

View File

@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
using NT8.Adapters.NinjaTrader;
namespace NT8.Adapters.Wrappers
{
/// <summary>
/// Base wrapper class for NT8 strategies that integrate with the SDK
/// This is a template that would be extended in actual NT8 strategy files
/// </summary>
public abstract class BaseNT8StrategyWrapper
{
#region SDK Components
protected IStrategy _sdkStrategy;
protected IRiskManager _riskManager;
protected IPositionSizer _positionSizer;
protected NT8Adapter _nt8Adapter;
protected StrategyConfig _strategyConfig;
#endregion
#region Properties
/// <summary>
/// Stop loss in ticks
/// </summary>
public int StopTicks { get; set; }
/// <summary>
/// Profit target in ticks (optional)
/// </summary>
public int TargetTicks { get; set; }
/// <summary>
/// Risk amount per trade in dollars
/// </summary>
public double RiskAmount { get; set; }
#endregion
#region Constructor
/// <summary>
/// Constructor for BaseNT8StrategyWrapper
/// </summary>
public BaseNT8StrategyWrapper()
{
// Set default values
StopTicks = 10;
TargetTicks = 20;
RiskAmount = 100.0;
// Initialize SDK components
InitializeSdkComponents();
}
#endregion
#region Abstract Methods
/// <summary>
/// Create the SDK strategy implementation
/// </summary>
protected abstract IStrategy CreateSdkStrategy();
#endregion
#region Public Methods
/// <summary>
/// Process a bar update (would be called from NT8's OnBarUpdate)
/// </summary>
public void ProcessBarUpdate(BarData barData, StrategyContext context)
{
// Call SDK strategy logic
var intent = _sdkStrategy.OnBar(barData, context);
if (intent != null)
{
// Convert SDK results to NT8 actions
ExecuteIntent(intent, context);
}
}
#endregion
#region Private Methods
/// <summary>
/// Initialize SDK components
/// </summary>
private void InitializeSdkComponents()
{
// In a real implementation, these would be injected or properly instantiated
// For now, we'll create placeholder instances
_riskManager = null; // This would be properly instantiated
_positionSizer = null; // This would be properly instantiated
// Create NT8 adapter
_nt8Adapter = new NT8Adapter();
_nt8Adapter.Initialize(_riskManager, _positionSizer);
// Create SDK strategy
_sdkStrategy = CreateSdkStrategy();
}
/// <summary>
/// Create SDK configuration from parameters
/// </summary>
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<string, object>()
);
// Create strategy configuration
_strategyConfig = new StrategyConfig(
name: "NT8Strategy",
symbol: "Unknown",
parameters: new Dictionary<string, object>(),
riskSettings: riskConfig,
sizingSettings: sizingConfig
);
}
/// <summary>
/// Execute strategy intent through NT8
/// </summary>
private void ExecuteIntent(StrategyIntent intent, StrategyContext context)
{
// Calculate position size
var sizingResult = _positionSizer != null ?
_positionSizer.CalculateSize(intent, context, _strategyConfig.SizingSettings) :
new SizingResult(1, RiskAmount, SizingMethod.FixedDollarRisk, new Dictionary<string, object>());
// Execute through NT8 adapter
_nt8Adapter.ExecuteIntent(intent, sizingResult);
}
#endregion
}
}

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using NT8.Core.Common.Interfaces;
using NT8.Core.Common.Models;
using NT8.Core.Logging;
namespace NT8.Adapters.Wrappers
{
/// <summary>
/// Simple ORB (Opening Range Breakout) strategy wrapper for NT8
/// This demonstrates how to implement a strategy that works with the SDK
/// </summary>
public class SimpleORBNT8Wrapper : BaseNT8StrategyWrapper
{
#region Strategy Parameters
/// <summary>
/// Opening range period in minutes
/// </summary>
public int OpeningRangeMinutes { get; set; }
/// <summary>
/// Number of standard deviations for breakout threshold
/// </summary>
public double StdDevMultiplier { get; set; }
#endregion
#region Strategy State
private DateTime _openingRangeStart;
private double _openingRangeHigh;
private double _openingRangeLow;
private bool _openingRangeCalculated;
private double _rangeSize;
#endregion
#region Constructor
/// <summary>
/// Constructor for SimpleORBNT8Wrapper
/// </summary>
public SimpleORBNT8Wrapper()
{
OpeningRangeMinutes = 30;
StdDevMultiplier = 1.0;
_openingRangeCalculated = false;
}
#endregion
#region Base Class Implementation
/// <summary>
/// Create the SDK strategy implementation
/// </summary>
protected override IStrategy CreateSdkStrategy()
{
return new SimpleORBStrategy();
}
#endregion
#region Strategy Logic
/// <summary>
/// Simple ORB strategy implementation
/// </summary>
private class SimpleORBStrategy : IStrategy
{
public StrategyMetadata Metadata { get; private set; }
public SimpleORBStrategy()
{
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)
{
// Initialize strategy with configuration
// In a real implementation, we would store references to the data provider and logger
}
public StrategyIntent OnBar(BarData bar, StrategyContext context)
{
// This is where the actual strategy logic would go
// For this example, we'll just return null to indicate no trade
return null;
}
public StrategyIntent OnTick(TickData tick, StrategyContext context)
{
// Most strategies don't need tick-level logic
return null;
}
public Dictionary<string, object> GetParameters()
{
return new Dictionary<string, object>();
}
public void SetParameters(Dictionary<string, object> parameters)
{
// Set strategy parameters from configuration
}
}
#endregion
}
}