# Duplicate Position Prevention - Fix Documentation **Date:** 2025-01-27 **Issue:** Multiple positions opened on same symbol from different timeframe signals **Status:** ✅ RESOLVED ## Problem Description User received TradingView alerts on both 15-minute AND 30-minute charts for SOLUSDT. The bot: 1. ✅ Correctly extracted timeframe from both alerts (15 and 30) 2. ✅ Correctly filtered out the 30-minute signal (as intended) 3. ❌ BUT allowed the 15-minute signal even though a position already existed 4. ❌ Result: Two LONG positions on SOL-PERP opened 15 minutes apart **Root Cause:** Risk check API (`/api/trading/check-risk`) had `TODO` comment for checking existing positions but was always returning `allowed: true`. ## Solution Implemented ### 1. Updated Risk Check API **File:** `app/api/trading/check-risk/route.ts` **Changes:** - Import `getInitializedPositionManager()` instead of `getPositionManager()` - Wait for Position Manager initialization (restores trades from database) - Check if any active trade exists on the requested symbol - Block trade if duplicate found ```typescript // Check for existing positions on the same symbol const positionManager = await getInitializedPositionManager() const existingTrades = Array.from(positionManager.getActiveTrades().values()) const duplicatePosition = existingTrades.find(trade => trade.symbol === body.symbol) if (duplicatePosition) { return NextResponse.json({ allowed: false, reason: 'Duplicate position', details: `Already have ${duplicatePosition.direction} position on ${body.symbol} (entry: $${duplicatePosition.entryPrice})`, }) } ``` ### 2. Fixed Timing Issue **Problem:** `getPositionManager()` creates instance immediately but trades are restored asynchronously in background. **Solution:** Use `getInitializedPositionManager()` which waits for the initialization promise to complete before returning. ### 3. Updated .dockerignore **Problem:** Test files in `tests/` and `archive/` directories were being included in Docker build, causing TypeScript compilation errors. **Solution:** Added to `.dockerignore`: ``` tests/ archive/ *.test.ts *.spec.ts ``` ## Testing Results ### Test 1: Duplicate Position (BLOCKED ✅) ```bash curl -X POST /api/trading/check-risk \ -d '{"symbol":"SOL-PERP","direction":"long"}' Response: { "allowed": false, "reason": "Duplicate position", "details": "Already have long position on SOL-PERP (entry: $202.835871)" } ``` **Logs:** ``` 🔍 Risk check for: { symbol: 'SOL-PERP', direction: 'long' } 🚫 Risk check BLOCKED: Duplicate position exists { symbol: 'SOL-PERP', existingDirection: 'long', requestedDirection: 'long', existingEntry: 202.835871 } ``` ### Test 2: Different Symbol (ALLOWED ✅) ```bash curl -X POST /api/trading/check-risk \ -d '{"symbol":"BTC-PERP","direction":"long"}' Response: { "allowed": true, "details": "All risk checks passed" } ``` ## System Behavior Now **n8n Workflow Flow:** 1. TradingView sends alert → n8n webhook 2. Extract timeframe from message (`\.P\s+(\d+)` regex) 3. **15min Chart Only?** IF node: Check `timeframe == "15"` 4. If passed → Call `/api/trading/check-risk` 5. **NEW:** Check if position exists on symbol 6. If no duplicate → Execute trade via `/api/trading/execute` **Risk Check Matrix:** | Scenario | Timeframe Filter | Risk Check | Result | |----------|------------------|------------|---------| | 15min signal, no position | ✅ PASS | ✅ PASS | Trade executes | | 15min signal, position exists | ✅ PASS | 🚫 BLOCK | Trade blocked | | 30min signal, no position | 🚫 BLOCK | N/A | Trade blocked | | 30min signal, position exists | 🚫 BLOCK | N/A | Trade blocked | ## Future Enhancements The risk check API still has TODO items: - [ ] Check daily drawdown limit - [ ] Check trades per hour limit - [ ] Check cooldown period after loss - [ ] Check Drift account health before trade - [ ] Allow opposite direction trades (hedging)? ## Files Modified 1. `app/api/trading/check-risk/route.ts` - Added duplicate position check 2. `.dockerignore` - Excluded test files from Docker build 3. Moved `test-*.ts` files from `/` to `archive/` ## Git Commits - **8f90339** - "Add duplicate position prevention to risk check" - **17b0806** - "Add 15-minute chart filter to n8n workflow" (previous) ## Deployment ```bash docker compose build trading-bot docker compose up -d --force-recreate trading-bot ``` Bot automatically restores existing positions from database on startup via Position Manager persistence. --- **Status:** System now prevents duplicate positions on same symbol. Multiple 15-minute signals will be blocked by risk check even if timeframe filter passes.