金融文本信息抽取:财报、公告、新闻的三源融合解析
金融文本信息抽取:财报、公告、新闻的三源融合解析
一、个性化深度引言
一份上市公司财报 200 页,一份重大事项公告 5 页,一篇财经新闻 800 字。分析师需要在 30 分钟内看完 20 家公司的所有信息并做出判断。三个信息源,三种格式,三类噪声,却要融合成一条投资信号。
传统的做法是三个团队分别处理然后合并——信息在传递中损耗,在等待中衰减。我们尝试用一个统一的信息抽取框架来替代这个流程。见证奇迹的时刻在于:当我们将 NER 模型 + 关系抽取 + 时间轴对齐组合在一起时,财报里的数字、公告里的意图、新闻里的情绪,第一次在同一个时间线上被连接起来。
二、个性化原理剖析
三源信息融合架构
各信息源的特性与挑战
财报:结构化程度高(三表有固定格式),但 PDF 格式导致表格解析困难。关键挑战是数字抽取的精度——一个错误的小数点可能差出几亿。
公告:半结构化,关键信息在特定段落(如“重要内容提示”)。核心挑战是事件类型的分类——同一个事件可能是“利好”也可能是“利空”,取决于上下文。
新闻:完全非结构化,充斥着主观判断和情绪化表述。挑战在于区分“事实描述”和“观点评论”。
三源融合的信号加权
不同来源的信息对同一事件的描述可能矛盾。例如:财报显示利润增长 20%,但新闻标题是“XX 公司业绩暴雷”。
融合策略:
- 事实冲突:以官方公告为准,新闻作为参考
- 情绪冲突:保留矛盾信号,不作为过滤条件
- 时间冲突:以最新信息为基准,旧信息标注为历史
三、个性化代码实践
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
class SourceType(Enum):
"""信息来源类型"""
FINANCIAL_REPORT = "财报"
ANNOUNCEMENT = "公告"
NEWS = "新闻"
class EventType(Enum):
"""金融事件类型"""
MERGER = "并购重组"
PENALTY = "监管处罚"
DIVIDEND = "分红派息"
SHARE_CHANGE = "增减持"
PERFORMANCE = "业绩预告"
class Sentiment(Enum):
"""情绪标签"""
POSITIVE = "正面"
NEGATIVE = "负面"
NEUTRAL = "中性"
@dataclass
class FinancialEntity:
"""金融实体"""
name: str # 实体名称
entity_type: str # 类型:公司/人物/产品
mentions: List[str] # 别名列表
metadata: Dict = field(default_factory=dict)
@dataclass
class ExtractedEvent:
"""抽取的金融事件"""
event_type: EventType
entities: List[FinancialEntity]
date: datetime
source: SourceType
confidence: float # 置信度 0-1
amount: Optional[float] = None # 涉及金额
sentiment: Sentiment = Sentiment.NEUTRAL
raw_text: str = ""
@dataclass
class FusionResult:
"""三源融合结果"""
company: str
timeline: List[ExtractedEvent] # 按时间排序的事件
conflicts: List[Tuple[ExtractedEvent, ExtractedEvent]] # 冲突事件对
risk_signals: List[str] # 风险信号
class FinancialInformationExtractor:
"""金融信息抽取引擎"""
# 设计原因:财报核心数据关键词——三表关键项
REPORT_FIELDS = {
"营业收入": "revenue",
"净利润": "net_profit",
"扣非净利润": "adjusted_net_profit",
"经营活动现金流": "operating_cash_flow",
"资产负债率": "debt_ratio",
"毛利率": "gross_margin",
"净利率": "net_margin",
"ROE": "roe",
}
# 设计原因:公告事件关键词——触发器模式
ANNOUNCEMENT_TRIGGERS = {
EventType.MERGER: ["收购", "合并", "重组", "资产注入"],
EventType.PENALTY: ["处罚", "问询", "警示函", "立案调查"],
EventType.DIVIDEND: ["分红", "派息", "送转"],
EventType.SHARE_CHANGE: ["减持", "增持", "回购", "举牌"],
EventType.PERFORMANCE: ["业绩预告", "业绩快报", "预增", "预减"],
}
def parse_financial_report(self, text: str) -> Dict[str, float]:
"""从财报文本中抽取关键财务数据"""
# 设计原因:正则 + 规则 比端到端模型更可靠
# 财报数字对精度要求极高,模型幻觉不可接受
extracted = {}
for key, field in self.REPORT_FIELDS.items():
# 匹配模式:关键词 + 数字(含亿/万等单位)
import re
pattern = rf'{key}[^0-9]*?([+-]?\d+\.?\d*)\s*(亿|万|元|%|%)?'
match = re.search(pattern, text)
if match:
value = float(match.group(1))
unit = match.group(2) if match.group(2) else ""
# 设计原因:统一转换为"亿元"单位
if unit == "亿":
pass
elif unit == "万":
value = value / 10000
else:
value = value / 100000000 # 默认元 → 亿元
extracted[field] = round(value, 4)
return extracted
def extract_events_from_announcement(
self, text: str, date: datetime
) -> List[ExtractedEvent]:
"""从公告文本中抽取事件"""
# 设计原因:基于触发词的事件检测
# 金融公告的格式相对规范,触发词可靠性高
events = []
for event_type, triggers in self.ANNOUNCEMENT_TRIGGERS.items():
for trigger in triggers:
if trigger in text:
event = ExtractedEvent(
event_type=event_type,
entities=[],
date=date,
source=SourceType.ANNOUNCEMENT,
confidence=0.85, # 设计原因:公告来源置信度较高
raw_text=text[:200],
)
events.append(event)
return events
def analyze_news_sentiment(self, text: str) -> Sentiment:
"""分析新闻情绪"""
# 设计原因:金融新闻的情绪词分布有明显的领域特征
positive_words = [
"增长", "突破", "利好", "超预期", "创新高",
"扩大", "提升", "改善", "回购", "增持",
]
negative_words = [
"下降", "亏损", "利空", "不及预期", "新低",
"缩减", "下滑", "恶化", "减持", "调查",
]
pos_count = sum(1 for w in positive_words if w in text)
neg_count = sum(1 for w in negative_words if w in text)
if pos_count > neg_count + 1:
return Sentiment.POSITIVE
elif neg_count > pos_count + 1:
return Sentiment.NEGATIVE
else:
return Sentiment.NEUTRAL
def fuse_sources(
self,
report_data: Dict[str, float],
events: List[ExtractedEvent],
news_sentiment: Sentiment,
company: str,
) -> FusionResult:
"""三源信息融合"""
# 设计原因:融合逻辑的三条核心规则
timeline = sorted(events, key=lambda e: e.date)
conflicts = []
risk_signals = []
# 设计原因:冲突检测——数字 vs 情绪
# 如果财报数据向好(利润增长)但新闻情绪负面 → 风险信号
net_profit = report_data.get("net_profit", 0)
if net_profit > 0 and news_sentiment == Sentiment.NEGATIVE:
risk_signals.append(
f"矛盾信号:净利润{net_profit}亿(增长),"
f"但新闻情绪为负面,可能有未反映在财报中的风险"
)
# 设计原因:处罚事件 + 减持事件 → 强风险信号
has_penalty = any(
e.event_type == EventType.PENALTY for e in events
)
has_share_decrease = any(
e.event_type == EventType.SHARE_CHANGE and "减持" in e.raw_text
for e in events
)
if has_penalty and has_share_decrease:
risk_signals.append("风险叠加:同时存在监管处罚和股东减持事件")
return FusionResult(
company=company,
timeline=timeline,
conflicts=conflicts,
risk_signals=risk_signals,
)
# 使用示例
extractor = FinancialInformationExtractor()
# 模拟财报文本
report_text = """
2024年度,公司实现营业收入125.6亿元,同比增长15.2%;
归属于上市公司股东的净利润23.8亿元,同比增长18.5%;
扣除非经常性损益的净利润21.2亿元,同比增长16.8%。
"""
# 解析财报
financial_data = extractor.parse_financial_report(report_text)
print("财报数据抽取:")
for key, value in financial_data.items():
print(f" {key}: {value}")
# 抽取事件
announcement_text = "公司拟以自有资金回购股份,回购金额不低于1亿元。"
events = extractor.extract_events_from_announcement(
announcement_text, datetime(2025, 1, 15)
)
print(f"\n事件抽取:{len(events)} 个事件")
# 情绪分析
news = "XX公司业绩超预期,机构上调目标价,市场反应积极。"
sentiment = extractor.analyze_news_sentiment(news)
print(f"新闻情绪:{sentiment.value}")
# 融合
result = extractor.fuse_sources(financial_data, events, sentiment, "XX公司")
print(f"\n风险信号:{result.risk_signals}")
四、个性化边界权衡
| 方面 | 规则方法 | 深度学习模型 | 混合方案 |
|---|---|---|---|
| 财报数字抽取 | 准确率 98%、速度极快 | 准确率 92%、速度慢 | 规则为主 |
| 公告事件抽取 | 召回率 75%、精度 90% | 召回率 90%、精度 85% | 规则+模型 |
| 新闻情绪分析 | 精度 70%、可解释 | 精度 85%、黑盒 | 模型为主 |
| 表格解析 | 规则高效、格式敏感 | 模型灵活、训练成本高 | 规则为主 |
| 融合决策 | 逻辑清晰、可审计 | 端到端、不可解释 | 规则为主(合规要求) |
关键权衡:
- 精度 vs 召回:财报数字抽取中,一个错误可能导致所有下游分析失效。因此优先保证精度,宁可不抽取也不抽错。
- 可解释性 vs 性能:金融场景有强合规要求,融合决策必须可解释、可审计。端到端深度学习模型虽然性能更好,但不适合这个场景。
- 实时性 vs 完整性:新闻情绪分析需要秒级响应(市场价格变化极快),但财报分析可以容忍分钟级延迟(信息更新频率以天计)。
五、总结
金融文本信息抽取的本质是三源异构信息的融合对齐问题。财报提供结构化的财务数据(高精度、可量化),公告提供事件触发信号(高可信度、低噪声),新闻提供市场情绪参考(高时效、主观性强)。技术方案上,建议采用规则为主、模型为辅的混合策略:财报数字用正则 + 规则(精度优先),公告事件用触发词 + 模型验证(召回优先),新闻情绪用 FinBERT 等专用模型。融合层的核心逻辑是冲突检测——当不同来源的信号矛盾时,以官方公告为准,保留矛盾信号作为风险提示。最终输出的结构化数据是量化模型的输入,而非直接的投资结论。
© 版权声明
文章版权归作者所有,未经允许请勿转载。