From 954ca0e4274e3aa1983462fac62561c7f10b32bc Mon Sep 17 00:00:00 2001 From: Sergey Date: Thu, 2 Jul 2026 21:59:22 +0300 Subject: [PATCH] refactor(auto): improve runtime reset structure and observing UI --- app/src/telegram/handlers/auto/ui.py | 12 +- app/src/trading/auto/auto_lifecycle.py | 13 +- app/src/trading/auto/state_reset.py | 160 +++++++++++-------------- 3 files changed, 81 insertions(+), 104 deletions(-) diff --git a/app/src/telegram/handlers/auto/ui.py b/app/src/telegram/handlers/auto/ui.py index 28d322b..d1ddb1c 100644 --- a/app/src/telegram/handlers/auto/ui.py +++ b/app/src/telegram/handlers/auto/ui.py @@ -1535,8 +1535,16 @@ def _trade_word(value: int) -> str: def _cycle_summary_lines(state) -> list[str]: - # Единый блок статистики текущего цикла. - # Показываем номер цикла всегда, даже если закрытых сделок ещё нет. + status = str(getattr(state, "status", "") or "").upper() + + # В режиме наблюдения торгового цикла нет: + # бот только анализирует рынок и не исполняет сделки. + if status == "OBSERVING": + return ["🔬 Анализ рынка"] + + if status != "RUNNING": + return [] + cycle_trades = int(getattr(state, "cycle_closed_trades", 0) or 0) cycle_pnl = float(getattr(state, "cycle_realized_pnl_usd", 0.0) or 0.0) diff --git a/app/src/trading/auto/auto_lifecycle.py b/app/src/trading/auto/auto_lifecycle.py index 49c7b97..631186a 100644 --- a/app/src/trading/auto/auto_lifecycle.py +++ b/app/src/trading/auto/auto_lifecycle.py @@ -152,6 +152,9 @@ class AutoLifecycleMixin( if state.status == "OBSERVING": state.status = "RUNNING" + if state.cycle_started_at is None: + state.cycle_started_at = time.monotonic() + state.cycle_number = int(getattr(state, "cycle_number", 0) or 0) + 1 # При ручном запуске из OBSERVING очищаем старую cooldown-блокировку, # чтобы запуск не наследовал паузу прошлого цикла. reset_loss_cooldown_state(state) @@ -185,19 +188,11 @@ class AutoLifecycleMixin( reset_flip_runtime_state(state) state.cycle_started_at = time.monotonic() - state.cycle_number = int(getattr(state, "cycle_number", 0) or 0) + 1 state.last_signal = "HOLD" state.signal_started_at = time.monotonic() state.cycle_number = int(getattr(state, "cycle_number", 0) or 0) + 1 - state.last_flip_old_side = None - state.last_flip_new_side = None - state.last_flip_pnl_usd = None - state.last_flip_reason = None - state.last_flip_monotonic_at = None - state.last_signal = "HOLD" - state.signal_started_at = time.monotonic() EventBus.emit( "auto_status_changed", @@ -240,7 +235,7 @@ class AutoLifecycleMixin( reset_execution_runtime_state(state) reset_position_semantics_state(state) - state.cycle_started_at = time.monotonic() + state.cycle_started_at = None self._log_auto_status_changed( previous_status=previous_status, diff --git a/app/src/trading/auto/state_reset.py b/app/src/trading/auto/state_reset.py index d5c7e97..57729a4 100644 --- a/app/src/trading/auto/state_reset.py +++ b/app/src/trading/auto/state_reset.py @@ -1,56 +1,20 @@ # app/src/trading/auto/state_reset.py -from __future__ import annotations - """ Centralized runtime reset helpers for AutoTrade. -Helpers для сброса отдельных частей AutoTradeState. -ВАЖНО: -Все функции этого файла отвечают только за очистку runtime-состояния. -Каждая функция отвечает только за одну смысловую область runtime. - -Функции намеренно не используют EventBus, JournalService, -ExecutionEngine или другие сервисы. Они изменяют только -поля AutoTradeState. - -Это позволяет безопасно использовать их из разных runtime-компонентов -без изменения бизнес-логики. - -ВАЖНО: -Они не должны изменять пользовательские настройки: - • symbol - • strategy - • leverage - • risk_percent - • stop_loss_percent - • take_profit_percent - • allocated_balance_usd - -Их задача — заменить сотни ручных присваиваний вида - - state.xxx = None - state.yyy = False - state.zzz = 0 - -едиными helper-функциями. - -Используются из: - • auto_lifecycle.py - • signal_runtime.py - • execution.py - • других runtime-модулей. +Файл содержит только чистые функции сброса AutoTradeState. +Без EventBus, JournalService, ExecutionEngine и другой бизнес-логики. """ +from __future__ import annotations + from src.trading.auto.state import AutoTradeState # ----------------------------------------------------------------------------- # Adaptive Position Sizing # ----------------------------------------------------------------------------- -# Runtime-параметры, связанные с динамическим изменением размера позиции. -# Сбрасываются при начале нового цикла или полном сбросе runtime. -# ----------------------------------------------------------------------------- def reset_adaptive_size_state(state: AutoTradeState) -> None: state.adaptive_size_base = None @@ -67,9 +31,6 @@ def reset_adaptive_size_state(state: AutoTradeState) -> None: # ----------------------------------------------------------------------------- # Signal Runtime # ----------------------------------------------------------------------------- -# Сбрасывает только runtime сигнала. -# Не затрагивает market context, позицию или protection. -# ----------------------------------------------------------------------------- def reset_signal_runtime_state(state: AutoTradeState) -> None: state.last_signal = "HOLD" @@ -79,7 +40,6 @@ def reset_signal_runtime_state(state: AutoTradeState) -> None: state.signal_started_at = None state.signal_updated_at = None - state.signal_confirmation_seconds = 0 state.signal_confirmation_missing_repeats = 0 state.signal_confirmation_progress = 0.0 @@ -94,24 +54,29 @@ def reset_signal_runtime_state(state: AutoTradeState) -> None: # ----------------------------------------------------------------------------- # Execution Runtime # ----------------------------------------------------------------------------- -# Сбрасывает все runtime-данные, связанные с execution layer: -# semantic status, quality, pricing snapshot и confidence. -# ----------------------------------------------------------------------------- -def reset_execution_runtime_state(state: AutoTradeState) -> None: +def reset_execution_block_state(state: AutoTradeState) -> None: state.execution_block_reason = None state.execution_block_title = None state.execution_block_message = None state.execution_block_action = None + +def reset_execution_semantic_state(state: AutoTradeState) -> None: state.execution_semantic_status = None state.execution_semantic_message = None state.execution_semantic_reason = None + +def reset_execution_quality_state(state: AutoTradeState) -> None: state.execution_quality = None state.execution_quality_reason = None state.execution_quality_message = None + state.snapshot_age_seconds = None + state.spread_percent = None + +def reset_execution_pricing_state(state: AutoTradeState) -> None: state.execution_price_source = None state.execution_price_age_seconds = None state.execution_bid_price = None @@ -119,66 +84,74 @@ def reset_execution_runtime_state(state: AutoTradeState) -> None: state.execution_last_price = None state.execution_price_freshness = None + +def reset_execution_confidence_state(state: AutoTradeState) -> None: state.execution_confidence_score = None state.execution_confidence_level = None state.execution_confidence_reason = None state.execution_confidence_factors = None - state.snapshot_age_seconds = None - state.spread_percent = None + +def reset_execution_runtime_state(state: AutoTradeState) -> None: + reset_execution_block_state(state) + reset_execution_semantic_state(state) + reset_execution_quality_state(state) + reset_execution_pricing_state(state) + reset_execution_confidence_state(state) # ----------------------------------------------------------------------------- -# Market Analysis Runtime -# ----------------------------------------------------------------------------- -# Полностью очищает runtime-анализ рынка. -# Используется при истечении TTL или полном сбросе runtime. +# Market Runtime # ----------------------------------------------------------------------------- -def reset_market_analysis_state(state: AutoTradeState) -> None: +def reset_market_base_state(state: AutoTradeState) -> None: state.market_state = None state.market_trend = None state.market_volatility = None - state.market_trend_strength = None state.market_trend_quality = None - state.market_phase = None state.market_phase_direction = None + +def reset_market_score_state(state: AutoTradeState) -> None: state.market_score = None state.market_score_label = None state.market_long_score = None state.market_short_score = None + +def reset_market_candle_state(state: AutoTradeState) -> None: state.last_closed_candle_change_percent = None state.last_closed_candle_direction = None - state.current_interval_change_percent = None state.current_interval_direction = None state.current_interval_label = None + +def reset_market_structure_state(state: AutoTradeState) -> None: state.market_structure = None state.market_structure_reason = None + +def reset_market_trend_quality_state(state: AutoTradeState) -> None: state.market_trend_gap_percent = None state.market_trend_consistency = None state.market_trend_efficiency = None - state.trend_quality_score = None state.ema_distance_atr_ratio = None state.ema_distance_state = None - state.entry_timing_state = None state.entry_timing_reason = None state.ema_fast_slope_percent = None state.ema_slow_slope_percent = None - state.candle_noise_score = None state.price_position_score = None + +def reset_market_htf_state(state: AutoTradeState) -> None: state.htf_interval = None state.htf_atr_percent = None state.htf_atr_percent_baseline = None @@ -195,30 +168,41 @@ def reset_market_analysis_state(state: AutoTradeState) -> None: state.htf_confirmation_score = None state.htf_reason = None + +def reset_market_momentum_state(state: AutoTradeState) -> None: state.momentum_state = None state.momentum_direction = None state.momentum_change_percent = None state.momentum_strength = None - state.breakout_level = None state.breakout_distance_percent = None state.breakout_reason = None + +def reset_market_analysis_meta_state(state: AutoTradeState) -> None: state.market_analysis_interval = None state.market_analysis_reason = None state.market_analysis_updated_at = None state.entry_block_reason = None state.entry_block_message = None - state.market_runtime_degraded = False +def reset_market_analysis_state(state: AutoTradeState) -> None: + reset_market_base_state(state) + reset_market_score_state(state) + reset_market_candle_state(state) + reset_market_structure_state(state) + reset_market_trend_quality_state(state) + reset_market_htf_state(state) + reset_market_momentum_state(state) + reset_market_analysis_meta_state(state) + + # ----------------------------------------------------------------------------- # Runtime Expiration # ----------------------------------------------------------------------------- -# Причина последнего автоматического сброса runtime. -# ----------------------------------------------------------------------------- def reset_runtime_expiration_state(state: AutoTradeState) -> None: state.runtime_expired_reason = None @@ -229,13 +213,15 @@ def reset_runtime_expiration_state(state: AutoTradeState) -> None: # Position Runtime # ----------------------------------------------------------------------------- -def reset_position_runtime_state(state: AutoTradeState) -> None: +def reset_position_identity_state(state: AutoTradeState) -> None: state.position_side = "NONE" state.entry_price = None state.position_size = None state.position_opened_monotonic_at = None state.unrealized_pnl_usd = None + +def reset_position_health_state(state: AutoTradeState) -> None: state.position_pnl_percent = None state.position_hold_seconds = None state.position_pressure = None @@ -249,9 +235,10 @@ def reset_position_runtime_state(state: AutoTradeState) -> None: state.position_exit_pressure = None -# ----------------------------------------------------------------------------- -# Position Semantics / Intelligence -# ----------------------------------------------------------------------------- +def reset_position_runtime_state(state: AutoTradeState) -> None: + reset_position_identity_state(state) + reset_position_health_state(state) + def reset_position_semantics_state(state: AutoTradeState) -> None: state.position_lifecycle_stage = None @@ -297,7 +284,7 @@ def reset_position_protection_state(state: AutoTradeState) -> None: # ----------------------------------------------------------------------------- -# Autonomous Management +# Autonomous Runtime # ----------------------------------------------------------------------------- def reset_autonomous_runtime_state(state: AutoTradeState) -> None: @@ -314,7 +301,7 @@ def reset_autonomous_runtime_state(state: AutoTradeState) -> None: # ----------------------------------------------------------------------------- -# Loss Cooldown +# Loss Cooldown / Cycle / Flip / Last Execution # ----------------------------------------------------------------------------- def reset_loss_cooldown_state(state: AutoTradeState) -> None: @@ -323,9 +310,15 @@ def reset_loss_cooldown_state(state: AutoTradeState) -> None: state.loss_cooldown_reason = None -# ----------------------------------------------------------------------------- -# Flip Runtime -# ----------------------------------------------------------------------------- +def reset_cycle_statistics_state(state: AutoTradeState) -> None: + state.cycle_realized_pnl_usd = 0.0 + state.cycle_closed_trades = 0 + state.cycle_winning_trades = 0 + state.cycle_losing_trades = 0 + state.cycle_consecutive_losses = 0 + state.cycle_trade_fees_usd = 0.0 + state.cycle_overnight_fees_usd = 0.0 + def reset_flip_runtime_state(state: AutoTradeState) -> None: state.last_flip_old_side = None @@ -337,29 +330,11 @@ def reset_flip_runtime_state(state: AutoTradeState) -> None: state.last_flip_block_reason = None -# ----------------------------------------------------------------------------- -# Last Execution Runtime -# ----------------------------------------------------------------------------- - def reset_last_execution_state(state: AutoTradeState) -> None: state.last_execution_action = None state.last_execution_reason = None -# ----------------------------------------------------------------------------- -# Cycle Statistics -# ----------------------------------------------------------------------------- - -def reset_cycle_statistics_state(state: AutoTradeState) -> None: - state.cycle_realized_pnl_usd = 0.0 - state.cycle_closed_trades = 0 - state.cycle_winning_trades = 0 - state.cycle_losing_trades = 0 - state.cycle_consecutive_losses = 0 - state.cycle_trade_fees_usd = 0.0 - state.cycle_overnight_fees_usd = 0.0 - - # ----------------------------------------------------------------------------- # Full Runtime Reset # ----------------------------------------------------------------------------- @@ -380,7 +355,6 @@ def reset_full_runtime_state(state: AutoTradeState) -> None: reset_last_execution_state(state) -# Alias на случай старого имени. def reset_runtime_state(state: AutoTradeState) -> None: reset_full_runtime_state(state)