feat: 关羽完成实时风险监控系统原型开发 - 16分钟冲刺完成
This commit is contained in:
@@ -0,0 +1,406 @@
|
||||
"""
|
||||
实时风险阈值监控系统
|
||||
============
|
||||
|
||||
实时监控风险指标,触发阈值自动预警:
|
||||
- 单日回撤预警
|
||||
- 累计回撤预警
|
||||
- 高仓位预警
|
||||
- 集中度预警
|
||||
- VaR超限预警
|
||||
"""
|
||||
from typing import Dict, List, Optional, Callable
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from risk_calculator import RiskMetrics, RealTimeRiskCalculator
|
||||
|
||||
|
||||
class AlertLevel(Enum):
|
||||
"""预警级别"""
|
||||
INFO = "信息"
|
||||
WARNING = "警告"
|
||||
CRITICAL = "严重"
|
||||
EMERGENCY = "紧急"
|
||||
|
||||
|
||||
@dataclass
|
||||
class RiskAlert:
|
||||
"""风险预警"""
|
||||
alert_id: str
|
||||
alert_type: str
|
||||
level: AlertLevel
|
||||
metric_name: str
|
||||
current_value: float
|
||||
threshold: float
|
||||
timestamp: datetime
|
||||
message: str
|
||||
handled: bool = False
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
return {
|
||||
'alert_id': self.alert_id,
|
||||
'alert_type': self.alert_type,
|
||||
'level': self.level.value,
|
||||
'metric_name': self.metric_name,
|
||||
'current_value': self.current_value,
|
||||
'threshold': self.threshold,
|
||||
'timestamp': self.timestamp.isoformat(),
|
||||
'message': self.message,
|
||||
'handled': self.handled
|
||||
}
|
||||
|
||||
|
||||
class ThresholdConfig:
|
||||
"""阈值配置"""
|
||||
|
||||
def __init__(self,
|
||||
daily_drawdown_warning: float = 0.03,
|
||||
daily_drawdown_critical: float = 0.05,
|
||||
total_drawdown_warning: float = 0.10,
|
||||
total_drawdown_critical: float = 0.15,
|
||||
max_drawdown_critical: float = 0.20,
|
||||
position_pct_warning: float = 0.80,
|
||||
position_pct_critical: float = 0.95,
|
||||
concentration_warning: float = 0.30,
|
||||
concentration_critical: float = 0.50,
|
||||
var_95_warning: float = 0.03,
|
||||
var_95_critical: float = 0.05,
|
||||
volatility_warning: float = 0.30,
|
||||
volatility_critical: float = 0.50):
|
||||
"""初始化阈值配置"""
|
||||
self.daily_drawdown_warning = daily_drawdown_warning
|
||||
self.daily_drawdown_critical = daily_drawdown_critical
|
||||
self.total_drawdown_warning = total_drawdown_warning
|
||||
self.total_drawdown_critical = total_drawdown_critical
|
||||
self.max_drawdown_critical = max_drawdown_critical
|
||||
self.position_pct_warning = position_pct_warning
|
||||
self.position_pct_critical = position_pct_critical
|
||||
self.concentration_warning = concentration_warning
|
||||
self.concentration_critical = concentration_critical
|
||||
self.var_95_warning = var_95_warning
|
||||
self.var_95_critical = var_95_critical
|
||||
self.volatility_warning = volatility_warning
|
||||
self.volatility_critical = volatility_critical
|
||||
|
||||
@classmethod
|
||||
def conservative(cls) -> 'ThresholdConfig':
|
||||
"""保守配置(严格控制)"""
|
||||
return cls(
|
||||
daily_drawdown_warning=0.02,
|
||||
daily_drawdown_critical=0.03,
|
||||
total_drawdown_warning=0.08,
|
||||
total_drawdown_critical=0.12,
|
||||
max_drawdown_critical=0.15,
|
||||
position_pct_warning=0.70,
|
||||
position_pct_critical=0.85,
|
||||
concentration_warning=0.25,
|
||||
concentration_critical=0.40,
|
||||
var_95_warning=0.02,
|
||||
var_95_critical=0.03,
|
||||
volatility_warning=0.25,
|
||||
volatility_critical=0.40
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def aggressive(cls) -> 'ThresholdConfig':
|
||||
"""进取配置(风险承受较高)"""
|
||||
return cls(
|
||||
daily_drawdown_warning=0.05,
|
||||
daily_drawdown_critical=0.08,
|
||||
total_drawdown_warning=0.15,
|
||||
total_drawdown_critical=0.20,
|
||||
max_drawdown_critical=0.25,
|
||||
position_pct_warning=0.85,
|
||||
position_pct_critical=0.95,
|
||||
concentration_warning=0.40,
|
||||
concentration_critical=0.60,
|
||||
var_95_warning=0.04,
|
||||
var_95_critical=0.06,
|
||||
volatility_warning=0.40,
|
||||
volatility_critical=0.60
|
||||
)
|
||||
|
||||
|
||||
class RealTimeRiskMonitor:
|
||||
"""实时风险监控器"""
|
||||
|
||||
def __init__(self,
|
||||
config: Optional[ThresholdConfig] = None,
|
||||
risk_calculator: Optional[RealTimeRiskCalculator] = None):
|
||||
"""初始化"""
|
||||
self.config = config or ThresholdConfig()
|
||||
self.risk_calculator = risk_calculator or RealTimeRiskCalculator()
|
||||
|
||||
# 预警列表
|
||||
self.alerts: List[RiskAlert] = []
|
||||
# 未处理预警
|
||||
self.unhandled_alerts: List[RiskAlert] = []
|
||||
# 预警回调
|
||||
self.on_alert_callback: Optional[Callable[[RiskAlert], None]] = None
|
||||
|
||||
# 告警计数
|
||||
self._alert_counter = 0
|
||||
|
||||
def _generate_alert_id(self) -> str:
|
||||
"""生成预警ID"""
|
||||
self._alert_counter += 1
|
||||
ts = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
return f"ALERT-{ts}-{self._alert_counter:04d}"
|
||||
|
||||
def check_daily_drawdown(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查单日回撤"""
|
||||
dd = metrics.daily_drawdown
|
||||
|
||||
if dd >= self.config.daily_drawdown_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="DAILY_DRAWDOWN",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="单日回撤",
|
||||
current_value=dd,
|
||||
threshold=self.config.daily_drawdown_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"单日回撤 {dd:.2%} 超过临界阈值 {self.config.daily_drawdown_critical:.2%}"
|
||||
)
|
||||
elif dd >= self.config.daily_drawdown_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="DAILY_DRAWDOWN",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="单日回撤",
|
||||
current_value=dd,
|
||||
threshold=self.config.daily_drawdown_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"单日回撤 {dd:.2%} 超过警告阈值 {self.config.daily_drawdown_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_total_drawdown(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查累计回撤"""
|
||||
dd = metrics.total_drawdown
|
||||
|
||||
if dd >= self.config.total_drawdown_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="TOTAL_DRAWDOWN",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="累计回撤",
|
||||
current_value=dd,
|
||||
threshold=self.config.total_drawdown_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"累计回撤 {dd:.2%} 超过临界阈值 {self.config.total_drawdown_critical:.2%}"
|
||||
)
|
||||
elif dd >= self.config.total_drawdown_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="TOTAL_DRAWDOWN",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="累计回撤",
|
||||
current_value=dd,
|
||||
threshold=self.config.total_drawdown_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"累计回撤 {dd:.2%} 超过警告阈值 {self.config.total_drawdown_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_max_drawdown(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查最大回撤"""
|
||||
dd = metrics.max_drawdown
|
||||
|
||||
if dd >= self.config.max_drawdown_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="MAX_DRAWDOWN",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="最大回撤",
|
||||
current_value=dd,
|
||||
threshold=self.config.max_drawdown_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"最大回撤 {dd:.2%} 超过临界阈值 {self.config.max_drawdown_critical:.2%},建议紧急止损"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_position_pct(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查仓位比例"""
|
||||
pct = metrics.position_pct
|
||||
|
||||
if pct >= self.config.position_pct_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="HIGH_POSITION",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="仓位比例",
|
||||
current_value=pct,
|
||||
threshold=self.config.position_pct_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"仓位比例 {pct:.2%} 超过临界阈值 {self.config.position_pct_critical:.2%}"
|
||||
)
|
||||
elif pct >= self.config.position_pct_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="HIGH_POSITION",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="仓位比例",
|
||||
current_value=pct,
|
||||
threshold=self.config.position_pct_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"仓位比例 {pct:.2%} 超过警告阈值 {self.config.position_pct_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_concentration(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查集中度"""
|
||||
pct = metrics.concentration_pct
|
||||
|
||||
if pct >= self.config.concentration_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="CONCENTRATION",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="持仓集中度",
|
||||
current_value=pct,
|
||||
threshold=self.config.concentration_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"最大持仓集中度 {pct:.2%} 超过临界阈值 {self.config.concentration_critical:.2%}"
|
||||
)
|
||||
elif pct >= self.config.concentration_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="CONCENTRATION",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="持仓集中度",
|
||||
current_value=pct,
|
||||
threshold=self.config.concentration_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"最大持仓集中度 {pct:.2%} 超过警告阈值 {self.config.concentration_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_var(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查VaR"""
|
||||
var = metrics.var_95
|
||||
|
||||
if var >= self.config.var_95_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="VAR_OVERFLOW",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="VaR(95%)",
|
||||
current_value=var,
|
||||
threshold=self.config.var_95_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"95%置信度VaR {var:.2%} 超过临界阈值 {self.config.var_95_critical:.2%}"
|
||||
)
|
||||
elif var >= self.config.var_95_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="VAR_OVERFLOW",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="VaR(95%)",
|
||||
current_value=var,
|
||||
threshold=self.config.var_95_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"95%置信度VaR {var:.2%} 超过警告阈值 {self.config.var_95_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_volatility(self, metrics: RiskMetrics) -> Optional[RiskAlert]:
|
||||
"""检查波动率"""
|
||||
vol = metrics.volatility
|
||||
|
||||
if vol >= self.config.volatility_critical:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="HIGH_VOLATILITY",
|
||||
level=AlertLevel.CRITICAL,
|
||||
metric_name="年化波动率",
|
||||
current_value=vol,
|
||||
threshold=self.config.volatility_critical,
|
||||
timestamp=datetime.now(),
|
||||
message=f"年化波动率 {vol:.2%} 超过临界阈值 {self.config.volatility_critical:.2%}"
|
||||
)
|
||||
elif vol >= self.config.volatility_warning:
|
||||
return RiskAlert(
|
||||
alert_id=self._generate_alert_id(),
|
||||
alert_type="HIGH_VOLATILITY",
|
||||
level=AlertLevel.WARNING,
|
||||
metric_name="年化波动率",
|
||||
current_value=vol,
|
||||
threshold=self.config.volatility_warning,
|
||||
timestamp=datetime.now(),
|
||||
message=f"年化波动率 {vol:.2%} 超过警告阈值 {self.config.volatility_warning:.2%}"
|
||||
)
|
||||
return None
|
||||
|
||||
def check_all(self, metrics: RiskMetrics) -> List[RiskAlert]:
|
||||
"""检查所有阈值"""
|
||||
new_alerts = []
|
||||
|
||||
checks = [
|
||||
self.check_daily_drawdown,
|
||||
self.check_total_drawdown,
|
||||
self.check_max_drawdown,
|
||||
self.check_position_pct,
|
||||
self.check_concentration,
|
||||
self.check_var,
|
||||
self.check_volatility
|
||||
]
|
||||
|
||||
for check_func in checks:
|
||||
alert = check_func(metrics)
|
||||
if alert:
|
||||
new_alerts.append(alert)
|
||||
|
||||
# 添加到列表
|
||||
for alert in new_alerts:
|
||||
self.alerts.append(alert)
|
||||
self.unhandled_alerts.append(alert)
|
||||
if self.on_alert_callback:
|
||||
self.on_alert_callback(alert)
|
||||
|
||||
return new_alerts
|
||||
|
||||
def update_and_check(self, timestamp: datetime,
|
||||
total_capital: float, cash: float) -> RiskMetrics:
|
||||
"""更新并检查所有风险指标"""
|
||||
metrics = self.risk_calculator.calculate_all_metrics(
|
||||
timestamp, total_capital, cash
|
||||
)
|
||||
new_alerts = self.check_all(metrics)
|
||||
|
||||
if new_alerts:
|
||||
print(f"⚠️ 产生 {len(new_alerts)} 个新风险预警")
|
||||
for alert in new_alerts:
|
||||
print(f" [{alert.level.value}] {alert.message}")
|
||||
|
||||
return metrics
|
||||
|
||||
def mark_handled(self, alert_id: str) -> bool:
|
||||
"""标记预警已处理"""
|
||||
for alert in self.unhandled_alerts:
|
||||
if alert.alert_id == alert_id:
|
||||
alert.handled = True
|
||||
self.unhandled_alerts.remove(alert)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_unhandled_alerts(self) -> List[RiskAlert]:
|
||||
"""获取未处理预警"""
|
||||
return self.unhandled_alerts
|
||||
|
||||
def get_all_alerts(self) -> List[RiskAlert]:
|
||||
"""获取所有预警"""
|
||||
return self.alerts
|
||||
|
||||
def get_alerts_by_level(self, level: AlertLevel) -> List[RiskAlert]:
|
||||
"""按级别获取预警"""
|
||||
return [a for a in self.alerts if a.level == level]
|
||||
|
||||
def has_critical_alerts(self) -> bool:
|
||||
"""是否有严重级别的未处理预警"""
|
||||
return any(a.level == AlertLevel.CRITICAL for a in self.unhandled_alerts)
|
||||
|
||||
def has_emergency_alerts(self) -> bool:
|
||||
"""是否有紧急级别的未处理预警"""
|
||||
return any(a.level == AlertLevel.EMERGENCY for a in self.unhandled_alerts)
|
||||
Reference in New Issue
Block a user