""" RiskAgent — 仓位控制与风险预警。 根据市场波动率、回撤、相关性输出仓位建议和止损线。 """ import numpy as np import pandas as pd from agents.base import BaseAgent class RiskAgent(BaseAgent): """仓位控制 Agent。""" name = "Risk" description = "仓位控制与风险预警" # 风险等级阈值 THRESHOLDS = { "high": {"vol": 35, "dd": -15}, "medium": {"vol": 25, "dd": -8}, } def execute( self, holdings: dict[str, float] | None = None, market_index: str = "000001.SH", # 上证指数,非个股 ts_codes: list[str] | None = None, ) -> dict: """ 评估市场风险并输出仓位建议。 参数: holdings: {ts_code: 持仓比例} market_index: 市场参考标的 ts_codes: 持仓股票列表 返回: {"risk_level": str, "target_exposure": float, "indicators": dict, "alerts": list} """ holdings = holdings or {} price = self.dm.get_daily(market_index) if price is None or price.empty: return self._default_result() price = price.set_index("trade_date").sort_index() close = price["close"] daily_ret = close.pct_change().dropna() # 市场波动率(年化) market_vol = float(daily_ret.tail(252).std() * np.sqrt(252) * 100) if len(daily_ret) >= 20 else 30 # 当前回撤 peak = close.expanding().max() current_dd = float((close.iloc[-1] / peak.iloc[-1] - 1) * 100) # 风险等级 if market_vol > self.THRESHOLDS["high"]["vol"] or current_dd < self.THRESHOLDS["high"]["dd"]: risk_level = "high" target_exposure = 0.30 elif market_vol > self.THRESHOLDS["medium"]["vol"] or current_dd < self.THRESHOLDS["medium"]["dd"]: risk_level = "medium" target_exposure = 0.60 else: risk_level = "low" target_exposure = 0.85 # 最近 N 日涨跌 ret_5d = float(close.pct_change(5).iloc[-1] * 100) if len(close) >= 6 else 0 ret_20d = float(close.pct_change(20).iloc[-1] * 100) if len(close) >= 21 else 0 # 单票上限(风险越高越集中) max_single = 0.15 if risk_level == "low" else (0.10 if risk_level == "medium" else 0.05) stop_loss = -0.05 if risk_level == "low" else (-0.08 if risk_level == "medium" else -0.12) # 持仓预警 alerts = [] if current_dd < -10: alerts.append(f"市场回撤 {current_dd:.1f}%,考虑减仓") if market_vol > 30: alerts.append(f"市场波动率 {market_vol:.1f}%,处于高位") for code, pct in holdings.items(): if pct > max_single: alerts.append(f"{code} 仓位 {pct:.0%} 超过上限 {max_single:.0%}") self.log(f"风险={risk_level} 波动={market_vol:.1f}% 回撤={current_dd:.1f}% 仓位→{target_exposure:.0%}") return { "risk_level": risk_level, "target_exposure": round(target_exposure, 2), "max_single_position": round(max_single, 2), "stop_loss": round(stop_loss, 2), "indicators": { "market_volatility": round(market_vol, 1), "current_drawdown": round(current_dd, 1), "return_5d": round(ret_5d, 1), "return_20d": round(ret_20d, 1), "close": round(float(close.iloc[-1]), 2), }, "alerts": alerts, } @staticmethod def _default_result() -> dict: return { "risk_level": "medium", "target_exposure": 0.60, "max_single_position": 0.10, "stop_loss": -0.08, "indicators": {}, "alerts": ["数据不足,使用默认风险参数"], }