<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Бэктест 1%/1%</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;
}
*{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:18px}
h1{font-size:25px;font-weight:700;letter-spacing:-.5px}
h1 .em{background:linear-gradient(90deg,var(--green),var(--blue));-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)}
.assume{padding:14px 18px;margin-bottom:20px;font-size:13px;color:var(--muted);display:flex;flex-wrap:wrap;gap:10px 26px}
.assume b{color:var(--txt)}
.kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-bottom:18px}
.kpi{padding:18px;text-align:center}
.kpi .lbl{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.5px}
.kpi .val{font-size:30px;font-weight:800;margin:8px 0 2px}
.kpi .sm{font-size:12px;color:var(--muted)}
.kpi .tag{font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px;display:inline-block;margin-bottom:4px}
.section{padding:20px;margin-bottom:20px}
.section h2{font-size:15px;font-weight:600;margin-bottom:14px}
.verdict{padding:16px 18px;margin-bottom:20px;border-left:3px solid var(--amber);font-size:13.5px}
.verdict b{color:var(--amber)}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{text-align:left;padding:9px 8px}
th{color:var(--muted);font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.3px;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}
.pos{color:var(--green)} .neg{color:var(--red)} .mut{color:var(--muted)}
.dist{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:6px}
.dist .d{padding:12px;text-align:center}
.dist .dv{font-size:22px;font-weight:700}
.dist .dl{font-size:11px;color:var(--muted);margin-top:2px}
.foot{color:var(--muted);font-size:11.5px;margin-top:8px;text-align:center}
.cap{font-size:12px;color:var(--muted);margin-top:12px;line-height:1.6}
svg{width:100%;height:auto;display:block}
@media(max-width:720px){.kpis,.dist{grid-template-columns:1fr 1fr}h1{font-size:20px}}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>🧪 Бэктест: <span class="em" id="title"></span> по всем сигналам</h1>
<div class="sub" id="period"></div>
</header>
<div class="assume glass" id="assume"></div>
<div class="section glass" style="padding:16px 18px;margin-bottom:20px">
<h2 style="margin-bottom:8px">🔀 Что произошло с ценой после сигнала (до ±0.5%)</h2>
<div class="dist" id="dist"></div>
<div class="cap">«Оба коснулись» = цена прошла и +1%, и −1%. По данным MFE/MAE <b>не видно, что было первым</b> — поэтому ниже 3 сценария.</div>
</div>
<div class="kpis" id="kpis"></div>
<div class="verdict glass" id="verdict"></div>
<div class="section glass">
<h2>📈 Кривая накопленного P&L (% , net, фикс. размер)</h2>
<div id="chart"></div>
<div class="cap"><span style="color:var(--green)">●</span> оптимистично <span style="color:var(--red)">●</span> пессимистично — реальность между ними.</div>
</div>
<div class="section glass">
<h2>🎯 По типам сигналов — net P&L на сделку (%)</h2>
<table id="typeTbl"><thead><tr>
<th>Тип</th><th class="num">Сделок</th><th class="num">WR</th>
<th class="num">🔴 Пессим.</th><th class="num">⚪ Реалист.</th><th class="num">🟢 Оптим.</th>
</tr></thead><tbody></tbody></table>
<div class="cap" id="typeNote"></div>
</div>
<div class="section glass">
<h2>🔬 Зависит ли WR от уверенности? (реалист. сценарий)</h2>
<table id="confTbl"><thead><tr>
<th>Confidence</th><th class="num">Сделок</th><th class="num">WR</th><th class="num">net/сделку</th>
</tr></thead><tbody></tbody></table>
<div class="cap" id="confNote"></div>
</div>
<div class="foot" id="foot"></div>
</div>
<script>
const DATA = {"assumptions": {"tp": 0.5, "sl": 0.5, "fee_roundtrip": 0.1, "n": 6014, "first": "2026-05-05 07:00:00", "last": "2026-05-26 09:25:00", "dist": {"both": 1625, "tp_only": 2301, "sl_only": 1861, "neither": 227}}, "overall": {"pess": {"n": 6014, "wins": 2414, "losses": 3594, "wr": 40.1, "gross": -588.9, "exp": -0.0979, "net": -1190.3, "net_exp": -0.1979}, "mid": {"n": 6014, "wins": 2414, "losses": 1969, "wr": 40.1, "gross": 223.6, "exp": 0.0372, "net": -377.8, "net_exp": -0.0628}, "opt": {"n": 6014, "wins": 4039, "losses": 1969, "wr": 67.2, "gross": 1036.1, "exp": 0.1723, "net": 434.7, "net_exp": 0.0723}}, "by_type": [{"type": "volume_spike", "n": 3575, "pess": {"n": 3575, "wins": 1510, "losses": 2060, "wr": 42.2, "gross": -272.8, "exp": -0.0763, "net": -630.3, "net_exp": -0.1763}, "mid": {"n": 3575, "wins": 1510, "losses": 1158, "wr": 42.2, "gross": 178.2, "exp": 0.0498, "net": -179.3, "net_exp": -0.0502}, "opt": {"n": 3575, "wins": 2412, "losses": 1158, "wr": 67.5, "gross": 629.2, "exp": 0.176, "net": 271.7, "net_exp": 0.076}}, {"type": "channel", "n": 2018, "pess": {"n": 2018, "wins": 733, "losses": 1285, "wr": 36.3, "gross": -273.2, "exp": -0.1354, "net": -475.0, "net_exp": -0.2354}, "mid": {"n": 2018, "wins": 733, "losses": 672, "wr": 36.3, "gross": 33.3, "exp": 0.0165, "net": -168.5, "net_exp": -0.0835}, "opt": {"n": 2018, "wins": 1346, "losses": 672, "wr": 66.7, "gross": 339.8, "exp": 0.1684, "net": 138.0, "net_exp": 0.0684}}, {"type": "oi_cvd", "n": 198, "pess": {"n": 198, "wins": 62, "losses": 135, "wr": 31.3, "gross": -37.8, "exp": -0.1909, "net": -57.6, "net_exp": -0.2909}, "mid": {"n": 198, "wins": 62, "losses": 74, "wr": 31.3, "gross": -7.3, "exp": -0.0369, "net": -27.1, "net_exp": -0.1369}, "opt": {"n": 198, "wins": 123, "losses": 74, "wr": 62.1, "gross": 23.2, "exp": 0.1171, "net": 3.4, "net_exp": 0.0171}}, {"type": "liq_sweep", "n": 78, "pess": {"n": 78, "wins": 31, "losses": 47, "wr": 39.7, "gross": -8.2, "exp": -0.1052, "net": -16.0, "net_exp": -0.2052}, "mid": {"n": 78, "wins": 31, "losses": 23, "wr": 39.7, "gross": 3.8, "exp": 0.0486, "net": -4.0, "net_exp": -0.0514}, "opt": {"n": 78, "wins": 55, "losses": 23, "wr": 70.5, "gross": 15.8, "exp": 0.2024, "net": 8.0, "net_exp": 0.1024}}, {"type": "oi_divergence", "n": 71, "pess": {"n": 71, "wins": 32, "losses": 39, "wr": 45.1, "gross": -2.9, "exp": -0.0404, "net": -10.0, "net_exp": -0.1404}, "mid": {"n": 71, "wins": 32, "losses": 21, "wr": 45.1, "gross": 6.1, "exp": 0.0863, "net": -1.0, "net_exp": -0.0137}, "opt": {"n": 71, "wins": 50, "losses": 21, "wr": 70.4, "gross": 15.1, "exp": 0.2131, "net": 8.0, "net_exp": 0.1131}}, {"type": "vpin_toxicity", "n": 49, "pess": {"n": 49, "wins": 36, "losses": 13, "wr": 73.5, "gross": 8.8, "exp": 0.1801, "net": 3.9, "net_exp": 0.0801}, "mid": {"n": 49, "wins": 36, "losses": 8, "wr": 73.5, "gross": 11.3, "exp": 0.2311, "net": 6.4, "net_exp": 0.1311}, "opt": {"n": 49, "wins": 41, "losses": 8, "wr": 83.7, "gross": 13.8, "exp": 0.2821, "net": 8.9, "net_exp": 0.1821}}, {"type": "oi_funding_squeeze", "n": 25, "pess": {"n": 25, "wins": 10, "losses": 15, "wr": 40.0, "gross": -2.9, "exp": -0.1177, "net": -5.4, "net_exp": -0.2177}, "mid": {"n": 25, "wins": 10, "losses": 13, "wr": 40.0, "gross": -1.9, "exp": -0.0777, "net": -4.4, "net_exp": -0.1777}, "opt": {"n": 25, "wins": 12, "losses": 13, "wr": 48.0, "gross": -0.9, "exp": -0.0377, "net": -3.4, "net_exp": -0.1377}}], "by_conf": [{"bucket": "50-59", "n": 165, "mid": {"n": 165, "wins": 91, "losses": 42, "wr": 55.2, "gross": 21.9, "exp": 0.133, "net": 5.4, "net_exp": 0.033}}, {"bucket": "60-69", "n": 3782, "mid": {"n": 3782, "wins": 1573, "losses": 1240, "wr": 41.6, "gross": 164.9, "exp": 0.0436, "net": -213.3, "net_exp": -0.0564}}, {"bucket": "70-79", "n": 1346, "mid": {"n": 1346, "wins": 516, "losses": 459, "wr": 38.3, "gross": 32.7, "exp": 0.0243, "net": -101.9, "net_exp": -0.0757}}, {"bucket": "80-89", "n": 631, "mid": {"n": 631, "wins": 206, "losses": 211, "wr": 32.6, "gross": -0.8, "exp": -0.0013, "net": -63.9, "net_exp": -0.1013}}, {"bucket": "90+", "n": 90, "mid": {"n": 90, "wins": 28, "losses": 17, "wr": 31.1, "gross": 4.8, "exp": 0.0531, "net": -4.2, "net_exp": -0.0469}}], "curve": [{"d": "2026-05-05", "p": -217.8, "o": 120.2}, {"d": "2026-05-06", "p": -429.2, "o": 201.8}, {"d": "2026-05-07", "p": -541.2, "o": 181.8}, {"d": "2026-05-08", "p": -617.9, "o": 115.1}, {"d": "2026-05-09", "p": -764.5, "o": 102.5}, {"d": "2026-05-10", "p": -870.7, "o": 130.3}, {"d": "2026-05-11", "p": -966.9, "o": 203.1}, {"d": "2026-05-12", "p": -991.3, "o": 243.7}, {"d": "2026-05-15", "p": -1003.5, "o": 253.5}, {"d": "2026-05-16", "p": -1060.1, "o": 339.9}, {"d": "2026-05-26", "p": -1190.3, "o": 434.7}]};
const fmt = n => n.toLocaleString('ru-RU');
const pct = (v,d=1) => (v>0?'+':'')+v.toFixed(d)+'%';
const sgn = v => v>0?'pos':v<0?'neg':'mut';
const A=DATA.assumptions, O=DATA.overall;
document.getElementById('title').textContent = `+${A.tp}% тейк / −${A.sl}% стоп`;
document.getElementById('period').textContent =
`Период: ${A.first.slice(0,10)} → ${A.last.slice(0,10)} · ${fmt(A.n)} сделок с известным исходом`;
document.getElementById('assume').innerHTML =
`<span><b>Тейк:</b> +${A.tp}%</span><span><b>Стоп:</b> −${A.sl}%</span>`+
`<span><b>Комиссия:</b> ${A.fee_roundtrip}% round-trip</span>`+
`<span><b>R:R</b> 1:1</span><span><b>Размер:</b> фикс., без плеча, без реинвеста</span>`;
const dist=A.dist;
document.getElementById('dist').innerHTML = [
['tp_only','✅ Только +1%','var(--green)'],
['sl_only','❌ Только −1%','var(--red)'],
['both','🔀 Оба коснулись','var(--amber)'],
['neither','➖ Ни один','var(--muted)'],
].map(([k,l,c])=>`<div class="d glass"><div class="dv" style="color:${c}">${fmt(dist[k])}</div><div class="dl">${l}</div></div>`).join('');
// KPI scenarios
const sc=[
{k:'pess',lbl:'Пессимистично',tag:'оба → стоп',tc:'var(--red)'},
{k:'mid',lbl:'Реалистично',tag:'оба → 50/50',tc:'var(--muted)'},
{k:'opt',lbl:'Оптимистично',tag:'оба → тейк',tc:'var(--green)'},
];
document.getElementById('kpis').innerHTML = sc.map(s=>{
const x=O[s.k];
return `<div class="kpi glass">
<span class="tag" style="background:${s.tc}22;color:${s.tc}">${s.tag}</span>
<div class="lbl">${s.lbl}</div>
<div class="val ${sgn(x.net)}">${pct(x.net,0)}</div>
<div class="sm">WR ${x.wr}% · ${pct(x.net_exp,3)}/сделку</div>
</div>`;
}).join('');
document.getElementById('verdict').innerHTML =
`<b>Вывод:</b> исход стратегии решают <b>${fmt(dist.both)}</b> сделок, где цена задела оба уровня — без тиковых данных не определить, тейк или стоп сработал первым. `+
`Чистые сделки дают перевес (${fmt(dist.tp_only)} тейков против ${fmt(dist.sl_only)} стопов), но комиссия 0.1%×${fmt(A.n)} ≈ −${(A.n*A.fee_roundtrip).toFixed(0)}% съедает его. `+
`Консервативно (после комиссии) — <span class="neg">убыток</span>; в лучшем случае — <span class="pos">плюс</span>. Голый брекет 1%/1% по ВСЕМ сигналам без фильтра = на грани, не Грааль.`;
// equity curve SVG
(function(){
const c=DATA.curve, W=1000,H=300,padL=56,padR=16,padT=16,padB=28;
const xs=c.map((_,i)=>padL+(W-padL-padR)*i/(c.length-1));
const all=c.flatMap(d=>[d.p,d.o]).concat([0]);
const mn=Math.min(...all), mx=Math.max(...all);
const y=v=>padT+(H-padT-padB)*(1-(v-mn)/(mx-mn));
const line=key=>c.map((d,i)=>`${i?'L':'M'}${xs[i].toFixed(1)},${y(d[key]).toFixed(1)}`).join(' ');
const band=`M${xs[0]},${y(c[0].o)} `+c.map((d,i)=>`L${xs[i].toFixed(1)},${y(d.o).toFixed(1)}`).join(' ')+
' '+c.slice().reverse().map((d,i)=>`L${xs[c.length-1-i].toFixed(1)},${y(d.p).toFixed(1)}`).join(' ')+' Z';
const zeroY=y(0).toFixed(1);
const ticks=[mx, (mx+mn)/2, mn].map(v=>`<text x="6" y="${(y(v)+4).toFixed(1)}" fill="#8b98a9" font-size="11">${(v>0?'+':'')+v.toFixed(0)}%</text>`).join('');
const xlabels=c.map((d,i)=> (i%2===0||i===c.length-1)?`<text x="${xs[i]}" y="${H-8}" fill="#8b98a9" font-size="10" text-anchor="middle">${d.d.slice(5)}</text>`:'').join('');
document.getElementById('chart').innerHTML=
`<svg viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet">
<path d="${band}" fill="rgba(59,130,246,.10)"/>
<line x1="${padL}" x2="${W-padR}" y1="${zeroY}" y2="${zeroY}" stroke="rgba(255,255,255,.18)" stroke-dasharray="4 4"/>
<path d="${line('o')}" fill="none" stroke="#21d07a" stroke-width="2.5"/>
<path d="${line('p')}" fill="none" stroke="#f6465d" stroke-width="2.5"/>
${ticks}${xlabels}
</svg>`;
})();
// by type
const TM={volume_spike:'📊 Volume Spike',channel:'📐 Channel',oi_cvd:'🔮 OI+CVD',liq_sweep:'🎯 Liq Sweep',oi_divergence:'🔀 OI Divergence',vpin_toxicity:'☣️ VPIN',oi_funding_squeeze:'⚡ Funding Squeeze'};
document.querySelector('#typeTbl tbody').innerHTML = DATA.by_type.map(t=>{
const cell=v=>`<td class="num ${sgn(v)}">${pct(v,3)}</td>`;
return `<tr><td>${TM[t.type]||t.type}</td><td class="num mut">${fmt(t.n)}</td>
<td class="num">${t.mid.wr}%</td>
${cell(t.pess.net_exp)}${cell(t.mid.net_exp)}${cell(t.opt.net_exp)}</tr>`;
}).join('');
document.getElementById('typeNote').innerHTML =
'☣️ <b>VPIN</b> прибылен во ВСЕХ сценариях (нет «пил», WR 65%) — самый устойчивый. 🔀 <b>OI Divergence</b> в плюсе. ⚡ <b>Funding Squeeze</b> убыточен везде. Остальные держатся на оптимистичном исходе «пил».';
// by conf
document.querySelector('#confTbl tbody').innerHTML = DATA.by_conf.map(b=>
`<tr><td>${b.bucket}</td><td class="num mut">${fmt(b.n)}</td>
<td class="num">${b.mid.wr}%</td><td class="num ${sgn(b.mid.net_exp)}">${pct(b.mid.net_exp,3)}</td></tr>`).join('');
document.getElementById('confNote').innerHTML =
'⚠️ WR <b>не растёт</b> с уверенностью — наоборот, бакет 80-89 худший (36%). Высокий confidence НЕ помогает на тесном брекете 1%/1%.';
document.getElementById('foot').textContent =
'Метод: TP=mfe_pct≥1%, SL=mae_pct≤−1% (отслежено до 1д). Источник: signal_log · Сгенерировано Бендером';
</script>
</body>
</html>