<!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">+1% тейк / −1% стоп</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">🔀 Что произошло с ценой после сигнала (до ±1%)</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": 1.0, "sl": 1.0, "fee_roundtrip": 0.1, "n": 6014, "first": "2026-05-05 07:00:00", "last": "2026-05-26 09:25:00", "dist": {"both": 1208, "tp_only": 2371, "sl_only": 1915, "neither": 520}}, "overall": {"pess": {"n": 6014, "wins": 2642, "losses": 3365, "wr": 43.9, "gross": -733.2, "exp": -0.1219, "net": -1334.6, "net_exp": -0.2219}, "mid": {"n": 6014, "wins": 2642, "losses": 2157, "wr": 43.9, "gross": 474.8, "exp": 0.0789, "net": -126.6, "net_exp": -0.0211}, "opt": {"n": 6014, "wins": 3850, "losses": 2157, "wr": 64.0, "gross": 1682.8, "exp": 0.2798, "net": 1081.4, "net_exp": 0.1798}}, "by_type": [{"type": "volume_spike", "n": 3575, "pess": {"n": 3575, "wins": 1646, "losses": 1924, "wr": 46.0, "gross": -289.8, "exp": -0.0811, "net": -647.3, "net_exp": -0.1811}, "mid": {"n": 3575, "wins": 1646, "losses": 1260, "wr": 46.0, "gross": 374.2, "exp": 0.1047, "net": 16.7, "net_exp": 0.0047}, "opt": {"n": 3575, "wins": 2310, "losses": 1260, "wr": 64.6, "gross": 1038.2, "exp": 0.2904, "net": 680.7, "net_exp": 0.1904}}, {"type": "channel", "n": 2018, "pess": {"n": 2018, "wins": 819, "losses": 1198, "wr": 40.6, "gross": -367.8, "exp": -0.1823, "net": -569.6, "net_exp": -0.2823}, "mid": {"n": 2018, "wins": 819, "losses": 733, "wr": 40.6, "gross": 97.2, "exp": 0.0482, "net": -104.6, "net_exp": -0.0518}, "opt": {"n": 2018, "wins": 1284, "losses": 733, "wr": 63.6, "gross": 562.2, "exp": 0.2786, "net": 360.4, "net_exp": 0.1786}}, {"type": "oi_cvd", "n": 198, "pess": {"n": 198, "wins": 67, "losses": 130, "wr": 33.8, "gross": -67.1, "exp": -0.339, "net": -86.9, "net_exp": -0.439}, "mid": {"n": 198, "wins": 67, "losses": 81, "wr": 33.8, "gross": -18.1, "exp": -0.0916, "net": -37.9, "net_exp": -0.1916}, "opt": {"n": 198, "wins": 116, "losses": 81, "wr": 58.6, "gross": 30.9, "exp": 0.1559, "net": 11.1, "net_exp": 0.0559}}, {"type": "liq_sweep", "n": 78, "pess": {"n": 78, "wins": 34, "losses": 44, "wr": 43.6, "gross": -9.8, "exp": -0.1258, "net": -17.6, "net_exp": -0.2258}, "mid": {"n": 78, "wins": 34, "losses": 30, "wr": 43.6, "gross": 4.2, "exp": 0.0537, "net": -3.6, "net_exp": -0.0463}, "opt": {"n": 78, "wins": 48, "losses": 30, "wr": 61.5, "gross": 18.2, "exp": 0.2332, "net": 10.4, "net_exp": 0.1332}}, {"type": "oi_divergence", "n": 71, "pess": {"n": 71, "wins": 34, "losses": 37, "wr": 47.9, "gross": -1.3, "exp": -0.0178, "net": -8.4, "net_exp": -0.1178}, "mid": {"n": 71, "wins": 34, "losses": 22, "wr": 47.9, "gross": 13.7, "exp": 0.1934, "net": 6.6, "net_exp": 0.0934}, "opt": {"n": 71, "wins": 49, "losses": 22, "wr": 69.0, "gross": 28.7, "exp": 0.4047, "net": 21.6, "net_exp": 0.3047}}, {"type": "vpin_toxicity", "n": 49, "pess": {"n": 49, "wins": 32, "losses": 17, "wr": 65.3, "gross": 8.9, "exp": 0.1811, "net": 4.0, "net_exp": 0.0811}, "mid": {"n": 49, "wins": 32, "losses": 17, "wr": 65.3, "gross": 8.9, "exp": 0.1811, "net": 4.0, "net_exp": 0.0811}, "opt": {"n": 49, "wins": 32, "losses": 17, "wr": 65.3, "gross": 8.9, "exp": 0.1811, "net": 4.0, "net_exp": 0.0811}}, {"type": "oi_funding_squeeze", "n": 25, "pess": {"n": 25, "wins": 10, "losses": 15, "wr": 40.0, "gross": -6.3, "exp": -0.2521, "net": -8.8, "net_exp": -0.3521}, "mid": {"n": 25, "wins": 10, "losses": 14, "wr": 40.0, "gross": -5.3, "exp": -0.2121, "net": -7.8, "net_exp": -0.3121}, "opt": {"n": 25, "wins": 11, "losses": 14, "wr": 44.0, "gross": -4.3, "exp": -0.1721, "net": -6.8, "net_exp": -0.2721}}], "by_conf": [{"bucket": "50-59", "n": 165, "mid": {"n": 165, "wins": 89, "losses": 53, "wr": 53.9, "gross": 29.6, "exp": 0.1796, "net": 13.1, "net_exp": 0.0796}}, {"bucket": "60-69", "n": 3782, "mid": {"n": 3782, "wins": 1727, "losses": 1344, "wr": 45.7, "gross": 368.4, "exp": 0.0974, "net": -9.8, "net_exp": -0.0026}}, {"bucket": "70-79", "n": 1346, "mid": {"n": 1346, "wins": 564, "losses": 507, "wr": 41.9, "gross": 65.8, "exp": 0.0489, "net": -68.8, "net_exp": -0.0511}}, {"bucket": "80-89", "n": 631, "mid": {"n": 631, "wins": 227, "losses": 234, "wr": 36.0, "gross": -1.4, "exp": -0.0022, "net": -64.5, "net_exp": -0.1022}}, {"bucket": "90+", "n": 90, "mid": {"n": 90, "wins": 35, "losses": 19, "wr": 38.9, "gross": 12.4, "exp": 0.138, "net": 3.4, "net_exp": 0.038}}], "curve": [{"d": "2026-05-05", "p": -235.6, "o": 260.4}, {"d": "2026-05-06", "p": -437.3, "o": 488.7}, {"d": "2026-05-07", "p": -552.9, "o": 471.1}, {"d": "2026-05-08", "p": -630.9, "o": 409.1}, {"d": "2026-05-09", "p": -789.6, "o": 416.4}, {"d": "2026-05-10", "p": -912.1, "o": 481.9}, {"d": "2026-05-11", "p": -1039.2, "o": 608.8}, {"d": "2026-05-12", "p": -1059.6, "o": 694.4}, {"d": "2026-05-15", "p": -1075.7, "o": 710.3}, {"d": "2026-05-16", "p": -1128.3, "o": 885.7}, {"d": "2026-05-26", "p": -1334.6, "o": 1081.4}]};
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('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>