import { useState, useCallback, useMemo, useEffect } from 'react'; import { useLocalStorage } from '@/hooks'; import { AlertModal, ConfirmModal } from '@/components/shared/Modal'; import { PlanHeader } from './PlanHeader'; import { PlanBiasSelector } from './PlanBiasSelector'; import { PlanRiskParameters } from './PlanRiskParameters'; import { PlanKeyLevelsEditor } from './PlanKeyLevelsEditor'; import { usePlanGeneration } from './usePlanGeneration'; import StrategyModeSelector from '@/components/StrategyModeSelector'; import TrendConfirmation from '@/components/TrendConfirmation'; import MultiDayPositionTracker from '@/components/MultiDayPositionTracker'; import NewsEventTracker from '@/components/NewsEventTracker'; import type { TradingPlan, DailyTradingPlanProps } from './types'; import type { StrategyMode } from '@/components/StrategyModeSelector'; import { STRATEGY_PRESETS } from '@/components/StrategyModeSelector'; import AdvancedMetricsDashboard, { type Trade as AdvancedMetricsTrade, type VolatilityBucket } from '@/components/AdvancedMetricsDashboard'; const createDefaultPlan = (currentPrice: number, strategyMode: StrategyMode = 'SWING'): TradingPlan => { const preset = STRATEGY_PRESETS[strategyMode]; const riskAmount = 10000 * (preset.riskPerTrade / 100); // Assume $10k account const stopLossDiff = currentPrice * (preset.stopLossPercent / 100); const takeProfitDiff = currentPrice * (preset.takeProfitPercent / 100); return { date: new Date().toDateString(), strategyMode, bias: 'NEUTRAL', dailyTarget: Math.round(riskAmount * 2), // 2x risk as daily target maxLoss: Math.round(riskAmount), entryZone: { min: currentPrice - (currentPrice * (preset.stopLossPercent / 200)), max: currentPrice + (currentPrice * (preset.stopLossPercent / 200)) }, targetPrice: currentPrice + takeProfitDiff, stopLoss: currentPrice - stopLossDiff, keyLevels: { support: [ currentPrice - (currentPrice * (preset.stopLossPercent / 50)), currentPrice - (currentPrice * (preset.stopLossPercent / 25)) ], resistance: [ currentPrice + (currentPrice * (preset.takeProfitPercent / 50)), currentPrice + (currentPrice * (preset.takeProfitPercent / 25)) ], }, tradingNotes: '', maxTrades: preset.maxDailyTrades, actualTrades: 0, actualPnL: 0, planFollowed: true, contextMetrics: null, }; }; const SIGNAL_LABELS: Record = { RSI_CROSSOVER: 'RSI Crossover', MA_CROSSOVER: 'MA Crossover', BB_BREAKOUT: 'Bollinger Breakout', MACD: 'MACD Signal', SUPPORT_BOUNCE: 'Support Bounce', TREND_CONFIRMATION: 'Trend Confirmation', NEWS_TRIGGERED: 'News Triggered', }; export default function DailyTradingPlan({ currentPrice, onPlanUpdate, openRouterReady, openRouterMessage, advancedTrades = [], advancedTradesSource = 'sample', }: DailyTradingPlanProps): JSX.Element { const [isEditing, setIsEditing] = useState(false); const [showResetConfirm, setShowResetConfirm] = useState(false); const [showSuccessAlert, setShowSuccessAlert] = useState(false); const [successMessage, setSuccessMessage] = useState(''); const [showAdvancedMetrics, setShowAdvancedMetrics] = useState(false); const [analyticsFocus, setAnalyticsFocus] = useState<{ timeframe: string | null; signalType: AdvancedMetricsTrade['signalType'] | null; volatility: string | null; }>({ timeframe: null, signalType: null, volatility: null }); const safeAdvancedTrades = advancedTrades ?? []; const advancedAnalyticsSummary = useMemo(() => { if (!safeAdvancedTrades.length) return null; const totalTrades = safeAdvancedTrades.length; const wins = safeAdvancedTrades.filter((trade) => trade.profitable).length; const winRate = (wins / totalTrades) * 100; const timeframePnL = safeAdvancedTrades.reduce>((acc, trade) => { acc[trade.timeframe] = (acc[trade.timeframe] ?? 0) + trade.pnl; return acc; }, {}); const bestTimeframeEntry = Object.entries(timeframePnL).sort((a, b) => b[1] - a[1])[0]; const signalPnL = safeAdvancedTrades.reduce>((acc, trade) => { acc[trade.signalType] = (acc[trade.signalType] ?? 0) + trade.pnl; return acc; }, {}); const bestSignalEntry = Object.entries(signalPnL).sort((a, b) => b[1] - a[1])[0]; const slippageImpact = safeAdvancedTrades.reduce((sum, trade) => { const gross = trade.grossPnL ?? trade.pnl; if (!gross) return sum; const impact = trade.slippage ? (trade.slippage / Math.abs(gross)) * 100 : 0; return sum + impact; }, 0) / totalTrades; return { totalTrades, winRate, bestTimeframe: bestTimeframeEntry?.[0] ?? null, bestSignal: (bestSignalEntry?.[0] as AdvancedMetricsTrade['signalType']) ?? null, slippageImpact, }; }, [safeAdvancedTrades]); const [plan, setPlan] = useLocalStorage( 'daily-trading-plan', createDefaultPlan(currentPrice) ); const { generating, error, generatePlan } = usePlanGeneration(openRouterReady); // Ensure plan resets when the stored date is stale (runs after render to avoid blocking updates) useEffect(() => { const today = new Date().toDateString(); if (plan.date !== today) { setPlan((prev) => { const next = createDefaultPlan(currentPrice, prev.strategyMode); return { ...next, bias: prev.bias, tradingNotes: prev.tradingNotes, }; }); } }, [plan.date, currentPrice, setPlan]); const handleEdit = useCallback(() => { setIsEditing(true); }, []); const handleSave = useCallback(() => { setIsEditing(false); if (onPlanUpdate) { onPlanUpdate(plan); } }, [plan, onPlanUpdate]); const handleConfirmReset = useCallback(() => { setPlan(createDefaultPlan(currentPrice)); setIsEditing(false); setShowResetConfirm(false); }, [currentPrice, setPlan]); const handleGenerateAI = useCallback(async () => { const generatedPlan = await generatePlan(currentPrice, plan); if (generatedPlan) { setPlan(generatedPlan); setSuccessMessage( `AI Plan Generated!\n\nBias: ${generatedPlan.bias}\nTarget: $${generatedPlan.dailyTarget}\n\nReview and edit the plan as needed.` ); setShowSuccessAlert(true); if (onPlanUpdate) { onPlanUpdate(generatedPlan); } } }, [currentPrice, plan, generatePlan, setPlan, onPlanUpdate]); const handleFieldChange = useCallback( (field: string, value: number) => { setPlan((prev) => { if (field === 'entryZoneMin') { return { ...prev, entryZone: { ...prev.entryZone, min: value } }; } if (field === 'entryZoneMax') { return { ...prev, entryZone: { ...prev.entryZone, max: value } }; } return { ...prev, [field]: value }; }); }, [setPlan] ); const handleBiasChange = useCallback( (bias: 'BULLISH' | 'BEARISH' | 'NEUTRAL') => { setPlan((prev) => ({ ...prev, bias })); }, [setPlan] ); const handleNotesChange = useCallback( (notes: string) => { setPlan((prev) => ({ ...prev, tradingNotes: notes })); }, [setPlan] ); const handleAdvancedTimeframeSelect = useCallback((timeframe: string) => { setAnalyticsFocus((prev) => ({ ...prev, timeframe: prev.timeframe === timeframe ? null : timeframe, })); }, []); const handleAdvancedSignalSelect = useCallback((signalType: string) => { const typedSignal = signalType as AdvancedMetricsTrade['signalType']; setAnalyticsFocus((prev) => ({ ...prev, signalType: prev.signalType === typedSignal ? null : typedSignal, })); }, []); const handleAdvancedVolatilitySelect = useCallback((bucket: VolatilityBucket) => { setAnalyticsFocus((prev) => ({ ...prev, volatility: prev.volatility === bucket.range ? null : bucket.range, })); }, []); const handleStrategyModeChange = useCallback( (mode: StrategyMode) => { const newPlan = createDefaultPlan(currentPrice, mode); setPlan((prev) => ({ ...newPlan, bias: prev.bias, tradingNotes: prev.tradingNotes, })); }, [currentPrice, setPlan] ); // Key levels handlers const handleAddSupport = useCallback(() => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, support: [...prev.keyLevels.support, currentPrice - 10], }, })); }, [currentPrice, setPlan]); const handleAddResistance = useCallback(() => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, resistance: [...prev.keyLevels.resistance, currentPrice + 10], }, })); }, [currentPrice, setPlan]); const handleRemoveSupport = useCallback( (index: number) => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, support: prev.keyLevels.support.filter((_, i) => i !== index), }, })); }, [setPlan] ); const handleRemoveResistance = useCallback( (index: number) => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, resistance: prev.keyLevels.resistance.filter((_, i) => i !== index), }, })); }, [setPlan] ); const handleUpdateSupport = useCallback( (index: number, value: number) => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, support: prev.keyLevels.support.map((level, i) => (i === index ? value : level)), }, })); }, [setPlan] ); const handleUpdateResistance = useCallback( (index: number, value: number) => { setPlan((prev) => ({ ...prev, keyLevels: { ...prev.keyLevels, resistance: prev.keyLevels.resistance.map((level, i) => (i === index ? value : level)), }, })); }, [setPlan] ); return (
setShowResetConfirm(true)} /> {/* Strategy Mode Info Banner */}
{STRATEGY_PRESETS[plan.strategyMode].emoji}

{plan.strategyMode} Mode Active

Max {STRATEGY_PRESETS[plan.strategyMode].maxDailyTrades} trades • R:R 1:{STRATEGY_PRESETS[plan.strategyMode].r2rRatio.toFixed(1)} • Stop: {STRATEGY_PRESETS[plan.strategyMode].stopLossPercent}%

{(error || (openRouterReady === false && openRouterMessage)) && (

{error || openRouterMessage}

)}
{/* Phase 3: Swing Trading Features - Show for SWING and HYBRID modes */} {(plan.strategyMode === 'SWING' || plan.strategyMode === 'HYBRID') && (
{/* Trend Confirmation for Swing Entry */} { setPlan(prev => ({ ...prev, trendConfirmed: trend.strength !== 'WEAK' })); }} /> {/* Multi-Day Position Tracker */} {plan.swingPositions && plan.swingPositions.length > 0 && ( { console.log('Position metrics updated:', metrics); }} /> )} {/* News Event Monitor */} {plan.newsEvents && plan.newsEvents.length > 0 && ( { console.log('News event alert:', event.title); }} /> )}
)} {/* Phase 4 Advanced Metrics Snapshot */}

Phase 4 · Execution intel

Advanced Metrics Summary

{advancedTradesSource === 'live' ? 'Powered by your closed trades.' : 'Showing curated sample data until you close a trade.'}

{advancedAnalyticsSummary ? `${advancedAnalyticsSummary.totalTrades} trades` : 'No trades yet'}

{advancedAnalyticsSummary ? `${advancedAnalyticsSummary.winRate.toFixed(1)}% win rate` : 'Run a session to unlock insights'}

{advancedAnalyticsSummary ? ( <>

Best timeframe

{advancedAnalyticsSummary.bestTimeframe ?? '—'}

Highest net P&L

Best signal

{advancedAnalyticsSummary.bestSignal ? SIGNAL_LABELS[advancedAnalyticsSummary.bestSignal] : '—'}

Highest risk / reward

Avg slippage impact

{Number.isFinite(advancedAnalyticsSummary.slippageImpact) ? `${advancedAnalyticsSummary.slippageImpact.toFixed(1)}%` : '—'}

Cost of fills vs. gross

{analyticsFocus.timeframe && ( Timeframe: {analyticsFocus.timeframe} )} {analyticsFocus.signalType && ( Signal: {SIGNAL_LABELS[analyticsFocus.signalType]} )} {analyticsFocus.volatility && ( Volatility: {analyticsFocus.volatility} )} {!analyticsFocus.timeframe && !analyticsFocus.signalType && !analyticsFocus.volatility && ( No filter focus selected )}
{showAdvancedMetrics && (
)} ) : (
Close at least one trade to unlock the Advanced Metrics Dashboard summary.
)}
{/* Trading Notes */}
{isEditing ? (