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
164 lines
5.8 KiB
C#
164 lines
5.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|