build 059.5: add internal candle close event model
This commit is contained in:
29
app/src/market_data/acquisition/models/candle_close.py
Normal file
29
app/src/market_data/acquisition/models/candle_close.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# app/src/market_data/acquisition/models/candle_close.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
|
# Внутреннее неизменяемое уведомление о закрытии OHLC-свечи.
|
||||||
|
#
|
||||||
|
# Модель не является канонической Candle, поскольку WebSocket-событие
|
||||||
|
# не содержит volume. Она используется как сигнал для последующего
|
||||||
|
# REST reconciliation и получения полной OHLCV-свечи.
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class CandleCloseEvent:
|
||||||
|
symbol: str
|
||||||
|
interval: str
|
||||||
|
candle_type: str
|
||||||
|
|
||||||
|
open_time: datetime
|
||||||
|
received_at: datetime
|
||||||
|
|
||||||
|
open_price: Decimal
|
||||||
|
high_price: Decimal
|
||||||
|
low_price: Decimal
|
||||||
|
close_price: Decimal
|
||||||
|
|
||||||
|
source: str
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# app/tests/unit/market_data/acquisition/models/test_candle_close.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import FrozenInstanceError
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.market_data.acquisition.models.candle import Candle
|
||||||
|
from src.market_data.acquisition.models.candle_close import (
|
||||||
|
CandleCloseEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _event() -> CandleCloseEvent:
|
||||||
|
return CandleCloseEvent(
|
||||||
|
symbol="BTC/USD_LEVERAGE",
|
||||||
|
interval="1m",
|
||||||
|
candle_type="classic",
|
||||||
|
open_time=datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
39,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
),
|
||||||
|
received_at=datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
40,
|
||||||
|
0,
|
||||||
|
125000,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
),
|
||||||
|
open_price=Decimal("63992.00"),
|
||||||
|
high_price=Decimal("64032.55"),
|
||||||
|
low_price=Decimal("63984.00"),
|
||||||
|
close_price=Decimal("64032.55"),
|
||||||
|
source="dzengi_websocket_ohlc",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_preserves_all_fields() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
assert event.symbol == "BTC/USD_LEVERAGE"
|
||||||
|
assert event.interval == "1m"
|
||||||
|
assert event.candle_type == "classic"
|
||||||
|
|
||||||
|
assert event.open_time == datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
39,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
)
|
||||||
|
assert event.received_at == datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
40,
|
||||||
|
0,
|
||||||
|
125000,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.open_price == Decimal("63992.00")
|
||||||
|
assert event.high_price == Decimal("64032.55")
|
||||||
|
assert event.low_price == Decimal("63984.00")
|
||||||
|
assert event.close_price == Decimal("64032.55")
|
||||||
|
|
||||||
|
assert event.source == "dzengi_websocket_ohlc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_is_immutable() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
with pytest.raises(FrozenInstanceError):
|
||||||
|
event.close_price = Decimal("1") # type: ignore[misc]
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_uses_slots() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
assert not hasattr(event, "__dict__")
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_is_not_canonical_candle() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
assert not isinstance(event, Candle)
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_has_no_volume_field() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
assert not hasattr(event, "volume")
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_supports_heikin_ashi_type() -> None:
|
||||||
|
event = CandleCloseEvent(
|
||||||
|
symbol="BTC/USD_LEVERAGE",
|
||||||
|
interval="1m",
|
||||||
|
candle_type="heikin-ashi",
|
||||||
|
open_time=datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
39,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
),
|
||||||
|
received_at=datetime(
|
||||||
|
2026,
|
||||||
|
7,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
40,
|
||||||
|
tzinfo=timezone.utc,
|
||||||
|
),
|
||||||
|
open_price=Decimal("64068.18"),
|
||||||
|
high_price=Decimal("64128.80"),
|
||||||
|
low_price=Decimal("64068.18"),
|
||||||
|
close_price=Decimal("64100.85"),
|
||||||
|
source="dzengi_websocket_ohlc",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert event.candle_type == "heikin-ashi"
|
||||||
|
|
||||||
|
|
||||||
|
def test_candle_close_event_keeps_open_and_receive_times_separate() -> None:
|
||||||
|
event = _event()
|
||||||
|
|
||||||
|
assert event.received_at > event.open_time
|
||||||
|
assert event.received_at != event.open_time
|
||||||
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