1067 lines
22 KiB
Markdown
1067 lines
22 KiB
Markdown
# Build 005 — Value Validation для Instrument Reference Data
|
||
|
||
**Статус:** Завершён
|
||
**Подсистема:** `market_data/acquisition`
|
||
**Область:** Instrument Reference Data
|
||
**Тип изменения:** Изолированное расширение новой архитектуры без подключения к production runtime
|
||
**Результат полного набора тестов:** `84 passed`
|
||
|
||
---
|
||
|
||
## 1. Цель Build 005
|
||
|
||
Цель Build 005 — реализовать отдельный слой проверки допустимости значений, полученных после:
|
||
|
||
1. проверки структуры исходного документа `exchangeInfo`;
|
||
2. преобразования проверенного документа в типизированные raw-модели адаптера Dzengi.
|
||
|
||
Build 005 добавляет третий этап обработки Instrument Reference Data:
|
||
|
||
```text
|
||
raw JSON
|
||
↓
|
||
schema validation
|
||
↓
|
||
parser
|
||
↓
|
||
value validation
|
||
```
|
||
|
||
Value validation проверяет не структуру JSON и не типы полей исходного документа, а предметную допустимость уже распарсенных значений.
|
||
|
||
Например:
|
||
|
||
```text
|
||
tickSize = 0
|
||
```
|
||
|
||
может быть корректным числовым значением с точки зрения JSON и parser layer, но недопустимым значением для шага цены торгового инструмента.
|
||
|
||
---
|
||
|
||
## 2. Почему Build 005 реализован именно на этом этапе
|
||
|
||
До Build 005 были завершены следующие этапы миграции:
|
||
|
||
```text
|
||
Build 001
|
||
↓
|
||
Каноническая source-independent модель Instrument
|
||
|
||
Build 002
|
||
↓
|
||
Raw transport models адаптера Dzengi
|
||
|
||
Build 003
|
||
↓
|
||
Schema validation исходного exchangeInfo
|
||
|
||
Build 004
|
||
↓
|
||
Parser: validated document → Dzengi raw models
|
||
|
||
Build 005
|
||
↓
|
||
Value validation Dzengi raw models
|
||
```
|
||
|
||
Такой порядок позволяет строго разделить ответственность слоёв.
|
||
|
||
### Schema validation отвечает за:
|
||
|
||
```text
|
||
Есть ли ожидаемые JSON-объекты?
|
||
Есть ли массив symbols?
|
||
Являются ли элементы symbols объектами?
|
||
Является ли filters массивом?
|
||
```
|
||
|
||
### Parser отвечает за:
|
||
|
||
```text
|
||
Как преобразовать проверенный JSON-документ
|
||
в типизированные raw-модели Dzengi?
|
||
```
|
||
|
||
### Value validation отвечает за:
|
||
|
||
```text
|
||
Допустимы ли конкретные значения полей
|
||
с точки зрения контракта Instrument Reference Data?
|
||
```
|
||
|
||
Это исключает смешивание:
|
||
|
||
- проверки JSON-структуры;
|
||
- транспортного parsing;
|
||
- проверки предметных значений;
|
||
- source-independent mapping.
|
||
|
||
---
|
||
|
||
## 3. Изменённые файлы
|
||
|
||
В рамках Build 005 изменены:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/exceptions.py
|
||
app/src/market_data/acquisition/validation/values.py
|
||
```
|
||
|
||
Добавлен тестовый файл:
|
||
|
||
```text
|
||
app/tests/unit/market_data/acquisition/validation/test_values.py
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Новая типизированная ошибка
|
||
|
||
В файл:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/exceptions.py
|
||
```
|
||
|
||
добавлена ошибка:
|
||
|
||
```python
|
||
class InstrumentReferenceValueError(MarketDataAcquisitionError):
|
||
pass
|
||
```
|
||
|
||
Иерархия ошибок Instrument Reference Data теперь выглядит так:
|
||
|
||
```text
|
||
MarketDataAcquisitionError
|
||
├── InstrumentReferenceSchemaError
|
||
├── InstrumentReferenceParseError
|
||
└── InstrumentReferenceValueError
|
||
```
|
||
|
||
Каждый этап обработки имеет собственную категорию ошибки:
|
||
|
||
| Этап | Ошибка |
|
||
|---|---|
|
||
| Schema validation | `InstrumentReferenceSchemaError` |
|
||
| Parsing | `InstrumentReferenceParseError` |
|
||
| Value validation | `InstrumentReferenceValueError` |
|
||
|
||
Это позволяет будущему orchestration/service layer точно определять, на каком этапе обработки произошла ошибка.
|
||
|
||
---
|
||
|
||
## 5. Главная функция Build 005
|
||
|
||
В файл:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/validation/values.py
|
||
```
|
||
|
||
добавлена функция:
|
||
|
||
```python
|
||
def validate_exchange_info_values(
|
||
response: DzengiExchangeInfoResponse,
|
||
) -> None:
|
||
```
|
||
|
||
Функция принимает:
|
||
|
||
```python
|
||
DzengiExchangeInfoResponse
|
||
```
|
||
|
||
то есть результат работы parser layer из Build 004.
|
||
|
||
При корректных значениях функция возвращает:
|
||
|
||
```python
|
||
None
|
||
```
|
||
|
||
При обнаружении недопустимого значения выбрасывается:
|
||
|
||
```python
|
||
InstrumentReferenceValueError
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Проверяемые обязательные строки инструмента
|
||
|
||
Для каждого `DzengiExchangeInfoSymbol` проверяются следующие обязательные строковые поля:
|
||
|
||
```text
|
||
symbol
|
||
name
|
||
status
|
||
baseAsset
|
||
quoteAsset
|
||
marketType
|
||
```
|
||
|
||
Они не должны быть пустыми или состоять только из пробелов.
|
||
|
||
Пример недопустимого значения:
|
||
|
||
```python
|
||
symbol=" "
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
InstrumentReferenceValueError:
|
||
$.payload.symbols[0].symbol не должен быть пустым.
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Проверка `orderTypes` и `marketModes`
|
||
|
||
Каждый элемент следующих последовательностей проверяется на непустое строковое значение:
|
||
|
||
```text
|
||
orderTypes
|
||
marketModes
|
||
```
|
||
|
||
Например:
|
||
|
||
```python
|
||
order_types=("LIMIT", " ")
|
||
```
|
||
|
||
отклоняется с ошибкой:
|
||
|
||
```text
|
||
$.payload.symbols[0].orderTypes[1] не должен быть пустым.
|
||
```
|
||
|
||
Аналогично:
|
||
|
||
```python
|
||
market_modes=("REGULAR", "")
|
||
```
|
||
|
||
отклоняется как недопустимое значение.
|
||
|
||
---
|
||
|
||
## 8. Проверка integer-полей
|
||
|
||
Следующие optional integer-поля не могут быть отрицательными:
|
||
|
||
```text
|
||
baseAssetPrecision
|
||
quotePrecision
|
||
swapChargeInterval
|
||
```
|
||
|
||
Допустимо:
|
||
|
||
```text
|
||
None
|
||
0
|
||
1
|
||
2
|
||
...
|
||
```
|
||
|
||
Недопустимо:
|
||
|
||
```text
|
||
-1
|
||
```
|
||
|
||
При нарушении выбрасывается:
|
||
|
||
```text
|
||
InstrumentReferenceValueError
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Проверка `tickSize`
|
||
|
||
Для:
|
||
|
||
```text
|
||
tickSize
|
||
```
|
||
|
||
применяется правило:
|
||
|
||
```text
|
||
tickSize > 0
|
||
```
|
||
|
||
Допустимы только положительные конечные числа.
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
0
|
||
отрицательные значения
|
||
NaN
|
||
+Infinity
|
||
-Infinity
|
||
```
|
||
|
||
Это правило подтверждено анализом реального sample Dzengi:
|
||
|
||
```text
|
||
tickSize:
|
||
present=51
|
||
min=1E-8
|
||
max=1
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Проверка конечности числовых значений
|
||
|
||
Для optional numeric-полей проверяется, что значение является конечным числом.
|
||
|
||
Проверяются:
|
||
|
||
```text
|
||
tickValue
|
||
tradingFee
|
||
exchangeFee
|
||
longRate
|
||
shortRate
|
||
minSLGap
|
||
maxSLGap
|
||
minTPGap
|
||
maxTPGap
|
||
```
|
||
|
||
Отклоняются:
|
||
|
||
```text
|
||
NaN
|
||
+Infinity
|
||
-Infinity
|
||
```
|
||
|
||
При этом нулевые значения не запрещаются автоматически.
|
||
|
||
---
|
||
|
||
## 11. Проверка LOT_SIZE
|
||
|
||
Для:
|
||
|
||
```text
|
||
DzengiLotSizeFilter
|
||
```
|
||
|
||
проверяются:
|
||
|
||
```text
|
||
minQty
|
||
maxQty
|
||
stepSize
|
||
```
|
||
|
||
Если значение присутствует, оно должно:
|
||
|
||
1. корректно преобразовываться в `Decimal`;
|
||
2. быть конечным;
|
||
3. быть строго больше нуля.
|
||
|
||
Дополнительно проверяется отношение:
|
||
|
||
```text
|
||
minQty <= maxQty
|
||
```
|
||
|
||
Если:
|
||
|
||
```text
|
||
minQty > maxQty
|
||
```
|
||
|
||
выбрасывается:
|
||
|
||
```text
|
||
InstrumentReferenceValueError
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Проверка MIN_NOTIONAL
|
||
|
||
Для:
|
||
|
||
```text
|
||
DzengiMinNotionalFilter
|
||
```
|
||
|
||
проверяется:
|
||
|
||
```text
|
||
minNotional >= 0
|
||
```
|
||
|
||
Нулевое значение разрешено.
|
||
|
||
Отрицательное значение отклоняется.
|
||
|
||
---
|
||
|
||
## 13. Использование Decimal
|
||
|
||
Строковые и числовые значения ограничений торгового инструмента преобразуются для проверки через:
|
||
|
||
```python
|
||
Decimal(str(value))
|
||
```
|
||
|
||
Это позволяет избежать ненужной потери точности при обработке таких значений, как:
|
||
|
||
```text
|
||
0.0001
|
||
0.00000001
|
||
6.9E-7
|
||
```
|
||
|
||
Именно такой подход соответствует уже принятому контракту канонической модели `Instrument`, где торговые числовые ограничения представлены через `Decimal`.
|
||
|
||
---
|
||
|
||
## 14. Особенность полей country, sector и industry
|
||
|
||
В ходе первой проверки Build 005 была обнаружена ошибка первоначальной реализации.
|
||
|
||
Изначально к полям:
|
||
|
||
```text
|
||
country
|
||
sector
|
||
industry
|
||
```
|
||
|
||
применялось правило:
|
||
|
||
```text
|
||
если значение присутствует, строка не должна быть пустой
|
||
```
|
||
|
||
Однако анализ реального ответа Dzengi и уже созданных raw-моделей показал, что Dzengi штатно возвращает:
|
||
|
||
```python
|
||
country=""
|
||
sector=""
|
||
industry=""
|
||
```
|
||
|
||
Поэтому пустая строка для этих полей является допустимым транспортным состоянием.
|
||
|
||
После исправления Build 005:
|
||
|
||
```text
|
||
country=""
|
||
sector=""
|
||
industry=""
|
||
```
|
||
|
||
не считаются ошибкой.
|
||
|
||
Это соответствует реальному контракту Dzengi и не выполняет преждевременную нормализацию transport-specific данных.
|
||
|
||
---
|
||
|
||
## 15. Проверка отрицательных longRate и shortRate
|
||
|
||
Отрицательные значения:
|
||
|
||
```text
|
||
longRate
|
||
shortRate
|
||
```
|
||
|
||
являются допустимыми.
|
||
|
||
Это подтверждено анализом реального sample Dzengi:
|
||
|
||
```text
|
||
longRate:
|
||
present=51
|
||
min=-0.0684932
|
||
max=0.1389493
|
||
negative=43
|
||
|
||
shortRate:
|
||
present=51
|
||
min=-0.1608693
|
||
max=0.01
|
||
negative=38
|
||
```
|
||
|
||
Поэтому value validation проверяет только:
|
||
|
||
```text
|
||
значение является корректным конечным числом
|
||
```
|
||
|
||
но не требует:
|
||
|
||
```text
|
||
value >= 0
|
||
```
|
||
|
||
---
|
||
|
||
## 16. Проверка допустимости нулевых optional numeric values
|
||
|
||
Нулевые значения разрешены для полей, для которых реальный контракт Dzengi допускает `0`.
|
||
|
||
В частности:
|
||
|
||
```text
|
||
tickValue
|
||
tradingFee
|
||
exchangeFee
|
||
minSLGap
|
||
maxSLGap
|
||
minTPGap
|
||
maxTPGap
|
||
```
|
||
|
||
Это соответствует анализу реального sample:
|
||
|
||
```text
|
||
tickValue:
|
||
zero=1
|
||
|
||
tradingFee:
|
||
zero=40
|
||
|
||
minSLGap:
|
||
zero=51
|
||
|
||
minTPGap:
|
||
zero=51
|
||
```
|
||
|
||
Value validation не вводит ограничений, которые не подтверждены фактическими данными или контрактом источника.
|
||
|
||
---
|
||
|
||
## 17. Проверка неизвестных instrument filters
|
||
|
||
Для неизвестного фильтра инструмента:
|
||
|
||
```python
|
||
DzengiUnknownFilter
|
||
```
|
||
|
||
поле:
|
||
|
||
```text
|
||
filterType
|
||
```
|
||
|
||
должно быть непустым.
|
||
|
||
Это позволяет сохранить forward compatibility с новыми типами фильтров Dzengi, одновременно предотвращая попадание полностью неопределённых instrument filters в дальнейшую обработку.
|
||
|
||
---
|
||
|
||
## 18. Особенность global exchange filters
|
||
|
||
Для:
|
||
|
||
```text
|
||
exchangeFilters
|
||
```
|
||
|
||
сохранено более мягкое поведение.
|
||
|
||
Реальный transport contract может содержать global filter без meaningful `filterType`, поэтому пустое значение не отклоняется автоматически.
|
||
|
||
Таким образом, Build 005 различает:
|
||
|
||
```text
|
||
instrument-level filters
|
||
```
|
||
|
||
и:
|
||
|
||
```text
|
||
global exchange filters
|
||
```
|
||
|
||
и не навязывает им одинаковые ограничения без подтверждения реального контракта.
|
||
|
||
---
|
||
|
||
## 19. Результаты анализа реального sample Dzengi
|
||
|
||
Перед реализацией Build 005 был выполнен анализ реального `exchangeInfo` sample.
|
||
|
||
Получены следующие результаты.
|
||
|
||
### Обязательные строки
|
||
|
||
Пустые значения отсутствуют для:
|
||
|
||
```text
|
||
symbol
|
||
name
|
||
status
|
||
baseAsset
|
||
quoteAsset
|
||
marketType
|
||
```
|
||
|
||
### Precision
|
||
|
||
```text
|
||
baseAssetPrecision:
|
||
count=51
|
||
min=2
|
||
max=8
|
||
invalid=[]
|
||
|
||
quotePrecision:
|
||
count=51
|
||
min=2
|
||
max=8
|
||
invalid=[]
|
||
```
|
||
|
||
### Числовые диапазоны
|
||
|
||
```text
|
||
tickSize:
|
||
present=51
|
||
min=1E-8
|
||
max=1
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
tickValue:
|
||
present=39
|
||
min=0
|
||
max=50665.5
|
||
zero=1
|
||
negative=0
|
||
invalid=[]
|
||
|
||
minQty:
|
||
present=51
|
||
min=0.0001
|
||
max=1
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
maxQty:
|
||
present=51
|
||
min=100
|
||
max=10000000
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
stepSize:
|
||
present=51
|
||
min=0.0001
|
||
max=1
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
minNotional:
|
||
present=39
|
||
min=6.9E-7
|
||
max=507
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
tradingFee:
|
||
present=51
|
||
min=0
|
||
max=0.075
|
||
zero=40
|
||
negative=0
|
||
invalid=[]
|
||
|
||
exchangeFee:
|
||
present=0
|
||
min=None
|
||
max=None
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
longRate:
|
||
present=51
|
||
min=-0.0684932
|
||
max=0.1389493
|
||
zero=0
|
||
negative=43
|
||
invalid=[]
|
||
|
||
shortRate:
|
||
present=51
|
||
min=-0.1608693
|
||
max=0.01
|
||
zero=0
|
||
negative=38
|
||
invalid=[]
|
||
|
||
minSLGap:
|
||
present=51
|
||
min=0
|
||
max=0
|
||
zero=51
|
||
negative=0
|
||
invalid=[]
|
||
|
||
maxSLGap:
|
||
present=51
|
||
min=20.0
|
||
max=50.0
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
|
||
minTPGap:
|
||
present=51
|
||
min=0
|
||
max=0
|
||
zero=51
|
||
negative=0
|
||
invalid=[]
|
||
|
||
maxTPGap:
|
||
present=51
|
||
min=20.0
|
||
max=50.0
|
||
zero=0
|
||
negative=0
|
||
invalid=[]
|
||
```
|
||
|
||
### Проверка отношения minQty и maxQty
|
||
|
||
Не обнаружено ни одного случая:
|
||
|
||
```text
|
||
minQty > maxQty
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
[]
|
||
```
|
||
|
||
---
|
||
|
||
## 20. Реализованные тесты
|
||
|
||
Добавлен файл:
|
||
|
||
```text
|
||
app/tests/unit/market_data/acquisition/validation/test_values.py
|
||
```
|
||
|
||
В нём реализовано:
|
||
|
||
```text
|
||
34 теста
|
||
```
|
||
|
||
Тестами покрыты:
|
||
|
||
- корректный полный `exchangeInfo`;
|
||
- пустые обязательные строки;
|
||
- пустой `orderType`;
|
||
- пустой `marketMode`;
|
||
- отрицательные precision values;
|
||
- отрицательный `swapChargeInterval`;
|
||
- нулевой `tickSize`;
|
||
- отрицательный `tickSize`;
|
||
- `NaN`;
|
||
- `+Infinity`;
|
||
- `-Infinity`;
|
||
- нечисловой `minQty`;
|
||
- неположительный `minQty`;
|
||
- неположительный `maxQty`;
|
||
- неположительный `stepSize`;
|
||
- `minQty > maxQty`;
|
||
- отрицательный `minNotional`;
|
||
- нулевой `minNotional`;
|
||
- отрицательные `longRate`;
|
||
- отрицательные `shortRate`;
|
||
- допустимые нулевые optional numeric values;
|
||
- rate limits;
|
||
- неизвестные instrument filters;
|
||
- global exchange filters.
|
||
|
||
---
|
||
|
||
## 21. Выполненные проверки
|
||
|
||
### Проверка 1 — unit-тесты Build 005
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest \
|
||
tests/unit/market_data/acquisition/validation/test_values.py \
|
||
-q
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
34 passed in 0.02s
|
||
```
|
||
|
||
---
|
||
|
||
### Проверка 2 — Python compilation
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m py_compile \
|
||
src/market_data/acquisition/exceptions.py \
|
||
src/market_data/acquisition/validation/values.py \
|
||
tests/unit/market_data/acquisition/validation/test_values.py
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
успешно, без ошибок
|
||
```
|
||
|
||
---
|
||
|
||
### Проверка 3 — полная ручная цепочка обработки
|
||
|
||
Проверена последовательность:
|
||
|
||
```text
|
||
raw JSON
|
||
↓
|
||
validate_exchange_info_schema()
|
||
↓
|
||
parse_exchange_info()
|
||
↓
|
||
validate_exchange_info_values()
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
Validation result: None
|
||
Symbol: BTC/USD_LEVERAGE
|
||
Filters: (
|
||
DzengiLotSizeFilter(
|
||
filter_type='LOT_SIZE',
|
||
min_qty='0.0001',
|
||
max_qty='1000',
|
||
step_size='0.0001'
|
||
),
|
||
DzengiMinNotionalFilter(
|
||
filter_type='MIN_NOTIONAL',
|
||
min_notional='1'
|
||
)
|
||
)
|
||
```
|
||
|
||
Это подтверждает совместимость Build 003, Build 004 и Build 005.
|
||
|
||
---
|
||
|
||
### Проверка 4 — полный набор тестов проекта
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest -q
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
84 passed in 0.06s
|
||
```
|
||
|
||
Регрессий не обнаружено.
|
||
|
||
---
|
||
|
||
### Проверка 5 — контроль области использования
|
||
|
||
Выполнен поиск:
|
||
|
||
```bash
|
||
grep -RIn \
|
||
--exclude-dir="__pycache__" \
|
||
--exclude="*.pyc" \
|
||
-E "validate_exchange_info_values|InstrumentReferenceValueError" \
|
||
src tests
|
||
```
|
||
|
||
Подтверждено:
|
||
|
||
- `validate_exchange_info_values()` используется только в собственном модуле и unit-тестах;
|
||
- `InstrumentReferenceValueError` используется только в `exceptions.py`, `values.py` и unit-тестах;
|
||
- новый validator ещё не подключён к legacy `ExchangeService`;
|
||
- UI не изменён;
|
||
- runtime не изменён;
|
||
- автоторговля не изменена;
|
||
- production-поведение работающего бота не изменено.
|
||
|
||
---
|
||
|
||
## 22. Архитектурная граница Build 005
|
||
|
||
Build 005 намеренно не реализует:
|
||
|
||
```text
|
||
REST client новой подсистемы
|
||
mapping Dzengi raw models → Instrument
|
||
instrument feed
|
||
instrument handler
|
||
registry
|
||
service orchestration
|
||
cache
|
||
legacy compatibility adapter
|
||
переключение production consumers
|
||
удаление старого ExchangeSymbol
|
||
```
|
||
|
||
Эти задачи относятся к следующим этапам миграции.
|
||
|
||
Build 005 отвечает только за:
|
||
|
||
```text
|
||
проверку допустимости значений
|
||
в уже распарсенных raw-моделях Dzengi exchangeInfo
|
||
```
|
||
|
||
---
|
||
|
||
## 23. Влияние на работающего legacy-бота
|
||
|
||
Build 005 не изменяет production-поведение существующего бота.
|
||
|
||
Старый runtime продолжает использовать существующие компоненты:
|
||
|
||
```text
|
||
src/integrations/exchange/models.py
|
||
src/integrations/exchange/service.py
|
||
src/integrations/exchange/status.py
|
||
src/integrations/exchange/symbol_utils.py
|
||
```
|
||
|
||
Новая реализация находится отдельно:
|
||
|
||
```text
|
||
src/market_data/acquisition/
|
||
```
|
||
|
||
На момент завершения Build 005 она ещё не подключена к production consumers.
|
||
|
||
Это соответствует принятой стратегии миграции:
|
||
|
||
```text
|
||
сначала построить и протестировать новый путь параллельно,
|
||
затем переключать consumers поэтапно,
|
||
не ломая работающего бота
|
||
```
|
||
|
||
---
|
||
|
||
## 24. Итог Build 005
|
||
|
||
Build 005 успешно завершён.
|
||
|
||
Реализовано:
|
||
|
||
```text
|
||
InstrumentReferenceValueError
|
||
validate_exchange_info_values()
|
||
валидация обязательных строк
|
||
валидация orderTypes
|
||
валидация marketModes
|
||
валидация precision
|
||
валидация tickSize
|
||
валидация finite numeric values
|
||
валидация LOT_SIZE
|
||
валидация MIN_NOTIONAL
|
||
валидация rate limits
|
||
поддержка отрицательных longRate/shortRate
|
||
поддержка допустимых нулевых значений
|
||
поддержка пустых country/sector/industry
|
||
проверка unknown filters
|
||
34 unit-теста
|
||
```
|
||
|
||
Финальный результат:
|
||
|
||
```text
|
||
84 passed in 0.06s
|
||
```
|
||
|
||
Регрессий не обнаружено.
|
||
|
||
Legacy production runtime не изменён.
|
||
|
||
---
|
||
|
||
## 25. Текущее состояние pipeline после Build 005
|
||
|
||
На момент завершения Build 005 построена следующая часть новой подсистемы:
|
||
|
||
```text
|
||
Dzengi exchangeInfo raw JSON
|
||
↓
|
||
validate_exchange_info_schema()
|
||
↓
|
||
ValidatedExchangeInfoDocument
|
||
↓
|
||
parse_exchange_info()
|
||
↓
|
||
DzengiExchangeInfoResponse
|
||
↓
|
||
validate_exchange_info_values()
|
||
↓
|
||
validated Dzengi raw models
|
||
```
|
||
|
||
Следующий архитектурный шаг должен продолжить pipeline от проверенных source-specific raw-моделей к канонической source-independent модели:
|
||
|
||
```text
|
||
validated Dzengi raw models
|
||
↓
|
||
mapper
|
||
↓
|
||
Instrument
|
||
```
|
||
|
||
Таким образом, естественным следующим этапом является реализация mapping layer:
|
||
|
||
```text
|
||
DzengiExchangeInfoSymbol → Instrument
|
||
```
|
||
|
||
при сохранении изоляции от legacy production runtime до завершения и проверки нового пути. |