From bb2432f3bf455495a76305af105ac55a52fa1647 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 7 Jan 2026 10:02:10 +0100 Subject: [PATCH] docs: Add Bug #92 - Exit order token sizing mismatch to Common Pitfalls - Added comprehensive documentation for TP/SL size mismatch bug - Root cause: usdToBase() recalculated tokens at each exit price - Fix: Pass positionSizeTokens from position fill, use tokensToBase() - Updated Quick Reference Table (now 74 pitfalls) - Updated last modified date to January 6, 2026 --- docs/COMMON_PITFALLS.md | 134 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/docs/COMMON_PITFALLS.md b/docs/COMMON_PITFALLS.md index b7e5936..0b3be6c 100644 --- a/docs/COMMON_PITFALLS.md +++ b/docs/COMMON_PITFALLS.md @@ -1,7 +1,7 @@ # Common Pitfalls Reference Documentation -> **Last Updated:** December 4, 2025 -> **Total Documented:** 72 Pitfalls +> **Last Updated:** January 6, 2026 +> **Total Documented:** 74 Pitfalls > **Primary Source:** `.github/copilot-instructions.md` ## Purpose @@ -98,6 +98,8 @@ This document is the **comprehensive reference** for all documented pitfalls, bu | 70 | 🔴 CRITICAL | Smart Entry | Dec 3, 2025 | Smart Validation Queue rejected by execute endpoint | | 71 | 🔴 CRITICAL | Revenge System | Dec 3, 2025 | Revenge system missing external closure integration | | 72 | 🔴 CRITICAL | Telegram | Dec 4, 2025 | Telegram webhook conflicts with polling bot | +| 91 | 🔴 CRITICAL | Orders | Jan 1, 2026 | Math.floor truncation in position close leaves fractional remnants | +| 92 | 🔴 CRITICAL | Orders | Jan 6, 2026 | Exit order token sizing mismatch - TP/SL different sizes than position | | 89 | 🔴 CRITICAL | Drift Protocol | Dec 16, 2025 | Drift fractional position remnants after SL execution | | 90 | 🔴 CRITICAL | Drift Protocol | Dec 31, 2025 | placePerpOrder vs placeAndTakePerpOrder - MARKET orders not filling | | 91 | 🔴 CRITICAL | Drift Protocol | Jan 1, 2026 | Math.floor truncation leaves fractional position remnants | @@ -1830,6 +1832,132 @@ if (percentToClose === 100) { --- +## 92. CRITICAL: Exit Order Token Sizing Mismatch - TP/SL Different Sizes Than Position (Jan 6, 2026) + +**Symptom:** Position opened with 142.91 SOL but exit orders show different sizes: +- TP1: 140.87 SOL +- Position: 142.91 SOL +- SL: 147.03 SOL (WRONG!) + +**User Report:** "that does not make sense. when i buy 1 sol and set tp and sl then i want to sell that 1 sol at these lvls. i dont care about the dollar value." + +**Financial Impact:** Exit orders closing wrong amounts - potential for: +- Under-closing: Leaving unprotected remnants +- Over-closing: Attempting to close more than exists (order rejection) +- Asymmetric risk: SL closing more than TP + +**Root Cause:** +* File: `lib/drift/orders.ts` function `placeExitOrders()` +* Code: `usdToBase()` calculated tokens as `USD / price` for each exit order +* Problem: Each exit order uses a DIFFERENT price (TP1 price, TP2 price, SL price) +* Result: Same USD amount / different prices = different token amounts + +**Math Example (SOL at entry $140):** +```typescript +// Position: $20,000 notional = 142.91 SOL at $140 + +// TP1 at $142 (1.43% above entry): +// $12,000 (60%) / $142 = 84.51 SOL ❌ (should be 85.75 SOL = 60% of 142.91) + +// SL at $136.19 (2.72% below entry): +// $20,000 (100%) / $136.19 = 146.85 SOL ❌ (should be 142.91 SOL) +``` + +**THE FIX (Jan 6, 2026):** +```typescript +// BEFORE: USD-based calculation (WRONG) +const usdToBase = (usd: number, price: number) => { + const base = usd / price // Different price = different tokens! + return Math.floor(base * 1e9) +} +const tp1Amount = usdToBase(tp1USD, options.tp1Price) // Wrong tokens + +// AFTER: Token-based calculation (CORRECT) +const tokensToBase = (tokens: number) => { + return Math.floor(tokens * 1e9) // Direct conversion +} + +// In PlaceExitOrdersOptions interface: +positionSizeTokens?: number // Pass actual token count from position fill + +// Usage - calculate percentages from actual tokens: +const tp1Tokens = positionSizeTokens * (options.tp1SizePercent / 100) +const tp1Amount = tokensToBase(tp1Tokens) // Exact token count +``` + +**Files Changed:** +1. `lib/drift/orders.ts`: + - Added `positionSizeTokens?: number` to PlaceExitOrdersOptions interface + - Added `tokensToBase(tokens)` helper function + - All exit sections (TP1, TP2, SL, soft SL, hard SL) use token-based when available + - Fixed TypeScript error: Changed `if (tp2USD > 0)` to `if (tp2Tokens > 0)` + +2. All callers updated to pass `positionSizeTokens`: + - `app/api/trading/execute/route.ts`: `openResult.fillSize` + - `lib/trading/smart-entry-timer.ts`: `openResult.fillSize` + - `lib/trading/sync-helper.ts`: `Math.abs(driftPos.size)` + - `lib/trading/position-manager.ts`: 7 calls updated with position fetching + - `lib/startup/init-position-manager.ts`: `Math.abs(position.size)` + - `lib/health/position-manager-health.ts`: Drift position fetch + token size + +**Position Fetching Pattern (for methods without position object):** +```typescript +// For runner methods that need current position size +let positionSizeTokens: number | undefined +try { + const driftService = getDriftService() + const marketConfig = getMarketConfig(trade.symbol) + const position = await driftService.getPosition(marketConfig.driftMarketIndex) + if (position) { + positionSizeTokens = Math.abs(position.size) + } +} catch (error) { + console.error('Failed to fetch position for token sizing:', error) +} + +await placeExitOrders({ + // ... other options + positionSizeTokens: positionSizeTokens, +}) +``` + +**Backward Compatible:** Falls back to USD calculation if `positionSizeTokens` not provided + +**Expected Result After Fix:** +- Position: 142.91 SOL +- TP1 (60%): 85.75 SOL ✅ +- TP2 (remaining 40%): 57.16 SOL ✅ +- SL: 142.91 SOL ✅ + +**Verification:** +```bash +# Check logs for new token-based output +docker logs -f trading-bot-v4 | grep "Exit order sizes" +# Expected: "📊 Exit order sizes (TOKEN-BASED - CORRECT)" +``` + +**Prevention Rules:** +1. NEVER use USD / price to calculate token amounts for exit orders +2. ALWAYS pass actual token count from position fill to exit order placement +3. Exit orders should close PORTIONS of actual position, not recalculated amounts +4. For runner methods, fetch current position size from Drift before placing orders + +**Red Flags Indicating This Bug:** +- TradingView chart shows TP/SL sizes different from position size +- Exit orders show values that don't match percentage calculations +- TP1 closes wrong amount, leaving unexpected runner size +- SL attempting to close more than position (order rejection) + +**Git Commit:** 361f3ba "critical: Fix exit order token sizing - TP/SL now use exact position size" + +**Deployment:** Jan 6, 2026 (container trading-bot-v4) + +**Status:** ✅ FIXED AND DEPLOYED + +**Lesson Learned:** When placing exit orders, the TOKEN COUNT matters, not the USD VALUE. Exit orders should close a percentage of the ACTUAL POSITION TOKENS, not a recalculated amount based on USD/price. The user said it best: "when i buy 1 sol and set tp and sl then i want to sell that 1 sol at these lvls." + +--- + ## Appendix: Pattern Recognition ### Common Root Causes @@ -1860,5 +1988,5 @@ if (percentToClose === 100) { --- -**Last Updated:** December 31, 2025 +**Last Updated:** January 6, 2026 **Maintainer:** AI Agent team following "NOTHING gets lost" principle