# Дослідження 29 — клас210х10-1х7д/д уздовж ноги

| | |
| --- | --- |
| Пари | BTC_USDT, ETH_USDT, SOL_USDT, XRP_USDT, BNB_USDT, DOGE_USDT |
| Клас | `клас210х10-1х7д/д` — шість відношень похідних сусідніх рівнів плюс похідна ціни |
| Ряд подій | `train` + `val` разом, сітка `t_start` (70 647 подій BTC) |
| Розмітка | зігзаг 2.3 % на ема10, нога ділиться на 10 десятин |
| Статус | етапи 4 і 5 підтверджені, 2026-09-12 і 2026-09-13; етапи 1–3 підтверджені, 2026-09-29 |
| Вкладка | [`/class7dd`](http://localhost:8080/class7dd) |
| Правила | [`01-data-and-db.md`](../../rules/01-data-and-db.md), [`02-markup.md`](../../rules/02-markup.md) |

## Ціль

Дослідження 28 виміряло ногу зігзага 2.3 % **на ціні**: рух збирається на
краях ноги, довжина ноги в подіях гуляє від десятків до тисяч. Тут те
саме питання ставиться до одного класу — `клас210х10-1х7д/д`:

1. **Де в нозі сидить кожна з семи фіч** — чи є в неї край ноги, чи вона
   однакова всюди, і чи міняє це напрямок та довжина ноги.
2. **За скільки подій класу видно, що почалась нова нога** — тобто коли
   можна сказати «нова нога зігзага 2.3 % формується» замість
   «продовжується попередня», і якою затримкою після півота це коштує.
3. **Чи можна з рівня фічі сказати довжину ноги наперед** — етап 1 знайшов
   звʼязок довжини з рівнем фічі, але міряв рівень по всій нозі; тут той
   самий звʼязок береться причинно, з перших `K` подій після півота.
4. **Чи більше знає модель, якій події подають по черзі** — замість голови
   ноги одним числом мережа з памʼяттю веде ногу подія за подією і на
   кожній каже, скільки подій лишилось до півота.

Клас узятий не випадково: у переліку всіх класів проєкту (дослідження 28,
етап 7) він один із трьох найсильніших на спільній сітці подій —
AUC 0.760 на вікні 39 подій.

## Метод: як міряється фіча вздовж ноги

Хід розмітки той самий, що в дослідженні 28 (етап 1), і береться з його
скрипта — `edge_profile.py`:

    ряд подій монети -> ема10(price_open) -> півоти зігзага 2.3 % -> ноги
    -> десятини -> згортка фічі по блоках події -> ранги по монеті
    -> середнє по десятині кожної ноги -> середнє по ногах

| # | Крок | Як саме |
| - | ---- | ------- |
| 1 | **Подія → одне число** | фіча згортається по 9 блоках події в середнє |
| 2 | **Ранги замість середніх** | відношення похідних не мають скінченного розкиду: ділення на майже нуль дає значення до `1e12`, і середнє по десятині там не значить нічого. Кожна фіча переводиться **в ранги всередині монети** (0…1) на подіях ніг |
| 3 | **Три криві** | `ранг` — ранг напрямленої величини (`v × напрямок ноги`), центрований на 0; `амплітуда` — ранг `\|v\|`, рівень ряду 0.5; `сплеск` — частка подій десятини з топ-5 % `\|v\|` монети, рівень 0.05 |
| 4 | **Приведення до напрямку** | ноги вниз читаються як ноги вгору |
| 5 | **Краї проти середини** | краї — десятини 1 і 10, середина — 4–7; різниця міряється в похибках середнього (`t`) |
| 6 | **Фільтр дір** | нога з розривами понад годину сумарно не рахується — як у дослідженні 28 |

Чому ранг, а не z. Робастна нормалізація (медіана та IQR) хвости не
прибирає: після неї середнє по десятині все одно доходило до `1e12` і
профіль читався як шум. Ранг обмежений за побудовою, і одна дика подія
зсуває його рівно на один крок.

## Метод: за скільки подій видно нову ногу

Питання й замір беруться з дослідження 28 (етап 7) без змін —
`class_new_leg.py`, той самий скрипт із ключем `--class`:

| # | Крок | Як саме |
| - | ---- | ------- |
| 1 | **Питання** | «розворот стався всередині останніх `X` подій» — нова нога; «не стався» — продовжується попередня |
| 2 | **Вхід** | тільки події класу, без жодної ціни: блоки події згортаються в число на фічу, вікно ділиться на 8 рівних частин за подіями, на кожну — середнє й розкид кожної фічі |
| 3 | **Розмір входу** | `8 × 7 × 2 = 112` колонок на будь-якому `X`, тому вікна порівнюються прямо |
| 4 | **Перебір** | `X` = 3, 5, 8, 10, 15, 20, 30, 39, 60, 100 подій |
| 5 | **Розбиття** | train до 14.08, val після; усі шість монет разом |
| 6 | **Оцінка** | AUC, крива порога і **затримка** — медіана події після півота, на якій сигнал спрацьовує |

## Метод: довжина ноги з рівня фічі

Питання етапу 1 — описове: рівень фічі там міряний **по всій нозі**,
разом із подіями, яких на початку ще немає. Тут воно ставиться причинно:
нога триває `K` подій після півота, видно тільки ці `K` подій — скільки
подій у нозі буде всього.

| # | Крок | Як саме |
| - | ---- | ------- |
| 1 | **Ряд ніг** | той самий, що в етапі 1: ема10, зігзаг 2.3 %, фільтр дір; нога без попередньої в замір не йде, бо для неї немає переносу |
| 2 | **Ранги** | таблиця рангів будується **тільки на train-подіях монети** і застосовується до всього ряду: інакше рівень val-події знав би про майбутнє |
| 3 | **Вхід** | `head` — рівень семи фіч × три криві в перших `K` подіях плюс напрямок ноги (22 колонки); `prev` — те саме по попередній нозі плюс її довжина; `head_prev` — обидва |
| 4 | **Ціль** | кількість подій ноги; модель учиться в логарифмі (довжина йде від десятків до тисяч), похибка рахується в подіях |
| 5 | **Бази** | train-медіана довжини і **перенос** — «довжина буде як у попередньої ноги», без навчання |
| 6 | **`K`** | 10, 20, 39, 60, 100 подій; нога коротша за `K` у замір цього `K` не йде |
| 7 | **Ділення** | за датою (train до 14.08) і відкладеною монетою — пʼять монет учать, шоста відповідає, шість разів |
| 8 | **Оцінка** | Спірмен видачі з правдою, медіанна похибка в подіях проти двох баз і AUC «нога довша за train-медіану» |

Модель — `HistGradientBoostingRegressor`, `max_iter` 300, `learning_rate`
0.05, `max_leaf_nodes` 7, `min_samples_leaf` 20, `l2` 1.0, seed 17.

## Метод: скільки подій до кінця ноги

Етап 3 бере голову ноги — `K` перших подій — і каже одне число про всю
ногу; модель там дерева. Тут те саме питання ставиться **послідовно**:
події подаються одна за одною, і на кожній модель каже, скільки їх
лишилось до півота. Памʼять несе ногу, тому з кожним кроком модель знає
більше, а не менше — рано чи пізно вона отримує всю ногу.

| # | Крок | Як саме |
| - | ---- | ------- |
| 1 | **Класи разом** | усі класи проєкту на сітці подій 210 с (`t_start`), які є в кожної монети: `клас210х10-1х2д`, `клас210х10-1х3+1дц`, `клас210х10-1х7дц`, `клас210х10-1х7д/д`, `клас210х10х7`, `клас210х10х7дц`, `клас210х10х9` — 43 фічі разом |
| 2 | **Подія → канали** | кожна фіча згортається по блоках у три числа: середнє, розкид, остання плашка → **129 каналів**. Ключ `--blocks` подає всі блоки як є (410 каналів) |
| 3 | **Нормалізація** | ключ `--norm`, чотири способи привести канал до шкали мережі; база завжди тільки з train-подій монети |
| 4 | **Ціль** | `left` — скільки подій лишилось до кінця ноги: 0 на півоті, довжина мінус один на першій події |
| 5 | **Мережа** | LSTM + причинна увага (дослідження 19, `leg_end_net.LegEnd`) з одним виходом-числом; паддинг у рекурсію не заходить, крок `t` бачить кроки `1…t` |
| 4а | **Причинні лічильники** | ключ `--clock` додає три канали: скільки подій минуло від півота, відповідь лічильника (медіана залишку серед train-подій із тим самим номером кроку) і вікно відповіді — одиниця перші `WINDOW` подій, нуль далі |
| 4а | **Причинні лічильники** | ключ `--clock` додає три канали: скільки подій минуло від півота; відповідь лічильника — медіана залишку серед train-подій із тим самим номером кроку; вікно відповіді — одиниця перші `WINDOW` подій після півота, нуль далі. Усі три причинні: майбутнього не бачать, база рахується тільки по train |
| 5а | **Памʼять через півоти** | `--history N` веде в послідовність ще `N` попередніх ніг: ціль на них замаскована, вони йдуть тільки в памʼять. Окремий канал каже, чи мережа вже у своїй нозі, ще один — напрямок ноги |
| 6 | **Втрата** | ключ `--loss`, шість форм на вибір; паддинг замаскований, похибка завжди рахується в подіях |
| 7 | **Розбиття** | нога цілком по один бік межі 14.08; ноги з дірою понад годину не беруться |
| 8 | **Бази** | train-медіана залишку і **лічильник** — медіана залишку серед train-подій із тим самим числом уже пройдених подій |
| 9 | **Оцінка** | медіана `\|сказано − насправді\|` в подіях, окремо по десятинах ноги: видно, з якого місця ноги модель починає знати |
| 10 | **Памʼять машини** | канали лежать у `.npy` (`float16`) і читаються memmap: у навчання їде тільки батч. Прогін не починається при вільній памʼяті менше за 2400 МБ (`rules/03-models.md` → Навчання без свопа), обхід — `--force` |

Без `--history` попередніх ніг мережі не видно взагалі: послідовність
починається з першої події після півота, памʼять чиста.

### Нормалізація каналу

Ключ `--norm`. Мережа не бачить сирої фічі — вона бачить те, у що фічу
перевели, і на цих класах перевід не дрібниця: `клас210х10-1х7д/д` — це
відношення похідних, і в ряді трапляються числа до `2.5e23`.

Замір — `norm_check.py`, на **410 справжніх каналах** BTC_USDT (усі
блоки всіх семи класів кроку), кожен кандидат проти самої фічі. База
скрізь тільки `train`, вибірка 200 000 значень на канал, seed 17.

| `--norm` | Що робить | Склеєно | Відновлення | Різниці як є | Розмах | Максимум |
| -------- | --------- | ------: | ----------: | -----------: | -----: | -------: |
| `rank` | ранг серед train-подій, 0…1 | 12.97 % | — | 0.0 % | 1.98 | 2 |
| `gauss` | той самий ранг під `Φ⁻¹`: канал стає стандартним нормальним | 12.97 % | — | 0.0 % | 3.84 | 6.39 |
| `asinh` | `asinh(v/s)`, `s` — медіана `\|v\|` | 0.06 % | 3.3e-16 | 50.6 % | 5.02 | 8.37 |
| `asinh99` | `asinh(v/s)/k`, `s` — 99-й перцентиль `\|v\|`, `k` — 90-й перцентиль `\|y\|` | 0.04 % | 3.0e-16 | 99.1 % | 37.05 | 146 |

- **Склеєно** — частка різних значень фічі, які стали одним числом.
- **Відновлення** — 99-й перцентиль відносної похибки зворотного ходу
  `y → v`. Прочерк означає, що з видачі фічу не дістати взагалі.
- **Різниці як є** — частка подій, де множник різниць `g'(v)` вище 0.7,
  тобто зміна фічі проходить крізь перетворення майже недоторканою.
- **Розмах** і **максимум** — 99-й перцентиль і найбільше `|видачі|`
  після ділення на медіану train: чим саме доводиться годувати мережу.

Ранг і розподіл — це квантування: вони лишають місце в черзі й
викидають і масштаб, і відстань між значеннями, тому −686 200 і −7432
стають сусідніми числами, а 12.97 % різних значень фічі злипаються в
одне. `gauss` від рангу відрізняється тільки формою шкали: замість
рівномірної 0…1 виходить дзвін із середнім 0 і розкидом 1, однаковий у
всіх фіч. Дані від цього не цілішими не стають — стає однаковим вигляд
входу, і густа середина ряду розтягується там, де подій багато.

`asinh` натомість строго зростає й нічого не склеює: перетворення
оборотне з похибкою `3e-16`, тобто фіча в ньому лежить ціла.

### Форми втрати

| `--loss` | Що штрафує | Формула |
| -------- | ---------- | ------- |
| `huber` | залишок у логарифмі — за замовчуванням | Хубер на `log1p(left)` |
| `mae` | те саме, але без квадрата біля нуля | `\|out − true\|` на `log1p` |
| `events` | помилку **в подіях**, не в логарифмі | Хубер на `expm1(·)/100` |
| `wmse` | довгий залишок дорожче (`rules/03-models.md`) | `(1 + α·\|y\|^β)·(out − true)²`, α = β = 1 |
| `tail` | кінець ноги | `events` × вага з місця події |
| `edges` | **обидва краї** ноги | `events` × вага з місця події |
| `ramp` | кінець ноги драбиною без стрибка | `events` × вага з місця події |
| `steep` | та сама драбина з крутішим хвостом | `events` × вага з місця події |
| `sides` | окрема ціна за завищення і за заниження на кожній десятині | `events` × вага з місця × вага за знаком похибки |
| `band` | коридор у частках цілі й мовчання після вікна | штраф за `сказано/правда`, далі модуль видачі |

Вага `tail` і `edges` береться **з місця події в нозі** — не з цілі й не
з прогнозу, тому вона не тягне видачу вгору чи вниз, а тільки каже, де
помилка дорога:

| Частка ноги | 0–10 % | 10–20 % | 20–30 % | 30–70 % | 70–80 % | 80–90 % | 90–100 % |
| ----------- | -----: | ------: | ------: | ------: | ------: | ------: | -------: |
| `tail` | ×1 | ×1 | ×1 | ×1 | ×2 | ×4 | ×8 |
| `edges` | ×10 | ×4 | ×2 | ×1 | ×2 | ×4 | ×8 |

`ramp` іде десятинами, рівно +1 на кожну — без стрибка й без рівної
середини:

| Десятина ноги | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| ------------- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: |
| `ramp` | ×1 | ×2 | ×3 | ×4 | ×5 | ×6 | ×7 | ×8 | ×9 | ×10 |
| `steep` | ×1 | ×2 | ×3 | ×4 | ×5 | ×6 | ×9 | ×11 | ×13 | ×15 |

`steep` — це `ramp` із подвоєним кроком з сьомої десятини; голова взята
з нього самого (`STEEP = RAMP[:6] + …`), тому початок і середина ноги в
обох прогонах важать однаково і різниця між ними — тільки хвіст.

`band` стоїть окремо від решти: у неї не вага збоку, а **своя втрата**,
і питання вона ставить не в подіях, а **в частках цілі**. Причина проста:
промах у 70 подій на нозі, де лишилось 700, — це 10 %, а на нозі, де
лишилось 70, — це 100 %, і одна мірка на обидві не годиться.

Мережа відповідає **перші `WINDOW = 150` подій після півота**, далі ціль
рівно 0 — вона мовчить. Межа стоїть на **лічильнику подій**, а не на
частці ноги: частка визначається повною довжиною, тобто майбутнім, і
вимога «до половини кажи, далі мовчи» непричинна — мережа не може знати,
в якій вона половині, і відповідає одним числом на всю ногу. Лічильник
вона бачить каналом, тому такий перемикач вивчається точно.

| `сказано/правда` | 0.00 | 0.20 | 0.40 | 0.60 | 0.89 | **0.90–1.10** | 1.30 | 1.70 | ≥ 2.20 |
| ---------------- | ---: | ---: | ---: | ---: | ---: | :-----------: | ---: | ---: | -----: |
| Штраф | **80** | 26.0 | 8.2 | 2.3 | 0.03 | **0** | 0.10 | 0.97 | **20** |

Усередині коридору штрафу немає зовсім — «влучив»; за його межами він
росте експоненційно (`GROW = 5`) і впирається в межу: `DOWN = 80` на
нулі, `UP = 20` на `CEIL = 2.2`. Униз дорожче вчетверо: сказати «нозі
лишилось мало», коли їй лишилось багато, — гірша помилка.

Після вікна штраф — **модуль** видачі в сотнях подій, вага `QUIET = 6`:
сказав 0 → 0, сказав 155 → 9.3, сказав 500 → 30. Модуль, а не Хубер:
Хубер за межею одиниці росте надто мляво, і «сказав 155» коштує майже
стільки ж, скільки «сказав 1000», — мережі вигідно хеджувати.

Профіль описаний числами в `TAIL`, `EDGES`, `RAMP` і `STEEP`
(`legleft_train.py`), а не зашитий у код; `RAMP` до того ж збирається з
`DECILES`, тому драбина сама йде за числом десятин.

## Метод: сигнал кінця ноги

Етап 4 просить у мережі **число** — скільки подій лишилось до півота, —
і міряє, на скільки подій вона промахнулась. Питання тут інше й простіше:
не «скільки лишилось», а **«ми вже в кінці?»**. Мітка на події ноги:

| Частка ноги | 0 … 95 % | 95 … 100 % |
| ----------- | :------: | :--------: |
| Ціль | 0 | **1 — сигнал** |

Тобто на кожній нозі стоїть рівно один короткий сплеск — її хвіст, — і
від мережі просять, щоб її видача мала **пік саме там**. Позначка
береться часткою, а не фіксованим числом подій: на нозі медіанної
довжини 392 події це останні 20, на короткій — менше. Щоб на
найкоротших ногах сигнал не зник зовсім, береться `max(1, round(5 %))`
останніх подій.

Вхід — той самий, що в кращому прогоні етапу 4, і в цьому суть: міняється
**тільки постановка питання**.

| # | Крок | Як саме |
| - | ---- | ------- |
| 1 | **Вхід** | `legleft_blocks4_gauss_clock` — усі блоки події як канали (410), плюс три причинні лічильники й канал «своя нога»: **415** |
| 2 | **Нормалізація** | `gauss` — місце значення в черзі train подається в `Φ⁻¹`, канал стає стандартним нормальним |
| 3 | **Памʼять** | `--history 4`: чотири попередні ноги йдуть у памʼять LSTM, мітка на них замаскована |
| 4 | **Ціль** | останні `TIP = 5 %` подій ноги — 1, уся решта — 0 |
| 5 | **Мережа** | та сама `leg_end_net.LegEnd` (LSTM + причинна увага), але вихід — **логіт**, не число подій |
| 6 | **Втрата** | `--loss`: `bce`, `focal`, `close`, `quiet` — див. нижче |
| 6а | **Вага позитиву** | `--pos-weight`: у скільки разів пропуск сигналу дорожчий за хибний сплеск. За замовчуванням 19 — стільки, щоб сигнал і фон важили порівну |
| 7 | **Розбиття** | нога цілком по один бік межі 14.08, як в етапах 1–4 |
| 8 | **Бази** | рівень сигналу (5 %) і **лічильник** — частка сигналу серед train-подій із тим самим номером кроку від півота |
| 9 | **Оцінка** | AUC, середня влучність `AP`, профіль видачі по десятинах ноги і частка ніг, де найгучніша подія потрапила в зону |
| 10 | **Чесна оцінка** | краща епоха обирається за `AP` на val, тому її число завищене; поруч лічиться замір з відбором на половині ніг val і виміром на другій, і навпаки |

### Чому лічильник — сильна база

Мітка залежить від **повної** довжини ноги, тобто від майбутнього. Але
довгі ноги рідкісні, тому сама по собі частка «я вже на кроці `i`»
зсуває ймовірність: чим далі йде нога, тим імовірніше, що вона от-от
скінчиться. Лічильник знає рівно це — і нічого більше. Усе, що мережа
дає понад нього, — це внесок класу.

Але сам лічильник **не безкоштовний**: його нуль ставить розмітка
зігзага по всьому ряду, а не спостерігач у реальному часі. Скільки це
коштує — заміряно окремим прогоном без лічильників, див. «Скільки з
цього дає розмітка, а скільки клас».

### Три форми втрати

| Ключ | Що робить |
| ---- | --------- |
| `bce` | BCE на логіті з вагою позитиву `pos_weight = 19`: сигнал у 20 разів рідший за фон, і без ваги мережа просто мовчить на всій нозі |
| `focal` | та сама BCE, помножена на `(1 − p)²`: легкі негативи, яких 95 %, перестають тягнути градієнт, і вага йде в ті події, де мережа ще помиляється |
| `close` | `focal` із вагою за місцем події в нозі: ×2 на 80–90 % ноги, ×4 на 90–95 %. Саме там проходить межа сигналу, і саме там вирішується, буде пік на місці чи розмажеться |
| `quiet` | `bce` плюс **тиша на початку ноги**: перші `HUSH = 50` подій після півота сигнал майже неможливий (0.3 % подій val), і хибний сплеск там коштує вчетверо дорожче. Межа стоїть на лічильнику кроків, який мережа бачить каналом, тому вимога причинна — це той самий прийом, що дає тишу прогону `band` етапу 4 |

Профіль ваги описаний числами в `CLOSE` (`legtip_train.py`), а не зашитий
у код.

## Дані

| | |
| --- | --- |
| Вхід | `data/datasets/<PAIR>/class210x10m1x7dd/{train,val}.npz` |
| Подія | `(9, 7)` — 9 блоків по 21 с (перша плашка знята), 7 фіч |
| Фічі | `d_up`, `d_down`, `d_resist_buy`, `d_bal_resist_buy`, `d_support_sell`, `d_bal_support_sell`, `d_price_avg` |
| Ніг у профілі | **554** (відкинуто з дірами — 158), подій 309 021 |
| Ряд для вікон | train 261 034 подій, val 47 873 |
| Ніг у прогнозі довжини | **495** (нога без попередньої не рахується): train 404, val 91 |
| Ноги в мережі етапу 4 | **554**: train 451, val 103; подій 308 897 (train 257 588, val 51 309) |
| Послідовності | `data/legleft_<tag>.npy` — канали `float16` на диску, `data/legleft_<tag>.npz` — межі ніг, час і ціна ряду |
| Памʼять чотирьох ніг | 785 811 подій ідуть тільки в памʼять, ціль на них замаскована |

## План роботи

| # | Етап | Статус |
| - | ---- | ------ |
| 1 | Фічі класу вздовж ноги: три криві по десятинах, краї проти середини, розріз за напрямком, довжиною і монетою, звʼязок рівня фічі з довжиною ноги; розділ вкладки `/class7dd` | підтверджено, 2026-09-29 |
| 2 | За скільки подій класу видно нову ногу: `X` від 3 до 100 подій, AUC, точність на кривій порога і затримка після півота; розділ вкладки `/class7dd` | підтверджено, 2026-09-29 |
| 3 | Довжина ноги з рівня фічі: рівень перших `K` подій ноги проти рівня по всій нозі, модель на трьох наборах входів проти медіани й переносу, групи ніг за рівнем; ряд на val — де ногу виявлено й де їй скінчитись; два розділи вкладки `/class7dd` | підтверджено, 2026-09-29 |
| 4 | Скільки подій до кінця ноги, подія за подією: сім класів сітки 210 с разом, мережа з памʼяттю проти медіани й лічильника, похибка по десятинах ноги, грід ручок, версія з чотирма попередніми ногами в памʼяті, форми втрати з вагою за місцем події в нозі і видача кожної епохи на ряді val; нормалізація каналів заміром проти сирої фічі; **коридор і мовчання** — мережа відповідає перші 150 подій після півота в частках цілі, далі мовчить, на причинних лічильниках, три засіви; вкладки `/class7dd` і `/gauss` | підтверджено, 2026-09-12 |
| 5 | Сигнал кінця ноги: мітка «останні 5 % подій ноги» проти всієї її решти на тому самому вході, що й етап 4 — чотири форми втрати й три ваги позитиву, AUC і середня влучність проти лічильника, профіль видачі по десятинах ноги, чесна оцінка на половині ніг val і замір ціни розміткового лічильника прогоном без нього; вкладка `/tip` | підтверджено, 2026-09-13 |

## Скрипти

| Скрипт | Що робить | Вихід |
| ------ | --------- | ----- |
| [`scripts/class_edge_profile.py`](scripts/class_edge_profile.py) | профіль кожної фічі вздовж ноги: три криві, краї проти середини | `class_edge_profile_class210x10m1x7dd.json` |
| [`../28-leg-edge-profile/scripts/class_new_leg.py`](../28-leg-edge-profile/scripts/class_new_leg.py) | нова нога на подіях класу, вікно у 8 частинах | `class_new_leg_class210x10m1x7dd.json` |
| [`scripts/leg_length.py`](scripts/leg_length.py) | довжина ноги з рівня фічі в перших `K` подіях | `leg_length_class210x10m1x7dd.json` |
| [`scripts/leg_length_series.py`](scripts/leg_length_series.py) | обидві моделі рядом на val: виявлення ноги і прогноз її кінця | `leg_length_series.json`, `leg_length_series_<PAIR>.json` |
| [`scripts/legleft_data.py`](scripts/legleft_data.py) | ноги як послідовності подій усіх класів сітки 210 с, ціль «до кінця ноги» | `../data/legleft_stats.npz` |
| [`scripts/legleft_train.py`](scripts/legleft_train.py) | LSTM із причинною увагою на цих послідовностях, грід ручок, вісім форм втрати, видача кожної епохи | `legleft_<tag>.json`, `legleft_<tag>_epochs.json`, `../models/legleft_<tag>/` |
| [`scripts/norm_check.py`](scripts/norm_check.py) | скільки інформації фічі лишається після кожної нормалізації, на справжніх каналах | `norm_check_<PAIR>.json` |
| [`scripts/norm_show.py`](scripts/norm_show.py) | одна подія класу до нормалізації і після неї, канал за каналом | `norm_show_<PAIR>_<клас>_<norm>.json` |
| [`scripts/legtip_train.py`](scripts/legtip_train.py) | та сама мережа на тому самому вході, але ціль — мітка «останні 5 % ноги»: три форми втрати, AUC, `AP`, профіль видачі й видача кожної епохи | `legtip_<tag>.json`, `legtip_<tag>_epochs.json`, `../models/legtip_<tag>/` |
| [`scripts/band_check.py`](scripts/band_check.py) | чи потрапляє видача в коридор довкола цілі: частки по десятинах ноги | `band_check.json` |
| [`scripts/wobble.py`](scripts/wobble.py) | рваність видачі вздовж ноги: відхилення кроку від −1, провали, ходи вгору | `wobble.json` |

```bash
# етап 1 — профіль фіч уздовж ноги
python3 research/29-class7dd-legs/scripts/class_edge_profile.py
python3 research/29-class7dd-legs/scripts/class_edge_profile.py --class class210x10x7

# етап 2 — за скільки подій видно нову ногу
python3 research/28-leg-edge-profile/scripts/class_new_leg.py \
    --class class210x10m1x7dd --holes sum \
    --window 3 --window 5 --window 8 --window 10 --window 15 \
    --window 20 --window 30 --window 39 --window 60 --window 100 \
    --out research/29-class7dd-legs/results

# етап 3 — довжина ноги з рівня фічі
python3 research/29-class7dd-legs/scripts/leg_length.py

# етап 3 рядом — виявлення ноги і прогноз її кінця на val
python3 research/29-class7dd-legs/scripts/leg_length_series.py

# етап 4 — скільки подій до кінця ноги, подія за подією
python3 research/29-class7dd-legs/scripts/legleft_data.py
python3 research/29-class7dd-legs/scripts/legleft_train.py --grid --force

# та сама мережа з чотирма попередніми ногами в памʼяті
python3 research/29-class7dd-legs/scripts/legleft_data.py --history 4
python3 research/29-class7dd-legs/scripts/legleft_train.py \
    --history 4 --hidden 32 --lr 1e-3 --force

# форми втрати на тій самій мережі, по одній-дві епохи
for loss in mae events wmse; do
    python3 research/29-class7dd-legs/scripts/legleft_train.py \
        --history 4 --hidden 32 --lr 1e-3 --epochs 1 --loss $loss
done
python3 research/29-class7dd-legs/scripts/legleft_train.py \
    --history 4 --hidden 32 --lr 1e-3 --epochs 2 --loss tail
for loss in edges ramp steep; do
    python3 research/29-class7dd-legs/scripts/legleft_train.py \
        --history 4 --hidden 32 --lr 1e-3 --epochs 2 --loss $loss
done

# нормалізація: замір на справжніх каналах і одна подія класу очима
python3 research/29-class7dd-legs/scripts/norm_check.py
python3 research/29-class7dd-legs/scripts/norm_show.py --norm gauss

# рваність видачі готового прогону
python3 research/29-class7dd-legs/scripts/wobble.py

# коридор і мовчання: датасет із причинними лічильниками і сам прогін
python3 research/29-class7dd-legs/scripts/legleft_data.py \
    --blocks --history 4 --norm gauss --clock
python3 research/29-class7dd-legs/scripts/legleft_train.py \
    --blocks --history 4 --norm gauss --clock \
    --hidden 32 --lr 1e-3 --epochs 3 --loss band --force
python3 research/29-class7dd-legs/scripts/band_check.py
for seed in 0 1 2; do
    python3 research/29-class7dd-legs/scripts/legleft_train.py \
        --blocks --history 4 --norm gauss --clock --hidden 32 --lr 1e-3 \
        --seed $seed --epochs 28 --patience 28 --loss band --force
done
python3 research/29-class7dd-legs/scripts/band_check.py --seeds

# етап 5 — сигнал кінця ноги на тому самому датасеті
for loss in focal close bce; do
    python3 research/29-class7dd-legs/scripts/legtip_train.py \
        --blocks --history 4 --norm gauss --clock \
        --hidden 32 --lr 1e-3 --epochs 14 --patience 5 --loss $loss --force
done

# bce на 14 епохах не зупинився, а вперся в стелю: той самий прогін далі
python3 research/29-class7dd-legs/scripts/legtip_train.py \
    --blocks --history 4 --norm gauss --clock \
    --hidden 32 --lr 1e-3 --epochs 30 --patience 6 --loss bce --force

# вага позитиву і тиша на початку ноги
for one in "bce 1" "quiet 1" "quiet 5"; do
    set -- $one
    python3 research/29-class7dd-legs/scripts/legtip_train.py \
        --blocks --history 4 --norm gauss --clock \
        --hidden 32 --lr 1e-3 --epochs 30 --patience 6 \
        --loss $1 --pos-weight $2 --force
done

# скільки з результату дає розмітковий лічильник: те саме без нього
python3 research/29-class7dd-legs/scripts/legtip_train.py \
    --blocks --history 4 --norm gauss \
    --hidden 32 --lr 1e-3 --epochs 30 --patience 6 \
    --loss bce --pos-weight 1 --force

# усі блоки без згортки, по одній нормалізації на прогін
for norm in asinh asinh99 gauss; do
    python3 research/29-class7dd-legs/scripts/legleft_data.py \
        --blocks --history 4 --norm $norm
    python3 research/29-class7dd-legs/scripts/legleft_train.py \
        --blocks --history 4 --norm $norm \
        --hidden 32 --lr 1e-3 --epochs 2 --loss ramp --force
done
```

`--force` тут свідомий: на цій машині вільної памʼяті менше за межу
2400 МБ, і без ключа прогін не почався б.

## Результат етапу 1

554 ноги, 6 монет, 309 021 подія.

### Амплітуда: ранг `|v|` по десятинах

| Фіча | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | краї | середина | t |
| ---- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | ---: | -------: | -: |
| `d_up` | 0.594 | 0.551 | 0.548 | 0.540 | 0.529 | 0.532 | 0.545 | 0.534 | 0.568 | 0.641 | **0.618** | 0.536 | +10.2 |
| `d_down` | 0.594 | 0.548 | 0.542 | 0.542 | 0.529 | 0.530 | 0.543 | 0.535 | 0.565 | 0.638 | **0.616** | 0.536 | +10.0 |
| `d_price_avg` | 0.580 | 0.557 | 0.551 | 0.545 | 0.539 | 0.539 | 0.546 | 0.537 | 0.553 | 0.603 | **0.592** | 0.542 | +8.2 |
| `d_resist_buy` | 0.389 | 0.441 | 0.444 | 0.458 | 0.466 | 0.461 | 0.449 | 0.459 | 0.423 | 0.343 | **0.366** | 0.458 | −10.1 |
| `d_support_sell` | 0.394 | 0.440 | 0.451 | 0.453 | 0.464 | 0.463 | 0.457 | 0.461 | 0.426 | 0.346 | **0.370** | 0.460 | −9.8 |
| `d_bal_resist_buy` | 0.419 | 0.461 | 0.462 | 0.473 | 0.480 | 0.475 | 0.461 | 0.470 | 0.437 | 0.374 | **0.396** | 0.472 | −9.9 |
| `d_bal_support_sell` | 0.421 | 0.461 | 0.468 | 0.467 | 0.476 | 0.473 | 0.468 | 0.470 | 0.440 | 0.373 | **0.397** | 0.471 | −9.6 |

Рівень ряду — 0.5. `t` — на скільки похибок середнього краї відрізняються
від середини.

- **Край ноги видно в кожній із семи фіч**, і скрізь `|t| ≥ 9.6`. Це не
  випадковість: на шумі той самий замір дає `|t| < 4`.
- **Дві групи, протилежні за знаком.** Похідні ходу (`d_up`, `d_down`,
  `d_price_avg`) на краях **вищі** — профіль U-подібний, дно у 5-й
  десятині, максимум в останній. Відношення обʼєму до рівнів
  (`d_resist_buy`, `d_support_sell` і їхні збалансовані пари) на краях
  **нижчі**, і найнижчі саме в останній десятині — профіль перевернутий.
- **Кінець ноги сильніший за початок**: 0.641 проти 0.594 у `d_up`,
  0.343 проти 0.389 у `d_resist_buy`.

### Сплески: частка подій із топ-5 % `|v|`

| Фіча | 1 | 5 | 10 | краї | середина | t |
| ---- | -: | -: | -: | ---: | -------: | -: |
| `d_up` | 0.091 | 0.064 | 0.129 | **0.110** | 0.072 | +5.8 |
| `d_down` | 0.093 | 0.064 | 0.128 | **0.111** | 0.072 | +5.8 |
| `d_price_avg` | 0.101 | 0.070 | 0.122 | **0.111** | 0.077 | +5.7 |
| `d_resist_buy` | 0.023 | 0.039 | 0.018 | **0.020** | 0.038 | −6.1 |
| `d_support_sell` | 0.026 | 0.038 | 0.019 | **0.023** | 0.038 | −5.4 |
| `d_bal_resist_buy` | 0.026 | 0.044 | 0.023 | **0.025** | 0.042 | −7.3 |
| `d_bal_support_sell` | 0.032 | 0.043 | 0.021 | **0.027** | 0.042 | −6.0 |

Рівень ряду — 0.05. В останній десятині сплесків ходу **вдвічі більше**
за рівний розподіл (0.129), а сплесків відношень до рівнів — **утричі
менше** (0.018).

### Напрямок фічі

Крива `ранг` — напрямлена величина. Тут край є лише в однієї фіча:

| Фіча | краї | середина | t |
| ---- | ---: | -------: | -: |
| `d_price_avg` | +0.023 | +0.003 | **+6.3** |
| решта шість | −0.004 … +0.010 | +0.001 … +0.010 | −1.8 … +0.9 |

Тобто на краях ноги **росте модуль** усіх семи фіч, а от у бік ноги
зміщується тільки похідна середньої ціни. Решта шість на краю сильні, але
без знака.

Рівень фіч при цьому дзеркально розділяє ноги росту й падіння
(`d_up`: −0.217 на ногах угору проти +0.240 на ногах униз) — це рівень
фічі, а не край ноги: у краях і в середині він однаковий.

### Довжина ноги

| Фіча | ранг | амплітуда | сплеск |
| ---- | ---: | --------: | -----: |
| `d_price_avg` | −0.640 | −0.606 | −0.540 |
| `d_down` | −0.027 | −0.598 | −0.444 |
| `d_up` | −0.019 | −0.588 | −0.456 |
| `d_support_sell` | −0.018 | +0.601 | +0.654 |
| `d_bal_support_sell` | +0.019 | +0.563 | +0.620 |
| `d_resist_buy` | −0.024 | +0.587 | +0.631 |
| `d_bal_resist_buy` | −0.013 | +0.539 | +0.599 |

Спірмен між кількістю подій ноги і рівнем фічі в ній. Довга нога —
**повільна**: похідні ходу в ній нижчі (−0.59), а відношення обʼєму до
рівнів вищі (+0.60). Знак розділяє ті самі дві групи.

Профіль тримається на будь-якій довжині: у групі 11…212 подій краї
`d_up` 0.725 проти середини 0.677, у групі 777…3099 — 0.520 проти 0.435.
Рівень падає, різниця лишається.

### По монетах

`d_price_avg`, амплітуда:

| Монета | Ніг | краї | середина | t |
| ------ | --: | ---: | -------: | -: |
| BTC_USDT | 70 | 0.589 | 0.529 | +4.2 |
| ETH_USDT | 103 | 0.587 | 0.523 | +5.0 |
| SOL_USDT | 109 | 0.582 | 0.533 | +3.8 |
| XRP_USDT | 103 | 0.615 | 0.579 | +2.1 |
| BNB_USDT | 64 | 0.587 | 0.531 | +3.7 |
| DOGE_USDT | 105 | 0.588 | 0.550 | +2.7 |

Знак однаковий на всіх шести монетах.

## Результат етапу 2

Мітка: «розворот стався всередині останніх `X` подій». Val — після 14.08.

| X, подій | Час вікна | Частка | AUC | Точність 0.1 % | Точність 1 % | Точність 5 % | Затримка |
| -------: | --------: | -----: | --: | -------------: | -----------: | -----------: | -------: |
| 3 | 10 хв | 0.006 | 0.737 | 0.021 | 0.033 | 0.023 | **2** |
| 5 | 18 хв | 0.010 | 0.757 | 0.104 | 0.054 | 0.045 | **2** |
| 8 | 28 хв | 0.017 | 0.759 | 0.167 | 0.106 | 0.075 | 5 |
| 10 | 35 хв | 0.021 | 0.754 | 0.167 | 0.138 | 0.094 | 5 |
| 15 | 53 хв | 0.031 | 0.758 | 0.396 | 0.255 | 0.144 | 8 |
| 20 | 1 год 10 хв | 0.041 | 0.754 | 0.542 | 0.315 | 0.186 | 9 |
| 30 | 1 год 45 хв | 0.062 | **0.762** | 0.792 | 0.486 | 0.315 | 12 |
| 39 | 2 год 17 хв | 0.079 | 0.760 | 0.917 | 0.618 | 0.406 | 16 |
| 60 | 3 год 30 хв | 0.119 | 0.755 | 0.979 | 0.781 | 0.535 | 23 |
| 100 | 5 год 50 хв | 0.189 | 0.740 | 1.000 | 0.948 | 0.703 | 29 |

Точності між рядками не порівнюються: чим довше вікно, тим частіша мітка
(0.006 на `X = 3` проти 0.189 на `X = 100`). Порівнюється AUC.

- **AUC не залежить від довжини вікна**: 0.737 … 0.762 на всьому
  діапазоні від 3 до 100 подій, максимум на `X = 30`. Клас **не бачить
  розворот краще від того, що йому дали довшу історію**.
- **Міняється не якість, а ціна питання.** Сказати «нова нога» з
  точністю 0.95 можна лише на `X = 100` — і це констатація через
  **29 подій** після півота (1 год 42 хв). На `X = 15` затримка 8 подій
  (28 хв), але точність на топ-1 % падає до 0.255.
- **Робоча середина — `X` = 30…39**: точність 0.49…0.62 на топ-1 %
  (0.79…0.92 на топ-0.1 %) при затримці 12…16 подій, тобто 42…56 хв
  після півота.
- **Найшвидший сигнал — `X` = 5…8**, затримка 2…5 подій, але на топ-1 %
  він правий у 3…11 % випадків: на такому вікні мітка в ряді трапляється
  раз на сто подій, і знайти її клас не вміє.
- **Що саме працює.** На коротких вікнах (`X` ≤ 8) найсильніші окремі
  колонки — **середні** рівнів опори й підтримки (`d_support_sell_1`
  0.708, `d_resist_buy_2` 0.700). Від `X` = 10 і далі наперед виходять їхні
  **розкиди** (`d_support_sell_1_std` 0.249, тобто зворотний звʼязок):
  коли розворот усередині вікна, розкид відношень **падає** — це та сама
  просадка амплітуди в останній десятині, яку показав етап 1.

Для порівняння: та сама модель на ході **ціни** (дослідження 28, етап 6)
дає AUC 0.830 на будь-якому вікні. Ціна досі сильніша за клас.

## Результат етапу 3

495 ніг (train 404, val 91), 6 монет. Медіана довжини — 393…450 події
залежно від `K`.

### Рівень фічі в голові ноги проти рівня по всій нозі

Спірмен довжини ноги з рівнем фічі, `K` = 39: причинний рівень / рівень
етапу 1.

| Фіча, крива | голова, 39 подій | вся нога |
| ----------- | ---------------: | -------: |
| `d_price_avg` сплеск | **−0.351** | −0.536 |
| `d_down` амплітуда | **−0.348** | −0.595 |
| `d_up` сплеск | **−0.343** | −0.470 |
| `d_support_sell` амплітуда | **+0.341** | +0.602 |
| `d_up` амплітуда | **−0.330** | −0.581 |
| `d_price_avg` амплітуда | **−0.330** | −0.587 |
| `d_bal_support_sell` амплітуда | **+0.326** | +0.573 |
| `d_resist_buy` амплітуда | **+0.326** | +0.582 |

Звʼязок **не зникає, коли ноги ще не видно**: з 39 подій після півота
лишається дві третини сили. Знак той самий, що в етапі 1: висока
амплітуда ходу — коротка нога, високі відношення обʼєму до рівнів —
довга.

### Групи ніг за рівнем фічі

Ноги поділені на пʼять рівних груп за рівнем найсильнішої фічі в перших
`K` подіях; довжина — події ноги, у дужках чверті.

`K` = 10, `d_support_sell` амплітуда:

| Рівень | Ніг | Медіана довжини |
| -----: | --: | --------------: |
| 0.054 | 99 | **238** (94 … 402) |
| 0.181 | 99 | 372 (198 … 734) |
| 0.312 | 99 | 476 (244 … 820) |
| 0.443 | 99 | 483 (291 … 755) |
| 0.674 | 99 | **565** (320 … 1004) |

`K` = 39, `d_price_avg` сплеск:

| Рівень | Ніг | Медіана довжини |
| -----: | --: | --------------: |
| 0.000 | 98 | **556** (344 … 1016) |
| 0.026 | 98 | 484 (271 … 818) |
| 0.051 | 98 | 446 (226 … 796) |
| 0.128 | 98 | 355 (200 … 600) |
| 0.308 | 98 | **252** (98 … 436) |

Крайні групи різняться вдвічі з гаком — 238 проти 565 подій на `K` = 10
і 556 проти 252 на `K` = 39. Чверті груп при цьому перекриваються:
рівень фічі рухає **розподіл** довжини, а не називає її.

### Модель

| `K` | Ніг | Вхід | за датою: Спірмен | виграш | AUC | перенос | монета: Спірмен | виграш | AUC | перенос |
| --: | --: | ---- | ----------------: | -----: | --: | ------: | --------------: | -----: | --: | ------: |
| 10 | 495 | голова | +0.379 | +0.262 | 0.699 | +0.417 | +0.317 | +0.061 | 0.648 | −0.159 |
| 10 | | попередня | +0.426 | +0.332 | 0.740 | +0.417 | +0.333 | +0.085 | 0.629 | −0.159 |
| 10 | | обидва | +0.388 | +0.216 | 0.722 | +0.417 | +0.413 | +0.138 | 0.675 | −0.159 |
| 39 | 490 | голова | +0.350 | +0.241 | 0.638 | +0.339 | +0.224 | −0.024 | 0.581 | −0.148 |
| 39 | | попередня | +0.430 | +0.298 | 0.736 | +0.339 | +0.330 | +0.132 | 0.630 | −0.148 |
| 39 | | обидва | +0.369 | +0.260 | 0.662 | +0.339 | +0.381 | +0.135 | 0.658 | −0.148 |
| 60 | 477 | голова | +0.317 | +0.239 | 0.636 | +0.253 | +0.263 | +0.045 | 0.626 | −0.190 |
| 60 | | попередня | +0.358 | +0.341 | 0.677 | +0.253 | +0.291 | +0.113 | 0.630 | −0.190 |
| 60 | | обидва | +0.444 | +0.317 | 0.721 | +0.253 | +0.381 | +0.162 | 0.681 | −0.190 |
| 100 | 450 | голова | +0.500 | +0.134 | 0.787 | −0.036 | +0.411 | +0.136 | 0.697 | −0.265 |
| 100 | | попередня | +0.258 | +0.075 | 0.638 | −0.036 | +0.161 | −0.023 | 0.588 | −0.265 |
| 100 | | обидва | +0.583 | +0.183 | 0.806 | −0.036 | +0.441 | +0.158 | 0.716 | −0.265 |

`K` = 20 у таблиці пропущено — числа між рядками 10 і 39, окремого
висновку не дають; у JSON він є.

- **Довжина ноги прогнозується.** Найкращий набір — голова плюс
  попередня нога: Спірмен +0.58 і AUC 0.806 за датою, +0.44 і 0.716 на
  відкладеній монеті. Медіанна похибка на `K` = 100 — **162 події**
  проти 198 у медіани.
- **Одного класу вистачає без ціни.** Набір `голова` — це рівень семи
  фіч і напрямок ноги, більше нічого: AUC 0.697 на відкладеній монеті
  при `K` = 100.
- **Чим довше нога вже йде, тим краще видно її кінець** — Спірмен голови
  росте з +0.32 (`K` = 10) до +0.50 (`K` = 100). Провал на `K` = 20…39 —
  це та сама середина ноги, де фічі рівні (етап 1).
- **Перенос через півот — сильна база тільки за датою** (+0.42 при
  `K` = 10, бо сусідні ноги одного вікна схожі за довжиною) і **гірший
  за медіану на відкладеній монеті** (−0.15 … −0.27). Модель обходить
  перенос на `K` = 60 і `K` = 100.
- Чверті груп перекриваються, тому це прогноз **розподілу**: «нога
  скоріш за все коротка» замість «в нозі буде 250 подій».

### Ряд на val: коли ногу виявлено і де їй скінчитись

Обидві моделі дослідження на одному ряді валідації. Детектор етапу 2
(вікно 39 подій, поріг — верхівка 5 % оцінок train) каже, що розворот
стався всередині останніх 39 подій; **виявленням** рахується лише перше
спрацювання в перших 39 подіях ноги, пізніші перетини порога — це хибні
спрацювання всередині ноги. Прогноз етапу 3 на той самий момент каже,
скільки подій у нозі буде всього, і тим самим — на якій події їй
скінчитись.

| Монета | Ніг | Виявлено | Подій до виявлення | Хибних | Похибка прогнозу | Частка ноги |
| ------ | --: | -------: | -----------------: | -----: | ---------------: | ----------: |
| BTC_USDT | 9 | 2 | 16 | 301 | 187 | 0.32 |
| ETH_USDT | 12 | 7 | 9 | 599 | 280 | 0.40 |
| SOL_USDT | 16 | 7 | 2 | 564 | 250 | 0.47 |
| XRP_USDT | 29 | 23 | 1 | 863 | 241 | 2.73 |
| BNB_USDT | 5 | 1 | 6 | 32 | 563 | 0.56 |
| DOGE_USDT | 24 | 7 | 1 | 304 | 112 | 0.61 |

- **Половину ніг видно, і рано.** 47 ніг із 95 детектор бере в перших
  39 подіях, медіана затримки 1…16 подій. Решта проходить повз.
- **Хибних спрацювань більше за виявлення в рази.** Поріг на 5 %
  оцінок train — це точність 0.406 з етапу 2: усередині ніг детектор
  кричить сотні разів. Сам по собі він не сигнал, а привід подивитись.
- **Прогноз кінця тримає порядок величини, не подію.** Медіанна похибка
  112…563 події — від третини до половини довжини ноги; на XRP, де ноги
  короткі, модель перебільшує втричі. Це та сама межа, що в таблиці
  вище: прогноз розподілу, а не числа.

## Результат етапу 4

554 ноги (train 451, val 103), 308 897 подій, 131 канал із семи класів.
Грід ручок — чотири поєднання ширини памʼяті й кроку навчання:

| Ручки | Краща епоха | val, подій |
| ----- | ----------: | ---------: |
| **`hidden 32 · lr 1e-3`** | 1 з 5 | **177** |
| `hidden 64 · lr 3e-4` | 1 з 5 | 178 |
| `hidden 32 · lr 3e-4` | 2 з 6 | 184 |
| `hidden 64 · lr 1e-3` | 1 з 5 | 199 |

Виграла вужча мережа — 24 481 ваги. Обидві версії далі вчаться саме на
цих ручках, щоб різниця між ними була різницею памʼяті, а не ручок.

### Похибка по десятинах ноги

Медіана `|сказано − насправді|` в подіях; жирне — хто найточніший у
десятині.

| Похибка, подій | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | разом |
| -------------- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | ----: |
| памʼять 4 ноги | 514 | 429 | 331 | 257 | 198 | **135** | 110 | 123 | 139 | 164 | 179 |
| без памʼяті | 551 | 472 | 381 | 305 | 232 | 152 | **105** | **93** | **111** | **143** | **177** |
| лічильник | **377** | **342** | **294** | **224** | **159** | 165 | 202 | 228 | 250 | 303 | 249 |
| медіана | 406 | 329 | 268 | 206 | 177 | 180 | 193 | 206 | 235 | 312 | 246 |

- **Мережа виграє з шостої десятини.** У кінці ноги вона точніша за
  лічильник утричі: 93 події проти 228 у восьмій десятині. Це той самий
  край ноги, який етап 1 знайшов у фічах.
- **На початку ноги обидві мережі програють лічильнику** — 514 і 551
  проти 377. Знати «нога триває вже `i` подій» на перших подіях
  корисніше, ніж усе, що каже клас.
- **Памʼять переносить виграш із кінця на початок.** Чотири попередні
  ноги зрізали похибку в десятинах 1–6 (514 проти 551, 257 проти 305) і
  віддали кінець (123 проти 93). Разом — 179 проти 177, тобто нічия:
  сума не змінилась, змінився її розподіл уздовж ноги.
- **Межі це не пробило.** Навіть із памʼяттю перша половина ноги гірша
  за просту базу; клас не бачить, наскільки довгою буде нога, поки та не
  пройшла половину.

### Навчання

Обидві версії беруть найкращу епоху рано — 1-шу і 2-гу з 5–6 — далі
похибка на val росте: 451 нога train проти 24 тисяч ваг. Кожна епоха
зберігається з **повною видачею на val**, тому на вкладці видно, що
казала кожна з них подія за подією.

### Форма втрати проти похибки

Та сама мережа з памʼяттю 4 ніг і тими самими ручками (`hidden 32 ·
lr 1e-3`), міняється тільки функція втрат. Медіана `|сказано −
насправді|` в подіях; жирне — хто найточніший у десятині.

| Похибка, подій | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | разом |
| -------------- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | ----: |
| `edges`, епоха 2 | 412 | **331** | 296 | 270 | 223 | 188 | 172 | 183 | 167 | 152 | 210 |
| `edges`, епоха 1 | 566 | 495 | 400 | 332 | 253 | 167 | 118 | 90 | 105 | 134 | 178 |
| **`ramp`, епоха 1** | 592 | 511 | 412 | 338 | 273 | 190 | 110 | 76 | 79 | 97 | 199 |
| `steep`, епоха 2 | 602 | 519 | 418 | 343 | 283 | 200 | 118 | 75 | 72 | 88 | 209 |
| `tail`, епоха 1 | 634 | 559 | 462 | 392 | 318 | 233 | 159 | **79** | **58** | **70** | 240 |
| `events` | 553 | 473 | 381 | 305 | 230 | 152 | **106** | 97 | 114 | 151 | **176** |
| `huber` | 514 | 429 | 331 | 257 | 198 | **135** | 110 | 123 | 139 | 164 | 179 |
| `wmse` | 530 | 450 | 356 | 282 | 209 | 141 | 107 | 113 | 132 | 168 | 179 |
| `mae` | 518 | 440 | 346 | 271 | **196** | 143 | 111 | 124 | 143 | 187 | 186 |
| лічильник | **377** | 342 | **294** | **224** | 159 | 165 | 202 | 228 | 250 | 303 | 249 |
| медіана | 406 | 329 | 268 | 206 | 177 | 180 | 193 | 206 | 235 | 312 | 246 |

- **Втрата вирішує, де мережа точна.** Загальне число майже не рухається
  (176–186 у чотирьох формах), але профіль уздовж ноги міняється вдвічі
  й більше. Одна й та сама мережа на тих самих даних дає 58 подій у
  девʼятій десятині або 132 — залежно тільки від того, що їй дорого.
- **`tail` бере кінець ноги.** ×2/×4/×8 на останніх 30 % зрізали
  девʼяту десятину до 58 подій — це вчетверо краще за лічильник (250) і
  вдвічі краще за `events` (114). Ціна — початок ноги (634 у першій
  десятині) і загальна медіана 240.
- **`edges` бере початок.** Додані ×10/×4/×2 на перших 30 % віддали
  результат не одразу: на першій епосі профіль ще звичайний (178 разом),
  на другій мережа перекинулась на початок — 412 і 331 у десятинах 1–2,
  тобто **вперше обходить лічильник у другій десятині** (331 проти 342) і
  майже дістає його в першій (412 проти 377). Ціна — кінець ноги (152
  замість 70) і загальна медіана 210.
- **`ramp` — половина шляху до `tail` за половину ціни.** Драбина ×1…×10
  без стрибка дала 76 / 79 / 97 у десятинах 8–10 при загальній медіані
  199. Це майже хвіст `tail` (79 / 58 / 70), але на 41 подію дешевше
  разом — і початок ноги при цьому не такий важкий (592 проти 634).
  Плавна вага тримає середину ноги живою там, де стрибок ×8 її кидає.
- **Крутіший хвіст майже нічого не додає.** `steep` (×9/×11/×13/×15 з
  сьомої десятини замість ×7…×10) зрушив кінець ноги на 5–9 подій — 72
  і 88 проти 79 і 97 — і заплатив за це десятьма подіями загальної
  медіани (209 проти 199) та гіршою серединою (200 проти 190 у шостій).
  Півтора раза ваги в хвості купують одиниці подій: **крива віддачі вже
  пласка**, вага там більше не головна.
- **Мережа вчиться, а не ламається.** На відміну від `tail`, де стрибок
  ×1 → ×8 підняв похибку до 240, обидві драбини лишились у межах
  199–215, і `steep` за другу епоху **покращився** (215 → 209), а не
  поплив. Плавний профіль тримає навчання навіть при 15-кратному
  перекосі між краями ноги.
- **Разом краї не даються.** Жодна форма не тримає обидва краї одночасно:
  вага на початку витягує початок і відпускає кінець, вага на кінці —
  навпаки. Для роботи це означає дві моделі, а не одну: одна відповідає
  «нога щойно почалась, скільки їй іти», друга — «скільки лишилось».
- **Обидва краї вимірюються по-різному.** Похибка в 400 подій на початку
  ноги і 60 у кінці — це не «в сім разів гірше»: на початку залишок сам
  по собі 400–500 подій, у кінці — десятки. У частках залишку початок
  ноги досі майже не прогнозований.

### Вхід мережі: нормалізація проти похибки

Та сама мережа, та сама втрата `ramp`, ті самі ручки (`hidden 32 ·
lr 1e-3`, памʼять 4 ніг, seed 0) — міняється **тільки те, що заходить у
крок**. Перший рядок — згортка кожної фічі у три числа (середнє,
розкид, остання плашка), решта — усі блоки як є. Показано першу епоху,
на другій усі чотири прогони гіршають.

| Вхід, епоха 1 | Каналів | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Разом |
| ------------- | ------: | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | ----: |
| блоки + `asinh` | 412 | 573 | **488** | **390** | **316** | **256** | **176** | 99 | 78 | 87 | 106 | **192** |
| згортка + `rank` | 131 | 592 | 511 | 412 | 338 | 273 | 190 | 110 | **76** | 79 | 97 | 199 |
| блоки + `asinh99` | 412 | **571** | 500 | 408 | 334 | 261 | 184 | 117 | 85 | 87 | 101 | 200 |
| блоки + `gauss` | 412 | 576 | 491 | 395 | 321 | 272 | 186 | **108** | 77 | **73** | **86** | 206 |
| лічильник | — | **377** | **342** | **294** | **224** | **159** | 165 | 202 | 228 | 250 | 303 | 249 |
| медіана | — | 406 | 329 | 268 | 206 | 177 | 180 | 193 | 206 | 235 | 312 | 246 |

- **Вхід майже нічого не міняє.** Між найкращим і найгіршим прогоном
  14 подій із 200 — 7 %, — при тому що каналів утричі більше (131 →
  412), ваг утричі більше (24 481 → 60 449), а самі числа на вході
  зовсім різні: ранг у 0…1, розподіл — дзвін, `asinh99` — хвіст до 21.
  Форма втрати (176 … 240) рухає результат утричі сильніше за все це.
- **Цілі дані самі по собі не допомагають.** `asinh99` лишає 99.1 %
  різниць недоторканими проти 50.6 % в `asinh` — і програє йому 8 подій.
  Збереження інформації і зручність шкали — різні речі, і для цієї
  мережі важила друга: у `asinh` типове число на вході біля одиниці, у
  `asinh99` — 0.15.
- **Розподіл дає найрівнішу шкалу і найкращий хвіст ноги.** У `gauss`
  усі 412 каналів мають однакові середнє 0 і розкид 1 — жодного ручного
  масштабу. Кінець ноги в нього найточніший з усіх (73 і 86 подій у
  десятинах 9–10 проти 79 і 97 у рангів), а загальна медіана найгірша
  (206): квантування, яке дало цю рівність, зʼїло середину ноги.
- **Початок ноги не піддається нікому.** У всіх чотирьох входах перша
  десятина — 571 … 592 подій проти 377 у лічильника. Ні зайві канали,
  ні цілі різниці, ні рівна шкала не дали мережі побачити те, чого в
  голові ноги немає.

#### Розподіл на тринадцятьох епохах

Один вхід, доведений до зупинки: `blocks4_gauss_ramp` учився далі з
епохи 3, а потім ще раз із епохи 10 (ваги й стан оптимізатора щоразу
підняті з файлу, `--resume`). Обидва рази прогін зупиняла рання зупинка —
чотири епохи без покращення val. Продовження бере ваги **кращої** епохи,
тому епохи 10–13 ростуть із пʼятої, а не з девʼятої.

| Епоха | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| ----- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: |
| Втрата train | 2.4203 | 2.0441 | 2.0305 | 2.0054 | 1.9891 | 1.9744 | 1.9810 | 1.9647 | 1.9336 | 1.9737 | 1.9797 | 1.9319 | 1.9491 |
| Похибка val, подій | 206 | 214 | 215 | 242 | **203.9** | 205 | 213 | 222 | 257 | 219 | 215 | 212 | 204.3 |

Краща — епоха 5, і вона краща за першу лише на дві події (204 проти 206):

| Похибка, подій | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Разом |
| -------------- | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: | ----: |
| `gauss`, епоха 5 | 545 | 462 | 371 | 297 | 245 | 162 | 108 | 86 | 76 | 88 | 204 |
| `gauss`, епоха 1 | 576 | 491 | 395 | 321 | 272 | 186 | 108 | 77 | 73 | 86 | 206 |

- **Дванадцять епох купують дві події.** Втрата на train падає рівно
  (2.42 → 1.93), val за той самий час гуляє 204 … 257 без напрямку. Це
  плато, а не перенавчання: розрив train/val не росте, мережа просто
  перестала дізнаватись нове після першої епохи. Друге продовження
  (епохи 10–13) підійшло до кращої точки впритул — 204.3 проти 203.9, —
  але не перейшло її; рівень 204 і є стелею цього входу.
- **Довше навчання переносить точність до голови ноги.** Епоха 5 краща
  за першу в десятинах 1–6 (545 проти 576 на початку) і гірша в 8–10
  (86 / 76 / 88 проти 77 / 73 / 86). Загальне число майже не рухається —
  рухається місце, де мережа точна, як і від форми втрати.

#### Коридор і мовчання: `band` із причинними лічильниками

Питання тут інше, ніж у решті етапу. Не «на скільки подій ми
помилились», а: **чи видно початок ноги очима**. Від моделі просять
форму — стрибок на півоті, утримання всю першу половину, тиша далі — і
влучання в коридор ±10 % від правди, поки вона говорить.

Мірка теж інша. Медіана `|сказано − насправді|` для цього не годиться:
після вікна ціль рівно 0, і чим краще мережа мовчить, тим гіршою вона за
тією міркою виглядає. Сама втрата теж не годиться — вона винагороджує
уникнення грубих промахів, а від моделі просять влучань. Тому оцінка
епохи — це **сама ціль**: частка подій вікна, що лягли в коридор
(вузький і широкий разом), плюс надбавка за балакучість після вікна.
Менше — краще, нуль — ідеал.

Ваги **кожної** епохи лягають на диск поруч із кращою: критерій відбору
по ходу роботи ще змінювався, і хороша епоха не має губитись через те,
чим її міряли.

**Про витік.** Канали причинні: база лічильника й ранги нормалізації
рахуються тільки по `train`-подіях, канал вікна дорівнює одиниці рівно
перші `WINDOW` кроків і довжини ноги не видає, ціль на ногах памʼяті
замаскована. Одне місце самопротікання є всередині `train`: кожна
train-нога бере участь у базі лічильника своїм же залишком, і на
великих кроках таких ніг лишаються одиниці. На val це не впливає — там
база береться з train, — але train-числа через це трохи оптимістичні.

Головна ж оптимістична похибка не в каналах, а у **відборі епох на
val**; вона виміряна нижче.

**Стеля розумного.** Перш ніж щось підбирати, поміряно, чи коридор
взагалі досяжний. Лічильник — база, яка знає лише номер кроку від
півота, — влучає в ±10 % у **8.7 %** подій, у ±30 % — у 22.1 %. Це і є
планка: довжина ноги на її початку не визначена точніше.

**Хід підбору.** Кожне коло — нова сітка штрафів і 2–3 епохи
продовженням від попередніх ваг:

| Коло | Що змінено | ±10 % | ±30 % | Після вікна (ціль 0) |
| ---- | ---------- | ----: | ----: | -------------------: |
| 1 | коридор ±10 %, край заниження 30, мовчання Хубером | 4.3 % | 14.6 % | 225 |
| 2 | край 80, експонента крутіша, мовчання ×3 | 4.8 % | 16.8 % | 234 |
| 3 | + причинні лічильники на вході | 5.6 % | 19.6 % | 281 |
| 4 | межа мовчання по лічильнику, не по частці ноги | 3.2 % | 11.4 % | 155 |
| 5 | мовчання модулем, ×6 | 3.4 % | 11.3 % | 59 |
| 6 | + канал вікна відповіді | 3.9 % | 12.2 % | 23 |
| 7 | без змін, ще три епохи | 5.5 % | 16.3 % | 14 |
| 8 | краща епоха за втратою `band`, а не за подіями | 9.1 % | 26.8 % | 18 |
| 9 | край завищення 10 → 20 | 9.0 % | 26.7 % | 1 |
| **10** | підпис сітки: зміна сітки скидає відбір | **9.3 %** | **28.0 %** | **6** |
| — | лічильник (база) | 8.7 % | 22.1 % | — |

**Результат.** Конфігурація `hidden 32 · lr 1e-3`, по 28–32 епохи з
нуля, ваги кожної епохи на диску, **три незалежні засіви** (0, 1, 2).
У кожного беруться три кращі епохи, їхні видачі усереднюються, і
усереднюються самі засіви:

| | ±10 % | ±30 % | Медіана частки | Після вікна |
| -------------------------- | ----: | ----: | -------------: | ----------: |
| **Три засіви разом** | **9.4 %** | **26.8 %** | 1.05 | **0** |
| Засів 0 сам (епохи 24, 27, 12) | 8.7 % | 26.5 % | 1.11 | 0 |
| Засів 2 сам (16, 10, 19) | 8.4 % | 24.9 % | — | 0 |
| Засів 1 сам (24, 12, 9) | 7.6 % | 24.0 % | — | 0 |
| Найкраща окрема епоха (0, е24) | 8.3 % | 25.5 % | 1.15 | 0 |
| Лічильник (база) | 8.7 % | 22.1 % | — | — |
| Коло 1 (початок підбору) | 4.3 % | 14.6 % | 0.40 | 225 |

Видача трьох засівів по кроку від півота: **274 · 386 · 393 · 388** у
вікні проти правди 336 · 326 · 342 · 379, далі **11 · 2 · 1 · 0** при
цілі 0.

**Ці числа завищені відбором.** Кращі епохи кожного засіву обрані за
їхнім покриттям **на val**, і поєднання засівів теж. Чесна оцінка —
відбір на половині ніг val, замір на другій:

| | ±10 % | ±30 % |
| ------------------------------------- | ----: | ----: |
| Відбір і замір на всьому val | 9.4 % | 26.8 % |
| **Відбір на половині, замір на другій** | **8.3 %** | **24.2 %** |
| Лічильник (без жодного відбору) | 8.7 % | 22.1 % |

Тобто **широкий коридор мережа виграє** — 24.2 % проти 22.1 %, — а
**вузький програє**: 8.3 % проти 8.7 %. Відбір коштує 1.1 пункта у
вузькому коридорі й 2.6 у широкому, і без цієї поправки порівняння з
базою некоректне.

Відтворюється командою `band_check.py --seeds`; окремий засів —
`band_check.py --tag …_seed0 --mix 24 27 12`.

Найвище число за весь підбір — 9.3 % / 28.0 % — отримано довгим
ланцюжком продовжень із кількома змінами сітки по дорозі; відтворити
його однією командою не вдалось, і ваги тієї епохи втрачено (тоді ще
зберігалась лише «краща» епоха, а критерій «кращої» по ходу мінявся).
Саме після цього ваги кожної епохи почали зберігатись.

**Форма видачі** (епоха 18 тієї довгої лінії), медіани по кроку від
півота:

| Крок від півота | 0–10 | 10–50 | 50–100 | 100–150 | 150–200 | 200–300 | 300+ |
| --------------- | ---: | ----: | -----: | ------: | ------: | ------: | ---: |
| Правда | 336 | 326 | 342 | 379 | — | — | — |
| **Мережа** | 283 | **422** | **422** | **409** | **6** | **0** | **−1** |

- **Форма є.** Стрибок на півоті (283 → 422), утримання 409–422 усе
  вікно, і після 150-ї події — нуль. На плашці видачі це видно як
  сходинку на кожному півоті.
- **Планку взято.** 9.3 % у коридорі ±10 % проти 8.7 % у лічильника і
  28.0 % у ±30 % проти 22.1 %. Мережа вперше обходить базу, яка знає
  лише номер кроку.
- **Що вирішило.** Три речі, у порядку внеску: **причинна межа вікна**
  (по лічильнику, а не по частці ноги) — без неї мережа хеджує одним
  числом на всю ногу; **канал вікна** — межу дали готовою, і вся ємність
  пішла на рівень; **правильний критерій відбору** — до нього прогін
  зберігав саме ті епохи, які найгірше мовчали.
- **Чого не вийшло.** Коридор ±10 % лишається майже порожнім: 9.3 % —
  це трохи краще за базу, але далеко від «загнати перші пʼять десятин у
  ±10 %». Розкид довжини ноги на її початку (10-й перцентиль 198 подій,
  90-й 1992) цього не дозволяє, і жодна форма штрафу цього не змінить.

##### Що ще перевірено і не спрацювало

- **Вага на голові вікна.** ×3 на перших десяти подіях і ×2 до 25-ї
  підняли старт (5.7 % проти 4.3 % у коридорі, 350 подій проти 283 при
  правді 336) — і посадили решту вікна: покриття ±30 % упало 28.0 % →
  24.1 %. Ваги в мережі спільні, тому піднявши голову, вона піднімає все.
- **Ширша мережа.** `hidden 64` (136 тисяч ваг проти 60) калібрується
  краще — частка видачі 0.98 проти 1.15, у середині вікна 326 проти
  правди 326 — але покриття не більше: 26.2 % проти 28.0 %. Вужча бере
  своє тим, що завищує: розподіл довжин перекошений вправо, і завищення
  частіше падає в ціль.
- **Менший крок навчання.** `lr 3e-4` прибирає стрибки (розмах оцінки по
  епохах удвічі менший), але приводить у те саме плато 18–23 %.
- **Довжини попередніх ніг каналами.** Дослідження 28 показало перенос
  довжини з ноги на ногу, тому довжина попередньої й позаминулої були
  дані готовими каналами. Результат гірший: 21.8 % проти 26.8 % без них,
  а частка видачі просіла до 0.6–0.8. Мережа чіпляється за минулу
  довжину й тягне відповідь до неї.
- **Калібрування масштабу.** Множник, підібраний **на самому val** (тобто
  нечесно, як стеля), піднімає 26.8 % до 27.9 %. Чесно підібраний дав би
  менше — не варте роботи.
- **Усереднення видач** дає невеликий, але стабільний приріст там, де
  жодна епоха не виділяється: середнє трьох кращих додає 0.6 пункта
  покриття (8.2 % / 23.5 % проти 7.6 % / 22.9 %). Помилки епох частково
  незалежні. Там, де одна епоха явно краща за решту, усереднення її
  псує.
- **Усереднення самих ваг** («суп» із трьох епох в одну модель) —
  23.7 % проти 24.5 % у найкращої з них. Не працює: епохи сидять у
  різних западинах, і середина між ними гірша за кожну.
- **Другий засів.** Той самий прогін із `--seed 1` дав 7.6 % / 24.0 % —
  помітно слабший за перший (8.7 % / 26.5 %). Разом вони дають 8.3 % /
  **27.0 %**: широкий коридор найвищий за всю роботу, вузький просідає.
  Головне тут інше: **різниця між двома засівами (2.5 пункта) більша за
  різницю між більшістю налаштувань, які перебирались**. Отже числа
  нижче 2–3 пунктів у цій задачі взагалі не є різницею.

### Клас після розподілу

`клас210х10-1х7д/д`, BTC_USDT, подія 60 000 (2026-08-10 23:41 UTC) —
сім фіч по девʼять блоків, спершу як лежать у датасеті, потім після
`--norm gauss` (`norm_show.py`):

| До розподілу | б1 | б2 | б3 | б4 | б5 | б6 | б7 | б8 | б9 |
| ------------ | -: | -: | -: | -: | -: | -: | -: | -: | -: |
| `d_up` | −0.00126 | −0.000742 | −6.78e-06 | −1.36e-05 | −9.42e-06 | −1.36e-06 | −1.04e-05 | 3.15e-05 | −8.8e-05 |
| `d_down` | −1.78e-07 | −1.37e-08 | −1.8e-05 | −3.94e-05 | −2.68e-05 | −0.000156 | −2.32e-06 | 3.3e-05 | −9.62e-08 |
| `d_resist_buy` | −792 | −1.35e+03 | −1.48e+05 | −7.33e+04 | −1.06e+05 | −7.36e+05 | −9.59e+04 | 3.18e+04 | −1.14e+04 |
| `d_bal_resist_buy` | 14.5 | 41.8 | −7.95e+04 | −4.08e+04 | −2.32e+04 | −2.52e+05 | 2.94e+03 | 3.61e+03 | 1.3e+03 |
| `d_support_sell` | −5.62e+06 | −7.28e+07 | −5.56e+04 | −2.54e+04 | −3.73e+04 | −6.42e+03 | −4.31e+05 | 3.03e+04 | −1.04e+07 |
| `d_bal_support_sell` | −2.8e+06 | −4.79e+07 | −1.5e+04 | −1.13e+03 | −1.63e+03 | −383 | 1.55e+04 | 2.89e+04 | −1.73e+06 |
| `d_price_avg` | 10 | 12.5 | 5.69 | −3.78 | −2.51 | −3.44 | 0.00119 | 1.55 | 2.59 |

| Після розподілу | б1 | б2 | б3 | б4 | б5 | б6 | б7 | б8 | б9 |
| --------------- | -: | -: | -: | -: | -: | -: | -: | -: | -: |
| `d_up` | −0.548 | −0.399 | 0.754 | 0.620 | 0.700 | 0.982 | 0.674 | 1.33 | 0.192 |
| `d_down` | 1.14 | 1.19 | 0.538 | 0.370 | 0.456 | 0.0239 | 0.890 | 1.32 | 1.16 |
| `d_resist_buy` | 0.232 | 0.0996 | −1.24 | −1.04 | −1.15 | −1.70 | −1.11 | 1.98 | −0.510 |
| `d_bal_resist_buy` | 0.471 | 0.549 | −1.55 | −1.36 | −1.20 | −1.85 | 1.19 | 1.23 | 1.04 |
| `d_support_sell` | −2.22 | −3.00 | −0.913 | −0.698 | −0.807 | −0.315 | −1.45 | 2.03 | −2.36 |
| `d_bal_support_sell` | −2.45 | −3.24 | −1.06 | −0.370 | −0.473 | −0.136 | 1.54 | 1.67 | −2.32 |
| `d_price_avg` | 0.863 | 1.00 | 0.580 | −0.451 | −0.357 | −0.418 | 0.0580 | 0.260 | 0.344 |

| Канали класу | Медіана `\|v\|` | 99-й перцентиль | Максимум |
| ------------ | ------------: | --------------: | -------: |
| до | 58.4 | 2.75e+06 | 2.5e+23 |
| після | 0.676 | 2.59 | 4.75 |

- **Одна шкала на всі сім фіч.** До перетворення `d_up` живе в
  `1e-5`, а `d_support_sell` — у `1e+7`: різниця в дванадцять порядків
  усередині однієї події. Після — усе між −3.3 і +2.0, і жодна фіча не
  перекрикує сусідню самим лише розміром числа.
- **Знак і порядок цілі.** `d_price_avg` іде 10 → 12.5 → 5.69 → −3.78,
  і після розподілу 0.863 → 1.00 → 0.580 → −0.451: більше лишилось
  більшим, мінус лишився мінусом, перехід через нуль на місці.
- **Хвоста більше немає.** Максимум `|каналу|` по класу падає з `2.5e23`
  до 4.75 — рівно чотири з половиною розкиди, як і має бути в нормального
  розподілу.
- **Ціна — відстань.** `d_resist_buy` у блоках 3 і 5 (−1.48e+05 і
  −1.06e+05, різниця в 40 %) стають −1.24 і −1.15: у дзвоні вони поруч,
  бо поруч у черзі. Скільки саме між ними було, мережа вже не дізнається
  — те саме квантування, що в рангах, тільки в іншій шкалі.

## Результат етапу 5

554 ноги (train 451, val 103), 60 833 ваги, ручки `hidden 32 · lr 1e-3`,
seed 0. Сигнал — 12 879 подій train із 257 588 (рівно 5.00 %).

Прогони різняться трьома речами: **формою втрати**, **вагою позитиву** і
тим, **чи заходять причинні лічильники**. Усе інше в них однакове.

| Прогін | Кан | Епоха | AUC | AP | Чесна AP | Пік | Влучн@5 % | ≥ 0.5 | Розрив |
| ------ | --: | ----: | --: | -: | -------: | --: | --------: | ----: | -----: |
| **`bce` · вага 19** | 415 | 20/26 | 0.714 | **0.151** | 0.143 | 21 % | **23.2 %** | 14.9 % | **×3.2** |
| **`quiet` · вага 5** | 415 | 7/13 | **0.720** | 0.146 | **0.149** | **23 %** | 19.8 % | 18.0 % | ×2.3 |
| `focal` · вага 19 | 415 | 6/11 | 0.717 | 0.129 | 0.115 | 14 % | 16.3 % | 42.5 % | ×1.2 |
| `quiet` · вага 1 | 415 | 5/11 | 0.702 | 0.127 | 0.116 | 17 % | 17.2 % | 0.0 % | ×2.0 |
| `close` · вага 19 | 415 | 6/11 | 0.705 | 0.122 | 0.103 | 17 % | 16.6 % | 65.1 % | ×1.2 |
| `bce` · вага 1 | 415 | 5/11 | 0.690 | 0.120 | 0.116 | 17 % | 16.2 % | 0.3 % | ×1.8 |
| `bce` · вага 1, **без лічильників** | 412 | 5/11 | 0.650 | 0.093 | 0.080 | 13 % | 12.9 % | 0.1 % | ×1.7 |
| лічильник (база) | — | — | 0.674 | 0.072 | — | 30 % | 4.7 % | — | — |
| рівень сигналу | — | — | 0.500 | 0.050 | — | — | 5.0 % | — | — |

`Розрив` — у скільки разів середня видача в зоні сигналу більша за
середню поза нею. `≥ 0.5` — на якій частці подій val видача перетинає
половину; сигналу 5 %, тому число сильно вище — це крик, сильно нижче —
німота.

### Скільки з цього дає розмітка, а скільки клас

Лічильник `leg:step` обнуляється на півоті зігзага, а півот береться з
розмітки по **всьому ряду** — і на train, і на val. Півот 2.3 %
підтверджується тільки тоді, коли ціна відійшла від нього на 2.3 %:
на 95 півотах val медіанна затримка підтвердження — **80 подій**
(4.7 год, 23 % медіанної ноги), чверть півотів чекає понад 180 подій.
Тобто нуль лічильника стоїть у точці, про яку в живому ряді дізнаєшся
сильно пізніше.

Щоб дізнатись ціну цього, той самий прогін зроблено на датасеті **без
трьох лічильників** (412 каналів замість 415):

| | з лічильниками | без них | лічильник сам по собі |
| --- | -: | -: | -: |
| AUC | 0.690 | 0.650 | 0.674 |
| AP | 0.120 | **0.093** | 0.072 |
| Влучність @5 % | 16.2 % | **12.9 %** | 4.7 % |

Лічильники варті **+0.027 AP**. Але головне інше: **клас сам по собі
сильніший за лічильник сам по собі** — AP 0.093 проти 0.072 при рівні
0.050. Прибери розмітковий лічильник зовсім, і модель усе одно влучає в
12.9 % подій на своїх найгучніших 5 % — у 2.6 раза краще за випадкове.
Результат етапу спирається на клас, а розмітка його підсилює, а не несе.

### Форма втрати й вага позитиву

**Фігурні втрати програли.** `focal` і `close` дають найгірший розрив
(×1.2): вони тримають усю ногу біля 0.5 і піднімають хвіст лише трохи.
Пік формально є, але з такої відповіді не вибереш подію — гуде вся нога.
`close` до того ж перетинає 0.5 на 65 % подій.

**Тиша на початку ноги працює.** `quiet` заряджає вчетверо дорожче за
хибний сплеск на перших `HUSH = 50` подіях після півота, де сигналу
лише 0.3 %. Середня видача там падає з 0.153 до 0.053 — утричі тихіше, —
і на тій самій вазі `quiet` обходить `bce` за всіма числами
(AP 0.127 проти 0.120).

**Вага позитиву — не та ручка, якою лікується крик.** Вага 19 дає
найкращий розрив (×3.2) і найкращу влучність (23.2 %), але перетинає
0.5 на 14.9 % подій. Вага 1 прибирає крик повністю (0.3 %), але зрізає
розрив до ×1.8 і AP до 0.120. Проміжна вага 5 з тишею дає найкращу
**чесну** AP (0.149) і найкращий пік (23 %), проте за розривом гірша за
вагу 19. Тихо **й** гостро одночасно жодна вага не дає.

### Чому поріг 0.5 тут ні до чого

Половина — довільне число: воно має сенс там, де класи рівні, а тут
сигналу 5 %. Корисність міряє не воно, а **влучність на найгучніших
5 % подій**: якщо взяти стільки ж подій, скільки в сигналі, скільки з
них вгадано. Найкраще — `bce · вага 19` з 23.2 % проти 5.0 % навмання,
тобто **×4.6**. Лічильник на тому самому відборі дає 4.7 % — **гірше за
випадкове**: його найгучніші події це кінці найдовших ніг, а не кінці
ніг узагалі.

Тому на вкладці цей поріг домальовано пунктиром: сигналом рахується
тільки те, що його перетнуло, а вся крива під ним до справи не йде.

### Пік видачі в кінці ноги

Середня видача по десятинах ноги у двох кращих прогонів:

| Прогін | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| ------ | -: | -: | -: | -: | -: | -: | -: | -: | -: | -: |
| `bce` · вага 19 | 0.083 | 0.043 | 0.062 | 0.104 | 0.113 | 0.112 | 0.157 | 0.235 | 0.291 | **0.410** |
| лічильник | 0.004 | 0.017 | 0.027 | 0.040 | 0.051 | 0.061 | 0.054 | 0.056 | 0.063 | 0.063 |

Пік стоїть у десятій десятині, і від другої до десятої — у девʼять
разів. Лічильник теж росте до кінця ноги, але це не пік, а рівна
висхідна пряма: він не знає нічого, крім номера кроку. Та сама
монотонність робить його першим за часткою `пік` (30 %) — у висхідної
відповіді максимум ноги завжди стоїть у її кінці. Тому `пік` читається
**разом з `AP`**, а не замість неї.

### Перевчання

На train `bce · вага 19` має AUC 0.998 і `AP` 0.949 — мережа вивчає ноги
train майже напамʼять. На val лишається AUC 0.714 і `AP` 0.151. Розрив
величезний, але val при цьому **росте** до двадцятої епохи, а не падає:
60 833 ваги на 451 нозі запамʼятовують train швидше, ніж встигають
узагальнити, і рання зупинка за `AP` на val тримає прогін у корисній
точці.

### Висновок етапу

Мітка «останні 5 % ноги» вивчається: видача має пік у кінці ноги, і він
досить гострий, щоб із нього вибирати події — **23 % влучань** на
найгучніших 5 % ряду проти 5 % навмання. Клас несе це сам: без
розміткових лічильників лишається 12.9 % влучань, теж удвічі краще за
випадкове. Форма втрати майже не важить, а важать дві інші речі —
**тривалість навчання** (проста `bce` вчиться найдовше й приходить
найдалі) і **тиша там, де сигналу не буває** (перші 50 подій ноги).

## Вкладка дашборду

[`/class7dd`](http://localhost:8080/class7dd) — «Клас210х10-1х7д/д
уздовж ноги». Два розділи, кожен вантажиться своїм ендпоінтом.

### Розділ «Фічі класу вздовж ноги»

- Ендпоінт `/api/class7dd`, файл
  `results/class_edge_profile_class210x10m1x7dd.meta.json`.
- Таблиця: рядок — фіча, стовпці 1…10 — десятини ноги, далі краї,
  середина і `t`.
- Клітинка фарбується від рівня ряду цієї кривої: зелене вище, червоне
  нижче; насиченість — від найбільшого відхилення в таблиці.
- Перемикач **криви**: ранг напрямленої · ранг модуля · частка сплесків.
- Перемикач **ніг**: усі · вгору · вниз.
- Друга таблиця — Спірмен довжини ноги проти рівня фічі, три криві.

### Розділ «Довжина ноги з рівня фічі»

- Ендпоінт `/api/class7dd/length`, файл
  `results/leg_length_class210x10m1x7dd.meta.json`.
- Перша таблиця — рядок на `K` × набір входів: Спірмен, виграш проти
  медіани, AUC і перенос, окремо за датою й на відкладеній монеті.
- Перемикач **`K`** міняє дві нижні таблиці: групи ніг за рівнем фічі
  (медіана довжини і чверті) і Спірмен «голова / вся нога» на кожній
  фічі та кривій.
- У легенді сказано, що рівень по всій нозі як прогноз не існує.

### Розділ «Нова нога рядом на val: виявлення і прогноз кінця»

- Ендпоінти `/api/class7dd/series` (перелік монет) і
  `/api/class7dd/series/<PAIR>` (ряд однієї монети), файли
  `results/leg_length_series.json` і `leg_length_series_<PAIR>.json`.
- Три плашки на спільній осі X: ема10 ціни із зігзагом, оцінка детектора
  з порогом, довжина ноги в подіях.
- На ціні: **зелена риска** — нову ногу виявлено, **жовтий пунктир** —
  подія, на якій нозі скінчитись за прогнозом.
- Знизу те саме числом: скільки подій ноги пройдено, що сказала модель
  і скільки вийшло насправді.
- Перемикач монети показує, скільки ніг цієї монети виявлено.

### Розділ «Скільки подій до кінця ноги»

- Ендпоінт `/api/class7dd/left` — **усі прогони етапу 4** разом; кнопка
  моделі підписана памʼяттю і згорткою («памʼять 4 ніг · згортка») і
  показує похибку прогону.
- Таблиця: рядок — мережа, лічильник і медіана, стовпці — десятини ноги;
  зелене — найточніший у десятині.
- Поруч дві таблиці: грід ручок і крива навчання по епохах.

### Розділ «Видача мережі на val»

- Ендпоінти `/api/class7dd/left/epochs` і `.../epochs/<tag>` — видача
  кожної епохи на кожній події val.
- Дві плашки на спільній осі X: зверху ема10 ціни з півотами зігзага,
  знизу «подій до кінця ноги» — сіра пилка факту й крива мережі.
- Перемикачі: **модель**, **монета** і **епоха**; кнопки епох додаються,
  тому першу й останню видно поруч. На кнопці епохи стоїть її медіанна
  похибка, зірочка — на кращій.
- У списку моделей — девʼять прогонів: без памʼяті, памʼять 4 ніг і сім
  форм втрати на ній. Кнопка підписана тим, чим прогін відрізняється:
  «памʼять 4 ніг · ramp», а не імʼям файлу.
- Під перемикачем стоїть **пояснення обраної моделі** — глибина памʼяті
  й що саме робить її форма втрати, з тими самими числами ваг, що в
  `legleft_train.py`. Повний перелік усіх форм — у легенді розділу
  «Скільки подій до кінця ноги».

### Розділ «За скільки подій класу видно нову ногу»

- Ендпоінт `/api/class7dd/newleg`, файл
  `results/class_new_leg_class210x10m1x7dd.meta.json`.
- Рядок — вікно `X`: час вікна, частка мітки, AUC, точність на трьох
  порогах, затримка і найсильніша окрема колонка.
- У легенді сказано прямо, що точності між рядками не порівнюються.

## Вкладка «Розподіл фіч і драбини штрафу»

URL: [`/gauss`](http://localhost:8080/gauss). Вузька вкладка на один
вхід: усі блоки події в стандартному нормальному розподілі, памʼять
4 ноги, ручки `hidden 32 · lr 1e-3`, seed 0. Кожен прогін на ній
відрізняється від сусіда **тільки формою втрати**.

### Розділ «Три форми втрати на одному вході»

Таблиця: рядок на прогін, стовпці — десятини ноги, останній —
загальна медіана. Знизу дві бази: лічильник і train-медіана. Зелене —
хто найточніший у десятині. Під таблицею — що саме робить драбина
кожного прогону.

Дані: `/api/gauss` — короткі файли `legleft_blocks4_gauss_*.json` плюс
`wobble.json`.

### Розділ «Коридор і мовчання»

Головний результат вкладки. Таблиця по сходинках кроку від півота:
скільки подій, що каже мережа, що насправді, їхня частка і три частки —
у коридорі, нижче, вище. Рядок із рискою — межа вікна відповіді: нижче
неї ціль рівно нуль. Під таблицею — підсумок по вікну проти лічильника,
зелене означає «краще за базу».

Дані: `/api/gauss` → `band_check.json`, який рахує
`band_check.py --seeds` (середнє трьох незалежних засівів).

### Розділ «Рваність видачі вздовж ноги»

Ті самі прогони, але міряється не де мережа стоїть, а як вона туди йде:
медіана `|Δ + 1|`, частка кроків угору і частка провалів глибших за
−20 / −30 / −40, на середині ноги (10 … 85 % її частки). Рахує
`wobble.py`; розділ не малюється, якщо заміру ще немає.

### Розділ «Видача мережі на val»

Той самий розділ, що на вкладці `class7dd`, але перемикач моделей
звужений до цих трьох. Кнопки епох складаються — кілька кривих на одному
полотні. Посилання на конкретний вигляд:
`/gauss?run=blocks4_gauss_ramp&pair=BTC_USDT&epochs=1,5`.

Дані: `/api/gauss/epochs` і `/api/gauss/epochs/<тег>`.

## Вкладка «Сигнал кінця ноги»

URL: [`/tip`](http://localhost:8080/tip). Вузька вкладка на одне
питання: вхід той самий, що на `/gauss` — усі блоки події в стандартному
нормальному розподілі, памʼять 4 ноги, ручки `hidden 32 · lr 1e-3`,
seed 0, — але ціль інша: **мітка «останні 5 % ноги»** замість числа
подій до кінця. Прогони відрізняються один від одного формою втрати,
вагою позитиву і тим, чи заходять причинні лічильники; усе це стоїть у
назві кнопки, тому два прогони однієї втрати з різною вагою не
сплутати.

### Розділ «Пік видачі в кінці ноги»

Головний результат вкладки. Таблиця: рядок на прогін, стовпці — середня
видача мережі по десятинах ноги, далі AUC, `AP`, частка ніг із піком у
зоні та чесна `AP`. Зелене — найбільша десятина рядка: у доброго прогону
це десята. Знизу дві бази — лічильник і рівень сигналу.

Друга таблиця під нею — що дає найгучніша частина видачі, якщо взяти її
сигналом: скільки це подій, поріг, влучність, у скільки разів це більше
за рівень і повнота.

Дані: `/api/tip` — короткі файли `legtip_blocks4_gauss_clock_*.json`.

### Розділ «Видача мережі на val»

Той самий розділ, що на `/class7dd` і `/gauss`, але під мітку: зверху
ема10 ціни із зігзагом 2.3 %, під нею сіра смуга останніх 5 % кожної
ноги, видача мережі поверх неї і **пунктир порога** — рівня, вище якого
лежать найгучніші 5 % подій усієї val. Сигналом рахується тільки те, що
цей пунктир перетнуло; по ньому ж лічиться влучність у таблиці зверху.
Найнижча плашка — сама видача на своїй шкалі, без смуги. Кнопки епох
складаються — кілька кривих на одному полотні, число на кнопці — `AP`
цієї епохи. Посилання на конкретний вигляд:
`/tip?run=blocks4_gauss_clock_bce&pair=BTC_USDT&epochs=20`.

Дані: `/api/tip/epochs` і `/api/tip/epochs/<тег>`.

## Приклади

Профіль однієї фічі:

```bash
python3 - <<'PY'
import json
p = json.load(open("research/29-class7dd-legs/results/"
                   "class_edge_profile_class210x10m1x7dd.meta.json"))
one = p["by_feature"]["d_up"]["all"]
print(" ".join(f"{v:.3f}" for v in one["amplitude"]))
print(one["amplitude_edges"], one["amplitude_middle"], one["amplitude_t"])
PY
```

Очікуваний вихід:

```
0.594 0.551 0.548 0.540 0.529 0.532 0.545 0.534 0.568 0.641
0.6177... 0.5359... 10.19...
```

Довжина ноги з рівня фічі:

```bash
python3 - <<'PY'
import json
p = json.load(open("research/29-class7dd-legs/results/"
                   "leg_length_class210x10m1x7dd.meta.json"))
one = p["by_window"]["100"]["by_kit"]["head_prev"]
print(round(one["spearman"], 3), round(one["auc"], 3),
      round(one["apart"]["auc"], 3))
PY
```

Очікуваний вихід:

```
0.583 0.806 0.716
```

Одна подія класу до нормалізації і після:

```bash
python3 research/29-class7dd-legs/scripts/norm_show.py --norm gauss
```

Очікуваний вихід — див. «Клас після розподілу» в результаті етапу 4:
`d_price_avg` блоку 1 дорівнює `10`, після розподілу `0.863`; медіана
`|каналу|` класу падає з 58.4 до 0.676, максимум — з `2.5e23` до 4.75.

## Перевірка перед завершенням

| # | Крок | Стан |
| - | ---- | ---- |
| 1 | окремий `.md` файл дослідження | цей файл |
| 2 | перевірка на зайве | у файлі лише пʼять етапів, кожен зі своїм скриптом і виводом |
| 3 | перевірка логіки | етап 1 показує край ноги в кожній фічі, етап 2 міряє, за скільки подій цей край стає сигналом, етап 3 — що з рівня фічі видно довжину ноги наперед, етап 4 веде ногу подія за подією числом, етап 5 питає те саме міткою і показує пік видачі в кінці ноги |
| 4 | відтворюваність | команди вище відновлюють усі JSON з нуля |
| 5 | чистка статті | — |

## Тести

| Файл | Що перевіряє |
| ---- | ------------ |
| [`tests/test_class7dd_legleft.py`](../../tests/test_class7dd_legleft.py) | канали класів, чотири нормалізації з train-бази, ціль «до кінця ноги», розбиття ніг, форми втрати, стеля довжини і готовий JSON етапу 4 |
| [`tests/test_class7dd_norm.py`](../../tests/test_class7dd_norm.py) | замір нормалізацій (склеювання, зворотний хід, множник різниць) і показ однієї події класу до перетворення й після |
| [`tests/test_class7dd_band.py`](../../tests/test_class7dd_band.py) | причинні лічильники, коридор у частках цілі, мовчання після вікна, оцінка епохи |
| [`tests/test_class7dd_legtip.py`](../../tests/test_class7dd_legtip.py) | мітка кінця ноги, ціль у батчі й маска памʼяті, AUC і середня влучність, база лічильника, чесна оцінка на половині ніг і три форми втрати |
| [`tests/test_class7dd_wobble.py`](../../tests/test_class7dd_wobble.py) | кроки видачі всередині ноги, відрізок 10 … 85 %, числа рваності |
| [`tests/test_class7dd_profile.py`](../../tests/test_class7dd_profile.py) | ранги, три криві, профіль по десятинах, розріз за довжиною, готовий JSON; база рангів тільки з train, рівень голови, набори входів, групи за рівнем і готовий JSON етапу 3 |
| [`tests/test_dashboard.py`](../../tests/test_dashboard.py) | `TestClass7dd`, `TestClass7ddNewLeg`, `TestClass7ddLength`, `TestClass7ddSeries`, `TestClass7ddLeft`, `TestClass7ddEpochs`, `TestClass7ddMarkup`, `TestTipTab`, `TestTipEpochs` — ендпоінти й розмітка вкладок |
