# bquant.analysis.zones.models — Модели глобальных свингов

Этот раздел описывает новые структуры данных, которые поддерживают режим глобального расчёта свингов в пайплайне анализа зон.

## `SwingPoint`

`SwingPoint` — дата-класс, описывающий одну точку свинга (пик или впадину), обнаруженную на **глобальном** ряду котировок. Экземпляры создаются стратегиями свингов один раз и переиспользуются для всех зон.

### Поля

| Поле | Тип | Описание |
| ---- | --- | -------- |
| `point_id` | `int` | Уникальный идентификатор точки в последовательности свингов. |
| `timestamp` | `datetime` | Метка времени, совпадающая со значением индекса исходного DataFrame. |
| `index` | `int` | Позиция точки в полном датасете (iloc). |
| `price` | `float` | Цена инструмента в момент свинга. |
| `swing_type` | `str` | Тип точки: `'peak'` или `'trough'`. |
| `amplitude_to_next` | `Optional[float]` | Процентное изменение до следующей точки свинга (если она существует). |
| `duration_to_next` | `Optional[int]` | Количество баров до следующего свинга. |
| `strategy_name` | `str` | Название стратегии, обнаружившей свинг. |
| `strategy_params` | `Dict[str, Any]` | Параметры стратегии для трассируемости (по умолчанию пустой словарь). |

### Основные особенности

- Структура полностью сериализуема: используется в `SwingContext.to_dict()` для кэширования.
- Совместима с любыми стратегиями свингов — поля не зависят от конкретного алгоритма.
- Позволяет сохранять не только геометрию свинга, но и метаданные стратегии (например, допуски ZigZag).

## `SwingContext`

`SwingContext` агрегирует все `SwingPoint`, рассчитанные для полного набора данных. Контекст хранится на уровне пайплайна и передаётся в зоны, что устраняет повторные расчёты.

### Ключевые поля

- `swing_points: List[SwingPoint]` — упорядоченный список свингов.
- `indices: np.ndarray` — отсортированный массив `iloc`-индексов, используемый для быстрых срезов через `bisect`.
- `full_data_length: int` — размер исходного датафрейма (для валидации).
- `strategy_name: str` и `strategy_params: Dict[str, Any]` — метаданные стратегии.

### Методы

- `slice(start_idx: int, end_idx: int) -> List[SwingPoint]`
  - Возвращает точки, попадающие в диапазон зоны, **с захватом соседей** слева и справа. Это обеспечивает корректные амплитуды при вычислении метрик.
- `get_swings_for_zone(zone: ZoneInfo) -> List[SwingPoint]`
  - Удобный враппер над `slice`, использующий `zone.start_idx` и `zone.end_idx`.
- `to_dict() -> Dict[str, Any]`
  - Сериализует весь контекст в словарь. Используется кэшем и для трассировки.

### Типичные сценарии

```python
from bquant.analysis.zones.models import SwingContext

context = strategy.calculate_global(prepared_df)
first_zone = result.zones[0]
zone_swings = context.get_swings_for_zone(first_zone)
```

## `ZoneInfo` и глобальные свинги

`ZoneInfo` теперь содержит поле `swing_context: Optional[SwingContext]`. Пайплайн вызывает `_inject_swing_context()` сразу после детекции зон (см. соответствующий раздел), поэтому каждая зона получает ссылку на общий контекст.

### Поле `swing_context`

- Устанавливается только в режиме `swing_scope="global"`.
- Остаётся `None` в режиме `per_zone`, что сохраняет обратную совместимость.
- Попадает в метод `to_analyzer_format()`, поэтому все анализаторы признаков имеют доступ к глобальным свингам.

### Метод `get_zone_swings()`

`ZoneInfo.get_zone_swings()` извлекает список `SwingPoint` для конкретной зоны. Если контекст не инъектирован, метод возвращает пустой список — таким образом старые сценарии не ломаются.

```python
zone = result.zones[0]
for point in zone.get_zone_swings():
    print(point.timestamp, point.price, point.swing_type)
```

Используйте этот метод в пользовательских метриках и визуализациях, чтобы работать с уже рассчитанными глобальными свингами без повторных вычислений.
