fix: auto-clean leftovers after stop hits
This commit is contained in:
@@ -64,15 +64,22 @@ async function validateOpenTrades() {
|
|||||||
const marketConfig = getMarketConfig(trade.symbol)
|
const marketConfig = getMarketConfig(trade.symbol)
|
||||||
const position = await driftService.getPosition(marketConfig.driftMarketIndex)
|
const position = await driftService.getPosition(marketConfig.driftMarketIndex)
|
||||||
|
|
||||||
// Calculate expected position size in base assets
|
// Prefer Position Manager snapshot (captures partial closes) before falling back to original size
|
||||||
const expectedSizeBase = trade.positionSizeUSD / trade.entryPrice
|
const configSnapshot = trade.configSnapshot as any
|
||||||
|
const pmState = configSnapshot?.positionManagerState
|
||||||
|
const expectedSizeUSD = typeof pmState?.currentSize === 'number' && pmState.currentSize > 0
|
||||||
|
? pmState.currentSize
|
||||||
|
: trade.positionSizeUSD
|
||||||
|
|
||||||
|
// Calculate expected position size in base assets (approximate using entry price for consistency)
|
||||||
|
const expectedSizeBase = expectedSizeUSD / trade.entryPrice
|
||||||
const actualSizeBase = position?.size || 0
|
const actualSizeBase = position?.size || 0
|
||||||
|
|
||||||
// Check if position exists and size matches (with 50% tolerance for partial fills)
|
// Check if position exists and size matches (with 50% tolerance for partial fills)
|
||||||
const sizeDiff = Math.abs(expectedSizeBase - actualSizeBase)
|
const sizeDiff = Math.abs(expectedSizeBase - actualSizeBase)
|
||||||
const sizeRatio = actualSizeBase / expectedSizeBase
|
const sizeRatio = expectedSizeBase > 0 ? actualSizeBase / expectedSizeBase : 0
|
||||||
|
|
||||||
if (!position || position.side === 'none' || sizeRatio < 0.5) {
|
if (!position || position.side === 'none' || sizeRatio < 0.2) {
|
||||||
console.log(`⚠️ PHANTOM TRADE DETECTED:`)
|
console.log(`⚠️ PHANTOM TRADE DETECTED:`)
|
||||||
console.log(` Trade ID: ${trade.id.substring(0, 20)}...`)
|
console.log(` Trade ID: ${trade.id.substring(0, 20)}...`)
|
||||||
console.log(` Symbol: ${trade.symbol} ${trade.direction}`)
|
console.log(` Symbol: ${trade.symbol} ${trade.direction}`)
|
||||||
|
|||||||
@@ -498,15 +498,31 @@ export class PositionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Position exists but size mismatch (partial close by TP1?)
|
// Position exists but size mismatch (partial close by TP1?)
|
||||||
if (position.size < trade.currentSize * 0.95) { // 5% tolerance
|
const onChainBaseSize = Math.abs(position.size)
|
||||||
console.log(`⚠️ Position size mismatch: expected ${trade.currentSize}, got ${position.size}`)
|
const onChainSizeUSD = onChainBaseSize * currentPrice
|
||||||
|
const trackedSizeUSD = trade.currentSize
|
||||||
|
|
||||||
|
if (trackedSizeUSD > 0 && onChainSizeUSD < trackedSizeUSD * 0.95) { // 5% tolerance
|
||||||
|
const expectedBaseSize = trackedSizeUSD / currentPrice
|
||||||
|
console.log(`⚠️ Position size mismatch: tracking $${trackedSizeUSD.toFixed(2)} (~${expectedBaseSize.toFixed(4)} units) but on-chain shows $${onChainSizeUSD.toFixed(2)} (${onChainBaseSize.toFixed(4)} units)`)
|
||||||
|
|
||||||
// CRITICAL: If mismatch is extreme (>50%), this is a phantom trade
|
// CRITICAL: If mismatch is extreme (>50%), this is a phantom trade
|
||||||
const sizeRatio = (position.size * currentPrice) / trade.currentSize
|
const sizeRatio = trackedSizeUSD > 0 ? onChainSizeUSD / trackedSizeUSD : 0
|
||||||
if (sizeRatio < 0.5) {
|
if (sizeRatio < 0.5) {
|
||||||
|
const tradeAgeSeconds = (Date.now() - trade.entryTime) / 1000
|
||||||
|
const probablyPartialRunner = trade.tp1Hit || tradeAgeSeconds > 60
|
||||||
|
|
||||||
|
if (probablyPartialRunner) {
|
||||||
|
console.log(`🛠️ Detected stray remainder (${(sizeRatio * 100).toFixed(1)}%) after on-chain exit - forcing market close`)
|
||||||
|
trade.currentSize = onChainSizeUSD
|
||||||
|
await this.saveTradeState(trade)
|
||||||
|
await this.executeExit(trade, 100, 'manual', currentPrice)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`🚨 EXTREME SIZE MISMATCH (${(sizeRatio * 100).toFixed(1)}%) - Closing phantom trade`)
|
console.log(`🚨 EXTREME SIZE MISMATCH (${(sizeRatio * 100).toFixed(1)}%) - Closing phantom trade`)
|
||||||
console.log(` Expected: $${trade.currentSize.toFixed(2)}`)
|
console.log(` Expected: $${trackedSizeUSD.toFixed(2)}`)
|
||||||
console.log(` Actual: $${(position.size * currentPrice).toFixed(2)}`)
|
console.log(` Actual: $${onChainSizeUSD.toFixed(2)}`)
|
||||||
|
|
||||||
// Close as phantom trade
|
// Close as phantom trade
|
||||||
try {
|
try {
|
||||||
@@ -534,10 +550,15 @@ export class PositionManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current size to match reality (convert base asset size to USD using current price)
|
// Update current size to match reality and run TP1 adjustments if needed
|
||||||
trade.currentSize = position.size * currentPrice
|
trade.currentSize = onChainSizeUSD
|
||||||
trade.tp1Hit = true
|
if (!trade.tp1Hit) {
|
||||||
await this.saveTradeState(trade)
|
trade.tp1Hit = true
|
||||||
|
await this.handlePostTp1Adjustments(trade, 'on-chain TP1 size sync')
|
||||||
|
} else {
|
||||||
|
await this.saveTradeState(trade)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user