Files
robinhood/TRADE_PERSISTENCE_IMPLEMENTATION.md
Krikorios 48e60d015f feat: Add Phase 4 advanced metrics and components
- Add advanced metrics dashboard with trade analytics
- Add new trading components (EntryTypeAnalysis, MultiDayPositionTracker, NewsEventTracker, etc.)
- Add strategy mode selector and trend confirmation
- Add risk automation panel and slippage correlation analysis
- Add daily trading plan enhancements with modal components
- Add custom hooks (useApi, useLocalStorage, useAdvancedTradeMetrics)
- Add broker service integration and trading API
- Add test setup and vitest configuration
- Include parquet data files for live market data
- Add comprehensive documentation in docs/ folder
2025-11-27 10:23:58 +02:00

423 lines
11 KiB
Markdown

# 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<TradeResponse>
getPortfolioAPI(): Promise<PortfolioState>
resetSimulationAPI(): Promise<{ message: string; portfolio: PortfolioState }>
getTradeHistoryAPI(limit?: number): Promise<Array<any>>
getTradingStatsAPI(): Promise<TradingStats>
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
<button
onClick={() => handleBuy(quantity)}
disabled={isLoadingTrade}
>
{isLoadingTrade ? 'Processing...' : 'Buy'}
</button>
```
---
## 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