Тема
RTDS Toxic-Flow Activation Runbook (pm-engine)
Операторский playbook пошаговой активации RTDS toxic-flow детектора в pm-engine. Это процедура (staged rollout), а не модель: контракт, knob-таблица и метрики — в SSOT
../integrations/rtds.md. Здесь только последовательность включения.
В pm-engine RTDS-соединение поднимается по потребителю в config.db (решение оператора 2026-08-31, env-переключателя нет), toxic-flow cancel — за двумя config.db-гейтами (fail-safe):
- RTDS-соединение включается тем, что активная строка стратегии называет потребителя:
stop_loss.confirm_source = "rtds_actor_buffer"либо присутствующая секцияdirectional.toxic_flow. Ни того ни другого — соединение не открывается. env для этого переключателя ЗАПРЕЩЁН (ADR-015); заданнаяENGINE_RTDS_ENABLEDотклоняется на старте как снятая переменная. - Toxic-flow cancel гейтится двумя knob'ами в том же блоке
directional.toxic_flow:log_only(Gate 1) →active_cancel_enabled(Gate 2). Оба по умолчанию SAFE (log_only=true,active_cancel_enabled=false). env для этих knob'ов ЗАПРЕЩЁН (ADR-015) — их нет в env, они не читаются.
Строгий порядок: не открывать Gate 2 до завершения baseline-обкатки и чистого прогона Gate 1.
Related:
../integrations/rtds.md— модель, knob-таблица, observability (SSOT).crates/pm-engine/src/adapters/rtds/— реализация (toxic_flow.rsдетектор,toxic_flow_consumer.rsгейтинг).
0. Как менять toxic_flow knob'ы
Блок directional.toxic_flow — вложенный объект внутри опубликованной версии definition стратегии в config.db. После schema v3 менять его можно только полной типизированной версией через dashboard Strategies editor или pm-cli strategy versions create; затем версию нужно явно опубликовать. Прямая правка строки и legacy field update запрещены.
Reload: движок читает config.db при старте; hot-reload нет. Любая правка knob'а вступает в силу только после рестарта pm-engine (scripts/deploy-vps.sh engine-bot или dashboard Reset — см. local-dashboard-runbook.md §Reset).
1. Pre-flight — включить ingestion
БЛОКИРУЮЩАЯ НЕСОВМЕСТИМОСТЬ — проверить ДО шага 1. Пара «
stop_loss.flow_sell_share_min_permilleзадан вconfig.db» + «stop_loss.confirm_source = "rtds_actor_buffer"» — это инвариант типизированной секции (ConfigError::StopFlowShareWithRtdsConfirmSource,config/loader.rs), а не предупреждение: доля продаж считается по анонимной ленте store'а, тогда какrtds_actor_bufferберёт подтверждающие принты из actor-буфера RTDS, то есть кандидата судили бы числа ЧУЖОЙ ленты (design.md D5 измененияlossless-stop-confirm-trade-window). Проверку проходят и загрузчик движка на старте, и путь сохранения ПОЛНОГОdefinition(pm_engine::config::versioning::validate_version_snapshot, тот же каскад, что вызывает и конфигуратор дашборда, иpm-cli strategy add/versions create) — отказ ловится ПРИ СОХРАНЕНИИ, а не только на следующем рестарте/Reset.pm-cli strategy updateэтот каскад дляconfirm_sourceНЕ проходит: структурные флаги команды поле не покрывают (crates/pm-cli/CLAUDE.md§8) — правка существующей строки только черезversions create.Проверить и, если ключ задан, СНЯТЬ его из
config.dbдо переключения источника наrtds_actor_buffer:bashscripts/stop-evidence-forward-check.sh --preflight-only # раздел 0 = ровно эта параИзвестная граница: сам скрипт пока читает снятую
ENGINE_RTDS_ENABLEDиз.env(не полеstop_loss.confirm_source) — его вывод о состоянии RTDS-транспорта после этой карточки не заслуживает доверия; проверка пары «доля потока + источник» в разделе 0 читаетconfig.dbнапрямую и не зависит от этого расхождения. Обновление скрипта — отдельная задача, не покрытая этой карточкой.
- Задать
stop_loss.confirm_source = "rtds_actor_buffer"в определении активной стратегии (dashboard Strategies editor илиpm-cli strategy versions create+ публикация — см. §0 выше), оставитьdirectional.toxic_flowна SAFE-дефолтах (log_only=true,active_cancel_enabled=false). Если цель — только детектор токсичного потока без переключения подтверждения стопа, источник можно оставитьanonymous_tapeи задать только секциюdirectional.toxic_flow— она сама по себе потребитель и поднимет транспорт. - Рестарт pm-engine. В логах ожидать
rtds: transport enabled — consumer(s) configuredсо строкойconsumers, перечисляющей включённых потребителей (stop_confirm,toxic_flowили оба). Если вместо неё виднаrtds: transport disabled, no consumer configured (no connection)— потребитель не сохранился, проверить строку стратегии. - В течение ~2 минут наблюдать, что ряды
pm_engine_rtds_tracked_tokensиpm_engine_rtds_own_*движутся (точный перечень и что каждый ряд значит —../integrations/rtds.md§Observability; toxic-flow счётчики на/metricsНЕ выведены — их наблюдаемость только черезtracing-логи, см. §2 ниже). Если соединение не поднимается — стоп, разобрать до продолжения.
Standalone one-shot RTDS wire-probe (legacy pm-bot) в pm-engine отсутствует — валидация wire-контракта = наблюдение rtds_* метрик на живом соединении.
2. Baseline в log-only (≥48h)
Оставить движок с SAFE-дефолтами (log_only=true) минимум на 48 часов. Toxic-flow счётчики (RtdsMetrics::toxic_*) на Prometheus /metrics НЕ выведены — ни один describe_counter!/metrics::counter! их не публикует; наблюдаемость baseline идёт только по структурным tracing-логам (подробности — ../integrations/rtds.md §Observability):
- Логи
rtds.toxic_flow.detected (log_only)— оценить false-positive rate поcondition_id(повторные срабатывания на тонком рынке ⇒ порог низок; корреляция с последующими CLOB-движениями ⇒ истинные позитивы).
Калибровка (опционально): если много false-positive — поднять threshold_size в directional.toxic_flow, рестарт, перезапустить baseline. Если строк rtds.toxic_flow.detected нет за 48h — порог слишком высок, либо рынки слишком тонкие, либо min_trade_size рубит все события.
3. Gate 1 — log_only=false
Выставить directional.toxic_flow.log_only=false, оставив active_cancel_enabled=false. Рестарт.
После рестарта:
- Логи меняются с
rtds.toxic_flow.detected (log_only)наrtds.toxic_flow.would_cancel (active_cancel disabled). - Ни один
Decision::CancelOrderне доходит до executor'а — проверить по логам executor'а (префикс причиныrtds_toxic_flow:не должен появляться).
Обкатать ≥24h. Убедиться, что частота would_cancel по магнитуде совпадает с частотой detected из шага 2 (сравнение только по логам — счётчиков на /metrics для этого нет).
4. Gate 2 — active_cancel_enabled=true
БЛОКИРУЮЩАЯ ГРАНИЦА (design.md D5 карточки
rtds-transport-remediation, принято 2026-09-06). Параlog_only=false+active_cancel_enabled=true— та самая, чтоToxicFlowGates::resolveпревращает вDecision::CancelOrder, — ОТКАЗЫВАЕТ на сохранении/старте, покаToxicFlowDefect::open()не пуст. Сегодня открыты четыре из пяти дефектов детектора (актор, агрегация без исхода Yes/No, скоуп отмены поcondition_id+сторона без токена, отсутствие hysteresis у повторной отмены — перечень и статус каждого в../integrations/rtds.md§«Harvester activation status»). Проверка одна на все три поверхности записи (движок на старте, сохранение конфигуратора,pm-cli strategy add/versions create) — этот шаг сегодня НЕ ВЫПОЛНИМ ни на одном из них, пока оператор не примет оставшиеся дефекты явным решением и код не снимет их изopen().
Только после чистого прогона Gate 1 (24h+) И закрытия/принятия оператором оставшихся дефектов из ToxicFlowDefect::open(). Выставить directional.toxic_flow.active_cancel_enabled=true. Рестарт.
После рестарта:
- Появляются
rtds.toxic_flow.actual_cancelлог-строки. - Executor начинает получать
Decision::CancelOrderсreason = rtds_toxic_flow:<signed_volume>. Сверить счётчики открытых ордеров в SPA.
Первый час — плотный мониторинг. Тревожно, если:
actual_cancels_total>>would_cancel_totalиз шага 3 — active-side фильтр слишком агрессивен, разобрать до продолжения.- Просадка P&L совпадает с отменами — проверить
condition_idв warn-логах, порог может быть занижен.
5. Rollback
Любой из гейтов откатывается правкой directional.toxic_flow в config.db + рестарт. active_cancel_enabled=false мгновенно останавливает эмиссию отмен, сохраняя would-cancel-логирование. Полный откат в log-only — вернуть log_only=true.
Полностью отключить RTDS-ingestion — убрать секцию directional.toxic_flow из определения (или оставить anonymous_tape в stop_loss.confirm_source, если она была единственным потребителем), опубликовать версию, рестарт: ни одного потребителя не осталось, транспорт не поднимается.