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, conservative >k)
- Real-time balance and position validation

This enables maximum profit potential while maintaining strict risk controls.
This commit is contained in:
mindesbunister
2025-07-24 12:23:47 +02:00
parent 1a6e57519a
commit 5336ab5d98
2 changed files with 433 additions and 26 deletions

View File

@@ -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<number> {
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(`<EFBFBD> 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<any> {
// 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%'
}
})
}
})