build 059.5: add internal candle close event model
This commit is contained in:
555
docs/migrations/build_059_5.md
Normal file
555
docs/migrations/build_059_5.md
Normal file
@@ -0,0 +1,555 @@
|
||||
# Build 059.5 — Internal Candle Close Event Model
|
||||
|
||||
**Проект:** Dzentra
|
||||
**Подсистема:** Market Data Acquisition
|
||||
**Этап:** 059.5
|
||||
**Статус:** Completed
|
||||
|
||||
---
|
||||
|
||||
# Цель
|
||||
|
||||
Добавить внутреннюю immutable-модель уведомления о закрытии OHLC-свечи.
|
||||
|
||||
Модель должна представлять уже проверенное WebSocket-событие внутри Dzentra, но не должна заменять каноническую модель:
|
||||
|
||||
```text
|
||||
Candle
|
||||
```
|
||||
|
||||
Причина разделения заключается в том, что фактическое событие Dzengi WebSocket:
|
||||
|
||||
```text
|
||||
ohlc.event
|
||||
```
|
||||
|
||||
содержит:
|
||||
|
||||
```text
|
||||
symbol
|
||||
interval
|
||||
type
|
||||
t
|
||||
o
|
||||
h
|
||||
l
|
||||
c
|
||||
```
|
||||
|
||||
но не содержит:
|
||||
|
||||
```text
|
||||
volume
|
||||
```
|
||||
|
||||
Поэтому WebSocket OHLC-событие не является полноценной OHLCV-свечой.
|
||||
|
||||
---
|
||||
|
||||
# Причина изменения
|
||||
|
||||
После Build 059.4 проект умеет:
|
||||
|
||||
1. структурно проверять входящее WebSocket-сообщение;
|
||||
2. преобразовывать его в transport-модель;
|
||||
3. проверять предметную допустимость значений.
|
||||
|
||||
Подготовленная цепочка выглядит так:
|
||||
|
||||
```text
|
||||
Raw WebSocket message
|
||||
↓
|
||||
ValidatedWebSocketOhlcDocument
|
||||
↓
|
||||
DzengiWebSocketOhlcEvent
|
||||
↓
|
||||
Value Validation
|
||||
```
|
||||
|
||||
Однако transport-модель:
|
||||
|
||||
```text
|
||||
DzengiWebSocketOhlcEvent
|
||||
```
|
||||
|
||||
принадлежит адаптеру конкретного внешнего источника Dzengi.
|
||||
|
||||
Перед дальнейшим использованием в runtime требуется источник-независимая внутренняя модель Dzentra.
|
||||
|
||||
---
|
||||
|
||||
# Реализовано
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/models/candle_close.py
|
||||
```
|
||||
|
||||
Добавлена модель:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Структура модели
|
||||
|
||||
Модель содержит следующие поля:
|
||||
|
||||
```text
|
||||
symbol
|
||||
interval
|
||||
candle_type
|
||||
|
||||
open_time
|
||||
received_at
|
||||
|
||||
open_price
|
||||
high_price
|
||||
low_price
|
||||
close_price
|
||||
|
||||
source
|
||||
```
|
||||
|
||||
Полное назначение модели:
|
||||
|
||||
```text
|
||||
внутреннее уведомление Dzentra
|
||||
о завершении OHLC-свечи,
|
||||
требующее последующего REST reconciliation
|
||||
для получения полной OHLCV-свечи
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Контракт модели
|
||||
|
||||
Модель объявлена как:
|
||||
|
||||
```text
|
||||
frozen=True
|
||||
slots=True
|
||||
```
|
||||
|
||||
Это обеспечивает:
|
||||
|
||||
- неизменяемость;
|
||||
- отсутствие динамического `__dict__`;
|
||||
- уменьшение вероятности случайного изменения события;
|
||||
- компактное представление в памяти;
|
||||
- предсказуемый runtime-контракт.
|
||||
|
||||
---
|
||||
|
||||
# Отличие от transport-модели
|
||||
|
||||
Transport-модель:
|
||||
|
||||
```text
|
||||
DzengiWebSocketOhlcEvent
|
||||
```
|
||||
|
||||
отражает внешний контракт Dzengi.
|
||||
|
||||
Она содержит:
|
||||
|
||||
```text
|
||||
open_time: int
|
||||
OHLC: str | int | float
|
||||
```
|
||||
|
||||
Внутренняя модель:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
содержит:
|
||||
|
||||
```text
|
||||
open_time: datetime
|
||||
received_at: datetime
|
||||
OHLC: Decimal
|
||||
source: str
|
||||
```
|
||||
|
||||
Таким образом transport-особенности внешнего API не распространяются на внутренние слои Dzentra.
|
||||
|
||||
---
|
||||
|
||||
# Отличие от канонической Candle
|
||||
|
||||
Каноническая модель:
|
||||
|
||||
```text
|
||||
Candle
|
||||
```
|
||||
|
||||
представляет полноценную OHLCV-свечу.
|
||||
|
||||
Она содержит:
|
||||
|
||||
```text
|
||||
volume: Decimal
|
||||
```
|
||||
|
||||
Модель:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
не содержит `volume`.
|
||||
|
||||
Это преднамеренное архитектурное решение.
|
||||
|
||||
WebSocket-событие не должно:
|
||||
|
||||
- подставлять фиктивный нулевой объём;
|
||||
- использовать nullable volume;
|
||||
- притворяться полной OHLCV-свечой;
|
||||
- наследоваться от `Candle`;
|
||||
- передаваться потребителям, ожидающим каноническую свечу.
|
||||
|
||||
Полная `Candle` будет получена только после REST reconciliation.
|
||||
|
||||
---
|
||||
|
||||
# Поля времени
|
||||
|
||||
## open_time
|
||||
|
||||
Поле:
|
||||
|
||||
```text
|
||||
open_time
|
||||
```
|
||||
|
||||
представляет время открытия завершённой свечи.
|
||||
|
||||
Для минутной свечи это начало минутного интервала, к которому относится событие.
|
||||
|
||||
Тип:
|
||||
|
||||
```text
|
||||
datetime
|
||||
```
|
||||
|
||||
Ожидаемая timezone:
|
||||
|
||||
```text
|
||||
UTC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## received_at
|
||||
|
||||
Поле:
|
||||
|
||||
```text
|
||||
received_at
|
||||
```
|
||||
|
||||
представляет момент получения события системой Dzentra.
|
||||
|
||||
Оно не равно `open_time`.
|
||||
|
||||
Разделение двух времён необходимо для:
|
||||
|
||||
- измерения задержки WebSocket;
|
||||
- диагностики запоздавших сообщений;
|
||||
- определения порядка фактической обработки;
|
||||
- runtime-метрик;
|
||||
- журналирования;
|
||||
- анализа качества источника данных.
|
||||
|
||||
---
|
||||
|
||||
# Поля OHLC
|
||||
|
||||
Внутренняя модель использует:
|
||||
|
||||
```text
|
||||
Decimal
|
||||
```
|
||||
|
||||
для:
|
||||
|
||||
```text
|
||||
open_price
|
||||
high_price
|
||||
low_price
|
||||
close_price
|
||||
```
|
||||
|
||||
Это исключает дальнейшее распространение внешних типов:
|
||||
|
||||
```text
|
||||
str
|
||||
int
|
||||
float
|
||||
```
|
||||
|
||||
Преобразование transport-значений в `Decimal` будет реализовано в следующем этапе:
|
||||
|
||||
```text
|
||||
Build 059.6 — Mapper
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Поле candle_type
|
||||
|
||||
Поле:
|
||||
|
||||
```text
|
||||
candle_type
|
||||
```
|
||||
|
||||
сохраняет тип серии свечей.
|
||||
|
||||
Подтверждённые runtime-значения:
|
||||
|
||||
```text
|
||||
classic
|
||||
heikin-ashi
|
||||
```
|
||||
|
||||
Для production-интеграции Candles Feed основной интерес представляет:
|
||||
|
||||
```text
|
||||
classic
|
||||
```
|
||||
|
||||
Поскольку именно классические свечи будут использоваться для REST reconciliation с каноническими OHLCV-свечами.
|
||||
|
||||
`heikin-ashi` остаётся поддерживаемым внутренним типом события, но не должен автоматически преобразовываться в обычную каноническую `Candle`.
|
||||
|
||||
---
|
||||
|
||||
# Поле source
|
||||
|
||||
Поле:
|
||||
|
||||
```text
|
||||
source
|
||||
```
|
||||
|
||||
фиксирует происхождение внутреннего события.
|
||||
|
||||
Пример:
|
||||
|
||||
```text
|
||||
dzengi_websocket_ohlc
|
||||
```
|
||||
|
||||
Это позволит в дальнейшем:
|
||||
|
||||
- различать источники;
|
||||
- поддерживать несколько адаптеров;
|
||||
- вести диагностику;
|
||||
- применять источник-зависимую политику reconciliation;
|
||||
- не встраивать имя Dzengi в общую доменную модель.
|
||||
|
||||
---
|
||||
|
||||
# Новый внутренний конвейер
|
||||
|
||||
После Build 059.5 архитектура выглядит так:
|
||||
|
||||
```text
|
||||
Raw WebSocket message
|
||||
↓
|
||||
Schema Validation
|
||||
↓
|
||||
ValidatedWebSocketOhlcDocument
|
||||
↓
|
||||
Parser
|
||||
↓
|
||||
DzengiWebSocketOhlcEvent
|
||||
↓
|
||||
Value Validation
|
||||
↓
|
||||
Mapper
|
||||
↓
|
||||
CandleCloseEvent
|
||||
↓
|
||||
REST reconciliation
|
||||
↓
|
||||
Canonical Candle
|
||||
```
|
||||
|
||||
На текущем этапе модель `CandleCloseEvent` уже существует, но mapper ещё не реализован.
|
||||
|
||||
---
|
||||
|
||||
# Unit Tests
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
tests/unit/market_data/acquisition/models/test_candle_close.py
|
||||
```
|
||||
|
||||
Проверяются:
|
||||
|
||||
- сохранение всех полей;
|
||||
- неизменяемость модели;
|
||||
- использование `slots`;
|
||||
- отсутствие `__dict__`;
|
||||
- отсутствие наследования от `Candle`;
|
||||
- отсутствие поля `volume`;
|
||||
- поддержка типа `heikin-ashi`;
|
||||
- раздельное хранение `open_time` и `received_at`.
|
||||
|
||||
Всего выполнено:
|
||||
|
||||
```text
|
||||
7 tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Проверка синтаксиса
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m compileall \
|
||||
src/market_data/acquisition/models/candle_close.py \
|
||||
tests/unit/market_data/acquisition/models/test_candle_close.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
успешно
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Целевые тесты
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q \
|
||||
tests/unit/market_data/acquisition/models/test_candle_close.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
7 passed in 0.01s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Регрессия слоя моделей
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q \
|
||||
tests/unit/market_data/acquisition/models
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
42 passed in 0.02s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Проверка форматирования
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Ошибок форматирования не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
# Созданные файлы
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/models/candle_close.py
|
||||
tests/unit/market_data/acquisition/models/test_candle_close.py
|
||||
docs/migrations/build_059_5.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Совместимость
|
||||
|
||||
Изменение полностью обратно совместимо.
|
||||
|
||||
Не изменены:
|
||||
|
||||
- каноническая модель `Candle`;
|
||||
- transport-модель Dzengi;
|
||||
- REST Candles Feed;
|
||||
- parser;
|
||||
- value validation;
|
||||
- WebSocket Adapter;
|
||||
- ExchangeService;
|
||||
- runtime;
|
||||
- Market Analysis;
|
||||
- Trading.
|
||||
|
||||
Новая модель пока не подключена к runtime и не влияет на работающий бот.
|
||||
|
||||
---
|
||||
|
||||
# Ограничения этапа
|
||||
|
||||
Build 059.5 не выполняет:
|
||||
|
||||
- преобразование transport-модели;
|
||||
- преобразование timestamp;
|
||||
- преобразование OHLC в `Decimal`;
|
||||
- установку реального `received_at`;
|
||||
- mapping;
|
||||
- WebSocket Adapter;
|
||||
- REST reconciliation;
|
||||
- проверку соответствия REST и WebSocket OHLC;
|
||||
- создание канонической `Candle`;
|
||||
- публикацию события в runtime.
|
||||
|
||||
---
|
||||
|
||||
# Итог
|
||||
|
||||
Build 059.5 добавляет источник-независимую внутреннюю модель:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Она представляет подтверждённое уведомление о завершении OHLC-свечи без фиктивного объёма.
|
||||
|
||||
Модель создаёт архитектурную границу между:
|
||||
|
||||
```text
|
||||
внешним Dzengi WebSocket контрактом
|
||||
```
|
||||
|
||||
и:
|
||||
|
||||
```text
|
||||
внутренним runtime Dzentra
|
||||
```
|
||||
|
||||
Следующий этап:
|
||||
|
||||
```text
|
||||
Build 059.6 — Mapper
|
||||
```
|
||||
Reference in New Issue
Block a user