feat(ai_trade): state.signals에 expires_at + cycle_id lifecycle 추가 (F5 part 1)
코드 리뷰 F5 — Phase 5 consumer(agent-office /signal) prereq: PollState.signal_cycle_id (process auto-increment) + get_active_signals(now) + purge_expired_signals(now) helper. expires_at 없는 legacy signal은 expired 취급. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""PollState — process-wide singleton."""
|
||||
from collections import deque
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -15,8 +16,44 @@ class PollState:
|
||||
chronos_predictions: dict[str, dict] = field(default_factory=dict)
|
||||
minute_momentum: dict[str, str] = field(default_factory=dict)
|
||||
signals: dict[str, dict] = field(default_factory=dict)
|
||||
# F5 lifecycle
|
||||
signal_cycle_id: int = 0
|
||||
last_updated: dict[str, str] = field(default_factory=dict)
|
||||
fetch_errors: dict[str, int] = field(default_factory=dict)
|
||||
|
||||
def get_active_signals(self, now: datetime) -> list[dict]:
|
||||
"""expires_at > now 인 신호만 반환. expires_at 없거나 파싱 실패는 expired 취급."""
|
||||
active: list[dict] = []
|
||||
for sig in self.signals.values():
|
||||
expires_at = sig.get("expires_at")
|
||||
if not expires_at:
|
||||
continue
|
||||
try:
|
||||
exp_dt = datetime.fromisoformat(expires_at)
|
||||
except ValueError:
|
||||
continue
|
||||
if exp_dt > now:
|
||||
active.append(sig)
|
||||
return active
|
||||
|
||||
def purge_expired_signals(self, now: datetime) -> int:
|
||||
"""만료된 signal 제거. expires_at 없거나 파싱 실패도 제거. 제거 개수 반환."""
|
||||
to_drop = []
|
||||
for ticker, sig in self.signals.items():
|
||||
expires_at = sig.get("expires_at")
|
||||
if not expires_at:
|
||||
to_drop.append(ticker)
|
||||
continue
|
||||
try:
|
||||
exp_dt = datetime.fromisoformat(expires_at)
|
||||
except ValueError:
|
||||
to_drop.append(ticker)
|
||||
continue
|
||||
if exp_dt <= now:
|
||||
to_drop.append(ticker)
|
||||
for t in to_drop:
|
||||
del self.signals[t]
|
||||
return len(to_drop)
|
||||
|
||||
|
||||
state = PollState()
|
||||
|
||||
Reference in New Issue
Block a user