07.4.4.1.8.1 — Spread Hysteresis Layer

This commit is contained in:
2026-05-11 20:39:13 +03:00
parent eb40ecc4dd
commit 9ba1297c46
4 changed files with 396 additions and 16 deletions

View File

@@ -45,10 +45,50 @@ class AutoTradeService:
_max_snapshot_age_seconds = 5.0
_warning_snapshot_age_seconds = 2.0
_max_spread_percent = 0.15
_warning_spread_percent = 0.08
_spread_warning_enter_percent = 0.08
_spread_warning_exit_percent = 0.06
_spread_block_enter_percent = 0.15
_spread_block_exit_percent = 0.12
_last_logged_execution_quality_key: str | None = None
def _spread_execution_quality(
self,
*,
state: AutoTradeState,
spread_percent: float | None,
) -> tuple[str | None, str | None, str | None, bool]:
if spread_percent is None:
return None, None, None, False
previous_quality = state.execution_quality
previous_reason = state.execution_quality_reason
if previous_quality == "BLOCKED" and previous_reason == "HIGH_SPREAD":
if spread_percent > self._spread_block_exit_percent:
return "BLOCKED", "HIGH_SPREAD", "высокий spread", False
if spread_percent > self._spread_warning_exit_percent:
return "WARNING", "WIDE_SPREAD", "spread повышен", False
return "GOOD", "MARKET_OK", "рынок готов", False
if previous_quality == "WARNING" and previous_reason == "WIDE_SPREAD":
if spread_percent >= self._spread_block_enter_percent:
return "BLOCKED", "HIGH_SPREAD", "высокий spread", False
if spread_percent > self._spread_warning_exit_percent:
return "WARNING", "WIDE_SPREAD", "spread повышен", False
return "GOOD", "MARKET_OK", "рынок готов", False
if spread_percent >= self._spread_block_enter_percent:
return "BLOCKED", "HIGH_SPREAD", "высокий spread", False
if spread_percent >= self._spread_warning_enter_percent:
return "WARNING", "WIDE_SPREAD", "spread повышен", False
return "GOOD", "MARKET_OK", "рынок готов", False
# debug: принудительно выставить сигнал и decision
def debug_force_signal(
self,
@@ -995,7 +1035,7 @@ class AutoTradeService:
else:
state.execution_quality = "BLOCKED"
state.execution_quality_reason = "SNAPSHOT_ERROR"
state.execution_quality_message = "нет market data"
state.execution_quality_message = "нет данных рынка"
state.market_runtime_degraded = True
self._log_execution_quality_if_changed(
@@ -1027,23 +1067,22 @@ class AutoTradeService:
state.execution_quality_message = "snapshot устарел"
state.market_runtime_degraded = True
elif state.spread_percent is not None and state.spread_percent > self._max_spread_percent:
state.execution_quality = "BLOCKED"
state.execution_quality_reason = "HIGH_SPREAD"
state.execution_quality_message = "высокий spread"
state.market_runtime_degraded = False
elif age_seconds is not None and age_seconds > self._warning_snapshot_age_seconds:
state.execution_quality = "WARNING"
state.execution_quality_reason = "AGING_SNAPSHOT"
state.execution_quality_message = "snapshot стареет"
state.market_runtime_degraded = not is_fresh
elif state.spread_percent is not None and state.spread_percent > self._warning_spread_percent:
state.execution_quality = "WARNING"
state.execution_quality_reason = "WIDE_SPREAD"
state.execution_quality_message = "spread повышен"
state.market_runtime_degraded = False
elif state.spread_percent is not None:
(
state.execution_quality,
state.execution_quality_reason,
state.execution_quality_message,
state.market_runtime_degraded,
) = self._spread_execution_quality(
state=state,
spread_percent=state.spread_percent,
)
else:
state.execution_quality = "GOOD"
@@ -1075,8 +1114,10 @@ class AutoTradeService:
"market_runtime_degraded": state.market_runtime_degraded,
"max_snapshot_age_seconds": self._max_snapshot_age_seconds,
"warning_snapshot_age_seconds": self._warning_snapshot_age_seconds,
"max_spread_percent": self._max_spread_percent,
"warning_spread_percent": self._warning_spread_percent,
"spread_warning_enter_percent": self._spread_warning_enter_percent,
"spread_warning_exit_percent": self._spread_warning_exit_percent,
"spread_block_enter_percent": self._spread_block_enter_percent,
"spread_block_exit_percent": self._spread_block_exit_percent,
},
)