diff --git a/.env b/.env index 9dd9c68..eb26dfc 100644 --- a/.env +++ b/.env @@ -392,7 +392,7 @@ TRAILING_STOP_PERCENT=0.5 TRAILING_STOP_ACTIVATION=0.4 MIN_SIGNAL_QUALITY_SCORE=91 MIN_SIGNAL_QUALITY_SCORE_LONG=90 # Nov 23, 2025: Longs have 71.4% WR at quality 90-94 (+$44.77 on 7 trades) -MIN_SIGNAL_QUALITY_SCORE_SHORT=95 # Nov 23, 2025: Shorts toxic at quality 90-94 (28.6% WR, -$553.76 on 7 trades) +MIN_SIGNAL_QUALITY_SCORE_SHORT=80 # Nov 23, 2025: Shorts toxic at quality 90-94 (28.6% WR, -$553.76 on 7 trades) # Adaptive Leverage System (Nov 24, 2025) USE_ADAPTIVE_LEVERAGE=true HIGH_QUALITY_LEVERAGE=15 diff --git a/app/api/trading/execute/route.ts b/app/api/trading/execute/route.ts index ef29975..3df77ea 100644 --- a/app/api/trading/execute/route.ts +++ b/app/api/trading/execute/route.ts @@ -184,12 +184,14 @@ export async function POST(request: NextRequest): Promise { let symbolSettings: { size: number; leverage: number; enabled: boolean } let usePercentage = false @@ -367,10 +369,21 @@ export async function getActualPositionSizeForSymbol( ) // NEW (Nov 24, 2025): Apply adaptive leverage based on quality score + // ENHANCED (Nov 25, 2025): Direction-specific thresholds let finalLeverage = symbolSettings.leverage if (qualityScore !== undefined && baseConfig.useAdaptiveLeverage) { - finalLeverage = getLeverageForQualityScore(qualityScore, baseConfig) - console.log(`📊 Adaptive leverage: Quality ${qualityScore} → ${finalLeverage}x leverage (threshold: ${baseConfig.qualityLeverageThreshold})`) + finalLeverage = getLeverageForQualityScore(qualityScore, baseConfig, direction) + + // Log SHORT-specific leverage decisions + if (direction === 'short') { + if (qualityScore >= 90) { + console.log(`📊 Adaptive leverage (SHORT): Quality ${qualityScore} → ${finalLeverage}x leverage (Tier 1: Q90+)`) + } else if (qualityScore >= 80) { + console.log(`📊 Adaptive leverage (SHORT): Quality ${qualityScore} → ${finalLeverage}x leverage (Tier 2: Q80-89)`) + } + } else { + console.log(`📊 Adaptive leverage: Quality ${qualityScore} → ${finalLeverage}x leverage (threshold: ${baseConfig.qualityLeverageThreshold})`) + } } return { @@ -652,16 +665,37 @@ export function getMinQualityScoreForDirection( // Get leverage based on signal quality score (Nov 24, 2025) // Data-driven: v8 quality 95+ = 100% WR (4/4 wins), quality 90-94 more volatile +// Get leverage based on signal quality score (Nov 24, 2025) +// ENHANCED Nov 25, 2025: Direction-specific thresholds for adaptive leverage +// Data-driven: +// LONGs: Quality 95+ = 100% WR (4/4 wins), quality 90-94 more volatile → 90/95 split +// SHORTs: Quality 80+/RSI 33+ = 100% WR (2/2 wins), quality 90+ safer → 80/90 split export function getLeverageForQualityScore( qualityScore: number, - config: TradingConfig + config: TradingConfig, + direction?: 'long' | 'short' ): number { // If adaptive leverage disabled, use fixed leverage if (!config.useAdaptiveLeverage) { return config.leverage } - // High quality signals get maximum leverage + // Direction-specific quality thresholds for leverage + // SHORTs are riskier, use lower threshold (80 vs 90) but still tier by quality + if (direction === 'short') { + // Quality 90+ SHORTs → Full leverage (when combined with RSI 33+) + if (qualityScore >= 90) { + return config.highQualityLeverage + } + // Quality 80-89 SHORTs → Reduced leverage (more conservative) + if (qualityScore >= 80) { + return config.lowQualityLeverage + } + // Below 80 shouldn't execute (filtered out), but return minimum if it does + return config.lowQualityLeverage + } + + // LONGs use original threshold (95+ for high leverage) if (qualityScore >= config.qualityLeverageThreshold) { return config.highQualityLeverage } diff --git a/lib/trading/signal-quality.ts b/lib/trading/signal-quality.ts index 6ca430a..20107d8 100644 --- a/lib/trading/signal-quality.ts +++ b/lib/trading/signal-quality.ts @@ -130,6 +130,17 @@ export async function scoreSignalQuality(params: { score -= 10 reasons.push(`RSI oversold (${params.rsi.toFixed(1)})`) } + + // CRITICAL (Nov 25, 2025): Data-driven SHORT filter + // Analysis of 14 SHORT signals showed: + // - All 4 disasters had RSI < 33 (avg RSI 28.3, lost -$665.70) + // - All 2 winners had RSI >= 33 (RSI 33.9, 66.3, won +$59.37) + // - RSI < 33 = shorting into oversold = catching falling knives + // This penalty drops quality below 80 threshold, blocking the signal + if (params.rsi < 33) { + score -= 25 + reasons.push(`🚨 SHORT oversold trap: RSI ${params.rsi.toFixed(1)} < 33 → BLOCKING (-25 pts)`) + } } }