704 lines
14 KiB
Markdown
704 lines
14 KiB
Markdown
# Build 059.4 — WebSocket OHLC Value Validation
|
||
|
||
**Проект:** Dzentra
|
||
**Подсистема:** Market Data Acquisition
|
||
**Этап:** 059.4
|
||
**Статус:** Completed
|
||
|
||
---
|
||
|
||
# Цель
|
||
|
||
Добавить предметную проверку значений транспортной модели:
|
||
|
||
```text
|
||
DzengiWebSocketOhlcEvent
|
||
```
|
||
|
||
После выполнения Build 059.3 проект умеет преобразовывать структурно проверенный WebSocket-документ в transport-модель адаптера.
|
||
|
||
Build 059.4 проверяет, что значения этой модели допустимы для дальнейшей обработки.
|
||
|
||
На данном этапе не выполняются:
|
||
|
||
- преобразование timestamp в `datetime`;
|
||
- преобразование цен в `Decimal` для внутренней модели;
|
||
- создание внутреннего события закрытия свечи;
|
||
- mapping;
|
||
- REST reconciliation;
|
||
- создание канонической `Candle`;
|
||
- runtime-интеграция.
|
||
|
||
---
|
||
|
||
# Причина изменения
|
||
|
||
Parser проверяет только базовые транспортные типы.
|
||
|
||
Например, он допускает следующие значения:
|
||
|
||
```text
|
||
symbol = ""
|
||
interval = "unsupported"
|
||
candle_type = "unknown"
|
||
open_time = -1
|
||
open_price = "NaN"
|
||
high_price = -10
|
||
low_price = 100
|
||
close_price = 0
|
||
```
|
||
|
||
Они корректны с точки зрения базовых Python-типов, но недопустимы с точки зрения предметного контракта OHLC.
|
||
|
||
Поэтому необходим отдельный слой Value Validation.
|
||
|
||
---
|
||
|
||
# Реализованные изменения
|
||
|
||
Изменён файл:
|
||
|
||
```text
|
||
src/market_data/acquisition/validation/values.py
|
||
```
|
||
|
||
Добавлена функция:
|
||
|
||
```text
|
||
validate_dzengi_websocket_ohlc_values()
|
||
```
|
||
|
||
Она принимает:
|
||
|
||
```text
|
||
DzengiWebSocketOhlcEvent
|
||
```
|
||
|
||
и проверяет предметную допустимость всех его полей.
|
||
|
||
---
|
||
|
||
# Новое исключение
|
||
|
||
В файл:
|
||
|
||
```text
|
||
src/market_data/acquisition/exceptions.py
|
||
```
|
||
|
||
добавлено специализированное исключение:
|
||
|
||
```text
|
||
CandleWebSocketValueError
|
||
```
|
||
|
||
Оно используется только для ошибок значений WebSocket OHLC-событий.
|
||
|
||
Ошибки WebSocket OHLC теперь разделены по слоям:
|
||
|
||
```text
|
||
CandleWebSocketSchemaError
|
||
CandleWebSocketParseError
|
||
CandleWebSocketValueError
|
||
```
|
||
|
||
Это сохраняет явное разделение ответственности:
|
||
|
||
```text
|
||
Schema Validation
|
||
↓
|
||
проверка структуры JSON
|
||
|
||
Parser
|
||
↓
|
||
проверка транспортных типов
|
||
|
||
Value Validation
|
||
↓
|
||
проверка предметной допустимости
|
||
```
|
||
|
||
---
|
||
|
||
# Поддерживаемые интервалы
|
||
|
||
В Value Validation зафиксирован набор интервалов, который уже поддерживается действующим Candles Feed:
|
||
|
||
```text
|
||
1m
|
||
5m
|
||
15m
|
||
1h
|
||
```
|
||
|
||
Допустимые значения хранятся в immutable-наборе:
|
||
|
||
```text
|
||
_SUPPORTED_WEBSOCKET_OHLC_INTERVALS
|
||
```
|
||
|
||
Значения сравниваются без автоматической нормализации.
|
||
|
||
Поэтому отклоняются:
|
||
|
||
```text
|
||
""
|
||
"1s"
|
||
"4h"
|
||
"1M"
|
||
" 1m "
|
||
```
|
||
|
||
Это преднамеренное поведение.
|
||
|
||
Parser и validator не должны незаметно исправлять внешний контракт.
|
||
|
||
---
|
||
|
||
# Поддерживаемые типы свечей
|
||
|
||
По результатам runtime-исследования Build 058 подтверждены два допустимых значения:
|
||
|
||
```text
|
||
classic
|
||
heikin-ashi
|
||
```
|
||
|
||
Они зафиксированы в immutable-наборе:
|
||
|
||
```text
|
||
_SUPPORTED_WEBSOCKET_OHLC_TYPES
|
||
```
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
""
|
||
"bid"
|
||
"ask"
|
||
"Classic"
|
||
"unknown"
|
||
" classic "
|
||
```
|
||
|
||
REST-параметр:
|
||
|
||
```text
|
||
priceType = bid | ask
|
||
```
|
||
|
||
не является WebSocket-параметром типа свечи.
|
||
|
||
WebSocket использует:
|
||
|
||
```text
|
||
type = classic | heikin-ashi
|
||
```
|
||
|
||
---
|
||
|
||
# Проверка символа
|
||
|
||
Поле:
|
||
|
||
```text
|
||
symbol
|
||
```
|
||
|
||
должно содержать хотя бы один непробельный символ.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
""
|
||
" "
|
||
"\t"
|
||
"\n"
|
||
```
|
||
|
||
Value Validation не выполняет:
|
||
|
||
- нормализацию символа;
|
||
- проверку существования инструмента;
|
||
- преобразование alias;
|
||
- обращение к Instrument Registry.
|
||
|
||
На этом слое проверяется только непустое значение.
|
||
|
||
---
|
||
|
||
# Проверка timestamp
|
||
|
||
Поле:
|
||
|
||
```text
|
||
open_time
|
||
```
|
||
|
||
должно быть больше нуля.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
0
|
||
-1
|
||
-1784224740000
|
||
```
|
||
|
||
Проверка базового типа `int` уже была выполнена parser-слоем в Build 059.3.
|
||
|
||
Value Validation не преобразует timestamp в `datetime`.
|
||
|
||
---
|
||
|
||
# Проверка OHLC
|
||
|
||
Поля:
|
||
|
||
```text
|
||
open_price
|
||
high_price
|
||
low_price
|
||
close_price
|
||
```
|
||
|
||
принимают транспортный тип:
|
||
|
||
```text
|
||
DzengiRawNumeric
|
||
```
|
||
|
||
То есть могут быть представлены как:
|
||
|
||
```text
|
||
str
|
||
int
|
||
float
|
||
```
|
||
|
||
На этапе Value Validation каждое значение временно преобразуется в `Decimal` только для проверки.
|
||
|
||
Transport-модель при этом не изменяется.
|
||
|
||
---
|
||
|
||
# Проверка корректности числовых значений
|
||
|
||
Каждое поле OHLC должно быть корректным числом.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
""
|
||
"invalid"
|
||
"--1"
|
||
```
|
||
|
||
Для таких значений выбрасывается:
|
||
|
||
```text
|
||
CandleWebSocketValueError
|
||
```
|
||
|
||
с указанием точного JSON-пути.
|
||
|
||
Пример:
|
||
|
||
```text
|
||
$.payload.o должно быть корректным числом
|
||
```
|
||
|
||
---
|
||
|
||
# Проверка конечности
|
||
|
||
Каждая цена должна быть конечным числом.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
"NaN"
|
||
"Infinity"
|
||
"-Infinity"
|
||
float("nan")
|
||
float("inf")
|
||
float("-inf")
|
||
```
|
||
|
||
Это предотвращает проникновение специальных значений в mapping и downstream-анализ.
|
||
|
||
---
|
||
|
||
# Проверка положительности
|
||
|
||
Все цены OHLC должны быть строго больше нуля.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
0
|
||
-1
|
||
"-0.01"
|
||
```
|
||
|
||
Проверка выполняется отдельно для:
|
||
|
||
```text
|
||
$.payload.o
|
||
$.payload.h
|
||
$.payload.l
|
||
$.payload.c
|
||
```
|
||
|
||
---
|
||
|
||
# OHLC-инварианты
|
||
|
||
После преобразования цен во временные `Decimal` проверяются стандартные инварианты свечи.
|
||
|
||
## High не меньше Low
|
||
|
||
Должно выполняться:
|
||
|
||
```text
|
||
high >= low
|
||
```
|
||
|
||
Иначе:
|
||
|
||
```text
|
||
$.payload.h не должно быть меньше $.payload.l
|
||
```
|
||
|
||
---
|
||
|
||
## High не меньше Open
|
||
|
||
Должно выполняться:
|
||
|
||
```text
|
||
high >= open
|
||
```
|
||
|
||
Иначе:
|
||
|
||
```text
|
||
$.payload.h не должно быть меньше $.payload.o
|
||
```
|
||
|
||
---
|
||
|
||
## High не меньше Close
|
||
|
||
Должно выполняться:
|
||
|
||
```text
|
||
high >= close
|
||
```
|
||
|
||
Иначе:
|
||
|
||
```text
|
||
$.payload.h не должно быть меньше $.payload.c
|
||
```
|
||
|
||
---
|
||
|
||
## Low не выше Open
|
||
|
||
Должно выполняться:
|
||
|
||
```text
|
||
low <= open
|
||
```
|
||
|
||
Иначе:
|
||
|
||
```text
|
||
$.payload.l не должно превышать $.payload.o
|
||
```
|
||
|
||
---
|
||
|
||
## Low не выше Close
|
||
|
||
Должно выполняться:
|
||
|
||
```text
|
||
low <= close
|
||
```
|
||
|
||
Иначе:
|
||
|
||
```text
|
||
$.payload.l не должно превышать $.payload.c
|
||
```
|
||
|
||
---
|
||
|
||
# Поддержка плоской свечи
|
||
|
||
Допускается свеча, у которой:
|
||
|
||
```text
|
||
open = high = low = close
|
||
```
|
||
|
||
Например:
|
||
|
||
```text
|
||
64000
|
||
64000
|
||
64000
|
||
64000
|
||
```
|
||
|
||
Такая свеча удовлетворяет всем OHLC-инвариантам и является допустимой.
|
||
|
||
---
|
||
|
||
# Отсутствие изменения transport-модели
|
||
|
||
Value Validation использует `Decimal` только как временный тип проверки.
|
||
|
||
Исходный объект:
|
||
|
||
```text
|
||
DzengiWebSocketOhlcEvent
|
||
```
|
||
|
||
не изменяется.
|
||
|
||
Сохраняются исходные значения и их транспортные типы:
|
||
|
||
```text
|
||
str
|
||
int
|
||
float
|
||
```
|
||
|
||
Постоянное преобразование во внутренние типы будет выполнено позже на mapping-этапе.
|
||
|
||
---
|
||
|
||
# Новый конвейер
|
||
|
||
После Build 059.4 подготовленная часть конвейера выглядит так:
|
||
|
||
```text
|
||
Raw WebSocket message
|
||
↓
|
||
validate_dzengi_websocket_ohlc_schema()
|
||
↓
|
||
ValidatedWebSocketOhlcDocument
|
||
↓
|
||
parse_dzengi_websocket_ohlc()
|
||
↓
|
||
DzengiWebSocketOhlcEvent
|
||
↓
|
||
validate_dzengi_websocket_ohlc_values()
|
||
↓
|
||
проверенная transport-модель
|
||
↓
|
||
Internal Close Event Model
|
||
↓
|
||
Mapper
|
||
↓
|
||
REST reconciliation
|
||
↓
|
||
Canonical Candle
|
||
```
|
||
|
||
---
|
||
|
||
# Unit Tests
|
||
|
||
Создан файл:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py
|
||
```
|
||
|
||
Проверяются:
|
||
|
||
- все поддерживаемые интервалы;
|
||
- оба поддерживаемых типа свечей;
|
||
- строковые, целые и вещественные представления цен;
|
||
- пустые символы;
|
||
- неподдерживаемые интервалы;
|
||
- неподдерживаемые типы свечей;
|
||
- неположительный timestamp;
|
||
- нулевые цены;
|
||
- отрицательные цены;
|
||
- `NaN`;
|
||
- положительная и отрицательная бесконечность;
|
||
- некорректные числовые строки;
|
||
- `high < low`;
|
||
- `high < open`;
|
||
- `high < close`;
|
||
- `low > open`;
|
||
- `low > close`;
|
||
- плоская свеча.
|
||
|
||
Всего выполнено:
|
||
|
||
```text
|
||
84 tests
|
||
```
|
||
|
||
---
|
||
|
||
# Проверка синтаксиса
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m compileall \
|
||
src/market_data/acquisition/exceptions.py \
|
||
src/market_data/acquisition/validation/values.py \
|
||
tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
успешно
|
||
```
|
||
|
||
---
|
||
|
||
# Целевые тесты
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m pytest -q \
|
||
tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
84 passed in 0.03s
|
||
```
|
||
|
||
---
|
||
|
||
# Регрессия value-validation слоя
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m pytest -q \
|
||
tests/unit/market_data/acquisition/validation/test_values.py \
|
||
tests/unit/market_data/acquisition/validation/test_quote_values.py \
|
||
tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py \
|
||
tests/unit/market_data/acquisition/validation/test_candle_values.py \
|
||
tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
150 passed in 0.05s
|
||
```
|
||
|
||
---
|
||
|
||
# Проверка форматирования
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
git diff --check
|
||
```
|
||
|
||
Ошибок форматирования не обнаружено.
|
||
|
||
---
|
||
|
||
# Изменённые файлы
|
||
|
||
```text
|
||
src/market_data/acquisition/exceptions.py
|
||
src/market_data/acquisition/validation/values.py
|
||
```
|
||
|
||
Создан файл:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py
|
||
```
|
||
|
||
Документация этапа:
|
||
|
||
```text
|
||
docs/migrations/build_059_4.md
|
||
```
|
||
|
||
---
|
||
|
||
# Совместимость
|
||
|
||
Изменение полностью обратно совместимо.
|
||
|
||
Не изменены:
|
||
|
||
- REST Candles Feed;
|
||
- REST Candle validation;
|
||
- Quote Feed;
|
||
- WebSocket Quote validation;
|
||
- parser WebSocket OHLC;
|
||
- ExchangeService;
|
||
- runtime;
|
||
- Market Analysis;
|
||
- Trading.
|
||
|
||
Новая функция пока не подключена к рабочему WebSocket Adapter и не влияет на работающий бот.
|
||
|
||
---
|
||
|
||
# Ограничения этапа
|
||
|
||
Build 059.4 не выполняет:
|
||
|
||
- преобразование timestamp в UTC `datetime`;
|
||
- преобразование цен в постоянные `Decimal`;
|
||
- создание внутреннего события закрытия свечи;
|
||
- добавление `source`;
|
||
- mapping;
|
||
- WebSocket Adapter;
|
||
- WebSocket subscription;
|
||
- REST reconciliation;
|
||
- проверку совпадения WebSocket и REST OHLC;
|
||
- создание канонической `Candle`;
|
||
- runtime-интеграцию.
|
||
|
||
---
|
||
|
||
# Итог
|
||
|
||
Build 059.4 завершает слой предметной проверки значений Dzengi WebSocket OHLC.
|
||
|
||
Проект теперь умеет безопасно проверять:
|
||
|
||
```text
|
||
DzengiWebSocketOhlcEvent
|
||
```
|
||
|
||
на:
|
||
|
||
- допустимость символа;
|
||
- допустимость интервала;
|
||
- допустимость типа свечи;
|
||
- корректность timestamp;
|
||
- корректность чисел;
|
||
- конечность цен;
|
||
- положительность цен;
|
||
- соблюдение OHLC-инвариантов.
|
||
|
||
Следующий этап:
|
||
|
||
```text
|
||
Build 059.5 — Internal Close Event Model
|
||
``` |