# Состояние контент-генератора — 4 августа 2026 (после правок)

Актуальный срез. Предыдущий документ —
[`content-audit-2026-08-04.md`](content-audit-2026-08-04.md) — оставлен как
снимок состояния **до** правок; здесь то, что есть сейчас.

> **Оперативное обновление, 04.08 20:00 МСК.** После первоначального аудита
> завершён ремонт CM-Balance: 22 рабочих 4H-цепи архивированы и пересобраны на
> production `backup`/wide STEP; RTSI переведён на автоматические weekly-свечи
> `RTSI@MISX` с Finam и сверку с ручными 13 событиями (`OK`); OI читает
> рассчитанный `current_regime`; CM считает оба направления, а матрица решений
> покрывает ранее недостижимые ветки. Все профильные тесты ремонта прошли
> (`40 passed`), полный набор — `54 passed`, шесть прежних падений
> `tests/test_database.py` остались вне этой работы. Подробности и чеклист —
> в [`cm-balance-repair-plan.md`](cm-balance-repair-plan.md).

---

## Что работает

### Продукты в проде (2)

| Что | Модуль | Запуск | Состояние |
|---|---|---|---|
| Ежедневный макро-брифинг → Telegram | `content/macro_briefing.py` + `orchestrator/scheduler.py` | systemd-юнит `cg-scheduler.service`: сборка **и публикация** в 10:00 МСК одним шагом; 19:00 — повторная попытка, только если утренняя не прошла | собирается, ~2.3 тыс. знаков, график сегментов прилагается |
| GCRM CM-Balance ротация → алерт | `content/russia_rotation.py`, `content/data_updater.py` | cron: `rotation-4h` каждые 4 ч, `rotation-tlt` 20:00, `rotation-oi` 20:55, `rotation-check` пт 20:55 | 22 тикера, 22 цепи 4H, алерт при смене статуса/появлении REVERSAL |

### Данные — все автоматом

| Источник | Объём | Свежесть |
|---|---|---|
| FRED | 23 924 строк | 04.08 |
| CBR | 3956 строк | 03.08 |
| MOEX ISS | 814 строк | 02.08 |
| Yahoo | 29 тикеров | 03.08, отстающих нет |
| Bybit BTC | ежедневно | 03.08 |
| COT (CFTC) | 23 196 строк, 34 инструмента | отчёт 28.07 (норма — недельная публикация) |
| Finam gRPC 60M | 22 тикера | 03.08 |
| Календарь (FRED release dates) | 25 будущих событий | 04.08 |
| World Bank / IMF | 7038 / 36 816 строк | еженедельно, годовые ряды |

---

## Что исправлено 4 августа

### Yahoo — 13 тикеров «встали»

**Причина оказалась не в Yahoo.** Тикеры удалили из `config/yahoo_tickers.json`:
в конфиге было 13, в БД 27. Сборщик отрабатывал свои 13 и честно рапортовал
`0 errors` — остальные просто никто не запрашивал. Отсюда и «молчаливая
деградация»: ошибки не было, потому что не было и запроса.

Возвращены 13 выпавших (`BND, COPX, DBB, FXE, FXI, FXY, GDX, LQD, UUP, VNQ,
VWO, XLE, XME`) плюс `IEF` и `SHY` — они нужны `rotation_state_machine`, но их
не было в конфиге никогда.

Было: 13 в конфиге / 27 в БД, из них 13 не обновлялись 13 дней.
Стало: 28 в конфиге / 29 в БД, все до 03.08, разрыв 21.07–03.08 закрыт без дыр.

### Календарь — Investing.com 403

Блокировка жёсткая: 403 на любые заголовки и User-Agent, с `www` и без. Обход
не делался — заменён источник.

Основной источник теперь **FRED release dates**: официальный API, ключ уже есть
в `FRED_API_KEY`. В `providers/economic_calendar.py` добавлен `_fetch_fred()` с
курируемой картой `FRED_RELEASES` из 12 релизов (NFP, CPI, PPI, GDP, PCE, Retail
Sales, Industrial Production, JOLTS, ECI, New Residential Construction, Advance
Economic Indicators, H.4.1) — с важностью и регламентным временем публикации.

Время конвертируется ET → MSK **на дату конкретного события** через `zoneinfo`,
а не фиксированным сдвигом: иначе зимой всё уехало бы на час из-за
американского перехода на летнее время.

Investing.com оставлен запасным источником — если оживёт, добавит прогнозы и
неамериканские события поверх фредовских дат. Его 403 логируется как `INFO`,
а не `ERROR` каждый день.

**Ограничение нового источника:** FRED отдаёт только дату публикации — без
прогноза, факта и предыдущего значения, и только по США. Раньше Investing давал
и это, и события по РФ/ЕС/Китаю. Календарь стал беднее, но работает.

### Росстат — 403, ноль строк

Главное: три показателя в `rosstat_indicators.json` (M2, денежная база, узкая
база) — **это данные ЦБ, а не Росстата**. Конфиг с самого начала ходил не туда.

Перебраны все достижимые источники:

| Источник | Результат |
|---|---|
| `fedstat.ru` | 403 с любыми заголовками (ответ 80 байт — страница блокировки) |
| `rosstat.gov.ru` | хост недоступен с этой машины |
| `sdmx.gks.ru` (ЕМИСС) | хост недоступен |
| `cbr.ru/statistics/ms/` | HTTP 200, но страница рендерится JS — ни таблиц, ни ссылок на файлы |
| `cbr.ru/dataservice/` | 501 Error / пустой ответ |
| CBR SOAP `MainInfoXML` | работает, но денежных агрегатов там нет — только ставка, инфляция, резервы |
| FRED, ряды по РФ | CPI, промпроизводство, безработица, M2 — оборваны на 2021–2022 |

Попутно объяснилось, почему у `cbr.m2` ноль строк: коллектор ищет M2 в
`MainInfoXML`, где его никогда не было.

Автоматического источника для российских денежных агрегатов с этого хоста
**нет**. Вместо этого подключены живые российские ряды FRED — 1985 строк:

```
NGDPRNSAXDCRUQ   ВВП РФ реальный                     до 2026-01
RBRUBIS          Реальный эфф. курс рубля (BIS)      до 2026-06
NBRUBIS          Номинальный эфф. курс рубля (BIS)   до 2026-06
CCUSMA02RUM618N  USD/RUB среднемесячный (OECD)       до 2026-06
RUSEPUINDXM      Индекс неопределённости политики РФ до 2026-06
SPASTT01RUM657N  Индекс акций РФ (широкий, OECD)     до 2026-06
```

Плюс уже работавший CBR: ключевая ставка, ИПЦ г/г, резервы, RUONIA, курсы.
Было «ноль российской макростатистики» — стало шесть рядов автоматом.

Коллектор Росстата не удалён: если fedstat разблокируется, он заработает. При
403 теперь пишет **один** внятный warning на прогон вместо трёх ERROR и
указывает, где данные искать вместо него.

### Сервисы переведены в systemd

Планировщик CG жил в tmux `cg:0`, портал — безнадзорным фоновым процессом.
Оба не переживали падение и перезагрузку машины.

Теперь это юниты **пользователя** (пароль для `sudo` на машине запрашивается,
системные юниты недоступны; но у `myhermess` включён linger, поэтому
пользовательские стартуют при загрузке):

```
systemctl --user status cg-scheduler   # планировщик CG
systemctl --user status s0-portal      # портал Next.js :3000
```

У обоих `Restart=always` и предохранитель на 5 перезапусков, чтобы сломанная
сборка не крутила бесконечный цикл.

Заодно переписан `auto-deploy.sh`: он делал `pkill` + `tmux send-keys`, что
после переезда подняло бы **второй** планировщик и дубли постов в канале.
Теперь дёргает `systemctl --user restart cg-scheduler` и сам выставляет
`XDG_RUNTIME_DIR` — иначе из-под cron не найдёт шину пользователя.

---

## Что осталось сломанным

### Команды без модулей (9)

```
gas-monitor  → content.thursday_gas_monitor      ModuleNotFoundError
aeo-context  → providers.aeo2026_provider        ModuleNotFoundError
signal-scan  → content.signal_scanner            ModuleNotFoundError
daily        → content.signal_card               ModuleNotFoundError
weekend      → content.signal_card               ModuleNotFoundError
cot-radar    → content.signal_card               ModuleNotFoundError
moex         → content.russia_crypto_scanner     ModuleNotFoundError
crypto       → content.russia_crypto_scanner     ModuleNotFoundError
fomc         → content.fomc_card                 ModuleNotFoundError
```

Шести модулей никогда не было в git-истории — они не удалены, а не написаны.
`run.py` объявляет команды под ненаписанный код. Это почти все «карточные»
форматы: дневная сигнальная карта, выходной сет-ап, крипто-уголок,
FOMC-карточка, газовый монитор.

### Мёртвый код (4 модуля, ~1800 строк)

`content/scenario_engine.py`, `content/sector_rotation.py`,
`content/m2_cot_cycle_analysis.py`, `content/regime_stepper.py` — никто не
вызывает. Последний вдобавок не импортируется:
`ImportError: cannot import name 'auto_load_csv' from 'calibrate_step_dw'`.

### Данные без автообновления

- **`rotation_panel.json`** — писателя в коде нет вообще, при 7 читателях
  (`macro_briefing`, `cot_analyzer`, `cot_es_deepdive`, `cot_gold_deepdive`,
  `intermarket_signals`, `scenario_engine`, плюс `update-segments` берёт его как
  определение вселенной US ETF). Заморожен с 22.07.
  Брифинг частично спасён: секция сегментов сначала пробует свежий
  `segment_state.json`. График сегментов и все COT-разборы читают
  `rotation_panel` напрямую, без фолбэка.
- **`update-segments`** — не в планировщике и не в cron, только руками.
- **`calendar_scenarios.json`** — писателя нет; читает `scenario_engine`
  (который и так мёртвый).
- **`vol_regimes.json`** — ни читателей, ни писателей.
- Исследовательские артефакты заморожены на 21–22.07: `cot_signals.json`,
  `rotation_leadlag.json`, `rotation_cot_crosscheck.json`,
  `m2_dollar_leadlag.json`, `macro_causal_chain_thesis.json`, `gold_*`,
  `fomc_*`, `*_xref.json`. Часть — вход для `rotation_state_machine`.
- `macro_briefing` ищет `../../../1st/config/economic_events.json` — каталога
  `1st/` не существует, ветка всегда возвращает пусто.

### Латентные баги

- **Невалидный HTML в публикации.** 31.07 брифинг пять раз подряд не ушёл:
  `Telegram API HTTP 400: Unsupported start tag "5%):</b"`. В текст попала
  подстрока вида `(<5%)`, отправка идёт в HTML без экранирования. Сейчас
  публикуется нормально — баг сработает снова, когда данные дадут такую же
  строку.
- **`gcrm-context` падает** — `AttributeError: 'NoneType' object has no
  attribute 'get'` в `gcrm_history.py:423`.
- **11 черновиков зависли неопубликованными.** Страховочная публикация в 19:00
  берёт ровно один черновик (`get_posts(status="draft", limit=1)`) и только
  если он создан сегодня. Всё, что накопилось от прошлых сбоев (самый старый —
  22.07), она не разберёт никогда.
- **Тесты:** 33 passed, 6 failed — все падения в `tests/test_database.py`,
  доэтапные.

### Пробелы в данных, которые правками не закрыть

- Российские ИПЦ, M2, денежная база, промпроизводство, безработица —
  автоматического источника нет (см. таблицу выше).
- В календаре нет прогноза/факта/предыдущего значения и нет событий вне США.

---

## Изменённые файлы

```
config/yahoo_tickers.json                    +15 тикеров
config/fred_series.json                      +6 российских рядов
providers/economic_calendar.py               источник FRED + понижен уровень лога Investing
providers/rosstat_client.py                  один warning на прогон вместо ERROR-спама
```

Бэкапы конфигов и календаря до правок — в скретчпаде сессии.
