S0 · STEP

Как выбирается STEP на странице «S0 · Сегменты»

Кнопка «Расчёт STEP» на S0 · Сегменты запускает калибратор автора s0/base/step_protocol_v2.py без правок (логика A, ядро Core v2). Ниже — что он делает по шагам, как я это прочитал в коде, и что вокруг него добавляет наша служба s0/run.py. Числа в примере — из настоящего расчёта BTCUSDT 15M от 18.10.2026.

Коротко

  1. Берутся последние окно профиля + 70 закрытых свечей до даты старта.
  2. В первых 70 свечах этого окна ищется S0 — экстремум, дальний от close 71-й свечи.
  3. Размахи свечей (high − low) последнего окна профиля делятся на две группы: «спокойные» (μA) и «широкие» (μB). Из них — два диапазона STEP: малый [μA/4 … 1,5·μA] и крупный [1,5·μA … 2·μB].
  4. Каждый диапазон перебирается сеткой от 50 до ~500 значений; каждое значение прогоняется через ядро по свечам окна от S0 до конца окна.
  5. У каждого кандидата считается score = средний PnL сделки × поправка на долю выигрышей × штраф за просадку.
  6. Выбирается кандидат с наибольшим score в малом диапазоне. Крупный считается и показывается в таблице, но выбран бывает, только если в малом нет ни одного кандидата со сделкой.

1. Окно калибровки

Калибратор смотрит только на прошлое: на свечи строго до даты старта, которую вы ставите в поле «Дата старта, UTC». Длина окна = окно профиля по ТФ (константа автора PROFILE_WINDOW_BY_TF) + 70 свечей под поиск S0.

ТФокно профилявсего свечейпримерно по времени
5M200020707,2 дня
15M8008709 дней
30M650 наше, у автора нет72015 дней
1H (60M)50057023,8 дня
4H (240M)30037062 дня
1D1251956,4 месяца
1W751452,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 ★малый2343,5+704,611,0103+2 015,6
193,28малый3450,0+940,810,4154−929,2
257,28малый2339,1+584,28,8103−295,2
425,68крупный757,1+2 499,6156,543+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; почему шаг сетки округляется вниз до степени десяти.