07.4.4.1.6 — Signal Aging & Runtime Expiration
This commit is contained in:
@@ -26,6 +26,10 @@ class AutoTradeService:
|
||||
# минимальная уверенность для готовности к будущему execution
|
||||
_ready_confidence = 0.3
|
||||
|
||||
_signal_ttl_seconds = 90
|
||||
_market_analysis_ttl_seconds = 180
|
||||
_last_logged_runtime_expired_key: str | None = None
|
||||
|
||||
_last_signal_key: str | None = None
|
||||
_last_signal_value: str | None = None
|
||||
_last_signal_reason: str = ""
|
||||
@@ -310,13 +314,17 @@ class AutoTradeService:
|
||||
state.is_signal_ready = False
|
||||
state.execution_block_reason = None
|
||||
state.signal_started_at = None
|
||||
state.signal_updated_at = None
|
||||
state.market_state = None
|
||||
state.market_trend = None
|
||||
state.market_volatility = 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.runtime_expired_reason = None
|
||||
state.runtime_expired_message = None
|
||||
|
||||
# собрать контекст для стратегии
|
||||
def _build_strategy_context(self) -> StrategyContext:
|
||||
@@ -513,6 +521,9 @@ class AutoTradeService:
|
||||
state.last_signal_repeat_count = self._same_signal_count
|
||||
state.last_signal_confidence = confidence
|
||||
state.last_signal_reason = reason
|
||||
state.signal_updated_at = time.monotonic()
|
||||
state.runtime_expired_reason = None
|
||||
state.runtime_expired_message = None
|
||||
|
||||
self._update_decision_state(
|
||||
state=state,
|
||||
@@ -684,6 +695,7 @@ class AutoTradeService:
|
||||
state.market_volatility = payload.get("market_volatility")
|
||||
state.market_analysis_interval = payload.get("market_analysis_interval")
|
||||
state.market_analysis_reason = payload.get("market_analysis_reason")
|
||||
state.market_analysis_updated_at = time.monotonic()
|
||||
state.entry_block_reason = payload.get("entry_block_reason")
|
||||
state.entry_block_message = payload.get("entry_block_message")
|
||||
|
||||
@@ -853,12 +865,100 @@ class AutoTradeService:
|
||||
}
|
||||
|
||||
return messages.get(market_state, "Состояние рынка анализируется.")
|
||||
|
||||
|
||||
def _expire_runtime_if_needed(self, state: AutoTradeState) -> None:
|
||||
now = time.monotonic()
|
||||
|
||||
signal_updated_at = getattr(state, "signal_updated_at", None)
|
||||
if signal_updated_at is not None:
|
||||
signal_age = now - float(signal_updated_at)
|
||||
|
||||
if signal_age > self._signal_ttl_seconds:
|
||||
previous_signal = state.last_signal
|
||||
|
||||
self._reset_signal_tracking()
|
||||
|
||||
state.runtime_expired_reason = "SIGNAL_TTL_EXPIRED"
|
||||
state.runtime_expired_message = "сигнал устарел и был сброшен"
|
||||
|
||||
self._log_runtime_expired_if_changed(
|
||||
state=state,
|
||||
reason="SIGNAL_TTL_EXPIRED",
|
||||
message="Сигнал устарел и был сброшен.",
|
||||
payload={
|
||||
"previous_signal": previous_signal,
|
||||
"signal_age_seconds": int(signal_age),
|
||||
"signal_ttl_seconds": self._signal_ttl_seconds,
|
||||
},
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
market_updated_at = getattr(state, "market_analysis_updated_at", None)
|
||||
if market_updated_at is not None:
|
||||
market_age = now - float(market_updated_at)
|
||||
|
||||
if market_age > self._market_analysis_ttl_seconds:
|
||||
state.market_state = None
|
||||
state.market_trend = None
|
||||
state.market_volatility = 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.runtime_expired_reason = "MARKET_ANALYSIS_TTL_EXPIRED"
|
||||
state.runtime_expired_message = "анализ рынка устарел"
|
||||
|
||||
self._log_runtime_expired_if_changed(
|
||||
state=state,
|
||||
reason="MARKET_ANALYSIS_TTL_EXPIRED",
|
||||
message="Анализ рынка устарел и был сброшен.",
|
||||
payload={
|
||||
"market_age_seconds": int(market_age),
|
||||
"market_analysis_ttl_seconds": self._market_analysis_ttl_seconds,
|
||||
},
|
||||
)
|
||||
|
||||
def _log_runtime_expired_if_changed(
|
||||
self,
|
||||
*,
|
||||
state: AutoTradeState,
|
||||
reason: str,
|
||||
message: str,
|
||||
payload: dict,
|
||||
) -> None:
|
||||
key = f"{state.status}:{state.symbol}:{state.strategy}:{reason}"
|
||||
|
||||
if key == type(self)._last_logged_runtime_expired_key:
|
||||
return
|
||||
|
||||
type(self)._last_logged_runtime_expired_key = key
|
||||
|
||||
try:
|
||||
JournalService().log_ui_warning(
|
||||
event_type="runtime_expired",
|
||||
message=message,
|
||||
screen="auto",
|
||||
action="runtime_expiration",
|
||||
payload={
|
||||
**payload,
|
||||
"symbol": state.symbol,
|
||||
"strategy": state.strategy,
|
||||
"status": state.status,
|
||||
"runtime_expired_reason": reason,
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def run_cycle(self) -> AutoTradeState:
|
||||
state = self.get_state()
|
||||
|
||||
if state.status == "OFF":
|
||||
return state
|
||||
|
||||
self._expire_runtime_if_needed(state)
|
||||
|
||||
strategy = self._get_strategy()
|
||||
context = self._build_strategy_context()
|
||||
|
||||
Reference in New Issue
Block a user