<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Futures Screener — Статистика сигналов</title>
<style>
:root{
--bg:#0a0e17; --card:rgba(255,255,255,.04); --border:rgba(255,255,255,.09);
--txt:#e6edf3; --muted:#8b98a9; --green:#21d07a; --red:#f6465d; --blue:#3b82f6;
--purple:#8b5cf6; --amber:#f59e0b; --orange:#f97316; --cyan:#06b6d4;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
background:radial-gradient(1200px 600px at 80% -10%,rgba(59,130,246,.10),transparent),
radial-gradient(900px 500px at 0% 0%,rgba(139,92,246,.08),transparent),var(--bg);
color:var(--txt);min-height:100vh;padding:24px 16px;line-height:1.5}
.wrap{max-width:1080px;margin:0 auto}
header{margin-bottom:24px}
h1{font-size:26px;font-weight:700;letter-spacing:-.5px}
h1 .em{background:linear-gradient(90deg,var(--blue),var(--purple));-webkit-background-clip:text;background-clip:text;color:transparent}
.sub{color:var(--muted);font-size:13px;margin-top:6px}
.glass{background:var(--card);border:1px solid var(--border);border-radius:16px;
backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:22px}
.kpi{padding:18px}
.kpi .lbl{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.5px}
.kpi .val{font-size:28px;font-weight:700;margin-top:6px}
.section{padding:20px;margin-bottom:22px}
.section h2{font-size:15px;font-weight:600;margin-bottom:14px;display:flex;align-items:center;gap:8px}
table{width:100%;border-collapse:collapse;font-size:13.5px}
th,td{text-align:left;padding:10px 10px}
th{color:var(--muted);font-weight:500;font-size:11.5px;text-transform:uppercase;letter-spacing:.4px;
border-bottom:1px solid var(--border)}
tr+tr td{border-top:1px solid rgba(255,255,255,.045)}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
.pill{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;border-radius:999px;font-size:12px;font-weight:600}
.bar{height:7px;border-radius:4px;background:rgba(255,255,255,.07);overflow:hidden;min-width:90px}
.bar>span{display:block;height:100%;border-radius:4px}
.pos{color:var(--green)} .neg{color:var(--red)} .mut{color:var(--muted)}
.two{display:grid;grid-template-columns:1fr 1fr;gap:22px}
.dirgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;text-align:center}
.dirgrid .d{padding:16px}
.dirgrid .dv{font-size:24px;font-weight:700}
.spark{display:flex;align-items:flex-end;gap:4px;height:120px;margin-top:6px}
.spark .col{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;min-width:0}
.spark .b{width:100%;max-width:34px;border-radius:5px 5px 0 0;background:linear-gradient(180deg,var(--blue),rgba(59,130,246,.35))}
.spark .b.gap{background:repeating-linear-gradient(45deg,rgba(246,70,93,.25),rgba(246,70,93,.25) 4px,transparent 4px,transparent 8px)}
.spark .dt{font-size:9.5px;color:var(--muted);white-space:nowrap;transform:rotate(-50deg);transform-origin:center;margin-top:8px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{padding:7px 11px;font-size:12.5px;font-weight:600}
.chip .n{color:var(--muted);font-weight:500;margin-left:5px}
.foot{color:var(--muted);font-size:12px;text-align:center;margin-top:8px}
.note{font-size:12px;color:var(--amber);margin-top:10px}
@media(max-width:720px){.kpis{grid-template-columns:repeat(2,1fr)}.two{grid-template-columns:1fr}h1{font-size:21px}}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>📡 Futures Screener — <span class="em">Статистика сигналов</span></h1>
<div class="sub" id="period"></div>
</header>
<div class="kpis" id="kpis"></div>
<div class="section glass">
<h2>🎯 По типам сигналов <span class="mut" style="font-weight:400">— Win Rate & средний PnL</span></h2>
<table id="typeTbl"><thead><tr>
<th>Тип</th><th class="num">Всего</th><th class="num">Резолв</th>
<th class="num">Win Rate</th><th style="width:130px"> </th><th class="num">Avg PnL</th>
</tr></thead><tbody></tbody></table>
<div class="note" id="typeNote"></div>
</div>
<div class="two">
<div class="section glass">
<h2>📈 По направлению</h2>
<div class="dirgrid" id="dir"></div>
</div>
<div class="section glass">
<h2>🔬 Win Rate по уверенности</h2>
<table id="confTbl"><thead><tr>
<th>Confidence</th><th class="num">Сигналов</th><th class="num">WR</th><th style="width:90px"> </th>
</tr></thead><tbody></tbody></table>
</div>
</div>
<div class="section glass">
<h2>📅 Объём сигналов по дням <span class="mut" style="font-weight:400">— последние 30 дней</span></h2>
<div class="spark" id="spark"></div>
<div class="note">Пунктирные/пропущенные столбцы = дни без данных (включая залип 27 мая – 1 июня).</div>
</div>
<div class="section glass">
<h2>🔥 Топ-15 символов по числу сигналов</h2>
<div class="chips" id="symbols"></div>
</div>
<div class="foot" id="foot"></div>
</div>
<script>
const DATA = {"overall": {"total": 6449, "first": "2026-05-05 07:00:00", "last": "2026-06-02 13:55:00", "resolved": 6014, "wins": 3242}, "by_type": [{"type": "volume_spike", "total": 3827, "resolved": 3575, "wins": 1953, "avgpnl": 0.61}, {"type": "channel", "total": 2173, "resolved": 2018, "wins": 1063, "avgpnl": 0.37}, {"type": "oi_cvd", "total": 213, "resolved": 198, "wins": 102, "avgpnl": 0.42}, {"type": "liq_sweep", "total": 86, "resolved": 78, "wins": 38, "avgpnl": 0.43}, {"type": "oi_divergence", "total": 74, "resolved": 71, "wins": 43, "avgpnl": 3.03}, {"type": "vpin_toxicity", "total": 49, "resolved": 49, "wins": 32, "avgpnl": 0.24}, {"type": "oi_funding_squeeze", "total": 27, "resolved": 25, "wins": 11, "avgpnl": -1.07}], "by_dir": {"LONG": 3103, "NEUTRAL": 24, "SHORT": 3322}, "by_conf": [{"bucket": "50-59", "total": 170, "resolved": 165, "wins": 100}, {"bucket": "60-69", "total": 4045, "resolved": 3782, "wins": 2056}, {"bucket": "70-79", "total": 1452, "resolved": 1346, "wins": 713}, {"bucket": "80-89", "total": 684, "resolved": 631, "wins": 319}, {"bucket": "90+", "total": 98, "resolved": 90, "wins": 54}], "top_symbols": [{"symbol": "FILUSDT", "n": 105}, {"symbol": "TAOUSDT", "n": 105}, {"symbol": "PUMPUSDT", "n": 100}, {"symbol": "CLUSDT", "n": 97}, {"symbol": "WLDUSDT", "n": 94}, {"symbol": "ENAUSDT", "n": 93}, {"symbol": "PENGUUSDT", "n": 92}, {"symbol": "WLFIUSDT", "n": 90}, {"symbol": "ONDOUSDT", "n": 88}, {"symbol": "TRUMPUSDT", "n": 88}, {"symbol": "UNIUSDT", "n": 88}, {"symbol": "VIRTUALUSDT", "n": 87}, {"symbol": "SUIUSDT", "n": 85}, {"symbol": "1000PEPEUSDT", "n": 84}, {"symbol": "BZUSDT", "n": 83}], "per_day": [{"d": "2026-05-05", "n": 858}, {"d": "2026-05-06", "n": 1324}, {"d": "2026-05-07", "n": 985}, {"d": "2026-05-08", "n": 731}, {"d": "2026-05-09", "n": 922}, {"d": "2026-05-10", "n": 437}, {"d": "2026-05-11", "n": 289}, {"d": "2026-05-12", "n": 152}, {"d": "2026-05-15", "n": 27}, {"d": "2026-05-16", "n": 359}, {"d": "2026-05-26", "n": 318}, {"d": "2026-06-02", "n": 47}]};
const TYPE_META = {
volume_spike:{label:'Volume Spike',icon:'📊',c:'#3b82f6'},
oi_cvd:{label:'OI + CVD',icon:'🔮',c:'#8b5cf6'},
oi_divergence:{label:'OI Divergence',icon:'🔀',c:'#f59e0b'},
oi_funding_squeeze:{label:'Funding Squeeze',icon:'⚡',c:'#f97316'},
liq_sweep:{label:'Liq Sweep',icon:'🎯',c:'#ef4444'},
channel:{label:'Channel',icon:'📐',c:'#06b6d4'},
vpin_toxicity:{label:'VPIN Toxicity',icon:'☣️',c:'#22d3ee'},
};
const wr = (w,r)=> r? (100*w/r):null;
const wrColor = v => v==null?'var(--muted)': v>=58?'var(--green)': v>=50?'#cbd5e1':'var(--red)';
const fmt = n => n.toLocaleString('ru-RU');
// period
const o=DATA.overall;
document.getElementById('period').textContent =
`Период: ${o.first.slice(0,10)} → ${o.last.slice(0,10)} · ${fmt(o.total)} сигналов · ${fmt(o.resolved)} с известным исходом`;
// KPIs
const overallWR = wr(o.wins,o.resolved);
const kpis=[
{lbl:'Всего сигналов',val:fmt(o.total)},
{lbl:'С исходом',val:fmt(o.resolved)},
{lbl:'Общий Win Rate',val:overallWR.toFixed(1)+'%',c:wrColor(overallWR)},
{lbl:'Типов сигналов',val:DATA.by_type.length},
];
document.getElementById('kpis').innerHTML = kpis.map(k=>
`<div class="kpi glass"><div class="lbl">${k.lbl}</div><div class="val" style="color:${k.c||'var(--txt)'}">${k.val}</div></div>`).join('');
// type table (sorted by WR desc)
const rows=[...DATA.by_type].map(t=>({...t,wrv:wr(t.wins,t.resolved)})).sort((a,b)=>(b.wrv||0)-(a.wrv||0));
document.querySelector('#typeTbl tbody').innerHTML = rows.map(t=>{
const m=TYPE_META[t.type]||{label:t.type,icon:'•',c:'#888'};
const v=t.wrv;
const pnlCls = t.avgpnl>0?'pos':t.avgpnl<0?'neg':'mut';
const pnlTxt = t.avgpnl==null?'—':(t.avgpnl>0?'+':'')+t.avgpnl.toFixed(2)+'%';
return `<tr>
<td><span class="pill" style="background:${m.c}22;color:${m.c}">${m.icon} ${m.label}</span></td>
<td class="num">${fmt(t.total)}</td>
<td class="num mut">${fmt(t.resolved)}</td>
<td class="num" style="color:${wrColor(v)};font-weight:700">${v==null?'—':v.toFixed(1)+'%'}</td>
<td><div class="bar"><span style="width:${v||0}%;background:${wrColor(v)}"></span></div></td>
<td class="num ${pnlCls}" style="font-weight:600">${pnlTxt}</td>
</tr>`;
}).join('');
document.getElementById('typeNote').textContent =
'⭐ Лучшие по качеству: oi_divergence (PnL +3%) и vpin_toxicity (WR 65%), но выборка мелкая. ⚠️ oi_funding_squeeze — единственный убыточный.';
// direction
const dirMeta={LONG:{c:'var(--green)',i:'🟢'},SHORT:{c:'var(--red)',i:'🔴'},NEUTRAL:{c:'var(--muted)',i:'⚪'}};
const dt=Object.values(DATA.by_dir).reduce((s,v)=>s+v,0);
document.getElementById('dir').innerHTML = ['LONG','SHORT','NEUTRAL'].map(k=>{
const v=DATA.by_dir[k]||0,m=dirMeta[k];
return `<div class="d glass"><div class="lbl mut" style="font-size:12px">${m.i} ${k}</div>
<div class="dv" style="color:${m.c}">${fmt(v)}</div>
<div class="mut" style="font-size:12px">${(100*v/dt).toFixed(1)}%</div></div>`;
}).join('');
// confidence
document.querySelector('#confTbl tbody').innerHTML = DATA.by_conf.map(c=>{
const v=wr(c.wins,c.resolved);
return `<tr><td>${c.bucket}</td><td class="num mut">${fmt(c.total)}</td>
<td class="num" style="color:${wrColor(v)};font-weight:700">${v==null?'—':v.toFixed(1)+'%'}</td>
<td><div class="bar"><span style="width:${v||0}%;background:${wrColor(v)}"></span></div></td></tr>`;
}).join('');
// sparkline (per day) — fill missing days as gaps
const pd=DATA.per_day;
const max=Math.max(...pd.map(d=>d.n));
const days=[]; const start=new Date(pd[0].d), end=new Date(pd[pd.length-1].d);
const map=Object.fromEntries(pd.map(d=>[d.d,d.n]));
for(let dt=new Date(start); dt<=end; dt.setDate(dt.getDate()+1)){
const key=dt.toISOString().slice(0,10); days.push({d:key,n:map[key]??0});
}
document.getElementById('spark').innerHTML = days.map(d=>{
const h=d.n?Math.max(6,Math.round(110*d.n/max)):4;
const gap=d.n===0?' gap':'';
return `<div class="col" title="${d.d}: ${d.n}">
<div class="b${gap}" style="height:${h}px"></div>
<div class="dt">${d.d.slice(5)}</div></div>`;
}).join('');
// symbols
document.getElementById('symbols').innerHTML = DATA.top_symbols.map(s=>
`<span class="chip glass">${s.symbol.replace('USDT','')}<span class="n">${s.n}</span></span>`).join('');
document.getElementById('foot').textContent =
'Источник: signal_log (futures-screener prod) · Сгенерировано Бендером';
</script>
</body>
</html>