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.
344 lines
13 KiB
TypeScript
344 lines
13 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { RefreshCw, TrendingUp, TrendingDown, Activity, Brain, DollarSign, Target } from 'lucide-react';
|
|
|
|
export default function AILearningDashboard() {
|
|
const [analytics, setAnalytics] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
const fetchAnalytics = async () => {
|
|
try {
|
|
const response = await fetch('/api/ai-analytics');
|
|
if (!response.ok) throw new Error('Failed to fetch analytics');
|
|
const data = await response.json();
|
|
setAnalytics(data);
|
|
setError(null);
|
|
} catch (err) {
|
|
setError(err.message);
|
|
} finally {
|
|
setLoading(false);
|
|
setRefreshing(false);
|
|
}
|
|
};
|
|
|
|
const handleRefresh = async () => {
|
|
setRefreshing(true);
|
|
await fetchAnalytics();
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchAnalytics();
|
|
const interval = setInterval(fetchAnalytics, 30000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center p-8">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
<span className="ml-2">Loading AI analytics...</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<p className="text-red-600">Error loading analytics: {error}</p>
|
|
<Button onClick={handleRefresh} className="mt-2" size="sm">
|
|
Try Again
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!analytics) return null;
|
|
|
|
const { overview, improvements, pnl, currentPosition, realTimeMetrics, learningProof } = analytics;
|
|
|
|
const getTrendIcon = (trend) => {
|
|
switch (trend) {
|
|
case 'IMPROVING': return <TrendingUp className=\"h-4 w-4 text-green-500\" />;
|
|
case 'DECLINING': return <TrendingDown className=\"h-4 w-4 text-red-500\" />;
|
|
default: return <Activity className=\"h-4 w-4 text-yellow-500\" />;
|
|
}
|
|
};
|
|
|
|
const getTrendColor = (trend) => {
|
|
switch (trend) {
|
|
case 'IMPROVING': return 'bg-green-100 text-green-800';
|
|
case 'DECLINING': return 'bg-red-100 text-red-800';
|
|
default: return 'bg-yellow-100 text-yellow-800';
|
|
}
|
|
};
|
|
|
|
const formatCurrency = (value) => {
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 4
|
|
}).format(value);
|
|
};
|
|
|
|
const formatPercentage = (value) => {
|
|
return `${value > 0 ? '+' : ''}${value.toFixed(2)}%`;
|
|
};
|
|
|
|
return (
|
|
<div className=\"space-y-6\">
|
|
{/* Header */}
|
|
<div className=\"flex items-center justify-between\">
|
|
<div>
|
|
<h2 className=\"text-2xl font-bold text-gray-900 flex items-center gap-2\">
|
|
<Brain className=\"h-6 w-6 text-blue-600\" />
|
|
AI Learning Analytics
|
|
</h2>
|
|
<p className=\"text-gray-600\">
|
|
Proof of AI improvement and trading performance since {new Date(analytics.period.start).toLocaleDateString()}
|
|
</p>
|
|
</div>
|
|
<Button
|
|
onClick={handleRefresh}
|
|
disabled={refreshing}
|
|
variant=\"outline\"
|
|
size=\"sm\"
|
|
>
|
|
<RefreshCw className={`h-4 w-4 mr-2 ${refreshing ? 'animate-spin' : ''}`} />
|
|
Refresh
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Overview Stats */}
|
|
<div className=\"grid grid-cols-1 md:grid-cols-4 gap-4\">
|
|
<Card>
|
|
<CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">
|
|
<CardTitle className=\"text-sm font-medium\">Learning Records</CardTitle>
|
|
<Brain className=\"h-4 w-4 text-muted-foreground\" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"text-2xl font-bold\">{overview.totalLearningRecords.toLocaleString()}</div>
|
|
<p className=\"text-xs text-muted-foreground\">
|
|
{realTimeMetrics.learningRecordsPerDay}/day average
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">
|
|
<CardTitle className=\"text-sm font-medium\">AI Trades</CardTitle>
|
|
<Activity className=\"h-4 w-4 text-muted-foreground\" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"text-2xl font-bold\">{overview.totalTrades}</div>
|
|
<p className=\"text-xs text-muted-foreground\">
|
|
{realTimeMetrics.tradesPerDay}/day average
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">
|
|
<CardTitle className=\"text-sm font-medium\">Active Sessions</CardTitle>
|
|
<Target className=\"h-4 w-4 text-muted-foreground\" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"text-2xl font-bold\">{overview.activeSessions}</div>
|
|
<p className=\"text-xs text-muted-foreground\">
|
|
of {overview.totalSessions} total sessions
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">
|
|
<CardTitle className=\"text-sm font-medium\">Days Active</CardTitle>
|
|
<Activity className=\"h-4 w-4 text-muted-foreground\" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"text-2xl font-bold\">{realTimeMetrics.daysSinceAIStarted}</div>
|
|
<p className=\"text-xs text-muted-foreground\">
|
|
Since AI trading began
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Learning Improvements */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className=\"flex items-center gap-2\">
|
|
<Brain className=\"h-5 w-5\" />
|
|
AI Learning Improvements
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Statistical proof of AI learning effectiveness over time
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">
|
|
<div>
|
|
<div className=\"flex items-center justify-between mb-2\">
|
|
<span className=\"text-sm font-medium\">Confidence Trend</span>
|
|
<Badge className={getTrendColor(improvements.trend)}>
|
|
{getTrendIcon(improvements.trend)}
|
|
<span className=\"ml-1\">{improvements.trend}</span>
|
|
</Badge>
|
|
</div>
|
|
<div className=\"text-2xl font-bold\">
|
|
{formatPercentage(improvements.confidenceImprovement)}
|
|
</div>
|
|
<div className=\"text-xs text-muted-foreground mt-1\">
|
|
Early: {improvements.earlyPeriod.avgConfidence}% → Recent: {improvements.recentPeriod.avgConfidence}%
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className=\"flex items-center justify-between mb-2\">
|
|
<span className=\"text-sm font-medium\">Sample Size</span>
|
|
<Badge variant={learningProof.isStatisticallySignificant ? 'default' : 'secondary'}>
|
|
{learningProof.isStatisticallySignificant ? 'Significant' : 'Building'}
|
|
</Badge>
|
|
</div>
|
|
<div className=\"text-2xl font-bold\">{learningProof.sampleSize}</div>
|
|
<div className=\"text-xs text-muted-foreground mt-1\">
|
|
{improvements.earlyPeriod.samples} early + {improvements.recentPeriod.samples} recent samples
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{learningProof.isStatisticallySignificant && (
|
|
<div className=\"mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg\">
|
|
<div className=\"flex items-center gap-2 text-blue-800\">
|
|
<Brain className=\"h-4 w-4\" />
|
|
<span className=\"font-medium\">Learning Status:</span>
|
|
{learningProof.hasImprovement ?
|
|
'AI is demonstrably improving over time!' :
|
|
'AI is learning and adapting to market conditions'}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* PnL Analysis */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className=\"flex items-center gap-2\">
|
|
<DollarSign className=\"h-5 w-5\" />
|
|
Total PnL Since AI Started
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Complete trading performance analysis since AI automation began
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">
|
|
<div className=\"text-center\">
|
|
<div className=\"text-3xl font-bold text-blue-600\">
|
|
{formatCurrency(pnl.totalPnL)}
|
|
</div>
|
|
<div className=\"text-sm text-muted-foreground\">Total PnL</div>
|
|
<div className=\"text-xs text-green-600 mt-1\">
|
|
{formatPercentage(pnl.totalPnLPercent)} overall
|
|
</div>
|
|
</div>
|
|
|
|
<div className=\"text-center\">
|
|
<div className=\"text-3xl font-bold text-green-600\">
|
|
{pnl.winRate.toFixed(1)}%
|
|
</div>
|
|
<div className=\"text-sm text-muted-foreground\">Win Rate</div>
|
|
<div className=\"text-xs text-muted-foreground mt-1\">
|
|
{pnl.winningTrades}W / {pnl.losingTrades}L / {pnl.breakEvenTrades}BE
|
|
</div>
|
|
</div>
|
|
|
|
<div className=\"text-center\">
|
|
<div className=\"text-3xl font-bold text-purple-600\">
|
|
{formatCurrency(pnl.avgTradeSize)}
|
|
</div>
|
|
<div className=\"text-sm text-muted-foreground\">Avg Trade Size</div>
|
|
<div className=\"text-xs text-muted-foreground mt-1\">
|
|
{pnl.totalTrades} total trades
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{pnl.totalTrades > 0 && (
|
|
<div className=\"mt-6 grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t\">
|
|
<div>
|
|
<div className=\"text-sm font-medium mb-1\">Average Win</div>
|
|
<div className=\"text-lg font-bold text-green-600\">{formatCurrency(pnl.avgWin)}</div>
|
|
</div>
|
|
<div>
|
|
<div className=\"text-sm font-medium mb-1\">Average Loss</div>
|
|
<div className=\"text-lg font-bold text-red-600\">{formatCurrency(pnl.avgLoss)}</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Current Position */}
|
|
{currentPosition && currentPosition.hasPosition && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className=\"flex items-center gap-2\">
|
|
<Activity className=\"h-5 w-5\" />
|
|
Current AI Position
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Live position being managed by AI risk system
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className=\"grid grid-cols-1 md:grid-cols-4 gap-4\">
|
|
<div>
|
|
<div className=\"text-sm font-medium\">Symbol</div>
|
|
<div className=\"text-lg font-bold\">{currentPosition.symbol}</div>
|
|
<Badge variant=\"outline\">{currentPosition.side.toUpperCase()}</Badge>
|
|
</div>
|
|
<div>
|
|
<div className=\"text-sm font-medium\">Size</div>
|
|
<div className=\"text-lg font-bold\">{currentPosition.size}</div>
|
|
</div>
|
|
<div>
|
|
<div className=\"text-sm font-medium\">Unrealized PnL</div>
|
|
<div className={`text-lg font-bold ${currentPosition.unrealizedPnl >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
|
{formatCurrency(currentPosition.unrealizedPnl)}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className=\"text-sm font-medium\">Risk Level</div>
|
|
<Badge className={
|
|
currentPosition.riskLevel === 'LOW' ? 'bg-green-100 text-green-800' :
|
|
currentPosition.riskLevel === 'MEDIUM' ? 'bg-yellow-100 text-yellow-800' :
|
|
'bg-red-100 text-red-800'
|
|
}>
|
|
{currentPosition.riskLevel}
|
|
</Badge>
|
|
<div className=\"text-xs text-muted-foreground mt-1\">
|
|
{currentPosition.distanceFromStopLoss}% from SL
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Footer */}
|
|
<div className=\"text-center text-xs text-muted-foreground\">
|
|
Last updated: {new Date(analytics.generated).toLocaleString()}
|
|
• Auto-refreshes every 30 seconds
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|