- 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
423 lines
11 KiB
Markdown
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
|