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