feat: Direction-specific adaptive leverage for SHORTs (Q80+, RSI 33+)
- Quality 80-89 + RSI 33+ → 10x leverage (conservative tier) - Quality 90+ + RSI 33+ → 15x leverage (full confidence tier) - RSI < 33 penalty: -25 points (drops below Q80 threshold) - Data-driven: 14 SHORT analysis showed 100% WR at Q80+ RSI33+ (2/2 wins) - All disasters had RSI < 33 (4 trades, -$665.70 total) - Modified: config/trading.ts, lib/trading/signal-quality.ts, execute endpoint - Updated: MIN_SIGNAL_QUALITY_SCORE_SHORT=80 (down from 95) - Expected impact: +$40.58 vs current system (+216% improvement)
This commit is contained in:
2
.env
2
.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
|
||||
|
||||
@@ -184,12 +184,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
||||
console.log(`📊 Signal quality score: ${qualityResult.score} (calculated early for adaptive leverage)`)
|
||||
|
||||
// Get symbol-specific position sizing with quality score for adaptive leverage
|
||||
// ENHANCED Nov 25, 2025: Pass direction for SHORT-specific leverage tiers
|
||||
const { getActualPositionSizeForSymbol } = await import('@/config/trading')
|
||||
const { size: positionSize, leverage, enabled, usePercentage } = await getActualPositionSizeForSymbol(
|
||||
driftSymbol,
|
||||
config,
|
||||
health.freeCollateral,
|
||||
qualityResult.score // Pass quality score for adaptive leverage
|
||||
qualityResult.score, // Pass quality score for adaptive leverage
|
||||
body.direction // Pass direction for SHORT-specific tiers (Q90+=15x, Q80-89=10x)
|
||||
)
|
||||
|
||||
// Check if trading is enabled for this symbol
|
||||
|
||||
@@ -322,13 +322,15 @@ export function calculateActualPositionSize(
|
||||
/**
|
||||
* Get actual position size for symbol with percentage support
|
||||
* Now supports adaptive leverage based on quality score (Nov 24, 2025)
|
||||
* ENHANCED Nov 25, 2025: Direction-specific leverage for SHORTs
|
||||
* This is the main function to use when opening positions
|
||||
*/
|
||||
export async function getActualPositionSizeForSymbol(
|
||||
symbol: string,
|
||||
baseConfig: TradingConfig,
|
||||
freeCollateral: number,
|
||||
qualityScore?: number // NEW: Optional quality score for adaptive leverage
|
||||
qualityScore?: number, // Optional quality score for adaptive leverage
|
||||
direction?: 'long' | 'short' // NEW: Direction for SHORT-specific leverage tiers
|
||||
): Promise<{ size: number; leverage: number; enabled: boolean; usePercentage: boolean }> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user