using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using NT8.Core.Analytics; using NT8.Core.Common.Models; using NT8.Core.Intelligence; using NT8.Core.Logging; namespace NT8.Core.Tests.Analytics { [TestClass] public class PerformanceCalculatorTests { private PerformanceCalculator _target; [TestInitialize] public void TestInitialize() { _target = new PerformanceCalculator(new BasicLogger("PerformanceCalculatorTests")); } [TestMethod] public void Calculate_Empty_ReturnsZeroTrades() { var m = _target.Calculate(new List()); Assert.AreEqual(0, m.TotalTrades); } [TestMethod] public void CalculateWinRate_Basic() { Assert.AreEqual(0.5, _target.CalculateWinRate(Sample()), 0.0001); } [TestMethod] public void CalculateProfitFactor_Basic() { Assert.IsTrue(_target.CalculateProfitFactor(Sample()) > 0.0); } [TestMethod] public void CalculateExpectancy_Basic() { Assert.IsTrue(_target.CalculateExpectancy(Sample()) != 0.0); } [TestMethod] public void CalculateSharpeRatio_Short_ReturnsZero() { Assert.AreEqual(0.0, _target.CalculateSharpeRatio(new List(), 0.0), 0.0001); } [TestMethod] public void CalculateMaxDrawdown_Basic() { Assert.IsTrue(_target.CalculateMaxDrawdown(Sample()) >= 0.0); } [TestMethod] public void CalculateSortinoRatio_Basic() { Assert.IsTrue(_target.CalculateSortinoRatio(Sample(), 0.0) >= 0.0 || _target.CalculateSortinoRatio(Sample(), 0.0) < 0.0); } [TestMethod] public void Calculate_Null_Throws() { Assert.ThrowsException(() => _target.Calculate(null)); } [TestMethod] public void WinRate_Null_Throws() { Assert.ThrowsException(() => _target.CalculateWinRate(null)); } [TestMethod] public void ProfitFactor_Null_Throws() { Assert.ThrowsException(() => _target.CalculateProfitFactor(null)); } [TestMethod] public void Expectancy_Null_Throws() { Assert.ThrowsException(() => _target.CalculateExpectancy(null)); } [TestMethod] public void Sharpe_Null_Throws() { Assert.ThrowsException(() => _target.CalculateSharpeRatio(null, 0)); } [TestMethod] public void Sortino_Null_Throws() { Assert.ThrowsException(() => _target.CalculateSortinoRatio(null, 0)); } [TestMethod] public void MaxDrawdown_Null_Throws() { Assert.ThrowsException(() => _target.CalculateMaxDrawdown(null)); } [TestMethod] public void Calculate_ReportsWinsAndLosses() { var m = _target.Calculate(Sample()); Assert.AreEqual(2, m.Wins); Assert.AreEqual(2, m.Losses); } [TestMethod] public void Calculate_NetProfitComputed() { var m = _target.Calculate(Sample()); Assert.AreEqual(10.0, m.NetProfit, 0.0001); } [TestMethod] public void Calculate_RecoveryFactorComputed() { var m = _target.Calculate(Sample()); Assert.IsTrue(m.RecoveryFactor >= 0.0); } [TestMethod] public void ProfitFactor_NoLosses_Infinite() { var list = new List(); list.Add(Trade(10)); Assert.AreEqual(double.PositiveInfinity, _target.CalculateProfitFactor(list)); } [TestMethod] public void Expectancy_Empty_Zero() { Assert.AreEqual(0.0, _target.CalculateExpectancy(new List()), 0.0001); } [TestMethod] public void MaxDrawdown_Empty_Zero() { Assert.AreEqual(0.0, _target.CalculateMaxDrawdown(new List()), 0.0001); } private static List Sample() { return new List { Trade(50), Trade(-25), Trade(15), Trade(-30) }; } private static TradeRecord Trade(double pnl) { var t = new TradeRecord(); t.TradeId = Guid.NewGuid().ToString(); t.Symbol = "ES"; t.StrategyName = "S"; t.EntryTime = DateTime.UtcNow; t.ExitTime = DateTime.UtcNow.AddMinutes(1); t.Side = OrderSide.Buy; t.Quantity = 1; t.EntryPrice = 100; t.ExitPrice = 101; t.RealizedPnL = pnl; t.UnrealizedPnL = 0; t.Grade = TradeGrade.B; t.ConfluenceScore = 0.7; t.RiskMode = RiskMode.PCP; t.VolatilityRegime = VolatilityRegime.Normal; t.TrendRegime = TrendRegime.Range; t.StopTicks = 8; t.TargetTicks = 16; t.RMultiple = pnl / 8.0; t.Duration = TimeSpan.FromMinutes(1); return t; } } }