using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using NT8.Core.Common.Models; using NT8.Core.Intelligence; using NT8.Core.Logging; namespace NT8.Core.Tests.Intelligence { [TestClass] public class ConfluenceScorerTests { [TestMethod] public void Constructor_NullLogger_ThrowsArgumentNullException() { Assert.ThrowsException(delegate { new ConfluenceScorer(null, 100); }); } [TestMethod] public void Constructor_InvalidHistory_ThrowsArgumentException() { Assert.ThrowsException(delegate { new ConfluenceScorer(new BasicLogger("test"), 0); }); } [TestMethod] public void MapScoreToGrade_At090_ReturnsAPlus() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.90); Assert.AreEqual(TradeGrade.APlus, grade); } [TestMethod] public void MapScoreToGrade_At080_ReturnsA() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.80); Assert.AreEqual(TradeGrade.A, grade); } [TestMethod] public void MapScoreToGrade_At070_ReturnsB() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.70); Assert.AreEqual(TradeGrade.B, grade); } [TestMethod] public void MapScoreToGrade_At060_ReturnsC() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.60); Assert.AreEqual(TradeGrade.C, grade); } [TestMethod] public void MapScoreToGrade_At050_ReturnsD() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.50); Assert.AreEqual(TradeGrade.D, grade); } [TestMethod] public void MapScoreToGrade_Below050_ReturnsF() { var scorer = CreateScorer(); var grade = scorer.MapScoreToGrade(0.49); Assert.AreEqual(TradeGrade.F, grade); } [TestMethod] public void CalculateScore_NullIntent_ThrowsArgumentNullException() { var scorer = CreateScorer(); var context = CreateContext(); var bar = CreateBar(); var factors = CreateFactors(); Assert.ThrowsException(delegate { scorer.CalculateScore(null, context, bar, factors); }); } [TestMethod] public void CalculateScore_NullContext_ThrowsArgumentNullException() { var scorer = CreateScorer(); var intent = CreateIntent(); var bar = CreateBar(); var factors = CreateFactors(); Assert.ThrowsException(delegate { scorer.CalculateScore(intent, null, bar, factors); }); } [TestMethod] public void CalculateScore_NullBar_ThrowsArgumentNullException() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var factors = CreateFactors(); Assert.ThrowsException(delegate { scorer.CalculateScore(intent, context, null, factors); }); } [TestMethod] public void CalculateScore_NullFactors_ThrowsArgumentNullException() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); Assert.ThrowsException(delegate { scorer.CalculateScore(intent, context, bar, null); }); } [TestMethod] public void CalculateScore_EmptyFactors_ReturnsZeroScoreAndF() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var result = scorer.CalculateScore(intent, context, bar, new List()); Assert.IsNotNull(result); Assert.AreEqual(0.0, result.RawScore, 0.000001); Assert.AreEqual(0.0, result.WeightedScore, 0.000001); Assert.AreEqual(TradeGrade.F, result.Grade); } [TestMethod] public void CalculateScore_SingleFactor_UsesFactorScore() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var factors = new List(); factors.Add(new FixedFactorCalculator(FactorType.Setup, 0.75, 1.0)); var result = scorer.CalculateScore(intent, context, bar, factors); Assert.AreEqual(0.75, result.RawScore, 0.000001); Assert.AreEqual(0.75, result.WeightedScore, 0.000001); Assert.AreEqual(TradeGrade.B, result.Grade); Assert.AreEqual(1, result.Factors.Count); } [TestMethod] public void CalculateScore_MultipleFactors_CalculatesWeightedAverage() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var factors = new List(); factors.Add(new FixedFactorCalculator(FactorType.Setup, 1.0, 1.0)); factors.Add(new FixedFactorCalculator(FactorType.Trend, 0.5, 1.0)); factors.Add(new FixedFactorCalculator(FactorType.Timing, 0.0, 1.0)); var result = scorer.CalculateScore(intent, context, bar, factors); Assert.AreEqual(0.5, result.RawScore, 0.000001); Assert.AreEqual(0.5, result.WeightedScore, 0.000001); Assert.AreEqual(TradeGrade.D, result.Grade); Assert.AreEqual(3, result.Factors.Count); } [TestMethod] public void UpdateFactorWeights_AppliesOverrides() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var updates = new Dictionary(); updates.Add(FactorType.Setup, 2.0); updates.Add(FactorType.Trend, 1.0); scorer.UpdateFactorWeights(updates); var factors = new List(); factors.Add(new FixedFactorCalculator(FactorType.Setup, 1.0, 1.0)); factors.Add(new FixedFactorCalculator(FactorType.Trend, 0.0, 1.0)); var result = scorer.CalculateScore(intent, context, bar, factors); Assert.AreEqual(0.666666, result.WeightedScore, 0.0005); } [TestMethod] public void UpdateFactorWeights_InvalidWeight_ThrowsArgumentException() { var scorer = CreateScorer(); var updates = new Dictionary(); updates.Add(FactorType.Setup, 0.0); Assert.ThrowsException(delegate { scorer.UpdateFactorWeights(updates); }); } [TestMethod] public void GetHistoricalStats_Empty_ReturnsDefaults() { var scorer = CreateScorer(); var stats = scorer.GetHistoricalStats(); Assert.IsNotNull(stats); Assert.AreEqual(0, stats.TotalCalculations); Assert.AreEqual(0.0, stats.AverageWeightedScore, 0.000001); Assert.AreEqual(0.0, stats.AverageRawScore, 0.000001); } [TestMethod] public void GetHistoricalStats_AfterScores_ReturnsCounts() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var factors1 = new List(); factors1.Add(new FixedFactorCalculator(FactorType.Setup, 0.90, 1.0)); var factors2 = new List(); factors2.Add(new FixedFactorCalculator(FactorType.Setup, 0.40, 1.0)); scorer.CalculateScore(intent, context, bar, factors1); scorer.CalculateScore(intent, context, bar, factors2); var stats = scorer.GetHistoricalStats(); Assert.AreEqual(2, stats.TotalCalculations); Assert.IsTrue(stats.BestWeightedScore >= stats.WorstWeightedScore); Assert.IsTrue(stats.GradeDistribution[TradeGrade.APlus] >= 0); Assert.IsTrue(stats.GradeDistribution[TradeGrade.F] >= 0); } [TestMethod] public void CalculateScore_CurrentBarOverload_UsesContextCustomData() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); context.CustomData["current_bar"] = bar; var factors = CreateFactors(); var result = scorer.CalculateScore(intent, context, factors); Assert.IsNotNull(result); Assert.IsTrue(result.WeightedScore >= 0.0); Assert.IsTrue(result.WeightedScore <= 1.0); } [TestMethod] public void CalculateScore_CurrentBarOverload_MissingBar_ThrowsArgumentException() { var scorer = CreateScorer(); var intent = CreateIntent(); var context = CreateContext(); var factors = CreateFactors(); Assert.ThrowsException(delegate { scorer.CalculateScore(intent, context, factors); }); } [TestMethod] public void CalculateScore_HistoryRespectsMaxCapacity() { var scorer = new ConfluenceScorer(new BasicLogger("test"), 2); var intent = CreateIntent(); var context = CreateContext(); var bar = CreateBar(); var factorsA = new List(); factorsA.Add(new FixedFactorCalculator(FactorType.Setup, 0.9, 1.0)); var factorsB = new List(); factorsB.Add(new FixedFactorCalculator(FactorType.Setup, 0.8, 1.0)); var factorsC = new List(); factorsC.Add(new FixedFactorCalculator(FactorType.Setup, 0.7, 1.0)); scorer.CalculateScore(intent, context, bar, factorsA); scorer.CalculateScore(intent, context, bar, factorsB); scorer.CalculateScore(intent, context, bar, factorsC); var stats = scorer.GetHistoricalStats(); Assert.AreEqual(2, stats.TotalCalculations); } private static ConfluenceScorer CreateScorer() { return new ConfluenceScorer(new BasicLogger("ConfluenceScorerTests"), 100); } private static StrategyIntent CreateIntent() { return new StrategyIntent( "ES", OrderSide.Buy, OrderType.Market, null, 8, 16, 0.8, "Test", new Dictionary()); } private static StrategyContext CreateContext() { return new StrategyContext( "ES", DateTime.UtcNow, new Position("ES", 0, 0, 0, 0, DateTime.UtcNow), new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow), new MarketSession(DateTime.Today.AddHours(9.5), DateTime.Today.AddHours(16), true, "RTH"), new Dictionary()); } private static BarData CreateBar() { return new BarData("ES", DateTime.UtcNow, 5000, 5005, 4998, 5002, 1000, TimeSpan.FromMinutes(1)); } private static List CreateFactors() { var factors = new List(); factors.Add(new FixedFactorCalculator(FactorType.Setup, 0.8, 1.0)); factors.Add(new FixedFactorCalculator(FactorType.Trend, 0.7, 1.0)); factors.Add(new FixedFactorCalculator(FactorType.Volatility, 0.6, 1.0)); return factors; } private class FixedFactorCalculator : IFactorCalculator { private readonly FactorType _type; private readonly double _score; private readonly double _weight; public FixedFactorCalculator(FactorType type, double score, double weight) { _type = type; _score = score; _weight = weight; } public FactorType Type { get { return _type; } } public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { return new ConfluenceFactor( _type, "Fixed", _score, _weight, "Test factor", new Dictionary()); } } } }