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
This commit is contained in:
44
commit-now.ps1
Normal file
44
commit-now.ps1
Normal file
@@ -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
|
||||||
BIN
deployment/backups/20260224_150604/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_150604/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_150604/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_150604/NT8.Core.dll
Normal file
Binary file not shown.
229
deployment/backups/20260224_150631/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_150631/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_150631/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_150631/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_150631/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_150631/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_150631/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_150631/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
229
deployment/backups/20260224_150834/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_150834/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_150834/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_150834/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_150834/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_150834/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_150834/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_150834/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
229
deployment/backups/20260224_154420/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_154420/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_154420/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_154420/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_154420/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_154420/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_154420/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_154420/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_154420/manifest.txt
Normal file
6
deployment/backups/20260224_154420/manifest.txt
Normal file
@@ -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
|
||||||
229
deployment/backups/20260224_155513/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_155513/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_155513/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_155513/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_155513/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_155513/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_155513/manifest.txt
Normal file
6
deployment/backups/20260224_155513/manifest.txt
Normal file
@@ -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
|
||||||
229
deployment/backups/20260224_160009/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_160009/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_160009/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_160009/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_160009/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_160009/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_160009/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_160009/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_160009/manifest.txt
Normal file
6
deployment/backups/20260224_160009/manifest.txt
Normal file
@@ -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
|
||||||
229
deployment/backups/20260224_160457/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_160457/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_160457/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_160457/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_160457/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_160457/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_160457/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_160457/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_160457/manifest.txt
Normal file
6
deployment/backups/20260224_160457/manifest.txt
Normal file
@@ -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
|
||||||
229
deployment/backups/20260224_165037/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_165037/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_165037/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_165037/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_165037/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_165037/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_165037/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_165037/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_165037/manifest.txt
Normal file
6
deployment/backups/20260224_165037/manifest.txt
Normal file
@@ -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
|
||||||
229
deployment/backups/20260224_165831/BaseNT8StrategyWrapper.cs
Normal file
229
deployment/backups/20260224_165831/BaseNT8StrategyWrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <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 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
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize SDK components
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
deployment/backups/20260224_165831/NT8.Adapters.dll
Normal file
BIN
deployment/backups/20260224_165831/NT8.Adapters.dll
Normal file
Binary file not shown.
BIN
deployment/backups/20260224_165831/NT8.Core.dll
Normal file
BIN
deployment/backups/20260224_165831/NT8.Core.dll
Normal file
Binary file not shown.
280
deployment/backups/20260224_165831/SimpleORBNT8Wrapper.cs
Normal file
280
deployment/backups/20260224_165831/SimpleORBNT8Wrapper.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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 Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for SimpleORBNT8Wrapper
|
||||||
|
/// </summary>
|
||||||
|
public SimpleORBNT8Wrapper()
|
||||||
|
{
|
||||||
|
OpeningRangeMinutes = 30;
|
||||||
|
StdDevMultiplier = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base Class Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes adapter reference for integration test assertions.
|
||||||
|
/// </summary>
|
||||||
|
public NT8Adapter GetAdapterForTesting()
|
||||||
|
{
|
||||||
|
return _nt8Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the SDK strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple ORB strategy implementation
|
||||||
|
/// </summary>
|
||||||
|
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<string, object> GetParameters()
|
||||||
|
{
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
parameters.Add("opening_range_minutes", _openingRangeMinutes);
|
||||||
|
parameters.Add("std_dev_multiplier", _stdDevMultiplier);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(Dictionary<string, object> 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<string, object>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
6
deployment/backups/20260224_165831/manifest.txt
Normal file
6
deployment/backups/20260224_165831/manifest.txt
Normal file
@@ -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
|
||||||
@@ -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 "ADAPTERS_BIN=%PROJECT_ROOT%\src\NT8.Adapters\bin\Release\net48"
|
||||||
set "WRAPPERS_SRC=%PROJECT_ROOT%\src\NT8.Adapters\Wrappers"
|
set "WRAPPERS_SRC=%PROJECT_ROOT%\src\NT8.Adapters\Wrappers"
|
||||||
set "BACKUP_ROOT=%SCRIPT_DIR%backups"
|
set "BACKUP_ROOT=%SCRIPT_DIR%backups"
|
||||||
set "MANIFEST_FILE=%BACKUP_DIR%\manifest.txt"
|
|
||||||
|
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
echo NT8 SDK Deployment
|
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"
|
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"') do set "STAMP=%%i"
|
||||||
set "BACKUP_DIR=%BACKUP_ROOT%\%STAMP%"
|
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...
|
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
|
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
|
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...
|
echo Verifying deployment files...
|
||||||
if not exist "%NT8_CUSTOM%\NT8.Core.dll" (
|
if not exist "%NT8_CUSTOM%\NT8.Core.dll" (
|
||||||
echo ERROR: Verification failed for NT8.Core.dll
|
echo ERROR: Verification failed for NT8.Core.dll
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using NinjaTrader.Gui.Tools;
|
|||||||
using NinjaTrader.NinjaScript;
|
using NinjaTrader.NinjaScript;
|
||||||
using NinjaTrader.NinjaScript.Indicators;
|
using NinjaTrader.NinjaScript.Indicators;
|
||||||
using NinjaTrader.NinjaScript.Strategies;
|
using NinjaTrader.NinjaScript.Strategies;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using NT8.Adapters.NinjaTrader;
|
using NT8.Adapters.NinjaTrader;
|
||||||
using NT8.Core.Common.Interfaces;
|
using NT8.Core.Common.Interfaces;
|
||||||
using NT8.Core.Common.Models;
|
using NT8.Core.Common.Models;
|
||||||
@@ -152,7 +151,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Print(string.Format("[SDK ERROR] Initialization failed: {0}", ex.Message));
|
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;
|
_sdkInitialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +235,7 @@ namespace NinjaTrader.NinjaScript.Strategies
|
|||||||
_logger.LogError("OnBarUpdate failed: {0}", ex.Message);
|
_logger.LogError("OnBarUpdate failed: {0}", ex.Message);
|
||||||
|
|
||||||
Print(string.Format("[SDK ERROR] OnBarUpdate: {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);
|
_riskManager = new BasicRiskManager(_logger);
|
||||||
_positionSizer = new BasicPositionSizer(_logger);
|
_positionSizer = new BasicPositionSizer(_logger);
|
||||||
_circuitBreaker = new ExecutionCircuitBreaker(
|
_circuitBreaker = new ExecutionCircuitBreaker(
|
||||||
NullLogger<ExecutionCircuitBreaker>.Instance,
|
_logger,
|
||||||
failureThreshold: 3,
|
failureThreshold: 3,
|
||||||
timeout: TimeSpan.FromSeconds(30));
|
timeout: TimeSpan.FromSeconds(30));
|
||||||
_executionAdapter = new NT8ExecutionAdapter();
|
_executionAdapter = new NT8ExecutionAdapter();
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("NT8.Core.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("NT8.Integration.Tests")]
|
||||||
|
|
||||||
namespace NT8.Core.Execution
|
namespace NT8.Core.Execution
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -11,6 +15,7 @@ namespace NT8.Core.Execution
|
|||||||
public class ExecutionCircuitBreaker
|
public class ExecutionCircuitBreaker
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly NT8.Core.Logging.ILogger _sdkLogger;
|
||||||
private readonly object _lock = new object();
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
private CircuitBreakerStatus _status;
|
private CircuitBreakerStatus _status;
|
||||||
@@ -21,24 +26,49 @@ namespace NT8.Core.Execution
|
|||||||
private readonly int _failureThreshold;
|
private readonly int _failureThreshold;
|
||||||
private readonly TimeSpan _retryTimeout;
|
private readonly TimeSpan _retryTimeout;
|
||||||
|
|
||||||
// Track execution times for latency monitoring
|
|
||||||
private readonly Queue<TimeSpan> _executionTimes;
|
private readonly Queue<TimeSpan> _executionTimes;
|
||||||
private readonly int _latencyWindowSize;
|
private readonly int _latencyWindowSize;
|
||||||
|
|
||||||
// Track order rejections
|
|
||||||
private readonly Queue<DateTime> _rejectionTimes;
|
private readonly Queue<DateTime> _rejectionTimes;
|
||||||
private readonly int _rejectionWindowSize;
|
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); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor for ExecutionCircuitBreaker
|
/// Constructor accepting NT8.Core.Logging.ILogger.
|
||||||
|
/// Use this overload from NinjaScript (.cs) files — no Microsoft.Extensions.Logging reference required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger">Logger instance</param>
|
|
||||||
/// <param name="failureThreshold">Number of failures to trigger circuit breaker</param>
|
|
||||||
/// <param name="timeout">How long to stay open before half-open</param>
|
|
||||||
/// <param name="retryTimeout">Time to wait between retries</param>
|
|
||||||
/// <param name="latencyWindowSize">Size of latency tracking window</param>
|
|
||||||
/// <param name="rejectionWindowSize">Size of rejection tracking window</param>
|
|
||||||
public ExecutionCircuitBreaker(
|
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<TimeSpan>();
|
||||||
|
_rejectionTimes = new Queue<DateTime>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor accepting Microsoft.Extensions.Logging.ILogger.
|
||||||
|
/// Use this overload from DLL projects and unit tests.
|
||||||
|
/// </summary>
|
||||||
|
internal ExecutionCircuitBreaker(
|
||||||
ILogger<ExecutionCircuitBreaker> logger,
|
ILogger<ExecutionCircuitBreaker> logger,
|
||||||
int failureThreshold = 3,
|
int failureThreshold = 3,
|
||||||
TimeSpan? timeout = null,
|
TimeSpan? timeout = null,
|
||||||
@@ -50,6 +80,7 @@ namespace NT8.Core.Execution
|
|||||||
throw new ArgumentNullException("logger");
|
throw new ArgumentNullException("logger");
|
||||||
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_sdkLogger = null;
|
||||||
_status = CircuitBreakerStatus.Closed;
|
_status = CircuitBreakerStatus.Closed;
|
||||||
_failureCount = 0;
|
_failureCount = 0;
|
||||||
_lastFailureTime = DateTime.MinValue;
|
_lastFailureTime = DateTime.MinValue;
|
||||||
@@ -58,15 +89,11 @@ namespace NT8.Core.Execution
|
|||||||
_failureThreshold = failureThreshold;
|
_failureThreshold = failureThreshold;
|
||||||
_latencyWindowSize = latencyWindowSize;
|
_latencyWindowSize = latencyWindowSize;
|
||||||
_rejectionWindowSize = rejectionWindowSize;
|
_rejectionWindowSize = rejectionWindowSize;
|
||||||
|
|
||||||
_executionTimes = new Queue<TimeSpan>();
|
_executionTimes = new Queue<TimeSpan>();
|
||||||
_rejectionTimes = new Queue<DateTime>();
|
_rejectionTimes = new Queue<DateTime>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Records execution time for latency monitoring.</summary>
|
||||||
/// Records execution time for monitoring
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="latency">Execution latency</param>
|
|
||||||
public void RecordExecutionTime(TimeSpan latency)
|
public void RecordExecutionTime(TimeSpan latency)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -74,31 +101,21 @@ namespace NT8.Core.Execution
|
|||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_executionTimes.Enqueue(latency);
|
_executionTimes.Enqueue(latency);
|
||||||
|
|
||||||
// Keep only the last N measurements
|
|
||||||
while (_executionTimes.Count > _latencyWindowSize)
|
while (_executionTimes.Count > _latencyWindowSize)
|
||||||
{
|
|
||||||
_executionTimes.Dequeue();
|
_executionTimes.Dequeue();
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have excessive latency
|
|
||||||
if (_status == CircuitBreakerStatus.Closed && HasExcessiveLatency())
|
if (_status == CircuitBreakerStatus.Closed && HasExcessiveLatency())
|
||||||
{
|
|
||||||
TripCircuitBreaker("Excessive execution latency detected");
|
TripCircuitBreaker("Excessive execution latency detected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Records an order rejection.</summary>
|
||||||
/// Records order rejection for monitoring
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reason">Reason for rejection</param>
|
|
||||||
public void RecordOrderRejection(string reason)
|
public void RecordOrderRejection(string reason)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(reason))
|
if (string.IsNullOrEmpty(reason))
|
||||||
@@ -109,31 +126,21 @@ namespace NT8.Core.Execution
|
|||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_rejectionTimes.Enqueue(DateTime.UtcNow);
|
_rejectionTimes.Enqueue(DateTime.UtcNow);
|
||||||
|
|
||||||
// Keep only the last N rejections
|
|
||||||
while (_rejectionTimes.Count > _rejectionWindowSize)
|
while (_rejectionTimes.Count > _rejectionWindowSize)
|
||||||
{
|
|
||||||
_rejectionTimes.Dequeue();
|
_rejectionTimes.Dequeue();
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have excessive rejections
|
|
||||||
if (_status == CircuitBreakerStatus.Closed && HasExcessiveRejections())
|
if (_status == CircuitBreakerStatus.Closed && HasExcessiveRejections())
|
||||||
{
|
TripCircuitBreaker(string.Format("Excessive order rejections: {0}", reason));
|
||||||
TripCircuitBreaker(String.Format("Excessive order rejections: {0}", reason));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Returns true if an order should be allowed through.</summary>
|
||||||
/// Determines if an order should be allowed based on circuit breaker state
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if order should be allowed, false otherwise</returns>
|
|
||||||
public bool ShouldAllowOrder()
|
public bool ShouldAllowOrder()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -143,26 +150,20 @@ namespace NT8.Core.Execution
|
|||||||
switch (_status)
|
switch (_status)
|
||||||
{
|
{
|
||||||
case CircuitBreakerStatus.Closed:
|
case CircuitBreakerStatus.Closed:
|
||||||
// Normal operation
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case CircuitBreakerStatus.Open:
|
case CircuitBreakerStatus.Open:
|
||||||
// Check if we should transition to half-open
|
|
||||||
if (DateTime.UtcNow >= _nextRetryTime)
|
if (DateTime.UtcNow >= _nextRetryTime)
|
||||||
{
|
{
|
||||||
_status = CircuitBreakerStatus.HalfOpen;
|
_status = CircuitBreakerStatus.HalfOpen;
|
||||||
_logger.LogWarning("Circuit breaker transitioning to Half-Open state");
|
LogWarn("Circuit breaker transitioning to Half-Open state");
|
||||||
return true; // Allow one test order
|
return true;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Circuit breaker is Open - blocking order");
|
|
||||||
return false; // Block orders
|
|
||||||
}
|
}
|
||||||
|
LogDebug("Circuit breaker is Open - blocking order");
|
||||||
|
return false;
|
||||||
|
|
||||||
case CircuitBreakerStatus.HalfOpen:
|
case CircuitBreakerStatus.HalfOpen:
|
||||||
// In half-open, allow limited operations to test if system recovered
|
LogDebug("Circuit breaker is Half-Open - allowing test order");
|
||||||
_logger.LogDebug("Circuit breaker is Half-Open - allowing test order");
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -172,15 +173,12 @@ namespace NT8.Core.Execution
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Returns the current circuit breaker state.</summary>
|
||||||
/// Gets the current state of the circuit breaker
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Current circuit breaker state</returns>
|
|
||||||
public CircuitBreakerState GetState()
|
public CircuitBreakerState GetState()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -191,20 +189,17 @@ namespace NT8.Core.Execution
|
|||||||
_status != CircuitBreakerStatus.Closed,
|
_status != CircuitBreakerStatus.Closed,
|
||||||
_status,
|
_status,
|
||||||
GetStatusReason(),
|
GetStatusReason(),
|
||||||
_failureCount
|
_failureCount);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Resets the circuit breaker to Closed state.</summary>
|
||||||
/// Resets the circuit breaker to closed state
|
|
||||||
/// </summary>
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -214,20 +209,17 @@ namespace NT8.Core.Execution
|
|||||||
_status = CircuitBreakerStatus.Closed;
|
_status = CircuitBreakerStatus.Closed;
|
||||||
_failureCount = 0;
|
_failureCount = 0;
|
||||||
_lastFailureTime = DateTime.MinValue;
|
_lastFailureTime = DateTime.MinValue;
|
||||||
|
LogInfo("Circuit breaker reset to Closed state");
|
||||||
_logger.LogInformation("Circuit breaker reset to Closed state");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Call after a successful order submission.</summary>
|
||||||
/// Called when an operation succeeds while in Half-Open state
|
|
||||||
/// </summary>
|
|
||||||
public void OnSuccess()
|
public void OnSuccess()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -237,20 +229,18 @@ namespace NT8.Core.Execution
|
|||||||
if (_status == CircuitBreakerStatus.HalfOpen)
|
if (_status == CircuitBreakerStatus.HalfOpen)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
_logger.LogInformation("Circuit breaker reset after successful test operation");
|
LogInfo("Circuit breaker reset after successful test operation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Call after a failed order submission.</summary>
|
||||||
/// Called when an operation fails
|
|
||||||
/// </summary>
|
|
||||||
public void OnFailure()
|
public void OnFailure()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -260,7 +250,6 @@ namespace NT8.Core.Execution
|
|||||||
_failureCount++;
|
_failureCount++;
|
||||||
_lastFailureTime = DateTime.UtcNow;
|
_lastFailureTime = DateTime.UtcNow;
|
||||||
|
|
||||||
// If we're in half-open and fail, go back to open
|
|
||||||
if (_status == CircuitBreakerStatus.HalfOpen ||
|
if (_status == CircuitBreakerStatus.HalfOpen ||
|
||||||
(_status == CircuitBreakerStatus.Closed && _failureCount >= _failureThreshold))
|
(_status == CircuitBreakerStatus.Closed && _failureCount >= _failureThreshold))
|
||||||
{
|
{
|
||||||
@@ -270,61 +259,35 @@ namespace NT8.Core.Execution
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("Failed to handle failure: {Message}", ex.Message);
|
LogErr(string.Format("Failed to handle OnFailure: {0}", ex.Message));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Trips the circuit breaker to open state
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reason">Reason for tripping</param>
|
|
||||||
private void TripCircuitBreaker(string reason)
|
private void TripCircuitBreaker(string reason)
|
||||||
{
|
{
|
||||||
_status = CircuitBreakerStatus.Open;
|
_status = CircuitBreakerStatus.Open;
|
||||||
_nextRetryTime = DateTime.UtcNow.Add(_timeout);
|
_nextRetryTime = DateTime.UtcNow.Add(_timeout);
|
||||||
|
LogWarn(string.Format("Circuit breaker TRIPPED: {0}. Will retry at {1}", reason, _nextRetryTime));
|
||||||
_logger.LogWarning("Circuit breaker TRIPPED: {Reason}. Will retry at {Time}",
|
|
||||||
reason, _nextRetryTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if we have excessive execution latency
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if latency is excessive</returns>
|
|
||||||
private bool HasExcessiveLatency()
|
private bool HasExcessiveLatency()
|
||||||
{
|
{
|
||||||
if (_executionTimes.Count < 3) // Need minimum samples
|
if (_executionTimes.Count < 3)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Calculate average latency
|
|
||||||
var avgLatency = TimeSpan.FromMilliseconds(_executionTimes.Average(ts => ts.TotalMilliseconds));
|
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;
|
return avgLatency.TotalSeconds > 5.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if we have excessive order rejections
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if rejections are excessive</returns>
|
|
||||||
private bool HasExcessiveRejections()
|
private bool HasExcessiveRejections()
|
||||||
{
|
{
|
||||||
if (_rejectionTimes.Count < _rejectionWindowSize)
|
if (_rejectionTimes.Count < _rejectionWindowSize)
|
||||||
return false;
|
return false;
|
||||||
|
var recentWindow = TimeSpan.FromMinutes(1);
|
||||||
// If all recent orders were rejected (100% rejection rate in window)
|
|
||||||
var recentWindow = TimeSpan.FromMinutes(1); // Check last minute
|
|
||||||
var recentRejections = _rejectionTimes.Count(dt => DateTime.UtcNow - dt <= recentWindow);
|
var recentRejections = _rejectionTimes.Count(dt => DateTime.UtcNow - dt <= recentWindow);
|
||||||
|
|
||||||
// If we have maximum possible rejections in the window, it's excessive
|
|
||||||
return recentRejections >= _rejectionWindowSize;
|
return recentRejections >= _rejectionWindowSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the reason for current status
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Reason string</returns>
|
|
||||||
private string GetStatusReason()
|
private string GetStatusReason()
|
||||||
{
|
{
|
||||||
switch (_status)
|
switch (_status)
|
||||||
@@ -332,8 +295,7 @@ namespace NT8.Core.Execution
|
|||||||
case CircuitBreakerStatus.Closed:
|
case CircuitBreakerStatus.Closed:
|
||||||
return "Normal operation";
|
return "Normal operation";
|
||||||
case CircuitBreakerStatus.Open:
|
case CircuitBreakerStatus.Open:
|
||||||
return String.Format("Tripped due to failures. Failures: {0}, Last: {1}",
|
return string.Format("Tripped due to failures. Count: {0}, Last: {1}", _failureCount, _lastFailureTime);
|
||||||
_failureCount, _lastFailureTime);
|
|
||||||
case CircuitBreakerStatus.HalfOpen:
|
case CircuitBreakerStatus.HalfOpen:
|
||||||
return "Testing recovery after timeout";
|
return "Testing recovery after timeout";
|
||||||
default:
|
default:
|
||||||
@@ -341,10 +303,7 @@ namespace NT8.Core.Execution
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Returns average execution latency.</summary>
|
||||||
/// Gets average execution time for monitoring
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Average execution time</returns>
|
|
||||||
public TimeSpan GetAverageExecutionTime()
|
public TimeSpan GetAverageExecutionTime()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -353,21 +312,17 @@ namespace NT8.Core.Execution
|
|||||||
{
|
{
|
||||||
if (_executionTimes.Count == 0)
|
if (_executionTimes.Count == 0)
|
||||||
return TimeSpan.Zero;
|
return TimeSpan.Zero;
|
||||||
|
|
||||||
return TimeSpan.FromMilliseconds(_executionTimes.Average(ts => ts.TotalMilliseconds));
|
return TimeSpan.FromMilliseconds(_executionTimes.Average(ts => ts.TotalMilliseconds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Returns rejection rate as a percentage.</summary>
|
||||||
/// Gets rejection rate for monitoring
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Rejection rate as percentage</returns>
|
|
||||||
public double GetRejectionRate()
|
public double GetRejectionRate()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -376,19 +331,14 @@ namespace NT8.Core.Execution
|
|||||||
{
|
{
|
||||||
if (_rejectionTimes.Count == 0)
|
if (_rejectionTimes.Count == 0)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
// Calculate rejections in last minute
|
|
||||||
var oneMinuteAgo = DateTime.UtcNow.AddMinutes(-1);
|
var oneMinuteAgo = DateTime.UtcNow.AddMinutes(-1);
|
||||||
var recentRejections = _rejectionTimes.Count(dt => dt >= oneMinuteAgo);
|
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;
|
return (double)recentRejections / _rejectionWindowSize * 100.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ namespace NT8.Strategies.Examples
|
|||||||
private readonly double _stdDevMultiplier;
|
private readonly double _stdDevMultiplier;
|
||||||
|
|
||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
|
private StrategyConfig _config;
|
||||||
private ConfluenceScorer _scorer;
|
private ConfluenceScorer _scorer;
|
||||||
private GradeFilter _gradeFilter;
|
private GradeFilter _gradeFilter;
|
||||||
private RiskModeManager _riskModeManager;
|
private RiskModeManager _riskModeManager;
|
||||||
@@ -98,6 +99,7 @@ namespace NT8.Strategies.Examples
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_config = config;
|
||||||
_scorer = new ConfluenceScorer(_logger, 500);
|
_scorer = new ConfluenceScorer(_logger, 500);
|
||||||
_gradeFilter = new GradeFilter();
|
_gradeFilter = new GradeFilter();
|
||||||
_riskModeManager = new RiskModeManager(_logger);
|
_riskModeManager = new RiskModeManager(_logger);
|
||||||
@@ -151,6 +153,10 @@ namespace NT8.Strategies.Examples
|
|||||||
ResetSession(context.Session != null ? context.Session.SessionStart : context.CurrentTime.Date);
|
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)
|
if (bar.Time <= _openingRangeEnd)
|
||||||
{
|
{
|
||||||
UpdateOpeningRange(bar);
|
UpdateOpeningRange(bar);
|
||||||
@@ -332,6 +338,13 @@ namespace NT8.Strategies.Examples
|
|||||||
|
|
||||||
private StrategyIntent CreateIntent(string symbol, OrderSide side, double openingRange, double lastPrice)
|
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<string, object>();
|
var metadata = new Dictionary<string, object>();
|
||||||
metadata.Add("orb_high", _openingRangeHigh);
|
metadata.Add("orb_high", _openingRangeHigh);
|
||||||
metadata.Add("orb_low", _openingRangeLow);
|
metadata.Add("orb_low", _openingRangeLow);
|
||||||
@@ -346,8 +359,8 @@ namespace NT8.Strategies.Examples
|
|||||||
side,
|
side,
|
||||||
OrderType.Market,
|
OrderType.Market,
|
||||||
null,
|
null,
|
||||||
8,
|
stopTicks,
|
||||||
16,
|
targetTicks,
|
||||||
0.75,
|
0.75,
|
||||||
"ORB breakout signal",
|
"ORB breakout signal",
|
||||||
metadata);
|
metadata);
|
||||||
|
|||||||
Reference in New Issue
Block a user