AI+金融:智能投研系统设计
AI 导读
AI+金融:智能投研系统设计 灵阙学院 | 行业 AI 系列 引言:研究员的信息战争 周一早晨 8 点,某券商研究所的分析师小李打开电脑。等待他的是:28 家覆盖公司的周末公告、147 条财经新闻、3 份行业报告、以及刚出炉的 PMI 数据。市场 9:30 开盘,他需要在 90 分钟内消化这些信息,判断哪些会影响覆盖标的,并在团队晨会上给出观点。...
AI+金融:智能投研系统设计
灵阙学院 | 行业 AI 系列
引言:研究员的信息战争
周一早晨 8 点,某券商研究所的分析师小李打开电脑。等待他的是:28 家覆盖公司的周末公告、147 条财经新闻、3 份行业报告、以及刚出炉的 PMI 数据。市场 9:30 开盘,他需要在 90 分钟内消化这些信息,判断哪些会影响覆盖标的,并在团队晨会上给出观点。
他不可能全部读完。于是他凭经验扫标题、挑重点——但上个月他就因为漏看了一份供应商的监管函,错过了某公司的供应链风险信号。
这就是智能投研系统要解决的问题:不是替代分析师的判断力,而是让他在信息采集和初筛环节不再"靠经验扫标题"。把人从重复性信息处理中解放出来,集中精力做真正需要深度思考的工作。
必须先说清楚一个前提:本文讨论的系统专注于投研辅助,不输出交易信号,不构成投资建议。这是监管合规的基本底线。
一、系统架构概览
┌──────────────────────────────────────────────────────────┐
│ 智能投研系统架构 │
├──────────────────────────────────────────────────────────┤
│ │
│ 数据采集层 │
│ +--------+ +--------+ +--------+ +--------+ +--------+ │
│ |A股公告 | |财经新闻| |研究报告| |宏观数据 | |社交媒体| │
│ |巨潮/上交| |RSS/爬虫| |PDF解析 | |Wind/iFind| |雪球/东财| │
│ +---+----+ +---+----+ +---+----+ +---+-----+ +---+----+ │
│ | | | | | │
│ +----------+----------+----------+------------+ │
│ | │
│ Kafka 实时分发 v │
│ │
│ +------------------------------------------------------+│
│ | NLP 处理层 |│
│ | +----------+ +----------+ +----------+ +------------+ |│
│ | |文本分类 | |情感分析 | |事件抽取 | |知识图谱更新| |│
│ | |去重过滤 | |细粒度 | |结构化 | |关系网络 | |│
│ | +----------+ +----------+ +----------+ +------------+ |│
│ +-------------------------+----------------------------+ │
│ | │
│ +-------------------------v----------------------------+ │
│ | 量化信号生成层 | │
│ | 情感信号 -> 多因子合成 -> 信号强度 -> 回测验证 | │
│ +-------------------------+----------------------------+ │
│ | │
│ +-------------------------v----------------------------+ │
│ | 投研工作台 (Research Workbench) | │
│ | 个股报告 事件跟踪 竞对分析 行业图谱 预警推送 | │
│ +------------------------------------------------------+ │
└──────────────────────────────────────────────────────────┘
二、财经文本分类与优先级分层
投研场景的第一步是把海量信息按重要性快速分层。
2.1 文档分类与优先级
| 文档类型 | 重要性权重 | 延迟要求 |
|---|---|---|
| 监管公告(问询函/立案调查) | 1.0 | 实时 |
| 股权变动(大额增减持/举牌) | 0.95 | 实时 |
| 并购重组 | 0.90 | < 5min |
| 业绩报告/预告 | 0.85 | < 5min |
| 重大合同 | 0.80 | < 10min |
| 管理层变动 | 0.75 | < 10min |
| 宏观政策 | 0.60 | < 30min |
| 行业新闻 | 0.30 | 小时级 |
2.2 规则 + 模型混合分类器
"""
财经文档分类器:规则优先(快+准),模型兜底(覆盖长尾)
"""
from dataclasses import dataclass
from enum import Enum
import re
class DocCategory(Enum):
REGULATORY = "regulatory_filing"
EQUITY_CHANGE = "equity_change"
MERGER = "merger_acquisition"
EARNINGS = "earnings_report"
CONTRACT = "major_contract"
MANAGEMENT = "management_change"
MACRO = "macro_policy"
INDUSTRY = "industry_news"
GENERAL = "general_news"
CATEGORY_IMPORTANCE = {
DocCategory.REGULATORY: 1.0,
DocCategory.EQUITY_CHANGE: 0.95,
DocCategory.MERGER: 0.90,
DocCategory.EARNINGS: 0.85,
DocCategory.CONTRACT: 0.80,
DocCategory.MANAGEMENT: 0.75,
DocCategory.MACRO: 0.60,
DocCategory.INDUSTRY: 0.30,
DocCategory.GENERAL: 0.10,
}
KEYWORD_RULES = {
DocCategory.REGULATORY: [
r"(证监会|交易所).{0,20}(问询|关注|立案|调查|处罚)",
r"(收到|收函).{0,10}(监管|问询|调查)函",
r"(违规|违法|行政处罚|责令改正)",
],
DocCategory.EQUITY_CHANGE: [
r"(增持|减持|举牌|权益变动|股权转让).{0,30}(公告|提示)",
r"(实际控制人|控股股东).{0,20}(拟|计划|已)(增持|减持)",
],
DocCategory.EARNINGS: [
r"(年度|半年|季度).{0,10}(报告|报)",
r"(净利润|营业收入|扣非).{0,20}(同比|增长|下降)",
r"(业绩预告|业绩快报|业绩修正)",
],
}
@dataclass
class ClassifiedDoc:
doc_id: str
title: str
category: DocCategory
importance: float
companies: list[str] # 涉及的股票代码
source: str
def classify(doc: dict) -> ClassifiedDoc:
"""规则优先,BERT 兜底"""
text = doc["title"] + " " + doc.get("content", "")[:500]
for cat, patterns in KEYWORD_RULES.items():
for p in patterns:
if re.search(p, text):
return _build(doc, cat)
# 降级到 BERT 分类
cat = bert_classify(text)
return _build(doc, cat)
def _build(doc, cat):
return ClassifiedDoc(
doc_id=doc["id"], title=doc["title"],
category=cat, importance=CATEGORY_IMPORTANCE[cat],
companies=extract_codes(doc.get("content", "")),
source=doc["source"],
)
三、细粒度金融情感分析
金融情感分析不能直接用通用模型。"营收大幅下滑"在通用语境下不算什么,但在财报语境下是强烈负面信号。
3.1 金融情感词典
class FinSentimentAnalyzer:
"""
细粒度金融情感分析。
区分:对哪个实体?短期还是长期影响?强度如何?
"""
POSITIVE = {
"strong": [
"超预期", "创历史新高", "大幅增长", "利润倍增",
"获得重大订单", "突破性进展",
],
"mild": [
"略超预期", "稳健增长", "符合预期", "业绩改善",
"市场份额提升", "获批",
],
}
NEGATIVE = {
"strong": [
"净利润大幅下滑", "亏损", "被立案调查", "客户流失",
"债务违约", "商誉减值", "核心管理层离职",
],
"mild": [
"低于预期", "业绩承压", "竞争加剧", "毛利率下降",
"成本上涨", "产能利用率下降",
],
}
def analyze(self, text: str, target: str) -> dict:
"""分析文本对目标实体的情感"""
contexts = self._extract_contexts(text, target, window=100)
if not contexts:
return {"sentiment": "neutral", "score": 0.0}
scores = [self._score(ctx) for ctx in contexts]
avg = sum(scores) / len(scores)
return {
"sentiment": "positive" if avg > 0.1 else "negative" if avg < -0.1 else "neutral",
"score": round(avg, 3),
"intensity": "strong" if abs(avg) > 0.6 else "mild",
"horizon": self._infer_horizon(text),
}
def _score(self, ctx: str) -> float:
s = 0.0
for phrase in self.POSITIVE["strong"]:
if phrase in ctx: s += 0.8
for phrase in self.POSITIVE["mild"]:
if phrase in ctx: s += 0.4
for phrase in self.NEGATIVE["strong"]:
if phrase in ctx: s -= 0.8
for phrase in self.NEGATIVE["mild"]:
if phrase in ctx: s -= 0.4
# 否定词翻转
if re.search(r"(未|没有|并非|不存在).{0,5}", ctx):
s = -s * 0.8
return max(-1.0, min(1.0, s))
四、事件抽取与知识图谱
4.1 结构化事件抽取
@dataclass
class FinEvent:
event_type: str # earnings_beat / contract_win / regulatory_action
subject: str # 公司名称/代码
predicate: str # 动作
objects: list[str] # 金额/对象方/条款
timestamp: str
sentiment_impact: float
confidence: float
EVENT_PATTERNS = {
"equity_acquisition": (
r"(?P<acquirer>.{2,10})(拟|计划|公告)(收购|并购)"
r"(?P<target>.{2,10})(?P<stake>\d+(?:\.\d+)?%)?(?:股权|股份)"
),
"earnings_disclosure": (
r"(?P<company>.{2,10})(?:预计)?(?P<period>[\d年季]+)?"
r"净利润(?P<direction>增长|下降|亏损)"
r"(?P<magnitude>\d+(?:\.\d+)?%|[\d,.]+亿元)"
),
"regulatory_action": (
r"(?P<regulator>证监会|上交所|深交所).{0,30}"
r"(?P<action>立案|问询|处罚|调查)"
r".{0,20}(?P<company>.{2,10}(?:股份|科技|集团))"
),
}
4.2 公司关系知识图谱
金融知识图谱核心关系:
公司 A --SUPPLIES_TO--> 公司 B (供应链)
公司 A --COMPETES_WITH--> 公司 C (竞争)
公司 A --CONTROLS--> 子公司 D (控股)
人物 X --MANAGES--> 公司 A (管理)
事件 E --AFFECTS--> 公司 A (影响)
供应链风险传导是知识图谱最有价值的应用之一:当某个上游供应商出现负面事件时,自动识别受影响的下游公司。
# Neo4j 查询:供应链风险传导
SUPPLY_CHAIN_RISK_QUERY = """
MATCH path = (c:Company {code: $code})<-[:SUPPLIES_TO*1..3]-(supplier)
WHERE supplier.risk_flag IS NOT NULL
RETURN
[node in nodes(path) | node.name] AS chain,
supplier.risk_flag AS risk_type,
length(path) AS depth
ORDER BY depth
LIMIT 50
"""
五、量化信号生成与回测
5.1 情感动量因子
import pandas as pd
import numpy as np
from scipy import stats
class NLPAlphaGenerator:
"""将情感分析结果转化为可回测的量化因子"""
def sentiment_momentum(
self,
data: pd.DataFrame, # [date, stock_code, sentiment_score, doc_count]
halflife: int = 5,
) -> pd.DataFrame:
"""
情感动量因子:
近期正面情感累积越强,短期表现可能越好。
使用指数加权移动平均做时间衰减。
"""
result = []
alpha = 1 - np.exp(-np.log(2) / halflife)
for code, grp in data.groupby("stock_code"):
g = grp.sort_values("date").copy()
g["sent_ema"] = g["sentiment_score"].ewm(alpha=alpha, adjust=False).mean()
g["sent_z"] = stats.zscore(g["sent_ema"].fillna(0))
result.append(g)
return pd.concat(result)
def event_surprise(
self, events: list, baseline_window: int = 20,
) -> pd.DataFrame:
"""
事件惊喜因子:
事件情感 - 过去 N 天情感基线 = 惊喜度。
惊喜度显著正/负 -> 信号。
"""
signals = []
for evt in events:
baseline = self._get_baseline(evt.subject, evt.timestamp, baseline_window)
surprise = evt.sentiment_impact - baseline
if abs(surprise) > 0.3: # 阈值过滤
signals.append({
"date": evt.timestamp,
"stock_code": evt.subject,
"event_type": evt.event_type,
"surprise": surprise,
"direction": "long" if surprise > 0 else "short",
})
return pd.DataFrame(signals)
5.2 回测框架
class Backtester:
"""
NLP 信号回测。
关键:严格防止数据窥视(look-ahead bias)。
"""
def run(
self,
signals: pd.DataFrame,
returns: pd.DataFrame,
holding_days: int = 5,
cost: float = 0.001,
) -> dict:
"""
严格执行:
1. 信号日 T -> T+1 开盘买入(不能用 T 日收盘)
2. T+1+holding 卖出
3. 扣除双边手续费
"""
# ... 对齐 + 计算持有期收益 ...
return self._metrics(portfolio_returns)
def _metrics(self, rets: pd.Series) -> dict:
ann_ret = rets.mean() * 252
ann_vol = rets.std() * np.sqrt(252)
sharpe = ann_ret / ann_vol if ann_vol > 0 else 0
cum = (1 + rets).cumprod()
peak = cum.expanding().max()
dd = (cum - peak) / peak
max_dd = dd.min()
return {
"annual_return_pct": round(ann_ret * 100, 2),
"annual_vol_pct": round(ann_vol * 100, 2),
"sharpe": round(sharpe, 3),
"max_drawdown_pct": round(max_dd * 100, 2),
"win_rate_pct": round((rets > 0).mean() * 100, 1),
"calmar": round(ann_ret / abs(max_dd), 3) if max_dd != 0 else 0,
}
六、监管合规要点
在中国,证券分析服务受严格监管。系统设计必须在合规框架内运行。
| 维度 | 要求 | 实现方式 |
|---|---|---|
| 资质 | 须持牌机构运营或内部使用 | 系统仅供内部研究 |
| 数据合规 | 不得使用内幕信息 | 仅接入公开数据源 + 溯源 |
| 数据本地化 | 重要数据境内存储 | 国内合规区域部署 |
| 风险提示 | 分析须附风险提示 | 输出自动附免责声明 |
| 信息安全 | 防市场操纵 | 数据验证 + 异常检测 + 审计日志 |
法律红线:系统所有输出均为辅助性研究参考,不构成投资建议。技术层面保障:
- 所有报告页头强制显示免责声明
- 不提供具体买卖点位或仓位建议
- 不支持实盘交易接口
- 操作日志全量留存
七、可解释性设计
金融场景对可解释性要求极高——分析师需要理解"为什么系统认为这是利空",而不是盲信一个分数。
7.1 解释层级
| 层级 | 内容 | 示例 |
|---|---|---|
| 信号来源 | 哪篇文档触发了信号 | "2026-02-15 XX公司公告,标题..." |
| 关键词句 | 文档中的决策关键句 | "净利润同比下降 42.3%" |
| 情感分解 | 正面/负面因素拆解 | "+0.3(获得合同) -0.8(利润下滑) = -0.5" |
| 历史对比 | 类似事件的历史表现 | "过去 5 次类似业绩预警,3 次 T+5 下跌 > 3%" |
| 关联传导 | 知识图谱路径 | "A 是 B 的核心供应商(占比 30%)" |
八、常见错误与避坑指南
| 错误 | 后果 | 正确做法 |
|---|---|---|
| 用通用情感模型 | "利润下滑"判为中性 | 金融领域 fine-tuned 模型 |
| 忽略否定词 | "未发生违约"判为负面 | 否定词检测 + 翻转 |
| 回测时信号日买入 | 引入 look-ahead bias | T 日信号 -> T+1 开盘买入 |
| 不做截面中性化 | 行业/市值因子污染 | Z-score 标准化 + 行业中性 |
| 情感词典不更新 | 新术语覆盖不到 | 定期从标注数据中扩充 |
| 只看 Sharpe 不看 IC | 可能是偶然高收益 | IC 均值 + ICIR 综合评估 |
| 忽略合规约束 | 法律风险 | 免责声明 + 审计日志 |
| 公告解析不处理表格 | 财务数据提取遗漏 | PDF 表格识别 + 结构化 |
九、评估指标体系
| 模块 | 指标 | 目标值 |
|---|---|---|
| 文本分类 | 准确率 | >= 92% |
| 情感分析 | 与人工标注一致率 | >= 85% |
| 事件抽取 | F1 | >= 80% |
| 量化信号 | IC 均值 | >= 0.05 |
| 量化信号 | ICIR | >= 0.8 |
| 回测绩效 | Sharpe | >= 1.0 |
| 系统延迟 | 公告到分析完成 | <= 3 分钟 |
| 数据覆盖 | A 股上市公司 | >= 95% |
十、总结
智能投研系统的最终价值,不在于替代研究员,而在于帮助研究员站在更高的信息起点上。技术降低了信息处理的成本,但对商业模式的理解、对管理层的判断、对行业趋势的前瞻——这些始终是人类研究员的核心竞争力。
三条核心建议:
- 合规第一:所有设计决策都要先过合规审查
- 可解释性:金融从业者不会信任黑盒,每个信号必须可追溯
- 闭环验证:NLP 指标再好也要经过回测验证和线上追踪
Maurice | maurice_wen@proton.me