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:
@@ -9,6 +9,7 @@ import { closePosition } from '../drift/orders'
|
||||
import { getPythPriceMonitor, PriceUpdate } from '../pyth/price-monitor'
|
||||
import { getMergedConfig, TradingConfig, getMarketConfig } from '../../config/trading'
|
||||
import { updateTradeExit, updateTradeState, getOpenTrades } from '../database/trades'
|
||||
import { canUseRunner, getViableTP2Percent } from './runner-calculator'
|
||||
|
||||
export interface ActiveTrade {
|
||||
id: string
|
||||
@@ -707,9 +708,37 @@ export class PositionManager {
|
||||
if (trade.tp1Hit && !trade.tp2Hit && this.shouldTakeProfit2(currentPrice, trade)) {
|
||||
console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
|
||||
|
||||
// Calculate how much to close based on TP2 size percent
|
||||
// Check if runner would be viable with current position size
|
||||
const runnerCheck = canUseRunner(
|
||||
trade.symbol,
|
||||
trade.currentSize,
|
||||
currentPrice,
|
||||
this.config.takeProfit1SizePercent,
|
||||
this.config.takeProfit2SizePercent
|
||||
)
|
||||
|
||||
if (!runnerCheck.viable) {
|
||||
console.log(`⚠️ Runner not viable: ${runnerCheck.reason}`)
|
||||
console.log(` Skipping TP2 close, will use trailing stop on full 25% remaining`)
|
||||
|
||||
// Mark TP2 as "hit" but don't close anything - activate trailing on full 25%
|
||||
trade.tp2Hit = true
|
||||
trade.trailingStopActive = true
|
||||
trade.runnerTrailingPercent = this.getRunnerTrailingPercent(trade)
|
||||
|
||||
console.log(
|
||||
`🏃 Runner activated on full remaining position: ${((trade.currentSize / trade.positionSize) * 100).toFixed(1)}% | trailing buffer ${trade.runnerTrailingPercent?.toFixed(3)}%`
|
||||
)
|
||||
|
||||
await this.saveTradeState(trade)
|
||||
return
|
||||
}
|
||||
|
||||
// Runner is viable - proceed with TP2 close
|
||||
const percentToClose = this.config.takeProfit2SizePercent
|
||||
|
||||
console.log(`✅ Runner viable: ${runnerCheck.runnerSizeBase.toFixed(4)} base (${runnerCheck.runnerSizeUSD.toFixed(2)} USD)`)
|
||||
|
||||
await this.executeExit(trade, percentToClose, 'TP2', currentPrice)
|
||||
|
||||
// If some position remains, mark TP2 as hit and activate trailing stop
|
||||
|
||||
Reference in New Issue
Block a user