Files
nt8-sdk/deployment/backups/20260224_155513/SimpleORBNT8Wrapper.cs
mo a283ef4673 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
2026-03-10 15:49:59 -04:00

281 lines
9.3 KiB
C#

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