File size: 5,509 Bytes
097168d
6340f89
097168d
 
 
 
6340f89
8c8b0ba
097168d
9c7fae0
8c8b0ba
 
 
 
d8c7fa5
9c7fae0
d8c7fa5
9c7fae0
 
8c8b0ba
 
 
 
 
9c7fae0
 
097168d
 
9c7fae0
097168d
9c7fae0
8c8b0ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
097168d
 
3e3365d
efd7324
8c8b0ba
 
efd7324
8c8b0ba
efd7324
8c8b0ba
 
3e3365d
8c8b0ba
9c7fae0
8c8b0ba
 
efd7324
3e3365d
efd7324
9c7fae0
8c8b0ba
 
efd7324
 
 
 
8c8b0ba
3e3365d
8c8b0ba
 
 
 
 
 
 
 
efd7324
097168d
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sentinel Arbitrage Engine</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
    <style>
        :root { font-family: 'SF Mono', 'Consolas', 'Menlo', monospace; }
        body { background-color: #111927; color: #E5E7EB; font-size: 14px; margin: 0; }
        .container { max-width: 1400px; margin: 1rem auto; padding: 0 1rem; }
        header h1 { color: #38BDF8; margin-bottom: 0; text-align: center; }
        .grid-container { display: grid; grid-template-columns: 2fr 1fr; gap: 1.5rem; }
        table { width: 100%; border-collapse: collapse; }
        th { background-color: #374151; text-align: left; position: sticky; top: 0; }
        td, th { padding: 0.75rem 1rem; border-bottom: 1px solid #374151; vertical-align: middle; }
        .buy { color: #34D399; } .sell { color: #F87171; }
        .risk-low { color: #34D399; } .risk-medium { color: #FBBF24; } .risk-high { color: #F87171; }
        .log-panel { background-color: #1F2937; border: 1px solid #374151; border-radius: 0.5rem; height: 60vh; overflow-y: scroll; font-size: 12px; padding: 1rem; }
        .log-panel p { margin: 0; padding: 0.25rem 0; border-bottom: 1px solid #2b3544; }
        .log-panel p:first-child { color: #38BDF8; font-weight: bold; }
        .asset-cell { display: flex; align-items: center; gap: 10px; }
        .asset-logo { width: 24px; height: 24px; }
        tbody tr { animation: fadeIn 0.5s ease-out; }
        @keyframes fadeIn { from { background-color: rgba(52, 211, 153, 0.2); } to { background-color: transparent; } }
    </style>
</head>
<body>
    <main class="container">
        <header><h1>Sentinel Arbitrage Engine</h1></header>
        <div class="grid-container">
            <section id="signal-section">
                <article>
                    <table>
                        <thead><tr><th>Pair</th><th>Oracle 1 (Pyth)</th><th>Oracle 2 (Agg.)</th><th>Discrepancy</th><th>AI Risk</th><th>AI Strategy</th></tr></thead>
                        <tbody id="opportunities-table">
                            <tr id="placeholder-row"><td colspan="6" style="text-align:center; padding: 2rem;">Awaiting first signal...</td></tr>
                        </tbody>
                    </table>
                </article>
            </section>
            <section id="log-section">
                <article>
                    <header>Live Engine Logs</header>
                    <div class="log-panel" id="log-container">
                        <p>Connecting to engine...</p>
                    </div>
                </article>
            </section>
        </div>
    </main>

    <script>
        const ASSET_LOGOS = {'BTC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png','ETH': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1027.png','SOL': 'https://s2.coinmarketcap.com/static/img/coins/64x64/5426.png','XRP': 'https://s2.coinmarketcap.com/static/img/coins/64x64/52.png','DOGE': 'https://s2.coinmarketcap.com/static/img/coins/64x64/74.png','ADA': 'https://s2.coinmarketcap.com/static/img/coins/64x64/2010.png','AVAX': 'https://s2.coinmarketcap.com/static/img/coins/64x64/5805.png','LINK': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1975.png','DOT': 'https://s2.coinmarketcap.com/static/img/coins/64x64/6636.png','MATIC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/3890.png'};
        const socket = io();
        const opportunitiesTable = document.getElementById('opportunities-table');
        const logContainer = document.getElementById('log-container');

        socket.on('connect', () => { logContainer.innerHTML = '<p>βœ… Connection Established. Engine is starting...</p>'; });
        socket.on('disconnect', () => { addLog('πŸ”₯ Engine Disconnected.'); });

        socket.on('log_message', (msg) => { addLog(msg); });

        socket.on('new_signal', (signal) => {
            document.getElementById('placeholder-row')?.remove();
            const newRow = opportunitiesTable.insertRow(0);
            const assetInfo = ASSET_LOGOS[signal.asset] || { logo: '' };
            newRow.innerHTML = `
                <td><div class="asset-cell"><img src="${assetInfo.logo}" class="asset-logo"><strong>${signal.asset}/USD</strong></div></td>
                <td><span class="${signal.pyth_price < signal.chainlink_price ? 'buy' : 'sell'}">${signal.pyth_price.toLocaleString('en-US',{style:'currency',currency:'USD'})}</span></td>
                <td><span class="${signal.pyth_price > signal.chainlink_price ? 'buy' : 'sell'}">${signal.chainlink_price.toLocaleString('en-US',{style:'currency',currency:'USD'})}</span></td>
                <td><strong>${signal.spread_pct.toFixed(3)}%</strong></td>
                <td><span class="risk-${signal.risk.toLowerCase()}">${signal.risk}</span></td>
                <td>${signal.strategy}</td>
            `;
        });

        function addLog(message) {
            const logEntry = document.createElement('p');
            logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
            logContainer.prepend(logEntry);
            if (logContainer.children.length > 100) {
                logContainer.lastChild.remove();
            }
        }
    </script>
</body>
</html>