381 lines
14 KiB
Python
381 lines
14 KiB
Python
"""
|
||
政策消息解析模块
|
||
功能:
|
||
1. 政策消息分类(货币/财政/产业/监管)
|
||
2. 政策力度分级
|
||
3. 受益板块识别
|
||
4. 政策驱动轮动信号生成
|
||
5. 利空政策预警
|
||
|
||
Author: 关羽(云长)
|
||
Date: 2026-03-27
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
from typing import List, Dict, Optional, Tuple
|
||
from enum import Enum
|
||
import re
|
||
|
||
|
||
class PolicyType(Enum):
|
||
MONETARY = "货币政策"
|
||
FISCAL = "财政政策"
|
||
INDUSTRY = "产业政策"
|
||
REGULATORY = "监管政策"
|
||
MACRO = "宏观政策"
|
||
OTHER = "其他"
|
||
|
||
|
||
class PolicyStrength(Enum):
|
||
WEAK = 1
|
||
MEDIUM = 2
|
||
STRONG = 3
|
||
VERY_STRONG = 4
|
||
|
||
|
||
class PolicySentiment(Enum):
|
||
BULL = 1
|
||
NEUTRAL = 2
|
||
BEAR = 3
|
||
|
||
|
||
@dataclass
|
||
class PolicyNews:
|
||
"""政策消息"""
|
||
title: str
|
||
content: str
|
||
date: str
|
||
source: str
|
||
policy_type: PolicyType = PolicyType.OTHER
|
||
strength: PolicyStrength = PolicyStrength.MEDIUM
|
||
sentiment: PolicySentiment = PolicySentiment.NEUTRAL
|
||
affected_sectors: List[str] = None
|
||
keywords: List[str] = None
|
||
|
||
def __post_init__(self):
|
||
if self.affected_sectors is None:
|
||
self.affected_sectors = []
|
||
if self.keywords is None:
|
||
self.keywords = []
|
||
|
||
|
||
@dataclass
|
||
class PolicySignal:
|
||
"""政策轮动信号"""
|
||
sector: str
|
||
sentiment: PolicySentiment
|
||
strength: int
|
||
signal_date: str
|
||
reason: str
|
||
|
||
|
||
class PolicyClassifier:
|
||
"""政策消息分类器"""
|
||
|
||
# 关键词分类词典
|
||
TYPE_KEYWORDS = {
|
||
PolicyType.MONETARY: [
|
||
"降息", "降准", "货币政策", "流动性", "MLF", "LPR", "美联储", "加息",
|
||
"存款准备金", "公开市场操作", "Shibor", "利率"
|
||
],
|
||
PolicyType.FISCAL: [
|
||
"财政政策", "减税", "降费", "赤字", "专项债", "国债", "转移支付",
|
||
"积极财政", "财政刺激"
|
||
],
|
||
PolicyType.INDUSTRY: [
|
||
"扶持", "鼓励", "补贴", "规划", "纲要", "产业链", "新能源", "AI",
|
||
"半导体", "芯片", "消费", "汽车", "地产", "基建", "数字经济",
|
||
"高端制造", "生物医药", "绿色能源", "碳中和"
|
||
],
|
||
PolicyType.REGULATORY: [
|
||
"监管", "整治", "整顿", "约谈", "退市", "处罚", "反垄断", "立案",
|
||
"调查", "整改", "规范", "限购", "限跌", "双减", "教培"
|
||
],
|
||
PolicyType.MACRO: [
|
||
"GDP", "CPI", "PMI", "经济数据", "失业率", "经济增长", "通胀",
|
||
"经济会议", "中央经济工作", "两会", "政治局会议"
|
||
]
|
||
}
|
||
|
||
# 受益板块映射
|
||
SECTOR_KEYWORDS = {
|
||
"AI": ["AI", "人工智能", "大模型", "ChatGPT", "算力"],
|
||
"半导体": ["半导体", "芯片", "光刻机", "集成电路"],
|
||
"新能源": ["新能源", "光伏", "风电", "储能", "新能源车", "动力电池"],
|
||
"消费": ["消费", "内需", "促消费", "零售", "食品饮料"],
|
||
"医药": ["医药", "生物医药", "创新药", "医疗", "集采"],
|
||
"金融": ["金融", "银行", "证券", "券商", "保险"],
|
||
"地产": ["房地产", "地产", "楼市", "保交楼"],
|
||
"基建": ["基建", "新基建", "传统基建", "基础设施"],
|
||
"汽车": ["汽车", "新能源车", "汽车消费", "智能驾驶"],
|
||
"军工": ["军工", "国防", "武器装备"],
|
||
"农业": ["农业", "粮食", "种子", "乡村振兴"],
|
||
"环保": ["环保", "碳中和", "绿色发展", "碳达峰"]
|
||
}
|
||
|
||
def classify_type(self, text: str) -> PolicyType:
|
||
"""根据关键词分类政策类型"""
|
||
max_count = 0
|
||
best_type = PolicyType.OTHER
|
||
|
||
for ptype, keywords in self.TYPE_KEYWORDS.items():
|
||
count = sum(1 for kw in keywords if kw in text)
|
||
if count > max_count:
|
||
max_count = count
|
||
best_type = ptype
|
||
|
||
return best_type
|
||
|
||
def recognize_affected_sectors(self, text: str) -> List[str]:
|
||
"""识别受影响板块"""
|
||
affected = []
|
||
for sector, keywords in self.SECTOR_KEYWORDS.items():
|
||
for kw in keywords:
|
||
if kw in text:
|
||
affected.append(sector)
|
||
break
|
||
return affected
|
||
|
||
def judge_strength(self, text: str, sentiment: PolicySentiment) -> PolicyStrength:
|
||
"""判断政策力度"""
|
||
strong_words = ["全面", "大力", "全力", "重磅", "重大", "万亿", "千亿",
|
||
"顶格", "全面放开", "重磅推出", "重大利好"]
|
||
medium_words = ["稳步", "适度", "推进", "支持", "鼓励"]
|
||
weak_words = ["研究", "酝酿", "讨论", "草案", "征求意见"]
|
||
|
||
strong_count = sum(1 for w in strong_words if w in text)
|
||
weak_count = sum(1 for w in weak_words if w in text)
|
||
|
||
if strong_count >= 2:
|
||
return PolicyStrength.VERY_STRONG
|
||
elif strong_count >= 1:
|
||
return PolicyStrength.STRONG
|
||
elif weak_count >= 1:
|
||
return PolicyStrength.WEAK
|
||
else:
|
||
return PolicyStrength.MEDIUM
|
||
|
||
def judge_sentiment(self, text: str) -> PolicySentiment:
|
||
"""判断政策多空"""
|
||
bull_words = ["利好", "扶持", "鼓励", "支持", "发展", "推广", "优惠",
|
||
"补贴", "降税", "松绑", "放开"]
|
||
bear_words = ["监管", "整治", "限制", "禁止", "处罚", "退市", "打压",
|
||
"收紧", "加息", "降准", "调控", "整顿"]
|
||
|
||
bull_count = sum(1 for w in bull_words if w in text)
|
||
bear_count = sum(1 for w in bear_words if w in text)
|
||
|
||
if bull_count > bear_count:
|
||
return PolicySentiment.BULL
|
||
elif bear_count > bull_count:
|
||
return PolicySentiment.BEAR
|
||
else:
|
||
return PolicySentiment.NEUTRAL
|
||
|
||
|
||
class PolicySignalGenerator:
|
||
"""政策驱动轮动信号生成"""
|
||
|
||
def __init__(self):
|
||
self.classifier = PolicyClassifier()
|
||
|
||
def parse_news(self, news: PolicyNews) -> PolicyNews:
|
||
"""解析新闻,自动分类、判断力度和多空"""
|
||
full_text = news.title + news.content
|
||
news.policy_type = self.classifier.classify_type(full_text)
|
||
news.sentiment = self.classifier.judge_sentiment(full_text)
|
||
news.strength = self.classifier.judge_strength(full_text, news.sentiment)
|
||
news.affected_sectors = self.classifier.recognize_affected_sectors(full_text)
|
||
return news
|
||
|
||
def generate_signal(self, news_list: List[PolicyNews]) -> List[PolicySignal]:
|
||
"""根据多个政策生成板块轮动信号"""
|
||
sector_scores: Dict[str, int] = {}
|
||
|
||
for news in news_list:
|
||
# 解析后得到评分
|
||
strength_weight = news.strength.value
|
||
if news.sentiment == PolicySentiment.BULL:
|
||
score = strength_weight * 2
|
||
elif news.sentiment == PolicySentiment.BEAR:
|
||
score = -strength_weight * 2
|
||
else:
|
||
score = 0
|
||
|
||
for sector in news.affected_sectors:
|
||
sector_scores[sector] = sector_scores.get(sector, 0) + score
|
||
|
||
# 生成信号
|
||
signals = []
|
||
for sector, score in sector_scores.items():
|
||
if score > 0:
|
||
sentiment = PolicySentiment.BULL
|
||
elif score < 0:
|
||
sentiment = PolicySentiment.BEAR
|
||
else:
|
||
sentiment = PolicySentiment.NEUTRAL
|
||
|
||
signals.append(PolicySignal(
|
||
sector=sector,
|
||
sentiment=sentiment,
|
||
strength=abs(score),
|
||
signal_date=news_list[-1].date,
|
||
reason=f"累计政策评分{score}"
|
||
))
|
||
|
||
# 按强度排序
|
||
signals.sort(key=lambda x: x.strength, reverse=True)
|
||
return signals
|
||
|
||
|
||
class PolicyRiskAlert:
|
||
"""政策风险预警"""
|
||
|
||
# 高风险关键词
|
||
HIGH_RISK_KEYWORDS = [
|
||
"反垄断", "强监管", "整治", "集采", "退市", "立案调查", "约谈",
|
||
"处罚", "退市风险", "退市警示", "全面收紧", "加息超预期"
|
||
]
|
||
|
||
def check_risk(self, news: PolicyNews) -> Tuple[bool, int, str]:
|
||
"""
|
||
检查政策风险
|
||
返回:(是否高风险, 风险等级 1~3, 原因)
|
||
"""
|
||
full_text = news.title + news.content
|
||
risk_count = sum(1 for kw in self.HIGH_RISK_KEYWORDS if kw in full_text)
|
||
|
||
if news.sentiment == PolicySentiment.BEAR and news.strength.value >= 3:
|
||
return True, 3, f"{news.policy_type.value}利空,力度强,高度风险"
|
||
elif risk_count >= 2:
|
||
return True, 2, f"多个高风险关键词,中度风险"
|
||
elif risk_count >= 1:
|
||
return True, 1, f"存在风险关键词,轻度风险"
|
||
else:
|
||
return False, 0, "无明显政策风险"
|
||
|
||
|
||
class PolicyRotationModule:
|
||
"""政策驱动轮动总模块"""
|
||
|
||
def __init__(self):
|
||
self.signal_generator = PolicySignalGenerator()
|
||
self.risk_alert = PolicyRiskAlert()
|
||
self.parsed_news: List[PolicyNews] = []
|
||
|
||
def add_and_parse(self, news: PolicyNews) -> PolicyNews:
|
||
"""添加并解析政策新闻"""
|
||
parsed = self.signal_generator.parse_news(news)
|
||
self.parsed_news.append(parsed)
|
||
return parsed
|
||
|
||
def get_current_signals(self) -> List[PolicySignal]:
|
||
"""获取当前轮动信号"""
|
||
return self.signal_generator.generate_signal(self.parsed_news[-20:])
|
||
|
||
def get_risk_alerts(self) -> List[Tuple[str, int, str]]:
|
||
"""获取当前风险预警"""
|
||
alerts = []
|
||
for news in self.parsed_news[-10:]:
|
||
is_risk, level, reason = self.risk_alert.check_risk(news)
|
||
if is_risk:
|
||
alerts.append((f"{news.title} ({news.date})", level, reason))
|
||
return alerts
|
||
|
||
def get_rotation_report(self) -> str:
|
||
"""生成政策轮动报告"""
|
||
signals = self.get_current_signals()
|
||
alerts = self.get_risk_alerts()
|
||
|
||
lines = []
|
||
lines.append("=" * 60)
|
||
lines.append("政策驱动板块轮动报告")
|
||
lines.append("=" * 60)
|
||
lines.append(f"已解析政策数量: {len(self.parsed_news)}")
|
||
lines.append("")
|
||
|
||
if signals:
|
||
lines.append("🔔 当前板块信号:")
|
||
for sig in signals[:10]: # 只放前10个
|
||
sentiment_emoji = {
|
||
PolicySentiment.BULL: "🔼",
|
||
PolicySentiment.NEUTRAL: "➖",
|
||
PolicySentiment.BEAR: "🔽"
|
||
}
|
||
lines.append(f" {sentiment_emoji[sig.sentiment]} {sig.sector}: 强度={sig.strength} → {sig.reason}")
|
||
lines.append("")
|
||
|
||
if alerts:
|
||
lines.append("⚠️ 政策风险预警:")
|
||
for title, level, reason in alerts:
|
||
lines.append(f" [{level}级风险] {title}: {reason}")
|
||
lines.append("")
|
||
|
||
if not signals and not alerts:
|
||
lines.append("无明确信号,保持观察")
|
||
|
||
lines.append("=" * 60)
|
||
return "\n".join(lines)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("=== 测试政策消息解析模块 ===\n")
|
||
|
||
module = PolicyRotationModule()
|
||
|
||
# 测试案例1:AI产业政策
|
||
news1 = PolicyNews(
|
||
title="重磅政策支持人工智能发展,算力基础设施加快建设",
|
||
content="近日,国务院印发《新一代人工智能发展规划》,全面支持人工智能产业发展,加大算力基础设施建设,对AI企业给予税收优惠。",
|
||
date="2026-03-27",
|
||
source="新华社"
|
||
)
|
||
|
||
parsed1 = module.add_and_parse(news1)
|
||
print(f"[测试1] {parsed1.title}")
|
||
print(f" 类型: {parsed1.policy_type.value}")
|
||
print(f" 力度: {parsed1.strength.name}")
|
||
print(f" 情绪: {parsed1.sentiment.name}")
|
||
print(f" 影响板块: {parsed1.affected_sectors}")
|
||
print()
|
||
|
||
# 测试案例2:监管政策
|
||
news2 = PolicyNews(
|
||
title="监管部门加强对互联网平台反垄断监管,约谈头部企业",
|
||
content="近日,市场监管总局对互联网平台企业反垄断问题开展专项整治,约谈头部三家企业要求整改,规范市场竞争秩序。",
|
||
date="2026-03-26",
|
||
source="证监会"
|
||
)
|
||
|
||
parsed2 = module.add_and_parse(news2)
|
||
print(f"[测试2] {parsed2.title}")
|
||
print(f" 类型: {parsed2.policy_type.value}")
|
||
print(f" 力度: {parsed2.strength.name}")
|
||
print(f" 情绪: {parsed2.sentiment.name}")
|
||
print()
|
||
|
||
# 测试案例3:降准
|
||
news3 = PolicyNews(
|
||
title="央行宣布全面降准0.5个百分点,释放长期资金一万亿",
|
||
content="中国人民银行决定下调金融机构存款准备金率0.5个百分点,释放长期资金约一万亿元,支持实体经济发展。",
|
||
date="2026-03-25",
|
||
source="央行官网"
|
||
)
|
||
|
||
parsed3 = module.add_and_parse(news3)
|
||
print(f"[测试3] {parsed3.title}")
|
||
print(f" 类型: {parsed3.policy_type.value}")
|
||
print(f" 力度: {parsed3.strength.name}")
|
||
print(f" 情绪: {parsed3.sentiment.name}")
|
||
print(f" 影响板块: {parsed3.affected_sectors}")
|
||
print()
|
||
|
||
# 生成报告
|
||
print(module.get_rotation_report())
|
||
|
||
# 风险预警测试
|
||
print("\n=== 风险预警测试 ===")
|
||
for n in module.parsed_news:
|
||
is_risk, level, reason = module.risk_alert.check_risk(n)
|
||
print(f"{n.title}: is_risk={is_risk}, level={level}, reason={reason}")
|