1169 lines
28 KiB
Markdown
1169 lines
28 KiB
Markdown
# Build 022 — Перевод валидации торгового символа на канонический `Instrument`
|
||
|
||
**Engineering Build Record**
|
||
|
||
---
|
||
|
||
## Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|---|---|
|
||
| Документ | Build 022 — Перевод валидации торгового символа на канонический `Instrument` |
|
||
| Тип документа | Engineering Build Record |
|
||
| Build | 022 |
|
||
| Статус | **Завершён** |
|
||
| Подсистема | Market Data Acquisition / Exchange Integration |
|
||
| Проект | Dzentra |
|
||
| Язык | Русский |
|
||
| Предыдущий Build | Build 021 — Перевод первой группы потребителей |
|
||
| Следующий этап | Build 023+ — Поэтапный перевод остальных потребителей |
|
||
|
||
---
|
||
|
||
## 1. Назначение Build
|
||
|
||
Build 022 продолжает поэтапную миграцию Dzentra с legacy-модели биржевого инструмента `ExchangeSymbol` на каноническую модель:
|
||
|
||
```text
|
||
Instrument
|
||
```
|
||
|
||
Основная задача Build 022:
|
||
|
||
> Перевести механизм валидации торгового символа `ExchangeService.validate_symbol()` и непосредственно связанный с ним runtime-status с legacy-модели `ExchangeSymbol` на канонический справочник `Instrument`, сохранив существующий внешний контракт и работоспособность старого бота.
|
||
|
||
После завершения Build 022 метод:
|
||
|
||
```python
|
||
ExchangeService.validate_symbol()
|
||
```
|
||
|
||
больше не использует:
|
||
|
||
```python
|
||
get_exchange_symbols()
|
||
```
|
||
|
||
для поиска и валидации торгового инструмента.
|
||
|
||
Каноническим источником данных теперь является:
|
||
|
||
```python
|
||
get_instruments()
|
||
```
|
||
|
||
который возвращает:
|
||
|
||
```python
|
||
tuple[Instrument, ...]
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Архитектурный контекст
|
||
|
||
До начала миграции в системе существовала следующая цепочка:
|
||
|
||
```text
|
||
Exchange API
|
||
↓
|
||
exchangeInfo
|
||
↓
|
||
ExchangeService.get_exchange_symbols()
|
||
↓
|
||
list[ExchangeSymbol]
|
||
↓
|
||
ExchangeService.validate_symbol()
|
||
↓
|
||
SymbolValidationResult.symbol_info
|
||
↓
|
||
ExchangeSymbol | None
|
||
```
|
||
|
||
После внедрения нового Market Data Acquisition Layer в системе появился канонический pipeline:
|
||
|
||
```text
|
||
Exchange API
|
||
↓
|
||
Instrument Document Source
|
||
↓
|
||
Instrument Document Handler
|
||
↓
|
||
Instrument Feed
|
||
↓
|
||
Instrument Feed Registry
|
||
↓
|
||
Instrument Acquisition Service
|
||
↓
|
||
Instrument Store
|
||
↓
|
||
tuple[Instrument, ...]
|
||
```
|
||
|
||
На предыдущих этапах был создан новый канонический API:
|
||
|
||
```python
|
||
ExchangeService.get_instruments() -> tuple[Instrument, ...]
|
||
```
|
||
|
||
Build 021 перевёл первого внешнего потребителя:
|
||
|
||
```text
|
||
src/telegram/ui/currency_ui.py
|
||
```
|
||
|
||
с:
|
||
|
||
```python
|
||
get_exchange_symbols()
|
||
ExchangeSymbol
|
||
```
|
||
|
||
на:
|
||
|
||
```python
|
||
get_instruments()
|
||
Instrument
|
||
```
|
||
|
||
Build 022 продолжает миграцию на более глубоком уровне и переводит на `Instrument` механизм валидации торговых символов.
|
||
|
||
---
|
||
|
||
## 3. Состояние до Build 022
|
||
|
||
До Build 022 метод:
|
||
|
||
```python
|
||
ExchangeService.validate_symbol()
|
||
```
|
||
|
||
использовал:
|
||
|
||
```python
|
||
symbols = self.get_exchange_symbols()
|
||
```
|
||
|
||
и выполнял поиск среди:
|
||
|
||
```python
|
||
list[ExchangeSymbol]
|
||
```
|
||
|
||
Результат валидации имел контракт:
|
||
|
||
```python
|
||
@dataclass(slots=True)
|
||
class SymbolValidationResult:
|
||
requested_symbol: str
|
||
normalized_symbol: str
|
||
|
||
is_valid: bool
|
||
message: str
|
||
|
||
symbol_info: ExchangeSymbol | None
|
||
```
|
||
|
||
Таким образом, несмотря на наличие канонического `Instrument Store`, валидация торгового символа всё ещё зависела от legacy-проекции:
|
||
|
||
```text
|
||
Instrument Store
|
||
↓
|
||
Instrument
|
||
↓
|
||
compatibility mapper
|
||
↓
|
||
ExchangeSymbol
|
||
↓
|
||
validate_symbol()
|
||
```
|
||
|
||
Это создавало лишний промежуточный слой:
|
||
|
||
```text
|
||
Instrument → ExchangeSymbol → validation
|
||
```
|
||
|
||
вместо прямой канонической цепочки:
|
||
|
||
```text
|
||
Instrument → validation
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Целевое состояние Build 022
|
||
|
||
После Build 022 архитектура валидации выглядит следующим образом:
|
||
|
||
```text
|
||
Exchange API
|
||
↓
|
||
Market Data Acquisition
|
||
↓
|
||
Instrument Store
|
||
↓
|
||
ExchangeService.get_instruments()
|
||
↓
|
||
tuple[Instrument, ...]
|
||
↓
|
||
ExchangeService.validate_symbol()
|
||
↓
|
||
SymbolValidationResult
|
||
↓
|
||
Instrument | None
|
||
```
|
||
|
||
Главное архитектурное правило Build 022:
|
||
|
||
> `validate_symbol()` обязан работать непосредственно с каноническим справочником `Instrument` и не должен использовать legacy-метод `get_exchange_symbols()`.
|
||
|
||
---
|
||
|
||
## 5. Изменённые компоненты
|
||
|
||
В рамках Build 022 были изменены следующие файлы:
|
||
|
||
```text
|
||
app/src/integrations/exchange/models.py
|
||
|
||
app/src/integrations/exchange/service.py
|
||
|
||
app/tests/unit/integrations/exchange/test_service_validate_symbol.py
|
||
|
||
app/tests/unit/integrations/exchange/test_service_symbol_runtime_status.py
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Изменение `SymbolValidationResult`
|
||
|
||
### До Build 022
|
||
|
||
Поле:
|
||
|
||
```python
|
||
symbol_info
|
||
```
|
||
|
||
содержало:
|
||
|
||
```python
|
||
ExchangeSymbol | None
|
||
```
|
||
|
||
Контракт:
|
||
|
||
```python
|
||
@dataclass(slots=True)
|
||
class SymbolValidationResult:
|
||
requested_symbol: str
|
||
normalized_symbol: str
|
||
|
||
is_valid: bool
|
||
message: str
|
||
|
||
symbol_info: ExchangeSymbol | None
|
||
```
|
||
|
||
### После Build 022
|
||
|
||
Поле:
|
||
|
||
```python
|
||
symbol_info
|
||
```
|
||
|
||
содержит каноническую модель:
|
||
|
||
```python
|
||
Instrument | None
|
||
```
|
||
|
||
Итоговый контракт:
|
||
|
||
```python
|
||
@dataclass(slots=True)
|
||
class SymbolValidationResult:
|
||
requested_symbol: str
|
||
normalized_symbol: str
|
||
|
||
is_valid: bool
|
||
message: str
|
||
|
||
symbol_info: Instrument | None
|
||
```
|
||
|
||
Для type checking используется импорт:
|
||
|
||
```python
|
||
if TYPE_CHECKING:
|
||
from src.market_data.acquisition.models.instrument import Instrument
|
||
```
|
||
|
||
Это позволяет избежать ненужной runtime-зависимости модели exchange integration от реализации acquisition model при сохранении корректной типизации.
|
||
|
||
---
|
||
|
||
## 7. Изменение `validate_symbol()`
|
||
|
||
### До Build 022
|
||
|
||
Метод получал legacy-справочник:
|
||
|
||
```python
|
||
symbols = self.get_exchange_symbols()
|
||
```
|
||
|
||
и выполнял поиск среди объектов:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
```
|
||
|
||
Архитектурная цепочка имела вид:
|
||
|
||
```text
|
||
validate_symbol()
|
||
↓
|
||
get_exchange_symbols()
|
||
↓
|
||
compatibility projection
|
||
↓
|
||
ExchangeSymbol
|
||
```
|
||
|
||
### После Build 022
|
||
|
||
Метод получает канонический справочник:
|
||
|
||
```python
|
||
instruments = self.get_instruments()
|
||
```
|
||
|
||
и выполняет поиск непосредственно среди:
|
||
|
||
```python
|
||
Instrument
|
||
```
|
||
|
||
Новая цепочка:
|
||
|
||
```text
|
||
validate_symbol()
|
||
↓
|
||
get_instruments()
|
||
↓
|
||
Instrument Store
|
||
↓
|
||
Instrument
|
||
```
|
||
|
||
Legacy compatibility projection больше не участвует в валидации торгового символа.
|
||
|
||
---
|
||
|
||
## 8. Сохранённая семантика валидации
|
||
|
||
Build 022 не меняет пользовательскую и runtime-семантику `validate_symbol()`.
|
||
|
||
Сохранены следующие сценарии.
|
||
|
||
### 8.1. Пустой символ
|
||
|
||
Вход:
|
||
|
||
```text
|
||
" "
|
||
```
|
||
|
||
Результат:
|
||
|
||
```python
|
||
SymbolValidationResult(
|
||
requested_symbol="",
|
||
normalized_symbol="",
|
||
is_valid=False,
|
||
message="Символ пустой.",
|
||
symbol_info=None,
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
### 8.2. Mock mode
|
||
|
||
При:
|
||
|
||
```python
|
||
exchange_enabled=False
|
||
```
|
||
|
||
символ принимается без обращения к каноническому справочнику.
|
||
|
||
Пример результата:
|
||
|
||
```python
|
||
SymbolValidationResult(
|
||
requested_symbol="BTC/USD_LEVERAGE",
|
||
normalized_symbol="BTC/USD_LEVERAGE",
|
||
is_valid=True,
|
||
message="Mock mode active.",
|
||
symbol_info=None,
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
### 8.3. Регистронезависимый поиск
|
||
|
||
Следующие значения считаются эквивалентными:
|
||
|
||
```text
|
||
BTC/USD_LEVERAGE
|
||
btc/usd_leverage
|
||
```
|
||
|
||
---
|
||
|
||
### 8.4. Игнорирование внешних пробелов
|
||
|
||
Следующие значения считаются эквивалентными:
|
||
|
||
```text
|
||
BTC/USD_LEVERAGE
|
||
btc/usd_leverage
|
||
```
|
||
|
||
---
|
||
|
||
### 8.5. Поддержка encoded separator
|
||
|
||
Поддерживается представление:
|
||
|
||
```text
|
||
BTC%2FUSD_LEVERAGE
|
||
```
|
||
|
||
наряду с:
|
||
|
||
```text
|
||
BTC/USD_LEVERAGE
|
||
```
|
||
|
||
---
|
||
|
||
### 8.6. Поддержка внутренних пробелов
|
||
|
||
Поддерживается нормализация значения:
|
||
|
||
```text
|
||
btc / usd_leverage
|
||
```
|
||
|
||
до:
|
||
|
||
```text
|
||
BTC/USD_LEVERAGE
|
||
```
|
||
|
||
---
|
||
|
||
### 8.7. Отсутствующий инструмент
|
||
|
||
Если инструмент отсутствует в каноническом справочнике, возвращается неуспешный результат:
|
||
|
||
```python
|
||
SymbolValidationResult(
|
||
requested_symbol="XRP/USD_LEVERAGE",
|
||
normalized_symbol="XRP/USD_LEVERAGE",
|
||
is_valid=False,
|
||
message=(
|
||
"Символ 'XRP/USD_LEVERAGE' "
|
||
"не найден в exchangeInfo."
|
||
),
|
||
symbol_info=None,
|
||
)
|
||
```
|
||
|
||
Сообщение сохранено для обратной совместимости.
|
||
|
||
---
|
||
|
||
### 8.8. Сохранение приоритета кандидатов
|
||
|
||
Если разные исходные значения после нормализации могут соответствовать одному символу, сохраняется существующий порядок выбора кандидатов.
|
||
|
||
В частности, точное исходное представление продолжает иметь приоритет в предусмотренных legacy-сценариях.
|
||
|
||
---
|
||
|
||
### 8.9. Сохранение первого дубликата
|
||
|
||
Если канонический справочник содержит несколько эквивалентных инструментов, возвращается первый подходящий объект согласно существующему порядку справочника.
|
||
|
||
---
|
||
|
||
## 9. Канонический объект в результате валидации
|
||
|
||
После Build 022 успешный результат:
|
||
|
||
```python
|
||
validation = service.validate_symbol(
|
||
"BTC/USD_LEVERAGE"
|
||
)
|
||
```
|
||
|
||
содержит:
|
||
|
||
```python
|
||
validation.symbol_info
|
||
```
|
||
|
||
типа:
|
||
|
||
```python
|
||
Instrument
|
||
```
|
||
|
||
а не:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
```
|
||
|
||
Дополнительно сохранена identity-семантика.
|
||
|
||
Если канонический справочник содержит объект:
|
||
|
||
```python
|
||
instrument
|
||
```
|
||
|
||
то после успешной валидации выполняется:
|
||
|
||
```python
|
||
validation.symbol_info is instrument
|
||
```
|
||
|
||
То есть `validate_symbol()`:
|
||
|
||
- не создаёт копию `Instrument`;
|
||
- не создаёт `ExchangeSymbol`;
|
||
- не выполняет compatibility mapping;
|
||
- возвращает исходный канонический объект из справочника.
|
||
|
||
---
|
||
|
||
## 10. Перевод runtime-status
|
||
|
||
Метод:
|
||
|
||
```python
|
||
ExchangeService.get_symbol_runtime_status()
|
||
```
|
||
|
||
использует:
|
||
|
||
```python
|
||
validation = self.validate_symbol(symbol_to_use)
|
||
```
|
||
|
||
и получает статус торгового инструмента через:
|
||
|
||
```python
|
||
validation.symbol_info.status
|
||
```
|
||
|
||
После изменения контракта:
|
||
|
||
```python
|
||
SymbolValidationResult.symbol_info
|
||
```
|
||
|
||
runtime-status теперь фактически получает канонический:
|
||
|
||
```python
|
||
Instrument
|
||
```
|
||
|
||
вместо legacy:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
```
|
||
|
||
При этом внешний контракт runtime-status не изменён.
|
||
|
||
Сохраняются:
|
||
|
||
```text
|
||
ExchangeRuntimeStatus
|
||
ExchangeStatusCode
|
||
get_symbol_market_status()
|
||
```
|
||
|
||
и существующая классификация состояний рынка.
|
||
|
||
---
|
||
|
||
## 11. Сохранённая классификация runtime-status
|
||
|
||
Build 022 сохраняет существующую классификацию биржевых статусов.
|
||
|
||
### Открытый рынок
|
||
|
||
Следующие статусы классифицируются как открытый рынок:
|
||
|
||
```text
|
||
TRADING
|
||
OPEN
|
||
ACTIVE
|
||
ENABLED
|
||
ONLINE
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
ExchangeStatusCode.OPEN
|
||
reason = "market_open"
|
||
```
|
||
|
||
---
|
||
|
||
### Перерыв или остановка торгов
|
||
|
||
Следующие статусы классифицируются как перерыв:
|
||
|
||
```text
|
||
BREAK
|
||
CLOSED
|
||
HALT
|
||
HALTED
|
||
PAUSED
|
||
SUSPENDED
|
||
DISABLED
|
||
SETTLING
|
||
POST_ONLY
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
ExchangeStatusCode.BREAK
|
||
reason = "market_break"
|
||
```
|
||
|
||
---
|
||
|
||
### Инструмент недоступен для торговли
|
||
|
||
Следующие статусы классифицируются как неторгуемые:
|
||
|
||
```text
|
||
NOT_TRADABLE
|
||
TRADING_DISABLED
|
||
MARKET_DISABLED
|
||
UNAVAILABLE_FOR_TRADING
|
||
CLOSE_ONLY
|
||
REDUCE_ONLY
|
||
VIEW_ONLY
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
ExchangeStatusCode.BREAK
|
||
reason = "market_not_tradable"
|
||
```
|
||
|
||
---
|
||
|
||
### Неизвестный статус
|
||
|
||
Неизвестные значения классифицируются как:
|
||
|
||
```text
|
||
ExchangeStatusCode.UNKNOWN
|
||
reason = "market_status_unknown"
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Сохранение проверки freshness
|
||
|
||
Build 022 не изменяет существующую проверку свежести рыночных данных.
|
||
|
||
Для открытого рынка runtime-status продолжает проверять:
|
||
|
||
```python
|
||
get_fresh_market_snapshot()
|
||
```
|
||
|
||
Если данные устарели, возвращается:
|
||
|
||
```text
|
||
ExchangeStatusCode.BREAK
|
||
reason = "market_data_stale"
|
||
raw_status = "STALE_MARKET_DATA"
|
||
```
|
||
|
||
Проверка freshness выполняется только для инструмента, который сначала классифицирован как открытый для торговли.
|
||
|
||
Для закрытого или приостановленного рынка получение snapshot не выполняется.
|
||
|
||
---
|
||
|
||
## 13. Сохранение legacy runtime-контракта
|
||
|
||
Метод:
|
||
|
||
```python
|
||
get_symbol_market_status()
|
||
```
|
||
|
||
продолжает возвращать legacy dictionary contract.
|
||
|
||
Пример:
|
||
|
||
```python
|
||
{
|
||
"code": "BREAK",
|
||
"status": "BREAK",
|
||
"symbol": "BTC/USD",
|
||
"is_open": False,
|
||
"is_available": True,
|
||
"is_auth_ok": True,
|
||
"title": "Перерыв в торгах",
|
||
"message": "Торги временно остановлены.",
|
||
"ui_line": "⏸️ Перерыв в торгах",
|
||
"reason": "market_break",
|
||
"raw_status": "BREAK",
|
||
"raw_error": None,
|
||
}
|
||
```
|
||
|
||
Таким образом, Build 022 меняет внутренний источник reference data, но не ломает существующий внешний runtime-контракт старого бота.
|
||
|
||
---
|
||
|
||
## 14. Архитектурные ограничения, закреплённые тестами
|
||
|
||
Build 022 вводит и закрепляет следующие архитектурные правила.
|
||
|
||
### Правило 1
|
||
|
||
`validate_symbol()` обязан использовать:
|
||
|
||
```python
|
||
get_instruments()
|
||
```
|
||
|
||
---
|
||
|
||
### Правило 2
|
||
|
||
`validate_symbol()` не должен использовать:
|
||
|
||
```python
|
||
get_exchange_symbols()
|
||
```
|
||
|
||
---
|
||
|
||
### Правило 3
|
||
|
||
`validate_symbol()` не должен напрямую вызывать:
|
||
|
||
```python
|
||
_load_instruments_via_acquisition()
|
||
```
|
||
|
||
Правильная цепочка:
|
||
|
||
```text
|
||
validate_symbol()
|
||
↓
|
||
get_instruments()
|
||
↓
|
||
Instrument Store
|
||
↓
|
||
Acquisition Pipeline при необходимости
|
||
```
|
||
|
||
Неправильная цепочка:
|
||
|
||
```text
|
||
validate_symbol()
|
||
↓
|
||
_load_instruments_via_acquisition()
|
||
```
|
||
|
||
---
|
||
|
||
### Правило 4
|
||
|
||
Успешная валидация должна возвращать исходный канонический:
|
||
|
||
```python
|
||
Instrument
|
||
```
|
||
|
||
---
|
||
|
||
### Правило 5
|
||
|
||
Legacy API:
|
||
|
||
```python
|
||
get_exchange_symbols()
|
||
```
|
||
|
||
может временно существовать только как compatibility layer для ещё не переведённых потребителей.
|
||
|
||
Он больше не является каноническим источником данных для `validate_symbol()`.
|
||
|
||
---
|
||
|
||
## 15. Тестовое покрытие `validate_symbol()`
|
||
|
||
После Build 022 тесты подтверждают следующие сценарии:
|
||
|
||
```text
|
||
пустой символ;
|
||
mock mode;
|
||
точное совпадение Instrument;
|
||
регистронезависимый поиск;
|
||
игнорирование внешних пробелов;
|
||
поддержка encoded separator;
|
||
поддержка внутренних пробелов;
|
||
отсутствующий инструмент;
|
||
возврат исходного объекта Instrument;
|
||
нормализация фактически найденного символа;
|
||
сохранение success message;
|
||
единственный вызов get_instruments();
|
||
запрет использования legacy get_exchange_symbols();
|
||
запрет прямого вызова acquisition;
|
||
сохранение приоритета кандидатов;
|
||
возврат первого дубликата;
|
||
сохранение типа SymbolValidationResult;
|
||
возврат канонического Instrument.
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
18 passed in 0.11s
|
||
```
|
||
|
||
---
|
||
|
||
## 16. Тестовое покрытие runtime-status
|
||
|
||
Тесты runtime-status были переведены с:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
```
|
||
|
||
на:
|
||
|
||
```python
|
||
Instrument
|
||
```
|
||
|
||
и подтверждают сохранение существующей runtime-семантики.
|
||
|
||
Проверяются:
|
||
|
||
```text
|
||
mock status;
|
||
explicit symbol;
|
||
default symbol;
|
||
invalid symbol;
|
||
exchange unavailable;
|
||
классификация открытого рынка;
|
||
классификация остановленного рынка;
|
||
классификация неторгуемого инструмента;
|
||
неизвестные статусы;
|
||
freshness только для открытого рынка;
|
||
stale market data;
|
||
граница freshness threshold;
|
||
отсутствующий age;
|
||
ошибка получения snapshot;
|
||
использование нормализованного символа;
|
||
сохранение legacy dictionary contract.
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
34 passed in 0.09s
|
||
```
|
||
|
||
---
|
||
|
||
## 17. Проверка компиляции
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m py_compile \
|
||
src/integrations/exchange/models.py \
|
||
src/integrations/exchange/service.py \
|
||
tests/unit/integrations/exchange/test_service_validate_symbol.py \
|
||
tests/unit/integrations/exchange/test_service_symbol_runtime_status.py
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
Ошибок компиляции нет.
|
||
```
|
||
|
||
---
|
||
|
||
## 18. Полный regression suite
|
||
|
||
После завершения Build 022 выполнен полный набор тестов:
|
||
|
||
```bash
|
||
python -m pytest -q
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
462 passed in 0.24s
|
||
```
|
||
|
||
Это подтверждает, что переход:
|
||
|
||
```text
|
||
validate_symbol()
|
||
↓
|
||
ExchangeSymbol → Instrument
|
||
```
|
||
|
||
не нарушил существующую функциональность проекта.
|
||
|
||
---
|
||
|
||
## 19. Состояние legacy-слоя после Build 022
|
||
|
||
После Build 022 legacy-компоненты всё ещё существуют:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
get_exchange_symbols()
|
||
map_instruments_to_exchange_symbols()
|
||
_exchange_symbols_projection_cache
|
||
```
|
||
|
||
Это ожидаемое состояние.
|
||
|
||
Они сохраняются для ещё не переведённых потребителей и не должны удаляться до завершения их поэтапной миграции.
|
||
|
||
Текущая архитектура:
|
||
|
||
```text
|
||
┌─────────────────────┐
|
||
│ Instrument Store │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ get_instruments() │
|
||
└──────────┬──────────┘
|
||
│
|
||
┌─────────────────┴─────────────────┐
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────────┐ ┌─────────────────────┐
|
||
│ Canonical consumers │ │ Compatibility layer │
|
||
└─────────────────────┘ └──────────┬──────────┘
|
||
│ │
|
||
│ ▼
|
||
│ ┌─────────────────────┐
|
||
│ │ ExchangeSymbol │
|
||
│ └──────────┬──────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────────┐ ┌─────────────────────┐
|
||
│ currency_ui.py │ │ Legacy consumers │
|
||
│ validate_symbol() │ │ not migrated yet │
|
||
│ runtime-status │ │ │
|
||
└─────────────────────┘ └─────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 20. Что не входило в Build 022
|
||
|
||
Build 022 намеренно не выполняет полное удаление legacy-слоя.
|
||
|
||
В рамках этого Build не удаляются:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
get_exchange_symbols()
|
||
map_instrument_to_exchange_symbol()
|
||
map_instruments_to_exchange_symbols()
|
||
_exchange_symbols_projection_cache
|
||
```
|
||
|
||
Также Build 022 не выполняет массовый перевод всех оставшихся потребителей.
|
||
|
||
Не входят в scope Build 022 отдельные runtime-потребители:
|
||
|
||
```text
|
||
src/integrations/exchange/market_stream.py
|
||
|
||
src/integrations/exchange/market_data_runner.py
|
||
|
||
src/telegram/handlers/market.py
|
||
```
|
||
|
||
Их миграция должна выполняться поэтапно в следующих Build с отдельной проверкой контрактов и regression suite.
|
||
|
||
---
|
||
|
||
## 21. Итоговое состояние после Build 022
|
||
|
||
После завершения Build 022 достигнуто следующее состояние:
|
||
|
||
```text
|
||
Instrument Acquisition Pipeline
|
||
↓
|
||
Instrument Store
|
||
↓
|
||
get_instruments()
|
||
↓
|
||
├── currency_ui.py
|
||
│
|
||
├── validate_symbol()
|
||
│ ↓
|
||
│ SymbolValidationResult
|
||
│ ↓
|
||
│ Instrument | None
|
||
│
|
||
└── runtime-status
|
||
```
|
||
|
||
Ключевой результат:
|
||
|
||
> Валидация торгового символа больше не зависит от legacy-модели `ExchangeSymbol` и работает непосредственно с каноническим справочником `Instrument`.
|
||
|
||
При этом:
|
||
|
||
```text
|
||
старый бот продолжает работать;
|
||
внешний контракт validate_symbol() сохранён;
|
||
runtime-status сохранён;
|
||
legacy dictionary contract сохранён;
|
||
полный regression suite проходит;
|
||
legacy compatibility layer остаётся доступен для ещё не переведённых потребителей.
|
||
```
|
||
|
||
---
|
||
|
||
## 22. Критерии завершения Build 022
|
||
|
||
Build 022 считается завершённым, поскольку выполнены все критерии:
|
||
|
||
- [x] `SymbolValidationResult.symbol_info` переведён на `Instrument | None`.
|
||
- [x] `validate_symbol()` использует `get_instruments()`.
|
||
- [x] `validate_symbol()` больше не использует `get_exchange_symbols()`.
|
||
- [x] `validate_symbol()` не вызывает acquisition pipeline напрямую.
|
||
- [x] Успешная валидация возвращает исходный канонический `Instrument`.
|
||
- [x] Сохранена существующая нормализация торговых символов.
|
||
- [x] Сохранена семантика mock mode.
|
||
- [x] Сохранена семантика invalid symbol.
|
||
- [x] Сохранён runtime-status.
|
||
- [x] Сохранена классификация биржевых статусов.
|
||
- [x] Сохранена freshness-проверка.
|
||
- [x] Сохранён legacy dictionary contract.
|
||
- [x] Целевые тесты `validate_symbol()` проходят: `18 passed`.
|
||
- [x] Целевые тесты runtime-status проходят: `34 passed`.
|
||
- [x] `py_compile` проходит без ошибок.
|
||
- [x] Полный regression suite проходит: `462 passed`.
|
||
|
||
---
|
||
|
||
## 23. Следующий этап
|
||
|
||
Следующий этап:
|
||
|
||
```text
|
||
Build 023+ — Поэтапный перевод остальных потребителей
|
||
```
|
||
|
||
Перед началом следующего Build необходимо определить очередную минимальную группу потребителей, которая всё ещё зависит от legacy reference data:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
get_exchange_symbols()
|
||
SymbolValidationResult.symbol_info
|
||
```
|
||
|
||
Рекомендуемый принцип дальнейшей миграции:
|
||
|
||
```text
|
||
один логически связанный набор потребителей
|
||
↓
|
||
перевод на Instrument
|
||
↓
|
||
целевые тесты
|
||
↓
|
||
полный regression suite
|
||
↓
|
||
grep-аудит
|
||
↓
|
||
документ Build
|
||
↓
|
||
следующий этап
|
||
```
|
||
|
||
Полное удаление:
|
||
|
||
```python
|
||
ExchangeSymbol
|
||
get_exchange_symbols()
|
||
map_instruments_to_exchange_symbols()
|
||
_exchange_symbols_projection_cache
|
||
```
|
||
|
||
должно выполняться только после того, как последний реальный потребитель будет переведён на канонический `Instrument`.
|
||
|
||
---
|
||
|
||
## 24. Финальный результат Build 022
|
||
|
||
```text
|
||
Build 022: ЗАВЕРШЁН
|
||
|
||
Канонический справочник:
|
||
Instrument Store
|
||
↓
|
||
get_instruments()
|
||
↓
|
||
Instrument
|
||
|
||
Переведённые потребители:
|
||
├── currency_ui.py
|
||
├── validate_symbol()
|
||
└── runtime-status через validate_symbol()
|
||
|
||
Legacy compatibility:
|
||
├── ExchangeSymbol
|
||
├── get_exchange_symbols()
|
||
├── compatibility mapper
|
||
└── projection cache
|
||
|
||
Статус legacy compatibility:
|
||
Временно сохранён для ещё не переведённых потребителей.
|
||
|
||
Regression:
|
||
462 passed.
|
||
``` |