S0 · STEP
Как выбирается STEP на странице «S0 · Сегменты»
Кнопка «Расчёт STEP» на S0 · Сегменты запускает калибратор автора s0/base/step_protocol_v2.py без правок (логика A, ядро Core v2). Ниже — что он делает по шагам, как я это прочитал в коде, и что вокруг него добавляет наша служба s0/run.py. Числа в примере — из настоящего расчёта BTCUSDT 15M от 18.10.2026.
Коротко
- Берутся последние окно профиля + 70 закрытых свечей до даты старта.
- В первых 70 свечах этого окна ищется S0 — экстремум, дальний от close 71-й свечи.
- Размахи свечей (high − low) последнего окна профиля делятся на две группы: «спокойные» (μA) и «широкие» (μB). Из них — два диапазона STEP: малый [μA/4 … 1,5·μA] и крупный [1,5·μA … 2·μB].
- Каждый диапазон перебирается сеткой от 50 до ~500 значений; каждое значение прогоняется через ядро по свечам окна от S0 до конца окна.
- У каждого кандидата считается score = средний PnL сделки × поправка на долю выигрышей × штраф за просадку.
- Выбирается кандидат с наибольшим score в малом диапазоне. Крупный считается и показывается в таблице, но выбран бывает, только если в малом нет ни одного кандидата со сделкой.
1. Окно калибровки
Калибратор смотрит только на прошлое: на свечи строго до даты старта, которую вы ставите в поле «Дата старта, UTC». Длина окна = окно профиля по ТФ (константа автора PROFILE_WINDOW_BY_TF) + 70 свечей под поиск S0.
| ТФ | окно профиля | всего свечей | примерно по времени |
|---|---|---|---|
| 5M | 2000 | 2070 | 7,2 дня |
| 15M | 800 | 870 | 9 дней |
| 30M | 650 наше, у автора нет | 720 | 15 дней |
| 1H (60M) | 500 | 570 | 23,8 дня |
| 4H (240M) | 300 | 370 | 62 дня |
| 1D | 125 | 195 | 6,4 месяца |
| 1W | 75 | 145 | 2,8 года |
Для 30M у автора нет окна профиля, и его CLI не принимает --timeframe 30M. Служба передаёт 30M как 15M с явным --profile-window 650 — это временное значение между 15M (800) и 1H (500), пока автор не задаст своё.
2. S0 в окне калибровки
Функция автора find_s0_starting:
- в первых 70 свечах окна берутся абсолютный High и Low;
- цена каждого уточняется: из open/close самой свечи экстремума и её соседей ±1 берётся ближайшая к High (Low) — то есть S0 стоит на уровне тела, а не тени;
- «якорь» — close 71-й свечи окна. S0 — тот из двух экстремумов, который дальше от якоря. Если это High — направление SHORT, если Low — LONG.
От этой S0 до конца окна и идёт цепочка, на которой проверяются кандидаты. Поэтому цепочка в окне короче самого окна: от свечи S0, а не от первой свечи.
3. Профиль размаха и диапазоны STEP
Берутся последние окно профиля свечей (без первых 70). Для каждой свечи считается размах high − low. Распределение размахов раскладывается смесью двух нормальных распределений (GMM, EM-алгоритм, 2 компоненты):
- A — спокойные свечи: средний размах μA, разброс σA, доля πA;
- B — широкие свечи: μB, σB, πB.
Из них только два числа задают, где искать STEP:
малый диапазон: μ_A / 4 … 1,5 · μ_A
крупный диапазон: 1,5 · μ_A … 2 · μ_B
Медиана размаха (median_range) нужна дальше для штрафа за просадку. Остальное (σ, π, среднее, 84-й перцентиль) сохраняется как «отпечаток» профиля — с ним блок «Фаза волатильности» потом сравнивает текущий рынок и говорит, не пора ли пересчитать STEP.
4. Сетка кандидатов
Каждый диапазон перебирается с постоянным шагом сетки:
шаг сетки = 10^⌊log10((верх − низ) / 50)⌋
То есть ширина диапазона делится на 50 и округляется вниз до степени десяти. Поэтому кандидатов не 50, а от 50 до ~500: для BTC 15M малый диапазон шириной 221 получил шаг 1 (222 кандидата), крупный шириной 575 — шаг 10 (58 кандидатов). Сетка начинается от нижней границы диапазона, поэтому STEP получаются «некруглые»: 256,28, а не 256.
5. Прогон каждого кандидата через ядро
Для каждого STEP вызывается core.chain_segments — та же цепочка сегментов, что рисуется на графике (KEY = S0 ± 3·STEP, PARTIAL ± 6·STEP с закрытием половины, TARGET ± 8·STEP, стоп с буфером 0,5·STEP, разворот). Затем core.evaluate_events считает по закрытым сделкам:
- avg_pnl — средний результат сделки в пунктах цены (PARTIAL + выход);
- win_rate — доля сделок с результатом > 0;
- max_dd — большее из двух: просадка накопленного PnL по цепочке сделок и худшее движение против позиции внутри одной сделки (по close).
Кандидат без единой закрытой сделки в окне отбрасывается. Комиссии, проскальзывание и фандинг не учитываются.
6. Оценка score
score = avg_pnl × (0,5 + 0,5 · win_rate) × dd_penalty
dd_penalty = 1 − 0,5 · min(1, max_dd / (10 · median_range))
- Множитель win rate — от 0,5 (ни одной прибыльной) до 1 (все прибыльные).
- Штраф за просадку — от 1 (просадки нет) до 0,5 (просадка ≥ 10 медианных размахов свечи).
- Score — в пунктах цены на сделку, как и avg_pnl. Отрицательный avg_pnl даёт отрицательный score.
7. Выбор
Автор сохраняет выбор (best_step) так: берёт диапазоны логики A по порядку — сначала малый, потом крупный — и в первом, где есть хоть один кандидат, берёт STEP с максимальным score. В таблице на странице он отмечен ★.
Значит, на деле выбор всегда из малого диапазона, если там нашлась хоть одна сделка. Крупный диапазон проверяется и печатается, но в выбор попадает только как запасной.
Что вокруг калибратора делает страница и служба
- «Расчёт STEP» — служба
s0/run.pyвырезает из БД окно до даты старта, проверяет, что в нём нет пропусков, и запускает калибратор автора как отдельную программу. - Стартовая S0 цепочки ищется той же
find_s0_starting, но по свечам от даты старта: нужна 71 закрытая свеча после неё. Отсюда разные даты старта у 1D (01.01.2026) и 1W (01.01.2025). - Сверка: служба заново прогоняет каждого кандидата через ядро на окне и сверяет PnL с цифрой калибратора; расхождение — ошибка расчёта.
- Колонки «От старта цепочки до последней закрытой свечи» — тот же STEP на свечах после даты старта, до сегодняшнего дня. Калибратор этих свечей не видит, и на выбор ★ они не влияют. Это проверка задним числом: если выбирать STEP по этим колонкам, мы подглядываем в будущее.
- Выбор строки и «Пересчитать» — контур переписывается выбранным STEP, служба строит цепочку заново. ● — принятый сейчас STEP. Если ввести STEP руками, не из таблицы, контур помечается «STEP задан владельцем вручную».
- Когда пересчитывать — подсказывает блок «Фаза волатильности» (
volatility_phase.pyавтора): сравнивает профиль, на котором выбран STEP, с текущим; если спокойная компонента A ушла из допуска —rescan_step.
Пример: BTCUSDT 15M, дата старта 01.09.2026
| Окно калибровки | 870 свечей, 22.08.2026 22:30 — 31.08.2026 23:45 UTC |
|---|---|
| S0 в окне | 75 903,1 LONG (Low) на 23.08.2026 05:15; цепочка в окне — 843 свечи |
| Профиль (800 свечей) | μA = 177,1 (72 % свечей), μB = 420,2 (28 %), медиана размаха 200,7 |
| Малый диапазон | 44,28 … 265,68, шаг сетки 1 → 222 кандидата |
| Крупный диапазон | 265,68 … 840,38, шаг сетки 10 → 58 кандидатов |
| Стартовая S0 цепочки | 79 155,1 SHORT на 01.09.2026 05:45 |
| STEP | диапазон | сделок в окне | win % | PnL окна, п. | score | после старта: сделок | PnL, п. |
|---|---|---|---|---|---|---|---|
| 256,28 ★ | малый | 23 | 43,5 | +704,6 | 11,0 | 103 | +2 015,6 |
| 193,28 | малый | 34 | 50,0 | +940,8 | 10,4 | 154 | −929,2 |
| 257,28 | малый | 23 | 39,1 | +584,2 | 8,8 | 103 | −295,2 |
| 425,68 | крупный | 7 | 57,1 | +2 499,6 | 156,5 | 43 | +5 631,4 |
Калибратор выбрал 256,28 — лучший score в малом диапазоне. У 425,68 score в 14 раз выше, но он в крупном диапазоне и в выбор не попадает. Сейчас в контуре BTC 15M стоит STEP 315, заданный вручную через «Пересчитать».
Как я это понимаю разбор 18.10.2026
- Score в пунктах растёт вместе со STEP. Сделка на STEP 500 приносит и теряет примерно вдвое больше пунктов, чем на STEP 250. Поэтому сравнивать score между диапазонами нечестно — у крупного он почти всегда больше. Видимо, поэтому автор берёт малый диапазон первым и не сравнивает его с крупным. Во всех 27 наших расчётах выбран малый диапазон; в 19 из них максимум score — в крупном.
- Внутри малого диапазона тот же перекос: при равном качестве сделок выигрывает STEP ближе к верхней границе. У BTC 15M выбранный 256,28 — у самого края (265,68).
- Сделок в окне мало. У BTC 15M 23 сделки за 9 дней. Соседние STEP 256,28 и 257,28 отличаются на 1 пункт, а после старта дали +2 016 и −295 пунктов. Выбор по такому окну шумный: лучший в окне STEP не обязан быть лучшим дальше.
- Издержки не входят в оценку. При малом STEP сделок больше, а край на сделку меньше — комиссия бьёт сильнее. Калибратор этого не видит (см. История S0: с комиссией все варианты в минусе).
- STEP задан в пунктах цены и после принятия не меняется, пока его не пересчитают. Следить за этим должен блок «Фаза волатильности».
- Это моё прочтение кода, а не формулировки автора. Вопросы к автору: почему крупный диапазон только запасной; почему верх крупного — 2·μB; почему шаг сетки округляется вниз до степени десяти.