# Trade Persistence Implementation Guide ## Overview This guide details how to integrate the new **persistent trading API** that saves trades to the database, ensuring data survives browser refresh and server restart. --- ## Backend Changes (✅ Complete) ### 1. New File: `backend/app/api/trading_persistent.py` - **Purpose**: Replace in-memory trading with database-backed persistence - **Key Features**: - All trades saved to `trades` table - Position state saved to `positions` table - Simulation state saved to `simulations` table - Automatic creation of simulation on first use - Full CRUD operations for portfolio management ### 2. Updated: `backend/app/main.py` - Changed import from `trading` to `trading_persistent as trading` - All existing API endpoints remain the same (`/api/trading/...`) - No breaking changes to API interface ### 3. Database Models (Already Exist) - `Simulation`: Tracks overall trading session - `Trade`: Individual trade records with P&L - `Position`: Current position state - All relationships configured correctly --- ## API Endpoints ### 1. **POST /api/trading/execute** Execute a trade and save to database. **Request:** ```json { "action": "BUY" | "SELL", "quantity": 1.5, "price": 2650.50, "symbol": "XAU/USD", "notes": "Optional trade notes", "stop_loss": 2640.0, // Optional "take_profit": 2670.0 // Optional } ``` **Response:** ```json { "trade": { "id": 123, "action": "BUY", "quantity": 1.5, "price": 2650.50, "total": 3975.75, "pnl": null, "timestamp": 1704841200 }, "portfolio": { "cash": 96024.25, "initial_capital": 100000.0, "position": { "symbol": "XAU/USD", "quantity": 1.5, "avg_price": 2650.50, "current_price": 2650.50, "unrealized_pnl": 0.0, "unrealized_pnl_percent": 0.0 }, "trades": [...], "equity_history": [...], "total_pnl": 0.0, "total_pnl_percent": 0.0 } } ``` ### 2. **GET /api/trading/portfolio** Get current portfolio state from database. **Response:** Same `portfolio` object as above ### 3. **POST /api/trading/reset** Reset simulation to initial state (deletes all trades/positions). **Response:** ```json { "message": "Simulation reset successfully", "portfolio": { /* New empty portfolio */ } } ``` ### 4. **GET /api/trading/history?limit=100** Get trade history. **Response:** ```json [ { "id": 123, "action": "BUY", "quantity": 1.5, "price": 2650.50, "total": 3975.75, "pnl": null, "timestamp": 1704841200 }, ... ] ``` ### 5. **GET /api/trading/stats** Get trading statistics. **Response:** ```json { "total_trades": 25, "winning_trades": 15, "losing_trades": 10, "win_rate": 60.0, "total_pnl": 2500.50, "total_pnl_percent": 2.5, "total_profit": 5000.0, "total_loss": 2500.0, "profit_factor": 2.0, "current_capital": 102500.50, "initial_capital": 100000.0 } ``` --- ## Frontend Integration ### 1. **New File: `frontend/src/services/tradingAPI.ts`** (✅ Created) API service layer for communicating with persistent backend. **Key Functions:** ```typescript executeTradeAPI(trade: TradeRequest): Promise getPortfolioAPI(): Promise resetSimulationAPI(): Promise<{ message: string; portfolio: PortfolioState }> getTradeHistoryAPI(limit?: number): Promise> getTradingStatsAPI(): Promise convertBackendPortfolio(backendPortfolio, currentPrice): Portfolio ``` ### 2. **Updates Needed in `frontend/src/App.tsx`** #### Step 1: Import the API service ```typescript import { executeTradeAPI, getPortfolioAPI, resetSimulationAPI, convertBackendPortfolio } from './services/tradingAPI' ``` #### Step 2: Add loading state ```typescript const [isLoadingTrade, setIsLoadingTrade] = useState(false) ``` #### Step 3: Add portfolio loader function ```typescript 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) setPortfolio(recalcPortfolio(createInitialPortfolio(), currentPrice)) } }, [currentPrice]) ``` #### Step 4: Load portfolio on mount ```typescript useEffect(() => { if (syncToBackend) { loadPortfolioFromBackend() } }, []) // Only run once on mount ``` #### Step 5: Update handleBuy Replace the existing `handleBuy` function with: ```typescript const handleBuy = useCallback(async (quantity: number) => { if (quantity <= 0 || Number.isNaN(quantity)) return if (!syncToBackend) { // Keep original in-memory logic for backward compatibility // ... existing code ... return } // NEW: Backend-persisted logic setIsLoadingTrade(true) try { await executeTradeAPI({ action: 'BUY', quantity, price: currentPrice, symbol: TRADING_SYMBOL }) // Reload portfolio from backend const backendPortfolio = await getPortfolioAPI() const converted = convertBackendPortfolio(backendPortfolio, currentPrice) setPortfolio(converted) console.log('✅ BUY trade executed and synced') } 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]) ``` #### Step 6: Update handleSell Similar pattern to handleBuy: ```typescript const handleSell = useCallback(async (quantity: number, reason = 'Manual exit') => { if (!syncToBackend) { // Keep original in-memory logic // ... existing code ... return } setIsLoadingTrade(true) try { const currentPortfolio = await getPortfolioAPI() if (!currentPortfolio.position) { alert('No open position to close') return } const size = Math.min(quantity, currentPortfolio.position.quantity) await executeTradeAPI({ action: 'SELL', quantity: size, price: currentPrice, symbol: TRADING_SYMBOL, notes: reason }) const backendPortfolio = await getPortfolioAPI() const converted = convertBackendPortfolio(backendPortfolio, currentPrice) setPortfolio(converted) console.log('✅ SELL trade executed and synced') } catch (error: any) { console.error('❌ Trade execution failed:', error) alert(`Trade failed: ${error.response?.data?.detail || 'Please try again'}`) } finally { setIsLoadingTrade(false) } }, [currentPrice, syncToBackend]) ``` #### Step 7: Update handleReset ```typescript const handleReset = useCallback(async () => { if (!syncToBackend) { setPortfolio(recalcPortfolio(createInitialPortfolio(), currentPrice)) setAiAnalysis(null) return } 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]) ``` #### Step 8: Add loading indicator (optional but recommended) In your Trade panel component, disable buttons during trades: ```typescript ``` --- ## Testing Checklist ### Backend Tests 1. ✅ Start backend: `cd backend && ./start.sh` 2. ✅ Check database tables exist: `simulations`, `trades`, `positions` 3. ✅ Test endpoints with curl or Postman: ```bash # Get portfolio curl http://localhost:8001/api/trading/portfolio # Execute trade curl -X POST http://localhost:8001/api/trading/execute \ -H "Content-Type: application/json" \ -d '{"action":"BUY","quantity":1.5,"price":2650.50,"symbol":"XAU/USD"}' # Reset curl -X POST http://localhost:8001/api/trading/reset ``` ### Frontend Tests 1. ✅ Ensure `syncToBackend` is enabled (toggle in UI) 2. ✅ Refresh browser → Portfolio should load from DB 3. ✅ Execute BUY trade → Should save to DB 4. ✅ Execute SELL trade → Should update DB 5. ✅ Refresh browser → Trades should persist 6. ✅ Restart backend → Trades should still exist 7. ✅ Reset simulation → Should clear all trades ### Integration Tests 1. ✅ Execute multiple trades 2. ✅ Restart backend server 3. ✅ Refresh browser 4. ✅ Verify all trades are present 5. ✅ Verify P&L is correct 6. ✅ Verify equity history is preserved --- ## Rollback Plan If issues arise, you can revert by: 1. Change `backend/app/main.py`: `from app.api import trading` (remove `_persistent`) 2. Restart backend 3. In-memory trading will be restored --- ## Key Benefits ✅ **Persistence**: Trades survive browser refresh and server restart ✅ **Data Integrity**: All trades stored in relational database with ACID guarantees ✅ **Audit Trail**: Complete history of all trades with timestamps ✅ **Statistics**: Real-time trading stats from database queries ✅ **Scalability**: Ready for multi-user support (user_id field exists) ✅ **Backward Compatible**: In-memory mode still available when `syncToBackend=false` --- ## Troubleshooting ### "Trade failed: Insufficient funds" - Check `current_capital` in database: `SELECT * FROM simulations;` - Verify trade total doesn't exceed available cash ### "No open position to close" - Check positions table: `SELECT * FROM positions;` - Ensure position exists before selling ### Portfolio not loading on refresh - Check backend logs for errors - Verify API endpoint returns 200 OK - Check browser console for CORS or network errors ### Database locked errors - Ensure only one backend instance is running - Check for zombie processes: `ps aux | grep python` - Kill if needed: `pkill -f "uvicorn app.main:app"` --- ## Next Steps 1. **Implement frontend updates** (follow steps in Frontend Integration section) 2. **Test thoroughly** (use Testing Checklist) 3. **Monitor logs** for any errors 4. **Add loading indicators** for better UX 5. **Consider adding optimistic updates** (update UI immediately, sync in background) --- ## File Reference - ✅ `backend/app/api/trading_persistent.py` - New persistent trading API - ✅ `backend/app/main.py` - Updated to use persistent trading - ✅ `backend/app/models/models.py` - Database models (already complete) - ✅ `backend/app/db/database.py` - Database connection (already complete) - ✅ `frontend/src/services/tradingAPI.ts` - API service layer - ⏳ `frontend/src/App.tsx` - Needs updates (follow guide above) --- ## Support If you encounter any issues: 1. Check backend logs: `tail -f backend/server.log` 2. Check browser console for errors 3. Verify database state: SQLite browser or `sqlite3 backend/test_phase1.db` 4. Review this guide for troubleshooting steps