Fix runner activation and order cancellation
- Change takeProfit2SizePercent from 100% to 80% to leave 5% runner - Fix cancelAllOrders() to detect trigger orders using orderId > 0 - Trigger orders (TRIGGER_MARKET, TRIGGER_LIMIT) now properly canceled - Trailing stop will now activate on 5% runner position
This commit is contained in:
162
FIXES_RUNNER_AND_CANCELLATION.md
Normal file
162
FIXES_RUNNER_AND_CANCELLATION.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# Runner and Order Cancellation Fixes
|
||||||
|
|
||||||
|
## Date: 2025-01-29
|
||||||
|
|
||||||
|
## Issues Found and Fixed
|
||||||
|
|
||||||
|
### 1. **5% Runner (Trailing Stop) Not Working**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- Config had `takeProfit2SizePercent: 100` which closed 100% of remaining position at TP2
|
||||||
|
- This left 0% for the runner, so trailing stop never activated
|
||||||
|
- Logs showed "Executing TP2 for SOL-PERP (80%)" but no "Runner activated" messages
|
||||||
|
|
||||||
|
**Root Cause:**
|
||||||
|
- After TP1 closes 75%, remaining position is 25%
|
||||||
|
- TP2 at 100% closes all of that 25%, leaving nothing for trailing stop
|
||||||
|
|
||||||
|
**Fix Applied:**
|
||||||
|
```typescript
|
||||||
|
// config/trading.ts line 98
|
||||||
|
takeProfit2SizePercent: 80, // Close 80% of remaining 25% at TP2 (leaves 5% as runner)
|
||||||
|
```
|
||||||
|
|
||||||
|
**How It Works Now:**
|
||||||
|
1. Entry: 100% position ($50)
|
||||||
|
2. TP1 hits: Closes 75% → Leaves 25% ($12.50)
|
||||||
|
3. TP2 hits: Closes 80% of remaining 25% (= 20% of original) → Leaves 5% ($2.50 runner)
|
||||||
|
4. Trailing stop activates when runner reaches +0.5% profit
|
||||||
|
5. Stop loss trails 0.3% below peak price
|
||||||
|
|
||||||
|
**Expected Behavior:**
|
||||||
|
- You should now see: `🏃 Runner activated: 5.0% remaining with trailing stop`
|
||||||
|
- Then: `📈 Trailing SL updated: $X.XX → $Y.YY (0.3% below peak $Z.ZZ)`
|
||||||
|
- Finally: `🔴 TRAILING STOP HIT: SOL-PERP at +X.XX%`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. **Stop-Loss Orders Not Being Canceled After Position Closes**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- When position closed (by software or on-chain orders), 2 SL orders remained open on Drift
|
||||||
|
- Drift UI showed orphaned TRIGGER_MARKET and TRIGGER_LIMIT orders
|
||||||
|
- Logs showed "Position fully closed, cancelling remaining orders..." but NO "Cancelled X orders"
|
||||||
|
|
||||||
|
**Root Cause:**
|
||||||
|
```typescript
|
||||||
|
// OLD CODE - lib/drift/orders.ts line 570
|
||||||
|
const ordersToCancel = userAccount.orders.filter(
|
||||||
|
(order: any) =>
|
||||||
|
order.marketIndex === marketConfig.driftMarketIndex &&
|
||||||
|
order.status === 0 // ❌ WRONG: Trigger orders have different status values
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The filter `order.status === 0` only caught LIMIT orders in "open" state, but missed:
|
||||||
|
- **TRIGGER_MARKET** orders (hard stop loss)
|
||||||
|
- **TRIGGER_LIMIT** orders (soft stop loss)
|
||||||
|
|
||||||
|
These trigger orders have different status enum values in Drift SDK.
|
||||||
|
|
||||||
|
**Fix Applied:**
|
||||||
|
```typescript
|
||||||
|
// NEW CODE - lib/drift/orders.ts line 569-573
|
||||||
|
const ordersToCancel = userAccount.orders.filter(
|
||||||
|
(order: any) =>
|
||||||
|
order.marketIndex === marketConfig.driftMarketIndex &&
|
||||||
|
order.orderId > 0 // ✅ Active orders have orderId > 0 (catches ALL types)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why This Works:**
|
||||||
|
- All active orders (LIMIT, TRIGGER_MARKET, TRIGGER_LIMIT) have `orderId > 0`
|
||||||
|
- Inactive/cancelled orders have `orderId = 0`
|
||||||
|
- This catches trigger orders regardless of their status enum value
|
||||||
|
|
||||||
|
**Expected Behavior:**
|
||||||
|
- When position closes, you should now see:
|
||||||
|
```
|
||||||
|
🗑️ Position fully closed, cancelling remaining orders...
|
||||||
|
📋 Found 2 open orders to cancel (including trigger orders)
|
||||||
|
✅ Orders cancelled! Transaction: 5x7Y8z...
|
||||||
|
✅ Cancelled 2 orders
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Recommendations
|
||||||
|
|
||||||
|
### Test 1: Verify Runner Activation
|
||||||
|
1. Place a test LONG trade
|
||||||
|
2. Wait for TP1 to hit (should close 75%)
|
||||||
|
3. Wait for TP2 to hit (should close 20%, leaving 5%)
|
||||||
|
4. Look for logs: `🏃 Runner activated: 5.0% remaining with trailing stop`
|
||||||
|
5. Watch for trailing stop updates as price moves
|
||||||
|
|
||||||
|
### Test 2: Verify Order Cancellation
|
||||||
|
1. Place a test trade with dual stops enabled
|
||||||
|
2. Manually close the position from Position Manager or let it hit TP2
|
||||||
|
3. Check Docker logs for cancellation messages
|
||||||
|
4. Verify on Drift UI that NO orders remain open for SOL-PERP
|
||||||
|
|
||||||
|
**Check Logs:**
|
||||||
|
```bash
|
||||||
|
docker logs trading-bot-v4 -f | grep -E "(Runner|Trailing|Cancelled|open orders)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check Drift Orders:**
|
||||||
|
Go to https://app.drift.trade/ → Orders tab → Should show 0 open orders after close
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
1. **config/trading.ts** (line 98)
|
||||||
|
- Changed `takeProfit2SizePercent: 100` → `80`
|
||||||
|
|
||||||
|
2. **lib/drift/orders.ts** (lines 569-573, 579)
|
||||||
|
- Fixed order filtering to catch trigger orders
|
||||||
|
- Changed `order.status === 0` → `order.orderId > 0`
|
||||||
|
- Updated log message to mention trigger orders
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Deployment
|
||||||
|
|
||||||
|
Changes deployed via:
|
||||||
|
```bash
|
||||||
|
docker compose build trading-bot
|
||||||
|
docker compose up -d --force-recreate trading-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
Container restarted successfully at: 2025-01-29 (timestamp in logs)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Monitor next trade** to confirm runner activates
|
||||||
|
2. **Check Drift UI** after any close to confirm no orphaned orders
|
||||||
|
3. **Adjust trailing stop settings** if needed:
|
||||||
|
- `trailingStopPercent: 0.3` (current: trail 0.3% below peak)
|
||||||
|
- `trailingStopActivation: 0.5` (current: activate at +0.5% profit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Configuration
|
||||||
|
|
||||||
|
Current trailing stop settings in `config/trading.ts`:
|
||||||
|
```typescript
|
||||||
|
useTrailingStop: true, // Enable trailing stop
|
||||||
|
trailingStopPercent: 0.3, // Trail 0.3% below peak
|
||||||
|
trailingStopActivation: 0.5, // Activate at +0.5% profit
|
||||||
|
takeProfit1SizePercent: 75, // TP1: Close 75%
|
||||||
|
takeProfit2SizePercent: 80, // TP2: Close 80% of remaining (= 20% total)
|
||||||
|
// Runner: 5% remains
|
||||||
|
```
|
||||||
|
|
||||||
|
**Math:**
|
||||||
|
- Entry: 100% ($50 position)
|
||||||
|
- After TP1: 25% remains ($12.50)
|
||||||
|
- After TP2: 25% × (100% - 80%) = 5% remains ($2.50)
|
||||||
|
- Runner: 5% with trailing stop
|
||||||
@@ -97,7 +97,7 @@ export const DEFAULT_TRADING_CONFIG: TradingConfig = {
|
|||||||
useMarketOrders: true, // Use market orders for reliable fills
|
useMarketOrders: true, // Use market orders for reliable fills
|
||||||
confirmationTimeout: 30000, // 30 seconds max wait
|
confirmationTimeout: 30000, // 30 seconds max wait
|
||||||
takeProfit1SizePercent: 75, // Close 75% at TP1 to lock in profit
|
takeProfit1SizePercent: 75, // Close 75% at TP1 to lock in profit
|
||||||
takeProfit2SizePercent: 100, // Close remaining 25% at TP2
|
takeProfit2SizePercent: 80, // Close 80% of remaining 25% at TP2 (leaves 5% as runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supported markets on Drift Protocol
|
// Supported markets on Drift Protocol
|
||||||
|
|||||||
@@ -563,21 +563,22 @@ export async function cancelAllOrders(
|
|||||||
throw new Error('User account not found')
|
throw new Error('User account not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter orders for this market
|
// Filter orders for this market (check for active orders, not just status)
|
||||||
|
// Note: Trigger orders may have different status values, so we check for non-zero orderId
|
||||||
const ordersToCancel = userAccount.orders.filter(
|
const ordersToCancel = userAccount.orders.filter(
|
||||||
(order: any) =>
|
(order: any) =>
|
||||||
order.marketIndex === marketConfig.driftMarketIndex &&
|
order.marketIndex === marketConfig.driftMarketIndex &&
|
||||||
order.status === 0 // 0 = Open status
|
order.orderId > 0 // Active orders have orderId > 0
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ordersToCancel.length === 0) {
|
if (ordersToCancel.length === 0) {
|
||||||
console.log('✅ No open orders to cancel')
|
console.log('✅ No open orders to cancel')
|
||||||
return { success: true, cancelledCount: 0 }
|
return { success: true, cancelledCount: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`📋 Found ${ordersToCancel.length} open orders to cancel (including trigger orders)`)
|
||||||
|
|
||||||
console.log(`📋 Found ${ordersToCancel.length} open orders to cancel`)
|
// Cancel all orders for this market (cancels all types: LIMIT, TRIGGER_MARKET, TRIGGER_LIMIT)
|
||||||
|
|
||||||
// Cancel all orders for this market
|
|
||||||
const txSig = await driftClient.cancelOrders(
|
const txSig = await driftClient.cancelOrders(
|
||||||
undefined, // Cancel by market type
|
undefined, // Cancel by market type
|
||||||
marketConfig.driftMarketIndex,
|
marketConfig.driftMarketIndex,
|
||||||
|
|||||||
Reference in New Issue
Block a user