From 5336ab5d98bee92d5f63d2d0fb610d11151fa566 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Thu, 24 Jul 2025 12:23:47 +0200 Subject: [PATCH] feat: implement AI-driven dynamic leverage calculator - Under k: Use 100% of available balance for maximum growth - Over k: Use 50% balance for controlled risk management - AI calculates optimal leverage maintaining safe liquidation distance - Liquidation price stays safely below stop loss (10% buffer) New Features: - AILeverageCalculator class with sophisticated risk assessment - Dynamic position sizing based on account value and market conditions - Liquidation price calculation and safety validation - Risk assessment levels (LOW/MEDIUM/HIGH) with reasoning - Support for both long and short positions with AI leverage - Enhanced automation-service-simple.ts with AI leverage - Position sizing now returns leverage + risk metrics - Trade execution uses AI-calculated leverage values - Database storage includes AI leverage metadata - Comprehensive logging for leverage decisions - Safety buffer prevents liquidation near stop loss - Maximum leverage limited by platform constraints (20x) - Account-based strategy (aggressive k) - Real-time balance and position validation This enables maximum profit potential while maintaining strict risk controls. --- lib/ai-leverage-calculator.ts | 251 +++++++++++++++++++++++++++++++ lib/automation-service-simple.ts | 208 +++++++++++++++++++++---- 2 files changed, 433 insertions(+), 26 deletions(-) create mode 100644 lib/ai-leverage-calculator.ts diff --git a/lib/ai-leverage-calculator.ts b/lib/ai-leverage-calculator.ts new file mode 100644 index 0000000..f8dfa53 --- /dev/null +++ b/lib/ai-leverage-calculator.ts @@ -0,0 +1,251 @@ +/** + * AI-Driven Dynamic Leverage Calculator + * + * Calculates optimal leverage to maximize profits while maintaining safe liquidation distance + * Strategy: Use maximum leverage possible without liquidation price being too close to stop loss + */ + +interface LeverageCalculationParams { + accountValue: number + availableBalance: number + entryPrice: number + stopLossPrice: number + side: 'long' | 'short' + maxLeverageAllowed: number // Platform maximum (e.g., 20x for Drift) + safetyBuffer: number // Distance between liquidation and SL (default: 10%) +} + +interface LeverageResult { + recommendedLeverage: number + positionSize: number + liquidationPrice: number + marginRequired: number + maxPossibleLeverage: number + riskAssessment: 'LOW' | 'MEDIUM' | 'HIGH' + reasoning: string +} + +export class AILeverageCalculator { + + /** + * Calculate optimal leverage for maximum profit with safe liquidation distance + */ + static calculateOptimalLeverage(params: LeverageCalculationParams): LeverageResult { + const { + accountValue, + availableBalance, + entryPrice, + stopLossPrice, + side, + maxLeverageAllowed, + safetyBuffer = 0.10 // 10% safety buffer by default + } = params + + console.log('๐Ÿงฎ AI Leverage Calculation:', { + accountValue: accountValue.toFixed(2), + availableBalance: availableBalance.toFixed(2), + entryPrice, + stopLossPrice, + side, + maxLeverageAllowed + }) + + // Strategy 1: Under $1k - Use 100% of available balance + const useFullBalance = accountValue < 1000 + const baseAmount = useFullBalance ? availableBalance : availableBalance * 0.5 // 50% for accounts over $1k + + console.log(`๐Ÿ’ฐ Balance Strategy: ${useFullBalance ? 'AGGRESSIVE (100%)' : 'CONSERVATIVE (50%)'} - Using $${baseAmount.toFixed(2)}`) + + // Calculate stop loss distance as percentage + const stopLossDistance = Math.abs(entryPrice - stopLossPrice) / entryPrice + console.log(`๐Ÿ“Š Stop Loss Distance: ${(stopLossDistance * 100).toFixed(2)}%`) + + // Calculate maximum safe leverage based on liquidation distance + const maxSafeLeverage = this.calculateMaxSafeLeverage( + entryPrice, + stopLossPrice, + side, + safetyBuffer + ) + + // Determine final leverage (limited by platform max and safety calculations) + const finalLeverage = Math.min(maxSafeLeverage, maxLeverageAllowed) + + // Calculate position size and margin + const positionSize = baseAmount * finalLeverage + const marginRequired = positionSize / finalLeverage + + // Calculate liquidation price for verification + const liquidationPrice = this.calculateLiquidationPrice( + entryPrice, + finalLeverage, + side, + marginRequired, + positionSize + ) + + // Risk assessment + const riskAssessment = this.assessRisk(finalLeverage, stopLossDistance, useFullBalance) + + // Generate AI reasoning + const reasoning = this.generateLeverageReasoning( + finalLeverage, + maxSafeLeverage, + maxLeverageAllowed, + liquidationPrice, + stopLossPrice, + useFullBalance, + side + ) + + const result: LeverageResult = { + recommendedLeverage: finalLeverage, + positionSize, + liquidationPrice, + marginRequired, + maxPossibleLeverage: maxSafeLeverage, + riskAssessment, + reasoning + } + + console.log('๐ŸŽฏ AI Leverage Result:', { + recommendedLeverage: `${finalLeverage.toFixed(1)}x`, + positionSize: `$${positionSize.toFixed(2)}`, + liquidationPrice: `$${liquidationPrice.toFixed(4)}`, + riskAssessment, + reasoning + }) + + return result + } + + /** + * Calculate maximum safe leverage where liquidation price maintains safety buffer from stop loss + */ + private static calculateMaxSafeLeverage( + entryPrice: number, + stopLossPrice: number, + side: 'long' | 'short', + safetyBuffer: number + ): number { + + // Calculate where liquidation should be (beyond stop loss + safety buffer) + let maxLiquidationPrice: number + + if (side === 'long') { + // For longs: liquidation should be below stop loss + maxLiquidationPrice = stopLossPrice * (1 - safetyBuffer) + } else { + // For shorts: liquidation should be above stop loss + maxLiquidationPrice = stopLossPrice * (1 + safetyBuffer) + } + + console.log(`๐Ÿ›ก๏ธ Max Safe Liquidation Price: $${maxLiquidationPrice.toFixed(4)} (${side} position)`) + + // Calculate max leverage that keeps liquidation at safe distance + // Simplified liquidation formula: For longs: liq = entry * (1 - 1/leverage) + let maxLeverage: number + + if (side === 'long') { + // entry * (1 - 1/leverage) = maxLiquidationPrice + // 1 - 1/leverage = maxLiquidationPrice / entry + // 1/leverage = 1 - maxLiquidationPrice / entry + // leverage = 1 / (1 - maxLiquidationPrice / entry) + maxLeverage = 1 / (1 - maxLiquidationPrice / entryPrice) + } else { + // For shorts: liq = entry * (1 + 1/leverage) + // entry * (1 + 1/leverage) = maxLiquidationPrice + // 1 + 1/leverage = maxLiquidationPrice / entry + // 1/leverage = maxLiquidationPrice / entry - 1 + // leverage = 1 / (maxLiquidationPrice / entry - 1) + maxLeverage = 1 / (maxLiquidationPrice / entryPrice - 1) + } + + // Ensure reasonable bounds + maxLeverage = Math.max(1, Math.min(maxLeverage, 50)) // Between 1x and 50x + + console.log(`โš–๏ธ Max Safe Leverage: ${maxLeverage.toFixed(2)}x`) + + return maxLeverage + } + + /** + * Calculate liquidation price for given position parameters + */ + private static calculateLiquidationPrice( + entryPrice: number, + leverage: number, + side: 'long' | 'short', + marginRequired: number, + positionSize: number + ): number { + + // Simplified liquidation calculation + // In reality, this would need to account for fees, funding, etc. + + if (side === 'long') { + // Long liquidation: entry * (1 - 1/leverage) + return entryPrice * (1 - 1/leverage) + } else { + // Short liquidation: entry * (1 + 1/leverage) + return entryPrice * (1 + 1/leverage) + } + } + + /** + * Assess risk level based on leverage and position parameters + */ + private static assessRisk( + leverage: number, + stopLossDistance: number, + useFullBalance: boolean + ): 'LOW' | 'MEDIUM' | 'HIGH' { + + if (leverage <= 2 && stopLossDistance >= 0.05) return 'LOW' + if (leverage <= 5 && stopLossDistance >= 0.03) return 'MEDIUM' + if (leverage <= 10 && stopLossDistance >= 0.02) return 'MEDIUM' + + return 'HIGH' + } + + /** + * Generate AI reasoning for leverage decision + */ + private static generateLeverageReasoning( + finalLeverage: number, + maxSafeLeverage: number, + maxPlatformLeverage: number, + liquidationPrice: number, + stopLossPrice: number, + useFullBalance: boolean, + side: 'long' | 'short' + ): string { + + const reasons = [] + + // Balance strategy reasoning + if (useFullBalance) { + reasons.push("Account <$1k: Using 100% available balance for maximum growth") + } else { + reasons.push("Account >$1k: Using 50% balance for controlled risk") + } + + // Leverage limitation reasoning + if (finalLeverage === maxSafeLeverage) { + reasons.push(`Max safe leverage ${finalLeverage.toFixed(1)}x maintains liquidation safely beyond stop loss`) + } else if (finalLeverage === maxPlatformLeverage) { + reasons.push(`Platform limit ${maxPlatformLeverage}x reached (could use ${maxSafeLeverage.toFixed(1)}x safely)`) + } + + // Liquidation safety reasoning + const liquidationBuffer = side === 'long' + ? (stopLossPrice - liquidationPrice) / stopLossPrice * 100 + : (liquidationPrice - stopLossPrice) / stopLossPrice * 100 + + reasons.push(`Liquidation $${liquidationPrice.toFixed(4)} is ${liquidationBuffer.toFixed(1)}% beyond stop loss`) + + return reasons.join('. ') + } +} + +export default AILeverageCalculator diff --git a/lib/automation-service-simple.ts b/lib/automation-service-simple.ts index f754fce..8335384 100644 --- a/lib/automation-service-simple.ts +++ b/lib/automation-service-simple.ts @@ -7,7 +7,8 @@ import aggressiveCleanup from './aggressive-cleanup' import { analysisCompletionFlag } from './analysis-completion-flag' import priceMonitorService from './price-monitor' -const prisma = new PrismaClient() +import prisma from '../lib/prisma' +import AILeverageCalculator from './ai-leverage-calculator' export interface AutomationConfig { userId: string @@ -634,13 +635,17 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. console.log('๐Ÿ“ˆ BUY signal detected') } - // Calculate position size based on risk percentage - const positionSize = await this.calculatePositionSize(analysis) + // Calculate AI-driven position size with optimal leverage + const positionResult = await this.calculatePositionSize(analysis) return { direction: analysis.recommendation, confidence: analysis.confidence, - positionSize, + positionSize: positionResult.tokenAmount, + leverageUsed: positionResult.leverageUsed, + marginRequired: positionResult.marginRequired, + liquidationPrice: positionResult.liquidationPrice, + riskAssessment: positionResult.riskAssessment, stopLoss: this.calculateStopLoss(analysis), takeProfit: this.calculateTakeProfit(analysis), marketSentiment: analysis.marketSentiment, @@ -687,39 +692,92 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. } } - private async calculatePositionSize(analysis: any): Promise { - const baseAmount = this.config!.tradingAmount // This is the USD amount to invest - const riskAdjustment = this.config!.riskPercentage / 100 - const confidenceAdjustment = analysis.confidence / 100 + private async calculatePositionSize(analysis: any): Promise<{ + tokenAmount: number + leverageUsed: number + marginRequired: number + liquidationPrice: number + riskAssessment: string + }> { + console.log('๐Ÿง  AI Position Sizing with Dynamic Leverage Calculation...') - // โœ… ENHANCED: Handle both BUY and SELL position sizing + // โœ… ENHANCED: Handle SELL positions with AI leverage for shorting if (analysis.recommendation === 'SELL') { - // For SELL orders, calculate how much SOL to sell based on current holdings - return await this.calculateSellAmount(analysis) + return await this.calculateSellPositionWithLeverage(analysis) } - // For BUY orders, calculate USD amount to invest - const usdAmount = baseAmount * riskAdjustment * confidenceAdjustment + // Get account balance + const balanceResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'}/api/drift/balance`) + const balanceData = await balanceResponse.json() - // Get current price to convert USD to token amount + if (!balanceData.success) { + throw new Error('Could not fetch account balance for position sizing') + } + + const accountValue = balanceData.accountValue || balanceData.totalCollateral + const availableBalance = balanceData.availableBalance + + console.log(`๐Ÿ’ฐ Account Status: Value=$${accountValue.toFixed(2)}, Available=$${availableBalance.toFixed(2)}`) + + // Get current price for entry let currentPrice = analysis.entry?.price || analysis.currentPrice if (!currentPrice) { try { const { default: PriceFetcher } = await import('./price-fetcher') currentPrice = await PriceFetcher.getCurrentPrice(this.config?.symbol || 'SOLUSD') - console.log(`๐Ÿ“Š Using current ${this.config?.symbol || 'SOLUSD'} price for position size: $${currentPrice}`) + console.log(`๐Ÿ“Š Using current ${this.config?.symbol || 'SOLUSD'} price: $${currentPrice}`) } catch (error) { - console.error('Error fetching price for position size, using fallback:', error) + console.error('Error fetching price for position sizing, using fallback:', error) currentPrice = this.config?.symbol === 'SOLUSD' ? 189 : 100 } } + + // Calculate stop loss price from analysis + const stopLossPercent = this.calculateAIStopLoss(analysis) / 100 + const direction = analysis.recommendation === 'BUY' ? 'long' : 'short' - // Calculate token amount: USD investment / token price - const tokenAmount = usdAmount / currentPrice - console.log(`๐Ÿ’ฐ BUY Position calculation: $${usdAmount} รท $${currentPrice} = ${tokenAmount.toFixed(4)} tokens`) - - return tokenAmount + let stopLossPrice: number + if (direction === 'long') { + stopLossPrice = currentPrice * (1 - stopLossPercent) + } else { + stopLossPrice = currentPrice * (1 + stopLossPercent) + } + + console.log(`๐ŸŽฏ Position Parameters: Entry=$${currentPrice}, StopLoss=$${stopLossPrice.toFixed(4)}, Direction=${direction}`) + + // Use AI Leverage Calculator for optimal leverage + const leverageResult = AILeverageCalculator.calculateOptimalLeverage({ + accountValue, + availableBalance, + entryPrice: currentPrice, + stopLossPrice, + side: direction, + maxLeverageAllowed: this.config!.maxLeverage || 20, // Platform max leverage + safetyBuffer: 0.10 // 10% safety buffer between liquidation and stop loss + }) + + // Calculate final position size + const baseAmount = accountValue < 1000 ? availableBalance : availableBalance * 0.5 + const leveragedAmount = baseAmount * leverageResult.recommendedLeverage + const tokenAmount = leveragedAmount / currentPrice + + console.log(`๏ฟฝ AI Position Result:`, { + baseAmount: `$${baseAmount.toFixed(2)}`, + leverage: `${leverageResult.recommendedLeverage.toFixed(1)}x`, + leveragedAmount: `$${leveragedAmount.toFixed(2)}`, + tokenAmount: tokenAmount.toFixed(4), + riskLevel: leverageResult.riskAssessment, + reasoning: leverageResult.reasoning + }) + + return { + tokenAmount, + leverageUsed: leverageResult.recommendedLeverage, + marginRequired: leverageResult.marginRequired, + liquidationPrice: leverageResult.liquidationPrice, + riskAssessment: leverageResult.riskAssessment + } } // โœ… NEW: Calculate SOL amount to sell for SELL orders @@ -756,6 +814,82 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. } } + // โœ… NEW: Calculate leveraged short position for SELL orders + private async calculateSellPositionWithLeverage(analysis: any): Promise<{ + tokenAmount: number + leverageUsed: number + marginRequired: number + liquidationPrice: number + riskAssessment: string + }> { + try { + console.log('๐Ÿ“‰ Calculating SELL position with AI leverage...') + + // Get account balance for leverage calculation + const balanceResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'}/api/drift/balance`) + const balanceData = await balanceResponse.json() + + const accountValue = balanceData.accountValue || balanceData.totalCollateral + const availableBalance = balanceData.availableBalance + + // Get current price + let currentPrice = analysis.entry?.price || analysis.currentPrice + if (!currentPrice) { + const { default: PriceFetcher } = await import('./price-fetcher') + currentPrice = await PriceFetcher.getCurrentPrice(this.config?.symbol || 'SOLUSD') + } + + // Calculate stop loss for short position (above entry price) + const stopLossPercent = this.calculateAIStopLoss(analysis) / 100 + const stopLossPrice = currentPrice * (1 + stopLossPercent) + + console.log(`๐ŸŽฏ SHORT Position Parameters: Entry=$${currentPrice}, StopLoss=$${stopLossPrice.toFixed(4)}`) + + // Use AI leverage for short position + const leverageResult = AILeverageCalculator.calculateOptimalLeverage({ + accountValue, + availableBalance, + entryPrice: currentPrice, + stopLossPrice, + side: 'short', + maxLeverageAllowed: this.config!.maxLeverage || 20, + safetyBuffer: 0.10 + }) + + // Calculate leveraged short amount + const baseAmount = accountValue < 1000 ? availableBalance : availableBalance * 0.5 + const leveragedAmount = baseAmount * leverageResult.recommendedLeverage + const tokenAmount = leveragedAmount / currentPrice + + console.log(`๐Ÿ“‰ SELL Position with AI Leverage:`, { + baseAmount: `$${baseAmount.toFixed(2)}`, + leverage: `${leverageResult.recommendedLeverage.toFixed(1)}x`, + leveragedAmount: `$${leveragedAmount.toFixed(2)}`, + tokenAmount: tokenAmount.toFixed(4), + riskLevel: leverageResult.riskAssessment, + reasoning: leverageResult.reasoning + }) + + return { + tokenAmount, + leverageUsed: leverageResult.recommendedLeverage, + marginRequired: leverageResult.marginRequired, + liquidationPrice: leverageResult.liquidationPrice, + riskAssessment: leverageResult.riskAssessment + } + + } catch (error) { + console.error('Error calculating SELL position with leverage:', error) + return { + tokenAmount: 0.01, // Fallback small amount + leverageUsed: 1, + marginRequired: 0, + liquidationPrice: 0, + riskAssessment: 'HIGH' + } + } + } + private calculateStopLoss(analysis: any): number { // โœ… AI-FIRST: Use AI analysis stopLoss if available if (analysis.stopLoss?.price) { @@ -929,8 +1063,10 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. } private async executeLiveTrade(decision: any): Promise { - // Execute real trade via Drift Protocol + // Execute real trade via Drift Protocol with AI-calculated leverage console.log(`๐ŸŒŠ Executing Drift trade: ${decision.direction} ${this.config!.symbol}`) + console.log(`๐Ÿง  AI Leverage: ${decision.leverageUsed.toFixed(1)}x (Risk: ${decision.riskAssessment})`) + console.log(`๐Ÿ’€ Liquidation Price: $${decision.liquidationPrice.toFixed(4)}`) // Calculate AI-generated stop loss and take profit from analysis const stopLossPercent = decision.stopLoss || this.calculateAIStopLoss(decision) @@ -950,12 +1086,19 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. symbol: this.config!.symbol, amount: this.config!.tradingAmount, side: decision.direction.toLowerCase(), - leverage: this.config!.maxLeverage || 2, + leverage: decision.leverageUsed || this.config!.maxLeverage || 2, // Use AI-calculated leverage stopLoss: true, takeProfit: true, stopLossPercent: stopLossPercent, takeProfitPercent: takeProfitPercent, - mode: this.config!.mode || 'SIMULATION' + mode: this.config!.mode || 'SIMULATION', + // Include AI leverage details for logging + aiLeverageDetails: { + calculatedLeverage: decision.leverageUsed, + liquidationPrice: decision.liquidationPrice, + riskAssessment: decision.riskAssessment, + marginRequired: decision.marginRequired + } }) }) @@ -970,7 +1113,9 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. direction: decision.direction, status: 'COMPLETED', timestamp: new Date(), - leverage: tradeResult.leverageUsed || this.config!.maxLeverage, + leverage: decision.leverageUsed || tradeResult.leverageUsed || this.config!.maxLeverage, + liquidationPrice: decision.liquidationPrice, + riskAssessment: decision.riskAssessment, stopLoss: stopLossPercent, takeProfit: takeProfitPercent, tradingAmount: this.config!.tradingAmount, @@ -1015,11 +1160,22 @@ ${validResults.map(r => `โ€ข ${r.timeframe}: ${r.analysis?.recommendation} (${r. confidence: decision.confidence, marketSentiment: decision.marketSentiment, createdAt: new Date(), + // Add AI leverage information + leverage: result.leverage || decision.leverageUsed, // Add Drift-specific fields for live trades ...(this.config!.mode === 'LIVE' && result.tradingAmount && { realTradingAmount: this.config!.tradingAmount, - leverage: result.leverage, driftTxId: result.transactionId + }), + // Add AI leverage details in metadata + metadata: JSON.stringify({ + aiLeverage: { + calculatedLeverage: decision.leverageUsed, + liquidationPrice: decision.liquidationPrice, + riskAssessment: decision.riskAssessment, + marginRequired: decision.marginRequired, + balanceStrategy: result.accountValue < 1000 ? 'AGGRESSIVE_100%' : 'CONSERVATIVE_50%' + } }) } })