# 07.4.4.1.6 — Signal Aging & Runtime Expiration

## Цель этапа

Добавить слой контроля возраста runtime-состояния автоторговли, чтобы сигналы, market analysis и внутренние окна стратегий не жили бесконечно и не влияли на новые решения после пауз, смены актива, смены стратегии или простоя рынка.

Этап закрывает проблему “устаревшего runtime”:

- старый BUY/SELL не должен продолжаться после долгой паузы;
- READY-состояние не должно сохраняться бесконечно;
- market diagnostics не должны визуально залипать;
- runtime-окна стратегий не должны использовать старые цены после простоя;
- HOLD lifecycle должен оставаться живым и понятным в UI.

---

## Что было до этапа

До внедрения Signal Aging runtime-состояние было логически изолировано по активам и стратегиям, но не имело срока жизни.

Это означало, что:

- `_price_window` мог хранить старые цены;
- `last_signal` мог оставаться актуальным дольше, чем фактический рынок;
- `READY` мог визуально выглядеть свежим, хотя сигнал уже устарел;
- market diagnostics могли оставаться на экране после смены условий;
- пользователь видел состояние, но не всегда понимал, насколько оно свежее.

---

## Что внедрено

### 1. Signal aging fields в AutoTradeState

В состояние автоторговли добавлены поля для отслеживания возраста runtime:

```python
signal_updated_at
market_analysis_updated_at
runtime_expired_reason
runtime_expired_message
```

Теперь состояние знает:

- когда последний раз обновлялся сигнал;
- когда последний раз обновлялась аналитика рынка;
- была ли runtime-информация сброшена по expiration;
- почему она была сброшена.

---

### 2. Signal TTL

Добавлен TTL для сигналов:

```python
_signal_ttl_seconds = 90
```

Если сигнал живёт дольше допустимого времени без актуального обновления, runtime сбрасывается.

Это защищает от ситуаций:

```text
BUY появился
↓
бот/рынок/цикл остановился
↓
через долгое время BUY всё ещё считается актуальным
```

Теперь такой сигнал считается устаревшим и должен быть пересобран заново.

---

### 3. Market analysis TTL

Добавлен TTL для market diagnostics:

```python
_market_analysis_ttl_seconds = 180
```

Если анализ рынка устарел, очищаются:

```python
market_state
market_trend
market_volatility
market_analysis_interval
market_analysis_reason
entry_block_reason
entry_block_message
```

Это предотвращает визуальное залипание старого состояния рынка.

---

### 4. Runtime expiration handler

В AutoTradeService добавлен метод:

```python
_expire_runtime_if_needed()
```

Он проверяет возраст:

- текущего сигнала;
- последнего market analysis;
- runtime diagnostics.

При необходимости он сбрасывает устаревшие данные и подготавливает state к новой оценке рынка.

---

### 5. Runtime expiration logging

Добавлено событие журнала:

```python
runtime_expired
```

Оно используется для фиксации случаев, когда runtime был сброшен по TTL.

Логика защищена от spam logging одинаковых expiration-событий.

---

### 6. Runtime window TTL для TREND

В TrendStrategy добавлен TTL live-окна:

```python
_window_ttl_seconds = 60
_price_window_updated_at
```

Теперь live-окно TREND очищается, если между обновлениями был слишком большой разрыв.

Это защищает от анализа старых цен как будто они свежие.

---

### 7. Runtime window TTL для SCALP

В ScalpStrategy добавлен отдельный TTL:

```python
_window_ttl_seconds = 30
_price_window_updated_at
```

SCALP чувствительнее к времени, поэтому его окно живёт меньше.

---

### 8. Runtime reset теперь очищает не только цены

`reset_runtime()` теперь очищает:

- `_price_window`;
- `_price_window_updated_at`.

Это важно, потому что сам факт времени последнего обновления тоже является runtime-состоянием.

---

### 9. UI продолжает показывать HOLD duration

Строка:

```text
Сигнал 🟡 HOLD · 27с
```

оставлена намеренно.

Это не торговый сигнал, а индикатор живого runtime-цикла:

- экран обновляется;
- HOLD не залип;
- цикл автоторговли работает;
- пользователь видит возраст текущего режима ожидания.

---

### 10. Compact Telegram UI

В рамках проверки этапа UI был приведён к более короткому виду.

Market state теперь отображается компактно:

```python
"TREND_UP": "📈 Тренд · Вверх"
"TREND_DOWN": "📉 Тренд · Вниз"
"RANGE": "🟰 Рынок · Флэт"
"HIGH_VOLATILITY": "⚠️ Рынок · Высокая волатильность"
"LOW_VOLATILITY": "🟰 Рынок · Низкая активность"
```

Entry diagnostics разделены по смыслу:

```text
Ожидание · слабый импульс
Вход · слабый импульс
```

---

## Проверка этапа

Проверено на live UI:

- HOLD timer работает;
- market state отображается компактно;
- после runtime reset старые данные не протекают;
- UI не залипает на старом активе;
- entry diagnostics стали короче;
- runtime state не смешивается между активами;
- выбранный актив отображается корректно;
- подготовка ордера продолжает рассчитываться штатно.

---

## Что обнаружено дополнительно

На LTC был выявлен uncovered HOLD diagnostic сценарий:

```text
Сигнал 🟡 HOLD
📉 Тренд · Вниз
```

При этом UI не показал причину, почему при нисходящем тренде не был выдан SELL.

Причина: не все HOLD-ветки TrendStrategy передают `entry_block_reason` и `entry_block_message`.

Это не блокер этапа 07.4.4.1.6, потому что относится не к aging, а к полноте диагностического слоя стратегии.

Рекомендуется вынести в следующий этап:

```text
Advanced Market Diagnostics Layer
```

---

## Итог этапа

Этап 07.4.4.1.6 добавил фундамент runtime expiration:

- сигналы получили возраст;
- market analysis получил возраст;
- runtime-окна стратегий получили TTL;
- stale state теперь можно сбрасывать;
- UI показывает живое состояние runtime;
- система подготовлена к adaptive thresholds и расширенной диагностике входа.