| <!DOCTYPE html>
|
| <html lang="ru">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>CR1 WHALE TERMINAL PRO</title>
|
|
|
| <link rel="preconnect" href="https://fonts.googleapis.com">
|
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
|
| <style>
|
| :root {
|
| --bg-main: #020204;
|
| --bg-card: rgba(10, 14, 26, 0.45);
|
| --bg-card-hover: rgba(14, 20, 38, 0.6);
|
| --neon-cyan: #06b6d4;
|
| --neon-purple: #a855f7;
|
| --neon-green: #10b981;
|
| --neon-red: #f43f5e;
|
| --neon-gold: #f59e0b;
|
| --text-main: #f3f4f6;
|
| --text-muted: #6b7280;
|
| --border-glow: rgba(168, 85, 247, 0.2);
|
| }
|
|
|
| *, *::before, *::after {
|
| box-sizing: border-box;
|
| }
|
|
|
| body {
|
| background-color: var(--bg-main);
|
| /* Мягкий виньетный градиент для фокуса по центру */
|
| background-image:
|
| radial-gradient(circle at center, rgba(2, 2, 4, 0.2) 0%, rgba(2, 2, 4, 0.8) 100%),
|
| url('space-bg.webp');
|
| background-size: cover;
|
| background-position: center center;
|
| background-attachment: fixed;
|
| background-repeat: no-repeat;
|
|
|
| color: var(--text-main);
|
| font-family: 'Inter', system-ui, sans-serif;
|
| margin: 0;
|
| padding: 25px;
|
| overflow-x: hidden;
|
| min-height: 100vh;
|
| }
|
|
|
| /* ТЕМАТИЧЕСКИЙ ЛОГОТИП И ШАПКА */
|
| header {
|
| display: flex;
|
| justify-content: space-between;
|
| align-items: center;
|
| margin-bottom: 25px;
|
| background: var(--bg-card);
|
| padding: 15px 25px;
|
| border-radius: 16px;
|
| border: 1px solid rgba(255, 255, 255, 0.05);
|
| backdrop-filter: blur(20px);
|
| -webkit-backdrop-filter: blur(20px);
|
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255,255,255,0.05);
|
| }
|
|
|
| .brand-wrapper {
|
| display: flex;
|
| align-items: center;
|
| gap: 15px;
|
| }
|
|
|
| /* Премиальный SVG Логотип (Плавник Кита + Радар) */
|
| .logo-svg {
|
| width: 32px;
|
| height: 32px;
|
| fill: none;
|
| stroke: url(#logoGradient);
|
| stroke-width: 2.5;
|
| filter: drop-shadow(0 0 8px rgba(6, 182, 212, 0.6));
|
| }
|
|
|
| h1 {
|
| margin: 0;
|
| font-size: 20px;
|
| font-weight: 800;
|
| letter-spacing: 0.07em;
|
| background: linear-gradient(135deg, #ffffff 30%, var(--neon-cyan) 70%, var(--neon-purple) 100%);
|
| -webkit-background-clip: text;
|
| -webkit-text-fill-color: transparent;
|
| }
|
|
|
| /* ПУЛЬСИРУЮЩИЙ БАДЖ STATUS */
|
| .status-container {
|
| display: flex;
|
| align-items: center;
|
| gap: 15px;
|
| }
|
|
|
| .status-badge {
|
| font-weight: 700;
|
| background: rgba(0, 0, 0, 0.5);
|
| border: 1px solid rgba(244, 63, 94, 0.2);
|
| padding: 8px 16px;
|
| border-radius: 8px;
|
| font-size: 11px;
|
| color: var(--neon-red);
|
| letter-spacing: 0.1em;
|
| display: flex;
|
| align-items: center;
|
| gap: 8px;
|
| }
|
|
|
| .status-dot {
|
| width: 6px;
|
| height: 6px;
|
| background-color: var(--neon-red);
|
| border-radius: 50%;
|
| }
|
|
|
| /* Когда статус ONLINE — включаем анимацию пульсации */
|
| .status-online {
|
| color: var(--neon-green) !important;
|
| border-color: rgba(16, 185, 129, 0.3) !important;
|
| }
|
| .status-online .status-dot {
|
| background-color: var(--neon-green);
|
| box-shadow: 0 0 10px var(--neon-green);
|
| animation: pulseGlow 2s infinite;
|
| }
|
|
|
| @keyframes pulseGlow {
|
| 0% { transform: scale(1); opacity: 1; }
|
| 50% { transform: scale(1.3); opacity: 0.5; box-shadow: 0 0 16px var(--neon-green); }
|
| 100% { transform: scale(1); opacity: 1; }
|
| }
|
|
|
| .btn-test {
|
| background: rgba(168, 85, 247, 0.1);
|
| color: var(--neon-purple);
|
| border: 1px solid rgba(168, 85, 247, 0.4);
|
| padding: 8px 18px;
|
| border-radius: 8px;
|
| cursor: pointer;
|
| font-weight: 700;
|
| font-size: 11px;
|
| letter-spacing: 0.05em;
|
| transition: all 0.3s ease;
|
| }
|
| .btn-test:hover {
|
| background: var(--neon-purple);
|
| color: #fff;
|
| box-shadow: 0 0 20px rgba(168, 85, 247, 0.5);
|
| }
|
|
|
| /* МОДУЛЬНЫЙ ТЕРМИНАЛЬНЫЙ ЛЕЙАУТ */
|
| .terminal-workspace {
|
| display: grid;
|
| grid-template-columns: 360px 1fr;
|
| gap: 25px;
|
| width: 100%;
|
| }
|
|
|
| .workspace-left {
|
| display: flex;
|
| flex-direction: column;
|
| gap: 25px;
|
| }
|
|
|
| .workspace-right {
|
| display: flex;
|
| flex-direction: column;
|
| gap: 25px;
|
| }
|
|
|
| .grid-tables {
|
| display: grid;
|
| grid-template-columns: 1fr 1fr 1fr;
|
| gap: 25px;
|
| }
|
|
|
| /* ПРЕМИАЛЬНАЯ СТЕКЛЯННАЯ КАРТОЧКА */
|
| .card {
|
| background: var(--bg-card);
|
| backdrop-filter: blur(20px);
|
| -webkit-backdrop-filter: blur(20px);
|
| border-radius: 16px;
|
| border: 1px solid rgba(255, 255, 255, 0.04);
|
| padding: 20px;
|
| box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.02);
|
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| }
|
| .card:hover {
|
| border-color: rgba(255, 255, 255, 0.08);
|
| background: var(--bg-card-hover);
|
| box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
|
| }
|
|
|
| .card h2 {
|
| margin-top: 0;
|
| font-size: 11px;
|
| text-transform: uppercase;
|
| color: #9ca3af;
|
| letter-spacing: 0.15em;
|
| font-weight: 800;
|
| margin-bottom: 20px;
|
| border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
| padding-bottom: 12px;
|
| display: flex;
|
| justify-content: space-between;
|
| align-items: center;
|
| }
|
|
|
| /* ТАБЛИЦЫ С МОНОШИРИННЫМИ ЦИФРАМИ */
|
| table { width: 100%; border-collapse: collapse; }
|
| th {
|
| color: var(--text-muted);
|
| padding: 10px 8px;
|
| text-align: left;
|
| font-size: 10px;
|
| font-weight: 700;
|
| text-transform: uppercase;
|
| letter-spacing: 0.08em;
|
| }
|
| td {
|
| padding: 12px 8px;
|
| font-size: 13px;
|
| border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
| font-family: 'JetBrains Mono', monospace; /* Идеальные ровные колонки цифр */
|
| }
|
|
|
| .row-ice { color: var(--neon-green); font-weight: 700; background: rgba(16, 185, 129, 0.04); transition: all 0.5s ease; }
|
|
|
| .badge-round { background: rgba(0, 0, 0, 0.4); color: #e5e7eb; padding: 4px 8px; border-radius: 6px; font-weight: 700; border: 1px solid rgba(255,255,255,0.06); }
|
| .badge-status { background: rgba(6, 182, 212, 0.12); color: var(--neon-cyan); padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: 700; border: 1px solid rgba(6,182,212,0.2); }
|
| .badge-super { background: rgba(244, 63, 94, 0.12); color: var(--neon-red); padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: 800; border: 1px solid rgba(244,63,94,0.25); box-shadow: 0 0 15px rgba(244, 63, 94, 0.1); }
|
|
|
| /* ВВОД ДАННЫХ И СТАТИСТИКА КИТА */
|
| .input-box-custom { background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.04); border-radius: 12px; padding: 14px; margin-bottom: 15px; }
|
| label { display: block; font-size: 10px; color: var(--text-muted); margin-bottom: 8px; font-weight: 700; letter-spacing: 0.1em; }
|
| input { background: rgba(0, 0, 0, 0.6); border: 1px solid rgba(255, 255, 255, 0.08); color: #fff; padding: 12px; border-radius: 8px; width: 100%; font-size: 14px; font-family: 'JetBrains Mono', monospace; outline: none; transition: border-color 0.3s; }
|
| input:focus { border-color: var(--neon-purple); box-shadow: 0 0 15px rgba(168, 85, 247, 0.15); }
|
|
|
| .btn-action { background: linear-gradient(135deg, #a855f7 0%, #7e22ce 100%); color: #fff; border: none; padding: 14px; border-radius: 8px; cursor: pointer; font-weight: 700; width: 100%; letter-spacing: 0.05em; transition: transform 0.2s, box-shadow 0.2s; }
|
| .btn-action:hover { transform: translateY(-1px); box-shadow: 0 6px 20px rgba(168, 85, 247, 0.3); }
|
| .btn-clear { background: transparent; color: #4b5563; border: 1px solid rgba(255, 255, 255, 0.04); margin-top: 10px; font-size: 10px; padding: 8px; border-radius: 8px; cursor: pointer; width: 100%; text-transform: uppercase; letter-spacing: 0.05em; transition: all 0.2s; }
|
| .btn-clear:hover { color: var(--neon-red); border-color: rgba(244, 63, 94, 0.2); background: rgba(244, 63, 94, 0.02); }
|
|
|
| /* МИНИ-МЕТРИКИ КИТА */
|
| .pnl-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px; }
|
| .stat-box { background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.03); padding: 15px; border-radius: 12px; text-align: center; border-left: 3px solid #374151; }
|
| .stat-box.blue { border-left-color: var(--neon-cyan); }
|
| .stat-box.orange { border-left-color: var(--neon-gold); }
|
| .stat-val { font-size: 20px; font-weight: 700; margin-top: 6px; font-family: 'JetBrains Mono', monospace; }
|
|
|
| /* КОНТЕЙНЕР ДЛЯ ТРЕЙДИНГВЬЮ ГРАФИКА */
|
| #chartWrapper {
|
| width: 100%;
|
| height: 420px;
|
| background: rgba(0, 0, 0, 0.4);
|
| border-radius: 16px;
|
| border: 1px solid rgba(255, 255, 255, 0.04);
|
| position: relative;
|
| overflow: hidden;
|
| box-shadow: inset 0 0 30px rgba(0,0,0,0.6);
|
| }
|
| </style>
|
| </head>
|
| <body>
|
|
|
| <svg style="display: none;">
|
| <defs>
|
| <linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
| <stop offset="0%" stop-color="#06b6d4" />
|
| <stop offset="100%" stop-color="#a855f7" />
|
| </linearGradient>
|
| </defs>
|
| </svg>
|
|
|
| <header>
|
| <div class="brand-wrapper">
|
| <svg class="logo-svg" viewBox="0 0 24 24">
|
| <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
| </svg>
|
| <h1>CR1 ALGO-RADAR <span style="font-size: 11px; color: var(--text-muted); font-weight: 400; margin-left: 10px; letter-spacing: 0;">QUANTUM LAB</span></h1>
|
| </div>
|
| <div class="status-container">
|
| <button class="btn-test" onclick="simulateLiveFeed()">🧪 Имитировать Айсберг Юаня</button>
|
| <span class="status-badge" id="netStatus"><div class="status-dot"></div>DISCONNECTED</span>
|
| </div>
|
| </header>
|
|
|
| <div class="terminal-workspace">
|
|
|
| <div class="workspace-left">
|
| <div class="card" style="border: 1px solid var(--border-glow);">
|
| <h2>🐋 ТРЕКЕР КИТА (CR1 / Si)</h2>
|
|
|
| <div class="pnl-stats">
|
| <div class="stat-box blue"><label>Объем сессии</label><div class="stat-val" id="totalVol" style="color: var(--neon-cyan);">0</div></div>
|
| <div class="stat-box orange"><label>AVG Цена входа</label><div class="stat-val" id="avgPrice" style="color: var(--neon-gold);">-</div></div>
|
| </div>
|
|
|
| <div class="input-box-custom">
|
| <label>ЦЕНА ОЧЕРЕДНОГО АЙСБЕРГА / СТУПЕНИ</label>
|
| <input type="number" id="whalePrice" value="12.265" step="0.001">
|
| </div>
|
|
|
| <div class="input-box-custom">
|
| <label>НАКОПЛЕННЫЙ ОБЪЕМ (ЛОТЫ)</label>
|
| <input type="number" id="whaleVolume" value="85000">
|
| </div>
|
|
|
| <button class="btn-action" onclick="sendWhaleTrade()">Внести пачку в SQLite</button>
|
| <button class="btn-clear" onclick="clearWhalePosition()">Сбросить историю входов</button>
|
| </div>
|
|
|
| <div class="card">
|
| <h2>📊 РЕЗУЛЬТАТ СТРАТЕГИИ</h2>
|
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
| <span style="font-size: 12px; color: var(--text-muted);">Текущий фьючерс Cr:</span>
|
| <span id="marketPrice" style="font-family: 'JetBrains Mono'; font-weight: 700;">12.315</span>
|
| </div>
|
| <div style="display: flex; justify-content: space-between; align-items: center;">
|
| <span style="font-size: 12px; color: var(--text-muted);">Текущий PnL позиции:</span>
|
| <span id="pnlStatus" style="font-family: 'JetBrains Mono'; font-weight: 800; font-size: 18px;">0 п.</span>
|
| </div>
|
| </div>
|
| </div>
|
|
|
| <div class="workspace-right">
|
|
|
| <div class="grid-tables">
|
| <div class="card">
|
| <h2>🔥 HFT-ПОТОК РОБОТОВ <span style="color: var(--neon-cyan);">●</span></h2>
|
| <table id="volumeTable">
|
| <thead><tr><th>Время</th><th>Инструмент</th><th>Объем</th><th>Дельта ОИ</th></tr></thead>
|
| <tbody>
|
| <tr><td>13:40:12</td><td>CR-6.26</td><td style="color:var(--neon-cyan)">1 450</td><td>+210</td></tr>
|
| <tr><td>13:40:15</td><td>CR-6.26</td><td style="color:var(--neon-cyan)">890</td><td>-40</td></tr>
|
| </tbody>
|
| </table>
|
| </div>
|
|
|
| <div class="card">
|
| <h2>🧊 ЖУРНАЛ АЙСБЕРГОВ (Cr1) <span style="color: var(--neon-purple);">●</span></h2>
|
| <table id="icebergTable">
|
| <thead><tr><th>Время</th><th>Цена</th><th>Скрытый Объем</th><th>Статус ОИ</th></tr></thead>
|
| <tbody></tbody>
|
| </table>
|
| </div>
|
|
|
| <div class="card">
|
| <h2>🎯 СТАКАН: КРУГЛЫЕ ПЛОТНОСТИ <span style="color: var(--neon-green);">●</span></h2>
|
| <table id="roundTable">
|
| <thead><tr><th>Уровень Cr1</th><th>Плотность</th><th>Ранг</th></tr></thead>
|
| <tbody>
|
| <tr><td><span class="badge-round">12.250</span></td><td style="color:var(--neon-green); font-weight:700;">4 250</td><td><span class="badge-status">Крупный</span></td></tr>
|
| <tr><td><span class="badge-round">12.300</span></td><td style="color:var(--neon-red); font-weight:800;">14 800</td><td><span class="badge-super">КИТ В СТАКАНЕ</span></td></tr>
|
| <tr><td><span class="badge-round">12.350</span></td><td style="color:var(--neon-green); font-weight:700;">3 110</td><td><span class="badge-status">Крупный</span></td></tr>
|
| </tbody>
|
| </table>
|
| </div>
|
| </div>
|
|
|
| <div id="chartWrapper">
|
| <div class="tradingview-widget-container" style="height:100%; width:100%;">
|
| <div id="tradingview_chart" style="height:100%; width:100%;"></div>
|
| <script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
| <script type="text/javascript">
|
| new TradingView.widget({
|
| "autosize": true,
|
| "symbol": "MOEX:CNYRUB",
|
| "interval": "1",
|
| "timezone": "Exchange",
|
| "theme": "dark",
|
| "style": "1",
|
| "locale": "ru",
|
| "toolbar_bg": "#f1f3f6",
|
| "enable_publishing": false,
|
| "hide_side_toolbar": false,
|
| "allow_symbol_change": true,
|
| "container_id": "tradingview_chart"
|
| });
|
| </script>
|
| </div>
|
| </div>
|
|
|
| </div>
|
| </div>
|
|
|
| <script>
|
| const ws = new WebSocket("ws://127.0.0.1:8000/ws");
|
|
|
| let marketPrice = 12.315;
|
| let whaleAvgPrice = 0;
|
| let totalVolume = 0;
|
|
|
| ws.onopen = () => {
|
| const badge = document.getElementById("netStatus");
|
| badge.innerHTML = '<div class="status-dot"></div>ONLINE';
|
| badge.classList.add("status-online");
|
| };
|
| ws.onclose = () => {
|
| const badge = document.getElementById("netStatus");
|
| badge.innerHTML = '<div class="status-dot"></div>DISCONNECTED';
|
| badge.classList.remove("status-online");
|
| };
|
|
|
| ws.onmessage = (event) => {
|
| const msg = JSON.parse(event.data);
|
| if (msg.type === "whale_update") {
|
| totalVolume = msg.total || 0;
|
| whaleAvgPrice = msg.avg > 100 ? parseFloat(document.getElementById('whalePrice').value) : (msg.avg || 0);
|
| let currentMarket = msg.market > 100 ? marketPrice : msg.market;
|
|
|
| document.getElementById('totalVol').innerText = totalVolume.toLocaleString();
|
| document.getElementById('avgPrice').innerText = whaleAvgPrice > 0 ? whaleAvgPrice.toFixed(3) : '-';
|
| document.getElementById('marketPrice').innerText = currentMarket.toFixed(3);
|
|
|
| let pnlBox = document.getElementById('pnlStatus');
|
| if (totalVolume === 0) {
|
| pnlBox.innerText = "0 п."; pnlBox.style.color = "var(--text-main)";
|
| return;
|
| }
|
|
|
| let diffPoints = Math.round((currentMarket - whaleAvgPrice) * 1000);
|
| if (diffPoints >= 0) {
|
| pnlBox.innerText = "+" + diffPoints + " п."; pnlBox.style.color = "var(--neon-green)";
|
| } else {
|
| pnlBox.innerText = diffPoints + " п."; pnlBox.style.color = "var(--neon-red)";
|
| }
|
| }
|
| };
|
|
|
| function sendWhaleTrade() {
|
| const p = document.getElementById('whalePrice').value;
|
| const v = document.getElementById('whaleVolume').value;
|
| if (p && v && ws.readyState === WebSocket.OPEN) {
|
| ws.send(JSON.stringify({ "type": "add_whale", "price": parseFloat(p), "vol": parseInt(v) }));
|
| } else if (ws.readyState !== WebSocket.OPEN) { alert("Нет подключения к Python-серверу!"); }
|
| }
|
|
|
| function clearWhalePosition() {
|
| if (ws.readyState === WebSocket.OPEN && confirm("Очистить базу данных SQLite?")) {
|
| ws.send(JSON.stringify({ "type": "clear_whale" }));
|
| }
|
| }
|
|
|
| function simulateLiveFeed() {
|
| const prices = ["12.260", "12.285", "12.310"];
|
| const vols = ["15,400 🧊", "22,000 🧊", "9,800 лотов"];
|
| const oiData = ["🟢 +1,250", "🟢 +3,400", "🔴 -850"];
|
|
|
| const rIdx = Math.floor(Math.random() * 3);
|
| const tbody = document.getElementById("icebergTable").getElementsByTagName('tbody')[0];
|
| const row = tbody.insertRow(0);
|
| row.className = "row-ice";
|
| row.innerHTML = `<td>${new Date().toLocaleTimeString()}</td><td><b style="color:var(--neon-purple)">${prices[rIdx]}</b></td><td>${vols[rIdx]}</td><td style="font-size:11px;">${oiData[rIdx]}</td>`;
|
|
|
| row.style.opacity = "0";
|
| setTimeout(() => { row.style.opacity = "1"; }, 50);
|
| if (tbody.rows.length > 5) tbody.deleteRow(5);
|
| }
|
| </script>
|
| </body>
|
| </html>
|