20260327 今日开发完成:三个策略新增+结构化适配+消息风控+任务跟踪

This commit is contained in:
cfdaily
2026-03-27 16:44:01 +08:00
parent f043f123a1
commit dd1b06ba4a
41 changed files with 12115 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
# 通用风控模块 - 个股利好利空风险监控
## 模块说明
`news_risk_monitor.py` + `structural_market_risk.py` 是通用模块,适配A股结构化行情,可被各个策略共享使用。
### 核心功能
基于公开数据,提前预判潜在消息面风险和板块风险:
| 监控维度 | 预警规则 | 预判逻辑 |
|----------|----------|----------|
| **量价异常** | 近5日放量下跌 >7%,无公开消息 | 可能利空提前泄露,资金先跑 |
| | 成交量放天量 >2倍,但股价不涨 | 可能利好兑现,主力出货 |
| | 向下跳空缺口未回补 | 技术面偏空,趋势向下 |
| **融资变化** | 一周融资余额减少 >20% | 杠杆资金出逃,不看好后市 |
| | 融资买入占成交额 >20% | 杠杆比例过高,波动风险大 |
| **龙虎大宗** | 机构大额卖出上榜 | 机构出逃,看空 |
| | 大宗折价 >8% | 大股东折价出货,利空 |
| **舆情监控** | 讨论量突然暴涨 >5倍 | 热度太高,往往见顶 |
| | 舆情情感分 < -0.5 | 市场一致看空,情绪偏空 |
| **国际联动** | A+H股H股隔夜跌幅 >3% | A股大概率跟随下跌 |
| | 大宗商品股对应期货跌幅 >4% | 个股价格承压 |
| | 美股隔夜跌幅 >2% | A股开盘承压,系统性风险 |
| | 重大国际利空消息 | 直接预警,建议降仓 |
| **结构化行情风控** | 单板块仓位 >15% | 提示减仓分散 |
| | 板块累计涨幅 >50% | 风险评分放大,警惕利好出尽 |
| | 单一风格仓位 >40% | 提示超配风险 |
| | 冷门板块连续大跌 >20% | 如果基本面没问题,提示低吸机会 |
| | 热点板块消息 | 风险评分放大2倍,灵敏度提高
### 量化规则
**风险等级划分:**
| 总分 | 等级 | 操作建议 |
|------|------|----------|
| ≥40 | 🔴 EXIT | 建议清仓离场 |
| 25~40 | 🟠 REDUCE | 建议减仓 |
| 10~25 | 🟡 WATCH | 继续观察,不新开仓 |
| <10 | 🟢 SAFE | 安全,按计划操作 |
**整合进五维风险评估:**
原来四个维度 + 消息风险维度:
1. 流动性风险 (15%)
2. 估值风险 (20%)
3. 技术面风险 (20%)
4. 基本面风险 (20%)
5. **消息面风险 (25%)** → 权重更高,因为黑天鹅危害大
### 使用方法
```python
# 导入通用模块
import sys
sys.path.append("../../../guanyu-risk/common/")
from news_risk_monitor import NewsRiskMonitor, StockNewsData, FiveDimensionRiskAssessment
# 初始化监控器
monitor = NewsRiskMonitor()
# 填充数据
data = StockNewsData(
code="600000",
name="浦发银行",
recent_vol_change=1.2, # 成交量较20日均变化
recent_pct_change=-8.5, # 近5日涨跌幅%
gap_down=True, # 是否有向下缺口
finance_balance_change=-0.25, # 融资余额周变化比例
has_large_order=False, # 龙虎榜是否大额卖出
has_bulk_discount=False, # 是否有大宗折价
discussion_count_change=3.0, # 讨论量较上周变化倍数
sentiment_score=-0.6, # 舆情情感分-1~1
# 国际联动数据
is_ah=False,
ah_hk_overnight_change=0,
is_commodity_related=False,
commodity_future_overnight_change=0,
us_index_overnight_change=-1.2,
has_major_international_news=False
)
# 分析
result = monitor.analyze_stock(data)
print(monitor.get_risk_report(result))
print(f"风险等级: {result.risk_level}")
print(f"建议: {result.suggestion}")
# 整合到五维风险评估
five_dim = FiveDimensionRiskAssessment(monitor)
eval_result = five_dim.calculate_total_risk(
liquidity_risk=0.2,
valuation_risk=0.5,
technical_risk=0.3,
fundamental_risk=0.4,
news_data=data
)
print(f"综合风险分: {eval_result['total_risk_score']:.2f}")
print(f"建议: {eval_result['suggestion']}")
```
### 数据获取说明
公开数据都可以从这些渠道获取:
- 量价、融资:东方财富、TuShare、AkShare 直接接口
- 龙虎榜:交易所官网、东方财富
- 大宗交易:交易所、TuShare
- 舆情:雪球、股吧公开数据,可以用爬虫获取讨论量
## 设计思路
核心思想:**A股很多消息会提前泄露,从量价、资金、情绪上能提前发现痕迹**,不等公告出来再反应,提前减仓规避黑天鹅。
层层设防:就算技术面、基本面都没问题,消息面不对劲,直接降仓,把风险拦在前面。
## 作者
关羽(云长)
风险都督
2026-03-27
+629
View File
@@ -0,0 +1,629 @@
"""
个股+板块利好利空风险监控模块
功能:基于公开数据提前预判消息面风险,适配结构化行情
监控维度:
1. 量价异常:没有消息但突然大幅波动,可能是提前泄密
2. 融资余额变化:融资快速增减可能预示资金面变化
3. 龙虎榜/大宗交易:机构大额进出
4. 舆情频率:股吧雪球讨论热度异常变化
5. 国际联动风险:外盘、商品、国际消息对A股影响预判
6. 结构化行情风控:板块集中度风险、热点透支风险
Author: 关羽(云长)
Date: 2026-03-27
"""
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple
from enum import Enum
class NewsSentiment(Enum):
STRONG_BULL = 2
BULL = 1
NEUTRAL = 0
BEAR = -1
STRONG_BEAR = -2
class RiskLevel(Enum):
SAFE = 0
WATCH = 1
REDUCE = 2
EXIT = 3
@dataclass
class StockNewsData:
"""个股监控数据"""
code: str
name: str
# 1. 量价数据
recent_vol_change: float # 近5日成交量相比20日均值变化比例,比如0.5就是放量50%
recent_pct_change: float # 近5日涨跌幅(%
gap_up: bool = False # 是否高开缺口未回补
gap_down: bool = False # 是否低开缺口未回补
# 2. 融资数据
finance_balance_change: float = 0.0 # 融资余额变化比例(周)
finance_pct_of_volume: float = 0.0 # 融资买入额占成交额比例
# 3. 龙虎榜/大宗
has_large_order: bool = False # 近日是否有龙虎榜大额卖出
has_bulk_discount: bool = False # 是否有大宗折价交易
bulk_discount_pct: float = 0.0 # 大宗折价幅度
# 4. 舆情数据
discussion_count_change: float = 0.0 # 讨论量相比上周变化倍数
sentiment_score: float = 0.0 # 舆情情感分 -1~1,负=利空
# 5. 国际联动数据
is_ah: bool = False # 是否A+H股
ah_hk_overnight_change: float = 0.0 # 港股H股隔夜涨跌幅%
is_commodity_related: bool = False # 是否大宗商品相关(有色/化工/农业)
commodity_future_overnight_change: float = 0.0 # 对应期货隔夜涨跌幅%
us_index_overnight_change: float = 0.0 # 美股隔夜涨跌%
has_major_international_news: bool = False # 是否有重大国际消息(加息/地缘政治等)
international_news_sentiment: int = 0 # -1利空 0中性 1利好
# 6. 结构化行情数据
sector_name: str = "" # 板块名称
sector_style: str = "" # 风格名称(AI/新能源/消费等)
sector_total_gain: float = 0.0 # 板块近期累计涨幅%
sector_position_ratio: float = 0.0 # 当前板块仓位占总仓位比例
@dataclass
class NewsRiskResult:
"""消息风险评估结果"""
code: str
name: str
risk_level: RiskLevel
sentiment: NewsSentiment
risk_score: float # 总分 0~100,越高风险越大
bear_points: List[str] # 利空信号点
bull_points: List[str] # 利好信号点
suggestion: str # 操作建议
class PriceVolumeMonitor:
"""量价异常监控"""
def __init__(self,
vol_alert_threshold: float = 2.0, # 放量超过2倍预警
vol_crush_threshold: float = -0.5, # 缩量超过50%预警
drop_alert_threshold: float = -7.0): # 5日跌幅超过7%预警
self.vol_alert_threshold = vol_alert_threshold
self.vol_crush_threshold = vol_crush_threshold
self.drop_alert_threshold = drop_alert_threshold
def analyze(self, data: StockNewsData) -> Tuple[int, List[str], List[str]]:
"""
返回:(风险分增量, 利空点列表, 利好点列表)
"""
risk_score = 0
bear_points = []
bull_points = []
# 天量天价无消息,警惕利好兑现出货
if data.recent_vol_change >= self.vol_alert_threshold and data.recent_pct_change >= 5:
risk_score += 15
bear_points.append(f"近5日放量{data.recent_vol_change:.1f}倍,涨幅{data.recent_pct_change:.1f}%,可能利好提前泄露,警惕出货")
# 莫名其妙大跌,可能有利空提前泄露
if data.recent_pct_change <= self.drop_alert_threshold and data.recent_vol_change >= 0.5:
risk_score += 20
bear_points.append(f"近5日放量下跌{data.recent_pct_change:.1f}%,无公开消息,警惕利空提前泄露")
# 向下跳空缺口
if data.gap_down:
risk_score += 10
bear_points.append("存在向下跳空缺口未回补,技术形态偏空")
# 向上跳空缺口
if data.gap_up:
bull_points.append("存在向上跳空缺口未回补,技术形态偏多")
# 突然严重缩量,警惕流动性枯竭
if data.recent_vol_change <= self.vol_crush_threshold:
risk_score += 10
bear_points.append(f"成交量缩量{(data.recent_vol_change*100):.0f}%,警惕流动性风险")
return risk_score, bear_points, bull_points
class FinanceMonitor:
"""融资余额监控"""
def __init__(self,
increase_threshold: float = 0.3, # 融资余额增加超30%
decrease_threshold: float = -0.2): # 融资余额减少超20%
self.increase_threshold = increase_threshold
self.decrease_threshold = decrease_threshold
def analyze(self, data: StockNewsData) -> Tuple[int, List[str], List[str]]:
risk_score = 0
bear_points = []
bull_points = []
# 融资快速增加,看多情绪升温
if data.finance_balance_change >= self.increase_threshold:
bull_points.append(f"融资余额一周增加{data.finance_balance_change:.1%},杠杆资金看多")
# 融资快速减少,资金出逃,利空
if data.finance_balance_change <= self.decrease_threshold:
risk_score += 15
bear_points.append(f"融资余额一周减少{data.finance_balance_change:.1%},杠杆资金快速出逃,警惕利空")
# 融资买入占比过高,波动会放大
if data.finance_pct_of_volume >= 0.2:
risk_score += 5
bear_points.append(f"融资买入占成交额{data.finance_pct_of_volume:.1%},杠杆比例高,波动风险大")
return risk_score, bear_points, bull_points
class InstitutionalMonitor:
"""龙虎榜/大宗交易监控"""
def __init__(self,
bulk_discount_threshold: float = -0.08): # 折价超过8%预警
self.bulk_discount_threshold = bulk_discount_threshold
def analyze(self, data: StockNewsData) -> Tuple[int, List[str], List[str]]:
risk_score = 0
bear_points = []
bull_points = []
# 龙虎榜大额机构卖出
if data.has_large_order:
risk_score += 20
bear_points.append("龙虎榜出现机构大额卖出,机构出逃")
# 大宗折价交易
if data.has_bulk_discount and data.bulk_discount_pct <= self.bulk_discount_threshold:
risk_score += 15
bear_points.append(f"大宗交易折价{data.bulk_discount_pct:.1%},大股东折价出货")
elif data.has_bulk_discount:
bull_points.append("大宗交易平价/溢价成交,有机构接盘")
return risk_score, bear_points, bull_points
class SentimentMonitor:
"""舆情热度监控"""
def __init__(self,
hot_threshold: float = 5.0, # 讨论量增加5倍,太热预警
cold_threshold: float = -0.8): # 讨论量减少80%,太凉预警
self.hot_threshold = hot_threshold
self.cold_threshold = cold_threshold
def analyze(self, data: StockNewsData) -> Tuple[int, List[str], List[str]]:
risk_score = 0
bear_points = []
bull_points = []
# 讨论量突然暴涨,关注度太高,往往是见顶信号
if data.discussion_count_change >= self.hot_threshold:
risk_score += 10
bear_points.append(f"股吧/雪球讨论量增加{data.discussion_count_change:.1f}倍,热度异常,可能见顶")
# 舆情已经明显偏空
if data.sentiment_score <= -0.5:
risk_score += 10
bear_points.append(f"市场舆情偏空,情感分{data.sentiment_score:.2f}")
# 舆情明显偏多
elif data.sentiment_score >= 0.5:
bull_points.append(f"市场舆情偏多,情感分{data.sentiment_score:.2f}")
return risk_score, bear_points, bull_points
class InternationalLinkageMonitor:
"""国际联动风险监控"""
def __init__(self,
ah_change_threshold: float = -3.0, # H股隔夜跌幅超过3%预警
commodity_change_threshold: float = -4.0, # 商品期货跌幅超过4%预警
us_index_change_threshold: float = -2.0): # 美股跌幅超过2%预警
self.ah_change_threshold = ah_change_threshold
self.commodity_change_threshold = commodity_change_threshold
self.us_index_change_threshold = us_index_change_threshold
def analyze(self, data: StockNewsData) -> Tuple[int, List[str], List[str]]:
risk_score = 0
bear_points = []
bull_points = []
# 1. A+H股,H股隔夜大跌预警
if data.is_ah:
if data.ah_hk_overnight_change <= self.ah_change_threshold:
risk_score += 15
bear_points.append(f"A+H股,H股隔夜大跌{data.ah_hk_overnight_change:.1f}%A股大概率跟随低开,风险预警")
elif data.ah_hk_overnight_change >= 3.0:
bull_points.append(f"A+H股,H股隔夜大涨{data.ah_hk_overnight_change:.1f}%,对A股有正面带动")
# 2. 大宗商品相关个股,对应期货隔夜大跌预警
if data.is_commodity_related:
if data.commodity_future_overnight_change <= self.commodity_change_threshold:
risk_score += 15
bear_points.append(f"大宗商品股,对应期货隔夜大跌{data.commodity_future_overnight_change:.1f}%,个股承压")
elif data.commodity_future_overnight_change >= 4.0:
bull_points.append(f"大宗商品股,对应期货隔夜大涨{data.commodity_future_overnight_change:.1f}%,对个股有利")
# 3. 美股隔夜大跌,系统性风险预警
if data.us_index_overnight_change <= self.us_index_change_threshold:
risk_score += 10
bear_points.append(f"美股隔夜大跌{data.us_index_overnight_change:.1f}%A股开盘可能承压,系统性风险")
elif data.us_index_overnight_change >= 2.0:
bull_points.append(f"美股隔夜大涨{data.us_index_overnight_change:.1f}%,对A股开盘有利")
# 4. 重大国际消息
if data.has_major_international_news:
if data.international_news_sentiment == -1:
risk_score += 20
bear_points.append("重大国际利空消息(加息/地缘政治等),系统性风险上升,建议降仓")
elif data.international_news_sentiment == 1:
bull_points.append("重大国际利好消息,市场情绪偏向乐观")
return risk_score, bear_points, bull_points
class StructuralMarketRisk:
"""
结构化行情风控
A股现在经常是结构化行情,少数板块上涨,其他板块不动
需要控制板块集中度,防范热点透支
"""
def __init__(self,
single_sector_threshold: float = 0.15, # 单板块仓位超15%预警
sector_rally_threshold: float = 50.0, # 板块累计涨幅超50%预警
hot_news_sensitivity: float = 2.0): # 热点板块消息风险放大倍数
self.single_sector_threshold = single_sector_threshold
self.sector_rally_threshold = sector_rally_threshold
self.hot_news_sensitivity = hot_news_sensitivity
def analyze_sector_risk(self, data: StockNewsData, total_portfolio_sectors: Dict[str, float]) -> Tuple[int, List[str], List[str]]:
"""
分析板块风险
total_portfolio_sectors: {板块名称: 板块仓位比例},用来计算整体板块集中度
"""
risk_score = 0
bear_points = []
bull_points = []
# 1. 单板块仓位超过阈值,提醒分散
if data.sector_position_ratio > self.single_sector_threshold:
extra_risk = int((data.sector_position_ratio - self.single_sector_threshold) * 100)
risk_score += extra_risk
bear_points.append(f"单板块仓位{data.sector_position_ratio:.1%},超过{self.single_sector_threshold:.1%}预警,建议分散减仓")
# 2. 板块连续大涨,提醒风险
if data.sector_total_gain >= self.sector_rally_threshold:
risk_score += 15
bear_points.append(f"板块{data.sector_name}累计涨幅{data.sector_total_gain:.1f}%,超过{self.sector_rally_threshold:.0f}%,警惕利好出尽")
# 3. 冷门板块连续大跌,基本面没问题提示低吸机会
# 这里只做提示,实际需要结合基本面判断
if data.sector_total_gain <= -20 and data.sector_total_gain >= -40:
bull_points.append(f"板块{data.sector_name}累计跌幅{data.sector_total_gain:.1f}%,如果基本面没问题,可考虑适度低吸")
return risk_score, bear_points, bull_points
def adjust_news_risk_for_hot_sector(self, base_risk: int, data: StockNewsData) -> int:
"""热点板块消息风险灵敏度提高,放大风险分"""
if data.sector_total_gain >= 30:
# 热点板块,消息更容易透支,放大风险评分
return int(base_risk * self.hot_news_sensitivity)
return base_risk
class StyleConcentrationRisk:
"""风格集中度风险控制,不要全仓押注一种风格"""
def __init__(self, single_style_threshold: float = 0.4): # 单一风格超40%预警
self.single_style_threshold = single_style_threshold
def analyze_style_risk(self, total_style_pos: Dict[str, float]) -> Tuple[int, List[str]]:
"""检查风格集中度"""
risk_score = 0
bear_points = []
for style, ratio in total_style_pos.items():
if ratio >= self.single_style_threshold:
extra_risk = int((ratio - self.single_style_threshold) * 50)
risk_score += extra_risk
bear_points.append(f"单一风格{style}仓位{ratio:.1%},超过{self.single_style_threshold:.0%},建议分散配置")
return risk_score, bear_points
class NewsRiskMonitor:
"""总消息风险监控器,整合所有监控维度,适配结构化行情"""
def __init__(self):
self.pv_monitor = PriceVolumeMonitor()
self.fin_monitor = FinanceMonitor()
self.inst_monitor = InstitutionalMonitor()
self.sent_monitor = SentimentMonitor()
self.intl_monitor = InternationalLinkageMonitor()
self.structural_rc = StructuralMarketRisk()
def analyze_stock(self, data: StockNewsData, total_portfolio_sectors: Dict[str, float] = None) -> NewsRiskResult:
"""综合分析个股消息风险,支持结构化板块风控"""
total_portfolio_sectors = total_portfolio_sectors or {}
total_risk = 0
all_bear = []
all_bull = []
# 原有各维度打分
r1, b1, bl1 = self.pv_monitor.analyze(data)
r2, b2, bl2 = self.fin_monitor.analyze(data)
r3, b3, bl3 = self.inst_monitor.analyze(data)
r4, b4, bl4 = self.sent_monitor.analyze(data)
r5, b5, bl5 = self.intl_monitor.analyze(data)
total_risk = r1 + r2 + r3 + r4 + r5
all_bear = b1 + b2 + b3 + b4 + b5
all_bull = bl1 + bl2 + bl3 + bl4 + bl5
# 新增结构化行情板块风控
if data.sector_name:
r6, b6, bl6 = self.structural_rc.analyze_sector_risk(data, total_portfolio_sectors)
# 热点板块消息风险放大
base_news_risk = total_risk
total_risk = self.structural_rc.adjust_news_risk_for_hot_sector(base_news_risk, data) + r6
all_bear.extend(b6)
all_bull.extend(bl6)
# 计算风险等级
# 计算风险等级
if total_risk >= 40:
risk_level = RiskLevel.EXIT
elif total_risk >= 25:
risk_level = RiskLevel.REDUCE
elif total_risk >= 10:
risk_level = RiskLevel.WATCH
else:
risk_level = RiskLevel.SAFE
# 计算整体情绪
bull_count = len(all_bull)
bear_count = len(all_bear)
if bull_count - bear_count >= 2:
sentiment = NewsSentiment.STRONG_BULL
elif bull_count - bear_count >= 1:
sentiment = NewsSentiment.BULL
elif bear_count - bull_count >= 2:
sentiment = NewsSentiment.STRONG_BEAR
elif bear_count - bull_count >= 1:
sentiment = NewsSentiment.BEAR
else:
sentiment = NewsSentiment.NEUTRAL
# 生成操作建议
suggestion = self._get_suggestion(risk_level, sentiment)
return NewsRiskResult(
code=data.code,
name=data.name,
risk_level=risk_level,
sentiment=sentiment,
risk_score=total_risk,
bear_points=all_bear,
bull_points=all_bull,
suggestion=suggestion
)
def analyze_stocks(self, datas: List[StockNewsData]) -> List[NewsRiskResult]:
"""批量分析"""
return [self.analyze_stock(d) for d in datas]
def _get_suggestion(self, risk_level: RiskLevel, sentiment: NewsSentiment) -> str:
"""根据风险和情绪给出操作建议"""
if risk_level == RiskLevel.EXIT:
return "⚠️ 风险较高,建议减仓或清仓离场,规避黑天鹅"
elif risk_level == RiskLevel.REDUCE:
return "⚠️ 存在明显利空信号,建议降低仓位,控制风险"
elif risk_level == RiskLevel.WATCH:
if sentiment in [NewsSentiment.BULL, NewsSentiment.STRONG_BULL]:
return "👀 有少量异常信号,但整体偏多,可继续观察,谨慎加仓"
else:
return "👀 存在轻度风险信号,继续观察,不新开仓"
else:
if sentiment in [NewsSentiment.BULL, NewsSentiment.STRONG_BULL]:
return "✅ 无明显风险,整体偏多,可按计划操作"
else:
return "✅ 无明显风险,按计划持有"
def get_risk_report(self, result: NewsRiskResult) -> str:
"""生成风险报告"""
levels = {
RiskLevel.SAFE: "🟢 安全",
RiskLevel.WATCH: "🟡 关注",
RiskLevel.REDUCE: "🟠 减仓",
RiskLevel.EXIT: "🔴 离场",
}
sentiments = {
NewsSentiment.STRONG_BULL: "🔼 强烈看多",
NewsSentiment.BULL: "▶️ 看多",
NewsSentiment.NEUTRAL: " 中性",
NewsSentiment.BEAR: "◀️ 看空",
NewsSentiment.STRONG_BEAR: "🔽 强烈看空",
}
lines = []
lines.append("=" * 60)
lines.append(f"个股消息风险评估: {result.name}({result.code})")
lines.append("=" * 60)
lines.append(f"风险等级: {levels[result.risk_level]} 风险分: {result.risk_score}/100")
lines.append(f"市场情绪: {sentiments[result.sentiment]}")
lines.append("")
if result.bull_points:
lines.append("✅ 利好信号:")
for point in result.bull_points:
lines.append(f"{point}")
lines.append("")
if result.bear_points:
lines.append("⚠️ 利空信号:")
for point in result.bear_points:
lines.append(f"{point}")
lines.append("")
lines.append(f"💡 操作建议: {result.suggestion}")
lines.append("=" * 60)
return "\n".join(lines)
def get_risk_score_for_risk_system(self, result: NewsRiskResult) -> float:
"""
获取归一化风险分,给五维风险评估体系使用
返回 0~1,越高风险越大
"""
return min(result.risk_score / 50, 1.0)
# 整合到原有风控体系的五维风险评估
class FiveDimensionRiskAssessment:
"""
五维度风险评估
整合:流动性风险 + 估值风险 + 技术面风险 + 基本面风险 + 消息面风险
"""
def __init__(self, news_monitor: NewsRiskMonitor = None):
self.news_monitor = news_monitor or NewsRiskMonitor()
def calculate_total_risk(self,
liquidity_risk: float, # 0~1
valuation_risk: float, # 0~1
technical_risk: float, # 0~1
fundamental_risk: float, # 0~1
news_data: StockNewsData) -> dict:
"""
计算五维综合风险
每个维度输入都是0~1,越高风险越大
"""
# 先算消息面风险
news_result = self.news_monitor.analyze_stock(news_data)
news_risk = self.news_monitor.get_risk_score_for_risk_system(news_result)
# 加权平均,消息面风险权重高一些,因为黑天鹅突发危害大
total_risk = (
liquidity_risk * 0.15 +
valuation_risk * 0.20 +
technical_risk * 0.20 +
fundamental_risk * 0.20 +
news_risk * 0.25
)
return {
"total_risk_score": total_risk, # 0~1
"dimension_scores": {
"liquidity": liquidity_risk,
"valuation": valuation_risk,
"technical": technical_risk,
"fundamental": fundamental_risk,
"news": news_risk
},
"news_result": news_result,
"overall_risk_level": self._get_level(total_risk),
"suggestion": self._get_suggestion(total_risk)
}
def _get_level(self, score: float) -> str:
if score >= 0.7:
return "高风险"
elif score >= 0.4:
return "中风险"
else:
return "低风险"
def _get_suggestion(self, score: float) -> str:
if score >= 0.7:
return "不参与,规避"
elif score >= 0.4:
return "轻仓参与,严格止损"
else:
return "正常参与,按计划执行"
if __name__ == "__main__":
# 测试案例
print("=== 测试1: 疑似提前泄露利空的个股 + 国际利空 ===")
data1 = StockNewsData(
code="600XXX",
name="XX股份",
recent_vol_change=1.2, # 放量120%
recent_pct_change=-8.5, # 5天下跌8.5%
gap_down=True,
finance_balance_change=-0.25, # 融资减少25%
discussion_count_change=3.0,
sentiment_score=-0.6,
has_major_international_news=True,
international_news_sentiment=-1
)
monitor = NewsRiskMonitor()
result1 = monitor.analyze_stock(data1)
print(monitor.get_risk_report(result1))
print("\n=== 测试2: 明显利好信号个股 ===")
data2 = StockNewsData(
code="002XXX",
name="XX科技",
recent_vol_change=0.8,
recent_pct_change=4.2,
gap_up=True,
finance_balance_change=0.4, # 融资增加40%
has_large_order=False,
discussion_count_change=2.0,
sentiment_score=0.5
)
result2 = monitor.analyze_stock(data2)
print(monitor.get_risk_report(result2))
print("\n=== 测试2: 大宗商品股 + 商品隔夜大跌 ===")
data2 = StockNewsData(
code="601899",
name="紫金矿业",
recent_vol_change=0.5,
recent_pct_change=-2.0,
is_commodity_related=True,
commodity_future_overnight_change=-5.2,
us_index_overnight_change=-2.5
)
result2 = monitor.analyze_stock(data2)
print(monitor.get_risk_report(result2))
print("\n=== 测试3: A+H股 + H股隔夜大跌 ===")
data3 = StockNewsData(
code="600016",
name="民生银行",
recent_vol_change=0.1,
recent_pct_change=-1.2,
is_ah=True,
ah_hk_overnight_change=-4.5
)
result3 = monitor.analyze_stock(data3)
print(monitor.get_risk_report(result3))
print("\n=== 测试4: 五维风险评估 ===")
five_dim = FiveDimensionRiskAssessment()
eval_result = five_dim.calculate_total_risk(
liquidity_risk=0.2,
valuation_risk=0.5,
technical_risk=0.3,
fundamental_risk=0.4,
news_data=data1
)
print(f"五维综合风险评分: {eval_result['total_risk_score']:.2f}")
print(f"风险等级: {eval_result['overall_risk_level']}")
print(f"建议: {eval_result['suggestion']}")
print(f"各维度分数: {eval_result['dimension_scores']}")
@@ -0,0 +1,241 @@
"""
结构化行情择时风控模块
专门处理A股结构化行情下的板块集中度风险和择时信号
功能:
1. 择时:单板块连续大涨提醒风险,连续大跌提示低吸
2. 风控:控制板块和风格集中度,不押注单一方向
3. 消息风险:热点板块灵敏度提高,提前预警利好出尽
Author: 关羽(云长)
Date: 2026-03-27
"""
from dataclasses import dataclass
from typing import List, Dict, Tuple
@dataclass
class SectorInfo:
"""板块信息"""
name: str
style: str # 风格:AI/新能源/消费/周期等
recent_gain: float # 近期累计涨幅%
position_ratio: float # 组合中该板块仓位比例
stock_count: int # 持有股票数量
is_hot: bool = False # 是否是当前热点板块
class StructuredMarketTiming:
"""结构化行情择时"""
def __init__(self,
high_position_threshold: float = 0.15, # 单板块仓位超15%提示减仓
sector_rally_warning: float = 30.0, # 累计涨30%提示风险
sector_rally_stop: float = 50.0, # 累计涨50%强制提高风控权重
sector_dump_opportunity: float = -20.0): # 跌20%提示机会
self.high_position_threshold = high_position_threshold
self.sector_rally_warning = sector_rally_warning
self.sector_rally_stop = sector_rally_stop
self.sector_dump_opportunity = sector_dump_opportunity
def timing_sector(self, sector: SectorInfo) -> Tuple[str, str, int]:
"""
板块择时
返回:(信号类型, 建议, 风险增量评分)
信号类型: bull/bear/neutral
"""
risk_increment = 0
suggestion = ""
signal = "neutral"
# 单板块仓位过高提醒
if sector.position_ratio > self.high_position_threshold:
risk_increment += int((sector.position_ratio - self.high_position_threshold) * 100)
suggestion += f"\n⚠️ 单板块仓位{sector.position_ratio:.1%},超过{self.high_position_threshold:.1%}阈值,建议适度减仓分散"
signal = "bear"
# 板块连续大涨提醒风险
if sector.recent_gain >= self.sector_rally_stop:
risk_increment += 20
suggestion += f"\n🔴 板块累计涨幅{sector.recent_gain:.1f}%,超过{self.sector_rally_stop:.0f}%,警惕过热,建议整体止盈"
signal = "bear"
elif sector.recent_gain >= self.sector_rally_warning:
risk_increment += 10
suggestion += f"\n🟡 板块累计涨幅{sector.recent_gain:.1f}%,已有较大涨幅,提高风控警惕"
signal = "bear"
# 板块连续大跌提示机会
if sector.recent_gain <= self.sector_dump_opportunity and sector.recent_gain >= -40:
suggestion += f"\n✅ 板块累计跌幅{-sector.recent_gain:.1f}%,如果基本面没问题,可考虑适度低吸布局"
signal = "bull"
if not suggestion:
suggestion = "✅ 板块仓位和涨幅正常,无特殊风险"
signal = "neutral"
return signal, suggestion.strip(), risk_increment
class SectorConcentrationRisk:
"""板块和风格集中度风控"""
def __init__(self,
max_single_sector: float = 0.25, # 单板块最大仓位25%
max_single_style: float = 0.40, # 单风格最大仓位40%
max_hot_sectors_total: float = 0.50): # 所有热点板块合计最大50%
self.max_single_sector = max_single_sector
self.max_single_style = max_single_style
self.max_hot_sectors_total = max_hot_sectors_total
def check_concentration(self, sectors: List[SectorInfo]) -> Tuple[int, List[str]]:
"""检查集中度风险,返回总风险增量和警告列表"""
risk_increment = 0
warnings = []
# 1. 单板块检查
for sector in sectors:
if sector.position_ratio > self.max_single_sector:
extra = (sector.position_ratio - self.max_single_sector) * 100
risk_increment += int(extra)
warnings.append(f"⚠️ 板块【{sector.name}】仓位{sector.position_ratio:.1%},超过最大限制{self.max_single_sector:.1%}")
# 2. 按风格汇总检查
style_summary: Dict[str, float] = {}
for sector in sectors:
if sector.style not in style_summary:
style_summary[sector.style] = 0.0
style_summary[sector.style] += sector.position_ratio
for style, ratio in style_summary.items():
if ratio > self.max_single_style:
extra = (ratio - self.max_single_style) * 50
risk_increment += int(extra)
warnings.append(f"⚠️ 风格【{style}】总仓位{ratio:.1%},超过最大限制{self.max_single_style:.1%},建议分散")
# 3. 热点板块合计检查
hot_total = sum(s.position_ratio for s in sectors if s.is_hot)
if hot_total > self.max_hot_sectors_total:
extra = (hot_total - self.max_hot_sectors_total) * 80
risk_increment += int(extra)
warnings.append(f"⚠️ 所有热点板块合计仓位{hot_total:.1%},超过{self.max_hot_sectors_total:.1%},总体过热风险")
return risk_increment, warnings
class HotSectorNewsRiskAdjust:
"""热点板块消息风险调整,热点更容易利好出尽,灵敏度提高"""
def __init__(self,
hot_risk_multiplier: float = 2.0, # 热点板块风险分放大倍数
medium_risk_multiplier: float = 1.5): # 中度上涨板块放大倍数
self.hot_risk_multiplier = hot_risk_multiplier
self.medium_risk_multiplier = medium_risk_multiplier
def adjust_risk_score(self, base_score: int, sector_recent_gain: float) -> int:
"""根据板块涨幅调整风险分"""
if sector_recent_gain >= 50:
return int(base_score * self.hot_risk_multiplier)
elif sector_recent_gain >= 30:
return int(base_score * self.medium_risk_multiplier)
else:
return base_score
def get_structural_risk_report(sectors: List[SectorInfo],
timing: StructuredMarketTiming,
concentration: SectorConcentrationRisk) -> str:
"""生成结构化行情风险报告"""
timing = StructuredMarketTiming()
concentration = SectorConcentrationRisk()
total_risk = 0
all_warnings = []
all_suggestions = []
# 逐个板块择时
for sector in sectors:
signal, suggestion, risk_inc = timing.timing_sector(sector)
total_risk += risk_inc
if signal != "neutral":
all_suggestions.append(f"{sector.name}{suggestion}")
# 集中度检查
risk_inc, warnings = concentration.check_concentration(sectors)
total_risk += risk_inc
all_warnings.extend(warnings)
# 生成报告
lines = []
lines.append("=" * 60)
lines.append("结构化行情板块风控择时报告")
lines.append("=" * 60)
lines.append(f"监控板块数量: {len(sectors)}")
lines.append(f"总风险增量评分: {total_risk}")
lines.append("")
if all_warnings:
lines.append("⚠️ 集中度风险警告:")
for w in all_warnings:
lines.append(f"{w}")
lines.append("")
if all_suggestions:
lines.append("💡 择时建议:")
for s in all_suggestions:
lines.append(f"{s}")
lines.append("")
if total_risk >= 30:
lines.append("⚠️ 整体结论:板块风险较高,建议减仓分散")
elif total_risk >= 10:
lines.append("🟡 整体结论:存在一定板块风险,建议密切观察")
else:
lines.append("✅ 整体结论:板块结构正常,无明显风险")
lines.append("=" * 60)
return "\n".join(lines)
if __name__ == "__main__":
# 测试
print("=== 测试结构化行情风控 ===\n")
sectors = [
SectorInfo(
name="AI算力",
style="AI",
recent_gain=65.0,
position_ratio=0.22,
stock_count=4,
is_hot=True
),
SectorInfo(
name="新能源",
style="新能源",
recent_gain=-25.0,
position_ratio=0.10,
stock_count=2,
is_hot=False
),
SectorInfo(
name="消费",
style="消费",
recent_gain=8.0,
position_ratio=0.12,
stock_count=3,
is_hot=False
)
]
timing = StructuredMarketTiming()
concentration = SectorConcentrationRisk()
print(get_structural_risk_report(sectors, timing, concentration))
# 测试热点风险放大
adjust = HotSectorNewsRiskAdjust()
base_score = 10
print(f"\n基础风险分10,不同涨幅放大后:")
print(f" 涨幅10% → {adjust.adjust_risk_score(10, 10)}")
print(f" 涨幅35% → {adjust.adjust_risk_score(10, 35)}")
print(f" 涨幅60% → {adjust.adjust_risk_score(10, 60)}")