// INSTRUCTIONS: Replace handleBuy, handleSell, and handleReset in App.tsx with these versions // Also add loadPortfolioFromBackend and call it in useEffect import { executeTradeAPI, getPortfolioAPI, resetSimulationAPI, convertBackendPortfolio } from './services/tradingAPI' // Add this state at the top of App component const [isLoadingTrade, setIsLoadingTrade] = useState(false) // Add this function to load portfolio on mount const loadPortfolioFromBackend = useCallback(async () => { try { const backendPortfolio = await getPortfolioAPI() const converted = convertBackendPortfolio(backendPortfolio, currentPrice) setPortfolio(converted) console.log('✅ Portfolio loaded from backend:', converted) } catch (error) { console.error('Failed to load portfolio from backend:', error) // Fall back to default portfolio setPortfolio(recalcPortfolio(createInitialPortfolio(), currentPrice)) } }, [currentPrice]) // Add this useEffect to load on mount useEffect(() => { if (syncToBackend) { loadPortfolioFromBackend() } }, []) // Only run once on mount // REPLACE handleBuy with this version const handleBuy = useCallback(async (quantity: number) => { if (quantity <= 0 || Number.isNaN(quantity)) return if (!syncToBackend) { // Original in-memory logic (keep for backward compatibility) setPortfolio((prev) => { const cost = quantity * currentPrice if (cost > prev.cash) { alert('Insufficient cash for this order') return prev } const existing = prev.position const totalQuantity = existing ? existing.quantity + quantity : quantity const avgPrice = existing ? ((existing.avgPrice * existing.quantity + currentPrice * quantity) / totalQuantity) : currentPrice const trade: Trade = { id: `${Date.now()}-${Math.floor(Math.random() * 1000)}`, timestamp: Date.now(), action: 'BUY', quantity, price: currentPrice, total: Number(cost.toFixed(2)), } const updated: Portfolio = { ...prev, cash: Number((prev.cash - cost).toFixed(2)), position: { symbol: TRADING_SYMBOL, quantity: Number(totalQuantity.toFixed(4)), avgPrice: Number(avgPrice.toFixed(2)), currentPrice, unrealizedPnl: 0, unrealizedPnlPercent: 0, }, trades: [trade, ...prev.trades].slice(0, 200), } return recalcPortfolio(updated, currentPrice) }) return } // NEW: Backend-persisted logic setIsLoadingTrade(true) try { const response = await executeTradeAPI({ action: 'BUY', quantity, price: currentPrice, symbol: TRADING_SYMBOL }) // Reload portfolio from backend to ensure sync const backendPortfolio = await getPortfolioAPI() const converted = convertBackendPortfolio(backendPortfolio, currentPrice) setPortfolio(converted) console.log('✅ BUY trade executed and synced:', response.trade) } catch (error: any) { console.error('❌ Trade execution failed:', error) if (error.response?.data?.detail) { alert(`Trade failed: ${error.response.data.detail}`) } else { alert('Trade execution failed. Please try again.') } } finally { setIsLoadingTrade(false) } }, [currentPrice, syncToBackend]) // REPLACE handleSell with this version const handleSell = useCallback(async (quantity: number, reason = 'Manual exit') => { if (!syncToBackend) { // Original in-memory logic (keep for backward compatibility) setPortfolio((prev) => { const position = prev.position if (!position) { alert('No open position to close') return prev } const size = Math.min(quantity, position.quantity) if (size <= 0) return prev const proceeds = size * currentPrice const pnl = (currentPrice - position.avgPrice) * size const trade: Trade = { id: `${Date.now()}-${Math.floor(Math.random() * 1000)}`, timestamp: Date.now(), action: 'SELL', quantity: size, price: currentPrice, total: Number(proceeds.toFixed(2)), pnl: Number(pnl.toFixed(2)), } const remainingQty = Number((position.quantity - size).toFixed(4)) const nextPosition = remainingQty > 0.0001 ? { ...position, quantity: remainingQty, currentPrice } : null const updated: Portfolio = { ...prev, cash: Number((prev.cash + proceeds).toFixed(2)), position: nextPosition, trades: [trade, ...prev.trades].slice(0, 200), } if (reason.startsWith('Auto')) { console.info(reason) } return recalcPortfolio(updated, currentPrice) }) if (!reason.startsWith('Manual')) { setAiAnalysis(null) } return } // NEW: Backend-persisted logic setIsLoadingTrade(true) try { // Check if we have a position const currentPortfolio = await getPortfolioAPI() if (!currentPortfolio.position) { alert('No open position to close') return } const size = Math.min(quantity, currentPortfolio.position.quantity) const response = await executeTradeAPI({ action: 'SELL', quantity: size, price: currentPrice, symbol: TRADING_SYMBOL, notes: reason }) // Reload portfolio from backend to ensure sync const backendPortfolio = await getPortfolioAPI() const converted = convertBackendPortfolio(backendPortfolio, currentPrice) setPortfolio(converted) console.log('✅ SELL trade executed and synced:', response.trade) if (reason.startsWith('Auto')) { console.info(reason) } } catch (error: any) { console.error('❌ Trade execution failed:', error) if (error.response?.data?.detail) { alert(`Trade failed: ${error.response.data.detail}`) } else { alert('Trade execution failed. Please try again.') } } finally { setIsLoadingTrade(false) } if (!reason.startsWith('Manual')) { setAiAnalysis(null) } }, [currentPrice, syncToBackend]) // REPLACE handleReset with this version const handleReset = useCallback(async () => { if (!syncToBackend) { // Original in-memory logic setPortfolio(recalcPortfolio(createInitialPortfolio(), currentPrice)) setAiAnalysis(null) return } // NEW: Backend-persisted logic setIsLoadingTrade(true) try { const response = await resetSimulationAPI() const converted = convertBackendPortfolio(response.portfolio, currentPrice) setPortfolio(converted) setAiAnalysis(null) console.log('✅ Simulation reset and synced') } catch (error) { console.error('❌ Reset failed:', error) alert('Failed to reset simulation. Please try again.') } finally { setIsLoadingTrade(false) } }, [currentPrice, syncToBackend]) // OPTIONAL: Add loading indicator in your Trade panel // Show a spinner or disable buttons when isLoadingTrade is true