diff --git a/.env b/.env index 4ef70da..8cbe625 100644 --- a/.env +++ b/.env @@ -151,7 +151,7 @@ MIN_TIME_BETWEEN_TRADES=1 # DEX execution settings # Maximum acceptable slippage on market orders (percentage) # Example: 1.0 = accept up to 1% slippage -SLIPPAGE_TOLERANCE=1 +SLIPPAGE_TOLERANCE=0.15 # How often to check prices (milliseconds) # Example: 2000 = check every 2 seconds diff --git a/app/api/analytics/tp-sl-optimization/route.ts b/app/api/analytics/tp-sl-optimization/route.ts index 0697c2c..7cad49e 100644 --- a/app/api/analytics/tp-sl-optimization/route.ts +++ b/app/api/analytics/tp-sl-optimization/route.ts @@ -73,6 +73,34 @@ export interface TPSLOptimizationResponse { slExits: number manualExits: number } + + // Dynamic ATR Analysis (v6 trades only) + dynamicATRAnalysis?: { + available: boolean + sampleSize: number + minSampleSize: number + sufficientData: boolean + avgATRPercent: number + + // Dynamic targets + dynamicTP2Percent: number // 2x ATR + dynamicSLPercent: number // 1.5x ATR + + // Simulated P&L comparison + actualPnL: number + fixedSimulatedPnL: number + dynamicSimulatedPnL: number + dynamicAdvantage: number + dynamicAdvantagePercent: number + + // Hit rates + dynamicTP2HitRate: number + dynamicSLHitRate: number + + // Recommendation + recommendation: string + reasoning: string + } } error?: string } @@ -233,6 +261,123 @@ export async function GET(request: NextRequest): Promise + t.indicatorVersion === 'v6' && + t.atrAtEntry !== null && + t.atrAtEntry !== undefined + ) + + let dynamicATRAnalysis = undefined + const minSampleSize = 30 + + if (v6Trades.length > 0) { + console.log(`📊 Analyzing ${v6Trades.length} v6 trades for dynamic ATR-based TP/SL`) + + // Calculate ATR-based targets + const atrAnalysis = v6Trades.map(t => { + const atrPercent = (t.atrAtEntry! / t.entryPrice) * 100 + const dynamicTP2 = atrPercent * 2 // 2x ATR + const dynamicSL = atrPercent * 1.5 // 1.5x ATR + + const mfe = t.maxFavorableExcursion || 0 + const mae = t.maxAdverseExcursion || 0 + + // Simulate outcomes + const wouldHitDynamicTP2 = mfe >= dynamicTP2 + const wouldHitDynamicSL = mae <= -dynamicSL + + // Calculate simulated P&L + let dynamicPnL = t.realizedPnL || 0 + if (wouldHitDynamicTP2) { + // Hit TP2 at 2x ATR + dynamicPnL = t.positionSizeUSD * dynamicTP2 / 100 + } else if (wouldHitDynamicSL) { + // Hit SL at 1.5x ATR + dynamicPnL = -t.positionSizeUSD * dynamicSL / 100 + } + + let fixedPnL = t.realizedPnL || 0 + if (mfe >= currentTP2) { + fixedPnL = t.positionSizeUSD * currentTP2 / 100 + } else if (mae <= currentSL) { + fixedPnL = t.positionSizeUSD * currentSL / 100 + } + + return { + atrPercent, + dynamicTP2, + dynamicSL, + wouldHitDynamicTP2, + wouldHitDynamicSL, + dynamicPnL, + fixedPnL, + actualPnL: t.realizedPnL || 0 + } + }) + + const avgATRPercent = atrAnalysis.reduce((sum, a) => sum + a.atrPercent, 0) / atrAnalysis.length + const avgDynamicTP2 = atrAnalysis.reduce((sum, a) => sum + a.dynamicTP2, 0) / atrAnalysis.length + const avgDynamicSL = atrAnalysis.reduce((sum, a) => sum + a.dynamicSL, 0) / atrAnalysis.length + + const totalActualPnL = atrAnalysis.reduce((sum, a) => sum + a.actualPnL, 0) + const totalFixedPnL = atrAnalysis.reduce((sum, a) => sum + a.fixedPnL, 0) + const totalDynamicPnL = atrAnalysis.reduce((sum, a) => sum + a.dynamicPnL, 0) + + const dynamicAdvantage = totalDynamicPnL - totalFixedPnL + const dynamicAdvantagePercent = totalFixedPnL !== 0 + ? (dynamicAdvantage / Math.abs(totalFixedPnL)) * 100 + : 0 + + const dynamicTP2Hits = atrAnalysis.filter(a => a.wouldHitDynamicTP2).length + const dynamicSLHits = atrAnalysis.filter(a => a.wouldHitDynamicSL).length + + const sufficientData = v6Trades.length >= minSampleSize + + let recommendation = '' + let reasoning = '' + + if (!sufficientData) { + recommendation = 'WAIT - Need more data' + reasoning = `Only ${v6Trades.length}/${minSampleSize} trades collected. Continue using fixed targets until we have ${minSampleSize}+ v6 trades for statistical significance.` + } else if (dynamicAdvantagePercent > 20) { + recommendation = 'IMPLEMENT - Strong advantage' + reasoning = `Dynamic ATR-based targets show ${dynamicAdvantagePercent.toFixed(1)}% better performance over ${v6Trades.length} trades. The tighter SL (${avgDynamicSL.toFixed(2)}% vs ${Math.abs(currentSL)}%) reduces losses significantly.` + } else if (dynamicAdvantagePercent > 10) { + recommendation = 'CONSIDER - Moderate advantage' + reasoning = `Dynamic ATR-based targets show ${dynamicAdvantagePercent.toFixed(1)}% improvement. Worth testing with smaller position sizes first.` + } else if (dynamicAdvantagePercent > 0) { + recommendation = 'NEUTRAL - Slight advantage' + reasoning = `Dynamic targets show only ${dynamicAdvantagePercent.toFixed(1)}% improvement. May not be worth the added complexity.` + } else { + recommendation = 'DO NOT IMPLEMENT' + reasoning = `Dynamic targets underperform fixed targets by ${Math.abs(dynamicAdvantagePercent).toFixed(1)}%. Stick with current fixed levels.` + } + + dynamicATRAnalysis = { + available: true, + sampleSize: v6Trades.length, + minSampleSize, + sufficientData, + avgATRPercent, + dynamicTP2Percent: avgDynamicTP2, + dynamicSLPercent: avgDynamicSL, + actualPnL: totalActualPnL, + fixedSimulatedPnL: totalFixedPnL, + dynamicSimulatedPnL: totalDynamicPnL, + dynamicAdvantage, + dynamicAdvantagePercent, + dynamicTP2HitRate: (dynamicTP2Hits / v6Trades.length) * 100, + dynamicSLHitRate: (dynamicSLHits / v6Trades.length) * 100, + recommendation, + reasoning + } + + console.log(`✅ Dynamic ATR analysis: ${recommendation}`) + console.log(` Sample: ${v6Trades.length}/${minSampleSize} trades`) + console.log(` Advantage: ${dynamicAdvantagePercent.toFixed(1)}% (${dynamicAdvantage >= 0 ? '+' : ''}$${dynamicAdvantage.toFixed(2)})`) + } + // Build response const analysis: TPSLOptimizationResponse = { success: true, @@ -295,6 +440,8 @@ export async function GET(request: NextRequest): Promise