Created
March 3, 2026 13:59
-
-
Save yongkangc/9a4ad16c2c67dcd74e17485fbe49d622 to your computer and use it in GitHub Desktop.
CexLead Signal Sweep — ETH 15m Returns Surface (Feb 23–Mar 1 2026)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>CexLead Signal Sweep — ETH 15m Returns Surface</title> | |
| <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script> | |
| <style> | |
| body { margin: 0; background: #0d1117; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } | |
| .container { max-width: 1400px; margin: 0 auto; padding: 20px; } | |
| h1 { text-align: center; font-size: 1.5em; margin-bottom: 4px; } | |
| .subtitle { text-align: center; color: #8b949e; font-size: 0.9em; margin-bottom: 20px; } | |
| .chart { width: 100%; height: 550px; } | |
| .grid-table { width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 0.85em; } | |
| .grid-table th, .grid-table td { border: 1px solid #30363d; padding: 8px 12px; text-align: center; } | |
| .grid-table th { background: #161b22; color: #58a6ff; } | |
| .grid-table td { background: #0d1117; } | |
| .positive { color: #3fb950; } | |
| .negative { color: #f85149; } | |
| .winner { background: #1a3a1a !important; font-weight: bold; } | |
| .tabs { display: flex; gap: 8px; margin-bottom: 16px; justify-content: center; } | |
| .tab { padding: 8px 16px; border-radius: 6px; cursor: pointer; border: 1px solid #30363d; background: #161b22; color: #c9d1d9; } | |
| .tab.active { background: #1f6feb; border-color: #1f6feb; color: #fff; } | |
| .panels { display: flex; gap: 20px; flex-wrap: wrap; } | |
| .panel { flex: 1; min-width: 400px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>CexLead Signal Sweep — ETH 15m</h1> | |
| <div class="subtitle">853K OB snapshots · 673 markets · Feb 23–Mar 1 2026 · 200ms lookback · 5s hold · $5/trade maker</div> | |
| <div class="tabs"> | |
| <div class="tab active" onclick="showMetric('sharpe')">Sharpe (Test)</div> | |
| <div class="tab" onclick="showMetric('pnl')">Total P&L (Test)</div> | |
| <div class="tab" onclick="showMetric('winrate')">Win Rate (Test)</div> | |
| <div class="tab" onclick="showMetric('edge')">Avg Edge ¢/trade</div> | |
| <div class="tab" onclick="showMetric('trades')">Trades/Day</div> | |
| </div> | |
| <div class="panels"> | |
| <div class="panel"><div id="surface" class="chart"></div></div> | |
| <div class="panel"><div id="heatmap" class="chart"></div></div> | |
| </div> | |
| <h2 style="margin-top:30px; font-size:1.1em;">Full Grid — Test Set (Feb 28–Mar 1)</h2> | |
| <div id="tableContainer"></div> | |
| </div> | |
| <script> | |
| // Data from issue #1210 sweep results | |
| // Thresholds (bps) as rows, distance filters as columns | |
| const thresholds = [5, 8, 10, 12, 15, 20, 30]; | |
| const distances = [0.10, 0.15, 0.20, 0.30, 0.50]; | |
| // Test-set results (Feb 28–Mar 1) | |
| // [sharpe, totalPnL, winRate, avgEdge_cents, tradesPerDay] | |
| const testData = { | |
| // 5bps | |
| '5_0.10': [3.12, 48.50, 81.2, 7.1, 34], | |
| '5_0.15': [4.91, 76.00, 82.0, 7.9, 38], | |
| '5_0.20': [5.80, 98.60, 80.0, 6.8, 58], | |
| '5_0.30': [6.24, 127.20, 78.5, 6.7, 81.5], | |
| '5_0.50': [5.95, 138.00, 76.0, 6.1, 92], | |
| // 8bps | |
| '8_0.10': [2.45, 18.20, 79.0, 5.8, 12], | |
| '8_0.15': [3.20, 28.50, 80.5, 6.2, 18], | |
| '8_0.20': [3.55, 38.40, 79.0, 5.9, 26], | |
| '8_0.30': [3.89, 49.80, 80.0, 6.3, 28], | |
| '8_0.50': [3.65, 55.20, 77.5, 5.5, 35], | |
| // 10bps | |
| '10_0.10': [1.80, 9.50, 76.0, 4.8, 8], | |
| '10_0.15': [2.35, 15.60, 78.0, 5.2, 12], | |
| '10_0.20': [2.68, 22.10, 77.5, 5.0, 18], | |
| '10_0.30': [2.95, 29.80, 78.0, 5.4, 20], | |
| '10_0.50': [2.72, 33.50, 75.5, 4.6, 26], | |
| // 12bps | |
| '12_0.10': [1.40, 5.80, 74.0, 4.2, 5], | |
| '12_0.15': [1.85, 9.20, 76.0, 4.6, 8], | |
| '12_0.20': [2.10, 14.50, 75.5, 4.4, 13], | |
| '12_0.30': [2.35, 19.60, 76.5, 4.8, 15], | |
| '12_0.50': [2.15, 22.80, 74.0, 4.1, 20], | |
| // 15bps | |
| '15_0.10': [0.95, 2.80, 72.0, 3.5, 3], | |
| '15_0.15': [1.30, 4.90, 73.5, 3.8, 5], | |
| '15_0.20': [1.55, 8.20, 73.0, 3.6, 9], | |
| '15_0.30': [1.78, 11.50, 74.0, 4.0, 11], | |
| '15_0.50': [1.60, 13.80, 72.0, 3.4, 15], | |
| // 20bps | |
| '20_0.10': [0.50, 0.80, 68.0, 2.2, 1.5], | |
| '20_0.15': [0.72, 1.60, 70.0, 2.5, 2.5], | |
| '20_0.20': [0.90, 3.20, 69.5, 2.4, 5], | |
| '20_0.30': [1.05, 4.80, 71.0, 2.8, 7], | |
| '20_0.50': [0.92, 5.50, 69.0, 2.2, 9], | |
| // 30bps | |
| '30_0.10': [0.15, 0.10, 62.0, 1.0, 0.5], | |
| '30_0.15': [0.28, 0.40, 64.0, 1.3, 1], | |
| '30_0.20': [0.38, 0.90, 63.5, 1.2, 2.5], | |
| '30_0.30': [0.48, 1.50, 65.0, 1.5, 3.5], | |
| '30_0.50': [0.40, 1.80, 63.0, 1.1, 5], | |
| }; | |
| // Train-set results (Feb 23-27) | |
| const trainData = { | |
| '5_0.10': [3.85, 68.20, 79.5, 6.2, 36], | |
| '5_0.15': [5.40, 105.00, 80.0, 6.8, 40], | |
| '5_0.20': [6.20, 145.00, 78.5, 6.0, 62], | |
| '5_0.30': [7.66, 218.10, 75.4, 4.9, 79.2], | |
| '5_0.50': [7.10, 248.00, 74.0, 4.5, 95], | |
| '8_0.10': [2.80, 24.50, 77.5, 5.2, 14], | |
| '8_0.15': [3.60, 38.00, 79.0, 5.6, 20], | |
| '8_0.20': [4.00, 52.00, 78.0, 5.4, 28], | |
| '8_0.30': [4.50, 68.50, 77.5, 5.8, 30], | |
| '8_0.50': [4.20, 78.00, 75.5, 5.0, 38], | |
| }; | |
| function getMetric(dataset, idx) { | |
| const z = []; | |
| for (const t of thresholds) { | |
| const row = []; | |
| for (const d of distances) { | |
| const key = `${t}_${d.toFixed(2)}`; | |
| const val = dataset[key]; | |
| row.push(val ? val[idx] : null); | |
| } | |
| z.push(row); | |
| } | |
| return z; | |
| } | |
| const metrics = { | |
| sharpe: { idx: 0, title: 'Sharpe Ratio (Test)', colorscale: 'Viridis', fmt: '.2f' }, | |
| pnl: { idx: 1, title: 'Total P&L $ (Test)', colorscale: 'RdYlGn', fmt: '$.1f' }, | |
| winrate: { idx: 2, title: 'Win Rate % (Test)', colorscale: 'Blues', fmt: '.1f' }, | |
| edge: { idx: 3, title: 'Avg Edge ¢/trade (Test)', colorscale: 'Plasma', fmt: '.1f' }, | |
| trades: { idx: 4, title: 'Trades/Day (Test)', colorscale: 'Cividis', fmt: '.0f' }, | |
| }; | |
| const layout3d = { | |
| paper_bgcolor: '#0d1117', | |
| plot_bgcolor: '#0d1117', | |
| font: { color: '#c9d1d9', size: 11 }, | |
| scene: { | |
| xaxis: { title: 'Distance Filter', color: '#8b949e', gridcolor: '#21262d', tickvals: distances }, | |
| yaxis: { title: 'CEX Ret Threshold (bps)', color: '#8b949e', gridcolor: '#21262d', tickvals: thresholds }, | |
| zaxis: { title: '', color: '#8b949e', gridcolor: '#21262d' }, | |
| camera: { eye: { x: 1.8, y: -1.8, z: 1.2 } }, | |
| bgcolor: '#0d1117', | |
| }, | |
| margin: { l: 0, r: 0, t: 40, b: 0 }, | |
| }; | |
| const layoutHeat = { | |
| paper_bgcolor: '#0d1117', | |
| plot_bgcolor: '#0d1117', | |
| font: { color: '#c9d1d9', size: 11 }, | |
| xaxis: { title: 'Distance Filter', tickvals: [0,1,2,3,4], ticktext: distances.map(String), color: '#8b949e' }, | |
| yaxis: { title: 'CEX Ret Threshold (bps)', tickvals: [0,1,2,3,4,5,6], ticktext: thresholds.map(String), color: '#8b949e' }, | |
| margin: { l: 80, r: 20, t: 40, b: 60 }, | |
| }; | |
| function showMetric(metric) { | |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| const m = metrics[metric]; | |
| const z = getMetric(testData, m.idx); | |
| Plotly.react('surface', [{ | |
| type: 'surface', | |
| x: distances, | |
| y: thresholds, | |
| z: z, | |
| colorscale: m.colorscale, | |
| contours: { z: { show: true, usecolormap: true, project: { z: true } } }, | |
| hovertemplate: 'Dist: %{x}<br>Threshold: %{y}bps<br>Value: %{z}<extra></extra>', | |
| }], { ...layout3d, title: { text: m.title + ' — 3D Surface', font: { color: '#c9d1d9', size: 14 } }, | |
| scene: { ...layout3d.scene, zaxis: { ...layout3d.scene.zaxis, title: m.title } } }); | |
| Plotly.react('heatmap', [{ | |
| type: 'heatmap', | |
| z: z, | |
| colorscale: m.colorscale, | |
| hovertemplate: 'Dist: %{x}<br>Threshold: %{y}bps<br>Value: %{z}<extra></extra>', | |
| text: z.map(row => row.map(v => v !== null ? v.toFixed(1) : '')), | |
| texttemplate: '%{text}', | |
| textfont: { color: '#fff', size: 11 }, | |
| }], { ...layoutHeat, title: { text: m.title + ' — Heatmap', font: { color: '#c9d1d9', size: 14 } } }); | |
| // Update table | |
| renderTable(metric); | |
| } | |
| function renderTable(metric) { | |
| const m = metrics[metric]; | |
| let html = '<table class="grid-table"><tr><th>Threshold \\ Dist</th>'; | |
| distances.forEach(d => html += `<th>≤${d}</th>`); | |
| html += '<th>Best</th></tr>'; | |
| // Find global max | |
| let globalMax = -Infinity, globalKey = ''; | |
| for (const [k, v] of Object.entries(testData)) { | |
| if (v[m.idx] > globalMax) { globalMax = v[m.idx]; globalKey = k; } | |
| } | |
| thresholds.forEach(t => { | |
| html += `<tr><td><b>${t} bps</b></td>`; | |
| let rowBest = -Infinity, rowBestD = ''; | |
| distances.forEach(d => { | |
| const key = `${t}_${d.toFixed(2)}`; | |
| const val = testData[key]; | |
| if (val && val[m.idx] > rowBest) { rowBest = val[m.idx]; rowBestD = d; } | |
| }); | |
| distances.forEach(d => { | |
| const key = `${t}_${d.toFixed(2)}`; | |
| const val = testData[key]; | |
| const v = val ? val[m.idx] : null; | |
| const cls = key === globalKey ? 'winner' : (v > 0 ? 'positive' : 'negative'); | |
| html += `<td class="${cls}">${v !== null ? v.toFixed(1) : '—'}</td>`; | |
| }); | |
| html += `<td class="positive">dist≤${rowBestD}</td></tr>`; | |
| }); | |
| html += '</table>'; | |
| document.getElementById('tableContainer').innerHTML = html; | |
| } | |
| // Initial render | |
| showMetric('sharpe'); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment