""" RiskAgent — 仓位控制与风险预警。 根据市场波动率、回撤、相关性输出仓位建议和止损线。 """ import numpy as np import pandas as pd from agents.base import BaseAgent from config.settings import ( RISK_THRESHOLDS, RISK_EXPOSURE, RISK_MAX_SINGLE, RISK_STOP_LOSS, ) class RiskAgent(BaseAgent): """仓位控制 Agent。""" name = "Risk" description = "仓位控制与风险预警" # 风险等级阈值(来自配置中心) THRESHOLDS = RISK_THRESHOLDS 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" elif market_vol > self.THRESHOLDS["medium"]["vol"] or current_dd < self.THRESHOLDS["medium"]["dd"]: risk_level = "medium" else: risk_level = "low" target_exposure = RISK_EXPOSURE[risk_level] # 最近 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 = RISK_MAX_SINGLE[risk_level] stop_loss = RISK_STOP_LOSS[risk_level] # 持仓预警 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": ["数据不足,使用默认风险参数"], }