Files
trading_bot_v4/app/analytics/optimization/page.tsx
mindesbunister ebe5e1ab5f feat: Add Dynamic ATR Analysis UI to TP/SL Optimization page
- Added dynamicATRAnalysis interface to page component
- New section displays after Current Configuration Performance
- Progress bar shows data collection: 14/30 trades (46.7%)
- Side-by-side comparison: Fixed vs Dynamic ATR targets
- Highlights advantage: +.72 (+39.8%) with current sample
- Color-coded recommendation: Yellow (WAIT) → Green (IMPLEMENT)
- Shows avg ATR (0.32%), dynamic TP2 (0.64%), dynamic SL (0.48%)
- Auto-updates as more v6 trades are collected
- Responsive design with gradient backgrounds

Enables user to track progress toward 30-trade threshold for implementation decision
2025-11-14 09:09:08 +01:00

573 lines
22 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useEffect, useState } from 'react'
import Link from 'next/link'
interface TPSLAnalysis {
success: boolean
analysis?: {
totalTrades: number
winningTrades: number
losingTrades: number
winRate: number
avgWin: number
avgLoss: number
profitFactor: number
maeAnalysis: {
avgMAE: number
medianMAE: number
percentile25MAE: number
percentile75MAE: number
worstMAE: number
}
mfeAnalysis: {
avgMFE: number
medianMFE: number
percentile25MFE: number
percentile75MFE: number
bestMFE: number
}
currentLevels: {
tp1Percent: number
tp2Percent: number
slPercent: number
tp1HitRate: number
tp2HitRate: number
slHitRate: number
moneyLeftOnTable: number
}
recommendations: {
optimalTP1: number
optimalTP2: number
optimalSL: number
reasoning: {
tp1: string
tp2: string
sl: string
}
projectedImpact: {
expectedWinRateChange: number
expectedProfitFactorChange: number
estimatedProfitImprovement: number
}
}
tradesByOutcome: {
tp1Exits: number
tp2Exits: number
slExits: number
manualExits: number
}
dynamicATRAnalysis?: {
available: boolean
sampleSize: number
minSampleSize: number
sufficientData: boolean
avgATRPercent: number
dynamicTP2Percent: number
dynamicSLPercent: number
actualPnL: number
fixedSimulatedPnL: number
dynamicSimulatedPnL: number
dynamicAdvantage: number
dynamicAdvantagePercent: number
dynamicTP2HitRate: number
dynamicSLHitRate: number
recommendation: string
reasoning: string
}
}
error?: string
}
export default function OptimizationPage() {
const [analysis, setAnalysis] = useState<TPSLAnalysis | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
fetchAnalysis()
}, [])
const fetchAnalysis = async () => {
try {
setLoading(true)
setError(null)
const response = await fetch('/api/analytics/tp-sl-optimization')
const data = await response.json()
setAnalysis(data)
if (!data.success) {
setError(data.error || 'Failed to load analysis')
}
} catch (err) {
setError('Failed to fetch analytics: ' + (err as Error).message)
} finally {
setLoading(false)
}
}
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex items-center justify-center h-64">
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div>
<p className="text-gray-400">Loading optimization analysis...</p>
</div>
</div>
</div>
</div>
)
}
if (error || !analysis?.success) {
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-yellow-900/20 border border-yellow-600 rounded-lg p-6">
<h2 className="text-xl font-semibold text-yellow-400 mb-2"> Insufficient Data</h2>
<p className="text-gray-300 mb-4">{error || analysis?.error}</p>
<p className="text-sm text-gray-400 mb-4">
Need at least 10 closed trades with MAE/MFE tracking data.
The next trades you take will automatically track this data.
</p>
<button
onClick={fetchAnalysis}
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-semibold transition-colors"
>
🔄 Refresh Analytics
</button>
</div>
</div>
</div>
)
}
const data = analysis.analysis!
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<Header />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header with Refresh */}
<div className="flex items-center justify-between mb-8">
<div>
<h2 className="text-2xl font-bold text-white">💡 TP/SL Optimization</h2>
<p className="text-sm text-gray-400 mt-1">Based on {data.totalTrades} trades with MAE/MFE data</p>
</div>
<button
onClick={fetchAnalysis}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors text-white"
>
🔄 Refresh
</button>
</div>
{/* Overview Stats */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<StatCard title="Total Trades" value={data.totalTrades.toString()} />
<StatCard
title="Win Rate"
value={data.winRate.toFixed(1) + '%'}
valueColor="text-green-400"
/>
<StatCard
title="Profit Factor"
value={data.profitFactor.toFixed(2)}
valueColor="text-blue-400"
/>
<StatCard
title="Money Left on Table"
value={'$' + data.currentLevels.moneyLeftOnTable.toFixed(2)}
valueColor="text-yellow-400"
/>
</div>
{/* MAE/MFE Analysis */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<h3 className="text-xl font-semibold mb-4 text-green-400">
📈 Maximum Favorable Excursion (MFE)
</h3>
<div className="space-y-3">
<MetricRow label="Average" value={data.mfeAnalysis.avgMFE.toFixed(2) + '%'} />
<MetricRow label="Median" value={data.mfeAnalysis.medianMFE.toFixed(2) + '%'} />
<MetricRow label="25th Percentile" value={data.mfeAnalysis.percentile25MFE.toFixed(2) + '%'} />
<MetricRow label="75th Percentile" value={data.mfeAnalysis.percentile75MFE.toFixed(2) + '%'} />
<div className="border-t border-gray-700 pt-3">
<MetricRow
label="Best"
value={data.mfeAnalysis.bestMFE.toFixed(2) + '%'}
valueColor="text-green-400 font-bold"
/>
</div>
</div>
</div>
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<h3 className="text-xl font-semibold mb-4 text-red-400">
📉 Maximum Adverse Excursion (MAE)
</h3>
<div className="space-y-3">
<MetricRow label="Average" value={data.maeAnalysis.avgMAE.toFixed(2) + '%'} />
<MetricRow label="Median" value={data.maeAnalysis.medianMAE.toFixed(2) + '%'} />
<MetricRow label="25th Percentile" value={data.maeAnalysis.percentile25MAE.toFixed(2) + '%'} />
<MetricRow label="75th Percentile" value={data.maeAnalysis.percentile75MAE.toFixed(2) + '%'} />
<div className="border-t border-gray-700 pt-3">
<MetricRow
label="Worst"
value={data.maeAnalysis.worstMAE.toFixed(2) + '%'}
valueColor="text-red-400 font-bold"
/>
</div>
</div>
</div>
</div>
{/* Current Configuration Performance */}
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700 mb-8">
<h3 className="text-xl font-semibold mb-6 text-white">🎯 Current Configuration Performance</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<HitRateBar
label={'TP1: ' + data.currentLevels.tp1Percent + '%'}
hitRate={data.currentLevels.tp1HitRate}
exits={data.tradesByOutcome.tp1Exits}
color="bg-green-500"
/>
<HitRateBar
label={'TP2: ' + data.currentLevels.tp2Percent + '%'}
hitRate={data.currentLevels.tp2HitRate}
exits={data.tradesByOutcome.tp2Exits}
color="bg-blue-500"
/>
<HitRateBar
label={'SL: ' + data.currentLevels.slPercent + '%'}
hitRate={data.currentLevels.slHitRate}
exits={data.tradesByOutcome.slExits}
color="bg-red-500"
/>
</div>
</div>
{/* Dynamic ATR Analysis */}
{data.dynamicATRAnalysis?.available && (
<div className={`rounded-xl p-6 border mb-8 ${
data.dynamicATRAnalysis.sufficientData
? 'bg-gradient-to-r from-purple-900/30 to-blue-900/30 border-purple-700'
: 'bg-yellow-900/20 border-yellow-600'
}`}>
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-semibold text-white">
🎯 Dynamic ATR-Based TP/SL Analysis
</h3>
<div className="text-sm text-gray-400">
{data.dynamicATRAnalysis.sampleSize}/{data.dynamicATRAnalysis.minSampleSize} trades collected
</div>
</div>
{/* Progress Bar */}
<div className="mb-6">
<div className="flex justify-between mb-2">
<span className="text-sm text-gray-400">Data Collection Progress</span>
<span className="text-sm font-semibold text-white">
{((data.dynamicATRAnalysis.sampleSize / data.dynamicATRAnalysis.minSampleSize) * 100).toFixed(1)}%
</span>
</div>
<div className="bg-gray-700 rounded-full h-3 overflow-hidden">
<div
className={`h-full transition-all duration-500 ${
data.dynamicATRAnalysis.sufficientData ? 'bg-green-500' : 'bg-yellow-500'
}`}
style={{
width: Math.min((data.dynamicATRAnalysis.sampleSize / data.dynamicATRAnalysis.minSampleSize) * 100, 100) + '%'
}}
/>
</div>
</div>
{/* Comparison Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
{/* Left: Current Fixed Targets */}
<div className="bg-gray-800/50 rounded-lg p-4 border border-gray-700">
<h4 className="text-sm font-semibold text-gray-400 mb-3">📌 Fixed Targets (Current)</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">TP2:</span>
<span className="text-blue-400 font-semibold">{data.currentLevels.tp2Percent}%</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-400">SL:</span>
<span className="text-red-400 font-semibold">{data.currentLevels.slPercent}%</span>
</div>
<div className="border-t border-gray-700 pt-2 mt-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">Simulated P&L:</span>
<span className="text-white font-bold">
${data.dynamicATRAnalysis.fixedSimulatedPnL.toFixed(2)}
</span>
</div>
</div>
</div>
</div>
{/* Right: Dynamic ATR Targets */}
<div className="bg-purple-900/30 rounded-lg p-4 border border-purple-600">
<h4 className="text-sm font-semibold text-purple-300 mb-3"> Dynamic ATR-Based</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">TP2 (2x ATR):</span>
<span className="text-blue-400 font-semibold">
{data.dynamicATRAnalysis.dynamicTP2Percent.toFixed(2)}%
</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-400">SL (1.5x ATR):</span>
<span className="text-red-400 font-semibold">
{data.dynamicATRAnalysis.dynamicSLPercent.toFixed(2)}%
</span>
</div>
<div className="border-t border-purple-600 pt-2 mt-2">
<div className="flex justify-between">
<span className="text-sm text-gray-400">Simulated P&L:</span>
<span className="text-green-400 font-bold">
${data.dynamicATRAnalysis.dynamicSimulatedPnL.toFixed(2)}
</span>
</div>
</div>
</div>
</div>
</div>
{/* Advantage Highlight */}
<div className={`rounded-lg p-4 mb-4 ${
data.dynamicATRAnalysis.dynamicAdvantage >= 0
? 'bg-green-900/30 border border-green-600'
: 'bg-red-900/30 border border-red-600'
}`}>
<div className="flex items-center justify-between">
<div>
<div className="text-sm text-gray-400 mb-1">Dynamic ATR Advantage</div>
<div className="text-3xl font-bold text-white">
{data.dynamicATRAnalysis.dynamicAdvantage >= 0 ? '+' : ''}
${data.dynamicATRAnalysis.dynamicAdvantage.toFixed(2)}
<span className="text-xl ml-2 text-gray-400">
({data.dynamicATRAnalysis.dynamicAdvantagePercent >= 0 ? '+' : ''}
{data.dynamicATRAnalysis.dynamicAdvantagePercent.toFixed(1)}%)
</span>
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-400 mb-1">Avg ATR</div>
<div className="text-lg font-semibold text-white">
{data.dynamicATRAnalysis.avgATRPercent.toFixed(2)}%
</div>
</div>
</div>
</div>
{/* Recommendation */}
<div className={`rounded-lg p-4 border ${
data.dynamicATRAnalysis.sufficientData
? data.dynamicATRAnalysis.recommendation.includes('IMPLEMENT')
? 'bg-green-900/30 border-green-600'
: data.dynamicATRAnalysis.recommendation.includes('CONSIDER')
? 'bg-blue-900/30 border-blue-600'
: 'bg-gray-800/50 border-gray-600'
: 'bg-yellow-900/30 border-yellow-600'
}`}>
<div className="font-semibold text-white mb-2">
{data.dynamicATRAnalysis.recommendation}
</div>
<div className="text-sm text-gray-300">
{data.dynamicATRAnalysis.reasoning}
</div>
</div>
</div>
)}
{/* Recommendations */}
<div className="bg-gradient-to-r from-blue-900/30 to-purple-900/30 border border-blue-700 rounded-xl p-6 mb-8">
<h2 className="text-2xl font-bold mb-6 text-white">💡 Optimization Recommendations</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<RecommendationCard
label="Optimal TP1"
value={data.recommendations.optimalTP1.toFixed(2) + '%'}
current={data.currentLevels.tp1Percent + '%'}
color="text-green-400"
/>
<RecommendationCard
label="Optimal TP2"
value={data.recommendations.optimalTP2.toFixed(2) + '%'}
current={data.currentLevels.tp2Percent + '%'}
color="text-blue-400"
/>
<RecommendationCard
label="Optimal SL"
value={data.recommendations.optimalSL.toFixed(2) + '%'}
current={data.currentLevels.slPercent + '%'}
color="text-red-400"
/>
</div>
{/* Reasoning */}
<div className="space-y-3 mb-6">
<ReasoningCard
label="TP1 Reasoning"
text={data.recommendations.reasoning.tp1}
color="border-green-700 bg-green-900/20"
/>
<ReasoningCard
label="TP2 Reasoning"
text={data.recommendations.reasoning.tp2}
color="border-blue-700 bg-blue-900/20"
/>
<ReasoningCard
label="SL Reasoning"
text={data.recommendations.reasoning.sl}
color="border-red-700 bg-red-900/20"
/>
</div>
{/* Projected Impact */}
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<h3 className="text-lg font-semibold mb-4 text-white">📊 Projected Impact</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<ImpactMetric
label="Win Rate Change"
value={data.recommendations.projectedImpact.expectedWinRateChange.toFixed(1) + '%'}
positive={data.recommendations.projectedImpact.expectedWinRateChange >= 0}
/>
<ImpactMetric
label="Profit Factor Change"
value={data.recommendations.projectedImpact.expectedProfitFactorChange.toFixed(2)}
positive={data.recommendations.projectedImpact.expectedProfitFactorChange >= 0}
/>
<ImpactMetric
label="Profit Improvement"
value={data.recommendations.projectedImpact.estimatedProfitImprovement.toFixed(1) + '%'}
positive={data.recommendations.projectedImpact.estimatedProfitImprovement >= 0}
/>
</div>
</div>
</div>
{/* Action Button */}
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700 text-center">
<p className="text-gray-400 mb-4">
Ready to apply these optimized levels? Update your configuration in Settings.
</p>
<Link
href="/settings"
className="inline-block px-8 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-semibold transition-colors text-white"
>
Go to Settings
</Link>
</div>
</div>
</div>
)
}
// Component helpers
function Header() {
return (
<div className="bg-gray-800/50 backdrop-blur-sm border-b border-gray-700">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div className="flex items-center space-x-4">
<Link href="/analytics" className="text-gray-400 hover:text-white transition">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</Link>
<div>
<h1 className="text-2xl font-bold text-white">🎯 TP/SL Optimization</h1>
<p className="text-sm text-gray-400">Data-driven recommendations for optimal exit levels</p>
</div>
</div>
</div>
</div>
)
}
function StatCard({ title, value, valueColor = 'text-white' }: { title: string, value: string, valueColor?: string }) {
return (
<div className="bg-gray-800/50 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
<div className="text-sm text-gray-400 mb-1">{title}</div>
<div className={'text-2xl font-bold ' + valueColor}>{value}</div>
</div>
)
}
function MetricRow({ label, value, valueColor = 'text-white' }: { label: string, value: string, valueColor?: string }) {
return (
<div className="flex justify-between">
<span className="text-gray-400">{label}:</span>
<span className={'font-semibold ' + valueColor}>{value}</span>
</div>
)
}
function HitRateBar({ label, hitRate, exits, color }: { label: string, hitRate: number, exits: number, color: string }) {
return (
<div>
<div className="text-sm text-gray-400 mb-2">{label}</div>
<div className="bg-gray-700 rounded-full h-4 overflow-hidden">
<div
className={color + ' h-full transition-all duration-500'}
style={{ width: hitRate + '%' }}
/>
</div>
<div className="flex justify-between mt-1">
<div className="text-xs text-gray-400">Hit Rate: {hitRate.toFixed(1)}%</div>
<div className="text-xs text-gray-500">{exits} exits</div>
</div>
</div>
)
}
function RecommendationCard({ label, value, current, color }: { label: string, value: string, current: string, color: string }) {
return (
<div className="bg-gray-800/50 rounded-lg p-4 border border-gray-700">
<div className="text-sm text-gray-400 mb-1">{label}</div>
<div className={'text-3xl font-bold ' + color}>{value}</div>
<div className="text-xs text-gray-400 mt-2">Current: {current}</div>
</div>
)
}
function ReasoningCard({ label, text, color }: { label: string, text: string, color: string }) {
return (
<div className={'rounded-lg p-4 border ' + color}>
<div className="font-semibold text-white mb-1">{label}</div>
<div className="text-sm text-gray-300">{text}</div>
</div>
)
}
function ImpactMetric({ label, value, positive }: { label: string, value: string, positive: boolean }) {
return (
<div>
<div className="text-sm text-gray-400 mb-1">{label}</div>
<div className={'text-2xl font-bold ' + (positive ? 'text-green-400' : 'text-red-400')}>
{positive ? '+' : ''}{value}
</div>
</div>
)
}