Harden market truth fallback failures

This commit is contained in:
Codex
2026-05-07 00:22:20 +08:00
parent 82c1f20879
commit a717cdfea3
2 changed files with 62 additions and 18 deletions
@@ -143,11 +143,22 @@ class MarketTruthSupervisor:
while not self._stop_event.is_set():
now = datetime.now(timezone.utc)
for subscription in self.stream_service.iter_subscriptions():
self.current_state(
instrument_id=subscription.instrument_id,
timeframe=subscription.timeframe,
now=now,
)
try:
self.current_state(
instrument_id=subscription.instrument_id,
timeframe=subscription.timeframe,
now=now,
)
except Exception as exc:
self._record_incident(
instrument_id=subscription.instrument_id,
timeframe=subscription.timeframe,
incident_type=INCIDENT_TYPE_STREAM,
incident_code="market_truth_supervisor_error",
status=INCIDENT_STATUS_OPEN,
observed_ts=now,
details={"error": str(exc)},
)
self._get_news_snapshot(now=now, force_refresh=False)
for _ in range(max(self.interval_seconds, 1)):
if self._stop_event.is_set():
@@ -188,19 +199,30 @@ class MarketTruthSupervisor:
if not symbol:
return
inst_id = build_okx_inst_id(symbol)
rows = self.rest_client.get_candles(inst_id=inst_id, bar=timeframe, limit=self.fallback_limit)
normalized = normalize_okx_candles(symbol=symbol, venue="OKX", timeframe=timeframe, candles=rows)
confirmed = [item for item in normalized if item.confirm]
written = self.candle_repository.upsert_many(confirmed, instrument_id=instrument_id)
self._record_incident(
instrument_id=instrument_id,
timeframe=timeframe,
incident_type=INCIDENT_TYPE_FALLBACK,
incident_code="rest_backfill_applied",
status=INCIDENT_STATUS_RESOLVED if written else INCIDENT_STATUS_OPEN,
observed_ts=now,
details={"symbol": symbol, "inst_id": inst_id, "timeframe": timeframe, "fetched": len(rows), "written": written},
)
try:
rows = self.rest_client.get_candles(inst_id=inst_id, bar=timeframe, limit=self.fallback_limit)
normalized = normalize_okx_candles(symbol=symbol, venue="OKX", timeframe=timeframe, candles=rows)
confirmed = [item for item in normalized if item.confirm]
written = self.candle_repository.upsert_many(confirmed, instrument_id=instrument_id)
self._record_incident(
instrument_id=instrument_id,
timeframe=timeframe,
incident_type=INCIDENT_TYPE_FALLBACK,
incident_code="rest_backfill_applied",
status=INCIDENT_STATUS_RESOLVED if written else INCIDENT_STATUS_OPEN,
observed_ts=now,
details={"symbol": symbol, "inst_id": inst_id, "timeframe": timeframe, "fetched": len(rows), "written": written},
)
except Exception as exc:
self._record_incident(
instrument_id=instrument_id,
timeframe=timeframe,
incident_type=INCIDENT_TYPE_FALLBACK,
incident_code="rest_backfill_failed",
status=INCIDENT_STATUS_OPEN,
observed_ts=now,
details={"symbol": symbol, "inst_id": inst_id, "timeframe": timeframe, "error": str(exc)},
)
def _get_news_snapshot(self, *, now: datetime, force_refresh: bool = False) -> NewsProviderSnapshot:
if not settings.news_provider_enabled:
+22
View File
@@ -122,6 +122,23 @@ class MarketTruthSupervisorTests(unittest.TestCase):
self.assertTrue(any(item["incident_code"] == "rest_backfill_applied" for item in repository.incidents))
self.assertTrue(any(item["incident_code"] == STREAM_CODE_DISCONNECTED for item in repository.incidents))
def test_supervisor_keeps_running_when_rest_fallback_fails(self) -> None:
now = datetime(2026, 5, 7, 12, 0, tzinfo=timezone.utc)
repository = _SupervisorRepository(now=now)
supervisor = MarketTruthSupervisor(
repository=repository,
candle_repository=_FallbackCandleRepository(repository),
stream_service=_DisconnectedStreamService(now=now),
rest_client=_FailingOKXClient(),
news_provider=_InactiveNewsProvider(now=now),
enabled=False,
)
state = supervisor.current_state(instrument_id=1, timeframe="1m", now=now)
self.assertEqual(state.stream_state["code"], STREAM_CODE_DISCONNECTED)
self.assertTrue(any(item["incident_code"] == "rest_backfill_failed" for item in repository.incidents))
def test_market_state_service_uses_supervisor_runtime_state(self) -> None:
now = datetime(2026, 5, 7, 12, 0, tzinfo=timezone.utc)
repository = _SupervisorRepository(now=now)
@@ -254,6 +271,11 @@ class _FallbackOKXClient:
]
class _FailingOKXClient:
def get_candles(self, inst_id: str, bar: str, limit: int):
raise TimeoutError("rest_timeout")
class _InactiveNewsProvider:
def __init__(self, now: datetime) -> None:
self.now = now