docs: ADR-008 (реализация цены из NATS) + PRICE-NATS.md + PlantUML (фаза 7)

ADR-007 (выбор источника binance-market/NATS) пришёл из другой сессии —
оставлен как есть, с апдейт-сноской. Реализация зафиксирована отдельным
ADR-008: NATS-консьюмер в шлюзе, цена x1e8 строковым парсером, раздача
фронту по WebSocket, деплой в k3s на testfront.binom.pw.
This commit is contained in:
2026-09-13 05:56:56 +03:00
parent 6e54e9ef4b
commit 30f851fb87
4 changed files with 331 additions and 8 deletions
+36 -1
View File
@@ -96,4 +96,39 @@
- **Результат:** задачи «TODO Цена SOL: реальный источник» (Фаза 0) и «PriceSource: реальная цена
(вместо RandomPriceSource)» (Фаза 2) переводятся в `IN_PROGRESS` со ссылкой на это решение.
Никаких новых подписок/оракулов не делаем — только интеграция в релейере (`smart-updown-token`/
`relayer` или `smart-updown-server`). Внешний статус сервиса см. README → «Внешние зависимости».
`relayer` или `smart-updown-server`). Внешний статус сервиса см. README → «Внешние зависимости».
**→ Обновление 2026-09-13: реализовано, см. ADR-008** (NATS-консьюмер в шлюзе, цена ×1e8,
раздача фронту по WS).
---
## ADR-008 — Реализация: цена SOL из NATS в шлюзе + раздача фронту по WS
- **Дата:** 2026-09-13
- **Статус:** принято, реализовано
- **Контекст:** ADR-006 зафиксировал предмет ставки (цена SOL), ADR-007 выбрал источник
(`binance-market` через NATS). Оставалось реализовать: сама цена была случайной
заглушкой `RandomPriceSource` / `simulated = 100_000_000 + Math.random()`. Исход раунда был
случаен, а не рыночен. При этом в контуре уже работает `binance-market` — сборщик сделок с
Binance, который публикует каждую цену в NATS (`market.price.<symbol>`).
- **Решение:** игровой шлюз (`smart-updown-token/relayer`) подписывается на NATS
(`market.price.solusdt`) и берёт цену оттуда. Заглушка удалена.
- **Транспорт:** core NATS `nats://192.168.88.93:4222`, subject `market.price.solusdt`.
Режим — **только чтение**; `binance-market` не трогаем.
- **Шкала:** строка из NATS переводится в целое ×**1e8** строковым парсером (без float):
`"101.89000000"` → `10189000000`. То же юнит-пространство, что у оракульных цен и у
прежней заглушки (`100_000_000`), поэтому контракт сравнивает цены корректно.
- **Раздача фронту:** шлюз транслирует каждый тик по WebSocket (`/ws`, снаружи
`wss://testfront.binom.pw/api/ws`), сообщение — только цена:
`{"type":"price","symbol":"solusdt","price":"102.30...","ts":...}`.
- **Отсутствие цены:** считается аварийным состоянием и обрабатывается мягко — шлюз
стартует без NATS, запрос цены ждёт таймаут, фронт показывает последнее значение и
переподключается. Отдельный сценарий деградации — тема для будущего решения.
- **За:** исход раунда становится рыночным (entry/exit — реальные цены Binance);
переиспользуется уже работающий сборщик, торговый контур не дублируется; шина отделяет
источник цены от её потребителей (игра, аналитика, будущие сервисы).
- **Против:** появляется зависимость от NATS и от `binance-market`; core NATS не хранит
историю (нет JetStream) — при разрыве подписки цена не «догоняется», нужен живой поток.
- **Альтернативы:** (а) прямая подписка шлюза на Binance WS — дублирование сборщика, лишние
внешние коннекты; (б) оракул on-chain (Pyth/Switchboard) — правильнее для боя, но
дороже и требует фидов; (в) заглушка — отклонено как единственный вариант, оставлена
возможность (`setPriceOverride`) для тестов.
- **Детали и диаграммы:** `docs/PRICE-NATS.md`, `docs/diagrams/price-flow.puml`.