#!/usr/bin/env node /** * Test Stop Loss / Take Profit Preservation Fix * Verifies that SL/TP orders are not being incorrectly canceled as orphaned orders */ console.log('๐Ÿงช Testing Stop Loss / Take Profit Preservation Fix...'); async function testSLTPPreservation() { try { console.log('\n1๏ธโƒฃ Testing Cleanup Logic...'); // Simulate position data const mockPositions = [ { marketIndex: 0, // SOL-PERP symbol: 'SOL-PERP', side: 'long', size: 1.5, entryPrice: 165.50 } ]; // Simulate orders including SL/TP const mockOrders = [ { orderId: 1, marketIndex: 0, // Same market as position direction: 1, // SHORT (opposite to position - correct for SL) reduceOnly: true, // This is a Stop Loss order baseAssetAmount: { isZero: () => false }, status: { open: true } }, { orderId: 2, marketIndex: 0, // Same market as position direction: 1, // SHORT (opposite to position - correct for TP) reduceOnly: true, // This is a Take Profit order baseAssetAmount: { isZero: () => false }, status: { open: true } }, { orderId: 3, marketIndex: 1, // Different market - truly orphaned direction: 0, // LONG reduceOnly: false, // Regular order baseAssetAmount: { isZero: () => false }, status: { open: true } } ]; // Test the fixed logic const positionMarkets = new Set(mockPositions.map(pos => pos.marketIndex)); console.log('๐Ÿ“Š Position markets:', Array.from(positionMarkets)); // OLD BROKEN LOGIC (what was causing the bug): const oldOrphanedOrders = mockOrders.filter(order => !positionMarkets.has(order.marketIndex) || (order.reduceOnly && !positionMarkets.has(order.marketIndex)) ); // NEW FIXED LOGIC: const newOrphanedOrders = mockOrders.filter(order => !positionMarkets.has(order.marketIndex) && !order.reduceOnly ); console.log(`โŒ OLD LOGIC would cancel: ${oldOrphanedOrders.length} orders`); oldOrphanedOrders.forEach(order => { const type = order.reduceOnly ? 'SL/TP' : 'Regular'; console.log(` - Order ${order.orderId} (${type}) on market ${order.marketIndex}`); }); console.log(`โœ… NEW LOGIC will cancel: ${newOrphanedOrders.length} orders`); newOrphanedOrders.forEach(order => { const type = order.reduceOnly ? 'SL/TP' : 'Regular'; console.log(` - Order ${order.orderId} (${type}) on market ${order.marketIndex}`); }); // Verify the fix const slTpOrdersPreserved = mockOrders .filter(order => order.reduceOnly && positionMarkets.has(order.marketIndex)) .filter(order => !newOrphanedOrders.includes(order)); console.log(`๐Ÿ›ก๏ธ SL/TP orders preserved: ${slTpOrdersPreserved.length}`); slTpOrdersPreserved.forEach(order => { console.log(` โœ… Order ${order.orderId} (SL/TP) preserved for market ${order.marketIndex}`); }); console.log('\n2๏ธโƒฃ Testing API Endpoints...'); // Test cleanup API with mock data console.log('๐Ÿ“ก Testing cleanup-orders API...'); const baseUrl = 'http://localhost:9001'; // Test positions endpoint try { const positionsResponse = await fetch(`${baseUrl}/api/drift/positions`); const positionsData = await positionsResponse.json(); console.log(`โœ… Positions API: ${positionsData.success ? 'Working' : 'Failed'}`); if (positionsData.success) { console.log(` ๐Ÿ“Š Found ${positionsData.positions?.length || 0} positions`); } } catch (error) { console.log(`โŒ Positions API error: ${error.message}`); } // Test orders endpoint try { const ordersResponse = await fetch(`${baseUrl}/api/drift/orders`); const ordersData = await ordersResponse.json(); console.log(`โœ… Orders API: ${ordersData.success ? 'Working' : 'Failed'}`); if (ordersData.success) { console.log(` ๐Ÿ“‹ Found ${ordersData.orders?.length || 0} orders`); // Analyze order types const orders = ordersData.orders || []; const reduceOnlyOrders = orders.filter(order => order.reduceOnly); const regularOrders = orders.filter(order => !order.reduceOnly); console.log(` ๐Ÿ›ก๏ธ Reduce-only orders (SL/TP): ${reduceOnlyOrders.length}`); console.log(` ๐Ÿ“ˆ Regular orders: ${regularOrders.length}`); } } catch (error) { console.log(`โŒ Orders API error: ${error.message}`); } console.log('\n3๏ธโƒฃ Testing Position Monitor...'); try { const monitorResponse = await fetch(`${baseUrl}/api/automation/position-monitor`); const monitorData = await monitorResponse.json(); if (monitorData.success) { const monitor = monitorData.monitor; console.log(`โœ… Position Monitor: Working`); console.log(` ๐Ÿ“Š Has Position: ${monitor.hasPosition}`); console.log(` ๐Ÿงน Cleanup Triggered: ${monitor.orphanedOrderCleanup?.triggered || false}`); console.log(` ๐Ÿ“ Message: ${monitor.orphanedOrderCleanup?.message || 'N/A'}`); } else { console.log(`โŒ Position Monitor failed: ${monitorData.error}`); } } catch (error) { console.log(`โŒ Position Monitor error: ${error.message}`); } console.log('\nโœ… Testing Complete!'); console.log('\n๐Ÿ“‹ Summary of Fixes:'); console.log(' ๐Ÿ›ก๏ธ Stop Loss and Take Profit orders are now preserved'); console.log(' ๐Ÿงน Only truly orphaned orders (non-reduce-only) will be cleaned up'); console.log(' ๐Ÿ“Š Position monitor is more conservative about cleanup'); console.log(' โš ๏ธ Background cleanup services should be stopped to prevent interference'); } catch (error) { console.error('โŒ Test failed:', error); } } // Run the test testSLTPPreservation();