Files
dzentra_bot/docs/migrations/build_007.md

999 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build 007 — Protocol и Exceptions
**Статус:** Завершён
**Подсистема:** `market_data/acquisition`
**Область:** Instrument Reference Data
**Тип изменения:** Изолированное расширение новой архитектуры без подключения к production runtime
**Результат полного набора тестов:** `113 passed`
---
## 1. Цель Build 007
Цель Build 007 — зафиксировать минимальные публичные контракты взаимодействия между следующими слоями новой подсистемы Instrument Reference Data:
```text
Build 008 — Dzengi REST Adapter
Build 009 — Instrument Handler
Build 010 — Instrument Feed
Build 011 — Registry
Build 012 — Acquisition Service
```
Также добавлена специализированная ошибка транспортного уровня для будущего REST adapter.
После завершения Build 007 архитектурная последовательность выглядит следующим образом:
```text
InstrumentDocumentSource
InstrumentDocumentHandler
InstrumentFeedProtocol
Registry
Acquisition Service
```
Build 007 не реализует получение или обработку данных и не подключает новую подсистему к существующему `ExchangeService`.
---
## 2. Почему Build 007 выполняется именно сейчас
До начала Build 007 были завершены предыдущие этапы:
```text
Build 001 — внутренняя модель Instrument Reference Data
Build 002 — raw-модели ответа Dzengi
Build 003 — структурная валидация exchangeInfo
Build 004 — parser exchangeInfo
Build 005 — value validation
Build 006 — mapper Dzengi → Instrument
```
К началу Build 007 уже существовал полный pipeline преобразования заранее полученного JSON-документа:
```text
raw JSON document
Schema Validation
Parser
Dzengi Raw Models
Value Validation
Mapper
tuple[Instrument, ...]
```
Следующие Build должны добавить транспортный источник, handler, feed, registry и acquisition service.
Перед их реализацией необходимо было определить минимальные интерфейсы взаимодействия между этими слоями и добавить специализированную ошибку транспортного уровня.
---
## 3. Архитектурная граница Build 007
Build 007 отвечает только за:
```text
определение контракта источника сырого документа;
определение контракта обработчика сырого документа;
определение контракта источника готовых Instrument;
добавление ошибки транспортного уровня.
```
Build 007 не выполняет:
```text
HTTP-запросы;
получение exchangeInfo;
schema validation;
parsing;
value validation;
mapping;
создание конкретного Handler;
создание конкретного Feed;
создание Registry;
создание Acquisition Service;
кэширование;
изменение ExchangeService;
изменение runtime;
изменение Telegram UI;
изменение автоторговли.
```
---
## 4. Изменённые файлы
В рамках Build 007 изменены:
```text
app/src/market_data/acquisition/protocol.py
app/src/market_data/acquisition/exceptions.py
```
Создан тестовый файл:
```text
app/tests/unit/market_data/acquisition/test_protocol.py
```
Не изменялись:
```text
app/src/market_data/acquisition/adapters/dzengi/rest.py
app/src/market_data/acquisition/handlers/instrument_handler.py
app/src/market_data/acquisition/feeds/instrument_feed.py
app/src/market_data/acquisition/registry.py
app/src/market_data/acquisition/service.py
app/src/market_data/acquisition/adapters/dzengi/models.py
app/src/market_data/acquisition/adapters/dzengi/parser.py
app/src/market_data/acquisition/adapters/dzengi/mapper.py
app/src/market_data/acquisition/validation/schema.py
app/src/market_data/acquisition/validation/values.py
app/src/integrations/exchange/*
app/src/telegram/*
app/src/trading/*
```
Работающий legacy-код бота не изменён.
---
## 5. Созданные Protocol
В файле:
```text
app/src/market_data/acquisition/protocol.py
```
созданы три минимальных протокола:
```text
InstrumentDocumentSource
InstrumentDocumentHandler
InstrumentFeedProtocol
```
Все протоколы основаны на структурной типизации Python:
```python
typing.Protocol
```
и объявлены как:
```python
@runtime_checkable
```
Это позволяет использовать их как для статической типизации, так и для ограниченной runtime-проверки через `isinstance()`.
---
## 6. InstrumentDocumentSource
Контракт:
```python
@runtime_checkable
class InstrumentDocumentSource(Protocol):
def fetch_instrument_document(self) -> object:
...
```
Назначение:
```text
получить декодированный транспортный документ Instrument Reference Data.
```
Предполагаемый конкретный потребитель этого контракта появится в:
```text
Build 008 — Dzengi REST Adapter
```
Будущий REST adapter должен реализовать метод:
```python
fetch_instrument_document()
```
и вернуть сырой декодированный документ.
---
## 7. Почему InstrumentDocumentSource возвращает object
Возвращаемый тип:
```python
object
```
выбран сознательно.
Транспортный источник не должен выполнять:
```text
schema validation;
parsing;
value validation;
mapping.
```
На транспортной границе REST adapter может получить произвольное декодированное JSON-значение:
```text
dict
list
str
int
float
bool
None
```
Проверка структуры является обязанностью:
```text
Schema Validation
```
Поэтому транспортный слой не должен преждевременно утверждать, что полученный документ является корректным JSON-объектом нужной структуры.
Архитектурная граница остаётся следующей:
```text
REST Adapter
object
Schema Validation
ValidatedExchangeInfoDocument
```
---
## 8. InstrumentDocumentHandler
Контракт:
```python
@runtime_checkable
class InstrumentDocumentHandler(Protocol):
def handle_instrument_document(
self,
document: object,
) -> tuple[Instrument, ...]:
...
```
Назначение:
```text
преобразовать сырой документ в проверенные внутренние модели Instrument.
```
Конкретная реализация появится в:
```text
Build 009 — Instrument Handler
```
Handler должен объединить уже существующий pipeline:
```text
object
validate_exchange_info_schema()
ValidatedExchangeInfoDocument
parse_exchange_info()
DzengiExchangeInfoResponse
validate_exchange_info_values()
map_dzengi_exchange_info_to_instruments()
tuple[Instrument, ...]
```
При этом Protocol не знает:
```text
какая биржа является источником;
какой parser используется;
какой mapper используется;
какие source-specific raw-модели существуют.
```
---
## 9. InstrumentFeedProtocol
Контракт:
```python
@runtime_checkable
class InstrumentFeedProtocol(Protocol):
def load_instruments(self) -> tuple[Instrument, ...]:
...
```
Назначение:
```text
получить полный immutable-набор внутренних моделей Instrument.
```
Конкретный Feed появится в:
```text
Build 010 — Instrument Feed
```
Предполагаемая композиция:
```text
InstrumentFeed
├── InstrumentDocumentSource
└── InstrumentDocumentHandler
```
Рабочая последовательность:
```text
InstrumentFeed.load_instruments()
InstrumentDocumentSource.fetch_instrument_document()
object
InstrumentDocumentHandler.handle_instrument_document()
tuple[Instrument, ...]
```
---
## 10. Почему протокол называется InstrumentFeedProtocol
Будущий файл:
```text
app/src/market_data/acquisition/feeds/instrument_feed.py
```
предназначен для конкретной реализации Feed.
Чтобы избежать конфликта между интерфейсом и конкретным классом, протокол получил имя:
```text
InstrumentFeedProtocol
```
Конкретная реализация в Build 010 сможет называться:
```text
InstrumentFeed
```
Таким образом:
```text
InstrumentFeedProtocol
контракт
InstrumentFeed
конкретная реализация
```
---
## 11. Structural Typing
Реализации не обязаны наследоваться от Protocol напрямую.
Например:
```python
class StubInstrumentDocumentSource:
def fetch_instrument_document(self) -> object:
return {
"symbols": [],
}
```
Такой объект удовлетворяет контракту:
```text
InstrumentDocumentSource
```
без явного наследования:
```python
class StubInstrumentDocumentSource(InstrumentDocumentSource):
...
```
Это уменьшает связанность между конкретными реализациями и интерфейсами.
---
## 12. Runtime Checkable
Все три Protocol объявлены с:
```python
@runtime_checkable
```
Благодаря этому допустима проверка:
```python
isinstance(source, InstrumentDocumentSource)
```
Тестами подтверждено:
```text
объект с требуемым методом
→ соответствует Protocol
объект без требуемого метода
→ не соответствует Protocol
```
Важно: runtime-проверка Protocol подтверждает структурное наличие требуемых атрибутов и методов, но не выполняет полную глубокую проверку всех аннотаций типов и фактических возвращаемых значений.
---
## 13. Новая транспортная ошибка
В файл:
```text
app/src/market_data/acquisition/exceptions.py
```
добавлена:
```python
class InstrumentReferenceTransportError(
MarketDataAcquisitionError
):
pass
```
Она предназначена для будущего:
```text
Build 008 — Dzengi REST Adapter
```
---
## 14. Назначение InstrumentReferenceTransportError
Ошибка предназначена для проблем получения Instrument Reference Data от внешнего источника.
Потенциальные случаи:
```text
ошибка соединения;
timeout;
HTTP error;
ошибка JSON decoding;
непредвиденная ошибка REST-клиента.
```
Она не используется для:
```text
неверной структуры документа;
ошибки parsing;
недопустимых значений;
ошибки mapping.
```
Эти случаи уже имеют специализированные типы исключений.
---
## 15. Итоговая иерархия ошибок
После Build 007 иерархия выглядит следующим образом:
```text
MarketDataAcquisitionError
├── InstrumentReferenceTransportError
├── InstrumentReferenceSchemaError
├── InstrumentReferenceParseError
├── InstrumentReferenceValueError
└── InstrumentReferenceMappingError
```
Назначение ошибок:
| Ошибка | Ответственность |
|---|---|
| `InstrumentReferenceTransportError` | Получение данных от внешнего источника |
| `InstrumentReferenceSchemaError` | Структура исходного документа |
| `InstrumentReferenceParseError` | Преобразование проверенного документа в raw-модели |
| `InstrumentReferenceValueError` | Допустимость значений |
| `InstrumentReferenceMappingError` | Преобразование raw-модели во внутреннюю модель `Instrument` |
---
## 16. Какие дополнительные Protocol не создавались
В Build 007 сознательно не создавались отдельные Protocol для:
```text
Schema Validator
Parser
Value Validator
Mapper
Registry
Acquisition Service
```
Причины:
```text
validators, parser и mapper уже реализованы как чистые функции;
Registry и Acquisition Service пока не имеют нескольких реализаций;
дополнительные интерфейсы сейчас не используются;
создание таких контрактов было бы преждевременной абстракцией.
```
Это соответствует принципу:
```text
не создавать абстракции «на будущее» без конкретного потребителя.
```
---
## 17. Какие дополнительные Exceptions не создавались
В Build 007 сознательно не добавлялись:
```text
InstrumentReferenceHandlerError
InstrumentReferenceFeedError
InstrumentReferenceRegistryError
InstrumentReferenceServiceError
```
Причины:
```text
Handler может передавать точные ошибки Schema, Parser, Value и Mapping;
Feed может передавать точные ошибки Transport и Processing;
Registry ещё не реализован;
Acquisition Service ещё не реализован;
новые типы ошибок следует вводить только там, где появляется реальная новая категория отказа.
```
---
## 18. Реализованные тестовые сценарии
Создан файл:
```text
app/tests/unit/market_data/acquisition/test_protocol.py
```
Реализовано 7 тестов.
Проверены:
1. соответствие корректного source объекта `InstrumentDocumentSource`;
2. соответствие корректного handler объекта `InstrumentDocumentHandler`;
3. соответствие корректного feed объекта `InstrumentFeedProtocol`;
4. отклонение объектов без обязательных методов;
5. structural typing без явного наследования;
6. наследование `InstrumentReferenceTransportError` от `MarketDataAcquisitionError`;
7. наличие общего базового типа у всех ошибок Instrument Reference Data.
---
## 19. Выполненные проверки
### Проверка 1 — unit-тесты Protocol и Exceptions
Команда:
```bash
python -m pytest \
tests/unit/market_data/acquisition/test_protocol.py \
-q
```
Результат:
```text
....... [100%]
7 passed in 0.01s
```
Статус:
```text
PASSED
```
---
### Проверка 2 — Python compilation
Команда:
```bash
python -m py_compile \
src/market_data/acquisition/protocol.py \
src/market_data/acquisition/exceptions.py \
tests/unit/market_data/acquisition/test_protocol.py
```
Результат:
```text
Команда завершилась без ошибок и без вывода.
```
Статус:
```text
PASSED
```
---
### Проверка 3 — полный набор тестов проекта
Команда:
```bash
python -m pytest -q
```
Результат:
```text
................................................................................................................. [100%]
113 passed in 0.06s
```
Статус:
```text
PASSED
```
---
### Проверка 4 — отсутствие преждевременной production-интеграции
Команда:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "InstrumentDocumentSource|InstrumentDocumentHandler|InstrumentFeedProtocol|InstrumentReferenceTransportError" \
src tests
```
Подтверждено:
```text
InstrumentDocumentSource
используется только в protocol.py и unit-тестах
InstrumentDocumentHandler
используется только в protocol.py и unit-тестах
InstrumentFeedProtocol
используется только в protocol.py и unit-тестах
InstrumentReferenceTransportError
объявлена в exceptions.py и используется только в unit-тестах
```
Не обнаружено подключения к:
```text
src/integrations/exchange/*
src/telegram/*
src/trading/*
```
Статус:
```text
PASSED
```
---
## 20. Архитектура после Build 007
После завершения Build 007 новая часть подсистемы имеет следующую структуру:
```text
market_data/
└── acquisition/
├── exceptions.py
│ ├── MarketDataAcquisitionError
│ ├── InstrumentReferenceTransportError
│ ├── InstrumentReferenceSchemaError
│ ├── InstrumentReferenceParseError
│ ├── InstrumentReferenceValueError
│ └── InstrumentReferenceMappingError
├── protocol.py
│ ├── InstrumentDocumentSource
│ ├── InstrumentDocumentHandler
│ └── InstrumentFeedProtocol
├── models/
│ └── instrument.py
│ └── Instrument
├── validation/
│ ├── schema.py
│ └── values.py
└── adapters/
└── dzengi/
├── models.py
├── parser.py
├── mapper.py
└── rest.py
```
На текущем этапе:
```text
rest.py
ещё не реализован
instrument_handler.py
ещё не реализован
instrument_feed.py
ещё не реализован
registry.py
ещё не реализован
service.py
ещё не реализован
```
---
## 21. Полная архитектурная цепочка после Build 007
Уже реализовано:
```text
raw JSON document
validate_exchange_info_schema()
ValidatedExchangeInfoDocument
parse_exchange_info()
DzengiExchangeInfoResponse
validate_exchange_info_values()
map_dzengi_exchange_info_to_instruments()
tuple[Instrument, ...]
```
Зафиксированы контракты для будущей orchestration-цепочки:
```text
InstrumentDocumentSource
InstrumentDocumentHandler
InstrumentFeedProtocol
Registry
Acquisition Service
```
После реализации Build 008012 предполагаемая полная цепочка будет выглядеть так:
```text
Dzengi REST API
Dzengi REST Adapter
implements InstrumentDocumentSource
object
Instrument Handler
implements InstrumentDocumentHandler
tuple[Instrument, ...]
Instrument Feed
implements InstrumentFeedProtocol
Registry
Acquisition Service
```
---
## 22. Влияние на legacy-систему
Build 007 не подключён к существующим компонентам:
```text
ExchangeService
ExchangeSymbol
SymbolValidationResult
Telegram UI
AutoTrade
Market Stream
Market Data Runner
Execution Quality
```
Не изменены:
```text
ExchangeService.get_exchange_symbols()
ExchangeService.validate_symbol()
ExchangeService.get_symbol_runtime_status()
normalize_symbol()
symbol_candidates()
```
Старый production-путь продолжает работать без изменений.
Новая подсистема развивается параллельно.
---
## 23. Обратная совместимость
Подтверждено сохранение:
```text
сигнатур существующих методов;
старых импортов;
существующего формата ошибок;
Telegram UI;
автоторговли;
runtime-поведения;
legacy ExchangeSymbol;
legacy-кэша.
```
Build 007 имеет полную обратную совместимость.
---
## 24. Классификация изменений
| Изменение | Классификация |
|---|---|
| `InstrumentDocumentSource` | Обязательное архитектурное изменение |
| `InstrumentDocumentHandler` | Обязательное архитектурное изменение |
| `InstrumentFeedProtocol` | Обязательное архитектурное изменение |
| `InstrumentReferenceTransportError` | Обязательное архитектурное изменение |
| Structural typing | Улучшение архитектурной независимости |
| `runtime_checkable` | Улучшение тестируемости и проверяемости контрактов |
| Дополнительные преждевременные Protocol | Не создавались |
| Дополнительные преждевременные Exceptions | Не создавались |
| Изменение production-поведения | Отсутствует |
---
## 25. Итог Build 007
Build 007 завершён успешно.
Реализовано:
```text
InstrumentDocumentSource
InstrumentDocumentHandler
InstrumentFeedProtocol
InstrumentReferenceTransportError
```
Подтверждено:
```text
7 protocol tests passed
113 total project tests passed
Python compilation passed
No production integration detected
Legacy bot behavior unchanged
```
Итоговый статус:
```text
BUILD 007 — COMPLETE
```
---
## 26. Следующий этап
Следующий этап утверждённого плана:
```text
Build 008 — Dzengi REST Adapter
```
Его задача — реализовать конкретный транспортный источник, соответствующий контракту:
```text
InstrumentDocumentSource
```
Будущая граница Build 008:
```text
Dzengi REST API
Dzengi REST Adapter
object
```
Build 008 не должен выполнять:
```text
schema validation;
parsing;
value validation;
mapping;
создание Instrument;
создание Handler;
создание Feed;
создание Registry;
создание Acquisition Service;
изменение ExchangeService;
production-подключение.
```