Co-authored-by: mindesbunister <32161838+mindesbunister@users.noreply.github.com>
This commit is contained in:
@@ -267,31 +267,89 @@ export class PositionManager {
|
||||
// Start monitoring if not already running
|
||||
if (!this.isMonitoring && this.activeTrades.size > 0) {
|
||||
await this.startMonitoring()
|
||||
|
||||
// BUG #77 FIX: Verify monitoring actually started
|
||||
if (this.activeTrades.size > 0 && !this.isMonitoring) {
|
||||
const errorMsg = `CRITICAL: Failed to start monitoring! activeTrades=${this.activeTrades.size}, isMonitoring=${this.isMonitoring}`
|
||||
console.error(`❌ ${errorMsg}`)
|
||||
|
||||
// Log to persistent file
|
||||
const { logCriticalError } = await import('../utils/persistent-logger')
|
||||
await logCriticalError('MONITORING_START_FAILED', {
|
||||
activeTradesCount: this.activeTrades.size,
|
||||
isMonitoring: this.isMonitoring,
|
||||
symbols: Array.from(this.activeTrades.values()).map(t => t.symbol),
|
||||
tradeIds: Array.from(this.activeTrades.keys())
|
||||
})
|
||||
|
||||
throw new Error(errorMsg)
|
||||
}
|
||||
|
||||
logger.log(`✅ Monitoring verification passed: isMonitoring=${this.isMonitoring}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a trade from monitoring
|
||||
* BUG #78 FIX: Safely handle order cancellation to avoid removing active position orders
|
||||
*/
|
||||
async removeTrade(tradeId: string): Promise<void> {
|
||||
const trade = this.activeTrades.get(tradeId)
|
||||
if (trade) {
|
||||
logger.log(`🗑️ Removing trade: ${trade.symbol}`)
|
||||
|
||||
// Cancel all orders for this symbol (cleanup orphaned orders)
|
||||
// BUG #78 FIX: Check Drift position size before canceling orders
|
||||
// If Drift shows an open position, DON'T cancel orders (may belong to active position)
|
||||
try {
|
||||
const { cancelAllOrders } = await import('../drift/orders')
|
||||
const cancelResult = await cancelAllOrders(trade.symbol)
|
||||
if (cancelResult.success && cancelResult.cancelledCount! > 0) {
|
||||
logger.log(`✅ Cancelled ${cancelResult.cancelledCount} orphaned orders`)
|
||||
const driftService = getDriftService()
|
||||
const marketConfig = getMarketConfig(trade.symbol)
|
||||
|
||||
// Query Drift for current position
|
||||
const driftPosition = await driftService.getPosition(marketConfig.driftMarketIndex)
|
||||
|
||||
if (driftPosition && Math.abs(driftPosition.size) >= 0.01) {
|
||||
// Position still open on Drift - DO NOT cancel orders
|
||||
console.warn(`⚠️ SAFETY CHECK: ${trade.symbol} position still open on Drift (size: ${driftPosition.size})`)
|
||||
console.warn(` Skipping order cancellation to avoid removing active position protection`)
|
||||
console.warn(` Removing from tracking only`)
|
||||
|
||||
// Just remove from map, don't cancel orders
|
||||
this.activeTrades.delete(tradeId)
|
||||
|
||||
// Log for monitoring
|
||||
const { logCriticalError } = await import('../utils/persistent-logger')
|
||||
await logCriticalError('ORPHAN_REMOVAL_SKIPPED_ACTIVE_POSITION', {
|
||||
tradeId,
|
||||
symbol: trade.symbol,
|
||||
driftSize: driftPosition.size,
|
||||
reason: 'Drift position still open - preserved orders for safety'
|
||||
})
|
||||
} else {
|
||||
// Position confirmed closed on Drift - safe to cancel orders
|
||||
logger.log(`✅ Drift position confirmed closed (size: ${driftPosition?.size || 0})`)
|
||||
logger.log(` Safe to cancel remaining orders`)
|
||||
|
||||
const { cancelAllOrders } = await import('../drift/orders')
|
||||
const cancelResult = await cancelAllOrders(trade.symbol)
|
||||
|
||||
if (cancelResult.success && cancelResult.cancelledCount! > 0) {
|
||||
logger.log(`✅ Cancelled ${cancelResult.cancelledCount} orphaned orders`)
|
||||
} else if (!cancelResult.success) {
|
||||
console.error(`❌ Failed to cancel orders: ${cancelResult.error}`)
|
||||
} else {
|
||||
logger.log(`ℹ️ No orders to cancel`)
|
||||
}
|
||||
|
||||
this.activeTrades.delete(tradeId)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to cancel orders during trade removal:', error)
|
||||
// Continue with removal even if cancel fails
|
||||
console.error('❌ Error checking Drift position during trade removal:', error)
|
||||
console.warn('⚠️ Removing from tracking without canceling orders (safety first)')
|
||||
|
||||
// On error, err on side of caution - don't cancel orders
|
||||
this.activeTrades.delete(tradeId)
|
||||
}
|
||||
|
||||
this.activeTrades.delete(tradeId)
|
||||
|
||||
// Stop monitoring if no more trades
|
||||
if (this.activeTrades.size === 0 && this.isMonitoring) {
|
||||
this.stopMonitoring()
|
||||
@@ -481,6 +539,7 @@ export class PositionManager {
|
||||
*/
|
||||
private async startMonitoring(): Promise<void> {
|
||||
if (this.isMonitoring) {
|
||||
logger.log('⚠️ Monitoring already active, skipping duplicate start')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -490,28 +549,49 @@ export class PositionManager {
|
||||
)]
|
||||
|
||||
if (symbols.length === 0) {
|
||||
logger.log('⚠️ No symbols to monitor, skipping start')
|
||||
return
|
||||
}
|
||||
|
||||
logger.log('🚀 Starting price monitoring for:', symbols)
|
||||
logger.log('🚀 Starting price monitoring...')
|
||||
logger.log(` Active trades: ${this.activeTrades.size}`)
|
||||
logger.log(` Symbols: ${symbols.join(', ')}`)
|
||||
logger.log(` Current isMonitoring: ${this.isMonitoring}`)
|
||||
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
|
||||
await priceMonitor.start({
|
||||
symbols,
|
||||
onPriceUpdate: async (update: PriceUpdate) => {
|
||||
await this.handlePriceUpdate(update)
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('❌ Price monitor error:', error)
|
||||
},
|
||||
})
|
||||
try {
|
||||
logger.log('📡 Calling priceMonitor.start()...')
|
||||
|
||||
await priceMonitor.start({
|
||||
symbols,
|
||||
onPriceUpdate: async (update: PriceUpdate) => {
|
||||
await this.handlePriceUpdate(update)
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('❌ Price monitor error:', error)
|
||||
},
|
||||
})
|
||||
|
||||
this.isMonitoring = true
|
||||
logger.log('✅ Position monitoring active')
|
||||
|
||||
// Schedule periodic validation to detect and cleanup ghost positions
|
||||
this.scheduleValidation()
|
||||
this.isMonitoring = true
|
||||
logger.log('✅ Position monitoring active')
|
||||
logger.log(` isMonitoring flag set to: ${this.isMonitoring}`)
|
||||
|
||||
// Schedule periodic validation to detect and cleanup ghost positions
|
||||
this.scheduleValidation()
|
||||
} catch (error) {
|
||||
console.error('❌ CRITICAL: Failed to start price monitoring:', error)
|
||||
|
||||
// Log error to persistent file
|
||||
const { logCriticalError } = await import('../utils/persistent-logger')
|
||||
await logCriticalError('PRICE_MONITOR_START_FAILED', {
|
||||
symbols,
|
||||
activeTradesCount: this.activeTrades.size,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
|
||||
throw error // Re-throw so caller knows monitoring failed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user