Files
sanguo_quant_live/strategies/structured-dynamic-factors-20260327/risk_warning_execution.py
T

401 lines
14 KiB
Python

"""
风控预警和执行系统
功能:
1. 三级预警机制(黄/橙/红)
2. 预警触发后的执行规则
3. 三级风控体系(个股→板块→整体)
4. 执行记录留存,方便回测复盘
Author: 关羽(云长)
Date: 2026-03-27
"""
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple, Callable
from enum import Enum
from datetime import datetime
import json
class WarningLevel(Enum):
YELLOW = 1 # 黄色预警:观察
ORANGE = 2 # 橙色预警:减仓
RED = 3 # 红色预警:清仓
@dataclass
class WarningMessage:
"""预警消息"""
level: WarningLevel
scope: str # individual / sector / portfolio
target: str # code / sector name
level_name: str
reason: str
timestamp: str
current_position: float # 当前仓位
target_position: float # 目标仓位
executed: bool = False
@dataclass
class RiskWarningConfig:
"""风控预警参数配置"""
# 个股预警阈值
yellow_threshold: float = 0.2 # 风险分0.2→黄
orange_threshold: float = 0.4 # 风险分0.4→橙
red_threshold: float = 0.7 # 风险分0.7→红
# 执行规则:仓位调整比例
yellow_reduce: float = 0.0 # 黄色不减仓
orange_reduce: float = 0.5 # 橙色减仓一半
red_reduce: float = 1.0 # 红色清仓
# 板块预警阈值
sector_warning_threshold: int = 10 # 风险积分10→黄
sector_orange_threshold: int = 25 # 积分25→橙
sector_red_threshold: int = 40 # 积分40→红
# 组合预警阈值
portfolio_yellow: float = 0.2 # 总分0.2→黄
portfolio_orange: float = 0.4 # 总分0.4→橙
portfolio_red: float = 0.7 # 总分0.7→红
default_config = RiskWarningConfig()
class IndividualRiskWarning:
"""个股风险预警"""
def __init__(self, config: RiskWarningConfig = None):
self.config = config or default_config
def evaluate(self, risk_score: float) -> WarningLevel:
"""根据风险分评估预警等级"""
if risk_score >= self.config.red_threshold:
return WarningLevel.RED
elif risk_score >= self.config.orange_threshold:
return WarningLevel.ORANGE
elif risk_score >= self.config.yellow_threshold:
return WarningLevel.YELLOW
else:
return None # 无预警
def calculate_target_position(self,
current_position: float,
level: WarningLevel) -> float:
"""计算目标仓位"""
if level == WarningLevel.RED:
return current_position * (1 - self.config.red_reduce)
elif level == WarningLevel.ORANGE:
return current_position * (1 - self.config.orange_reduce)
elif level == WarningLevel.YELLOW:
return current_position * (1 - self.config.yellow_reduce)
else:
return current_position
class SectorRiskWarning:
"""板块风险预警"""
def __init__(self, config: RiskWarningConfig = None):
self.config = config or default_config
def evaluate(self, risk_score: int) -> WarningLevel:
if risk_score >= self.config.sector_red_threshold:
return WarningLevel.RED
elif risk_score >= self.config.sector_orange_threshold:
return WarningLevel.ORANGE
elif risk_score >= self.config.sector_warning_threshold:
return WarningLevel.YELLOW
else:
return None
def get_target_ratio(self, current_ratio: float, level: WarningLevel) -> float:
"""计算目标仓位比例"""
if level == WarningLevel.RED:
return current_ratio * 0.25 # 保留25%
elif level == WarningLevel.ORANGE:
return current_ratio * 0.5 # 保留一半
elif level == WarningLevel.YELLOW:
return current_ratio # 不变
else:
return current_ratio
class PortfolioRiskWarning:
"""整体组合风险预警"""
def __init__(self, config: RiskWarningConfig = None):
self.config = config or default_config
def evaluate(self, total_risk_score: float) -> WarningLevel:
if total_risk_score >= self.config.portfolio_red:
return WarningLevel.RED
elif total_risk_score >= self.config.portfolio_orange:
return WarningLevel.ORANGE
elif total_risk_score >= self.config.portfolio_yellow:
return WarningLevel.YELLOW
else:
return None
def get_overall_target_ratio(self, level: WarningLevel) -> float:
"""整体目标仓位比例"""
if level == WarningLevel.RED:
return 0.25 # 保留25%
elif level == WarningLevel.ORANGE:
return 0.5 # 保留一半
elif level == WarningLevel.YELLOW:
return 0.8 # 保留80%
else:
return 1.0 # 满仓
class RiskWarningSystem:
"""三级风控预警总系统(个股→板块→整体)"""
def __init__(self, config: RiskWarningConfig = None):
self.config = config or default_config
self.individual_warn = IndividualRiskWarning(config)
self.sector_warn = SectorRiskWarning(config)
self.portfolio_warn = PortfolioRiskWarning(config)
self.warning_history: List[WarningMessage] = []
self.execution_callback: Optional[Callable] = None
def register_execution_callback(self, callback: Callable):
"""注册执行回调,预警触发后自动调用"""
self.execution_callback = callback
def evaluate_stock(self,
code: str,
name: str,
risk_score: float,
current_position: float) -> Optional[WarningMessage]:
"""评估个股风险,产生预警"""
level = self.individual_warn.evaluate(risk_score)
if level is None:
return None
target = self.individual_warn.calculate_target_position(current_position, level)
level_names = {
WarningLevel.YELLOW: "黄色",
WarningLevel.ORANGE: "橙色",
WarningLevel.RED: "红色"
}
msg = WarningMessage(
level=level,
scope="individual",
target=f"{code} {name}",
level_name=level_names[level],
reason=f"个股风险分{risk_score:.2f},触发{level_names[level]}预警",
timestamp=datetime.now().isoformat(),
current_position=current_position,
target_position=target
)
self.warning_history.append(msg)
return msg
def evaluate_sector(self,
name: str,
risk_score: int,
current_ratio: float) -> Optional[WarningMessage]:
"""评估板块风险,产生预警"""
level = self.sector_warn.evaluate(risk_score)
if level is None:
return None
target = self.sector_warn.get_target_ratio(current_ratio, level)
level_names = {
WarningLevel.YELLOW: "黄色",
WarningLevel.ORANGE: "橙色",
WarningLevel.RED: "红色"
}
msg = WarningMessage(
level=level,
scope="sector",
target=name,
level_name=level_names[level],
reason=f"板块风险积分{risk_score},触发{level_names[level]}预警",
timestamp=datetime.now().isoformat(),
current_position=current_ratio,
target_position=target
)
self.warning_history.append(msg)
return msg
def evaluate_portfolio(self, total_risk_score: float) -> Optional[WarningMessage]:
"""评估整体组合风险"""
level = self.portfolio_warn.evaluate(total_risk_score)
if level is None:
return None
target_ratio = self.portfolio_warn.get_overall_target_ratio(level)
level_names = {
WarningLevel.YELLOW: "黄色",
WarningLevel.ORANGE: "橙色",
WarningLevel.RED: "红色"
}
msg = WarningMessage(
level=level,
scope="portfolio",
target="整体组合",
level_name=level_names[level],
reason=f"组合总风险分{total_risk_score:.2f},触发{level_names[level]}预警",
timestamp=datetime.now().isoformat(),
current_position=1.0, # 当前总仓位比例
target_position=target_ratio
)
self.warning_history.append(msg)
return msg
def get_pending_warnings(self) -> List[WarningMessage]:
"""获取未执行预警"""
return [w for w in self.warning_history if not w.executed]
def mark_executed(self, warning: WarningMessage):
"""标记已执行"""
warning.executed = True
def get_warning_report(self) -> str:
"""生成预警报告"""
pending = self.get_pending_warnings()
all_warnings = self.warning_history
levels = {
WarningLevel.YELLOW: "🟡 黄色",
WarningLevel.ORANGE: "🟠 橙色",
WarningLevel.RED: "🔴 红色",
}
lines = []
lines.append("=" * 60)
lines.append("风控预警系统报告")
lines.append("=" * 60)
lines.append(f"总预警数量: {len(all_warnings)}")
lines.append(f"未执行预警: {len(pending)}")
lines.append("")
if pending:
lines.append("⚠️ 待执行预警:")
for w in pending:
lines.append(f" [{levels[w.level]}] [{w.scope}] {w.target}")
lines.append(f" 原因: {w.reason}")
lines.append(f" 当前仓位: {w.current_position:.2%} → 目标仓位: {w.target_position:.2%}")
lines.append("")
else:
lines.append("✅ 无待执行预警")
lines.append("=" * 60)
return "\n".join(lines)
def export_history(self, path: str):
"""导出预警历史到json,方便复盘"""
data = []
for w in self.warning_history:
data.append({
"level": w.level.value,
"scope": w.scope,
"target": w.target,
"level_name": w.level_name,
"reason": w.reason,
"timestamp": w.timestamp,
"current_position": w.current_position,
"target_position": w.target_position,
"executed": w.executed
})
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
class RiskExecution:
"""风控执行器,根据预警执行调仓"""
def __init__(self, warning_system: RiskWarningSystem):
self.warning_system = warning_system
def execute_warnings(self) -> List[dict]:
"""执行所有待执行预警"""
pending = self.warning_system.get_pending_warnings()
execution_records = []
for warning in pending:
record = {
"scope": warning.scope,
"target": warning.target,
"level": warning.level_name,
"current": warning.current_position,
"target": warning.target_position,
"adjust_amount": warning.current_position - warning.target_position,
"timestamp": warning.timestamp
}
execution_records.append(record)
self.warning_system.mark_executed(warning)
return execution_records
def get_execution_report(self, records: List[dict]) -> str:
"""生成执行报告"""
lines = []
lines.append("=" * 60)
lines.append("风控执行报告")
lines.append("=" * 60)
lines.append(f"本次执行 {len(records)} 条预警")
lines.append("")
for r in records:
lines.append(f"⚠️ [{r['level']}] {r['scope']} {r['target']}:")
lines.append(f" 仓位调整: {r['current']:.2%}{r['target']:.2%},需要卖出{r['adjust_amount']:.2%}")
if not records:
lines.append("✅ 无需要执行的调整")
lines.append("=" * 60)
return "\n".join(lines)
if __name__ == "__main__":
print("=== 测试风控预警和执行系统 ===\n")
system = RiskWarningSystem(default_config)
# 测试个股预警
msg1 = system.evaluate_stock("600000", "浦发银行", 0.45, 100000)
msg2 = system.evaluate_stock("000001", "平安银行", 0.75, 150000)
msg3 = system.evaluate_stock("002XXX", "AI龙头", 0.15, 80000)
print("个股预警测试:")
for msg in [msg1, msg2, msg3]:
if msg:
print(f" {msg.target}: {msg.level_name} → 当前{msg.current_position:.0f} → 目标{msg.target_position:.0f}")
print()
# 测试板块预警
s_msg1 = system.evaluate_sector("AI", 30, 0.22)
s_msg2 = system.evaluate_sector("新能源", 15, 0.10)
print("板块预警测试:")
for msg in [s_msg1, s_msg2]:
if msg:
print(f" {msg.target}: {msg.level_name} → 当前{msg.current_position:.1%} → 目标{msg.target_position:.1%}")
print()
# 测试组合预警
p_msg = system.evaluate_portfolio(0.45)
print(f"组合预警: {p_msg.level_name if p_msg else 'None'}")
print()
# 生成报告
print(system.get_warning_report())
print()
# 执行预警
executor = RiskExecution(system)
records = executor.execute_warnings()
print(executor.get_execution_report(records))