feat: add market data architecture and complete migration through build 039

This commit is contained in:
2026-07-14 09:58:16 +03:00
parent 26deb861bc
commit a996f2f797
443 changed files with 80452 additions and 1335 deletions

View File

@@ -0,0 +1,421 @@
# Build 002 — Raw Models Dzengi exchangeInfo
**Проект:** Dzentra
**Подсистема:** Market Data Acquisition
**Миграция:** Instrument Reference Data
**Статус:** ✅ Завершён
**Дата:** 2026-07-10
---
# Цель Build
Создать транспортные модели (Raw Models), полностью описывающие ответ REST API Dzengi `exchangeInfo`.
На данном этапе запрещалось:
- изменять ExchangeService;
- изменять ExchangeSymbol;
- изменять SymbolValidationResult;
- изменять runtime;
- изменять Telegram UI;
- выполнять parser JSON;
- выполнять mapper в предметную модель Instrument.
Build создаёт исключительно транспортный контракт между REST API и будущим parser.
---
# Причина выполнения Build
После Build 001 уже существует независимая предметная модель:
```text
Instrument
```
Следующим архитектурным слоем является транспортная модель адаптера.
До начала Build 002 существующая реализация выглядела следующим образом:
```text
REST
dict
ExchangeService
ExchangeSymbol
```
Parser и mapper были объединены внутри `ExchangeService`.
Это нарушало принцип разделения ответственности.
После Build 002 появилась отдельная транспортная модель:
```text
REST
Raw Models
```
которая станет входом для parser в следующем Build.
---
# Проанализированные материалы
Перед реализацией были полностью проанализированы:
```text
app/tools/dzengi_probe/runtime_samples/rest/exchangeInfo/all.json
docs/market_intelligence/information/dzengi_openapi.json
```
Также были повторно использованы результаты анализа:
```text
app/src/integrations/exchange/service.py
app/src/integrations/exchange/models.py
```
и результаты grep по использованию ExchangeSymbol.
---
# Основные выводы анализа
Подтверждено:
реальный ответ API значительно богаче текущей модели ExchangeSymbol.
В ответе присутствуют:
- symbol
- name
- status
- assetType
- baseAsset
- baseAssetPrecision
- quoteAsset
- quoteAssetId
- quotePrecision
- orderTypes
- marketModes
- marketType
- country
- sector
- industry
- tradingHours
- tickSize
- tickValue
- tradingFee
- exchangeFee
- longRate
- shortRate
- swapChargeInterval
- minSLGap
- maxSLGap
- minTPGap
- maxTPGap
- filters
- rateLimits
- exchangeFilters
Часть этих данных ранее полностью терялась.
---
# Принятые архитектурные решения
## 1. Raw Models полностью отделены от предметной модели
Созданы транспортные модели.
Они:
- ничего не вычисляют;
- ничего не нормализуют;
- ничего не валидируют.
Они только описывают транспортный контракт.
---
## 2. Поддержка двух форматов ответа API
Поддерживаются оба варианта:
### Wrapped
```text
status
correlationId
payload
```
и
### Unwrapped
```text
timezone
serverTime
symbols
```
Parser следующего Build сможет привести оба формата к единому контракту.
Классификация:
**Улучшение надёжности.**
---
## 3. Decimal сознательно не используется
Raw Models сохраняют транспортный тип.
Например:
```json
"stepSize": "0.001"
```
остаётся строкой.
Преобразование в Decimal является обязанностью mapper.
---
## 4. Filters представлены отдельной иерархией
Созданы:
```text
DzengiLotSizeFilter
DzengiMinNotionalFilter
DzengiUnknownFilter
```
Неизвестные фильтры не приводят к ошибке.
Они сохраняются для дальнейшего анализа.
Классификация:
**Улучшение надёжности.**
---
## 5. Все коллекции являются immutable
Используются:
```python
tuple
```
вместо
```python
list
```
Причина:
Raw Models являются снимком транспортного ответа.
Их нельзя изменять после создания.
---
## 6. Все Raw Models являются immutable
Все dataclass объявлены как:
```python
@dataclass(frozen=True, slots=True)
```
Это гарантирует неизменяемость транспортного контракта.
---
# Созданные файлы
Создан:
```text
app/src/market_data/acquisition/adapters/dzengi/models.py
```
Создан:
```text
app/tests/unit/market_data/acquisition/adapters/dzengi/test_models.py
```
Другие production-файлы не изменялись.
---
# Проверки
Выполнены все проверки Build.
---
## Unit Test
Статус:
✅ Passed
```
6 passed
```
---
## Импорт полной транспортной модели
Проверено:
- импортируются все классы;
- создаётся полная иерархия объектов;
- вложенные dataclass работают корректно.
Статус:
✅ Passed
---
## Компиляция
Проверено:
```text
python -m py_compile
```
Статус:
✅ Passed
---
## Проверка отсутствия использования в production
Подтверждено:
Raw Models используются только:
```text
src/market_data/acquisition/adapters/dzengi/models.py
tests/unit/market_data/acquisition/adapters/dzengi/test_models.py
```
ExchangeService их не использует.
Parser их ещё не использует.
Mapper их ещё не использует.
Статус:
✅ Passed
---
## Полный набор тестов
Выполнено:
```text
python -m pytest -q
```
Результат:
```text
11 passed
```
Статус:
✅ Passed
---
# Архитектурный результат
После Build 002 структура подсистемы стала выглядеть следующим образом:
```text
Dzengi REST API
Raw Models
```
Следующие слои пока отсутствуют:
```text
Parser
Mapper
Instrument
Handler
Feed
Service
```
Именно это соответствует утверждённому плану миграции.
---
# Обратная совместимость
Полностью сохранена.
Не изменены:
- ExchangeService
- ExchangeSymbol
- SymbolValidationResult
- validate_symbol()
- normalize_symbol()
- symbol_candidates()
- Runtime
- Telegram UI
Новые Raw Models пока не подключены к работающему боту.
---
# Итог Build
Build 002 завершён успешно.
Получен полноценный транспортный контракт REST API Dzengi.
Создан фундамент для следующих этапов миграции.
Работающий бот не изменил своего поведения.
---
# Следующий Build
## Build 003 — Структурная валидация exchangeInfo
Следующий этап реализует parser, который будет преобразовывать сырой JSON REST API в созданные транспортные модели.
На Build 003 по-прежнему:
- ExchangeService изменяться не будет;
- предметная модель Instrument использоваться не будет;
- parser останется полностью независимым от бизнес-логики.