🔧 CRITICAL FIX: Price Data Sync & Position Monitor Enhancement
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.
This commit is contained in:
361
ai-learning-analytics.js
Normal file
361
ai-learning-analytics.js
Normal file
@@ -0,0 +1,361 @@
|
||||
#!/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;
|
||||
Reference in New Issue
Block a user