Add score
This commit is contained in:
128
src/Managing.Domain/Shared/Helpers/BacktestScore.cs
Normal file
128
src/Managing.Domain/Shared/Helpers/BacktestScore.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using Managing.Domain.Backtests;
|
||||
|
||||
public class BacktestScorer
|
||||
{
|
||||
// Updated weights without ProfitEfficiency
|
||||
private static readonly Dictionary<string, double> Weights = new Dictionary<string, double>
|
||||
{
|
||||
{ "SharpeRatio", 0.22 }, // Increased weight
|
||||
{ "GrowthPercentage", 0.22 }, // Increased weight
|
||||
{ "MaxDrawdownPc", 0.15 },
|
||||
{ "WinRate", 0.15 }, // Increased weight
|
||||
{ "HodlComparison", 0.15 }, // Increased weight
|
||||
{ "TradeCount", 0.06 },
|
||||
{ "RecoveryTime", 0.04 },
|
||||
{ "RiskAdjustedGrowth", 0.01 }
|
||||
};
|
||||
|
||||
public static double CalculateTotalScore(BacktestScoringParams p)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Detect inactive strategies
|
||||
if (IsInactiveStrategy(p))
|
||||
{
|
||||
return Math.Min(CalculateBaseScore(p) * 0.3, 40);
|
||||
}
|
||||
|
||||
var score = CalculateBaseScore(p);
|
||||
return double.Clamp(score, 0, 100);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static double CalculateBaseScore(BacktestScoringParams p)
|
||||
{
|
||||
var componentScores = new Dictionary<string, double>
|
||||
{
|
||||
{ "SharpeRatio", CalculateSharpeScore(p.SharpeRatio) },
|
||||
{ "GrowthPercentage", CalculateGrowthScore(p.GrowthPercentage) },
|
||||
{ "MaxDrawdownPc", CalculateDrawdownScore(p.MaxDrawdownPc) },
|
||||
{ "WinRate", CalculateWinRateScore(p.WinRate, p.TradeCount) },
|
||||
{ "HodlComparison", CalculateHodlComparisonScore(p.GrowthPercentage, p.HodlPercentage) },
|
||||
{ "TradeCount", CalculateTradeCountScore(p.TradeCount) },
|
||||
{ "RecoveryTime", CalculateRecoveryScore(p.MaxDrawdownRecoveryTime) },
|
||||
{ "RiskAdjustedGrowth", CalculateRiskAdjustedGrowthScore(p.GrowthPercentage, p.MaxDrawdownPc) }
|
||||
};
|
||||
|
||||
return componentScores.Sum(kvp => kvp.Value * Weights[kvp.Key]);
|
||||
}
|
||||
|
||||
private static bool IsInactiveStrategy(BacktestScoringParams p)
|
||||
{
|
||||
// Detect strategies with no economic value
|
||||
return (p.GrowthPercentage <= p.HodlPercentage &&
|
||||
p.TotalPnL <= 0) ||
|
||||
p.TradeCount < 3;
|
||||
}
|
||||
|
||||
private static double CalculateSharpeScore(double sharpeRatio)
|
||||
{
|
||||
return sharpeRatio switch
|
||||
{
|
||||
< 0 => 0,
|
||||
> 3 => 100,
|
||||
_ => (sharpeRatio / 3) * 100
|
||||
};
|
||||
}
|
||||
|
||||
private static double CalculateGrowthScore(double growthPercentage)
|
||||
{
|
||||
if (growthPercentage < 0)
|
||||
return Math.Max(0, 50 + (growthPercentage * 2)); // -5% → 40, -25% → 0
|
||||
|
||||
return Math.Min(100, (Math.Log(1 + growthPercentage) / Math.Log(1 + 1000)) * 100);
|
||||
}
|
||||
|
||||
private static double CalculateDrawdownScore(double maxDrawdownPc)
|
||||
{
|
||||
return maxDrawdownPc switch
|
||||
{
|
||||
> 90 => 0,
|
||||
_ => 100 - Math.Pow(maxDrawdownPc / 90 * 100, 2) / 100
|
||||
};
|
||||
}
|
||||
|
||||
private static double CalculateWinRateScore(double winRate, int tradeCount)
|
||||
{
|
||||
var baseScore = winRate * 100;
|
||||
var significanceFactor = Math.Min(1, tradeCount / 100.0);
|
||||
return baseScore * significanceFactor;
|
||||
}
|
||||
|
||||
private static double CalculateHodlComparisonScore(double strategyGrowth, double hodlGrowth)
|
||||
{
|
||||
var difference = strategyGrowth - hodlGrowth;
|
||||
return difference switch
|
||||
{
|
||||
> 0 => 100 - (100 / (1 + difference / 5)),
|
||||
_ => Math.Max(0, 30 + difference * 3)
|
||||
};
|
||||
}
|
||||
|
||||
private static double CalculateTradeCountScore(int tradeCount)
|
||||
{
|
||||
return Math.Min(100, Math.Max(0, (tradeCount - 10) * 0.5));
|
||||
}
|
||||
|
||||
private static double CalculateRecoveryScore(TimeSpan recoveryTime)
|
||||
{
|
||||
var days = recoveryTime.TotalDays;
|
||||
return days switch
|
||||
{
|
||||
< 0 => 100,
|
||||
> 365 => 0,
|
||||
_ => 100 - (days / 365 * 100)
|
||||
};
|
||||
}
|
||||
|
||||
private static double CalculateRiskAdjustedGrowthScore(double growth, double drawdown)
|
||||
{
|
||||
if (drawdown == 0) return 100;
|
||||
var ratio = growth / drawdown;
|
||||
return Math.Min(ratio * 10, 100);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user