Add documentation for duplicate position fix
This commit is contained in:
146
docs/history/DUPLICATE_POSITION_FIX.md
Normal file
146
docs/history/DUPLICATE_POSITION_FIX.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user