Spaces:
Running
Running
File size: 5,568 Bytes
097168d 6340f89 097168d 6340f89 9c7fae0 097168d 9c7fae0 d8c7fa5 9c7fae0 d8c7fa5 9c7fae0 d8c7fa5 9c7fae0 d8c7fa5 097168d 9c7fae0 097168d 9c7fae0 efd7324 9c7fae0 097168d 9c7fae0 097168d 6340f89 097168d 6340f89 097168d efd7324 9c7fae0 097168d efd7324 d0a6c56 9c7fae0 efd7324 9c7fae0 efd7324 9c7fae0 efd7324 9c7fae0 efd7324 9c7fae0 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 90 91 92 93 94 95 96 97 98 99 100 101 |
<!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; padding: 1rem; }
.container { max-width: 1280px; margin: 0 auto; }
header { text-align: center; margin-bottom: 1.5rem; }
h1 { color: #38BDF8; margin-bottom: 0; }
.status-bar { font-size: 12px; color: #9CA3AF; text-align: right; margin-bottom: 1rem; }
.status-online { color: #34D399; } .status-offline { color: #F87171; }
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; }
tbody tr { animation: fadeIn 0.5s ease-out; }
@keyframes fadeIn { from { background-color: rgba(52, 211, 153, 0.2); } to { background-color: transparent; } }
.asset-cell { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: center; }
.asset-logo { width: 24px; height: 24px; }
.asset-cell a { color: inherit; text-decoration: none; font-weight: bold; }
</style>
</head>
<body>
<main class="container">
<header><h1>Sentinel Arbitrage Engine</h1></header>
<div class="status-bar">
Engine Status: <span id="status-light" class="status-offline">OFFLINE</span> | Last Signal: <span id="last-update-time">--:--:--</span>
</div>
<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;">Connecting to engine...</td></tr>
</tbody>
</table>
</article>
</main>
<script>
const ASSET_CONFIG = {
'BTC': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png' },
'ETH': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/1027.png' },
'SOL': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/5426.png' },
'XRP': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/52.png' },
'DOGE': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/74.png' },
'ADA': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/2010.png' },
'AVAX': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/5805.png' },
'LINK': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/1975.png' },
'DOT': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/6636.png' },
'MATIC': { logo: 'https://s2.coinmarketcap.com/static/img/coins/64x64/3890.png' }
};
const socket = io();
const statusLight = document.getElementById('status-light');
const opportunitiesTable = document.getElementById('opportunities-table');
const lastUpdateTime = document.getElementById('last-update-time');
socket.on('connect', () => {
statusLight.textContent = 'ONLINE';
statusLight.className = 'status-online';
document.getElementById('placeholder-row').cells[0].textContent = 'Monitoring for oracle dislocations...';
});
socket.on('disconnect', () => {
statusLight.textContent = 'OFFLINE';
statusLight.className = 'status-offline';
});
socket.on('new_signal', (signal) => {
document.getElementById('placeholder-row')?.remove();
const newRow = opportunitiesTable.insertRow(0);
const assetInfo = ASSET_CONFIG[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>
`;
lastUpdateTime.textContent = new Date(signal.timestamp).toLocaleTimeString('en-US');
});
</script>
</body>
</html> |