<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OpenClaw 交易仪表盘</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: #0f1419;
            color: #e6e6e6;
            line-height: 1.6;
        }

        .header {
            background: linear-gradient(135deg, #1a2332 0%, #0f1419 100%);
            padding: 20px 30px;
            border-bottom: 1px solid #2d3a4a;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .header h1 {
            font-size: 24px;
            font-weight: 600;
            color: #00d4aa;
        }

        .connection-status {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 14px;
        }

        .status-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #ff4444;
            transition: background 0.3s;
        }

        .status-dot.connected {
            background: #00d4aa;
            box-shadow: 0 0 10px #00d4aa;
        }

        .container {
            padding: 20px 30px;
            max-width: 1400px;
            margin: 0 auto;
        }

        .metrics-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 25px;
        }

        .metric-card {
            background: #1a2332;
            border-radius: 12px;
            padding: 20px;
            border: 1px solid #2d3a4a;
            transition: transform 0.2s, border-color 0.2s;
        }

        .metric-card:hover {
            transform: translateY(-2px);
            border-color: #00d4aa;
        }

        .metric-label {
            font-size: 12px;
            color: #8b9aad;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            margin-bottom: 8px;
        }

        .metric-value {
            font-size: 28px;
            font-weight: 700;
            color: #fff;
        }

        .metric-change {
            font-size: 14px;
            margin-top: 5px;
        }

        .positive { color: #00d4aa; }
        .negative { color: #ff4444; }

        .dashboard-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
            gap: 20px;
            margin-bottom: 25px;
        }

        .card {
            background: #1a2332;
            border-radius: 12px;
            border: 1px solid #2d3a4a;
            overflow: hidden;
        }

        .card-header {
            background: #222d3d;
            padding: 15px 20px;
            border-bottom: 1px solid #2d3a4a;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .card-title {
            font-size: 16px;
            font-weight: 600;
            color: #fff;
        }

        .card-body {
            padding: 20px;
        }

        .chart-container {
            position: relative;
            height: 300px;
        }

        .agents-table {
            width: 100%;
            border-collapse: collapse;
        }

        .agents-table th,
        .agents-table td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #2d3a4a;
        }

        .agents-table th {
            background: #222d3d;
            font-size: 11px;
            text-transform: uppercase;
            color: #8b9aad;
            font-weight: 600;
        }

        .agents-table tr:hover {
            background: #222d3d;
        }

        .agent-status {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 4px 10px;
            border-radius: 20px;
            font-size: 12px;
            font-weight: 500;
        }

        .status-thriving { background: rgba(0, 212, 170, 0.15); color: #00d4aa; }
        .status-stable { background: rgba(76, 175, 80, 0.15); color: #4caf50; }
        .status-struggling { background: rgba(255, 193, 7, 0.15); color: #ffc107; }
        .status-critical { background: rgba(255, 87, 34, 0.15); color: #ff5722; }
        .status-bankrupt { background: rgba(255, 68, 68, 0.15); color: #ff4444; }

        .activity-badge {
            display: inline-block;
            padding: 3px 8px;
            border-radius: 4px;
            font-size: 11px;
            background: #2d3a4a;
            color: #8b9aad;
        }

        .trades-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 13px;
        }

        .trades-table th,
        .trades-table td {
            padding: 10px 12px;
            text-align: left;
            border-bottom: 1px solid #2d3a4a;
        }

        .trades-table th {
            background: #222d3d;
            font-size: 11px;
            text-transform: uppercase;
            color: #8b9aad;
        }

        .trades-table tr:hover {
            background: #222d3d;
        }

        .side-buy { color: #00d4aa; font-weight: 600; }
        .side-sell { color: #ff4444; font-weight: 600; }

        .alert-container {
            position: fixed;
            top: 80px;
            right: 20px;
            width: 350px;
            z-index: 1000;
        }

        .alert {
            background: #1a2332;
            border-radius: 10px;
            padding: 15px;
            margin-bottom: 10px;
            border-left: 4px solid;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
            animation: slideIn 0.3s ease-out;
        }

        @keyframes slideIn {
            from { transform: translateX(100%); opacity: 0; }
            to { transform: translateX(0); opacity: 1; }
        }

        .alert-critical { border-color: #ff4444; }
        .alert-error { border-color: #ff5722; }
        .alert-warning { border-color: #ffc107; }
        .alert-info { border-color: #00d4aa; }

        .alert-title {
            font-weight: 600;
            margin-bottom: 5px;
        }

        .alert-message {
            font-size: 13px;
            color: #8b9aad;
        }

        .alert-time {
            font-size: 11px;
            color: #5a6a7a;
            margin-top: 5px;
        }

        .two-column {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }

        @media (max-width: 1200px) {
            .two-column {
                grid-template-columns: 1fr;
            }
        }

        .progress-bar {
            width: 100%;
            height: 6px;
            background: #2d3a4a;
            border-radius: 3px;
            overflow: hidden;
            margin-top: 8px;
        }

        .progress-fill {
            height: 100%;
            border-radius: 3px;
            transition: width 0.3s;
        }

        .progress-fill.positive { background: #00d4aa; }
        .progress-fill.negative { background: #ff4444; }
    </style>
</head>
<body>
    <div class="header">
        <h1>🦀 OpenClaw 交易仪表盘</h1>
        <div class="nav-links" style="display: flex; gap: 20px; align-items: center;">
            <a href="/" style="color: #00d4aa; text-decoration: none; font-weight: 500;">仪表盘</a>
            <a href="/orders" style="color: #8b9aad; text-decoration: none; font-weight: 500;">订单</a>
            <a href="/config" style="color: #8b9aad; text-decoration: none; font-weight: 500;">配置</a>
            <a href="/exchanges" style="color: #8b9aad; text-decoration: none; font-weight: 500;">交易所</a>
        </div>
        <div class="connection-status">
            <span class="status-dot" id="statusDot"></span>
            <span id="statusText">连接中...</span>
        </div>
    </div>

    <div class="alert-container" id="alertContainer"></div>

    <div class="container">
        <!-- System Metrics -->
        <div class="metrics-grid">
            <div class="metric-card">
                <div class="metric-label">系统权益</div>
                <div class="metric-value" id="systemEquity">$0</div>
                <div class="metric-change" id="equityChange">--</div>
            </div>
            <div class="metric-card">
                <div class="metric-label">总盈亏</div>
                <div class="metric-value" id="totalPnL">$0</div>
                <div class="metric-change positive" id="pnlChange">--</div>
            </div>
            <div class="metric-card">
                <div class="metric-label">活跃 Agent</div>
                <div class="metric-value" id="activeAgents">0</div>
                <div class="metric-change" id="agentStatus">--</div>
            </div>
            <div class="metric-card">
                <div class="metric-label">总交易数</div>
                <div class="metric-value" id="totalTrades">0</div>
                <div class="metric-change" id="tradeVolume">成交量: $0</div>
            </div>
            <div class="metric-card">
                <div class="metric-label">平均胜率</div>
                <div class="metric-value" id="avgWinRate">0%</div>
                <div class="progress-bar">
                    <div class="progress-fill positive" id="winRateBar" style="width: 0%"></div>
                </div>
            </div>
        </div>

        <!-- Charts Row -->
        <div class="dashboard-grid">
            <div class="card">
                <div class="card-header">
                    <span class="card-title">📈 权益曲线</span>
                </div>
                <div class="card-body">
                    <div class="chart-container">
                        <canvas id="equityChart"></canvas>
                    </div>
                </div>
            </div>
            <div class="card">
                <div class="card-header">
                    <span class="card-title">📊 盈亏分布</span>
                </div>
                <div class="card-body">
                    <div class="chart-container">
                        <canvas id="pnlChart"></canvas>
                    </div>
                </div>
            </div>
        </div>

        <!-- Agent Status & Recent Trades -->
        <div class="two-column">
            <div class="card">
                <div class="card-header">
                    <span class="card-title">🤖 Agent 状态</span>
                </div>
                <div class="card-body" style="padding: 0;">
                    <table class="agents-table">
                        <thead>
                            <tr>
                                <th>Agent</th>
                                <th>状态</th>
                                <th>余额</th>
                                <th>胜率</th>
                                <th>交易数</th>
                            </tr>
                        </thead>
                        <tbody id="agentsTableBody">
                        </tbody>
                    </table>
                </div>
            </div>
            <div class="card">
                <div class="card-header">
                    <span class="card-title">💰 最近交易</span>
                </div>
                <div class="card-body" style="padding: 0;">
                    <table class="trades-table">
                        <thead>
                            <tr>
                                <th>时间</th>
                                <th>Agent</th>
                                <th>交易对</th>
                                <th>方向</th>
                                <th>盈亏</th>
                            </tr>
                        </thead>
                        <tbody id="tradesTableBody">
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <script>
        // WebSocket connection
        let ws = null;
        let reconnectInterval = 3000;
        let charts = {};
        let lastAlerts = new Set();

        // Initialize charts
        function initCharts() {
            // Equity Curve Chart
            const equityCtx = document.getElementById('equityChart').getContext('2d');
            charts.equity = new Chart(equityCtx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [{
                        label: '总资产',
                        data: [],
                        borderColor: '#00d4aa',
                        backgroundColor: 'rgba(0, 212, 170, 0.1)',
                        borderWidth: 2,
                        fill: true,
                        tension: 0.4,
                        pointRadius: 0,
                        pointHoverRadius: 6,
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    interaction: {
                        mode: 'index',
                        intersect: false,
                    },
                    plugins: {
                        legend: { display: false },
                    },
                    scales: {
                        x: {
                            grid: { color: '#2d3a4a' },
                            ticks: { color: '#8b9aad', maxTicksLimit: 6 }
                        },
                        y: {
                            grid: { color: '#2d3a4a' },
                            ticks: {
                                color: '#8b9aad',
                                callback: function(value) {
                                    return '$' + value.toLocaleString();
                                }
                            }
                        }
                    }
                }
            });

            // P&L Distribution Chart
            const pnlCtx = document.getElementById('pnlChart').getContext('2d');
            charts.pnl = new Chart(pnlCtx, {
                type: 'bar',
                data: {
                    labels: [],
                    datasets: [
                        {
                            label: '盈利',
                            data: [],
                            backgroundColor: '#00d4aa',
                            borderRadius: 4,
                        },
                        {
                            label: '亏损',
                            data: [],
                            backgroundColor: '#ff4444',
                            borderRadius: 4,
                        }
                    ]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'top',
                            labels: { color: '#e6e6e6' }
                        },
                    },
                    scales: {
                        x: {
                            grid: { display: false },
                            ticks: { color: '#8b9aad' }
                        },
                        y: {
                            grid: { color: '#2d3a4a' },
                            ticks: { color: '#8b9aad' }
                        }
                    }
                }
            });
        }

        // Format currency
        function formatCurrency(value) {
            return '$' + value.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
        }

        // Format time
        function formatTime(timestamp) {
            const date = new Date(timestamp);
            return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        }

        // Get status class
        function getStatusClass(status) {
            if (status.includes('thriving')) return 'status-thriving';
            if (status.includes('stable')) return 'status-stable';
            if (status.includes('struggling')) return 'status-struggling';
            if (status.includes('critical')) return 'status-critical';
            return 'status-bankrupt';
        }

        // Create element helper
        function createElement(tag, className, text) {
            const el = document.createElement(tag);
            if (className) el.className = className;
            if (text) el.textContent = text;
            return el;
        }

        // Update metrics
        function updateMetrics(metrics) {
            document.getElementById('systemEquity').textContent = formatCurrency(metrics.system_equity);
            document.getElementById('totalPnL').textContent = formatCurrency(metrics.total_pnl);
            document.getElementById('totalPnL').className = 'metric-value ' + (metrics.total_pnl >= 0 ? 'positive' : 'negative');
            document.getElementById('activeAgents').textContent = `${metrics.active_agents}/${metrics.total_agents}`;
            document.getElementById('totalTrades').textContent = metrics.total_trades.toLocaleString();
            document.getElementById('tradeVolume').textContent = `Vol: ${formatCurrency(metrics.total_volume)}`;
            document.getElementById('avgWinRate').textContent = (metrics.avg_win_rate * 100).toFixed(1) + '%';
            document.getElementById('winRateBar').style.width = (metrics.avg_win_rate * 100) + '%';

            const pnlChange = document.getElementById('pnlChange');
            pnlChange.textContent = (metrics.total_pnl >= 0 ? '+' : '') + formatCurrency(metrics.total_pnl);
            pnlChange.className = 'metric-change ' + (metrics.total_pnl >= 0 ? 'positive' : 'negative');

            document.getElementById('agentStatus').textContent = `${metrics.bankrupt_agents} 破产`;
        }

        // Update agents table
        function updateAgentsTable(agents) {
            const tbody = document.getElementById('agentsTableBody');
            tbody.innerHTML = '';
            agents.forEach(agent => {
                const row = createElement('tr');

                const agentCell = createElement('td');
                const agentName = createElement('strong', '', agent.agent_id);
                agentCell.appendChild(agentName);
                if (agent.current_activity) {
                    const badge = createElement('span', 'activity-badge', agent.current_activity);
                    agentCell.appendChild(document.createTextNode(' '));
                    agentCell.appendChild(badge);
                }
                row.appendChild(agentCell);

                const statusCell = createElement('td');
                const statusSpan = createElement('span', 'agent-status ' + getStatusClass(agent.status), agent.status);
                statusCell.appendChild(statusSpan);
                row.appendChild(statusCell);

                const balanceCell = createElement('td');
                const balanceValue = createElement('strong', '', formatCurrency(agent.balance));
                balanceCell.appendChild(balanceValue);
                const pnlDiv = createElement('div', '', (agent.profit_loss >= 0 ? '+' : '') + formatCurrency(agent.profit_loss || (agent.balance - agent.initial_capital)));
                pnlDiv.style.cssText = 'font-size: 11px; color: #8b9aad;';
                balanceCell.appendChild(pnlDiv);
                row.appendChild(balanceCell);

                const winRateCell = createElement('td', '', (agent.win_rate * 100).toFixed(1) + '%');
                row.appendChild(winRateCell);

                const tradesCell = createElement('td', '', agent.total_trades.toString());
                row.appendChild(tradesCell);

                tbody.appendChild(row);
            });
        }

        // Update trades table
        function updateTradesTable(trades) {
            const tbody = document.getElementById('tradesTableBody');
            tbody.innerHTML = '';
            trades.forEach(trade => {
                const row = createElement('tr');

                row.appendChild(createElement('td', '', formatTime(trade.timestamp)));
                row.appendChild(createElement('td', '', trade.agent_id));

                const symbolCell = createElement('td');
                const symbolStrong = createElement('strong', '', trade.symbol);
                symbolCell.appendChild(symbolStrong);
                row.appendChild(symbolCell);

                const sideCell = createElement('td', 'side-' + trade.side, trade.side.toUpperCase());
                row.appendChild(sideCell);

                const pnlCell = createElement('td', trade.pnl >= 0 ? 'positive' : 'negative',
                    (trade.pnl >= 0 ? '+' : '') + formatCurrency(trade.pnl));
                row.appendChild(pnlCell);

                tbody.appendChild(row);
            });
        }

        // Update charts
        function updateCharts(data) {
            if (data.equity_curve) {
                charts.equity.data.labels = data.equity_curve.map(p => formatTime(p.timestamp));
                charts.equity.data.datasets[0].data = data.equity_curve.map(p => p.equity);
                charts.equity.update('none');
            }

            if (data.pnl_distribution) {
                charts.pnl.data.labels = data.pnl_distribution.bins;
                charts.pnl.data.datasets[0].data = data.pnl_distribution.wins;
                charts.pnl.data.datasets[1].data = data.pnl_distribution.losses;
                charts.pnl.update('none');
            }
        }

        // Show alert
        function showAlert(alert) {
            if (lastAlerts.has(alert.alert_id)) return;
            lastAlerts.add(alert.alert_id);

            const container = document.getElementById('alertContainer');
            const alertDiv = createElement('div', 'alert alert-' + alert.level);

            const titleDiv = createElement('div', 'alert-title', alert.title);
            const messageDiv = createElement('div', 'alert-message', alert.message);
            const timeDiv = createElement('div', 'alert-time', formatTime(alert.timestamp));

            alertDiv.appendChild(titleDiv);
            alertDiv.appendChild(messageDiv);
            alertDiv.appendChild(timeDiv);
            container.appendChild(alertDiv);

            // Auto remove after 10 seconds for non-critical alerts
            if (alert.level !== 'critical') {
                setTimeout(() => {
                    alertDiv.style.animation = 'slideIn 0.3s ease-out reverse';
                    setTimeout(() => alertDiv.remove(), 300);
                }, 10000);
            }
        }

        // Update connection status
        function updateStatus(connected) {
            const dot = document.getElementById('statusDot');
            const text = document.getElementById('statusText');
            if (connected) {
                dot.classList.add('connected');
                text.textContent = '实时';
            } else {
                dot.classList.remove('connected');
                text.textContent = '已断开';
            }
        }

        // Connect WebSocket
        function connectWebSocket() {
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            const wsUrl = `${protocol}//${window.location.host}/ws`;

            ws = new WebSocket(wsUrl);

            ws.onopen = () => {
                console.log('WebSocket connected');
                updateStatus(true);
            };

            ws.onmessage = (event) => {
                const message = JSON.parse(event.data);
                handleMessage(message);
            };

            ws.onclose = () => {
                console.log('WebSocket disconnected');
                updateStatus(false);
                setTimeout(connectWebSocket, reconnectInterval);
            };

            ws.onerror = (error) => {
                console.error('WebSocket error:', error);
            };
        }

        // Handle incoming messages
        function handleMessage(message) {
            switch (message.type) {
                case 'initial_state':
                    updateMetrics(message.data.metrics);
                    updateAgentsTable(message.data.agents);
                    updateTradesTable(message.data.recent_trades);
                    updateCharts(message.data);
                    if (message.data.alerts) {
                        message.data.alerts.forEach(showAlert);
                    }
                    break;
                case 'state_update':
                    updateMetrics(message.data.metrics);
                    updateAgentsTable(message.data.agents);
                    updateTradesTable(message.data.recent_trades);
                    break;
                case 'alert':
                    showAlert(message.data);
                    break;
            }
        }

        // Initialize
        document.addEventListener('DOMContentLoaded', () => {
            initCharts();
            connectWebSocket();
        });
    </script>
</body>
</html>
