#!/usr/bin/env node /** * AI Learning Analytics System * * Analyzes AI trading performance improvements and generates proof of learning effectiveness */ const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient(); class AILearningAnalytics { constructor() { this.startDate = new Date('2025-07-24'); // When AI trading started } async generateLearningReport() { console.log('🧠 AI LEARNING EFFECTIVENESS REPORT'); console.log('=' .repeat(60)); console.log(''); try { // Get all learning data since AI started const learningData = await this.getLearningData(); const tradeData = await this.getTradeData(); const automationSessions = await this.getAutomationSessions(); // Calculate improvement metrics const improvements = await this.calculateImprovements(learningData); const pnlAnalysis = await this.calculateTotalPnL(tradeData); const accuracyTrends = await this.calculateAccuracyTrends(learningData); const confidenceEvolution = await this.calculateConfidenceEvolution(learningData); // Generate report this.displayOverallStats(learningData, tradeData, automationSessions); this.displayLearningImprovements(improvements); this.displayPnLAnalysis(pnlAnalysis); this.displayAccuracyTrends(accuracyTrends); this.displayConfidenceEvolution(confidenceEvolution); // Generate JSON for frontend const reportData = { generated: new Date().toISOString(), period: { start: this.startDate.toISOString(), end: new Date().toISOString(), daysActive: Math.ceil((Date.now() - this.startDate.getTime()) / (1000 * 60 * 60 * 24)) }, overview: { totalLearningRecords: learningData.length, totalTrades: tradeData.length, totalSessions: automationSessions.length, activeSessions: automationSessions.filter(s => s.status === 'ACTIVE').length }, improvements, pnl: pnlAnalysis, accuracy: accuracyTrends, confidence: confidenceEvolution }; // Save report for API await this.saveReport(reportData); console.log('\nšŸ“Š Report saved and ready for dashboard display!'); return reportData; } catch (error) { console.error('āŒ Error generating learning report:', error.message); throw error; } } async getLearningData() { return await prisma.aILearningData.findMany({ where: { createdAt: { gte: this.startDate } }, orderBy: { createdAt: 'asc' } }); } async getTradeData() { return await prisma.trade.findMany({ where: { createdAt: { gte: this.startDate }, isAutomated: true // Only AI trades }, orderBy: { createdAt: 'asc' } }); } async getAutomationSessions() { return await prisma.automationSession.findMany({ where: { createdAt: { gte: this.startDate } }, orderBy: { createdAt: 'desc' } }); } async calculateImprovements(learningData) { if (learningData.length < 10) { return { improvement: 0, trend: 'INSUFFICIENT_DATA', message: 'Need more learning data to calculate improvements' }; } // Split data into early vs recent periods const midPoint = Math.floor(learningData.length / 2); const earlyData = learningData.slice(0, midPoint); const recentData = learningData.slice(midPoint); // Calculate average confidence scores const earlyConfidence = this.getAverageConfidence(earlyData); const recentConfidence = this.getAverageConfidence(recentData); // Calculate accuracy if outcomes are available const earlyAccuracy = this.getAccuracy(earlyData); const recentAccuracy = this.getAccuracy(recentData); const confidenceImprovement = ((recentConfidence - earlyConfidence) / earlyConfidence) * 100; const accuracyImprovement = earlyAccuracy && recentAccuracy ? ((recentAccuracy - earlyAccuracy) / earlyAccuracy) * 100 : null; return { confidenceImprovement: Number(confidenceImprovement.toFixed(2)), accuracyImprovement: accuracyImprovement ? Number(accuracyImprovement.toFixed(2)) : null, earlyPeriod: { samples: earlyData.length, avgConfidence: Number(earlyConfidence.toFixed(2)), accuracy: earlyAccuracy ? Number(earlyAccuracy.toFixed(2)) : null }, recentPeriod: { samples: recentData.length, avgConfidence: Number(recentConfidence.toFixed(2)), accuracy: recentAccuracy ? Number(recentAccuracy.toFixed(2)) : null }, trend: confidenceImprovement > 5 ? 'IMPROVING' : confidenceImprovement < -5 ? 'DECLINING' : 'STABLE' }; } async calculateTotalPnL(tradeData) { const analysis = { totalTrades: tradeData.length, totalPnL: 0, totalPnLPercent: 0, winningTrades: 0, losingTrades: 0, breakEvenTrades: 0, avgTradeSize: 0, bestTrade: null, worstTrade: null, winRate: 0, avgWin: 0, avgLoss: 0, profitFactor: 0 }; if (tradeData.length === 0) { return analysis; } let totalProfit = 0; let totalLoss = 0; let totalAmount = 0; tradeData.forEach(trade => { const pnl = trade.profit || 0; const pnlPercent = trade.pnlPercent || 0; const amount = trade.amount || 0; analysis.totalPnL += pnl; analysis.totalPnLPercent += pnlPercent; totalAmount += amount; if (pnl > 0) { analysis.winningTrades++; totalProfit += pnl; } else if (pnl < 0) { analysis.losingTrades++; totalLoss += Math.abs(pnl); } else { analysis.breakEvenTrades++; } // Track best/worst trades if (!analysis.bestTrade || pnl > analysis.bestTrade.profit) { analysis.bestTrade = trade; } if (!analysis.worstTrade || pnl < analysis.worstTrade.profit) { analysis.worstTrade = trade; } }); analysis.avgTradeSize = totalAmount / tradeData.length; analysis.winRate = (analysis.winningTrades / tradeData.length) * 100; analysis.avgWin = analysis.winningTrades > 0 ? totalProfit / analysis.winningTrades : 0; analysis.avgLoss = analysis.losingTrades > 0 ? totalLoss / analysis.losingTrades : 0; analysis.profitFactor = analysis.avgLoss > 0 ? analysis.avgWin / analysis.avgLoss : 0; // Round numbers Object.keys(analysis).forEach(key => { if (typeof analysis[key] === 'number') { analysis[key] = Number(analysis[key].toFixed(4)); } }); return analysis; } async calculateAccuracyTrends(learningData) { const trends = []; const chunkSize = Math.max(5, Math.floor(learningData.length / 10)); // At least 5 samples per chunk for (let i = 0; i < learningData.length; i += chunkSize) { const chunk = learningData.slice(i, i + chunkSize); const accuracy = this.getAccuracy(chunk); const confidence = this.getAverageConfidence(chunk); trends.push({ period: i / chunkSize + 1, samples: chunk.length, accuracy: accuracy ? Number(accuracy.toFixed(2)) : null, confidence: Number(confidence.toFixed(2)), timestamp: chunk[chunk.length - 1]?.createdAt }); } return trends; } async calculateConfidenceEvolution(learningData) { return learningData.map((record, index) => ({ index: index + 1, timestamp: record.createdAt, confidence: record.confidenceScore || 0, accuracy: record.accuracyScore || null, symbol: record.symbol, outcome: record.outcome })); } getAverageConfidence(data) { const confidenceScores = data .map(d => d.confidenceScore || d.analysisData?.confidence || 0.5) .filter(score => score > 0); return confidenceScores.length > 0 ? confidenceScores.reduce((a, b) => a + b, 0) / confidenceScores.length : 0.5; } getAccuracy(data) { const withOutcomes = data.filter(d => d.outcome && d.accuracyScore); if (withOutcomes.length === 0) return null; const avgAccuracy = withOutcomes.reduce((sum, d) => sum + (d.accuracyScore || 0), 0) / withOutcomes.length; return avgAccuracy; } displayOverallStats(learningData, tradeData, automationSessions) { console.log('šŸ“ˆ OVERALL AI TRADING STATISTICS'); console.log(` Period: ${this.startDate.toDateString()} - ${new Date().toDateString()}`); console.log(` Learning Records: ${learningData.length}`); console.log(` AI Trades Executed: ${tradeData.length}`); console.log(` Automation Sessions: ${automationSessions.length}`); console.log(` Active Sessions: ${automationSessions.filter(s => s.status === 'ACTIVE').length}`); console.log(''); } displayLearningImprovements(improvements) { console.log('🧠 AI LEARNING IMPROVEMENTS'); if (improvements.trend === 'INSUFFICIENT_DATA') { console.log(` āš ļø ${improvements.message}`); } else { console.log(` šŸ“Š Confidence Improvement: ${improvements.confidenceImprovement > 0 ? '+' : ''}${improvements.confidenceImprovement}%`); if (improvements.accuracyImprovement !== null) { console.log(` šŸŽÆ Accuracy Improvement: ${improvements.accuracyImprovement > 0 ? '+' : ''}${improvements.accuracyImprovement}%`); } console.log(` šŸ“ˆ Trend: ${improvements.trend}`); console.log(` Early Period: ${improvements.earlyPeriod.avgConfidence}% confidence (${improvements.earlyPeriod.samples} samples)`); console.log(` Recent Period: ${improvements.recentPeriod.avgConfidence}% confidence (${improvements.recentPeriod.samples} samples)`); } console.log(''); } displayPnLAnalysis(pnl) { console.log('šŸ’° TOTAL PnL ANALYSIS'); console.log(` Total Trades: ${pnl.totalTrades}`); console.log(` Total PnL: $${pnl.totalPnL.toFixed(4)}`); console.log(` Total PnL %: ${pnl.totalPnLPercent.toFixed(2)}%`); console.log(` Win Rate: ${pnl.winRate.toFixed(1)}%`); console.log(` Winning Trades: ${pnl.winningTrades}`); console.log(` Losing Trades: ${pnl.losingTrades}`); console.log(` Break Even: ${pnl.breakEvenTrades}`); if (pnl.totalTrades > 0) { console.log(` Average Trade Size: $${pnl.avgTradeSize.toFixed(2)}`); console.log(` Average Win: $${pnl.avgWin.toFixed(4)}`); console.log(` Average Loss: $${pnl.avgLoss.toFixed(4)}`); console.log(` Profit Factor: ${pnl.profitFactor.toFixed(2)}`); } console.log(''); } displayAccuracyTrends(trends) { console.log('šŸ“Š ACCURACY TRENDS OVER TIME'); trends.forEach(trend => { console.log(` Period ${trend.period}: ${trend.confidence}% confidence, ${trend.accuracy ? trend.accuracy + '% accuracy' : 'no accuracy data'} (${trend.samples} samples)`); }); console.log(''); } displayConfidenceEvolution(evolution) { console.log('šŸ“ˆ RECENT CONFIDENCE EVOLUTION'); const recentData = evolution.slice(-10); // Last 10 records recentData.forEach(record => { const date = new Date(record.timestamp).toLocaleDateString(); console.log(` ${date}: ${(record.confidence * 100).toFixed(1)}% confidence (${record.symbol})`); }); console.log(''); } async saveReport(reportData) { const fs = require('fs'); const reportPath = './public/ai-learning-report.json'; // Ensure public directory exists if (!fs.existsSync('./public')) { fs.mkdirSync('./public', { recursive: true }); } fs.writeFileSync(reportPath, JSON.stringify(reportData, null, 2)); console.log(`šŸ“ Report saved to: ${reportPath}`); } } // Run the analytics async function main() { const analytics = new AILearningAnalytics(); try { await analytics.generateLearningReport(); } catch (error) { console.error('Failed to generate report:', error); } finally { await prisma.$disconnect(); } } if (require.main === module) { main(); } module.exports = AILearningAnalytics;