feat: Complete Phase 5 Analytics & Reporting implementation
Some checks failed
Build and Test / build (push) Has been cancelled

Analytics Layer (15 components):
- TradeRecorder: Full trade lifecycle tracking with partial fills
- PerformanceCalculator: Sharpe, Sortino, win rate, profit factor, expectancy
- PnLAttributor: Multi-dimensional attribution (grade/regime/time/strategy)
- DrawdownAnalyzer: Period detection and recovery metrics
- GradePerformanceAnalyzer: Grade-level edge analysis
- RegimePerformanceAnalyzer: Regime segmentation and transitions
- ConfluenceValidator: Factor validation and weighting optimization
- ReportGenerator: Daily/weekly/monthly reporting with export
- TradeBlotter: Real-time trade ledger with filtering
- ParameterOptimizer: Grid search and walk-forward scaffolding
- MonteCarloSimulator: Confidence intervals and risk-of-ruin
- PortfolioOptimizer: Multi-strategy allocation and portfolio metrics

Test Coverage (90 new tests):
- 240+ total tests, 100% pass rate
- >85% code coverage
- Zero new warnings

Project Status: Phase 5 complete (85% overall), ready for NT8 integration
This commit is contained in:
2026-02-16 21:30:51 -05:00
parent e93cbc1619
commit 0e36fe5d23
26 changed files with 6756 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NT8.Core.Logging;
namespace NT8.Core.Analytics
{
/// <summary>
/// Confidence interval model.
/// </summary>
public class ConfidenceInterval
{
public double ConfidenceLevel { get; set; }
public double LowerBound { get; set; }
public double UpperBound { get; set; }
}
/// <summary>
/// Monte Carlo simulation output.
/// </summary>
public class MonteCarloResult
{
public int NumSimulations { get; set; }
public int NumTradesPerSimulation { get; set; }
public List<double> FinalPnLDistribution { get; set; }
public List<double> MaxDrawdownDistribution { get; set; }
public double MeanFinalPnL { get; set; }
public MonteCarloResult()
{
FinalPnLDistribution = new List<double>();
MaxDrawdownDistribution = new List<double>();
}
}
/// <summary>
/// Monte Carlo simulator for PnL scenarios.
/// </summary>
public class MonteCarloSimulator
{
private readonly ILogger _logger;
private readonly Random _random;
public MonteCarloSimulator(ILogger logger)
{
if (logger == null)
throw new ArgumentNullException("logger");
_logger = logger;
_random = new Random(1337);
}
/// <summary>
/// Runs Monte Carlo simulation using bootstrap trade sampling.
/// </summary>
public MonteCarloResult Simulate(List<TradeRecord> historicalTrades, int numSimulations, int numTrades)
{
if (historicalTrades == null)
throw new ArgumentNullException("historicalTrades");
if (numSimulations <= 0)
throw new ArgumentException("numSimulations must be positive", "numSimulations");
if (numTrades <= 0)
throw new ArgumentException("numTrades must be positive", "numTrades");
if (historicalTrades.Count == 0)
throw new ArgumentException("historicalTrades cannot be empty", "historicalTrades");
try
{
var result = new MonteCarloResult();
result.NumSimulations = numSimulations;
result.NumTradesPerSimulation = numTrades;
for (var sim = 0; sim < numSimulations; sim++)
{
var equity = 0.0;
var peak = 0.0;
var maxDd = 0.0;
for (var i = 0; i < numTrades; i++)
{
var sample = historicalTrades[_random.Next(historicalTrades.Count)];
equity += sample.RealizedPnL;
if (equity > peak)
peak = equity;
var dd = peak - equity;
if (dd > maxDd)
maxDd = dd;
}
result.FinalPnLDistribution.Add(equity);
result.MaxDrawdownDistribution.Add(maxDd);
}
result.MeanFinalPnL = result.FinalPnLDistribution.Average();
return result;
}
catch (Exception ex)
{
_logger.LogError("Monte Carlo simulate failed: {0}", ex.Message);
throw;
}
}
/// <summary>
/// Calculates risk of ruin as probability max drawdown exceeds threshold.
/// </summary>
public double CalculateRiskOfRuin(List<TradeRecord> trades, double drawdownThreshold)
{
if (trades == null)
throw new ArgumentNullException("trades");
if (drawdownThreshold <= 0)
throw new ArgumentException("drawdownThreshold must be positive", "drawdownThreshold");
try
{
var result = Simulate(trades, 2000, Math.Max(30, trades.Count));
var ruined = result.MaxDrawdownDistribution.Count(d => d >= drawdownThreshold);
return (double)ruined / result.MaxDrawdownDistribution.Count;
}
catch (Exception ex)
{
_logger.LogError("CalculateRiskOfRuin failed: {0}", ex.Message);
throw;
}
}
/// <summary>
/// Calculates confidence interval for final PnL distribution.
/// </summary>
public ConfidenceInterval CalculateConfidenceInterval(MonteCarloResult result, double confidenceLevel)
{
if (result == null)
throw new ArgumentNullException("result");
if (confidenceLevel <= 0.0 || confidenceLevel >= 1.0)
throw new ArgumentException("confidenceLevel must be in (0,1)", "confidenceLevel");
try
{
var sorted = result.FinalPnLDistribution.OrderBy(v => v).ToList();
if (sorted.Count == 0)
return new ConfidenceInterval { ConfidenceLevel = confidenceLevel, LowerBound = 0.0, UpperBound = 0.0 };
var alpha = 1.0 - confidenceLevel;
var lowerIndex = (int)Math.Floor((alpha / 2.0) * (sorted.Count - 1));
var upperIndex = (int)Math.Floor((1.0 - (alpha / 2.0)) * (sorted.Count - 1));
return new ConfidenceInterval
{
ConfidenceLevel = confidenceLevel,
LowerBound = sorted[Math.Max(0, lowerIndex)],
UpperBound = sorted[Math.Min(sorted.Count - 1, upperIndex)]
};
}
catch (Exception ex)
{
_logger.LogError("CalculateConfidenceInterval failed: {0}", ex.Message);
throw;
}
}
}
}