diff --git a/app/src/telegram/handlers/auto/main.py b/app/src/telegram/handlers/auto/main.py index 1ef40ea..b4603d6 100644 --- a/app/src/telegram/handlers/auto/main.py +++ b/app/src/telegram/handlers/auto/main.py @@ -126,12 +126,12 @@ async def auto_start(callback: CallbackQuery) -> None: _, message = service.start() - AutoTradeRunner.start() - if callback.message is not None: await _prepare_auto_from_callback(callback) await render_auto_screen(callback.message, edit_mode=True) + AutoTradeRunner.start() + await callback.answer(message) @@ -153,12 +153,12 @@ async def auto_observe(callback: CallbackQuery) -> None: _, message = service.observe() - AutoTradeRunner.start() - if callback.message is not None: await _prepare_auto_from_callback(callback) await render_auto_screen(callback.message, edit_mode=True) + AutoTradeRunner.start() + await callback.answer(message) diff --git a/app/src/trading/auto/runner.py b/app/src/trading/auto/runner.py index 63d7b1a..8803c0f 100644 --- a/app/src/trading/auto/runner.py +++ b/app/src/trading/auto/runner.py @@ -179,16 +179,46 @@ class AutoTradeRunner: MarketDataRunner.stop("auto") break - service.run_cycle() + try: + service.run_cycle() + except Exception as exc: + cls._log_refresh_error( + "auto_run_cycle_error", + { + "error": str(exc), + "error_type": type(exc).__name__, + "symbol": state.symbol, + "strategy": state.strategy, + "status": state.status, + }, + ) current_event_version = EventBus.version() has_important_event = current_event_version != cls._last_event_version if has_important_event: cls._last_event_version = current_event_version - await cls._handle_important_event(state) + try: + await cls._handle_important_event(state) + except Exception as exc: + cls._log_refresh_error( + "auto_event_handler_error", + { + "error": str(exc), + "error_type": type(exc).__name__, + }, + ) - await cls._refresh_screen(force=has_important_event) + try: + await cls._refresh_screen(force=has_important_event) + except Exception as exc: + cls._log_refresh_error( + "auto_refresh_loop_error", + { + "error": str(exc), + "error_type": type(exc).__name__, + }, + ) await asyncio.sleep(cls._analysis_interval_seconds) diff --git a/app/src/trading/strategies/scalp.py b/app/src/trading/strategies/scalp.py index 1410cca..8606b88 100644 --- a/app/src/trading/strategies/scalp.py +++ b/app/src/trading/strategies/scalp.py @@ -53,6 +53,8 @@ class ScalpStrategy: "strategy": self.name, "symbol": context.symbol, "error": str(exc), + "entry_block_reason": "MARKET_PRICE_ERROR", + "entry_block_message": "нет данных рынка", }, ) @@ -75,17 +77,25 @@ class ScalpStrategy: if len(prices) > self._window_size: prices.pop(0) + base_payload = { + "strategy": self.name, + "symbol": symbol, + "price": current_price, + "runtime_window_ttl_seconds": self._window_ttl_seconds, + "runtime_window_size": len(prices), + } + if len(prices) < self._window_size: return SignalResult( signal=SignalType.HOLD, reason="Недостаточно данных для SCALP.", confidence=0.0, payload={ - "strategy": self.name, - "symbol": symbol, - "price": current_price, + **base_payload, "window_size": len(prices), "required_window_size": self._window_size, + "entry_block_reason": "NOT_ENOUGH_LIVE_DATA", + "entry_block_message": "мало данных", }, ) @@ -98,9 +108,10 @@ class ScalpStrategy: reason="Некорректная стартовая цена в окне SCALP.", confidence=0.0, payload={ - "strategy": self.name, - "symbol": symbol, + **base_payload, "prices": prices, + "entry_block_reason": "INVALID_WINDOW_PRICE", + "entry_block_message": "ошибка цены", }, ) @@ -108,8 +119,7 @@ class ScalpStrategy: direction_ratio = self._direction_ratio(prices, change_percent) payload = { - "strategy": self.name, - "symbol": symbol, + **base_payload, "first_price": first_price, "current_price": last_price, "change_percent": round(change_percent, 5), @@ -141,11 +151,23 @@ class ScalpStrategy: payload=payload, ) + expected_direction = "BUY" if change_percent >= 0 else "SELL" + entry_block_reason = ( + "WEAK_UP_IMPULSE" + if expected_direction == "BUY" + else "WEAK_DOWN_IMPULSE" + ) + return SignalResult( signal=SignalType.HOLD, reason="SCALP-импульс недостаточно сильный.", confidence=0.0, - payload=payload, + payload={ + **payload, + "entry_block_reason": entry_block_reason, + "entry_block_message": "слабый импульс", + "expected_direction": expected_direction, + }, ) def _direction_ratio(self, prices: list[float], change_percent: float) -> float: diff --git a/app/src/trading/strategies/trend.py b/app/src/trading/strategies/trend.py index 91cd4ef..ca8db06 100644 --- a/app/src/trading/strategies/trend.py +++ b/app/src/trading/strategies/trend.py @@ -64,6 +64,8 @@ class TrendStrategy: "symbol": context.symbol, "error": str(exc), "market_analysis": market.payload, + "entry_block_reason": "MARKET_SNAPSHOT_ERROR", + "entry_block_message": "нет данных рынка", }, ) @@ -80,6 +82,8 @@ class TrendStrategy: "symbol": symbol, "snapshot": snapshot, "market_analysis": market.payload, + "entry_block_reason": "INVALID_MARKET_PRICE", + "entry_block_message": "нет цены", }, ) @@ -138,6 +142,8 @@ class TrendStrategy: **base_payload, "window_size": len(prices), "required_window_size": self._window_size, + "entry_block_reason": "NOT_ENOUGH_LIVE_DATA", + "entry_block_message": "мало данных", }, ) @@ -152,6 +158,8 @@ class TrendStrategy: payload={ **base_payload, "prices": prices, + "entry_block_reason": "INVALID_WINDOW_PRICE", + "entry_block_message": "ошибка цены", }, ) @@ -185,7 +193,12 @@ class TrendStrategy: signal=SignalType.HOLD, reason="TREND_UP есть, но live-импульс вверх недостаточно сильный.", confidence=0.0, - payload=payload, + payload={ + **payload, + "entry_block_reason": "WEAK_UP_IMPULSE", + "entry_block_message": "слабый импульс", + "expected_direction": "BUY", + }, ) if market.state == MarketState.TREND_DOWN: @@ -204,14 +217,23 @@ class TrendStrategy: signal=SignalType.HOLD, reason="TREND_DOWN есть, но live-импульс вниз недостаточно сильный.", confidence=0.0, - payload=payload, + payload={ + **payload, + "entry_block_reason": "WEAK_DOWN_IMPULSE", + "entry_block_message": "слабый импульс", + "expected_direction": "SELL", + }, ) return SignalResult( signal=SignalType.HOLD, reason=f"Market state не подходит для TREND: {market.state.value}.", confidence=0.0, - payload=payload, + payload={ + **payload, + "entry_block_reason": "MARKET_STATE_NOT_TREND", + "entry_block_message": "рынок флэт", + }, ) def _analysis_price(self, snapshot: dict[str, object]) -> float: diff --git a/docs/roadmap/master-roadmap.md b/docs/roadmap/master-roadmap.md index 69dc324..33dc0c3 100644 --- a/docs/roadmap/master-roadmap.md +++ b/docs/roadmap/master-roadmap.md @@ -519,6 +519,62 @@ - подготовлена база для adaptive thresholds - подготовлена база для signal freshness-aware execution +#### 07.4.4.1.7 ✅ Live Market Runtime & Advanced Trend Diagnostics +- внедрён полноценный live market runtime pipeline +- добавлен websocket-based realtime market runtime +- добавлен REST fallback для market runtime +- внедрён runtime-aware MarketPriceCache +- cache переведён на isolation по runtime_key +- добавлен market snapshot layer в ExchangeService +- добавлены get_market_snapshot и get_execution_snapshot +- добавлен get_fresh_market_snapshot +- добавлен refresh_market_snapshot_cache +- внедрена модель ExecutionPriceSnapshot +- execution pipeline получил поддержку bid/ask +- execution pipeline получил freshness tracking +- execution pipeline получил source tracking +- MarketDataRunner теперь обновляет runtime cache +- websocket runtime теперь автоматически reconnect'ится +- websocket runtime теперь безопасно fallback'ится в REST +- TrendStrategy переведена на market snapshot analysis +- TrendStrategy теперь использует bid/ask mid-price +- добавлен _analysis_price для execution-aware анализа +- добавлен direction_ratio analysis layer +- добавлен live impulse confirmation layer +- TREND теперь требует подтверждение live momentum +- live impulse теперь анализируется через runtime price windows +- улучшена фильтрация noise movement +- снижено количество ложных BUY/SELL импульсов +- TrendStrategy получила расширенные HOLD diagnostics +- HOLD ветки теперь передают entry_block_reason +- HOLD ветки теперь передают entry_block_message +- Telegram UI теперь отображает причины HOLD +- Telegram UI теперь отображает compact market states +- HOLD runtime теперь визуально подтверждает живой цикл +- HOLD timer сохранён как runtime heartbeat indicator +- AutoTradeRunner получил protected execution loop +- исключения стратегии больше не убивают runtime +- исключения UI refresh больше не убивают runtime +- исключения event handler больше не убивают runtime +- runtime loop теперь логирует auto_run_cycle_error +- runtime loop теперь логирует auto_refresh_loop_error +- защищён asyncio lifecycle автоторговли +- исправлен critical runtime bug с TrendStrategy.analyze +- восстановлен realtime refresh автоторговли +- исправлен freeze Telegram UI +- исправлено зависание market state на “Идёт анализ” +- исправлено зависание HOLD timer +- исправлена race condition между screen register и runner.start +- стабилизирована смена инструмента во время runtime +- подтверждена корректная работа websocket runtime +- подтверждена корректная работа REST fallback +- подготовлена база для spread-aware execution +- подготовлена база для stale snapshot protection +- подготовлена база для adaptive execution engine +- подготовлена база для execution quality metrics +- подготовлена база для advanced market diagnostics +- подготовлена база для multi-timeframe analysis + --- ### 07.4.5 diff --git a/docs/roadmap/stage-07-auto-trading-roadmap.md b/docs/roadmap/stage-07-auto-trading-roadmap.md index 031f479..2b5c57c 100644 --- a/docs/roadmap/stage-07-auto-trading-roadmap.md +++ b/docs/roadmap/stage-07-auto-trading-roadmap.md @@ -495,6 +495,62 @@ - подготовлена база для adaptive thresholds - подготовлена база для signal freshness-aware execution +#### 07.4.4.1.7 ✅ Live Market Runtime & Advanced Trend Diagnostics +- внедрён полноценный live market runtime pipeline +- добавлен websocket-based realtime market runtime +- добавлен REST fallback для market runtime +- внедрён runtime-aware MarketPriceCache +- cache переведён на isolation по runtime_key +- добавлен market snapshot layer в ExchangeService +- добавлены get_market_snapshot и get_execution_snapshot +- добавлен get_fresh_market_snapshot +- добавлен refresh_market_snapshot_cache +- внедрена модель ExecutionPriceSnapshot +- execution pipeline получил поддержку bid/ask +- execution pipeline получил freshness tracking +- execution pipeline получил source tracking +- MarketDataRunner теперь обновляет runtime cache +- websocket runtime теперь автоматически reconnect'ится +- websocket runtime теперь безопасно fallback'ится в REST +- TrendStrategy переведена на market snapshot analysis +- TrendStrategy теперь использует bid/ask mid-price +- добавлен _analysis_price для execution-aware анализа +- добавлен direction_ratio analysis layer +- добавлен live impulse confirmation layer +- TREND теперь требует подтверждение live momentum +- live impulse теперь анализируется через runtime price windows +- улучшена фильтрация noise movement +- снижено количество ложных BUY/SELL импульсов +- TrendStrategy получила расширенные HOLD diagnostics +- HOLD ветки теперь передают entry_block_reason +- HOLD ветки теперь передают entry_block_message +- Telegram UI теперь отображает причины HOLD +- Telegram UI теперь отображает compact market states +- HOLD runtime теперь визуально подтверждает живой цикл +- HOLD timer сохранён как runtime heartbeat indicator +- AutoTradeRunner получил protected execution loop +- исключения стратегии больше не убивают runtime +- исключения UI refresh больше не убивают runtime +- исключения event handler больше не убивают runtime +- runtime loop теперь логирует auto_run_cycle_error +- runtime loop теперь логирует auto_refresh_loop_error +- защищён asyncio lifecycle автоторговли +- исправлен critical runtime bug с TrendStrategy.analyze +- восстановлен realtime refresh автоторговли +- исправлен freeze Telegram UI +- исправлено зависание market state на “Идёт анализ” +- исправлено зависание HOLD timer +- исправлена race condition между screen register и runner.start +- стабилизирована смена инструмента во время runtime +- подтверждена корректная работа websocket runtime +- подтверждена корректная работа REST fallback +- подготовлена база для spread-aware execution +- подготовлена база для stale snapshot protection +- подготовлена база для adaptive execution engine +- подготовлена база для execution quality metrics +- подготовлена база для advanced market diagnostics +- подготовлена база для multi-timeframe analysis + --- ### 07.4.5 diff --git a/docs/stages/stage-07_4_4_1_7-live_market_runtime_and_advanced_trend_diagnostics.md b/docs/stages/stage-07_4_4_1_7-live_market_runtime_and_advanced_trend_diagnostics.md new file mode 100644 index 0000000..b78412b --- /dev/null +++ b/docs/stages/stage-07_4_4_1_7-live_market_runtime_and_advanced_trend_diagnostics.md @@ -0,0 +1,387 @@ +# 07.4.4.1.7 — Live Market Runtime & Advanced Trend Diagnostics + +## Цель этапа + +Перевести автоторговлю с простой polling-модели в полноценный live runtime pipeline с realtime market snapshots, websocket runtime, устойчивым execution loop и прозрачной диагностикой причин HOLD. + +Этап решает несколько критичных проблем: + +- UI автоторговли визуально “замирал”; +- HOLD мог выглядеть как зависший; +- рынок отображался как “Идёт анализ” слишком долго; +- стратегии использовали только last price; +- runtime loop мог полностью остановиться после исключения стратегии; +- пользователь не видел причин отсутствия входа; +- live execution pipeline не был устойчив к websocket/runtime ошибкам. + +--- + +# Что было до этапа + +До внедрения нового runtime pipeline: + +- стратегии использовали упрощённый get_price(); +- live bid/ask не использовались; +- отсутствовал execution snapshot layer; +- runtime loop мог полностью упасть при exception внутри стратегии; +- websocket runtime не имел безопасного fallback поведения; +- UI не показывал диагностический контекст HOLD; +- market analysis был слабо связан с live market execution; +- Telegram UI не показывал причину отсутствия входа в позицию. + +Из-за этого пользователь видел: + +text ⏳ Рынок · Идёт анализ + +даже когда runtime уже фактически остановился. + +--- + +# Что внедрено + +## 1. Live market runtime pipeline + +Введён полноценный runtime pipeline: + +text Exchange WS ↓ MarketDataRunner ↓ MarketPriceCache ↓ ExchangeService.get_market_snapshot() ↓ Strategy analyze() ↓ AutoTradeService ↓ Telegram UI + +Теперь стратегии работают не с одиночной ценой, а с realtime market snapshot. + +--- + +## 2. Market snapshot layer + +В ExchangeService добавлены: + +python get_market_snapshot() refresh_market_snapshot_cache() get_execution_snapshot() get_fresh_market_snapshot() + +Теперь runtime может: + +- получать bid/ask; +- понимать freshness snapshot; +- использовать cache/runtime isolation; +- безопасно fallback'иться на REST. + +--- + +## 3. Execution snapshot model + +Добавлена модель: + +python ExecutionPriceSnapshot + +Теперь execution pipeline знает: + +- last price; +- bid price; +- ask price; +- freshness; +- возраст snapshot; +- источник snapshot. + +Это подготовило основу для: + +- execution quality scoring; +- spread analysis; +- slippage protection; +- stale execution protection. + +--- + +## 4. MarketPriceCache runtime isolation + +MarketPriceCache переведён на runtime-aware architecture: + +python (runtime_key, symbol) + +Теперь: + +- AUTO runtime; +- DEBUG runtime; +- future paper/live runtimes + +не конфликтуют между собой. + +--- + +## 5. Websocket runtime + +Внедрён live websocket runtime через: + +python MarketDataRunner + +Теперь runtime: + +- держит live stream; +- обновляет cache; +- следит за runtime lifecycle; +- автоматически reconnect'ится; +- умеет fallback в REST. + +--- + +## 6. REST fallback layer + +Добавлен безопасный fallback: + +python _rest_fallback_once() + +Если websocket отключается: + +text WS disconnect ↓ REST snapshot refresh ↓ runtime продолжает жить + +Автоторговля больше не “умирает” полностью при потере websocket. + +--- + +## 7. Runtime stability protection + +AutoTradeRunner получил защищённый execution loop. + +Теперь: + +python service.run_cycle() + +выполняется внутри protected try/except. + +Даже если стратегия падает: + +- UI остаётся жив; +- runtime остаётся жив; +- websocket продолжает работать; +- HOLD timer продолжает тикать; +- ошибка уходит в журнал. + +--- + +## 8. Protected UI refresh loop + +Защищены: + +python _handle_important_event() _refresh_screen() + +Теперь единичная ошибка Telegram/UI/runtime: + +- не убивает asyncio task; +- не ломает live screen; +- не останавливает автоторговлю. + +--- + +## 9. Runtime startup stabilization + +Изменён порядок запуска: + +Было: + +text runner.start() ↓ screen.register() + +Стало: + +text screen.register() ↓ runner.start() + +Это устранило race condition между: + +- запуском runtime; +- регистрацией Telegram screen; +- первым refresh cycle. + +--- + +# Улучшения аналитики + +## 10. TrendStrategy переведена на market snapshot analysis + +TrendStrategy теперь анализирует: + +python bid_price ask_price last_price + +а не только одиночный ticker price. + +--- + +## 11. Mid-price analysis + +Добавлен: + +python _analysis_price() + +Теперь стратегия использует: + +python (bid + ask) / 2 + +как основную execution-aware цену анализа. + +Это снижает шум: + +- spread spikes; +- случайных last trades; +- микродвижений. + +--- + +## 12. Live impulse confirmation + +TREND больше не принимает решение только по market analysis. + +Теперь требуется: + +text market trend + live impulse + direction consistency + +--- + +## 13. Direction ratio analysis + +Добавлен: + +python _direction_ratio() + +Теперь стратегия оценивает: + +- сколько движений были вверх; +- сколько вниз; +- насколько импульс последовательный. + +Это защищает от: + +text хаотического шума + +и уменьшает ложные входы. + +--- + +## 14. Runtime price windows + +TREND и SCALP получили собственные live price windows. + +Теперь анализируется: + +python изменение цены во времени + +а не только моментальный snapshot. + +--- + +## 15. TREND impulse confirmation + +TREND теперь требует: + +python change_percent >= threshold direction_ratio >= min_ratio + +Только после этого: + +python BUY / SELL + +становится валидным. + +--- + +## 16. HOLD diagnostics layer + +Практически все HOLD-сценарии получили diagnostics payload: + +python entry_block_reason entry_block_message + +Теперь UI знает: + +- почему нет входа; +- почему HOLD продолжается; +- что именно заблокировало execution. + +--- + +## 17. Market state visualization + +Telegram UI теперь отображает: + +text 📈 Тренд · Вверх 📉 Тренд · Вниз 🟰 Рынок · Флэт ⚠️ Рынок · Высокая волатильность + +Вместо “чёрного ящика”. + +--- + +## 18. HOLD explanation layer + +Теперь пользователь видит: + +text Ожидание · слабый импульс Ожидание · мало данных Ожидание · волатильность + +Это резко повысило прозрачность runtime. + +--- + +## 19. Live HOLD timer + +HOLD timer сохранён намеренно: + +text Сигнал 🟡 HOLD · 2м 05с + +Теперь это индикатор: + +- живого runtime; +- живого UI refresh; +- активного execution loop; +- актуального market cycle. + +--- + +# Что было исправлено в процессе этапа + +Во время интеграции был найден критичный runtime bug: + +text TrendStrategy.analyze() оказалась вне класса + +Из-за этого: + +text AutoTradeRunner._worker() падал полностью + +После исправления: + +- runtime loop восстановлен; +- UI refresh восстановлен; +- HOLD timer снова realtime; +- market diagnostics снова обновляются. + +--- + +# Проверка этапа + +Проверено: + +- websocket runtime работает; +- REST fallback работает; +- HOLD timer realtime; +- UI не замирает; +- market state обновляется; +- смена инструмента работает; +- runtime survives exceptions; +- Telegram refresh стабилен; +- snapshot runtime обновляется; +- HOLD diagnostics отображаются корректно. + +--- + +# Что подготовлено для следующих этапов + +Подготовлена база для: + +text adaptive execution engine spread-aware execution advanced diagnostics market freshness scoring multi-timeframe analysis signal persistence scoring adaptive thresholds execution quality metrics + +--- + +# Итог этапа + +Этап 07.4.4.1.7 завершил переход автоторговли к: + +text живому realtime market runtime + +Теперь система: + +- использует live market snapshots; +- имеет websocket runtime; +- имеет REST fallback; +- не умирает при exception; +- показывает причины HOLD; +- отображает состояние рынка; +- поддерживает execution-aware analysis; +- подготовлена к advanced execution engine. \ No newline at end of file