from __future__ import annotations from typing import Any from backend.data.numbers import finite_number as _number from backend.features.screener.catalog import REGIMES from backend.features.screener.indicators import _regime_reason from backend.features.sentiment.engine import build_sentiment_history, latest_contiguous_history from database import ReviewDatabase class RegimeDetector: def __init__(self, database: ReviewDatabase) -> None: self.database = database def detect_regime(self, trade_date: str) -> dict[str, Any]: series = latest_contiguous_history( build_sentiment_history(self.database.list_snapshot_payloads(trade_date, 260)) ) if not series: return { "id": "repair", "label": REGIMES["repair"], "confidence": 25, "reason": "复盘快照不足,暂按中性修复处理。", "evidence": [], "history": [], } current = series[-1] previous = series[-2] if len(series) > 1 else current score = _number(current.get("score")) previous_score = _number(previous.get("score")) delta = score - previous_score seal_rate = _number(current.get("seal_rate")) limit_up = _number(current.get("limit_up_count")) broken = _number(current.get("broken_count")) regime = next( (key for key, label in REGIMES.items() if label == current.get("phase")), "divergence", ) confidence = min(92, 45 + len(series[-8:]) * 5 + min(abs(delta), 12)) evidence = [ f"情绪温度 {score:.0f},较前一交易日 {delta:+.0f},{current.get('direction') or '持平'}", f"封板率 {seal_rate:.1f}%", f"涨停 {limit_up:.0f} 家,炸板 {broken:.0f} 家", ] return { "id": regime, "label": REGIMES[regime], "confidence": round(confidence), "reason": _regime_reason(regime), "evidence": evidence, "history": [ {"trade_date": item["trade_date"], "score": _number(item.get("score"))} for item in series[-8:] ], }