Разбор по коду и живой базе

Доверенные события: что действительно закрыто из ТЗ и в каком порядке закрывать остальное

Ответ на документ «События watchrobot», построчная сверка реализации с исходным техзаданием на метрики и план работ — сначала бесплатные и быстрые, потом контрактные.

4 августа 2026 ветка bot_squad/dev каталог 2026-07-28.atomic-v7 база signal_tracker_dev

Как проверялось

Каждое утверждение ниже опирается либо на строку кода, либо на запрос к живой базе. Наличие модуля в репозитории доказательством не считалось: несколько правил написаны, покрыты тестами и при этом не выполняются в проде. Ссылки вида trusted_market.py:1062 — это точное место, где можно проверить.

Важная методологическая оговорка, которая касается всех статусов сразу. Все 8 057 строк в таблице events записаны за последние 48 часов, а в контейнере стенда стоит TRUSTED_EVENTS_ENABLED=0. То есть «доходит» сейчас означает «правило прогоняется бэкфилл-ранами и даёт строки», а не «работает непрерывный контур». Ниже слово «живой» используется именно в этом, ослабленном смысле.

Б

Ответ на документ «События watchrobot»

Документ в целом добросовестный, и его методология лучше, чем у большинства отчётов такого рода. Но в нём девять фактических ошибок, и две из них изменили порядок работ.

Что сделано правильно

Три решения в документе я бы сохранил как есть.

  • Колонка «Доходит» ставится по живой ленте, а не по наличию кода. Это единственно верный способ мерить такую систему, и именно он позволил обнаружить, что типов двадцать, а доходит одно семейство.
  • Четыре причины «почему не доходило» разделены. Выключатели, контролёр, дизайн watched-вселенной и сеть — это действительно четыре разных класса проблем с разной стоимостью починки, и смешивать их было бы хуже.
  • Цифры по ЦБ точны. 1269 наблюдений ставки плюс 2470 курсов дают ровно 3739 сырых записей, что совпадает с базой. source_records_raw where source='cbr' → 3739

Главное, чего в документе не хватает: контролёр покрытия — несущая конструкция

Контролёр вынесен в пункт 4 конвейера как «выключаемый». Подтвердил: feed.coverage_gate, default=False, решение стейкхолдера от 04.08 записано прямо в докстринге. trusted_event_feed.py:44-54 · event_settings.py:110-118

Но следствие не проговорено. Каталог покрытия устроен fail-closed: ни один атом не получает full по умолчанию. Значит, каждый статус «доходит ✅» в таблице существует ровно потому, что гейт отключён. Включите его обратно — и лента опустеет почти целиком, включая кривую, спреды и цену/объём.

Развилка бинарная, и её должен закрыть человек, а не код. Либо гейт не вернётся никогда — тогда 327 атомов, верификатор и материализатор превращаются в мёртвый груз, который вы продолжаете сопровождать. Либо вернётся при подписании контрактов — тогда в этот день лента гаснет, если атомы не промоутнуты заранее. Ни один из планов работ этого не учитывает.

Ошибки документа

  1. Б.1
    «По облигациям свечи докачиваются» — свечи уже там.

    41 798 баров по 339 облигациям, с 2021-09-30 по 2026-08-04 включительно, лежат в ohlcv_daily. Ничего не докачивается. ohlcv_daily ⋈ instruments → bond: 339 instr / 41 798 bars

    Событий ноль потому, что вызов передаёт evaluate_anomalies = kind == "stock", и ветка с BOND_THRESHOLDS недостижима — вне trusted_rules.py эта константа встречается только в тестах. trusted_market.py:1062, :1201 · пороги на :638

    Это не косметическая правка формулировки. Пока строка говорит «данные подтягиваются», пункт I.10 читается как «подождать», и из-за этого четыре требования ТЗ выпали из плана работ. На самом деле это одна строка кода и реплей по данным, которые уже собраны.

  2. Б.2
    Ключевая ставка — не «каждое изменение», а порог 0,75 п.п.

    KEY_RATE_CHANGE_THRESHOLD_PP = Decimal("0.75"), и это ровно M-RATE-1 из ТЗ. Направление события — conditional, а не автоматический знак, и это правильно. trusted_cbr.py:68, detect_key_rate_changes

  3. Б.3
    «Все акции покрыты событиями» — активные события есть у 14 инструментов из 54.

    Свечи есть у 53. Формулировка смешивает загруженность данными и наличие событий, а перечисление пяти тикеров создаёт впечатление полноты. distinct instrument_id, kind=price_volume_anomaly, active → 14

  4. Б.4
    G-спреды: прогон закончился, но остаток — не «2 бумаги», а 333.

    3635 событий вместо 681, последняя запись 15:00. Покрыто 140 облигаций из 473 — остальные не проходят гейт ликвидности либо не имеют сопоставимой базы. Разница между «2» и «333» существенна для планирования. distinct instrument_id, kind=credit_spread_move → 140 / 473

  5. Б.5
    Рейтинги: живых апгрейдов, даунгрейдов, прогнозов и watch — ноль.

    Все 60 видимых в ленте строк (12 active + 48 corrected) — это WD — Отзыв кредитного рейтинга, все по облигациям Роснефти, 2020–2021, у всех direction=neutral. Отброшенные 426 — это AF (342) и NW (84). events kind=rating_action, группировка по коду действия

    Формулировка «исторические 60 материальных событий показываются» создаёт впечатление, что пункт I.6 частично закрыт исторически. Он пуст: ни одного из требуемых ТЗ действий в ленте нет.

  6. Б.6
    Оба дефолтных события — это снятие дефолта, а не дефолт.

    Обе записи имеют подтип payment_default_resolved (B-PAY-8) и direction=positive: «обязательство исполнено — снят дефолт по купону» и «…по погашению номинала». Событий самого факта невыплаты (B-PAY-1…6) — ноль. RU000A107AX1 · RU000A103SV6, cbonds-emission-default.v1

    Пояснение «честные 2: наши эмитенты не дефолтились» не согласуется с этим: дефолты были, распознан только выход из них.

  7. Б.7
    «factor_exposure_move из воркера НЕ вызывается» — вызывается.

    Воркер импортирует reconcile_all_watched_factor_impacts as project_watched_factor_events и вызывает его. Затенена одноимённая функция из trusted_factor_projection — это неопрятно, но дефекта интеграции нет. trusted_worker.py:57-59, вызов на :398

  8. Б.8
    Счётчик «0» для факторной чувствительности измеряет не ту таблицу.

    Импакты пишутся в event_impacts, а в ленте появляются как семейство factor_exposure_move через маппинг родителя. По events.kind там ноль по построению, независимо от работоспособности. trusted_event_feed.py:247

    Настоящие числа: event_impacts — 0 строк; issuer_factor_profiles — 67 строк, все с ролью rate_sensitive. Профилей выручки, долга, производителя и потребителя нет ни одного.

  9. Б.9
    Строка «Аномалия цены/объёма → Доходит ✅» описывает две разные вещи одним статусом.

    По акциям — да, 202 активных события. По облигациям — ноль, и логика, описанная в той же ячейке («размах дня ≥1%, ≥3% за 5 сессий»), не выполняется вообще. Одна галочка на две асимметричные половины скрывает, что бо́льшая по объёму половина рынка не обслуживается.

  10. Б.10
    «Публичный showcase-адаптер запрещён архитектурно» — неверно, и цена ошибки здесь самая высокая.

    Бесплатный адаптер витрины разбирает значения и выдаёт ровно метрики ТЗ. Блокируют его две снимаемые без денег вещи: env-флаг FNS_GIRBO_PUBLIC_UI_CONTRACT_VERIFIED (подтверждение письменного разрешения на использование) и правило маршрутизации, уводящее trusted-режим на платный REST. trusted_fns_bfo.py:105-116 · trusted_worker.py:480-490

    При этом платный REST значений не разбирает вообще — его собственный докстринг это фиксирует. То есть строка «ГИР БО открывает три семейства» описывает бесплатный адаптер, а рекомендация «купить REST» ведёт к источнику, после которого остаётся написать парсер документов. Разбор целиком — в разделе Г; в плане это решение 0.2, шаг 2.5 и условный шаг 3.4.

Ошибка Б.10 найдена при разборе альтернативных источников и изменила не только текст документа, но и порядок работ: платный ГИР БО ушёл из третьего приоритета в условный шаг.

Одно замечание не к фактам, а к рамке

Пункт «TRUSTED_EVENTS_ENABLED_STAGING=1 в protected .env» стоит шестым в техническом хвосте раздела «что делать дальше». Ему там не место. Пока флага нет, контур не работает — он воспроизводится руками, и вся база собрана ad-hoc прогонами за двое суток. Это первая строка раздела «текущий статус», а не последняя строка раздела «доделать».

А

Соответствие исходному ТЗ на метрики

Построчно, по номерам разделов и пунктов исходного документа. Привязка к требованиям канонического каталога (B-CURVE-1, T-VOL-3, M-FX-3 и т.д.), где она есть.

4группы требований закрыты живыми правилами
14реализованы, но не выполняются
17производителя нет вовсе
5спорных трактовок в самом ТЗ

Значения статусов

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

Соответствие: полное · частичное · нет · сверх ТЗ.

№ ТЗ Требование Реализация Техстатус Соответствие Чего не хватает и в чём проблема трактовки
Раздел I. Облигации
I.1 Спреды по кривой 1–3 / 1–5 / 1–10 / 2–10 лет yield_curve_move
B-CURVE-1…4
живой · 2051 частичное Все четыре спреда, порог 10 б.п. от последней отправленной базы, подпороговые дрейфы копятся. Инверсия разобрана честно: inversion_started / deepened / normalized / ended плюс обычные flatten/steepen. Не хватает декомпозиции по ногам — yields_pct лежит в payload, но знак не различает «длинный конец вырос» и «короткий упал», а ТЗ различает явно.
I.2a Долг / EBITDA B-CREDIT-1/2 кода нет нет Принципиально невыводимо из РСБУ: EBITDA там отсутствует, слово встречается в модуле ГИР БО один раз — в дисклеймере. Нужен point-in-time МСФО. Метод Cbonds отдаёт 401 — не входит в подписку.
I.2b Чистый денежный поток credit_metric_change
B-CREDIT-3
реализовано, не запущено частичное Правило есть: negative_net_cash_flow и falling_net_cash_flow, оба из ТЗ. Только годовой РСБУ, поэтому событие приходит с опозданием до года. Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2.
I.2c Покрытие процентов B-CREDIT-4/5 реализовано, не запущено частичное interest_coverage_below_2x реализован ровно по ТЗ. Числитель и знаменатель берутся из строк 2300/2330 РСБУ, а не из EBIT и процентных расходов по МСФО. Отдельного события «восстановление выше 2x» нет. Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2.
I.2d Объём и доля короткого долга B-CREDIT-6/7 реализовано, не запущено частичное short_debt и short_debt_share_pct уже считаются. Событий не дают сознательно: комментарий в коде фиксирует, что заказчик задал уровни только для покрытия процентов и ЧДП. Это открытый вопрос к вам, а не пробел разработки (решение 0.3). Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2. trusted_fns_bfo.py:760-771, :946-948
I.2e Динамика общего долга B-CREDIT-8 реализовано, не запущено частичное То же: total_debt считается, порога существенности нет. Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2.
I.3a Дефолт эмитента default_or_missed_payment
B-PAY-1…6
реализовано, не запущено частичное Cbonds-парсер работает, типы разведены («Технический дефолт» → B-PAY-5, severity high). Но живых событий факта дефолта ноль — обе записи в базе это снятие. Модификаторы ТЗ (срок до погашения, доля розничных держателей, ожидаемый recovery) не реализованы.
I.3b Дефолт другого эмитента в секторе (заражение) sector_contagion
B-PAY-9
кода нет нет Нет производителя, нет отраслевой принадлежности as-of, нет versioned peer-универсума. Спецификация отмечает отдельно: опрос только по наблюдаемым бумагам этот атом не закрывает в принципе — нужен источник дефолтов по всему рынку.
I.3c Блокировка счетов account_restriction
L-ACC-1
реализовано, не запущено частичное FINDPRS по верифицированному ИНН и универсальному БИК, опрос раз в сутки, якорь события — номер решения плюс дата плюс ИФНС плюс БИК банка. Не выставлены три env-флага подтверждения. Матчинг идёт по точному верифицированному ИНН, а их 17 на 71 эмитента: без шага 1.4 источник покроет четверть вселенной и будет выглядеть неисправным. Нет оценки масштаба: один счёт или вся расчётная инфраструктура.
I.3d Уголовное дело criminal_case_event
L-CRIM-1/2
кода нет нет Ни источника, ни адаптера, ни правил матчинга и классификации возбуждения/прекращения.
I.3e Претензии надзорных органов L-REG-1/2 кода нет нет Существующий legal_regulatory_event — это только банкротные сообщения Федресурса (L-BKR). Предписания ЦБ и ФАС, Росприроднадзор, налоговые претензии, обеспечительные меры и лицензии не покрыты никем. Матчинг идёт по точному верифицированному ИНН, а их 17 на 71 эмитента: без шага 1.4 источник покроет четверть вселенной и будет выглядеть неисправным.
I.3f Обратный сценарий: разблокировка, снятие претензий, закрытие дела L-ACC-2, L-CRIM-2, L-REG-2 кода нет нет Код ФНС сознательно отказывается выводить разблокировку из исчезновения строки: unblock_inference_allowed: False в четырёх местах. Решение правильное, но требование остаётся незакрытым — нужен исторический фид ограничений.
I.4a Смена топ-менеджмента management_change
G-MGMT-1/2
реализовано, не запущено частичное Производитель есть, но без словаря типов сообщений он raw-only. Есть бесплатный обходной путь: в leadintel на этом же сервере лежат ФИО и должность руководителя по ИНН — периодический дифф закрывает пункт в первом приближении без контракта (раздел Г, шаг 1.5). Нет оценки внезапности и значимости должности — без неё событие нейтрально, а не автоматически негативно, как читает ТЗ.
I.4b Смена контролирующего собственника ownership_change
G-OWN-1
кода нет нет Ни контракта, ни адаптера. Нет различения технической перестройки структуры владения и реального перехода контроля.
I.4c Продажа акций владельцами G-OWN-2 / E-CORP-12 кода нет нет Отдельный класс от смены собственника, и подменять одно другим нельзя. Нужен поток сделок инсайдеров и владельцев существенных долей.
I.4d Позитив: сильная команда, стратегический инвестор G-OWN-3/4 кода нет нет Спецификация помечает G-OWN-4 как «не автоматизировать до policy approval». «Сильная команда» не формализуема без ручной оценки аналитика — и это честная позиция, а не отговорка.
I.5a Спред конкретного эмитента credit_spread_move
B-SPREAD-1/2
живой · 3635 частичное Порог 25 б.п. и гейты (≥10 сделок, ≥1 млн ₽) подтверждены из payload события. База берётся только при совпадающем типе даты доходности. Покрыто 140 облигаций из 473 — это установившееся состояние, а не незаконченный прогон.
I.5b Широкое расширение спредов по рынку или сектору B-SPREAD-3…6 кода нет нет Нет versioned корзины или индекса с составом as-of, нет breadth-метрики, нет отделения системного движения от идиосинкратического риска эмитента.
I.6a Повышение и понижение рейтинга rating_action
B-RATING-1…4
реализовано, не запущено частичное Классификатор UP/DG написан, живых событий ноль. Живой поток лежит (CbrRatingsAmbiguousEmpty) — нужен ремонт парсера stateful-интерфейса.
I.6b Улучшение и ухудшение прогноза B-RATING-5/6 реализовано, не запущено частичное То же самое, ноль событий.
I.6c Постановка на пересмотр (watch) B-RATING-7/8 реализовано, не запущено частичное Спецификация держит эти атомы в статусе shadow: нужны отревьюенные направленные фикстуры watch. Ноль событий.
I.7a Досрочное или добровольное погашение, call и put bond_redemption
B-RED-1…5
реализовано, не запущено частичное Маппинг НРД зашит в код, а не подаётся конфигом — это плюс. Но НРД работает в режиме «только текущий снимок»: историю жизненного цикла он не докажет. Контракта нет. Зашитый маппинг делает НРД заметно быстрее e-Disclosure после оплаты — поэтому в плане он стоит выше (шаг 3.2).
I.7b Дисконт к номиналу → переоценка вверх B-RED-6 кода нет нет Нет сопоставления объявления с чистой ценой относительно номинала на дату объявления. База дисконта и политика ликвидности не утверждены.
I.7c Mandatory call при росте ставок → реинвестиционный риск B-RED-7 кода нет нет Спорная трактовка в самом ТЗ. При росте ставок вынужденное реинвестирование происходит по более высокой доходности. Экономически call болезнен при падении ставок: эмитент отзывает дорогой долг, держатель теряет высокий купон. Формулировку стоит переписать до реализации.
I.8a Пропуск купона, технический дефолт B-PAY-1/3/5/6 реализовано, не запущено частичное См. I.3a. Типы в коде разведены корректно, живых фактов нет.
I.8b Переговоры о реструктуризации до формального дефолта B-PAY-7 кода нет нет Требует отдельного типа раскрытия из e-Disclosure. Это содержательно ценный пункт: он ловит момент до обвала, а не после. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь типов подаётся конфигом; насколько это отражает всю проделанную работу — судить по репозиторию нельзя, часть её может быть вне его. Объём после получения ключа уточнить у команды.
I.8c Заражение соседних выпусков того же эмитента B-PAY-10 реализовано, не запущено частичное Отличается от I.3b и устроено умнее: работает по точной юридической идентичности эмитента, generic-профиль не нужен, покрывает и не удерживаемые в портфеле выпуски. Не срабатывал только потому, что нет родительских дефолтов.
I.9a Размещение состоялось, сорвано или перенесено bond_placement
B-PLC-1/2
кода нет нет Производителя нет. Нужны итог размещения, объём, дата переноса или отмены.
I.9b Переподписка книги, купон ниже ориентира B-PLC-3/4 кода нет нет Нужны книга заявок и сохранённый первоначальный ориентир купона. Ни один из подключённых источников этого не отдаёт.
I.9c Первый день вторичных торгов относительно номинала B-PLC-5/6 кода нет нет price_volume_anomaly это не покрывает: он не знает, что день первый после размещения, не сравнивает цену с номиналом и не отслеживает давление продавцов 1–3 дня. Плюс «сильная распродажа» численно не определена.
I.10a Цена облигации ≥1% внутри дня T-PRICE (bond) реализовано, не запущено частичное Порог и формула («внутри дня» = размах high−low к предыдущему закрытию, потому что дневная свеча не несёт тиков) написаны и покрыты юнит-тестами. Отключено булевым флагом. Ноль событий при 41 798 готовых баров.
I.10b Цена облигации ≥3% за пять сессий T-PRICE (bond) реализовано, не запущено частичное То же самое, тем же флагом.
I.10c Существенное изменение оборота облигации T-VOL (bond) реализовано, не запущено частичное Правило общее с акциями и распространяется на облигации автоматически. Отключено тем же флагом.
I.10d Оборот при стабильной цене как смена держателя T-VOL-3 (bond) реализовано, не запущено частичное Механика нейтральной полосы ±1% реализована, для облигаций не прогоняется.
Раздел II. Общие темы
II.1 Неисполнение обязательств: невыплата и частичная выплата купона, непогашение и частичное погашение тела B-PAY-1…4 реализовано, не запущено частичное Полная невыплата покрыта правилом. Частичная выплата (B-PAY-2/4) требует полей «начислено / выплачено» — Cbonds их не даёт, нужен e-Disclosure или НРД. Живых фактов ноль. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь типов подаётся конфигом; насколько это отражает всю проделанную работу — судить по репозиторию нельзя, часть её может быть вне его. Объём после получения ключа уточнить у команды.
II.2 Блокировка счетов со стороны ФНС = I.3c реализовано, не запущено частичное См. I.3c. Отдельно: ТЗ требует влияния и на акции, и на облигации — механизм issuer-scope это даёт, но без запуска потока проверить нечем.
Раздел III. Сырьё и капзатраты
III.1a Долгосрочные прогнозы цен по сырью, порог >10%, список из 12 товаров commodity_forecast_move
C-FCST-1
живой · 86 частичное Порог строго >10%, сравниваются только соседние винтажи одного бенчмарка по одному целевому году — методологически корректно. Покрыто 7 товаров из 12: нет алмазов, стали, вольфрама, марганца, лития. Спецификация отдельно фиксирует, что железная руда не прокси для стали.
III.1b Прогнозы спроса и предложения (профицит негативен производителю) C-FCST-2/3 кода нет нет World Bank публикует только цены. Спрос и предложение не покрыты ни по одному из 12 товаров. Это половина требования III.1, которую легко не заметить за галочкой «доходит».
III.1c Знак влияния для производителя и потребителя C-FCST-4/5 реализовано, не запущено частичное Логика знака написана полностью: производителю прямой знак, потребителю обратный, смешанная роль остаётся conditional. Профилей producer и consumer в базе ноль, поэтому событие остаётся глобальным и условным.
III.2 Изменение CAPEX более чем на 10% capex_change
C-CAPEX-3/4
реализовано, не запущено частичное Порог строго >10%, направление conditional, и в payload честно записано interpretation: requires_capex_purpose_and_debt_context — это ровно двусторонняя трактовка из ТЗ. Но реализован факт отчётности; пересмотр плана капзатрат (C-CAPEX-1/2) кода не имеет. Связи с FCF, долгом и дивидендами нет. Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2.
Раздел IV. Календарь и анализ портфеля акций
IV.1.1 Заседание СД с вопросом о распределении прибыли corporate_calendar
E-CAL-1
реализовано, не запущено частичное Производитель НРД есть, включая повестку собрания. Для окраски нужны ожидаемый дивиденд и консенсус — их нет ни в одном источнике.
IV.1.2 Ожидаемая дата публикации отчётности E-CAL-2/3 реализовано, не запущено частичное НРД даёт корпоративные действия, но не даты отчётности — нужен e-Disclosure. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь типов подаётся конфигом; насколько это отражает всю проделанную работу — судить по репозиторию нельзя, часть её может быть вне его. Объём после получения ключа уточнить у команды. Нет различения официальной, ожидаемой и фактической даты публикации.
IV.1.3 Ранее анонсированные сущфакты corporate_action
E-CORP-1…9
реализовано, не запущено частичное Механизм маппинга есть, но самого словаря типов нет: он подаётся env-JSON и без флага активации адаптер возвращает None, то есть raw-only. Контракт — не последний шаг. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь типов подаётся конфигом; насколько это отражает всю проделанную работу — судить по репозиторию нельзя, часть её может быть вне его. Объём после получения ключа уточнить у команды. trusted_edisclosure.py:436
IV.1.4 Изменение или отмена назначенного события E-CAL-4 реализовано, не запущено частичное Жизненный цикл в адаптере НРД предусмотрен, поток выключен.
IV.2a Факт против ожиданий, отклонение >20% E-RES-1…5 кода нет нет Консенсуса нет ни у одного подключённого источника. Прогноз компании — отдельный контрактный маршрут через файлы e-Disclosure, тоже не подключён. Это и есть настоящий «сюрприз» из ТЗ.
IV.2b Факт против прошлого периода и АППГ financial_result_surprise
E-RES-10/11
реализовано, не запущено частичное Названо неверно, и код это признаёт: в payload зашито is_consensus_comparison: False, заголовок пишет «отклонился от предыдущего периода», а спецификация прямо требует «не называть surprise». Порог 20% совпадает. Покрыты выручка, операционная и чистая прибыль; EBITDA и EPS из РСБУ не выводятся. Блокер — не оплата REST. Парсер значений уже написан для бесплатной витрины; мешают env-флаг разрешения на её использование и правило маршрутизации, уводящее trusted-режим на платный REST, который значений не разбирает вовсе — см. раздел Г и решение 0.2.
IV.2c Отраслевые уточнения: LFL, трафик, NIM, ROE E-RES-6…9 кода нет нет Метрики перечислены в каталоге как отдельные атомы, производителя нет.
IV.3 Сырьевая чувствительность: доля >15%, цена сырья >10% за неделю или месяц C-SPOT-1…4 кода нет нет Частая ошибка трактовки. commodity_forecast_move — это пересмотр прогноза, а IV.3 требует движения фактической спот-цены. Это разные требования, и первое второе не закрывает. Источника спот-цен по сырью нет вообще. Порог 15% лежит в коде без входных данных.
IV.4 Чувствительность к ставке: ND/EBITDA >3 и Δставки ≥0,75 п.п. M-RATE-1 + M-RATE-2 родитель живой · 20
импакт не запущен
частичное Порог ставки точен, направление conditional корректно. 67 профилей rate_sensitive существуют и дают ноль импактов, потому что под ними нет production source contract. Банки и финкомпании, к которым логика долговой нагрузки неприменима, не выделены.
IV.5 Валютная чувствительность: доля ≥50%, курс >5% за неделю, >10% за месяц M-FX-1/2 + M-FX-3/4/5 родитель живой · 343
импакт не запущен
частичное Оба порога реализованы, включая недельный 5% с базой «последний официальный курс на дату минус семь дней». Порог 50% в коде. Профилей revenue и debt ноль. При совпадении обеих ролей код даёт conditional — это соответствует ТЗ («знак зависит от доминанты»), а не противоречит ему. Хеджи и естественное хеджирование не моделируются.
IV.6a Сделка, M&A, байбэк, допэмиссия, неожиданные дивиденды E-CORP-1…10 реализовано, не запущено частичное См. IV.1.3. «Неожиданный дивиденд» (E-CORP-10) требует консенсуса — без него остаётся просто «дивиденд объявлен». «Выгодное M&A» спецификация оставляет условным до формальной оценочной модели, и это правильно. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь типов подаётся конфигом; насколько это отражает всю проделанную работу — судить по репозиторию нельзя, часть её может быть вне его. Объём после получения ключа уточнить у команды.
IV.6b Сделки инсайдеров E-CORP-11/12 кода нет нет Ни источника, ни адаптера.
IV.6c Окраска по комментариям аналитиков E-CORP-13 кода нет нет Спецификация помечает как gap вне первой очереди: нужен источник с именованным аналитиком, правила доверия и point-in-time консенсус.
IV.7a Изменение цены акции более 10% за день price_volume_anomaly
T-PRICE-1/2
живой · 105 полное Строго >10%, через Decimal, чтобы ровно 10,00% не срабатывало из-за двоичного округления. Дивиденд возвращается в цену, сплит подавляется с указанием коэффициента, разрыв сессий больше 14 дней подавляется. Это единственный пункт ТЗ, закрытый без оговорок.
IV.7b Объём ≥2× среднего за пять сессий как самостоятельная аномалия T-VOL-1/2 живой · 89 частичное Осознанное отступление от ТЗ. Самостоятельным событием объём становится только при ≥5×; 2× лишь подтверждает движение цены и повышает severity. Обоснование в коде: на пороге 2× 1669 строк из 1774 были объёмными, с медианой 2,57 — это дневной фон, который трейдер и так видит в терминале. Отступление разумное, но это ваше решение, а не разработки.
IV.7c Всплеск объёма при стабильной цене T-VOL-3 живой · 8 частичное Нейтральная полоса ±1% определена явно, направление conditional, заголовок пишет «цена практически не изменилась». Гипотеза «смена держателя» из ТЗ сохранена. Не хватает только разметки «накопление / распределение» — а её по дневной свече достоверно и не получить.
Реализовано сверх ТЗ
Биржевой режим дефолта TQRD default_status_flag живой · 0 сверх ТЗ Ноль честный: ни одна наблюдаемая бумага не в режиме «Облигации Д». Ценное наблюдение внутри кода: флаги HASDEFAULT на этом рынке мертвы — все 47 бумаг в TQRD показывают ноль, поэтому режим первичен. Ограничение: видит только текущий снимок, историю режимов не восстанавливает.
Ставка и курсы как самостоятельные глобальные события M-RATE-1, M-FX-1/2 живой · 363 сверх ТЗ В ТЗ это входы для IV.4 и IV.5, а не отдельный класс событий. Как самостоятельные факты полезны, но issuer-требование они не выполняют.
Отбрасывание подтверждений и первичных рейтингов rating_action живой сверх ТЗ Полезная фильтрация шума: 426 строк из 486. Но первичный рейтинг для только что размещённого выпуска — значимое событие; лучше выделить отдельным подтипом, чем отбрасывать.
Подавление дивидендного гэпа, сплитов, разрывов сессий, несовместимых единиц объёма price-volume.v4 живой сверх ТЗ Прямо требуется спецификацией, но в исходном ТЗ не описано. Без этого порог >10% срабатывал бы на каждой дивидендной отсечке голубых фишек — российские доходности регулярно превышают 10%.
Жизненный цикл active / corrected / retracted и реплей по версиям правил живой сверх ТЗ 1572 из 1774 событий цены/объёма и 426 из 486 рейтингов ретрактированы при смене версии правила. В ТЗ этого нет, но без такого механизма исправление правила не откатывает уже сделанные выводы — и лента накапливает ложь.

Пять мест, где проблема в трактовке, а не в реализации

  1. I.7c, реинвестиционный риск при call. ТЗ считает болезненным рост ставок; экономически болезненно падение. Требование нужно переписать, иначе будет реализовано зеркально неверно.
  2. IV.3 против III.1. Пересмотр долгосрочного прогноза и движение спот-цены — разные события с разными горизонтами. Сейчас живёт первое, требуется и второе; риск в том, что галочка на III.1 закроет и IV.3.
  3. IV.2, слово «сюрприз». Реализовано изменение к прошлому периоду. Это не сюрприз ни в каком смысле, и без point-in-time консенсуса им не станет. Разделить на два разных события.
  4. IV.7b, порог объёма. ТЗ говорит 2×, реализация 5×. Отступление обоснованное, но нужно ваше явное «да»: иначе ТЗ и код расходятся молча.
  5. I.2d и I.2e, короткий и общий долг. Метрики считаются, событий нет, потому что вы не задали порог существенности. Это не пробел разработки — это незакрытый вопрос к вам, и он бесплатный.

Чего не хватает в самом ТЗ

ТЗ почти целиком описывает негативные триггеры и почти не описывает обратные события. Снятие дефолтного статуса в коде есть, а разблокировки счетов, снятия претензий, прекращения дела и выхода из банкротных процедур в ТЗ нет как требований — только вскользь, в разделе I.3, как «обратный сценарий». Между тем это половина ценности: событие, которое никогда не гаснет, через год превращает карточку эмитента в кладбище.

Второе: ТЗ нигде не задаёт, что делать с эмитентом, у которого две роли сразу — экспортёр с валютным долгом, производитель одного сырья и потребитель другого. Код отвечает conditional, и это честно, но продуктово бесполезно. Решение нужно принять на уровне ТЗ, а не догадываться в реализации.

Обратная связь стейкхолдера: однотипных событий столько, что лента нечитаема глазами

Замерил, чтобы это перестало быть ощущением. За июль 2026 в ленту попало 746 видимых событий — около 25 в день. Из них 635 — движения G-спредов: 594 события за последние 30 дней по 83 бумагам, то есть примерно двадцать в день и по семь на бумагу в месяц. events, status IN (active,corrected), группировка по месяцу и типу

Но главная причина нечитаемости не в объёме, а в том, что ранжировать поток нечем. Из 6255 видимых событий 6197 имеют severity='medium'. Значение high стоит ровно у 47 строк — это события цены и объёма, где сработало два предиката сразу. Ещё 58 строк не имеют severity вовсе. То есть поле формально есть, фильтр по нему формально работает, а информации в нём нет: отсортировать «сначала важное» физически невозможно. 6197 medium · 47 high · 58 null

Два обстоятельства делают это срочным, а не косметическим.

Первое: будет хуже, причём от работ, которые я сам поставил первыми. Спреды сейчас считаются по 140 бумагам из 473 — доведение покрытия умножает поток примерно на 3,4 и даёт порядка двух тысяч событий в месяц. Включение облигационного правила цены и объёма (шаг 1.1) добавляет 339 инструментов туда, где сейчас работают 53. Обе работы правильные, но выпускать их без группировки и ранжирования — значит утопить ленту окончательно.

Второе: прецедент уже есть, и он показывает, что это лечится. Семейство цены и объёма ровно эту болезнь уже перенесло: версия правила v4 ретрактировала 1572 строки из 1774 — 89% — потому что объёмные события на пороге 2× составляли 94% ленты при медиане 2,57, то есть были обычным дневным фоном. Лечение состояло не в удалении требования, а в поднятии планки для самостоятельного события с сохранением числа в payload. К двум самым крупным семействам — спредам и кривой — это мышление не применяли ни разу.

Одна конкретная асимметрия, которая объясняет пилу по спредам. У кривой порог считается от последней отправленной базы, поэтому подпороговые дрейфы копятся и не порождают событий. У спредов сравниваются соседние ликвидные сессии — поэтому колебание вокруг 25 б.п. выдаёт событие каждые несколько дней по одной и той же бумаге. Это разное поведение двух соседних правил, а не разная природа рынка.

И самое полезное: правильное лекарство здесь — невыполненное требование ТЗ, а не костыль. Двадцать спредовых событий за день по 83 бумагам — это одно рыночное событие «спреды разъехались по рынку», а не двадцать независимых. Ровно это и есть B-SPREAD-3…6, пункт I.5b, который в таблице выше числится как «кода нет». Агрегат по рынку и сектору одновременно закрывает пункт ТЗ и схлопывает основной источник шума.

В

Что делать дальше

Цель, относительно которой отсортировано: максимум живых и корректно интерпретируемых требований ТЗ на единицу затрат. Без явной цели любой порядок субъективен — в исходном списке она не названа, и поэтому там смешаны скорость, покрытие и стоимость.

Единый список, проверенный на полноту: каждая позиция таблицы А, которая не закрыта полностью, попадает ровно в один шаг. Волны отсортированы по стоимости входа — от нуля до контрактов и собственной разработки. В конце отдельно названо то, что не закроется никогда, и почему это нормально.

Позиция стейкхолдера: контролёр, атомы, материализаторы и версии правил — жёсткий оверинжиниринг

Дословно: «казалось бы, надо собирать просто из надёжного источника метрики типа „объём торгов“, и ивенты, что они подскочили по сравнению с предыдущим значением на N процентов».

По большей части это подтверждается тем, что я нашёл, и подтверждается жёстко.

Контролёр покрытия за всё время дал отрицательную ценность. Единственное, что он делал — прятал от пользователя уже произведённые события: 60 рейтингов, дефолты, спреды. Его выключили решением стейкхолдера, и лента от этого только наполнилась. 327 атомов, верификатор и материализатор обслуживают механизм, который в проде выключен и без которого стало лучше.

Каталог требований — это план, скомпилированный в код. 116 требований, 120 определений, 327 атомов, версия каталога, планы capability, семь разных статусов ожидания (policy_pending, identity_pending, profile_pending и далее). Всё это описывает работу, которая не сделана. Такое место — таблица в документе, а не модуль на 1428 строк, который надо сопровождать.

Слой контрактов источника прямо сейчас мешает продукту работать. 67 профилей эмитентов существуют и дают ноль импактов, потому что под ними нет production source contract. То есть данные есть, правило есть, а событий нет из-за юридической формальности, которую никто не закрыл.

И самый неприятный аргумент в пользу этой позиции: вся эта машинерия строилась ради гарантий корректности — и не поймала ни одного из трёх реальных дефектов. Облигационное правило выключено булевым флагом. Парсер рейтингов молча падает. Семейство называется «сюрприз», сравнивая с прошлым годом, причём в его же payload записано, что консенсуса нет. Ни fail-closed гейт, ни 327 атомов, ни верификатор ничего из этого не заметили.

Одно исключение, и оно доказано вашими же данными. Версии правил вместе с ретрактом и реплеем — не церемония. Когда порог по объёму оказался неверным, price-volume.v4 ретрактировал 1572 строки из 1774. Без версионирования эти 1572 неверных события остались бы в ленте навсегда, а исправление порога действовало бы только на будущее. Сегодняшняя жалоба на нечитаемость ленты лечится ровно этим механизмом: поднять планку по спредам и переиграть историю. Из всего перечисленного я бы оставил только его.

И одна ловушка в простой формулировке. «Подскочил на N% к предыдущему значению» — это буквально правило, которое дало 3635 спредовых событий и нечитаемую ленту: значение колеблется вокруг порога и выдаёт событие каждые несколько дней. У кривой этой болезни нет, потому что там порог считается от последней отправленной величины, а не от предыдущей. Одна строчка разницы, и её стоит перенести в упрощённую версию — это не защита машинерии, а конкретная дешёвая идея.

Хорошая новость: то, что вы описываете, уже лежит на дне этой системы. detect_key_rate_changes — сорок строк. Правило цены и объёма — одна функция с порогами. Простота, которую вы предлагаете, не требует переписывания, она требует удаления обвязки. Это дешевле, чем кажется, и это же естественная подготовка к переходу на калькуляторы плюс сигналы.

Волна 0. Решения, которые ничего не стоят и блокируют оценку

Ни один из этих пунктов не требует разработки, но каждый меняет содержание шагов ниже. Пока они открыты, любая оценка трудоёмкости — предположение.

  1. 0.1

    Судьба контролёра покрытия и всей обвязки вокруг правил

    блокирует всё

    Всё, что ниже, добавляет события в ленту, из которой fail-closed гейт может убрать их одним переключателем. Если гейт когда-нибудь вернётся, каждый шаг должен дополнительно включать промоушен атомов — это другая оценка.

    С учётом позиции стейкхолдера выше решение формулируется резче: не «когда включим обратно», а что из этого сносим. Мой ответ по каждому куску — снести контролёр, верификатор и материализатор; каталог требований вернуть в документ; слой контрактов источника упростить до того, что реально блокирует юридически; оставить только версии правил с ретрактом и реплеем, потому что это единственный элемент, который уже доказал пользу на живых данных.

    Снос стоит поставить перед волной 2, а не после: чем больше семейств оживёт, тем дороже будет вынимать обвязку из-под них.

  2. 0.2

    Витрина ГИР БО против платного REST

    определяет волну 2 или волну 3

    Письменное разрешение на использование публичной витрины — и метрики появляются бесплатно, потому что парсер уже написан. Или оплата REST — и остаётся написать парсер скачанных документов. Сначала проверьте третий вариант: публикует ли ФНС ГИР БО в открытых данных единым архивом. От этого решения зависит, где стоят шесть позиций ТЗ.

  3. 0.3

    Четыре порога и одна формулировка

    ваши числа

    Порог существенности для короткого долга (I.2d) и общего долга (I.2e) — метрики уже считаются и лежат мёртвым грузом. Порог объёма 2× или 5× (IV.7b) — код отступил от ТЗ обоснованно, но молча. И переписать I.7c: call болезнен при падении ставок, а не при росте.

  4. 0.4

    Что делать с эмитентом в двух ролях

    продуктовое решение

    Экспортёр с валютным долгом, производитель одного сырья и потребитель другого. Код честно отвечает conditional, ТЗ ответа не даёт. Решать на уровне ТЗ, иначе реализация будет угадывать.

  5. 0.5

    Права на показ для каждого нового источника

    объявлять сразу

    trusted_source_rights.py отделяет «можем читать» от «можем показывать пользователю»; коммерческие вендоры числятся необъявленными. Объявляйте оба права при подключении, иначе позднее включение проверки молча погасит часть ленты.

Волна 1. Быстрые победы: дни, без единого контракта

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

  1. 1.1

    Включить облигационное правило цены и объёма

    одна строка × 2 места + реплей

    Заменить evaluate_anomalies = kind == "stock" на условие, допускающее bond. Пороги, формулы и юнит-тесты написаны, данные собраны: 339 облигаций, 41 798 баров, история с сентября 2021.

    Закрывает I.10a, I.10b, I.10c, I.10d. Универсум растёт с 53 инструментов до 392.

  2. 1.2

    Поднять контур на стенде

    одна переменная окружения

    TRUSTED_EVENTS_ENABLED_STAGING=1. Пока флага нет, база собирается ручными прогонами, и любая проверка «работает ли» недостоверна.

    Предусловие для всего остального.

  3. 1.3

    Починить парсер репозитория рейтингов ЦБ

    дни, источник бесплатный

    Правило, классификация действий и жизненный цикл написаны и отревьюены. Живой поток падает на CbrRatingsAmbiguousEmpty — stateful-интерфейс.

    Закрывает I.6a, I.6b, I.6c, представленные сейчас только отзывами рейтинга Роснефти пятилетней давности.

  4. 1.4

    Резолвить ИНН и ОГРН в момент заведения эмитента

    встроить в существующий флоу

    Сейчас 17 верифицированных ИНН на 71 эмитента. Федресурс и блокировки ФНС матчатся именно по точному верифицированному идентификатору — без этого шага оба источника дадут 24% покрытия и будут выглядеть неисправными.

    Разовая проставка не решает задачу: инструменты приезжают из myavi в избранное, и новый эмитент заводится сам. Точка входа уже есть — при неизвестном эмитенте вызывается генератор сущности, который отдаёт match_tokens, keywords, facets, kind, notes. Юридических идентификаторов среди них нет. Резолв ИНН/ОГРН должен встать туда же, в тот же шаг онбординга, и писать в issuer_identifiers с verified и evidence_url. routers/integration.py:_call_entity_gen · PROMPT_KEY_ENTITY_GEN

    Источник данных — не Dadata, а leadintel на этом же сервере (см. раздел Г): 48 558 компаний, у всех ИНН и ОГРН, плюс готовый матчер по названию. Dadata остаётся фолбэком для того, чего там не нашлось.

    Предусловие для I.3c, I.3e, II.2. Архитектурных решений не требует: идентификатор — не факт, у него нет вопроса авторитета.

  5. 1.5

    Дифф по руководителям

    дни, поверх шага 1.4

    В leadintel уже лежат director_name, director_inn и director_position по ИНН. Периодический дифф даёт рабочий сигнал без контракта с ЦРКИ и без внешнего API. Оговорка: ЕГРЮЛ отражает смену с задержкой в дни-недели, то есть это подтверждение, а не опережение.

    Закрывает I.4a в первом приближении; авторитетная версия придёт с e-Disclosure (шаг 3.3).

  6. 1.6

    Разделить financial_result_surprise

    час

    financial_result_change — факт к прошлому периоду, то, что реализовано. financial_result_surprise — только против point-in-time консенсуса, то, чего нет. Сделать до закупки любого источника отчётности, иначе переименовывать придётся с живыми данными.

    Чинит трактовку IV.2b и освобождает имя для IV.2a.

  7. 1.7

    Сделать severity информативным — условие выпуска шага 1.1

    дни

    Сейчас 6197 видимых событий из 6255 имеют severity='medium', high стоит у 47 строк, у 58 severity нет вовсе. Поле есть, фильтр есть, информации нет — отсортировать «сначала важное» невозможно. Это и делает ленту нечитаемой глазами, а не сам объём.

    Каждое правило должно вычислять severity из своих же чисел, которые уже лежат в payload: во сколько раз превышен порог, ликвидность бумаги, размер дельты относительно исторического распределения. Ноль новых источников.

    Ставить перед шагом 1.1, а не после. Включение облигационного правила добавляет 339 инструментов туда, где сейчас 53; выпускать это в ленту без ранжирования — значит утопить её окончательно.

Волна 2. Бесплатно, но недели работы

Закрывают ещё тринадцать позиций, из них шесть — при условии, что решение 0.2 принято в пользу витрины. Шаги 2.6 и 2.7 добавлены после обратной связи стейкхолдера о нечитаемости ленты (см. конец раздела А).

  1. 2.1

    Технический хвост MOEX

    дни

    Две бумаги падают на проверке артефактов. Отдельно разобрать, почему G-спреды покрывают 140 облигаций из 473: гейт ликвидности отсекает по делу или база не находится там, где должна.

    Доводит I.5a до заявленного покрытия.

  2. 2.2

    Декомпозиция кривой по ногам

    дни

    yields_pct уже лежит в payload; нужно, чтобы знак различал «длинный конец вырос» и «короткий упал» — ТЗ различает явно, код пока нет.

    Доводит I.1 до полного.

  3. 2.3

    Провенанс профилей эмитента, затем минимальные профили

    решение, затем недели

    Порядок здесь важнее содержания: 67 профилей rate_sensitive уже существуют и дают ноль импактов, потому что под ними нет production source contract. Сначала решить, чем удостоверяется профиль. Затем минимальный набор по наблюдаемой вселенной: чистый долг к EBITDA, доля валютной выручки, доля валютного долга, профильный товар, роль производителя или потребителя, доля сырья в выручке или себестоимости.

    Закрывает IV.4, IV.5, III.1c — крупнейший продуктовый блок волны.

  4. 2.4

    Блокировки счетов ФНС

    три флага + решение по снятию

    Код готов, источник бесплатный, нужны подтверждения машинного доступа. Но не выпускайте без ответа на вопрос снятия: код сознательно отказывается выводить разблокировку из исчезновения строки, значит без исторического фида вы публикуете негативное событие, которое никогда не гаснет.

    Закрывает I.3c и II.2, половину I.3f оставляет открытой.

  5. 2.5

    Витрина ГИР БО — если решение 0.2 в её пользу

    снять один флаг и правило маршрутизации

    Парсер значений уже написан и выдаёт ровно метрики ТЗ. Помимо флага FNS_GIRBO_PUBLIC_UI_CONTRACT_VERIFIED нужно поправить _fns_bfo_adapter_enabled(): сейчас в trusted-режиме он всегда уводит на платный REST, поэтому после шага 1.2 витрина станет недостижимой.

    Закрывает I.2b, I.2c, I.2d, I.2e, III.2, IV.2b — шесть позиций бесплатно.

  6. 2.6

    Рыночный и отраслевой агрегат спредов

    недели, свои же данные

    Двадцать спредовых событий за день по 83 бумагам — это одно рыночное событие «спреды разъехались по рынку», а не двадцать независимых. Versioned корзина с составом на дату, breadth-метрика, симметричные пороги на расширение и сужение.

    Вынесено сюда из волны 4: в отличие от заражения, агрегат считается из наших собственных рядов G-спредов и внешнего источника не требует.

    Закрывает I.5b и одновременно схлопывает главный источник шума в ленте — то есть это не борьба с симптомом, а невыполненное требование ТЗ, которое случайно оказалось и лекарством.

  7. 2.7

    Порог спредов — от последней отправленной базы

    дни

    У кривой порог считается от последней отправленной базы, поэтому подпороговые дрейфы копятся и не порождают событий. У спредов сравниваются соседние ликвидные сессии — поэтому колебание вокруг 25 б.п. выдаёт событие каждые несколько дней по одной и той же бумаге. Это разное поведение двух соседних правил, а не разная природа рынка.

    Требует бампа версии правила и реплея, поэтому не в первой волне. Проверить на истории, сколько из 3635 строк переживут смену — по аналогии с price-volume.v4, где отсеялось 89%.

Волна 3. Контракты, по порядку «сколько работы остаётся после оплаты»

Порядок определяется не ценой и не числом семейств, а дистанцией от платежа до события в ленте. По этому критерию Федресурс выигрывает у всех, а самый дорогой блок оказывается не самым срочным.

Источник Работа после оплаты Закрывает Комментарий
3.1 Федресурс нет I.3e (банкротная часть) Единственный контракт без остаточной разработки: восемь типов схемы 3.9, матчинг, жизненный цикл — всё написано. Требует шага 1.4, иначе покроет 17 эмитентов из 71.
3.2 НРД включить и проверить I.7a · IV.1.1 · IV.1.4 · часть II.1 Маппинг типов зашит в код, а не подаётся конфигом — это делает НРД быстрее e-Disclosure. Ограничение: режим «только текущий снимок», историю жизненного цикла не докажет.
3.3 e-Disclosure (шлюз ЦРКИ) уточнить у команды I.8b · II.1 полностью · IV.1.2 · IV.1.3 · IV.6a · I.4a авторитетно Нужен выданный ключ шлюза; лежащие IFX_* — креды новостного фида и к шлюзу не подходят. По коду в репозитории видно, что схема сообщения не несёт идентификатора выпуска, а словарь «тип → атом» подаётся конфигом; но часть работы может лежать вне репозитория, поэтому остаточный объём после ключа надо уточнить у команды, а не оценивать по коду.
3.4 Платный REST ГИР БО — только если 0.2 решён против витрины парсер документов те же шесть, что и 2.5 Адаптер скачивает файлы отчётности как байты и значения не разбирает. Покупает транспорт, а не метрики. Если витрина разрешена — этот пункт не нужен вовсе.
3.5 Point-in-time консенсус сопоставление винтажей IV.2a · часть IV.6a Единственный способ получить настоящий «сюрприз» и «неожиданный дивиденд». Требует хранения оценки на дату до публикации, а не текущей.
3.6 МСФО point-in-time нормализация I.2a Долг/EBITDA из РСБУ невыводим в принципе. Метод Cbonds отдаёт 401 — не входит в текущую подписку; нужен либо апгрейд тарифа, либо другой вендор.
3.7 Фид сделок инсайдеров матчинг лиц I.4c · IV.6b Отдельный класс от смены собственника; подменять одно другим нельзя.
3.8 СПАРК: собственники адаптер с нуля I.4b Ни контракта, ни кода. Нужно различать техническую перестройку владения и реальный переход контроля — иначе события будут ложными.
3.9 Уголовные и надзорные производства адаптер с нуля I.3d · I.3e полностью Претензии надзорных органов шире банкротства: предписания ЦБ и ФАС, Росприроднадзор, налоговые претензии, обеспечительные меры, лицензии.
3.10 Исторический фид ограничений жизненный цикл I.3f Единственный способ получить разблокировку, снятие претензий и закрытие дела честно, а не выводом из исчезновения строки.
3.11 Спот-цены по сырью окна недели и месяца IV.3 Это не то же, что пересмотр прогноза World Bank. Разные горизонты, разные требования; первое второе не закрывает.
3.12 Прогнозы спроса и предложения нормализация мер III.1b World Bank публикует только цены. Половина требования III.1, которую легко не заметить за галочкой «доходит».
3.13 Пять недостающих товаров маппинг бенчмарков III.1a полностью Алмазы, сталь, вольфрам, марганец, литий. Железная руда не прокси для стали — спецификация фиксирует это отдельно.
3.14 Отраслевые метрики: LFL, трафик, NIM, ROE производитель с нуля IV.2c Атомы в каталоге перечислены, кода нет.
3.15 Аналитические комментарии классификатор + правила доверия IV.6c Спецификация помечает как gap вне первой очереди. Требует именованного аналитика, а не тональности новости.

Волна 4. Собственная разработка поверх купленного

Здесь нет источника, который можно подключить: это новые модели данных и новая аналитика. Каждый пункт зависит от волн выше и до них бессмысленно доходить.

  1. 4.1

    Первичные размещения

    квартал

    Ориентир купона, итоговый купон, объём книги, объём размещения, факт переноса или отмены, поведение в первый день торгов относительно номинала. price_volume_anomaly это не покрывает: он не знает, что день первый, и не сравнивает цену с номиналом.

    Закрывает I.9a, I.9b, I.9c.

  2. 4.2

    Отраслевое заражение

    квартал + источник дефолтов по всему рынку

    Отраслевая классификация, peer group с составом на дату, отделение системного движения от специфического, правила каскада. Спецификация отдельно отмечает: опрос только по наблюдаемым бумагам этот атом не закрывает в принципе — нужен источник дефолтов по всему рынку.

    Рыночный агрегат спредов (I.5b) отсюда вынесен в шаг 2.6: он считается из наших собственных рядов и не ждёт внешнего источника.

    Закрывает I.3b.

  3. 4.3

    Производные импакты по погашениям

    после 3.2

    Сопоставление объявления с чистой ценой относительно номинала (I.7b) и реинвестиционный эффект (I.7c). Второй пункт делать только после того, как формулировка ТЗ будет исправлена в решении 0.3 — иначе он будет реализован зеркально неверно.

    Закрывает I.7b, I.7c.

  4. 4.4

    Модификаторы дефолта и пересмотр плана капзатрат

    после 3.2 и 3.3

    Срок до погашения, доля розничных держателей, ожидаемый recovery — доводят I.3a и I.8a до полного. Отдельно: прогнозный CAPEX против фактического (сейчас реализован только факт) доводит III.2.

Что не закроется никогда — и это правильно

I.4d, «приход сильной команды». Спецификация помечает как неавтоматизируемое до утверждения политики. Формализовать «сильную» команду без ручной оценки аналитика нельзя, и попытка это сделать даст события, которым нельзя верить.

IV.7c, разметка накопления и распределения. Всплеск объёма при стоящей цене фиксируется честно, но определить по дневной свече, кто накапливал и кто распределял, недостоверно. Сейчас это conditional — и должно им остаться.

I.2a, Долг/EBITDA из РСБУ. EBITDA в российском стандарте нет. Закроется только через МСФО (3.6), никакой парсер ГИР БО этого не изменит.

Итог по арифметике: из 56 позиций ТЗ одна закрыта полностью сейчас (IV.7a). Волны 1 и 2 доводят это примерно до двадцати — без единого контракта, если решение 0.2 принято в пользу витрины. Остальные тридцать шесть требуют денег, и три из них не закроются вовсе.

Г

Альтернативные источники: где можно не платить

Вопрос поставлен правильно, и по ГИР БО ответ оказался неожиданным. Этот раздел — не приложение: его находка стала ошибкой Б.10, изменила формулировку блокера в восьми строках таблицы А и переставила пункт плана. Здесь собрано обоснование, ссылки на него расставлены по всему документу.

Главное: платный REST ГИР БО покупает транспорт, а не метрики

В коде два независимых адаптера ГИР БО, и они делают разные вещи.

trusted_fns_bfo.py — бесплатный, ходит во внутренний JSON публичной витрины bo.nalog.gov.ru и разбирает значения в ровно те метрики, которые нужны ТЗ: общий и короткий долг, доля короткого долга, ЧДП, покрытие процентов, CAPEX-proxy, FCF-proxy, выручка. Из него же выходят правила credit_metric_change, capex_change и annual-change.

trusted_fns_girbo_rest.py — платный, подписочный REST v1.0.3. И в его собственном докстринге написано прямо: «does not parse financial statement values and therefore makes no claim about IFRS, consolidated scope, EBITDA, consensus, or semantic event coverage». Он скачивает файлы отчётности как байты и складывает их в хранилище сырья. trusted_fns_girbo_rest.py:1-35

Из этого следует то, чего нет ни в одном плане: оплата подписки ФНС не даёт ни одного события. После неё остаётся написать парсер скачанных документов БФО — то есть ту самую работу, которая для витрины уже сделана. В плане работ пункт «платный REST ГИР БО открывает три семейства» переоценивает отдачу примерно вдвое.

Почему бесплатный адаптер не работает — и это не архитектурный запрет

Формулировка «публичный showcase-адаптер запрещён архитектурно» неточна. Он заблокирован двумя разными вещами, и обе снимаются без денег.

  • Одним env-флагом. fns_girbo_enabled() требует операторскую половину (она уже true в настройках) и FNS_GIRBO_PUBLIC_UI_CONTRACT_VERIFIED — подтверждение письменного разрешения на использование. Это юридический вопрос, а не платёж и не техника. trusted_fns_bfo.py:105-116 · settings: source.fns_girbo.enabled=true
  • Правилом маршрутизации. _fns_bfo_adapter_enabled() отдаёт управление платному REST всякий раз, когда включён trusted-режим — «две несовместимые схемы не должны писать один и тот же source/stream». То есть как только вы поднимете контур (шаг 2 плана), бесплатная витрина станет недостижимой независимо от флагов. trusted_worker.py:480-490

Развилка, которую стоит поставить перед стейкхолдером явно: получить письменное разрешение ФНС на использование витрины (бесплатно, но небыстро) — или заплатить за REST и дописать парсер документов. Третий вариант — проверить, публикует ли ФНС наборы бухотчётности ГИР БО в разделе открытых данных единым архивом: если да, это снимает и вопрос разрешения на витрину, и вопрос парсинга недокументированного транспорта. Этот вариант в коде не рассматривался вовсе.

leadintel: уже развёрнут на этом сервере и закрывает две задачи

Самый недооценённый источник — свой же. /home/www/leadintel, база lead_intel на том же Postgres. Проверил содержимое:

  • 48 558 компаний, у всех без исключения проставлены ИНН и ОГРН. Из них 8 048 полностью обогащены, 948 имеют выручку выше 50 млрд ₽ — то есть наша вселенная публичных эмитентов попадает в обогащённую полосу целиком. companies: 48 558 / inn 48 558 / ogrn 48 558
  • Есть готовый матчер по названию (matching_service, brand_index) и таблица company_tickers — со схемой, но пустая. Заполнить её для наших эмитентов один раз означает получить точный двусторонний джойн тикер ↔ ИНН вместо сопоставления по имени.
  • Помимо идентификаторов там лежат director_name, director_inn, director_position, founders, status, liquidation_date, а также выручка, прибыль, активы и численность.

Проверил на живых данных: Газпром — ИНН 7736050003, Миллер; Роснефть — 7706107510, Сечин; Лукойл — 7708004767. Всё сходится с реальностью.

Одна честная оговорка. Сопоставление по названию неоднозначно: запрос «Лукойл» даёт головное ПАО и три дочерних общества с выручкой в сотни миллиардов. Резолвер обязан иметь правило разрешения неоднозначности — правовая форма плюс максимальная выручка плюс ручное подтверждение — и писать verified только после него. Иначе вы привяжете события материнской компании к «Лукойл-Пермь», и это будет хуже, чем отсутствие ИНН.

Dadata: где действительно дешевле, а где иллюзия

Интуиция верная, но применима не там, где кажется. Разберу по блокам ответа findById/party.

  • Замена ГИР БО — нет. Финансовый блок Dadata приходит из открытых данных ФНС и содержит агрегаты (выручка, расходы, налоги, недоимка), а не строки баланса и ОДДС. Покрытие процентов считается из строк 2300 и 2330, CAPEX-proxy — из 4221 и 4100. Этих строк в Dadata нет. Значит I.2b, I.2c и III.2 она не закрывает. Частично закрывает выручку для IV.2b — одну метрику из трёх.
  • Смена руководителя — да, и это лучший кандидат. Блок management отдаёт ФИО и должность единоличного исполнительного органа по ИНН. Периодический опрос с диффом даёт рабочий сигнал для I.4a, который сейчас числится за контрактом e-Disclosure. Оговорка: ЕГРЮЛ фиксирует смену с задержкой в дни-недели после решения, поэтому для трейдера это подтверждение, а не опережение. Но лучше, чем ноль.
  • Собственники — слабо. Блок учредителей для публичных АО почти всегда номинальный: реестр акционеров ведёт регистратор, в ЕГРЮЛ он не отражается. Для I.4b и I.4c это не замена СПАРКу, и выдавать такое за смену контролирующего собственника было бы хуже, чем не выдавать ничего.
  • Статус юрлица — полезный дешёвый скрининг. Ликвидация, реорганизация, банкротные отметки приходят раньше, чем вы заметите их вручную. Авторитет факта остаётся за Федресурсом, но как ранний флаг годится.

Самое дешёвое применение Dadata никак не связано с отчётностью

В базе 17 верифицированных ИНН и 17 ОГРН на 71 эмитента. issuer_identifiers: INN 17/17 verified, OGRN 17/17, всего issuers 71

А матчинг Федресурса и блокировок ФНС идёт именно по точному верифицированному ИНН или ОГРН. То есть если завтра придут креды Федресурса, покрытие составит 24% эмитентов — и это будет выглядеть как «источник работает плохо», хотя источник будет ни при чём.

Dadata по названию и МОЕХ-идентификатору закрывает ровно эту задачу, стоит копейки и не требует никаких архитектурных решений, потому что ИНН — не событие, а идентификатор: у него нет проблемы авторитета факта. Это подготовительный шаг к пунктам 8 и 9 плана, который надо сделать до них, а не после.

Итог по разделу

Все пять выводов ниже уже учтены в списке раздела В — здесь они собраны вместе, чтобы было видно, что именно изменилось от разбора альтернатив.

  • Раздел Б получил новую ошибку документа Б.10: витрина ГИР БО не запрещена архитектурно, а платный REST не разбирает значения.
  • Таблица А: в строках I.2b, I.2c, I.2d, I.2e, III.2 и IV.2b блокер переформулирован — это не оплата REST, а разрешение на витрину плюс правило маршрутизации. В I.3c и I.3e добавлено, что матчинг упирается в 17 верифицированных ИНН из 71. В I.4a появился бесплатный обходной путь через leadintel.
  • Волна 0: выбор «разрешение на витрину против оплаты REST» стал решением 0.2 — от него зависит, стоят шесть позиций ТЗ в волне 2 или в волне 3. Туда же добавлена проверка открытых данных ФНС.
  • Волна 1: шаг 1.4 из разовой проставки ИНН превратился во встраивание резолва в онбординг эмитента, источник — leadintel. Шаг 1.5 (дифф по руководителям) тоже переведён на leadintel вместо внешнего API.
  • Волна 3: платный REST ГИР БО из третьего приоритета стал условным шагом 3.4, нужным только если решение 0.2 принято против витрины.

Одно предостережение по всем аггрегаторам сразу — и оно относится к leadintel тоже. В архитектуре уже есть уровень авторитета contracted_aggregator_with_primary_provenance, так что и Dadata, и собственный агрегатор туда формально помещаются. Но у неё есть и отдельный гейт прав на показ: trusted_source_rights.py отделяет «можем читать» от «можем показывать пользователю», и коммерческие вендоры там числятся необъявленными. Подключая любой новый источник, объявляйте оба права сразу, иначе позже включение проверки прав молча погасит часть ленты.

Д

Служебные сигналы: новостной слой над теми же требованиями

Отдельный механизм, спроектированный стейкхолдером и заведённый в бэклог как T-0496. Он закрывает те же пункты ТЗ, но с другой стороны — из новостей, а не из официального источника, и потому доступен раньше и без контрактов.

Как это устроено, коротко

Служебный сигнал не подписываемый и не создаёт тегов: он просто знает состав избранного компании. Новости матчатся на его ключевые слова, и при совпадении уходят в стандартный ИИ-триаж — но с дополнительным условием, что новость должна попасть хотя бы в одного эмитента или инструмент из избранного.

Ключевые слова сигнал пишет и обновляет сам, но целятся они в события, а не в эмитентов: попадание в конкретную бумагу — отдельный шаг. После триажа анализатор не только пишет разбор, но и может создать событие с пометкой «кандидат» — это полноценное событие системы, просто с иной достоверностью. В ленте новость и порождённое событие склеиваются в тему с пилюлей «Событие [N]»; если позже приходит подтверждение из официального источника, в теме оказываются все трое.

Ключевое отличие от калькуляторов: кандидат — это утверждение, прочитанное в тексте, а не вычисленное из ряда. Поэтому он не заменяет официальный факт, а предшествует ему. Решение стейкхолдера от 4 августа: в первой версии дублирование с калькулятором допустимо — «если есть новость, значит есть повод»; разделение ответственности делается позже, отдельным этапом.

Сигнал Что ловит Закрывает Почему именно новостями
S1 Корпоративные действия и структура капитала Размещение состоялось, сорвано или перенесено; переподписка книги; купон ниже ориентира; оферта и досрочное погашение; созыв СД о прибыли; дивиденды; байбэк; допэмиссия; крупная сделка; M&A I.7a · I.9a · I.9b · I.9c · IV.1.1 · IV.1.3 · IV.1.4 · IV.6a Восемь позиций ТЗ, все заперты за контрактами НРД и ЦРКИ. При этом объявляются публично раньше, чем приходят по контрактному каналу.
S2 Платёжный стресс и дефолт Пропуск купона, технический дефолт, неисполнение оферты, переговоры о реструктуризации до формального дефолта, дефолт сопоставимого эмитента в секторе I.3a · I.3b · I.8a · I.8b · I.8c · II.1 I.8b — единственный пункт ТЗ, ловящий момент до обвала, и он принципиально новостной: в официальный источник переговоры не попадают вообще. Плюс сегодня в базе ноль событий факта невыплаты.
S3 Надзор, суд, правоохрана — и обратные события Уголовное дело, предписания ЦБ и ФАС, претензии Росприроднадзора и налоговой, обеспечительные меры, лицензии, блокировка счетов — и симметрично: снятие претензий, разблокировка, прекращение дела I.3c · I.3d · I.3e · I.3f · II.2 Крупнейший кластер «кода нет». И единственный практичный способ получить обратные события: у ФНС снятие принципиально не выводится из исчезновения строки, а исторического фида ограничений нет. Новость о разблокировке есть всегда.
S4 Контроль, собственность и менеджмент Смена контролирующего собственника, вход стратегического инвестора, продажа доли владельцем, сделки инсайдеров, внезапный уход или назначение первых лиц I.4a · I.4b · I.4c · I.4d · IV.6b I.4d спецификация помечает как неавтоматизируемое детерминированным правилом — «сильная команда» не формализуема. Это ровно случай, где кандидат является правильным ответом, а не суррогатом. Плюс ЕГРЮЛ по публичным АО даёт номинальных учредителей, то есть по I.4b/I.4c новости объективно точнее реестра.
S5 Результаты против ожиданий и сырьевые ориентиры Факт выше или ниже консенсуса и гайденса, пересмотр прогноза компании, объявленные даты отчётности, LFL и трафик у ритейла, NIM и ROE у банков, прогнозы спроса и предложения по сырью, пересмотр плана капзатрат IV.2a · IV.2c · IV.1.2 · III.1a (5 товаров) · III.1b · C-CAPEX-1/2 Весь блок требует либо платного консенсус-вендора, либо источников, которых нет ни у кого из подключённых. При этом «отчиталась хуже ожиданий» и «понизила прогноз по литию» — стандартный новостной жанр.
S6 Рейтинговые действия Повышение и понижение рейтинга эмитента и выпуска, изменение прогноза, watch и пересмотр, отзыв, присвоение первичного рейтинга I.6a · I.6b · I.6c Калькулятор сейчас мёртв: живой поток репозитория ЦБ падает, и в ленте нет ни одного апгрейда или даунгрейда — только отзывы рейтингов Роснефти 2020–2021. Сигнал подменяет сломанный калькулятор немедленно и остаётся полезным после ремонта.
S7 Рыночные движения, ставки и чувствительности Резкое движение цены или оборота, расширение и сужение спредов, наклон кривой, решение по ставке и его эффект на закредитованных, курс и экспортёры, спот по профильному сырью I.1 · I.5a · I.5b · I.10a–d · IV.3 · IV.4 · IV.5 · IV.7a–c Новость про движение цены — это освещение события, а не конкурирующий источник числа; механизм тем поглощает такой случай корректно. Плюс честно закрываются дыры, которых у калькулятора нет по устройству: внутридневное движение, сглаженное дневной свечой, и бумаги вне покрытия.

Что это меняет для плана из раздела В

Семь сигналов дают новостной слой по всем 56 позициям ТЗ. Это не отменяет ни одного пункта плана: кандидат — не подтверждённый факт, и каталог покрытия по-прежнему закрывается калькуляторами и официальными источниками.

Но это радикально меняет что видит пользователь, пока идут волны 2 и 3. Сегодня по семнадцати позициям ТЗ в ленте нет ничего вообще — ни строки, ни намёка. С сигналами по ним появляется освещение с первого дня, а официальный факт приезжает позже и садится в ту же тему.

Два пункта, которые от этого становятся важнее, чем выглядели. Ранжирование (шаг 1.7) — потому что семь сигналов добавят в ленту ещё один поток, а severity уже сейчас не несёт информации. И замер матчера новость→эмитент: портфельный гейт опирается на него, а по прошлым замерам около 95% строк ленты не имеют серверной привязки к эмитенту. Если это так, выход кандидатов будет близок к нулю — и это надо измерить до постройки, а не после.