15 KiB
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:
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, если одновременно подтверждены следующие условия:
- production-код уже использует канонический
DzengiWebSocketQuoteAdapter; - удаляемая функция больше не вызывается;
- соответствующая логика уже реализована в
market_data/acquisition; - полный набор тестов подтверждает отсутствие регрессий.
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-котировок уже сосредоточен в:
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
def _extract_depth_prices(
event: JsonDict,
) -> tuple[float | None, float | None]:
...
def _extract_first_price(
value: object,
) -> float | None:
...
Эти функции самостоятельно разбирали:
bids
asks
bidPrice
askPrice
и тем самым дублировали ответственность канонического Dzengi WebSocket quote adapter.
src/integrations/exchange/market_data_runner.py
def _extract_best_price(
...
) -> float | None:
...
Функция также содержала legacy-логику извлечения цены непосредственно из сырой структуры WebSocket payload.
5. Подтверждение канонического пути
До удаления legacy helpers было подтверждено, что рабочий runtime уже использует:
DzengiWebSocketQuoteAdapter
Канонический путь обработки WebSocket-котировки:
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 получает уже каноническую модель:
Quote
и не должен повторно разбирать сырой Dzengi payload.
6. Выполненные изменения
В Build 041 удалена подтверждённо избыточная legacy quote parsing logic.
Изменения затронули:
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:
_extract_depth_prices
_extract_first_price
_extract_best_price
_extract_market_event
Также удалены остаточные прямые обращения вида:
first.get("bidPrice")
first.get("askPrice")
в проверяемом legacy integration-контуре.
7. Что не изменялось
Build 041 не изменял:
REST Quotes Feed
Не изменялись:
DzengiQuoteDocumentSource
DzengiQuoteDocumentHandler
QuotesFeed
QuoteAcquisitionService
Не изменялся endpoint:
/api/v1/ticker/24hr
Не изменялся REST fallback, восстановленный и зафиксированный в Build 040.
Каноническая модель Quote
Не изменялись:
symbol
last_price
bid_price
ask_price
source
exchange_timestamp
received_at
WebSocket acquisition pipeline
Не изменялись:
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:
Raw WebSocket payload
├──→ legacy parser in market_stream
├──→ legacy parser in market_data_runner
└──→ canonical DzengiWebSocketQuoteAdapter
После Build 041:
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:
python -m pytest -q \
tests/unit/integrations/exchange/test_market_data_runner.py \
tests/unit/integrations/exchange/test_market_stream.py
Результат:
15 passed in 0.10s
Выполнены целевые тесты канонического WebSocket quote acquisition pipeline:
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
Результат:
24 passed in 0.02s
Выполнен полный набор тестов проекта:
python -m pytest -q
Результат:
614 passed in 1.77s
10. Финальный grep-контроль
После удаления legacy quote parsing выполнена проверка:
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
Результат:
совпадений нет
Это подтверждает отсутствие проверяемых legacy helpers и прямого parsing bidPrice / askPrice через старый integration-код.
11. Сохранённые допустимые transport-specific поля
После Build 041 в проекте по-прежнему существуют обращения к:
lastPrice
bidPrice
askPrice
closeTime
eventTime
bids
asks
Их наличие само по себе не является legacy-зависимостью.
Они допустимы внутри канонического Dzengi acquisition adapter:
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 считается завершённым, поскольку выполнены все критерии:
- проведён аудит legacy quote parsing;
- подтверждён единственный канонический WebSocket parsing pipeline;
- удалён
_extract_depth_prices; - удалён
_extract_first_price; - удалён
_extract_best_price; - удалён
_extract_market_event; - удалены проверяемые прямые обращения к
bidPriceиaskPriceв legacy integration layer; - REST Quotes Feed не изменён;
- REST fallback Build 040 сохранён;
- каноническая модель
Quoteне изменена; - торговая логика не изменена;
- runtime semantics не изменены;
- целевые integration-тесты проходят;
- целевые acquisition-тесты проходят;
- полный набор из 614 тестов проходит;
- финальный grep не обнаруживает удалённые legacy helpers.
13. Commit
Build зафиксирован коммитом:
24a4da9 build 041: remove legacy websocket quote parsing
Предыдущий Build:
e68ed7f build 040: restore quote REST fallback after build 039
14. Итог
Build 041 завершает удаление подтверждённых остатков legacy WebSocket quote parsing из integration layer.
Итоговая архитектура:
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 завершён.