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
|
TRAILING_STOP_ACTIVATION=0.4
|
||||||
MIN_SIGNAL_QUALITY_SCORE=91
|
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_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)
|
# Adaptive Leverage System (Nov 24, 2025)
|
||||||
USE_ADAPTIVE_LEVERAGE=true
|
USE_ADAPTIVE_LEVERAGE=true
|
||||||
HIGH_QUALITY_LEVERAGE=15
|
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)`)
|
console.log(`📊 Signal quality score: ${qualityResult.score} (calculated early for adaptive leverage)`)
|
||||||
|
|
||||||
// Get symbol-specific position sizing with quality score 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 { getActualPositionSizeForSymbol } = await import('@/config/trading')
|
||||||
const { size: positionSize, leverage, enabled, usePercentage } = await getActualPositionSizeForSymbol(
|
const { size: positionSize, leverage, enabled, usePercentage } = await getActualPositionSizeForSymbol(
|
||||||
driftSymbol,
|
driftSymbol,
|
||||||
config,
|
config,
|
||||||
health.freeCollateral,
|
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
|
// Check if trading is enabled for this symbol
|
||||||
|
|||||||
@@ -322,13 +322,15 @@ export function calculateActualPositionSize(
|
|||||||
/**
|
/**
|
||||||
* Get actual position size for symbol with percentage support
|
* Get actual position size for symbol with percentage support
|
||||||
* Now supports adaptive leverage based on quality score (Nov 24, 2025)
|
* 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
|
* This is the main function to use when opening positions
|
||||||
*/
|
*/
|
||||||
export async function getActualPositionSizeForSymbol(
|
export async function getActualPositionSizeForSymbol(
|
||||||
symbol: string,
|
symbol: string,
|
||||||
baseConfig: TradingConfig,
|
baseConfig: TradingConfig,
|
||||||
freeCollateral: number,
|
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 }> {
|
): Promise<{ size: number; leverage: number; enabled: boolean; usePercentage: boolean }> {
|
||||||
let symbolSettings: { size: number; leverage: number; enabled: boolean }
|
let symbolSettings: { size: number; leverage: number; enabled: boolean }
|
||||||
let usePercentage = false
|
let usePercentage = false
|
||||||
@@ -367,10 +369,21 @@ export async function getActualPositionSizeForSymbol(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NEW (Nov 24, 2025): Apply adaptive leverage based on quality score
|
// NEW (Nov 24, 2025): Apply adaptive leverage based on quality score
|
||||||
|
// ENHANCED (Nov 25, 2025): Direction-specific thresholds
|
||||||
let finalLeverage = symbolSettings.leverage
|
let finalLeverage = symbolSettings.leverage
|
||||||
if (qualityScore !== undefined && baseConfig.useAdaptiveLeverage) {
|
if (qualityScore !== undefined && baseConfig.useAdaptiveLeverage) {
|
||||||
finalLeverage = getLeverageForQualityScore(qualityScore, baseConfig)
|
finalLeverage = getLeverageForQualityScore(qualityScore, baseConfig, direction)
|
||||||
console.log(`📊 Adaptive leverage: Quality ${qualityScore} → ${finalLeverage}x leverage (threshold: ${baseConfig.qualityLeverageThreshold})`)
|
|
||||||
|
// 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 {
|
return {
|
||||||
@@ -652,16 +665,37 @@ export function getMinQualityScoreForDirection(
|
|||||||
|
|
||||||
// Get leverage based on signal quality score (Nov 24, 2025)
|
// 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
|
// 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(
|
export function getLeverageForQualityScore(
|
||||||
qualityScore: number,
|
qualityScore: number,
|
||||||
config: TradingConfig
|
config: TradingConfig,
|
||||||
|
direction?: 'long' | 'short'
|
||||||
): number {
|
): number {
|
||||||
// If adaptive leverage disabled, use fixed leverage
|
// If adaptive leverage disabled, use fixed leverage
|
||||||
if (!config.useAdaptiveLeverage) {
|
if (!config.useAdaptiveLeverage) {
|
||||||
return config.leverage
|
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) {
|
if (qualityScore >= config.qualityLeverageThreshold) {
|
||||||
return config.highQualityLeverage
|
return config.highQualityLeverage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,17 @@ export async function scoreSignalQuality(params: {
|
|||||||
score -= 10
|
score -= 10
|
||||||
reasons.push(`RSI oversold (${params.rsi.toFixed(1)})`)
|
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