FEATURES: - Position monitor now automatically detects orphaned orders when no positions - Triggers cleanup only when hasPosition: false to eliminate redundant polling - Provides detailed cleanup results in monitoring response - Leverages existing frequent position checks vs separate timers - Modified /app/api/automation/position-monitor/route.js to check for orphaned orders - Calls existing /api/drift/cleanup-orders endpoint when no positions detected - Returns cleanup status, success/failure, and summary in monitoring response - Handles cleanup errors gracefully with detailed error reporting - Eliminates need for separate 60-second cleanup polling - Uses existing position monitoring infrastructure - Only runs cleanup when positions close (triggered by hasPosition: false) - Automatic handling of orphaned orders after SL/TP execution - Added test-orphaned-cleanup-integration.js for verification - Tests both position monitor integration and direct cleanup API - Provides detailed feedback on cleanup operations This completes the automation enhancement requested - no more manual cleanup needed!
140 lines
4.8 KiB
JavaScript
140 lines
4.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Drift Order Cleanup Management Script
|
|
* Easy commands to manage the automated cleanup service
|
|
*/
|
|
|
|
const { driftOrderCleanupService } = require('./lib/drift-order-cleanup-service.js')
|
|
|
|
const commands = {
|
|
status: async () => {
|
|
console.log('📊 Drift Order Cleanup Status')
|
|
console.log('============================')
|
|
|
|
const status = driftOrderCleanupService.getStatus()
|
|
console.log(`Running: ${status.isRunning ? '✅ YES' : '❌ NO'}`)
|
|
|
|
if (status.lastCleanupTime > 0) {
|
|
const ago = Math.floor((Date.now() - status.lastCleanupTime) / 1000)
|
|
console.log(`Last cleanup: ${ago}s ago`)
|
|
} else {
|
|
console.log('Last cleanup: Never')
|
|
}
|
|
|
|
if (status.cooldownRemaining > 0) {
|
|
console.log(`Cooldown: ${Math.floor(status.cooldownRemaining / 1000)}s remaining`)
|
|
}
|
|
|
|
// Get current positions and orders
|
|
try {
|
|
const [positionsRes, ordersRes] = await Promise.all([
|
|
fetch('http://localhost:9001/api/drift/positions'),
|
|
fetch('http://localhost:9001/api/drift/orders')
|
|
])
|
|
|
|
if (positionsRes.ok && ordersRes.ok) {
|
|
const positions = await positionsRes.json()
|
|
const orders = await ordersRes.json()
|
|
|
|
console.log(`\nCurrent: ${positions.positions?.length || 0} positions, ${orders.orders?.length || 0} orders`)
|
|
|
|
if (positions.positions?.length > 0) {
|
|
console.log('\nActive Positions:')
|
|
positions.positions.forEach(pos => {
|
|
console.log(` 📈 ${pos.symbol}: ${pos.size > 0 ? 'LONG' : 'SHORT'} ${Math.abs(pos.size)} ($${pos.value?.toFixed(2) || 'N/A'})`)
|
|
})
|
|
}
|
|
|
|
if (orders.orders?.length > 0) {
|
|
console.log('\nActive Orders:')
|
|
orders.orders.forEach(order => {
|
|
console.log(` 📋 ${order.symbol}: ${order.side} ${order.size} @ $${order.price} (${order.orderType})`)
|
|
})
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log('⚠️ Could not fetch current positions/orders')
|
|
}
|
|
},
|
|
|
|
start: () => {
|
|
console.log('🚀 Starting automated cleanup service...')
|
|
driftOrderCleanupService.start()
|
|
console.log('✅ Service started! It will check for orphaned orders every 60 seconds.')
|
|
},
|
|
|
|
stop: () => {
|
|
console.log('🛑 Stopping automated cleanup service...')
|
|
driftOrderCleanupService.stop()
|
|
console.log('✅ Service stopped.')
|
|
},
|
|
|
|
cleanup: async () => {
|
|
console.log('🧹 Running manual cleanup...')
|
|
try {
|
|
const result = await driftOrderCleanupService.forceCleanup()
|
|
console.log('\n📊 Cleanup Results:')
|
|
console.log(` Positions: ${result.summary.activePositions}`)
|
|
console.log(` Orders: ${result.summary.activeOrders}`)
|
|
console.log(` Orphaned: ${result.summary.orphanedOrders}`)
|
|
console.log(` Conflicting: ${result.summary.conflictingOrders}`)
|
|
console.log(` ✅ Canceled: ${result.summary.totalCanceled}`)
|
|
console.log(` ❌ Failed: ${result.summary.totalFailed}`)
|
|
|
|
if (result.canceledOrders?.length > 0) {
|
|
console.log('\nCanceled Orders:')
|
|
result.canceledOrders.forEach(order => {
|
|
if (order.success) {
|
|
console.log(` ✅ ${order.symbol} order ${order.orderId} (${order.reason})`)
|
|
} else {
|
|
console.log(` ❌ Order ${order.orderId}: ${order.error}`)
|
|
}
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Cleanup failed:', error.message)
|
|
}
|
|
},
|
|
|
|
help: () => {
|
|
console.log('🧹 Drift Order Cleanup Commands')
|
|
console.log('===============================')
|
|
console.log('')
|
|
console.log('Commands:')
|
|
console.log(' status - Show service status and current positions/orders')
|
|
console.log(' start - Start automated cleanup monitoring')
|
|
console.log(' stop - Stop automated cleanup monitoring')
|
|
console.log(' cleanup - Run manual cleanup now')
|
|
console.log(' help - Show this help')
|
|
console.log('')
|
|
console.log('Examples:')
|
|
console.log(' node drift-cleanup-manager.js status')
|
|
console.log(' node drift-cleanup-manager.js start')
|
|
console.log(' node drift-cleanup-manager.js cleanup')
|
|
console.log('')
|
|
console.log('What it does:')
|
|
console.log('• Detects orphaned orders (orders for markets with no position)')
|
|
console.log('• Finds conflicting reduce-only orders')
|
|
console.log('• Automatically cancels problematic orders')
|
|
console.log('• Prevents manual order management after SL/TP hits')
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const command = process.argv[2] || 'help'
|
|
|
|
if (commands[command]) {
|
|
await commands[command]()
|
|
} else {
|
|
console.log(`❌ Unknown command: ${command}`)
|
|
console.log('Run "node drift-cleanup-manager.js help" for available commands')
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch(console.error)
|
|
}
|
|
|
|
module.exports = { commands }
|