build 039: complete Quotes Feed migration foundation
This commit is contained in:
850
docs/migrations/build_003.md
Normal file
850
docs/migrations/build_003.md
Normal file
@@ -0,0 +1,850 @@
|
||||
# Dzentra — Instrument Reference Data Migration
|
||||
|
||||
## Build 003 — структурная валидация `exchangeInfo`
|
||||
|
||||
**Статус:** Завершён
|
||||
**Подсистема:** Market Data Acquisition
|
||||
**Область:** Instrument Reference Data
|
||||
**Проект:** Dzentra
|
||||
**Язык реализации:** Python 3.12
|
||||
|
||||
---
|
||||
|
||||
## 1. Цель Build 003
|
||||
|
||||
Цель Build 003 — создать отдельный слой структурной валидации сырого JSON-документа `exchangeInfo` до его преобразования в raw-модели Dzengi.
|
||||
|
||||
После завершения Build 003 формируется следующий архитектурный поток:
|
||||
|
||||
```text
|
||||
JSON response
|
||||
↓
|
||||
Schema validation
|
||||
↓
|
||||
Parser
|
||||
↓
|
||||
Dzengi Raw Models
|
||||
```
|
||||
|
||||
В рамках этого Build реализована только проверка формы и структуры входных данных.
|
||||
|
||||
Schema validation не выполняет:
|
||||
|
||||
- преобразование JSON в raw-модели Dzengi;
|
||||
- преобразование camelCase в snake_case;
|
||||
- преобразование строковых чисел в `Decimal`, `float` или `int`;
|
||||
- нормализацию символов;
|
||||
- проверку допустимости числовых значений;
|
||||
- проверку торговой семантики статусов;
|
||||
- создание внутренней модели `Instrument`;
|
||||
- фильтрацию или отбрасывание инструментов.
|
||||
|
||||
---
|
||||
|
||||
## 2. Почему Build 003 выполняется перед parser
|
||||
|
||||
После Build 002 уже существуют типизированные raw-модели ответа Dzengi.
|
||||
|
||||
Следующий этап — parser, однако parser не должен одновременно:
|
||||
|
||||
- определять wrapped или unwrapped формат ответа;
|
||||
- проверять тип корневого объекта;
|
||||
- проверять наличие `symbols`;
|
||||
- проверять тип `symbols`;
|
||||
- проверять структуру элементов `symbols`;
|
||||
- проверять структуру вложенных коллекций;
|
||||
- создавать raw-модели.
|
||||
|
||||
Поэтому перед parser был выделен отдельный слой:
|
||||
|
||||
```text
|
||||
validation/schema.py
|
||||
```
|
||||
|
||||
Разделение ответственности теперь выглядит следующим образом:
|
||||
|
||||
```text
|
||||
schema.py
|
||||
Проверяет форму и структуру данных.
|
||||
|
||||
parser.py
|
||||
Преобразует структурно корректные данные в raw-модели Dzengi.
|
||||
|
||||
values.py
|
||||
Проверяет допустимость предметных значений.
|
||||
|
||||
mapper.py
|
||||
Преобразует raw-модели Dzengi во внутреннюю модель Instrument.
|
||||
```
|
||||
|
||||
Это является обязательным архитектурным изменением для утверждённой структуры Dzentra.
|
||||
|
||||
---
|
||||
|
||||
## 3. Изменённые и созданные файлы
|
||||
|
||||
В рамках Build 003 были изменены или созданы только следующие файлы:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/exceptions.py
|
||||
app/src/market_data/acquisition/validation/schema.py
|
||||
app/tests/unit/market_data/acquisition/validation/test_schema.py
|
||||
```
|
||||
|
||||
Другие файлы проекта не изменялись.
|
||||
|
||||
---
|
||||
|
||||
## 4. Реализованные исключения
|
||||
|
||||
В файле:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/exceptions.py
|
||||
```
|
||||
|
||||
добавлены:
|
||||
|
||||
```python
|
||||
class MarketDataAcquisitionError(Exception):
|
||||
pass
|
||||
```
|
||||
|
||||
и:
|
||||
|
||||
```python
|
||||
class InstrumentReferenceSchemaError(MarketDataAcquisitionError):
|
||||
pass
|
||||
```
|
||||
|
||||
Иерархия ошибок:
|
||||
|
||||
```text
|
||||
Exception
|
||||
↓
|
||||
MarketDataAcquisitionError
|
||||
↓
|
||||
InstrumentReferenceSchemaError
|
||||
```
|
||||
|
||||
`InstrumentReferenceSchemaError` используется для явного обозначения ошибки структуры документа Instrument Reference Data.
|
||||
|
||||
Это позволяет в последующих Build отличать структурную ошибку от:
|
||||
|
||||
- сетевой ошибки;
|
||||
- ошибки HTTP;
|
||||
- ошибки JSON-декодирования;
|
||||
- ошибки значений;
|
||||
- ошибки parser;
|
||||
- ошибки mapper.
|
||||
|
||||
Исключение создано не «на будущее»: оно непосредственно используется schema validation в Build 003.
|
||||
|
||||
---
|
||||
|
||||
## 5. Реализованный контракт schema validation
|
||||
|
||||
В файле:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/validation/schema.py
|
||||
```
|
||||
|
||||
создана модель:
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ValidatedExchangeInfoDocument:
|
||||
payload: Mapping[str, object]
|
||||
is_wrapped: bool
|
||||
status: object | None
|
||||
correlation_id: object | None
|
||||
```
|
||||
|
||||
Она представляет структурно проверенный документ `exchangeInfo`.
|
||||
|
||||
Модель содержит:
|
||||
|
||||
| Поле | Назначение |
|
||||
|---|---|
|
||||
| `payload` | Проверенный payload с данными `exchangeInfo` |
|
||||
| `is_wrapped` | Признак wrapped/unwrapped формата |
|
||||
| `status` | Верхнеуровневый статус wrapped-ответа |
|
||||
| `correlation_id` | Верхнеуровневый `correlationId` wrapped-ответа |
|
||||
|
||||
Основная функция:
|
||||
|
||||
```python
|
||||
validate_exchange_info_schema(document: object) -> ValidatedExchangeInfoDocument
|
||||
```
|
||||
|
||||
принимает произвольный объект и либо:
|
||||
|
||||
- возвращает `ValidatedExchangeInfoDocument`;
|
||||
- либо выбрасывает `InstrumentReferenceSchemaError`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Поддерживаемые форматы ответа
|
||||
|
||||
### 6.1. Unwrapped-формат
|
||||
|
||||
Поддерживается непосредственный payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"timezone": "UTC",
|
||||
"serverTime": 1783537921471,
|
||||
"symbols": []
|
||||
}
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
is_wrapped = False
|
||||
status = None
|
||||
correlation_id = None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6.2. Wrapped-формат
|
||||
|
||||
Поддерживается ответ с вложенным `payload`:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "OK",
|
||||
"correlationId": "2",
|
||||
"payload": {
|
||||
"timezone": "UTC",
|
||||
"serverTime": 1783537921471,
|
||||
"symbols": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
is_wrapped = True
|
||||
status = "OK"
|
||||
correlation_id = "2"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Что проверяет schema validation
|
||||
|
||||
Build 003 проверяет следующие структурные свойства документа.
|
||||
|
||||
### 7.1. Корень документа
|
||||
|
||||
Корень должен быть JSON-объектом.
|
||||
|
||||
Отклоняются:
|
||||
|
||||
```json
|
||||
[]
|
||||
```
|
||||
|
||||
```json
|
||||
null
|
||||
```
|
||||
|
||||
```json
|
||||
"invalid"
|
||||
```
|
||||
|
||||
```json
|
||||
123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2. Wrapped payload
|
||||
|
||||
Если присутствует ключ:
|
||||
|
||||
```text
|
||||
payload
|
||||
```
|
||||
|
||||
его значение должно быть JSON-объектом.
|
||||
|
||||
Например, отклоняется:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "OK",
|
||||
"payload": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3. Наличие `symbols`
|
||||
|
||||
Payload должен содержать:
|
||||
|
||||
```text
|
||||
symbols
|
||||
```
|
||||
|
||||
Отсутствие `symbols` считается ошибкой структуры.
|
||||
|
||||
---
|
||||
|
||||
### 7.4. Тип `symbols`
|
||||
|
||||
`symbols` должен быть JSON-массивом.
|
||||
|
||||
Отклоняется:
|
||||
|
||||
```json
|
||||
{
|
||||
"symbols": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5. Элементы `symbols`
|
||||
|
||||
Каждый элемент `symbols` должен быть JSON-объектом.
|
||||
|
||||
Отклоняется:
|
||||
|
||||
```json
|
||||
{
|
||||
"symbols": [
|
||||
"BTC/USD"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6. `filters`
|
||||
|
||||
Если у инструмента присутствует:
|
||||
|
||||
```text
|
||||
filters
|
||||
```
|
||||
|
||||
то:
|
||||
|
||||
- `filters` должен быть JSON-массивом;
|
||||
- каждый элемент `filters` должен быть JSON-объектом.
|
||||
|
||||
Отклоняется:
|
||||
|
||||
```json
|
||||
{
|
||||
"symbols": [
|
||||
{
|
||||
"symbol": "BTC/USD",
|
||||
"filters": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Также отклоняется:
|
||||
|
||||
```json
|
||||
{
|
||||
"symbols": [
|
||||
{
|
||||
"symbol": "BTC/USD",
|
||||
"filters": [
|
||||
"LOT_SIZE"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7. `marketModes`
|
||||
|
||||
Если присутствует:
|
||||
|
||||
```text
|
||||
marketModes
|
||||
```
|
||||
|
||||
то:
|
||||
|
||||
- значение должно быть JSON-массивом;
|
||||
- каждый элемент должен быть строкой.
|
||||
|
||||
---
|
||||
|
||||
### 7.8. `orderTypes`
|
||||
|
||||
Если присутствует:
|
||||
|
||||
```text
|
||||
orderTypes
|
||||
```
|
||||
|
||||
то:
|
||||
|
||||
- значение должно быть JSON-массивом;
|
||||
- каждый элемент должен быть строкой.
|
||||
|
||||
---
|
||||
|
||||
### 7.9. `rateLimits`
|
||||
|
||||
Если присутствует:
|
||||
|
||||
```text
|
||||
rateLimits
|
||||
```
|
||||
|
||||
то:
|
||||
|
||||
- значение должно быть JSON-массивом;
|
||||
- каждый элемент должен быть JSON-объектом.
|
||||
|
||||
---
|
||||
|
||||
### 7.10. `exchangeFilters`
|
||||
|
||||
Если присутствует:
|
||||
|
||||
```text
|
||||
exchangeFilters
|
||||
```
|
||||
|
||||
то:
|
||||
|
||||
- значение должно быть JSON-массивом;
|
||||
- каждый элемент должен быть JSON-объектом.
|
||||
|
||||
---
|
||||
|
||||
## 8. Что намеренно не проверяется в Build 003
|
||||
|
||||
Build 003 не проверяет допустимость значений.
|
||||
|
||||
Следующие примеры не относятся к schema validation:
|
||||
|
||||
```json
|
||||
{
|
||||
"tickSize": -1
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"baseAssetPrecision": -5
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": ""
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"minQty": "not-a-number"
|
||||
}
|
||||
```
|
||||
|
||||
Такие проверки относятся к:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/validation/values.py
|
||||
```
|
||||
|
||||
и должны реализовываться отдельно, без смешения структурной и предметной валидации.
|
||||
|
||||
---
|
||||
|
||||
## 9. Защита проверенного payload
|
||||
|
||||
Поле:
|
||||
|
||||
```python
|
||||
payload: Mapping[str, object]
|
||||
```
|
||||
|
||||
возвращается через:
|
||||
|
||||
```python
|
||||
MappingProxyType
|
||||
```
|
||||
|
||||
Это предотвращает случайное изменение корневого payload последующим parser.
|
||||
|
||||
Таким образом, schema validation передаёт следующему слою проверенное read-only представление данных.
|
||||
|
||||
---
|
||||
|
||||
## 10. Диагностические пути ошибок
|
||||
|
||||
Schema validation формирует ошибки с указанием пути до проблемного значения.
|
||||
|
||||
Примеры:
|
||||
|
||||
```text
|
||||
$ должен быть JSON-объектом, получен list.
|
||||
```
|
||||
|
||||
```text
|
||||
$.payload.symbols должен быть JSON-массивом, получен dict.
|
||||
```
|
||||
|
||||
```text
|
||||
$.payload.symbols[0] должен быть JSON-объектом, получен str.
|
||||
```
|
||||
|
||||
```text
|
||||
$.payload.symbols[0].filters должен быть JSON-массивом, получен dict.
|
||||
```
|
||||
|
||||
На текущем этапе для `symbols` используется унифицированный диагностический путь:
|
||||
|
||||
```text
|
||||
$.payload.symbols
|
||||
```
|
||||
|
||||
как для wrapped-, так и для unwrapped-формата.
|
||||
|
||||
Это сознательное упрощение текущего контракта и не влияет на production-поведение.
|
||||
|
||||
---
|
||||
|
||||
## 11. Реализованные тесты
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
app/tests/unit/market_data/acquisition/validation/test_schema.py
|
||||
```
|
||||
|
||||
Он проверяет:
|
||||
|
||||
- корректный unwrapped-документ;
|
||||
- корректный wrapped-документ;
|
||||
- отклонение не-объекта в корне;
|
||||
- отклонение некорректного wrapped payload;
|
||||
- отсутствие `symbols`;
|
||||
- некорректный тип `symbols`;
|
||||
- некорректный элемент `symbols`;
|
||||
- некорректный тип `filters`;
|
||||
- некорректный элемент `filters`;
|
||||
- некорректный тип `marketModes`;
|
||||
- некорректный тип `orderTypes`;
|
||||
- нестроковый элемент `marketModes`;
|
||||
- нестроковый элемент `orderTypes`;
|
||||
- некорректный тип `rateLimits`;
|
||||
- некорректный элемент `rateLimits`;
|
||||
- некорректный тип `exchangeFilters`;
|
||||
- некорректный элемент `exchangeFilters`.
|
||||
|
||||
Итог:
|
||||
|
||||
```text
|
||||
20 passed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Выполненные проверки
|
||||
|
||||
### Проверка 1 — unit-тесты Build 003
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
python -m pytest \
|
||||
tests/unit/market_data/acquisition/validation/test_schema.py \
|
||||
-q
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
.................... [100%]
|
||||
20 passed in 0.01s
|
||||
```
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Проверка 2 — компиляция файлов Build 003
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
python -m py_compile \
|
||||
src/market_data/acquisition/exceptions.py \
|
||||
src/market_data/acquisition/validation/schema.py \
|
||||
tests/unit/market_data/acquisition/validation/test_schema.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
Ошибок нет.
|
||||
```
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Проверка 3 — ручная проверка wrapped/unwrapped контрактов
|
||||
|
||||
Получен результат:
|
||||
|
||||
```text
|
||||
Unwrapped:
|
||||
is_wrapped: False
|
||||
status: None
|
||||
correlation_id: None
|
||||
symbols: []
|
||||
|
||||
Wrapped:
|
||||
is_wrapped: True
|
||||
status: OK
|
||||
correlation_id: 2
|
||||
symbols: []
|
||||
```
|
||||
|
||||
Подтверждено:
|
||||
|
||||
- unwrapped-формат определяется корректно;
|
||||
- wrapped-формат определяется корректно;
|
||||
- `status` сохраняется;
|
||||
- `correlationId` сохраняется как `correlation_id`;
|
||||
- `symbols` доступны через проверенный payload.
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Проверка 4 — типизированные ошибки
|
||||
|
||||
Проверены четыре некорректных документа.
|
||||
|
||||
Получен результат:
|
||||
|
||||
```text
|
||||
1: InstrumentReferenceSchemaError: $ должен быть JSON-объектом, получен list.
|
||||
2: InstrumentReferenceSchemaError: $.payload.symbols должен быть JSON-массивом, получен dict.
|
||||
3: InstrumentReferenceSchemaError: $.payload.symbols[0] должен быть JSON-объектом, получен str.
|
||||
4: InstrumentReferenceSchemaError: $.payload.symbols[0].filters должен быть JSON-массивом, получен dict.
|
||||
```
|
||||
|
||||
Ни один ошибочный документ не был принят.
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Проверка 5 — полный набор тестов проекта
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
............................... [100%]
|
||||
31 passed in 0.04s
|
||||
```
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Проверка 6 — отсутствие подключения к production-коду
|
||||
|
||||
Выполнен поиск:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
-E "validate_exchange_info_schema|ValidatedExchangeInfoDocument|InstrumentReferenceSchemaError" \
|
||||
src tests
|
||||
```
|
||||
|
||||
Подтверждено, что новые сущности Build 003 используются только в:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/exceptions.py
|
||||
src/market_data/acquisition/validation/schema.py
|
||||
tests/unit/market_data/acquisition/validation/test_schema.py
|
||||
```
|
||||
|
||||
Они пока не подключены к:
|
||||
|
||||
```text
|
||||
ExchangeService
|
||||
legacy exchangeInfo
|
||||
Telegram UI
|
||||
runtime
|
||||
автоторговле
|
||||
market stream
|
||||
production parser
|
||||
production mapper
|
||||
```
|
||||
|
||||
Статус:
|
||||
|
||||
```text
|
||||
PASSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Обратная совместимость
|
||||
|
||||
Build 003 не изменяет:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
ExchangeService.validate_symbol()
|
||||
ExchangeService.get_symbol_runtime_status()
|
||||
ExchangeService.get_symbol_market_status()
|
||||
```
|
||||
|
||||
Не изменены:
|
||||
|
||||
```text
|
||||
ExchangeSymbol
|
||||
SymbolValidationResult
|
||||
normalize_symbol()
|
||||
symbol_candidates()
|
||||
```
|
||||
|
||||
Не изменено поведение:
|
||||
|
||||
- Telegram UI;
|
||||
- автоторговли;
|
||||
- runtime-проверок символа;
|
||||
- legacy-кэша инструментов;
|
||||
- получения цен;
|
||||
- market stream;
|
||||
- существующего `exchangeInfo`.
|
||||
|
||||
Новый schema validation пока изолирован от production-кода.
|
||||
|
||||
---
|
||||
|
||||
## 14. Точки обратной совместимости
|
||||
|
||||
После Build 003 продолжают действовать прежние точки совместимости:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
→ list[ExchangeSymbol]
|
||||
|
||||
ExchangeService.validate_symbol()
|
||||
→ SymbolValidationResult
|
||||
|
||||
ExchangeService.get_symbol_runtime_status()
|
||||
→ ExchangeRuntimeStatus
|
||||
|
||||
ExchangeService.get_symbol_market_status()
|
||||
→ dict[str, object]
|
||||
```
|
||||
|
||||
Ни одна из этих сигнатур не изменена.
|
||||
|
||||
---
|
||||
|
||||
## 15. Классификация изменений
|
||||
|
||||
| Изменение | Классификация |
|
||||
|---|---|
|
||||
| Создание schema validation | Обязательное архитектурное изменение |
|
||||
| Создание `MarketDataAcquisitionError` | Обязательное архитектурное изменение |
|
||||
| Создание `InstrumentReferenceSchemaError` | Обязательное архитектурное изменение |
|
||||
| Поддержка wrapped/unwrapped форматов | Улучшение надёжности |
|
||||
| Явные структурные ошибки | Улучшение надёжности |
|
||||
| Read-only представление проверенного payload | Улучшение надёжности |
|
||||
| Изменение production-поведения | Отсутствует |
|
||||
| Изменение публичных legacy-интерфейсов | Отсутствует |
|
||||
|
||||
---
|
||||
|
||||
## 16. Условие завершения Build 003
|
||||
|
||||
Build 003 считается завершённым, потому что выполнены все условия:
|
||||
|
||||
- создан отдельный schema validation layer;
|
||||
- поддержан wrapped-формат;
|
||||
- поддержан unwrapped-формат;
|
||||
- проверяется обязательная структура `symbols`;
|
||||
- проверяются вложенные коллекции;
|
||||
- ошибки типизированы;
|
||||
- создан read-only контракт для передачи данных parser;
|
||||
- написаны unit-тесты;
|
||||
- все тесты Build проходят;
|
||||
- полный набор тестов проекта проходит;
|
||||
- production-код не изменён;
|
||||
- обратная совместимость сохранена.
|
||||
|
||||
---
|
||||
|
||||
## 17. Итоговый статус
|
||||
|
||||
```text
|
||||
Build 003 — COMPLETED
|
||||
```
|
||||
|
||||
Итоговый набор тестов проекта:
|
||||
|
||||
```text
|
||||
31 passed in 0.04s
|
||||
```
|
||||
|
||||
Следующий этап:
|
||||
|
||||
```text
|
||||
Build 004 — Parser exchangeInfo
|
||||
```
|
||||
|
||||
На Build 004 новый parser должен:
|
||||
|
||||
1. принимать структурно проверенный `ValidatedExchangeInfoDocument`;
|
||||
2. преобразовывать payload в raw-модели Dzengi, созданные в Build 002;
|
||||
3. не выполнять повторную schema validation;
|
||||
4. не создавать внутреннюю модель `Instrument`;
|
||||
5. не подключаться к `ExchangeService`;
|
||||
6. не менять production-поведение работающего бота.
|
||||
Reference in New Issue
Block a user