Update score

This commit is contained in:
2025-03-01 23:20:01 +07:00
parent bcf9d21b0a
commit 6f4db52bbb

View File

@@ -5,28 +5,25 @@ public class BacktestScorer
// Updated weights without ProfitEfficiency // Updated weights without ProfitEfficiency
private static readonly Dictionary<string, double> Weights = new Dictionary<string, double> private static readonly Dictionary<string, double> Weights = new Dictionary<string, double>
{ {
{ "SharpeRatio", 0.22 }, // Increased weight { "GrowthPercentage", 0.30 }, // Increased weight for profitability
{ "GrowthPercentage", 0.22 }, // Increased weight { "SharpeRatio", 0.18 },
{ "MaxDrawdownPc", 0.15 }, { "MaxDrawdownPc", 0.15 },
{ "WinRate", 0.15 }, // Increased weight { "HodlComparison", 0.15 },
{ "HodlComparison", 0.15 }, // Increased weight { "WinRate", 0.10 },
{ "TradeCount", 0.06 }, { "ProfitabilityBonus", 0.07 }, // New direct profitability component
{ "RecoveryTime", 0.04 }, { "TradeCount", 0.03 },
{ "RiskAdjustedGrowth", 0.01 } { "RecoveryTime", 0.02 }
}; };
public static double CalculateTotalScore(BacktestScoringParams p) public static double CalculateTotalScore(BacktestScoringParams p)
{ {
try try
{ {
// Detect inactive strategies var baseScore = CalculateBaseScore(p);
if (IsInactiveStrategy(p)) var finalScore = ApplyProfitabilityRules(baseScore, p);
{
return Math.Min(CalculateBaseScore(p) * 0.3, 40);
}
var score = CalculateBaseScore(p); return double.Clamp(finalScore, 0, 100);
return double.Clamp(score, 0, 100);
} }
catch catch
{ {
@@ -38,19 +35,85 @@ public class BacktestScorer
{ {
var componentScores = new Dictionary<string, double> var componentScores = new Dictionary<string, double>
{ {
{ "SharpeRatio", CalculateSharpeScore(p.SharpeRatio) },
{ "GrowthPercentage", CalculateGrowthScore(p.GrowthPercentage) }, { "GrowthPercentage", CalculateGrowthScore(p.GrowthPercentage) },
{ "SharpeRatio", CalculateSharpeScore(p.SharpeRatio) },
{ "MaxDrawdownPc", CalculateDrawdownScore(p.MaxDrawdownPc) }, { "MaxDrawdownPc", CalculateDrawdownScore(p.MaxDrawdownPc) },
{ "WinRate", CalculateWinRateScore(p.WinRate, p.TradeCount) },
{ "HodlComparison", CalculateHodlComparisonScore(p.GrowthPercentage, p.HodlPercentage) }, { "HodlComparison", CalculateHodlComparisonScore(p.GrowthPercentage, p.HodlPercentage) },
{ "WinRate", CalculateWinRateScore(p.WinRate, p.TradeCount) },
{ "ProfitabilityBonus", CalculateProfitabilityBonus(p.GrowthPercentage) },
{ "TradeCount", CalculateTradeCountScore(p.TradeCount) }, { "TradeCount", CalculateTradeCountScore(p.TradeCount) },
{ "RecoveryTime", CalculateRecoveryScore(p.MaxDrawdownRecoveryTime) }, { "RecoveryTime", CalculateRecoveryScore(p.MaxDrawdownRecoveryTime) }
{ "RiskAdjustedGrowth", CalculateRiskAdjustedGrowthScore(p.GrowthPercentage, p.MaxDrawdownPc) }
}; };
return componentScores.Sum(kvp => kvp.Value * Weights[kvp.Key]); return componentScores.Sum(kvp => kvp.Value * Weights[kvp.Key]);
} }
private static double ApplyProfitabilityRules(double baseScore, BacktestScoringParams p)
{
// 1. Negative PnL Penalty (Core Rule)
if (p.GrowthPercentage < 0)
{
baseScore = Math.Min(baseScore, 70) * GetNegativePnLMultiplier(p.GrowthPercentage);
}
// 2. Absolute PnL Validation (Additional Recommendation)
if (p.TotalPnL <= 0)
{
baseScore = Math.Min(baseScore, 50);
}
// 3. Win Rate Validation (Additional Recommendation)
if (p.WinRate < 0.3 && p.TradeCount > 10)
{
baseScore = Math.Min(baseScore, 60);
}
// 4. Minimum Profit Threshold (Additional Recommendation)
if (p.GrowthPercentage < 2 && p.TradeCount > 5)
{
baseScore = Math.Min(baseScore, 80);
}
return baseScore;
}
private static double CalculateGrowthScore(double growthPercentage)
{
// More aggressive penalty for negative growth
if (growthPercentage < 0)
{
return Math.Max(0, 40 + (growthPercentage * 2)); // -10% → 20, -20% → 0
}
// Require minimum 5% growth for full score
return growthPercentage switch
{
< 5 => growthPercentage * 15, // 2% → 30, 4% → 60
_ => 100
};
}
// Existing multiplier calculation
private static double GetNegativePnLMultiplier(double growthPercentage)
{
return growthPercentage switch
{
> -5 => 0.8,
> -10 => 0.6,
> -20 => 0.4,
_ => 0.2
};
}
private static double CalculateProfitabilityBonus(double growthPercentage)
{
return growthPercentage switch
{
> 0 => 100 * (1 - 1 / (1 + growthPercentage / 50)), // Diminishing returns
_ => 0
};
}
private static bool IsInactiveStrategy(BacktestScoringParams p) private static bool IsInactiveStrategy(BacktestScoringParams p)
{ {
// Detect strategies with no economic value // Detect strategies with no economic value
@@ -69,14 +132,6 @@ public class BacktestScorer
}; };
} }
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) private static double CalculateDrawdownScore(double maxDrawdownPc)
{ {
return maxDrawdownPc switch return maxDrawdownPc switch