Files
dzentra_bot/docs/migrations/build_041.md

15 KiB
Raw Permalink Blame History

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, если одновременно подтверждены следующие условия:

  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-котировок уже сосредоточен в:

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 завершён.