Fix runner system by checking minimum position size viability
PROBLEM: Runner never activated because Drift force-closes positions below
minimum size. TP2 would close 80% leaving 5% runner (~$105), but Drift
automatically closed the entire position.
SOLUTION:
1. Created runner-calculator.ts with canUseRunner() to check if remaining
size would be above Drift minimums BEFORE executing TP2 close
2. If runner not viable: Skip TP2 close entirely, activate trailing stop
on full 25% remaining (from TP1)
3. If runner viable: Execute TP2 as normal, activate trailing on 5%
Benefits:
- Runner system will now actually work for viable position sizes
- Positions that are too small won't try to force-close below minimums
- Better logs showing why runner did/didn't activate
- Trailing stop works on larger % if runner not viable (better R:R)
Example: $2100 position → $525 after TP1 → $105 runner = VIABLE
$4 ETH position → $1 after TP1 → $0.20 runner = NOT VIABLE
Runner will trail with ATR-based dynamic % (0.25-0.9%) below peak price.
This commit is contained in:
@@ -500,19 +500,24 @@ export async function closePosition(
|
||||
}
|
||||
|
||||
// Calculate size to close
|
||||
let sizeToClose = position.size * (params.percentToClose / 100)
|
||||
const sizeToClose = position.size * (params.percentToClose / 100)
|
||||
const remainingSize = position.size - sizeToClose
|
||||
|
||||
// CRITICAL FIX: If calculated size is below minimum, close 100% instead
|
||||
// This prevents "runner" positions from being too small to close
|
||||
if (sizeToClose < marketConfig.minOrderSize) {
|
||||
console.log(`⚠️ Calculated close size ${sizeToClose.toFixed(4)} is below minimum ${marketConfig.minOrderSize}`)
|
||||
console.log(` Forcing 100% close to avoid Drift rejection`)
|
||||
sizeToClose = position.size // Close entire position
|
||||
// CRITICAL: Check if remaining position would be below Drift minimum
|
||||
// If so, Drift will force-close the entire position anyway
|
||||
// Better to detect this upfront and return fullyClosed=true
|
||||
const willForceFullClose = remainingSize > 0 && remainingSize < marketConfig.minOrderSize
|
||||
|
||||
if (willForceFullClose && params.percentToClose < 100) {
|
||||
console.log(`⚠️ WARNING: Remaining size ${remainingSize.toFixed(4)} would be below Drift minimum ${marketConfig.minOrderSize}`)
|
||||
console.log(` Drift will force-close entire position. Proceeding with 100% close.`)
|
||||
console.log(` 💡 TIP: Increase position size or decrease TP2 close % to enable runner`)
|
||||
}
|
||||
|
||||
console.log(`📝 Close order details:`)
|
||||
console.log(` Current position: ${position.size.toFixed(4)} ${position.side}`)
|
||||
console.log(` Closing: ${params.percentToClose}% (${sizeToClose.toFixed(4)})`)
|
||||
console.log(` Remaining after close: ${remainingSize.toFixed(4)}`)
|
||||
console.log(` Entry price: $${position.entryPrice.toFixed(4)}`)
|
||||
console.log(` Unrealized P&L: $${position.unrealizedPnL.toFixed(2)}`)
|
||||
|
||||
@@ -620,8 +625,8 @@ export async function closePosition(
|
||||
|
||||
// Check remaining position size after close
|
||||
const updatedPosition = await driftService.getPosition(marketConfig.driftMarketIndex)
|
||||
const remainingSize = updatedPosition ? Math.abs(updatedPosition.size) : 0
|
||||
const fullyClosed = !updatedPosition || remainingSize === 0
|
||||
const actualRemainingSize = updatedPosition ? Math.abs(updatedPosition.size) : 0
|
||||
const fullyClosed = !updatedPosition || actualRemainingSize === 0 || willForceFullClose
|
||||
|
||||
if (fullyClosed) {
|
||||
console.log('🗑️ Position fully closed, cancelling remaining orders...')
|
||||
@@ -631,7 +636,7 @@ export async function closePosition(
|
||||
}
|
||||
} else if (params.percentToClose === 100) {
|
||||
console.log(
|
||||
`⚠️ Requested 100% close but ${remainingSize.toFixed(4)} base remains on-chain`
|
||||
`⚠️ Requested 100% close but ${actualRemainingSize.toFixed(4)} base remains on-chain`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -642,7 +647,7 @@ export async function closePosition(
|
||||
closedSize: sizeToClose,
|
||||
realizedPnL,
|
||||
fullyClosed,
|
||||
remainingSize,
|
||||
remainingSize: actualRemainingSize,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user