Fixed major price data sync issues: - Removed hardcoded price (77.63) from position monitor - Added real-time oracle data instead of stale TWAP pricing - Implemented cache-busting headers for fresh data - Updated fallback prices to current market levels - Real-time P&L tracking with trend indicators (📈📉➡️) - Enhanced stop loss proximity alerts with color-coded risk levels - Analysis progress indicators during automation cycles - Performance metrics (runtime, cycles, trades, errors) - Fresh data validation and improved error handling - Price accuracy: 77.63 → 84.47 (matches Drift UI) - P&L accuracy: -.91 → -.59 (correct calculation) - Risk assessment: CRITICAL → MEDIUM (proper evaluation) - Stop loss distance: 0.91% → 4.8% (safe distance) - CLI monitor script with 8-second updates - Web dashboard component (PositionMonitor.tsx) - Real-time automation status tracking - Database and error monitoring improvements This fixes the automation showing false emergency alerts when position was actually performing normally.
362 lines
12 KiB
JavaScript
362 lines
12 KiB
JavaScript
#!/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;
|