/** * AI-Driven DCA (Dollar Cost Averaging) Manager * * Intelligently adds to positions when market shows reversal potential * Manages risk by respecting leverage limits and adjusting stop loss/take profit */ import AILeverageCalculator from './ai-leverage-calculator' interface DCAAnalysisParams { currentPosition: { side: 'long' | 'short' size: number entryPrice: number currentPrice: number unrealizedPnl: number stopLoss: number takeProfit: number } accountStatus: { accountValue: number availableBalance: number leverage: number liquidationPrice: number } marketData: { price: number priceChange24h: number volume: number rsi?: number support?: number resistance?: number } maxLeverageAllowed: number } interface DCAResult { shouldDCA: boolean dcaAmount: number newAveragePrice: number newStopLoss: number newTakeProfit: number newLeverage: number newLiquidationPrice: number riskAssessment: 'LOW' | 'MEDIUM' | 'HIGH' reasoning: string confidence: number } export class AIDCAManager { /** * Analyze if DCA opportunity exists and calculate optimal DCA parameters */ static analyzeDCAOpportunity(params: DCAAnalysisParams): DCAResult { const { currentPosition, accountStatus, marketData, maxLeverageAllowed } = params console.log('🔄 AI DCA Analysis:', { position: `${currentPosition.side} ${currentPosition.size} @ $${currentPosition.entryPrice}`, currentPrice: `$${marketData.price}`, pnl: `$${currentPosition.unrealizedPnl.toFixed(2)}`, availableBalance: `$${accountStatus.availableBalance.toFixed(2)}` }) // Step 1: Analyze reversal potential const reversalAnalysis = this.analyzeReversalPotential(currentPosition, marketData) if (!reversalAnalysis.hasReversalPotential) { return { shouldDCA: false, dcaAmount: 0, newAveragePrice: currentPosition.entryPrice, newStopLoss: currentPosition.stopLoss, newTakeProfit: currentPosition.takeProfit, newLeverage: accountStatus.leverage, newLiquidationPrice: accountStatus.liquidationPrice, riskAssessment: 'HIGH', reasoning: reversalAnalysis.reasoning, confidence: 0 } } // Step 2: Calculate safe DCA amount const dcaCalculation = this.calculateSafeDCAAmount( currentPosition, accountStatus, marketData, maxLeverageAllowed ) if (dcaCalculation.dcaAmount === 0) { return { shouldDCA: false, dcaAmount: 0, newAveragePrice: currentPosition.entryPrice, newStopLoss: currentPosition.stopLoss, newTakeProfit: currentPosition.takeProfit, newLeverage: accountStatus.leverage, newLiquidationPrice: accountStatus.liquidationPrice, riskAssessment: 'HIGH', reasoning: dcaCalculation.reasoning, confidence: 0 } } // Step 3: Calculate new position parameters const newPositionSize = currentPosition.size + dcaCalculation.dcaAmount const newAveragePrice = this.calculateNewAveragePrice( currentPosition.size, currentPosition.entryPrice, dcaCalculation.dcaAmount, marketData.price ) // Step 4: Calculate new stop loss and take profit const newSLTP = this.calculateNewStopLossAndTakeProfit( currentPosition.side, newAveragePrice, newPositionSize, marketData, reversalAnalysis.confidence ) // Step 5: Calculate new leverage and liquidation price const newLeverage = this.calculateNewLeverage( newPositionSize, newAveragePrice, accountStatus.accountValue, dcaCalculation.dcaAmount ) const newLiquidationPrice = this.calculateNewLiquidationPrice( newAveragePrice, newLeverage, currentPosition.side ) // Step 6: Final risk assessment const riskAssessment = this.assessDCARisk( newLeverage, newLiquidationPrice, newSLTP.stopLoss, accountStatus.availableBalance, dcaCalculation.dcaAmount ) const result: DCAResult = { shouldDCA: true, dcaAmount: dcaCalculation.dcaAmount, newAveragePrice, newStopLoss: newSLTP.stopLoss, newTakeProfit: newSLTP.takeProfit, newLeverage, newLiquidationPrice, riskAssessment, reasoning: this.generateDCAReasoning(reversalAnalysis, dcaCalculation, newAveragePrice, newLeverage), confidence: reversalAnalysis.confidence } console.log('🚀 DCA Recommendation:', { shouldDCA: result.shouldDCA, dcaAmount: `$${result.dcaAmount.toFixed(2)}`, newAverage: `$${result.newAveragePrice.toFixed(4)}`, newSL: `$${result.newStopLoss.toFixed(4)}`, newTP: `$${result.newTakeProfit.toFixed(4)}`, newLeverage: `${result.newLeverage.toFixed(1)}x`, riskLevel: result.riskAssessment, confidence: `${result.confidence}%` }) return result } /** * Analyze market conditions for reversal potential */ private static analyzeReversalPotential(position: any, marketData: any): { hasReversalPotential: boolean reasoning: string confidence: number } { const priceMovement = ((marketData.price - position.entryPrice) / position.entryPrice) * 100 const isMovingAgainstPosition = (position.side === 'long' && priceMovement < 0) || (position.side === 'short' && priceMovement > 0) // Only consider DCA if price moved against us (creating discount opportunity) if (!isMovingAgainstPosition) { return { hasReversalPotential: false, reasoning: "Price moving in our favor - no DCA needed", confidence: 0 } } let confidence = 0 const reasons = [] // Factor 1: Price drop magnitude (for longs) or rise magnitude (for shorts) const movementMagnitude = Math.abs(priceMovement) if (movementMagnitude >= 1 && movementMagnitude <= 5) { confidence += 30 reasons.push(`${movementMagnitude.toFixed(1)}% movement creates DCA opportunity`) } else if (movementMagnitude > 5 && movementMagnitude <= 10) { confidence += 40 reasons.push(`${movementMagnitude.toFixed(1)}% movement shows strong discount`) } else if (movementMagnitude > 10) { confidence += 20 reasons.push(`${movementMagnitude.toFixed(1)}% movement may indicate trend change`) } // Factor 2: 24h price change context if (marketData.priceChange24h !== undefined) { if (position.side === 'long' && marketData.priceChange24h < -3) { confidence += 25 reasons.push("24h downtrend creates long DCA opportunity") } else if (position.side === 'short' && marketData.priceChange24h > 3) { confidence += 25 reasons.push("24h uptrend creates short DCA opportunity") } } // Factor 3: Support/Resistance levels if (marketData.support && position.side === 'long' && marketData.price <= marketData.support * 1.02) { confidence += 20 reasons.push("Price near support level") } if (marketData.resistance && position.side === 'short' && marketData.price >= marketData.resistance * 0.98) { confidence += 20 reasons.push("Price near resistance level") } // Factor 4: RSI oversold/overbought if (marketData.rsi !== undefined) { if (position.side === 'long' && marketData.rsi < 35) { confidence += 15 reasons.push("RSI oversold - reversal likely") } else if (position.side === 'short' && marketData.rsi > 65) { confidence += 15 reasons.push("RSI overbought - reversal likely") } } // Minimum confidence threshold for DCA const hasReversalPotential = confidence >= 50 return { hasReversalPotential, reasoning: hasReversalPotential ? reasons.join(", ") : `Insufficient reversal signals (${confidence}% confidence)`, confidence } } /** * Calculate safe DCA amount respecting leverage and liquidation limits */ private static calculateSafeDCAAmount( position: any, accountStatus: any, marketData: any, maxLeverageAllowed: number ): { dcaAmount: number, reasoning: string } { // Use AI Leverage Calculator to determine safe additional position size const currentStopLossPrice = position.side === 'long' ? position.entryPrice * (1 - position.stopLoss / 100) : position.entryPrice * (1 + position.stopLoss / 100) const leverageResult = AILeverageCalculator.calculateOptimalLeverage({ accountValue: accountStatus.accountValue, availableBalance: accountStatus.availableBalance, entryPrice: marketData.price, // Current price for DCA entry stopLossPrice: currentStopLossPrice, // Keep same SL initially side: position.side, maxLeverageAllowed, safetyBuffer: 0.15 // More conservative for DCA }) // Calculate maximum safe DCA amount const maxDCAUSD = accountStatus.availableBalance * 0.5 // Use up to 50% of available balance const leveragedDCAAmount = maxDCAUSD * leverageResult.recommendedLeverage const dcaTokenAmount = leveragedDCAAmount / marketData.price // Ensure DCA doesn't make position too large relative to account const currentPositionValue = position.size * position.entryPrice const maxPositionValue = accountStatus.accountValue * 3 // Max 3x account value const remainingCapacity = maxPositionValue - currentPositionValue const finalDCAAmount = Math.min( dcaTokenAmount, remainingCapacity / marketData.price, position.size * 0.5 // Max 50% of current position size ) if (finalDCAAmount < 0.01) { return { dcaAmount: 0, reasoning: "Insufficient available balance or position capacity for safe DCA" } } return { dcaAmount: finalDCAAmount, reasoning: `Safe DCA: ${finalDCAAmount.toFixed(4)} tokens using ${leverageResult.recommendedLeverage.toFixed(1)}x leverage` } } /** * Calculate new average price after DCA */ private static calculateNewAveragePrice( currentSize: number, currentPrice: number, dcaSize: number, dcaPrice: number ): number { const totalValue = (currentSize * currentPrice) + (dcaSize * dcaPrice) const totalSize = currentSize + dcaSize return totalValue / totalSize } /** * Calculate new stop loss and take profit for the averaged position */ private static calculateNewStopLossAndTakeProfit( side: 'long' | 'short', newAveragePrice: number, newPositionSize: number, marketData: any, confidence: number ): { stopLoss: number, takeProfit: number } { // Adjust SL/TP based on confidence and position size const baseStopLossPercent = 3 // Base 3% stop loss const baseTakeProfitPercent = confidence > 70 ? 8 : 6 // Higher TP if very confident // Make SL tighter for larger positions const positionSizeMultiplier = Math.min(newPositionSize / 5, 1.5) // Cap at 1.5x const adjustedSLPercent = baseStopLossPercent / positionSizeMultiplier let stopLoss: number let takeProfit: number if (side === 'long') { stopLoss = newAveragePrice * (1 - adjustedSLPercent / 100) takeProfit = newAveragePrice * (1 + baseTakeProfitPercent / 100) } else { stopLoss = newAveragePrice * (1 + adjustedSLPercent / 100) takeProfit = newAveragePrice * (1 - baseTakeProfitPercent / 100) } return { stopLoss, takeProfit } } /** * Calculate new effective leverage after DCA */ private static calculateNewLeverage( newPositionSize: number, newAveragePrice: number, accountValue: number, dcaAmount: number ): number { const totalPositionValue = newPositionSize * newAveragePrice return totalPositionValue / accountValue } /** * Calculate new liquidation price */ private static calculateNewLiquidationPrice( averagePrice: number, leverage: number, side: 'long' | 'short' ): number { if (side === 'long') { return averagePrice * (1 - 1/leverage) } else { return averagePrice * (1 + 1/leverage) } } /** * Assess risk of DCA operation */ private static assessDCARisk( newLeverage: number, liquidationPrice: number, stopLoss: number, availableBalance: number, dcaAmount: number ): 'LOW' | 'MEDIUM' | 'HIGH' { const liquidationBuffer = Math.abs(liquidationPrice - stopLoss) / stopLoss * 100 const balanceUsagePercent = (dcaAmount * liquidationPrice) / availableBalance * 100 if (newLeverage <= 3 && liquidationBuffer >= 15 && balanceUsagePercent <= 30) return 'LOW' if (newLeverage <= 6 && liquidationBuffer >= 10 && balanceUsagePercent <= 50) return 'MEDIUM' return 'HIGH' } /** * Generate reasoning for DCA decision */ private static generateDCAReasoning( reversalAnalysis: any, dcaCalculation: any, newAveragePrice: number, newLeverage: number ): string { return `${reversalAnalysis.reasoning}. ${dcaCalculation.reasoning}. New average: $${newAveragePrice.toFixed(4)} with ${newLeverage.toFixed(1)}x leverage.` } } export default AIDCAManager