#!/usr/bin/env node /** * DRIFT FEEDBACK LOOP IMPLEMENTATION * Real-time feedback system for Drift Protocol trades * Tracks outcomes and feeds back to AI learning system */ const { PrismaClient } = require('@prisma/client') const { DriftClient, initialize } = require('@drift-labs/sdk') const { Connection, Keypair } = require('@solana/web3.js') class DriftFeedbackLoop { constructor() { this.prisma = new PrismaClient() this.driftClient = null this.isMonitoring = false this.monitoringInterval = null } async initialize() { console.log('šŸ”„ Initializing Drift Feedback Loop System...') try { // Initialize Drift client const connection = new Connection( process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com', 'confirmed' ) const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY) const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)) const wallet = { publicKey: keypair.publicKey, signTransaction: async (tx) => { tx.partialSign(keypair) return tx }, signAllTransactions: async (txs) => { return txs.map(tx => { tx.partialSign(keypair) return tx }) } } const env = 'mainnet-beta' const sdkConfig = initialize({ env }) this.driftClient = new DriftClient({ connection, wallet, programID: sdkConfig.DRIFT_PROGRAM_ID, opts: { commitment: 'confirmed' } }) await this.driftClient.subscribe() console.log('āœ… Drift client initialized and subscribed') } catch (error) { console.error('āŒ Failed to initialize Drift client:', error.message) throw error } } async startMonitoring(userId = 'drift-user') { console.log('šŸŽÆ Starting real-time Drift trade monitoring...') this.isMonitoring = true // Monitor every 30 seconds this.monitoringInterval = setInterval(async () => { try { await this.checkTradeOutcomes(userId) } catch (error) { console.error('āŒ Monitoring error:', error.message) } }, 30000) // Also do an immediate check await this.checkTradeOutcomes(userId) console.log('āœ… Monitoring started - checking every 30 seconds') } async checkTradeOutcomes(userId) { try { // Get all open trades that haven't been checked recently const openTrades = await this.prisma.trade.findMany({ where: { userId, status: 'EXECUTED', outcome: null, // Not yet determined driftTxId: { not: null }, // Has Drift transaction ID executedAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours } }, orderBy: { executedAt: 'desc' } }) console.log(`šŸ” Checking ${openTrades.length} open Drift trades...`) for (const trade of openTrades) { await this.checkIndividualTrade(trade) } } catch (error) { console.error('āŒ Error checking trade outcomes:', error.message) } } async checkIndividualTrade(trade) { try { console.log(`šŸ” Checking trade ${trade.id} (${trade.symbol} ${trade.side})...`) // Get current Drift positions and account state const userAccount = await this.driftClient.getUserAccount() const currentPositions = userAccount.perpPositions || [] // Find position for this trade's market const marketIndex = this.getMarketIndex(trade.symbol) const position = currentPositions.find(pos => pos.marketIndex === marketIndex && !pos.baseAssetAmount.isZero() ) // Check if trade has been closed (no position remaining) const isClosed = !position || position.baseAssetAmount.isZero() if (isClosed) { console.log(`āœ… Trade ${trade.id} appears to be closed, analyzing outcome...`) await this.analyzeTradeOutcome(trade) } else { // Check if stop loss or take profit levels have been hit await this.checkStopLossAndTakeProfit(trade, position) } } catch (error) { console.error(`āŒ Error checking trade ${trade.id}:`, error.message) } } async analyzeTradeOutcome(trade) { try { // Determine trade outcome based on current vs entry price const currentPrice = await this.getCurrentPrice(trade.symbol) const entryPrice = trade.entryPrice || trade.price let outcome, pnlPercent, exitPrice if (trade.side === 'BUY') { pnlPercent = ((currentPrice - entryPrice) / entryPrice) * 100 } else { pnlPercent = ((entryPrice - currentPrice) / entryPrice) * 100 } // Determine outcome if (pnlPercent > 0.1) { outcome = 'WIN' } else if (pnlPercent < -0.1) { outcome = 'LOSS' } else { outcome = 'BREAKEVEN' } exitPrice = currentPrice // Calculate actual risk/reward ratio const actualRR = this.calculateActualRiskReward(trade, exitPrice) console.log(`šŸ“Š Trade outcome: ${outcome}, P&L: ${pnlPercent.toFixed(2)}%, RR: ${actualRR.toFixed(2)}`) // Update trade record const updatedTrade = await this.prisma.trade.update({ where: { id: trade.id }, data: { outcome, pnlPercent, exitPrice, actualRR, closedAt: new Date(), status: 'CLOSED', learningData: JSON.stringify({ exitReason: this.determineExitReason(trade, exitPrice), marketBehavior: this.analyzeMarketBehavior(trade, exitPrice), accuracyVsPrediction: this.calculatePredictionAccuracy(trade, exitPrice), driftSpecificData: { platformUsed: 'DRIFT_PROTOCOL', executionMethod: 'REAL_TRADING', tradeType: 'PERPETUAL_FUTURES' } }) } }) // Create AI learning feedback await this.createAILearningFeedback(updatedTrade) console.log(`āœ… Updated trade ${trade.id} with outcome: ${outcome}`) } catch (error) { console.error(`āŒ Error analyzing trade outcome:`, error.message) } } async checkStopLossAndTakeProfit(trade, position) { try { const currentPrice = await this.getCurrentPrice(trade.symbol) // Check if stop loss or take profit should have been triggered let shouldClose = false let exitReason = null if (trade.side === 'BUY') { if (trade.stopLoss && currentPrice <= trade.stopLoss) { shouldClose = true exitReason = 'STOP_LOSS' } else if (trade.takeProfit && currentPrice >= trade.takeProfit) { shouldClose = true exitReason = 'TAKE_PROFIT' } } else { if (trade.stopLoss && currentPrice >= trade.stopLoss) { shouldClose = true exitReason = 'STOP_LOSS' } else if (trade.takeProfit && currentPrice <= trade.takeProfit) { shouldClose = true exitReason = 'TAKE_PROFIT' } } if (shouldClose) { console.log(`šŸŽÆ Trade ${trade.id} hit ${exitReason} at price ${currentPrice}`) // Update trade with specific exit reason await this.prisma.trade.update({ where: { id: trade.id }, data: { exitPrice: currentPrice, learningData: JSON.stringify({ exitReason, triggeredAt: new Date(), expectedBehavior: true // SL/TP worked as expected }) } }) } } catch (error) { console.error(`āŒ Error checking SL/TP levels:`, error.message) } } async createAILearningFeedback(trade) { try { // Link this trade outcome back to the AI analysis that generated it const relatedAnalysis = await this.prisma.aILearningData.findFirst({ where: { userId: trade.userId, symbol: trade.symbol, tradeId: trade.id }, orderBy: { createdAt: 'desc' } }) if (relatedAnalysis) { // Update the AI learning record with real trade outcome await this.prisma.aILearningData.update({ where: { id: relatedAnalysis.id }, data: { outcome: trade.outcome, actualPrice: trade.exitPrice, accuracyScore: this.calculateAccuracy(relatedAnalysis, trade), feedbackData: JSON.stringify({ realTradeOutcome: { tradeId: trade.id, pnlPercent: trade.pnlPercent, actualRR: trade.actualRR, exitReason: JSON.parse(trade.learningData || '{}').exitReason, driftProtocolData: { platform: 'DRIFT_PROTOCOL', orderType: 'PERPETUAL_FUTURES', leverage: trade.leverage, fees: trade.fees } }, aiPredictionAccuracy: { predictedOutcome: this.extractPredictedOutcome(relatedAnalysis), actualOutcome: trade.outcome, priceAccuracy: Math.abs((trade.exitPrice - relatedAnalysis.predictedPrice) / relatedAnalysis.predictedPrice) * 100, confidenceValidation: this.validateConfidence(relatedAnalysis, trade) } }) } }) console.log(`🧠 Created AI learning feedback for analysis ${relatedAnalysis.id}`) } // Create new learning insights await this.generateLearningInsights(trade.userId) } catch (error) { console.error(`āŒ Error creating AI learning feedback:`, error.message) } } async generateLearningInsights(userId) { try { // Generate comprehensive learning insights from real Drift trades const recentTrades = await this.prisma.trade.findMany({ where: { userId, outcome: { not: null }, driftTxId: { not: null }, // Only real Drift trades closedAt: { gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days } }, orderBy: { closedAt: 'desc' } }) const insights = { totalDriftTrades: recentTrades.length, winRate: this.calculateWinRate(recentTrades), avgPnL: this.calculateAveragePnL(recentTrades), bestPerformingTimeframe: this.findBestTimeframe(recentTrades), riskRewardAnalysis: this.analyzeRiskReward(recentTrades), commonFailurePatterns: this.identifyFailurePatterns(recentTrades), driftSpecificInsights: { platformEfficiency: this.analyzePlatformEfficiency(recentTrades), optimalLeverage: this.findOptimalLeverage(recentTrades), stopLossEffectiveness: this.analyzeStopLossEffectiveness(recentTrades) } } console.log('šŸ“Š Generated learning insights:', insights) // Store insights for AI to use in future decisions await this.prisma.aILearningData.create({ data: { userId, symbol: 'INSIGHTS', timeframe: '30d', analysisData: JSON.stringify(insights), marketConditions: JSON.stringify({ dataSource: 'REAL_DRIFT_TRADES', analysisType: 'PERFORMANCE_INSIGHTS', sampleSize: recentTrades.length }), confidenceScore: insights.winRate * 100, createdAt: new Date() } }) } catch (error) { console.error(`āŒ Error generating learning insights:`, error.message) } } // Helper methods getMarketIndex(symbol) { const marketMap = { 'SOL': 0, 'BTC': 1, 'ETH': 2, 'APT': 3, 'AVAX': 4, 'BNB': 5, 'MATIC': 6, 'ARB': 7, 'DOGE': 8, 'OP': 9 } return marketMap[symbol.toUpperCase()] || 0 } async getCurrentPrice(symbol) { try { const marketIndex = this.getMarketIndex(symbol) const perpMarketAccount = this.driftClient.getPerpMarketAccount(marketIndex) return Number(perpMarketAccount.amm.lastMarkPriceTwap) / 1e6 } catch (error) { console.error(`āŒ Error getting current price for ${symbol}:`, error.message) return null } } calculateActualRiskReward(trade, exitPrice) { const entryPrice = trade.entryPrice || trade.price const stopLoss = trade.stopLoss const takeProfit = trade.takeProfit if (!stopLoss || !takeProfit) return 0 const riskAmount = Math.abs(entryPrice - stopLoss) const rewardAmount = Math.abs(exitPrice - entryPrice) return riskAmount > 0 ? rewardAmount / riskAmount : 0 } determineExitReason(trade, exitPrice) { if (trade.stopLoss && Math.abs(exitPrice - trade.stopLoss) < 0.01) { return 'STOP_LOSS' } else if (trade.takeProfit && Math.abs(exitPrice - trade.takeProfit) < 0.01) { return 'TAKE_PROFIT' } else { return 'MANUAL_CLOSE' } } analyzeMarketBehavior(trade, exitPrice) { const entryPrice = trade.entryPrice || trade.price const priceMove = (exitPrice - entryPrice) / entryPrice * 100 if (Math.abs(priceMove) < 0.5) return 'SIDEWAYS' if (priceMove > 0) return 'BULLISH' return 'BEARISH' } calculatePredictionAccuracy(trade, exitPrice) { const entryPrice = trade.entryPrice || trade.price const expectedDirection = trade.side === 'BUY' ? 'UP' : 'DOWN' const actualDirection = exitPrice > entryPrice ? 'UP' : 'DOWN' return expectedDirection === actualDirection ? 100 : 0 } calculateAccuracy(analysis, trade) { try { const predicted = analysis.predictedPrice const actual = trade.exitPrice if (!predicted || !actual) return null const accuracy = 1 - Math.abs(predicted - actual) / predicted return Math.max(0, Math.min(1, accuracy)) } catch (error) { return null } } extractPredictedOutcome(analysis) { try { const data = JSON.parse(analysis.analysisData) return data.recommendation || 'UNKNOWN' } catch (error) { return 'UNKNOWN' } } validateConfidence(analysis, trade) { const confidence = analysis.confidenceScore || 0 const wasCorrect = trade.outcome === 'WIN' return { confidence, wasCorrect, calibration: wasCorrect ? 'WELL_CALIBRATED' : 'OVERCONFIDENT' } } calculateWinRate(trades) { const wins = trades.filter(t => t.outcome === 'WIN').length return trades.length > 0 ? wins / trades.length : 0 } calculateAveragePnL(trades) { const totalPnL = trades.reduce((sum, t) => sum + (t.pnlPercent || 0), 0) return trades.length > 0 ? totalPnL / trades.length : 0 } findBestTimeframe(trades) { const timeframes = {} trades.forEach(trade => { const tf = trade.timeframe || '1h' if (!timeframes[tf]) timeframes[tf] = { wins: 0, total: 0 } timeframes[tf].total++ if (trade.outcome === 'WIN') timeframes[tf].wins++ }) let bestTf = '1h' let bestRate = 0 Object.entries(timeframes).forEach(([tf, data]) => { const rate = data.total > 0 ? data.wins / data.total : 0 if (rate > bestRate && data.total >= 3) { // At least 3 trades bestRate = rate bestTf = tf } }) return { timeframe: bestTf, winRate: bestRate } } analyzeRiskReward(trades) { const validTrades = trades.filter(t => t.actualRR) const avgRR = validTrades.reduce((sum, t) => sum + t.actualRR, 0) / validTrades.length return { averageRiskReward: avgRR || 0, tradesWithGoodRR: validTrades.filter(t => t.actualRR > 1.5).length, totalAnalyzedTrades: validTrades.length } } identifyFailurePatterns(trades) { const failures = trades.filter(t => t.outcome === 'LOSS') const patterns = [] // Analyze common failure reasons const exitReasons = {} failures.forEach(trade => { try { const data = JSON.parse(trade.learningData || '{}') const reason = data.exitReason || 'UNKNOWN' exitReasons[reason] = (exitReasons[reason] || 0) + 1 } catch (error) { exitReasons['UNKNOWN'] = (exitReasons['UNKNOWN'] || 0) + 1 } }) Object.entries(exitReasons).forEach(([reason, count]) => { if (count >= 2) { patterns.push({ pattern: reason, frequency: count, percentage: (count / failures.length) * 100 }) } }) return patterns } analyzePlatformEfficiency(trades) { const driftTrades = trades.filter(t => t.driftTxId) return { totalDriftTrades: driftTrades.length, avgExecutionTime: this.calculateAvgExecutionTime(driftTrades), successRate: this.calculateSuccessRate(driftTrades), avgFees: this.calculateAvgFees(driftTrades) } } findOptimalLeverage(trades) { const leverageGroups = {} trades.forEach(trade => { const lev = Math.floor(trade.leverage || 1) if (!leverageGroups[lev]) leverageGroups[lev] = { wins: 0, total: 0, totalPnL: 0 } leverageGroups[lev].total++ leverageGroups[lev].totalPnL += trade.pnlPercent || 0 if (trade.outcome === 'WIN') leverageGroups[lev].wins++ }) let optimalLeverage = 1 let bestScore = 0 Object.entries(leverageGroups).forEach(([lev, data]) => { if (data.total >= 3) { const winRate = data.wins / data.total const avgPnL = data.totalPnL / data.total const score = winRate * avgPnL if (score > bestScore) { bestScore = score optimalLeverage = parseInt(lev) } } }) return { leverage: optimalLeverage, score: bestScore } } analyzeStopLossEffectiveness(trades) { const slTrades = trades.filter(t => { try { const data = JSON.parse(t.learningData || '{}') return data.exitReason === 'STOP_LOSS' } catch (error) { return false } }) return { stopLossActivations: slTrades.length, avgLossWhenTriggered: slTrades.reduce((sum, t) => sum + (t.pnlPercent || 0), 0) / slTrades.length || 0, effectiveness: slTrades.length / trades.length } } calculateAvgExecutionTime(trades) { const validTrades = trades.filter(t => t.executionTime && t.createdAt) if (validTrades.length === 0) return 0 const totalTime = validTrades.reduce((sum, trade) => { const diff = new Date(trade.executionTime) - new Date(trade.createdAt) return sum + diff }, 0) return totalTime / validTrades.length / 1000 // Convert to seconds } calculateSuccessRate(trades) { const executed = trades.filter(t => t.status === 'EXECUTED' || t.status === 'CLOSED') return trades.length > 0 ? executed.length / trades.length : 0 } calculateAvgFees(trades) { const tradesWithFees = trades.filter(t => t.fees !== null && t.fees !== undefined) const totalFees = tradesWithFees.reduce((sum, t) => sum + t.fees, 0) return tradesWithFees.length > 0 ? totalFees / tradesWithFees.length : 0 } async stopMonitoring() { console.log('ā¹ļø Stopping Drift feedback monitoring...') this.isMonitoring = false if (this.monitoringInterval) { clearInterval(this.monitoringInterval) this.monitoringInterval = null } if (this.driftClient) { await this.driftClient.unsubscribe() } await this.prisma.$disconnect() console.log('āœ… Monitoring stopped and resources cleaned up') } } // Export for use in other modules module.exports = { DriftFeedbackLoop } // CLI usage if (require.main === module) { const feedbackLoop = new DriftFeedbackLoop() async function runFeedbackLoop() { try { await feedbackLoop.initialize() await feedbackLoop.startMonitoring('drift-user') console.log('šŸŽÆ Drift feedback loop is running...') console.log('Press Ctrl+C to stop') // Handle graceful shutdown process.on('SIGINT', async () => { console.log('\nšŸ›‘ Shutting down gracefully...') await feedbackLoop.stopMonitoring() process.exit(0) }) } catch (error) { console.error('āŒ Failed to start feedback loop:', error.message) process.exit(1) } } runFeedbackLoop() }