build 043: finalize Quotes Feed architecture verification

This commit is contained in:
2026-07-14 22:16:39 +03:00
parent 225f07bc4b
commit a6325cf4cf
5 changed files with 2448 additions and 0 deletions

View File

@@ -0,0 +1,514 @@
# Build 041 — Аудит и удаление остатков legacy quote parsing
**Статус:** завершён
**Подсистема:** Market Data Acquisition / Quotes Feed
**Проект:** Dzentra
**Тип изменения:** архитектурная миграция без изменения рабочего поведения
**Основное требование:** не изменять логику работы текущих файлов и наблюдаемое поведение бота
---
## 1. Цель Build
Цель Build 041 — выполнить аудит оставшихся production-зависимостей от legacy-разбора котировок и удалить подтверждённо неиспользуемую дублирующую логику WebSocket quote parsing из legacy integration layer.
Build продолжает миграцию Quotes Feed после:
- Build 039 — удаления legacy `TickerPrice` и market snapshot dict layer;
- Build 040 — восстановления и фиксации REST fallback через канонический Quotes acquisition pipeline.
Основной архитектурный принцип Build 041:
~~~text
Raw Dzengi WebSocket payload
DzengiWebSocketQuoteAdapter
schema validation
parser
value validation
mapper
canonical Quote
runtime consumers
~~~
Production-код вне `market_data/acquisition` не должен самостоятельно интерпретировать структуру WebSocket payload для получения `bid_price` и `ask_price`.
---
## 2. Основное ограничение
Главное требование Build 041:
> **Не менять логику работы текущих файлов.**
В рамках Build запрещалось:
- изменять торговую логику;
- изменять алгоритмы принятия решений;
- изменять execution pricing semantics;
- изменять формат канонической модели `Quote`;
- изменять REST Quotes Feed;
- изменять WebSocket transport;
- изменять алгоритм переподключения WebSocket;
- изменять runtime lifecycle;
- изменять журналирование;
- изменять UI;
- изменять обработку торговых сигналов;
- изменять формат runtime-состояния;
- удалять код без доказанного отсутствия production-зависимостей.
Допускалось только удаление legacy quote parsing, если одновременно подтверждены следующие условия:
1. production-код уже использует канонический `DzengiWebSocketQuoteAdapter`;
2. удаляемая функция больше не вызывается;
3. соответствующая логика уже реализована в `market_data/acquisition`;
4. полный набор тестов подтверждает отсутствие регрессий.
---
## 3. Предварительный аудит
Перед изменениями был выполнен аудит всех известных признаков legacy quote parsing.
Проверялись:
- прямое чтение `lastPrice`;
- прямое чтение `bidPrice`;
- прямое чтение `askPrice`;
- прямое чтение `closeTime`;
- прямое чтение `eventTime`;
- прямое чтение `bids`;
- прямое чтение `asks`;
- функции извлечения best bid / best ask;
- старые market snapshot helpers;
- старые ticker price helpers;
- прямое создание `Quote`;
- REST endpoint `/api/v1/ticker/24hr`;
- все production-потребители `DzengiWebSocketQuoteAdapter`;
- все production-потребители `QuotesFeed`;
- все production-потребители `QuoteAcquisitionService`.
Аудит показал, что корректный разбор REST-котировок уже сосредоточен в:
~~~text
src/market_data/acquisition/
├── adapters/dzengi/models.py
├── adapters/dzengi/parser.py
├── adapters/dzengi/mapper.py
├── adapters/dzengi/rest.py
├── adapters/dzengi/websocket.py
├── validation/schema.py
├── validation/values.py
├── handlers/quotes_handler.py
├── feeds/quotes_feed.py
└── service.py
~~~
При этом в legacy integration layer оставались отдельные helpers, самостоятельно интерпретировавшие структуру WebSocket payload.
---
## 4. Обнаруженные legacy helpers
Аудит выявил следующие legacy-функции.
### `src/integrations/exchange/market_stream.py`
~~~python
def _extract_depth_prices(
event: JsonDict,
) -> tuple[float | None, float | None]:
...
~~~
~~~python
def _extract_first_price(
value: object,
) -> float | None:
...
~~~
Эти функции самостоятельно разбирали:
~~~text
bids
asks
bidPrice
askPrice
~~~
и тем самым дублировали ответственность канонического Dzengi WebSocket quote adapter.
### `src/integrations/exchange/market_data_runner.py`
~~~python
def _extract_best_price(
...
) -> float | None:
...
~~~
Функция также содержала legacy-логику извлечения цены непосредственно из сырой структуры WebSocket payload.
---
## 5. Подтверждение канонического пути
До удаления legacy helpers было подтверждено, что рабочий runtime уже использует:
~~~python
DzengiWebSocketQuoteAdapter
~~~
Канонический путь обработки WebSocket-котировки:
~~~text
ExchangeWebSocketClient
raw WebSocket message
DzengiWebSocketQuoteAdapter.map_message()
validate_dzengi_websocket_quote_schema()
parse_dzengi_websocket_quote()
validate_dzengi_websocket_quote_values()
map_dzengi_websocket_quote_to_quote()
Quote
~~~
Таким образом, production runtime получает уже каноническую модель:
~~~python
Quote
~~~
и не должен повторно разбирать сырой Dzengi payload.
---
## 6. Выполненные изменения
В Build 041 удалена подтверждённо избыточная legacy quote parsing logic.
Изменения затронули:
~~~text
app/src/integrations/exchange/market_stream.py
app/src/integrations/exchange/market_data_runner.py
app/tests/unit/integrations/exchange/test_market_stream.py
app/tests/unit/integrations/exchange/test_market_data_runner.py
~~~
Удалены legacy helpers:
~~~text
_extract_depth_prices
_extract_first_price
_extract_best_price
_extract_market_event
~~~
Также удалены остаточные прямые обращения вида:
~~~text
first.get("bidPrice")
first.get("askPrice")
~~~
в проверяемом legacy integration-контуре.
---
## 7. Что не изменялось
Build 041 не изменял:
### REST Quotes Feed
Не изменялись:
~~~text
DzengiQuoteDocumentSource
DzengiQuoteDocumentHandler
QuotesFeed
QuoteAcquisitionService
~~~
Не изменялся endpoint:
~~~text
/api/v1/ticker/24hr
~~~
Не изменялся REST fallback, восстановленный и зафиксированный в Build 040.
### Каноническая модель Quote
Не изменялись:
~~~text
symbol
last_price
bid_price
ask_price
source
exchange_timestamp
received_at
~~~
### WebSocket acquisition pipeline
Не изменялись:
~~~text
DzengiWebSocketQuoteAdapter
validate_dzengi_websocket_quote_schema
parse_dzengi_websocket_quote
validate_dzengi_websocket_quote_values
map_dzengi_websocket_quote_to_quote
~~~
### Runtime semantics
Не изменялись:
- выбор торгового инструмента;
- запуск и остановка market runtime;
- reconnect logic;
- обработка WebSocket-соединения;
- вычисление spread;
- формирование runtime-состояния;
- execution pricing;
- стратегия;
- автоторговля;
- Telegram UI;
- журнал.
---
## 8. Архитектурный результат
До Build 041 часть legacy integration layer всё ещё содержала собственную интерпретацию WebSocket payload:
~~~text
Raw WebSocket payload
├──→ legacy parser in market_stream
├──→ legacy parser in market_data_runner
└──→ canonical DzengiWebSocketQuoteAdapter
~~~
После Build 041:
~~~text
Raw WebSocket payload
DzengiWebSocketQuoteAdapter
Quote
market_stream / market_data_runner
~~~
Таким образом:
- разбор Dzengi WebSocket payload имеет единственный канонический путь;
- transport-specific parsing находится в `market_data/acquisition`;
- integration layer работает с канонической моделью `Quote`;
- дублирующая legacy parsing logic удалена;
- runtime-поведение сохранено.
---
## 9. Проверки
После изменений выполнены целевые тесты integration layer:
~~~bash
python -m pytest -q \
tests/unit/integrations/exchange/test_market_data_runner.py \
tests/unit/integrations/exchange/test_market_stream.py
~~~
Результат:
~~~text
15 passed in 0.10s
~~~
Выполнены целевые тесты канонического WebSocket quote acquisition pipeline:
~~~bash
python -m pytest -q \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_parser.py \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_mapper.py \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_adapter.py \
tests/unit/market_data/acquisition/validation/test_websocket_quote_schema.py \
tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py
~~~
Результат:
~~~text
24 passed in 0.02s
~~~
Выполнен полный набор тестов проекта:
~~~bash
python -m pytest -q
~~~
Результат:
~~~text
614 passed in 1.77s
~~~
---
## 10. Финальный grep-контроль
После удаления legacy quote parsing выполнена проверка:
~~~bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "_extract_depth_prices|_extract_first_price|_extract_best_price|_extract_market_event|first\.get\(\"bidPrice\"\)|first\.get\(\"askPrice\"\)" \
src tests
~~~
Результат:
~~~text
совпадений нет
~~~
Это подтверждает отсутствие проверяемых legacy helpers и прямого parsing `bidPrice` / `askPrice` через старый integration-код.
---
## 11. Сохранённые допустимые transport-specific поля
После Build 041 в проекте по-прежнему существуют обращения к:
~~~text
lastPrice
bidPrice
askPrice
closeTime
eventTime
bids
asks
~~~
Их наличие само по себе не является legacy-зависимостью.
Они допустимы внутри канонического Dzengi acquisition adapter:
~~~text
market_data/acquisition/adapters/dzengi/
market_data/acquisition/validation/
~~~
поскольку именно этот слой отвечает за:
- знание transport-specific формата Dzengi;
- structural validation;
- parsing;
- value validation;
- mapping в каноническую модель `Quote`.
Также такие поля допустимы в unit-тестах соответствующего transport adapter.
---
## 12. Критерии завершения Build
Build 041 считается завершённым, поскольку выполнены все критерии:
- [x] проведён аудит legacy quote parsing;
- [x] подтверждён единственный канонический WebSocket parsing pipeline;
- [x] удалён `_extract_depth_prices`;
- [x] удалён `_extract_first_price`;
- [x] удалён `_extract_best_price`;
- [x] удалён `_extract_market_event`;
- [x] удалены проверяемые прямые обращения к `bidPrice` и `askPrice` в legacy integration layer;
- [x] REST Quotes Feed не изменён;
- [x] REST fallback Build 040 сохранён;
- [x] каноническая модель `Quote` не изменена;
- [x] торговая логика не изменена;
- [x] runtime semantics не изменены;
- [x] целевые integration-тесты проходят;
- [x] целевые acquisition-тесты проходят;
- [x] полный набор из 614 тестов проходит;
- [x] финальный grep не обнаруживает удалённые legacy helpers.
---
## 13. Commit
Build зафиксирован коммитом:
~~~text
24a4da9 build 041: remove legacy websocket quote parsing
~~~
Предыдущий Build:
~~~text
e68ed7f build 040: restore quote REST fallback after build 039
~~~
---
## 14. Итог
Build 041 завершает удаление подтверждённых остатков legacy WebSocket quote parsing из integration layer.
Итоговая архитектура:
~~~text
Dzengi REST ticker/24hr
DzengiQuoteDocumentSource
DzengiQuoteDocumentHandler
QuotesFeed
QuoteAcquisitionService
Quote
Dzengi WebSocket message
DzengiWebSocketQuoteAdapter
Quote
Quote
runtime / execution / strategy / UI consumers
~~~
После Build 041:
- REST и WebSocket transport-specific parsing сосредоточен в `market_data/acquisition`;
- runtime получает каноническую модель `Quote`;
- legacy WebSocket parsing helpers удалены;
- дублирующая интерпретация raw payload устранена;
- рабочая логика текущих файлов сохранена;
- полный test suite подтверждает отсутствие обнаруженных регрессий.
Build 041 завершён.