""" 市场状态分类引擎 职责: - 根据量化特征判断当前市场处于哪类 regime - 输出当前允许的交易行为集合 - 为 LLM 与执行层提供统一约束 """ from typing import Any, Dict, List class RegimeEngine: """市场状态分类引擎""" def classify( self, *, range_metrics: Dict[str, Any] | None, market_location: Dict[str, Any] | None, trend_direction: str = "neutral", trend_strength: str = "weak", derivatives_state: Dict[str, Any] | None = None, reversal_detection: Dict[str, Any] | None = None, trend_stage: Dict[str, Any] | None = None, ) -> Dict[str, Any]: range_metrics = range_metrics or {} market_location = market_location or {} derivatives_state = derivatives_state or {} reversal_detection = reversal_detection or {} trend_stage = trend_stage or {} location_tag = str(market_location.get("location_tag") or "unknown") relative_to_range = str(market_location.get("relative_to_range") or "unknown") range_regime = str(range_metrics.get("regime") or "unknown") crowding_regime = str(derivatives_state.get("crowding_regime") or "low") crowding_bias = str(derivatives_state.get("crowding_bias") or "neutral") bb_squeeze = bool(range_metrics.get("bb_squeeze")) no_trade_reasons: List[str] = [] profile: Dict[str, Any] = { "regime_key": "neutral", "market_state_label": "中性市", "tradability": "avoid", "risk_mode": "defensive", "allowed_lanes": [], "preferred_lanes": [], "allowed_setups": [], "preferred_entry_types": [], "crowding_bias": crowding_bias, "crowding_regime": crowding_regime, "no_trade_reasons": no_trade_reasons, "summary": "", } if location_tag in {"middle_of_range", "far_from_trade_zone"}: no_trade_reasons.append(f"位置不佳: {location_tag}") if range_regime == "ranging": profile.update( { "regime_key": "range", "market_state_label": "震荡市", "tradability": "selective", "risk_mode": "defensive", "allowed_lanes": ["short_term"], "preferred_lanes": ["short_term", "medium_term"], "allowed_setups": ["range_reversal"], "preferred_entry_types": ["limit", "market"], } ) if relative_to_range not in {"near_range_support", "near_range_resistance"}: no_trade_reasons.append("震荡市但不在区间边界") elif range_regime == "transitional": if bb_squeeze and location_tag not in {"far_from_trade_zone", "middle_of_range"}: profile.update( { "regime_key": "breakout_compression", "market_state_label": "压缩待突破", "tradability": "selective", "risk_mode": "defensive", "allowed_lanes": ["short_term"], "preferred_lanes": ["short_term", "medium_term"], "allowed_setups": ["breakout_confirmation", "breakout_pullback"], "preferred_entry_types": ["market", "limit"], } ) else: profile.update( { "regime_key": "transition", "market_state_label": "过渡市", "tradability": "avoid", "risk_mode": "defensive", "allowed_lanes": [], "preferred_lanes": [], "allowed_setups": [], "preferred_entry_types": [], } ) no_trade_reasons.append("趋势与震荡切换期,方向优势不足") elif range_regime in {"weak_trend", "strong_trend"} and trend_direction in {"uptrend", "downtrend"}: allow_reversal = bool(reversal_detection.get("is_reversing")) and trend_strength != "strong" if crowding_regime == "high": profile.update( { "regime_key": "crowded_trend", "market_state_label": "拥挤趋势", "tradability": "selective", "risk_mode": "defensive", "allowed_lanes": ["medium_term"], "preferred_lanes": ["medium_term", "short_term"], "allowed_setups": ["deep_pullback_continuation"], "preferred_entry_types": ["limit"], } ) if location_tag not in {"near_long_zone", "near_short_zone"}: no_trade_reasons.append("趋势拥挤,且不在深回踩/反抽交易区") else: profile.update( { "regime_key": "trend", "market_state_label": "趋势市", "tradability": "tradable", "risk_mode": "normal" if range_regime == "strong_trend" else "reduced", "allowed_lanes": ["medium_term", "short_term"], "preferred_lanes": ["medium_term", "short_term"], "allowed_setups": ["trend_continuation_pullback"], "preferred_entry_types": ["limit", "market"], } ) if allow_reversal: profile["allowed_setups"].append("trend_reversal") if location_tag in {"far_from_trade_zone", "middle_of_range"}: profile["tradability"] = "selective" no_trade_reasons.append("趋势存在,但当前价格没有位置优势") if str(trend_stage.get("stage") or "") == "late": profile["tradability"] = "selective" profile["risk_mode"] = "defensive" no_trade_reasons.append("趋势晚期,避免追价") else: profile.update( { "regime_key": "neutral", "market_state_label": "中性市", "tradability": "avoid", "risk_mode": "defensive", "allowed_lanes": [], "preferred_lanes": [], "allowed_setups": [], "preferred_entry_types": [], } ) no_trade_reasons.append("无清晰市场结构") if no_trade_reasons and profile["tradability"] == "selective": profile["summary"] = f"{profile['market_state_label']} | 谨慎交易 | {';'.join(no_trade_reasons[:2])}" elif no_trade_reasons and profile["tradability"] == "avoid": profile["summary"] = f"{profile['market_state_label']} | 观望优先 | {';'.join(no_trade_reasons[:2])}" else: profile["summary"] = ( f"{profile['market_state_label']} | " f"允许: {', '.join(profile['allowed_setups']) if profile['allowed_setups'] else '空仓'}" ) return profile