feat: add quality score display and timezone fixes

- Add qualityScore to ExecuteTradeResponse interface and response object
- Update analytics page to always show Signal Quality card (N/A if unavailable)
- Fix n8n workflow to pass context metrics and qualityScore to execute endpoint
- Fix timezone in Telegram notifications (Europe/Berlin)
- Fix symbol normalization in /api/trading/close endpoint
- Update Drift ETH-PERP minimum order size (0.002 ETH not 0.01)
- Add transaction confirmation to closePosition() to prevent phantom closes
- Add 30-second grace period for new trades in Position Manager
- Fix execution order: database save before Position Manager.addTrade()
- Update copilot instructions with transaction confirmation pattern
This commit is contained in:
mindesbunister
2025-11-01 17:00:37 +01:00
parent 7788327a4e
commit 056440bf8f
9 changed files with 110 additions and 49 deletions

View File

@@ -34,6 +34,7 @@ await positionManager.addTrade(activeTrade)
- Closes positions via `closePosition()` market orders when targets hit
- Acts as backup if on-chain orders don't fill
- State persistence: Saves to database, restores on restart via `configSnapshot.positionManagerState`
- **Grace period for new trades:** Skips "external closure" detection for positions <30 seconds old (Drift positions take 5-10s to propagate)
### 2. Drift Client (`lib/drift/client.ts`)
**Purpose:** Solana/Drift Protocol SDK wrapper for order execution
@@ -47,7 +48,25 @@ const health = await driftService.getAccountHealth()
**Wallet handling:** Supports both JSON array `[91,24,...]` and base58 string formats from Phantom wallet
### 3. Order Placement (`lib/drift/orders.ts`)
**Critical function:** `placeExitOrders()` - places TP/SL orders on-chain
**Critical functions:**
- `openPosition()` - Opens market position with transaction confirmation
- `closePosition()` - Closes position with transaction confirmation
- `placeExitOrders()` - Places TP/SL orders on-chain
**CRITICAL: Transaction Confirmation Pattern**
Both `openPosition()` and `closePosition()` MUST confirm transactions on-chain:
```typescript
const txSig = await driftClient.placePerpOrder(orderParams)
console.log('⏳ Confirming transaction on-chain...')
const connection = driftService.getConnection()
const confirmation = await connection.confirmTransaction(txSig, 'confirmed')
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`)
}
console.log('✅ Transaction confirmed on-chain')
```
Without this, the SDK returns signatures for transactions that never execute, causing phantom trades/closes.
**Dual Stop System** (USE_DUAL_STOPS=true):
```typescript
@@ -253,7 +272,7 @@ docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt"
2. **Wrong DATABASE_URL:** Container runtime needs `trading-bot-postgres`, Prisma CLI from host needs `localhost:5432`
3. **Symbol format mismatch:** Always normalize with `normalizeTradingViewSymbol()` before calling Drift
3. **Symbol format mismatch:** Always normalize with `normalizeTradingViewSymbol()` before calling Drift (applies to ALL endpoints including `/api/trading/close`)
4. **Missing reduce-only flag:** Exit orders without `reduceOnly: true` can accidentally open new positions
@@ -268,6 +287,24 @@ docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt"
- `TAKE_PROFIT_2_SIZE_PERCENT=80` means "close 80% of REMAINING" (not of original)
- Actual runner size = (100 - TP1%) × (100 - TP2%) / 100 = 5% with defaults
9. **Transaction confirmation CRITICAL:** Both `openPosition()` AND `closePosition()` MUST call `connection.confirmTransaction()` after `placePerpOrder()`. Without this, the SDK returns transaction signatures that aren't confirmed on-chain, causing "phantom trades" or "phantom closes". Always check `confirmation.value.err` before proceeding.
10. **Execution order matters:** When creating trades via API endpoints, the order MUST be:
1. Open position + place exit orders
2. Save to database (`createTrade()`)
3. Add to Position Manager (`positionManager.addTrade()`)
If Position Manager is added before database save, race conditions occur where monitoring checks before the trade exists in DB.
11. **New trade grace period:** Position Manager skips "external closure" detection for trades <30 seconds old because Drift positions take 5-10 seconds to propagate after opening. Without this grace period, new positions are immediately detected as "closed externally" and cancelled.
12. **Drift minimum position sizes:** Actual minimums differ from documentation:
- SOL-PERP: 0.1 SOL (~$5-15 depending on price)
- ETH-PERP: 0.002 ETH (~$7-8 at $4000/ETH) - NOT 0.01 ETH
- BTC-PERP: 0.0001 BTC (~$10-12 at $100k/BTC)
Always calculate: `minOrderSize × currentPrice` must exceed Drift's $4 minimum. Add buffer for price movement.
## File Conventions
- **API routes:** `app/api/[feature]/[action]/route.ts` (Next.js 15 App Router)