Compare commits

...

2 Commits

Author SHA1 Message Date
mo
ee4da1b607 chore: add deployment/backups and DLLs to .gitignore
Some checks failed
Build and Test / build (push) Has been cancelled
2026-03-10 15:50:53 -04:00
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
6 changed files with 155 additions and 129 deletions

7
.gitignore vendored
View File

@@ -85,3 +85,10 @@ Thumbs.db
tools/output/ tools/output/
market-data/*.csv market-data/*.csv
replay-data/ replay-data/
# Deployment backups (local only)
deployment/backups/
# Build artifacts in deployment
*.dll
*.pdb

44
commit-now.ps1 Normal file
View 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

View File

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

View File

@@ -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();

View File

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

View File

@@ -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);