/** * Position Consolidator - Clean Order Management * Consolidates multiple fragmented orders into a simple 3-order structure */ class PositionConsolidator { /** * Analyze current position and create consolidation plan */ static async analyzeAndConsolidate(analysis = null) { console.log('๐Ÿงน ANALYZING POSITION FOR CONSOLIDATION'); console.log('='.repeat(50)); // Get current position const position = await this.getCurrentPosition(); if (!position) { throw new Error('No active position found'); } const symbol = position.symbol.replace('-PERP', ''); console.log(`๐Ÿ“Š Position: ${position.side.toUpperCase()} ${position.size} ${symbol}`); console.log(`๐Ÿ’ฐ Entry: $${position.entryPrice.toFixed(4)}`); console.log(`๐Ÿ“ˆ Current: $${position.currentPrice?.toFixed(4) || 'N/A'}`); console.log(`๐Ÿ’ธ P&L: ${position.pnl ? '$' + position.pnl.toFixed(2) : 'N/A'}`); // Calculate optimal consolidated levels (with AI analysis if available) const consolidatedPlan = this.calculateConsolidatedLevels(position, analysis); console.log('\n๐Ÿ“Š CONSOLIDATED ORDER PLAN:'); console.log('='.repeat(30)); console.log(`Stop Loss: $${consolidatedPlan.stopLoss.toFixed(4)} (${consolidatedPlan.stopLossPercent.toFixed(1)}% risk)`); console.log(`Take Profit 1: $${consolidatedPlan.takeProfit1.toFixed(4)} (${consolidatedPlan.tp1Percent.toFixed(1)}% gain) - ${consolidatedPlan.tp1Size} ${symbol}`); console.log(`Take Profit 2: $${consolidatedPlan.takeProfit2.toFixed(4)} (${consolidatedPlan.tp2Percent.toFixed(1)}% gain) - ${consolidatedPlan.tp2Size} ${symbol}`); console.log(`Risk/Reward: ${consolidatedPlan.riskReward.toFixed(1)}:1`); return consolidatedPlan; } /** * Calculate optimal consolidated order levels * Priority: AI-calculated levels > Dynamic adaptive levels */ static calculateConsolidatedLevels(position, analysis = null) { const { side, size, entryPrice } = position; let stopLoss, takeProfit1, takeProfit2; let stopLossPercent, takeProfit1Percent, takeProfit2Percent; let usingAILevels = false; // ๐Ÿง  PRIORITY 1: Extract AI-calculated optimal levels if (analysis) { console.log('๐Ÿง  Checking AI analysis for optimal levels...'); // Extract AI stop loss let aiStopLoss = null; if (analysis.stopLoss?.price) { aiStopLoss = analysis.stopLoss.price; } else if (analysis.stopLoss && typeof analysis.stopLoss === 'number') { aiStopLoss = analysis.stopLoss; } else if (analysis.levels?.stopLoss) { aiStopLoss = analysis.levels.stopLoss; } // Extract AI take profits let aiTakeProfit1 = null, aiTakeProfit2 = null; if (analysis.takeProfits?.tp1?.price) { aiTakeProfit1 = analysis.takeProfits.tp1.price; aiTakeProfit2 = analysis.takeProfits?.tp2?.price || null; } else if (analysis.takeProfit && typeof analysis.takeProfit === 'number') { aiTakeProfit1 = analysis.takeProfit; } else if (analysis.levels?.takeProfit) { aiTakeProfit1 = analysis.levels.takeProfit; } // Use AI levels if available if (aiStopLoss && aiTakeProfit1) { console.log('โœ… Using AI-calculated optimal levels'); stopLoss = aiStopLoss; takeProfit1 = aiTakeProfit1; // Calculate percentages from AI prices if (side.toLowerCase() === 'long') { stopLossPercent = ((entryPrice - stopLoss) / entryPrice) * 100; takeProfit1Percent = ((takeProfit1 - entryPrice) / entryPrice) * 100; } else { stopLossPercent = ((stopLoss - entryPrice) / entryPrice) * 100; takeProfit1Percent = ((entryPrice - takeProfit1) / entryPrice) * 100; } // If no second TP from AI, calculate based on risk/reward extension if (aiTakeProfit2) { takeProfit2 = aiTakeProfit2; if (side.toLowerCase() === 'long') { takeProfit2Percent = ((takeProfit2 - entryPrice) / entryPrice) * 100; } else { takeProfit2Percent = ((entryPrice - takeProfit2) / entryPrice) * 100; } } else { // Extend first TP by additional 60% distance for aggressive second target const tpDistance = Math.abs(takeProfit1 - entryPrice); takeProfit2 = side.toLowerCase() === 'long' ? takeProfit1 + (tpDistance * 0.6) : takeProfit1 - (tpDistance * 0.6); takeProfit2Percent = takeProfit1Percent * 1.6; // 60% more than first TP } usingAILevels = true; } } // ๐ŸŽฏ FALLBACK: Dynamic adaptive levels when AI levels unavailable if (!usingAILevels) { console.log('๐Ÿ“Š Using dynamic adaptive levels (AI levels not available)...'); // Adaptive percentages based on position size and performance const baseStopLossPercent = 2.0; // Base 2% stop loss const baseTp1Percent = 3.5; // Base 3.5% first take profit const baseTp2Percent = 6.0; // Base 6% second take profit // Adjust based on position size (tighter for larger positions) const positionValue = size * entryPrice; const sizeMultiplier = Math.min(positionValue / 2000, 1.5); // Cap at 1.5x stopLossPercent = baseStopLossPercent / sizeMultiplier; takeProfit1Percent = baseTp1Percent * Math.min(sizeMultiplier, 1.2); takeProfit2Percent = baseTp2Percent * Math.min(sizeMultiplier, 1.2); if (side.toLowerCase() === 'long') { stopLoss = entryPrice * (1 - stopLossPercent / 100); takeProfit1 = entryPrice * (1 + takeProfit1Percent / 100); takeProfit2 = entryPrice * (1 + takeProfit2Percent / 100); } else { stopLoss = entryPrice * (1 + stopLossPercent / 100); takeProfit1 = entryPrice * (1 - takeProfit1Percent / 100); takeProfit2 = entryPrice * (1 - takeProfit2Percent / 100); } } // Position sizing: 70% at TP1, 30% at TP2 const tp1Size = Math.floor(size * 0.7 * 100) / 100; // 70% of position const tp2Size = size - tp1Size; // Remaining 30% const riskReward = takeProfit1Percent / stopLossPercent; return { stopLoss, takeProfit1, takeProfit2, stopLossPercent, tp1Percent: takeProfit1Percent, tp2Percent: takeProfit2Percent, tp1Size, tp2Size, riskReward, totalOrders: 3 // Much cleaner than 24! }; } /** * Execute consolidated order placement */ static async executeConsolidation(analysis = null) { try { console.log('\n๐ŸŽฏ EXECUTING CONSOLIDATED ORDERS'); console.log('='.repeat(40)); // Get current position first const position = await this.getCurrentPosition(); if (!position) { throw new Error('No active position found for consolidation'); } // Calculate consolidation plan with AI analysis if provided const consolidatedPlan = this.calculateConsolidatedLevels(position, analysis); const results = []; // 1. Cancel all existing orders first console.log('1๏ธโƒฃ Canceling all existing orders...'); const cancelResult = await this.cancelAllOrders(); console.log(` โœ… Canceled ${cancelResult.totalCanceled} orders`); // 2. Place consolidated stop loss console.log('2๏ธโƒฃ Placing consolidated stop loss...'); const stopLossResult = await this.placeConsolidatedOrder({ type: 'STOP_LOSS', symbol: position.symbol, side: position.side === 'long' ? 'SHORT' : 'LONG', size: position.size, triggerPrice: consolidatedPlan.stopLoss, price: consolidatedPlan.stopLoss }); results.push(stopLossResult); // 3. Place first take profit (70% of position) console.log('3๏ธโƒฃ Placing primary take profit...'); const tp1Result = await this.placeConsolidatedOrder({ type: 'TAKE_PROFIT_1', symbol: position.symbol, side: position.side === 'long' ? 'SHORT' : 'LONG', size: consolidatedPlan.tp1Size, triggerPrice: consolidatedPlan.takeProfit1, price: consolidatedPlan.takeProfit1 }); results.push(tp1Result); // 4. Place second take profit (30% of position) console.log('4๏ธโƒฃ Placing extended take profit...'); const tp2Result = await this.placeConsolidatedOrder({ type: 'TAKE_PROFIT_2', symbol: position.symbol, side: position.side === 'long' ? 'SHORT' : 'LONG', size: consolidatedPlan.tp2Size, triggerPrice: consolidatedPlan.takeProfit2, price: consolidatedPlan.takeProfit2 }); results.push(tp2Result); console.log('\nโœ… CONSOLIDATION COMPLETE!'); console.log(`๐Ÿ“Š Orders reduced from 24 โ†’ 3`); console.log(`๐ŸŽฏ Clean risk management structure`); console.log(`๐Ÿ’ฐ Optimized profit taking strategy`); return { success: true, message: 'Position successfully consolidated', ordersBefore: 24, ordersAfter: 3, results: results }; } catch (error) { console.error('โŒ Consolidation failed:', error.message); return { success: false, error: error.message }; } } /** * Cancel all existing orders */ static async cancelAllOrders() { try { const response = await fetch('http://localhost:3000/api/drift/cancel-all-orders', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); return result; } catch (error) { console.error('Error canceling orders:', error); return { success: false, totalCanceled: 0 }; } } /** * Place a consolidated order via Drift API */ static async placeConsolidatedOrder(orderParams) { try { const response = await fetch('http://localhost:3000/api/drift/place-order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: orderParams.symbol, orderType: 'TRIGGER_LIMIT', direction: orderParams.side, size: orderParams.size.toString(), price: orderParams.price.toString(), triggerPrice: orderParams.triggerPrice.toString(), reduceOnly: true }) }); const result = await response.json(); if (result.success) { console.log(` โœ… ${orderParams.type}: ${orderParams.size} @ $${orderParams.triggerPrice.toFixed(4)}`); } else { console.log(` โŒ ${orderParams.type} failed: ${result.error}`); } return result; } catch (error) { console.error(`Error placing ${orderParams.type}:`, error); return { success: false, error: error.message }; } } /** * Get current position from Drift API */ static async getCurrentPosition() { try { const response = await fetch('http://localhost:3000/api/drift/positions'); const result = await response.json(); if (result.success && result.positions.length > 0) { const position = result.positions[0]; return { symbol: position.symbol, side: position.side, size: position.size, entryPrice: position.entryPrice, currentPrice: position.markPrice, pnl: position.unrealizedPnl }; } else { return null; } } catch (error) { console.error('Error fetching position:', error); return null; } } } module.exports = PositionConsolidator;