Files
dzentra_bot/docs/migrations/build_041.md

514 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 завершён.