build 043: finalize Quotes Feed architecture verification
This commit is contained in:
514
docs/migrations/build_041.md
Normal file
514
docs/migrations/build_041.md
Normal 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 завершён.
|
||||
Reference in New Issue
Block a user