# Build 001 — Внутренняя модель Instrument Reference Data

**Проект:** Dzentra  
**Подсистема:** Market Data Acquisition  
**Миграция:** Instrument Reference Data  
**Статус:** ✅ Завершён  
**Дата:** 2026-07-10

---

# Цель Build

Создать независимую внутреннюю модель Instrument Reference Data.

На данном этапе запрещается:

- изменять ExchangeService;
- изменять существующий runtime;
- менять Telegram UI;
- менять Symbol Validation;
- подключать новую модель к production-коду.

Build создаёт исключительно новую внутреннюю модель, которая станет целевой моделью для последующего mapper.

---

# Причина выполнения Build

В утверждённой архитектуре Dzentra модель предметной области должна существовать отдельно от:

- REST API Dzengi;
- parser;
- mapper;
- ExchangeService;
- Telegram UI;
- runtime.

Поэтому сначала создаётся независимая модель Instrument, а только затем будут строиться parser и mapper.

---

# Проанализированные файлы

В ходе Build были полностью проанализированы:

```text
app/src/integrations/exchange/service.py
app/src/integrations/exchange/models.py
app/src/integrations/exchange/symbol_utils.py
app/src/integrations/exchange/rest_client.py
app/src/integrations/exchange/status.py

app/src/telegram/ui/currency_ui.py

docs/stages/stage-03_3-exchange_info.md
docs/decisions/0007-symbol-validation.md
```

Также были проанализированы:

- сохранённый runtime-ответ `/exchangeInfo`;
- OpenAPI Dzengi;
- все текущие потребители ExchangeSymbol;
- результаты grep по всему проекту.

---

# Основные выводы анализа

Установлено:

- ExchangeSymbol создаётся только в одном месте;
- parser и mapper сейчас объединены внутри ExchangeService;
- SymbolValidationResult используется только ExchangeService;
- normalize_symbol() и symbol_candidates() централизованы;
- отдельного parser ещё не существует;
- отдельного mapper ещё не существует;
- кэш справочника представляет собой неуправляемый singleton без TTL.

Также подтверждено:

- новый Build не должен менять существующий ExchangeService;
- новая модель не должна зависеть от Telegram UI;
- runtime-статусы не являются частью Instrument Reference Data.

---

# Анализ реального ответа exchangeInfo

Подтверждено наличие следующих полей:

Обязательные:

- symbol
- name
- status
- baseAsset
- quoteAsset
- marketModes
- marketType
- tickSize

Через filters:

- stepSize
- minQty
- maxQty
- minNotional

Также обнаружены дополнительные справочные поля:

- assetType
- orderTypes
- baseAssetPrecision
- quotePrecision
- tickValue
- country
- sector
- industry
- tradingHours

---

# Принятые архитектурные решения

## Новая модель не копирует ExchangeSymbol

Новая модель является самостоятельной внутренней моделью предметной области.

---

## Runtime не входит в Instrument

Из модели исключены:

- title
- message
- ui_line
- reason
- is_open
- is_available
- is_auth_ok

Эти поля относятся к Runtime Status.

---

## Использование Decimal

Для следующих значений принято использовать Decimal:

- tick_size
- tick_value
- step_size
- min_qty
- max_qty
- min_notional

Причина:

данные используются при нормализации цен и количества и не должны терять точность.

Поскольку модель пока нигде не используется, изменение не влияет на работающего бота.

Классификация:

**Улучшение надёжности.**

---

## Не включены в Instrument

Сознательно исключены:

- trading_fee
- long_rate
- short_rate
- swap_charge_interval
- min_sl_gap
- max_sl_gap
- min_tp_gap
- max_tp_gap

Причина:

данные относятся к другим предметным подсистемам.

---

# Созданные файлы

Создан:

```text
app/src/market_data/acquisition/models/instrument.py
```

Создан:

```text
app/tests/unit/market_data/acquisition/models/test_instrument.py
```

Другие production-файлы не изменялись.

---

# Проверки

Выполнены проверки.

## Unit Test новой модели

Статус:

✅ Passed

```
4 passed
```

---

## Импорт модели

Проверено:

- импорт проходит;
- dataclass создаётся;
- Decimal работает;
- tuple работают.

Статус:

✅ Passed

---

## Компиляция

Проверено:

```
python -m py_compile
```

Статус:

✅ Passed

---

## Legacy Compatibility

Проверено:

- ExchangeSymbol
- SymbolValidationResult
- ExchangeService
- normalize_symbol()
- symbol_candidates()

Статус:

✅ Полностью совместимо.

---

## Проверка отсутствия подключения новой модели

Подтверждено:

новая модель используется исключительно в unit-тестах.

Production-код её не импортирует.

Статус:

✅ Passed

---

## Полный набор тестов

Выполнено:

```
python -m pytest -q
```

Результат:

```
5 passed
```

Статус:

✅ Passed

---

# Итог Build

Build 001 завершён успешно.

Подтверждено:

- создана независимая модель Instrument;
- модель не зависит от legacy-кода;
- обратная совместимость полностью сохранена;
- работающий бот не изменён;
- новая архитектура готова к реализации Build 002.

---

# Следующий Build

Build 002

**Dzengi Raw Models**

Следующий этап создаст модели сырого ответа REST API Dzengi.

На этом этапе ExchangeService по-прежнему изменяться не будет.