From 6772a86081ae7186763efb94fe1a1c1e4030e75d Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Mon, 13 Jul 2026 00:18:05 +0900 Subject: [PATCH] refactor(dotnet): simplify factor calculator guards --- .../Domain/FactorCalculator.cs | 41 +++++-------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/src/dotnet/QuantEngine.Core/Domain/FactorCalculator.cs b/src/dotnet/QuantEngine.Core/Domain/FactorCalculator.cs index 2b378b49..c1e9d866 100644 --- a/src/dotnet/QuantEngine.Core/Domain/FactorCalculator.cs +++ b/src/dotnet/QuantEngine.Core/Domain/FactorCalculator.cs @@ -26,31 +26,16 @@ namespace QuantEngine.Core.Domain return new FactorOutputs(0, 0, 0, 0, 0, 1.0, 0); } - // Ensure sorted chronologically (oldest to newest) var sortedStock = stockBars.OrderBy(b => b.TradeDate).ToList(); var sortedIndex = indexBars?.OrderBy(b => b.TradeDate).ToList() ?? new List(); - - int count = sortedStock.Count; - double closeToday = (double)sortedStock[^1].Close; - - // 1. Momentum - double mom20 = CalculateMomentum(sortedStock, 20); - double mom60 = CalculateMomentum(sortedStock, 60); - double mom120 = CalculateMomentum(sortedStock, 120); - - // 2. ATR 20D Percentage - double atrPct = CalculateAtr20Pct(sortedStock); - - // 3. Price Standard Deviation 20D - double stdev = CalculatePriceStDev20D(sortedStock); - - // 4. Beta 60D - double beta = CalculateBeta60D(sortedStock, sortedIndex); - - // 5. Relative Strength (RS) 20D (vs Index) - double rs = CalculateRs20D(sortedStock, sortedIndex); - - return new FactorOutputs(mom20, mom60, mom120, atrPct, stdev, beta, rs); + return new FactorOutputs( + CalculateMomentum(sortedStock, 20), + CalculateMomentum(sortedStock, 60), + CalculateMomentum(sortedStock, 120), + CalculateAtr20Pct(sortedStock), + CalculatePriceStDev20D(sortedStock), + CalculateBeta60D(sortedStock, sortedIndex), + CalculateRs20D(sortedStock, sortedIndex)); } private static double CalculateMomentum(List bars, int period) @@ -58,7 +43,7 @@ namespace QuantEngine.Core.Domain if (bars.Count <= period) return 0.0; double current = (double)bars[^1].Close; double prev = (double)bars[^(period + 1)].Close; - if (prev <= 0.0) return 0.0; + if (prev <= 0.0 || double.IsNaN(prev) || double.IsInfinity(prev)) return 0.0; return ((current - prev) / prev) * 100.0; } @@ -79,7 +64,7 @@ namespace QuantEngine.Core.Domain double atr = trList.Average(); double closeToday = (double)bars[^1].Close; - if (closeToday <= 0.0) return 0.0; + if (closeToday <= 0.0 || double.IsNaN(closeToday) || double.IsInfinity(closeToday)) return 0.0; return (atr / closeToday) * 100.0; } @@ -90,8 +75,6 @@ namespace QuantEngine.Core.Domain var subset = bars.Skip(bars.Count - 20).Select(b => (double)b.Close).ToList(); double avg = subset.Average(); double sumOfSquares = subset.Sum(val => Math.Pow(val - avg, 2)); - - // Sample standard deviation (N-1) return Math.Sqrt(sumOfSquares / (subset.Count - 1)); } @@ -99,11 +82,9 @@ namespace QuantEngine.Core.Domain { if (stock.Count < 61 || index.Count < 61) return 1.0; - // Align daily returns var stockMap = stock.ToDictionary(b => b.TradeDate); var indexMap = index.ToDictionary(b => b.TradeDate); - // Compute returns for overlapping dates var overlappingDates = stockMap.Keys.Intersect(indexMap.Keys).OrderBy(d => d).ToList(); if (overlappingDates.Count < 61) return 1.0; @@ -113,7 +94,6 @@ namespace QuantEngine.Core.Domain var stockReturns = new List(); var indexReturns = new List(); - // Calculate returns starting from last 60 days int startIdx = Math.Max(1, alignedStock.Count - 60); for (int i = startIdx; i < alignedStock.Count; i++) { @@ -154,7 +134,6 @@ namespace QuantEngine.Core.Domain { if (stock.Count < 21 || index.Count < 21) return 0.0; - // Align dates var stockMap = stock.ToDictionary(b => b.TradeDate); var indexMap = index.ToDictionary(b => b.TradeDate);