Week 4 金融大数据分析

Big Data Analytics in Finance

本周内容概览

理论部分 (2h)

  • 大数据分析框架概览
  • 文本分析管线
  • 情感分析:词典→ML→LLM
  • 主题建模:LDA → BERTopic
  • 图像分析:图表识别、卫星图像
  • 多模态分析
  • LLM作为NLP通用接口
  • 论文案例:LDA经济周期度量
  • 论文案例:CNN价格趋势识别

实践部分 (2h)

  • 项目A:新闻文本经济周期度量(论文1复现)
  • 项目B:CNN股票价格趋势预测(论文2复现)
  • 数据处理与模型实现
  • 结果分析与讨论

4.1.1 大数据技术生态详解

批处理 vs 流处理

特性 批处理 (Hadoop/Spark) 流处理 (Flink/Storm)
延迟 分钟-小时级 毫秒-秒级
数据范围 全量历史数据 实时数据窗口
计算模式 分批次处理 持续计算
适用场景 日报/月报生成 实时风控/交易
金融案例 收盘后风险报告 实时市场监控

Lambda架构

graph LR Data["实时数据流"] --> Speed["速度层
流处理
低延迟"] Data --> Batch["批处理层
全量计算
高精度"] Speed --> Serving["服务层
合并结果"] Batch --> Serving Serving --> Query["查询
API"]

金融大数据平台典型架构

  • 数据采集层:Kafka (消息队列)
  • 存储层:HDFS + ClickHouse (列式存储)
  • 计算层:Spark (批处理) + Flink (流处理)
  • 查询层:Presto / Trino (交互式查询)
  • 分析层:Python ML pipeline

4.1.2 金融数据仓库设计

星型模型 vs 雪花模型

-- 金融数据仓库 - 交易事实表
CREATE TABLE fact_trades (
    trade_id          BIGINT,
    security_id       INT,      -- 维表: 证券信息
    trader_id         INT,      -- 维表: 交易员
    branch_id         INT,      -- 维表: 分支机构
    trade_date        DATE,     -- 维表: 时间
    trade_time        TIMESTAMP,
    quantity          DECIMAL(18,2),
    price             DECIMAL(10,4),
    amount            DECIMAL(18,2),
    commission        DECIMAL(10,4)
);

-- 时间维表 - 支持按日/周/月/季度聚合
CREATE TABLE dim_calendar (
    date_id           DATE PRIMARY KEY,
    year              INT,
    quarter           INT,
    month             INT,
    week_of_year      INT,
    is_trading_day    BOOLEAN,
    is_month_end      BOOLEAN
);

典型金融数据集市

数据集市 数据源 用途
交易数据集市 交易系统 交易分析、算法评估
风控数据集市 风控系统 VaR计算、压力测试
客户数据集市 CRM系统 客户画像、产品推荐
市场数据集市 行情源 因子计算、定价

4.1.3 实时数据处理

Kafka消息队列在金融中的应用

from kafka import KafkaConsumer, KafkaProducer
import json

# 行情数据生产者
producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# 发送tick级行情
tick_data = {
    'symbol': '600519.SH',
    'timestamp': '2025-03-15 09:30:00.123',
    'price': 1685.50,
    'volume': 100,
    'bid': 1685.00,
    'ask': 1685.50
}
producer.send('market_tick', tick_data)
# 实时风控消费者
consumer = KafkaConsumer(
    'market_tick',
    bootstrap_servers=['localhost:9092'],
    group_id='risk_monitor'
)
for msg in consumer:
    tick = json.loads(msg.value)
    # 实时检查价格异常
    if abs(tick['price'] - tick['bid']) / tick['bid'] > 0.05:
        print(f"价格偏离预警: {tick['symbol']}")

Flink实时流处理

// Flink流处理 - 实时波动率计算
DataStream<TickData> tickStream = env.addSource(kafkaSource);

tickStream
    .keyBy(tick -> tick.symbol)
    .timeWindow(Time.minutes(1))
    .apply(new RealizedVolatilityCalculator())
    .addSink(riskDashboardSink);

4.1.4 时间序列数据库

为什么金融需要时序数据库

特性 传统数据库 时序数据库(InfluxDB/ClickHouse)
写入性能 行式存储,写入慢 列式存储,写入快100x
压缩比 3-5x 10-20x
时间查询 全表扫描慢 按时间分区,毫秒级
聚合查询 GROUP BY开销大 预聚合+物化视图
窗口函数 支持有限 原生支持滑动窗口
# 使用InfluxDB存储金融时序数据
from influxdb_client import InfluxDBClient

client = InfluxDBClient(url="http://localhost:8086", token="...")
query_api = client.query_api()

# 查询过去1小时A股所有股票的tick数据
query = '''
from(bucket: "stock_ticks")
  |> range(start: -1h)
  |> filter(fn: (r) => r["_measurement"] == "trade")
  |> filter(fn: (r) => r["exchange"] == "SSE")
  |> aggregateWindow(every: 1m, fn: mean)
  |> yield(name: "minute_ohlc")
'''

result = query_api.query(query)

4.2.1 命名实体识别(NER)在金融中的应用

金融NER:从非结构化文本中提取结构化信息

# 使用LAC进行金融命名实体识别
from LAC import LAC

lac = LAC(mode='lac')

text = "贵州茅台酒股份有限公司2024年营收1500亿元,同比增长20%"
result = lac.run(text)

for word, tag in zip(*result):
    print(f"{word}: {tag}")
    # 贵州茅台酒股份有限公司: ORG (组织机构)
    # 2024年: TIME
    # 1500亿元: MONEY
    # 同比增长20%: PERCENT

金融关键实体类型

实体类型 标签 示例
公司/机构 ORG 贵州茅台、中国平安、上交所
人名 PER 马明哲、易会满
金融产品 PROD 雪球产品、中证500ETF
金额 MONEY 1500亿元、100万美元
百分比 PERCENT 20%、5.5%
时间 TIME 2024年Q4、3月15日
金融指标 METRIC ROE、PE、资产负债率

4.2.2 关系抽取与知识图谱

金融关系抽取:从文本中提取实体间的关系

文本:"宁德时代向特斯拉供应电池"

实体关系:
(宁德时代, 供应, 电池) → 产品供应关系
(宁德时代, 客户, 特斯拉) → 客户关系
# 使用LLM进行金融关系抽取
prompt = """
从以下金融新闻中提取实体和关系:

"比亚迪与宁德时代签署战略合作协议,
双方将在动力电池领域展开深度合作。
协议金额预计超过100亿元。"
请输出:
{
  "relations": [
    {
      "subject": "比亚迪",
      "relation": "合作",
      "object": "宁德时代",
      "details": "动力电池领域战略合作"
    }
  ],
  "amount": "100亿元",
  "impact": "positive"
}
"""

金融知识图谱应用

  • 供应链分析:识别上下游关系,预测连锁反应
  • 股权穿透:挖掘实际控制人
  • 关联交易:识别潜在利益输送
  • 风险传导:单一公司风险如何通过网络传导

4.3.1 情感分析方法对比实验

基于词典 vs ML vs LLM情感分析对比

方法 准确率 召回率 F1 速度 可部署性
词典(LM词典) 65% 58% 0.61 极快
词袋+逻辑回归 72% 68% 0.70
FinBERT微调 85% 82% 0.83 中等
GPT-4零样本 88% 86% 0.87 依赖API
本地LLM零样本 82% 79% 0.80 中等

选择指南

  • 需要实时/高频:用词典或ML模型
  • 需要高精度且非实时:用LLM
  • 最佳实践:ML模型做第一轮,LLM复核边界案例

4.3.2 Loughran-McDonald词典详解

LM词典 (Loughran-McDonald, 2011) 是金融情感分析的标准词典

词典结构

类别 词数 示例
正面(Positive) 354个 profit, growth, record, strategic
负面(Negative) 2,355个 loss, default, impairment, penalty
不确定(Uncertainty) 297个 approximate, contingent, depend
诉讼(Litigious) 903个 claimant, deposition, injunction
强语气(Strong Modal) 19个 always, never, undoubtedly
弱语气(Weak Modal) 27个 almost, barely, could

为什么LM词典比通用词典更适合金融

通用词典标记为"负面的词"在金融中可能是"正面的":

"liability" (负债) → 通用词典: 负面 → LM词典: 中性(会计术语)
"risk" (风险) → 通用词典: 负面 → LM词典: 中性(金融核心概念)
"loss" (亏损) → 通用词典: 负面 → LM词典: 负面 ✓
"default" (违约) → 通用词典: 负面 → LM词典: 负面 ✓

4.3.3 情感因子构建与回测

# 构建情感因子
import pandas as pd
import numpy as np

class SentimentFactor:
    """基于新闻情感构建交易因子"""
    
    def __init__(self, sentiment_score, price_data):
        """
        sentiment_score: DataFrame(index=日期, columns=股票, values=情感得分)
        price_data: DataFrame(index=日期, columns=股票, values=收益率)
        """
        self.sentiment = sentiment_score
        self.prices = price_data
    
    def construct_factor(self, lookback=5):
        """构建情感动量因子"""
        # 移动平均情感得分
        sentiment_ma = self.sentiment.rolling(lookback).mean()
        # 情感变化率
        sentiment_momentum = sentiment_ma.diff()
        return sentiment_momentum
    def backtest_factor(self, factor, holding_period=5):
        """因子回测"""
        # 每日按因子排序分十组
        ranks = factor.rank(axis=1, pct=True)
        long = ranks > 0.9  # 做多最高分组
        short = ranks < 0.1  # 做空最低分组
        
        # 计算多空组合收益
        long_ret = self.prices.shift(-holding_period).rolling(
            holding_period).mean() * long
        short_ret = -self.prices.shift(-holding_period).rolling(
            holding_period).mean() * short
        
        portfolio_ret = (long_ret + short_ret).mean(axis=1)
        
        # 计算夏普比率
        sharpe = np.sqrt(252) * portfolio_ret.mean() / portfolio_ret.std()
        return sharpe, portfolio_ret

# 研究结论:A股市场新闻情感因子的多空夏普约0.6-1.2
# 在中小盘股票上效果更显著

4.3.4 情感分析在中国市场的特殊挑战

A股情感分析挑战

  1. 中文金融词典缺失:中文缺乏类似LM词典的标准金融情感词典
解决方案: 
- 基于百度/东方财富评论语料自建词典
- 用LLM自动构建金融情感词表
- 使用预训练中文金融模型(FinBERT-Chinese)
  1. 散户情绪占比高:A股散户交易占比~60%,情感来源特殊
数据源选择:
- 雪球/股吧评论 → 散户情绪 (对中小盘影响大)
- 券商研报 → 机构观点 (对大盘影响大)
- 财经新闻 → 整体市场情绪
  1. 政策敏感性:A股受政策影响大
政策情感分析重点:
- 国务院常务会议公告
- 央行货币政策报告
- 证监会监管动态
- 政治局会议通稿
  1. 事件驱动:A股概念炒作频繁
"元宇宙"概念 → 相关股票普涨30-50%
"AI大模型"概念 → 科技股集体爆发
这种情感传导不是基于基本面,而是主题炒作

4.4.1 动态主题演化分析

主题强度随时间变化的分析

# 动态主题建模 - 监测主题强度变化
from gensim.models import LdaSeqModel
import matplotlib.pyplot as plt

# 将文档按时间分段
time_slices = [len(docs_2022), len(docs_2023), len(docs_2024)]

# 动态LDA
ldaseq = LdaSeqModel(
    corpus=corpus,
    id2word=dictionary,
    time_slice=time_slices,
    num_topics=5
)

# 提取各时间段主题强度
for t in range(3):
    print(f"=== 时间段 {t} ===")
    topics = ldaseq.print_topic(t, top_terms=10)
    for topic_id, words in topics:
        print(f"主题{topic_id}: {words}")

金融应用:主题生命周期分析

主题生命周期:出现 → 增长 → 成熟 → 衰退

案例: "新能源汽车"主题(2019-2025)
2019: 出现, 强度0.05
2020: 增长, 强度0.12 (政策补贴)
2021: 暴增, 强度0.35 (销量爆发)
2022: 成熟, 强度0.40
2023: 分化, 强度0.30 (竞争加剧)
2024: 衰退, 强度0.15
2025: 稳定, 强度0.10

4.4.2 BERTopic实战详解

# BERTopic高级用法 - 动态主题建模
from bertopic import BERTopic
from bertopic.dimensionality import BaseDimensionalityReduction
from sklearn.decomposition import TruncatedSVD

# 自定义主题表示
topic_model = BERTopic(
    embedding_model="shibing624/text2vec-base-chinese",
    umap_model=TruncatedSVD(n_components=50),  # 替代UMAP,更快
    min_topic_size=10,
    nr_topics="auto",  # 自动缩减主题数
    verbose=True
)

# 拟合模型
topics, probs = topic_model.fit_transform(documents)

# 调优主题表示 - 使用LLM
from bertopic.representation import OpenAI

representation_model = OpenAI(
    model="qwen2.5:7b",  # 本地LLM
    prompt="根据以下关键词,总结一个金融主题名称:\n{keywords}",
    nr_docs=5
)

topic_model.update_representations(
    documents, 
    topic_model.topic_representations_,
    representation_model
)

# 可视化主题演化
topics_over_time = topic_model.topics_over_time(
    docs, topics, timestamps
)
topic_model.visualize_topics_over_time(topics_over_time)

4.5.1 多模态金融分析

多模态数据融合

graph TD Text["文本数据
新闻/公告/研报"] --> TextFeat["文本特征
BERT/LSTM"] Image["图像数据
图表/卫星/文件"] --> ImageFeat["图像特征
CNN/ViT"] Audio["音频数据
电话会/访谈"] --> AudioFeat["音频特征
Wav2Vec"] Numeric["数值数据
行情/财务"] --> NumFeat["数值特征
MLP"] TextFeat --> Fusion["多模态融合层
Transformer/Cross-Attention"] ImageFeat --> Fusion AudioFeat --> Fusion NumFeat --> Fusion Fusion --> Output["输出
预测/分类/生成"]

多模态融合方法

方法 描述 金融应用
早期融合 特征层拼接 财报+CEO语气情感
中期融合 模态间交叉注意 新闻图片+文本影响分析
晚期融合 独立预测后投票 多源信号综合交易
动态融合 学习模态权重 不同市态下调整模态重要性

4.5.2 语音情感分析

财报电话会议语音分析

import librosa
import numpy as np

# 提取语音特征
def extract_prosodic_features(audio_path):
    y, sr = librosa.load(audio_path, sr=16000)
    
    features = {
        'pitch_mean': np.mean(librosa.pyin(y, fmin=50, fmax=300)[0]),
        'speech_rate': len(librosa.effects.split(y)) / (len(y) / sr) * 60,
        'energy_mean': np.mean(librosa.feature.rms(y=y)),
        'voice_breaks': len(librosa.effects.split(y, top_db=30))
    }
    
    return features

# 研究表明:
# - 管理层语速加快 → 可能隐藏负面信息
# - 音调波动增大 → 情绪波动,不确定性强
# - 停顿增多 → 犹豫,可能隐瞒不利信息
# - 语音情感+文本情感结合预测精度提高15-20%

多模态情感分析的增量价值

模态 预测精度 单独使用
文本 70-75% 需大量标注
语音 65-70% 依赖音频质量
文本+语音 80-85% 互补效果显著
文本+语音+图像 82-88% 收益递减

4.5.3 OCR在金融文档处理中的应用

import pytesseract
from PIL import Image
import cv2

# 金融文档OCR管线
def extract_financial_table(image_path):
    """从扫描的财报PDF中提取表格数据"""
    
    # 1. 图像预处理
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 二值化
    _, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
    # 去噪
    denoised = cv2.medianBlur(binary, 3)
    
    # 2. OCR识别
    config = '--oem 3 --psm 6 -l chi_sim+eng'
    text = pytesseract.image_to_string(denoised, config=config)
    
    # 3. LLM后处理 - 提取结构化数据
    response = llm_client.chat.completions.create(
        model="qwen2.5:7b",
        messages=[{
            "role": "user",
            "content": f"""
            从以下OCR结果中提取表格数据,输出CSV格式:
            
            {text}
            
            表格包含:项目名称、本期金额、上期金额、变动比例
            """
        }]
    )
    
    return response.choices[0].message.content

# 应用场景:批量处理500+份年报
# 提取关键财务指标 → 构建因子数据库

4.5.4 另类数据:网络爬虫与API

金融另类数据获取

# 1. 电商数据监测
# 天猫/京东商品销量 → 品牌营收预测
# 使用平台API或网络爬虫

# 2. 招聘数据
# 公司招聘岗位数量和类型
# 研发岗增多 → 技术投入加大
# 销售岗缩减 → 可能缩减业务

# 3. 专利数据
# 专利申请数量和质量
# 分析公司技术创新能力
# 科创板公司的重要指标

# 4. 供应链数据
# 进出口海关数据
# 货运物流数据
# 开工率数据

# 5. 社交媒体
# 微博/雪球讨论热度
# 百度搜索指数
# 微信指数

def build_supply_chain_indicator(port_shipment_data):
    """基于港口货运量构建贸易活跃度指标"""
    # 集装箱吞吐量 → 贸易量代理
    # 波罗的海干散货指数(BDI) → 航运成本
    # 两者结合 → 贸易活跃度综合指标
    pass

4.5.5 数据质量与清洗

金融数据常见质量问题

问题 表现 处理方式
缺失值 停牌导致价格缺失 前向填充/插值
异常值 数据录入错误 Winsorize/IQR截断
幸存者偏差 只保留现存股票 退市股票必须包含
前视偏差 使用了未来信息 严格按时间序列处理
数据不一致 不同源数据冲突 权威源优先/交叉验证
财报调整 会计准则变化 可比性调整

def clean_financial_data(df):
    """金融数据清洗管线"""
    # 1. 处理缺失值
    df = df.ffill()  # 前向填充(时间序列标准做法)
    
    # 2. 异常值处理
    for col in df.select_dtypes(include=[np.number]).columns:
        q1, q3 = df[col].quantile(0.01), df[col].quantile(0.99)
        df[col] = df[col].clip(q1, q3)
    
    # 3. 去趋势(适用于非平稳金融时间序列)
    if is_non_stationary(df['price']):
        df['return'] = df['price'].pct_change()
        df = df.dropna()
    
    return df

4.6.1 数据可视化最佳实践

金融数据可视化关键原则

import plotly.graph_objects as go
from plotly.subplots import make_subplots

# 专业金融图表 - K线图+成交量+指标
fig = make_subplots(
    rows=3, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.05,
    row_heights=[0.6, 0.2, 0.2]
)
# K线图
fig.add_trace(
    go.Candlestick(
        x=df.index, open=df['Open'], 
        high=df['High'], low=df['Low'], close=df['Close'],
        name="K线"
    ),
    row=1, col=1
)
# 成交量
fig.add_trace(
    go.Bar(x=df.index, y=df['Volume'], name="成交量"),
    row=2, col=1
)
# 情感得分叠加
fig.add_trace(
    go.Scatter(x=df.index, y=sentiment_scores, 
              mode='lines', name='情感得分'),
    row=3, col=1
)

fig.update_layout(title="股价与情感得分联动分析")

4.6.2 LLM增强数据标注

# 使用LLM进行金融文本标注

def llm_annotate_financial_text(texts, task_type):
    """使用LLM批量标注金融文本"""
    
    annotations = []
    
    for text in texts:
        if task_type == "sentiment":
            prompt = f"""
            判断以下金融文本的情感倾向。
            只输出JSON:{{"sentiment": "positive/negative/neutral", "confidence": 0-1}}
            
            文本:{text}
            """
        elif task_type == "event_type":
            prompt = f"""
            识别以下金融文本的事件类型。
            可选:业绩发布/并购重组/股东变动/监管处罚/其他
            只输出JSON:{{"event_type": "...", "confidence": 0-1}}
            
            文本:{text}
            """
        
        response = client.chat.completions.create(
            model="qwen2.5:7b",
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"}
        )
        
        annotations.append(json.loads(
            response.choices[0].message.content
        ))
    
    return pd.DataFrame(annotations)

# 优势:
# - 无需人工标注,零成本启动
# - 一致性高(同一模型标注标准统一)
# - 可快速迭代标签体系
# - 局限:对细致分类任务精度有限

4.1 大数据分析框架

Big Data Architecture

  • 数据分析完整流程
  • 大数据技术栈
  • 实时流处理
  • 金融数据特征

金融大数据分析整体框架

graph LR subgraph DS["数据源"] A1["行情数据
(Tick/分钟/日)"] A2["基本面数据
(财报/指标)"] A3["另类数据
(新闻/文本/图表/卫星)"] end Collect["数据采集/清洗/规范化"] Batch["批处理/特征工程:Spark(离线)"] Store["数据存储:HDFS(湖)+ 数仓(SQL/NoSQL)"] Analysis["分析建模:回测/预测/风控"] Visual["结果展示:看板/报表/API/告警(可选)"] subgraph Tech["技术栈(离线重点)"] B1["HDFS:批量存储/湖"] B2["Spark:离线ETL/特征工程"] B4["SQL/NoSQL:数仓/索引"] B3["Kafka(可选):落地/缓冲"] end %% 数据源 -> 采集 A1 --> Collect A2 --> Collect A3 --> Collect %% 采集 -> 批处理 -> 存储 -> 建模 -> 展示 Collect --> Batch Batch --> Store Store --> Analysis Analysis --> Visual %% 技术栈语义绑定(离线强调) B1 -. 承载 .-> Store B2 -. 支撑 .-> Batch B4 -. 提供 .-> Store B3 -. 可选用于 .-> Collect

金融数据特征

高频

  • Tick级行情数据(微秒级)
  • 每天上亿条数据
  • 存储和处理挑战大
  • 需要实时流处理

高噪

  • 信噪比极低(<5%)
  • 信号淹没在噪声中
  • 需要复杂的信号提取
  • 容易过拟合

多源异构

  • 结构化数据(行情、财报)
  • 半结构化(新闻、公告)
  • 非结构化(文本、图像、语音、视频)
  • 需要多模态融合

非平稳

  • 统计特性随时间变化
  • 历史模式可能不再适用
  • 需要在线学习和自适应
  • 模型需要持续更新

4.2 文本分析管线

Text Analytics Pipeline

  • 中文分词
  • TF-IDF
  • Word2Vec
  • BERT/FinBERT
  • LLM增强NLP

中文分词

挑战:中文没有空格分隔,需要分词

方法 代表工具 原理 速度 精度
基于词典 jieba 前缀词典+DP ⭐⭐⭐⭐⭐ ⭐⭐⭐
基于统计 HanLP CRF/HMM ⭐⭐⭐ ⭐⭐⭐⭐
基于深度学习 LAC, BERT BiLSTM+CRF ⭐⭐ ⭐⭐⭐⭐⭐
大模型 ChatGPT/Claude 语义理解 ⭐ ⭐⭐⭐⭐⭐

金融分词注意事项

  • 金融术语词典很重要("做空"、"套利"、"RiskMetrics"等)
  • 上市公司名称需要特殊处理
  • 数字+单位组合("10bp"、"5年期")
import jieba
jieba.add_word("量化交易")  # 确保自定义词被正确切分

text = "机器学习在量化交易中的应用日益广泛"
words = jieba.lcut(text)
# ['机器学习', '在', '量化交易', '中', '的', '应用', '日益', '广泛']

文本向量化

TF-IDF

  • 简单、可解释、适合关键词提取
  • 缺点是稀疏高维、忽略语义

Word2Vec

  • 分布式表示,捕捉语义相似性
  • "国王 - 男人 + 女人 ≈ 女王"
  • 金融类比:"茅台 - 白酒 + 银行 ≈ 招商银行"

BERT/FinBERT

  • 上下文相关的动态表示
  • "股票上涨"vs"利率上涨"中同一个"上涨"有不同表示
  • FinBERT在BERT基础上用金融语料继续预训练

LLM Embedding

  • 最先进的语义理解
  • 但计算成本高,延迟大

中文分词技术对比

金融专有名词的分词挑战

错误分词: "上证综指/今日/大/涨" → "上证/综指/今日/大涨"
          "科创板/50/ETF" → "科创/板/50/ETF"

正确分词应是: "上证综指"、"科创板50ETF" 作为整体

主流工具对比

工具 原理 速度 金融专业词 使用建议
jieba 词典+Trie树+HMM ⭐⭐⭐⭐⭐ 需自定义词典 通用首选
pkuseg CRF+深度学习 ⭐⭐⭐ ✅ 较好 学术场景
HanLP BiLSTM+CRF ⭐⭐⭐⭐ ✅ 金融预训练 金融推荐
LAC (百度) BiGRU+CRF ⭐⭐⭐⭐ ⚠️ 通用 快速场景

金融分词关键:必须加载金融自定义词典!

import jieba
jieba.load_userdict("finance_dict.txt")  # 含"上证综指""量化宽松"等

词干提取 vs 词形还原:英文金融文本

方法 原理 示例 金融影响
Stemming 规则砍后缀 "investing"→"invest" 可能丢失信息
Lemmatization 词典+词性→原形 "worse"→"bad" 保持语义
金融专用 映射到统一实体 "AAPL"/"Apple Inc"/"苹果"→AAPL 必需

停用词的金融陷阱

通用停用词列表包含:
  "up" → 但 "EPS beat, stock up" 中的 "up" 是方向信号
  "down" → 但 "downgrade, down 3%" 中的 "down" 是关键信息
  "high" → 但 "52-week high" 是重要信号
  "low" → 同样

金融NLP需要定制停用词表!
保留方向词(bull/bear/up/down/high/low)、数值、货币符号

LDA主题模型直观解释

LDA (Latent Dirichlet Allocation)

假设: 每篇文档是主题的混合,每个主题是词的分布

生成过程:
1. 为文档d选择主题分布 θ_d ~ Dir(α)
2. 为每个词:
   a. 从主题分布中选择主题 z ~ Cat(θ_d)
   b. 从该主题的词分布中选择词 w ~ Cat(β_z)

学习目标: 给定文档,推断主题分布 θ 和词分布 β

金融应用示例

从10000篇研报中提取20个主题:
  主题1: {利率(0.15), 央行(0.12), 通胀(0.08), 货币政策(0.07)...}
        → "宏观政策"
  主题2: {ROE(0.10), 净利(0.09), 毛利率(0.08), 营收(0.07)...}
        → "基本面分析"
  主题3: {阻力位(0.12), 支撑(0.10), 量能(0.08), MACD(0.06)...}
        → "技术分析"

NER与关系抽取在金融中的特殊需求

金融NER的实体类型

实体类型 示例 难度
公司名 "阿里巴巴""Alibaba""BABA" → 同一实体 高(多名称映射)
人名 "马云""Jack Ma" → 同一实体
金额 "10亿元""$1.5B""一千五百万元" 中(单位转换)
日期 "2024Q1""24年一季度""FY2024"
百分比 "同比增长15%""+15% YoY"
金融产品 "雪球""沪深300ETF""IO2406-C-5800"

BiLSTM-CRF架构

输入: 中国/央行/宣布/降准/50/个/基点
  ↓
BiLSTM: 每个字的上下文表示
  ↓
CRF: 学习标签转移约束 → 输出 BIO标签
  ↓
输出: B-ORG I-ORG O O O O O O
      (中国-央行 为组织实体)

LDA主题模型直观解释

动态主题建模 (Dynamic Topic Model)
追踪主题随时间演化:

主题"房地产"的词分布演化:
  2020: {房价(0.15), 调控(0.10), 去库存(0.08)...}
  2022: {暴雷(0.12), 违约(0.10), 纾困(0.09)...}
  2024: {回暖(0.08), 政策松绑(0.07), 并购重组(0.06)...}

→ 清晰看到市场关注焦点的转移
→ 可用于构造"主题动量"因子

另类数据实证与合规

另类数据的学术证据

数据类型 实证发现 信号衰减
卫星图像 停车场数据预测零售销售 (R²≈0.7) 6-12个月
信用卡消费 实时消费追踪 vs 官方统计 (领先2-3周) 3-6个月
社交媒体 Reddit情绪预测短期反转 1-3个月
招聘数据 岗位增长→未来营收增长 12-24个月

数据采购与爬取的合规边界

✅ 合法:
  - 公开网站的数据爬取 (遵守robots.txt)
  - 购买授权数据 (Bloomberg/Wind/通联数据)
  - 使用API获取数据 (在条款允许范围内)
  
❌ 不合法/不合规:
  - 爬取反爬虫保护的网站
  - 未经授权使用第三方付费数据
  - 获取个人隐私数据 (如未经授权的通讯记录)
  - 使用内部/非公开信息

金融文本分析端到端实战

# 完整金融情感因子构建Pipeline
import akshare as ak
import jieba
from transformers import pipeline

# Step 1: 获取新闻数据
news = ak.stock_news_em()  # 东方财富新闻
# Step 2: 金融分词
jieba.load_userdict("finance_dict.txt")
def tokenize_finance(text):
    words = jieba.lcut(text)
    # 金融停用词过滤
    stopwords = load_finance_stopwords()  # 不含方向词的定制停用词表
    return [w for w in words if w not in stopwords]
# Step 3: 情感分析 (FinBERT)
sentiment_pipeline = pipeline(
    "text-classification",
    model="ProsusAI/finbert",
    device=0
)
# Step 4: 批量分析
results = []
for _, row in news.iterrows():
    sentiment = sentiment_pipeline(row['content'])[0]
    results.append({
        'datetime': row['datetime'],
        'title': row['title'],
        'sentiment': sentiment['label'],    # positive/negative/neutral
        'score': sentiment['score'],
        'tokens': tokenize_finance(row['content'])
    })
# Step 5: 构建日频情感因子
df = pd.DataFrame(results)
df['date'] = pd.to_datetime(df['datetime']).dt.date
daily_sentiment = df.groupby('date').agg({
    'score': ['mean', 'count'],
    'sentiment': lambda x: (x=='positive').mean() - (x=='negative').mean()
})
# Step 6: 回测检验
# 情感因子 vs 未来收益的IC分析
daily_sentiment.columns = ['avg_score', 'news_count', 'sentiment_score']

LLM增强的NLP管线

传统NLP管线 vs LLM管线

任务 传统方法 LLM方法
分词/词性标注 jieba + HMM LLM可选任何方式
命名实体识别 BiLSTM-CRF "请提取公司名、人名、金额"
关系抽取 管道式(pipeline) "找出公司A和公司B的关系"
情感分类 训练集微调 "请判断该新闻的情感倾向"
文本摘要 T5/BART微调 "请用3句话总结这篇财报"

核心优势

  • 零样本/少样本:无需标注数据
  • 统一接口:所有NLP任务统一为"指令+文本"
  • 多任务:一次推理完成多个NLP任务

金融实践:LLM作为NLP的后端引擎,传统方法作为轻量级前端

TF-IDF手工计算示例

语料库:3篇金融新闻摘要

文档 内容
d1 "美联储加息 银行股上涨"
d2 "银行股下跌 美联储降息"
d3 "科技股上涨 美联储加息"





Step 1: TF计算(词频 = 词出现次数/文档总词数)

TF(d1) TF(d2) TF(d3)
美联储 1/3 ≈ 0.33 1/3 ≈ 0.33 1/3 ≈ 0.33
加息 1/3 ≈ 0.33 0 1/3 ≈ 0.33
银行股 1/3 ≈ 0.33 1/3 ≈ 0.33 0
上涨 1/3 ≈ 0.33 0 1/3 ≈ 0.33
下跌 0 1/3 ≈ 0.33 0
降息 0 1/3 ≈ 0.33 0
科技股 0 0 1/3 ≈ 0.33

Step 2: IDF计算 ()

DF(文档频率) IDF
美联储 3 log(3/3) = 0
加息 2 log(3/2) ≈ 0.18
银行股 2 log(3/2) ≈ 0.18
上涨 2 log(3/2) ≈ 0.18
下跌 1 log(3/1) ≈ 0.48
降息 1 log(3/1) ≈ 0.48
科技股 1 log(3/1) ≈ 0.48

关键洞察:"美联储"出现在所有文档中 → IDF=0 → 无区分能力。稀有词("下跌"、"降息")有最高IDF权重。

Word2Vec实战

import gensim
from gensim.models import Word2Vec
import numpy as np

# 准备金融语料
financial_corpus = [
    ['茅台', '股价', '上涨', '白酒', '板块', '走强'],
    ['银行', '股', '下跌', '利率', '上调', '影响'],
    ['科技', '股', '大涨', 'AI', '概念', '活跃'],
    ['白酒', '板块', '回调', '茅台', '估值', '偏高'],
    ['新能源', '汽车', '销量', '增长', '利好', '产业链'],
]
# 训练Word2Vec模型
model = Word2Vec(
    sentences=financial_corpus,
    vector_size=100,    # 词向量维度
    window=5,           # 上下文窗口
    min_count=1,        # 最低词频
    workers=4,          # 并行线程
    sg=1               # skip-gram (1) vs CBOW (0)
)
# 查找相似词
print("与'茅台'最相似的词:")
for word, sim in model.wv.most_similar('茅台', topn=3):
    print(f"  {word}: {sim:.3f}")
# 金融语义类比
result = model.wv.most_similar(
    positive=['科技', '白酒'], 
    negative=['茅台']
)
print(f"\n科技 - 茅台 + 白酒 ≈ {result[0][0]}")
# 保存/加载模型
model.save('financial_word2vec.model')
# model = Word2Vec.load('financial_word2vec.model')
# 获取词向量
maotai_vec = model.wv['茅台']  # shape: (100,)

金融中Word2Vec的价值

  • 发现隐含的股票关联(同类股票聚类)
  • 构建"金融词汇语义空间"用于相似事件识别
  • 为公司名称、产品名称创建标准化的向量表示

论文案例:WSJ文本的文档-词矩阵构建

Bybee, Kelly, Manela & Xiu (2024) "Business News and Business Cycles"

数据:约80万篇《华尔街日报》文章(1984-2017)

DTM构建流程

步骤 操作 说明
1 1-gram + 2-gram 同时提取单词和双词组合
2 停用词过滤 移除高频无信息词(约500个)
3 词形还原 将词还原为标准形式(running→run)
4 低频词去除 出现次数 < 5 的词移除
5 按月聚合 将每篇文章的词频按月汇总
# 简化的DTM构建示例(参考论文代码 code/build_data/)
from gensim.corpora import Dictionary
from collections import Counter

# 1. 分词 + bigram
tokens = [word_tokenize(doc.lower()) for doc in documents]
bigrams = [list(nltk.bigrams(doc)) for doc in tokens]
# 合并unigram和bigram
all_tokens = [t + ['_'.join(b)] for t, b in zip(tokens, bigrams)]

# 2. 构建词典并过滤
dictionary = Dictionary(all_tokens)
dictionary.filter_extremes(no_below=5, no_above=0.5)

# 3. 转换为词袋表示
corpus = [dictionary.doc2bow(doc) for doc in all_tokens]

# 4. 按月聚合(论文中按月份分区存储)
# 月度DTM用于后续LDA训练

关键设计选择

  • 双词组合(bigram)能捕捉"Federal Reserve"、"stock market"等金融术语
  • 词形还原确保"running"/"runs"/"ran"统一为"run"
  • 月度聚合降低文档数量,提高LDA训练效率

论文案例:LDA主题模型训练与主题解读

LDA模型配置(论文核心设定):

from gensim.models import LdaModel

# 训练LDA模型(参考 code/prep_LDA/fit_LDA_WSJ/)
lda_model = LdaModel(
    corpus=corpus,
    id2word=dictionary,
    num_topics=180,       # 通过交叉验证选定
    passes=10,            # 遍历语料次数
    chunksize=10000,      # 每批文档数
    random_state=153090   # 固定随机种子
)
# 交叉验证选择主题数(K=100到260,步长10,10折CV)
# 在gensim中实现:
from gensim.models import CoherenceModel

k_values = range(100, 270, 10)
coherence_scores = []
for k in k_values:
    model = LdaModel(corpus=corpus, id2word=dictionary,
                     num_topics=k, passes=5, random_state=42)
    cm = CoherenceModel(model=model, texts=tokenized_docs,
                        dictionary=dictionary, coherence='c_v')
    coherence_scores.append(cm.get_coherence())
# 最优K=180(对应最高一致性得分)

180个主题的解读示例

主题编号 Top-5关键词 主题标签
Topic 23 recession, unemployment, jobless, layoff, downturn "经济衰退"
Topic 45 profit, revenue, earnings, quarterly, growth "企业盈利"
Topic 67 fed, rate, interest, monetary, inflation "货币政策"
Topic 89 oil, energy, crude, opec, production "能源市场"
Topic 112 china, trade, tariff, import, export "中美贸易"

主题注意力时间序列

每月计算各主题的文章比例 → 主题注意力时间序列

"衰退"主题注意力:
  2007年6月: 2.1%  ← 正常水平
  2008年9月: 8.7%  ← 雷曼倒闭后激增
  2009年3月: 12.3% ← 峰值
  2010年6月: 3.2%  ← 回落

4.3 情感分析

Sentiment Analysis

  • 金融情感分析的意义
  • 基于词典的方法
  • 基于ML的方法
  • LLM情感分析
  • 案例分析

金融情感分析的意义

核心假说:市场情绪影响资产价格

情感数据源

财经新闻 → 新闻情感 → 事件驱动策略
社交媒体 → 舆情情感 → 情绪指标
财报电话会 → 管理层情感 → 业绩预测
央行声明 → 政策情感 → 宏观预测

学术证据

  • Tetlock (2007): 华尔街日报专栏情感预测道琼斯指数
  • Baker & Wurgler (2006): 投资者情绪指数
  • 近期研究表明LLM情感分析对收益预测有显著增量信息

实践应用

  • 构建情感因子加入多因子模型
  • 新闻事件驱动的短线交易
  • 风险预警(舆情恶化提前减仓)

基于词典的情感分析

金融情感词典

  • Henry词典:金融领域情感词典,含正面/负面词库
  • Loughran-McDonald词典:基于10-K报告,包含金融特有词汇
    • 正面:profit, growth, record, strong
    • 负面:loss, default, decline, impairment
    • 不确定性:uncertain, risk, volatility
    • 法律词汇:litigation, regulatory
    • 强语气:must, will, worst
# 简单情感得分
def sentiment_score(text, positive_words, negative_words):
    words = jieba.lcut(text)
    pos_count = sum(1 for w in words if w in positive_words)
    neg_count = sum(1 for w in words if w in negative_words)
    return (pos_count - neg_count) / (pos_count + neg_count + 1)

LLM情感分析

零样本情感分析

分析以下金融新闻的情感倾向(positive/negative/neutral):
"宁德时代2024年营收同比增长45%,净利润突破600亿元,
市场份额持续扩大。"

情感:

结构化输出

请对以下新闻进行多维情感分析,输出JSON格式:

新闻:[文本]

{
  "overall_sentiment": "positive/negative/neutral",
  "confidence": 0.95,
  "dimensions": {
    "业绩": "positive",
    "前景": "neutral",
    "风险": "negative"
  },
  "key_entities": [
    {"name": "...", "sentiment": "...", "reason": "..."}
  ]
}

优势:理解上下文、讽刺、隐含情绪(传统词典方法在这些场景表现差)

案例:财报电话会议情感分析

# 电话会议文本 -> 管理层情感信号
# 使用FinBERT进行情感分析

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
tokenizer = AutoTokenizer.from_pretrained("ProsusAI/finbert")
model = AutoModelForSequenceClassification.from_pretrained(
    "ProsusAI/finbert"
)
text = """
We are pleased to report strong revenue growth this quarter,
driven by our cloud computing segment. However, we remain
cautious about the macroeconomic environment.
"""
inputs = tokenizer(text, return_tensors="pt")
outputs = model(**inputs)
scores = torch.nn.functional.softmax(outputs.logits, dim=-1)
# FinBERT输出: positive, negative, neutral
print(f"Positive: {scores[0][0]:.2f}")
print(f"Negative: {scores[0][1]:.2f}")
print(f"Neutral: {scores[0][2]:.2f}")

# → 观察到管理层"积极但谨慎"的语调

交易信号

  • 管理层积极语调 → 超预期业绩信号 → 买入
  • 语调突然转变 → 预警信号
  • 同行对比 → 相对优势判断

4.4 主题建模

Topic Modeling

  • LDA主题模型
  • BERTopic
  • 金融应用场景
  • 热点发现

LDA (Latent Dirichlet Allocation)

生成式概率模型:每篇文档是多个主题的混合

直觉

  • 文档 = 主题的概率分布
  • 主题 = 词语的概率分布
from gensim import corpora, models

# 文本预处理后
dictionary = corpora.Dictionary(tokenized_docs)
corpus = [dictionary.doc2bow(doc) for doc in tokenized_docs]

lda = models.LdaModel(
    corpus=corpus,
    id2word=dictionary,
    num_topics=10,     # 主题数需要预设
    passes=20
)

# 输出每个主题的关键词
for topic_id, words in lda.print_topics(num_words=10):
    print(f"主题 {topic_id}: {words}")
    # 主题 0: "利率 央行 通胀 货币 政策 加息 降息 ..."
    # 主题 1: "新能源 电池 电动车 光伏 储能 ..."

局限性:需要预设主题数、词袋假设忽略词序和语义

BERTopic

现代化主题模型:结合Embedding + 聚类

graph LR Docs["文档"] --> Embed["SBERT
句子嵌入"] Embed --> Reduce["UMAP
降维"] Reduce --> Cluster["HDBSCAN
聚类"] Cluster --> Topic["主题表示
c-TF-IDF"] Topic --> Final["层次化主题"]

优势

  • 无需预设主题数(HDBSCAN自动确定)
  • 保留语义信息(SBERT嵌入)
  • 支持层次化主题
  • 支持动态主题演化

金融应用

  • 发现市场热点主题的演化
  • 从大量新闻中识别新兴风险
  • 行业主题分类

金融主题发现案例

应用:从新闻中识别市场热点变化

输入:每天10万条财经新闻
输出:主题强度时序图

主题1: "AI与金融科技"   → 强度从2023年快速上升
主题2: "碳中和与绿色金融" → 政策驱动型波动
主题3: "地缘政治风险"   → 事件驱动型脉冲
主题4: "利率与货币政策" → 周期性波动

交易信号

  • 主题强度变化可作为宏观对冲信号
  • 新兴主题早期识别 → 提前布局相关资产
  • 主题热度见顶 → 警惕反转

4.5 图像分析

Image Analytics

  • 金融图表识别
  • CNN迁移学习
  • 卫星图像应用
  • 多模态融合

金融图像数据分类体系

类别 具体类型 数据来源 分析技术 金融应用
图表图像 K线图、成交量图、技术指标图 行情软件截图 CNN+模式识别 技术形态自动识别
文档图像 财报PDF、合同扫描件、发票 公司公告/交易所 OCR+NLP 财务数据自动提取
交易界面 订单簿截图、交易终端 交易系统 模板匹配+CV 交易行为分析
卫星图像 停车场、农田、港口、油罐 商业卫星公司 CNN+语义分割 另类数据alpha
消费图像 门店排队、商品货架 众包数据采集 目标检测(YOLO) 消费趋势预测

图像+文本多模态Pipeline

财报PDF (图像+文本混合)
  ├─ OCR引擎 → 文字提取 → FinBERT语义分析
  ├─ 表格检测 → 结构化数据 → 财务建模
  └─ 图表识别 → K线/柱状图 → 技术信号
       ↓
  多模态特征融合 → 综合投资信号

迁移学习:用预训练CNN进行金融图像分类

场景1:图表识别

  • K线图模式识别(头肩顶、双底等)
  • 技术分析自动化
  • 财报表格提取

场景2:文档处理

  • OCR + NLP 提取PDF中的金融数据
  • 合同条款自动识别
  • 发票/单据处理

场景3:卫星图像

  • 停车场车辆数 → 零售店客流
  • 农田卫星图 → 农产品产量预估
  • 港口集装箱数量 → 贸易活跃度
  • 油罐储量 → 原油库存
# 迁移学习:用预训练CNN进行金融图像分类
from torchvision import models

resnet = models.resnet50(pretrained=True)
# 替换最后一层适配金融任务
num_ftrs = resnet.fc.in_features
resnet.fc = nn.Linear(num_ftrs, n_classes)

另类数据:卫星图像的金融价值

数据源 可观测指标 预测目标 准确性
停车场卫星图 车辆数变化 零售门店营收 80-90%
农田多光谱图 植被指数(NDVI) 农产品期货价格 70-85%
港口卫星图 集装箱/船舶数 贸易量/航运价格 75-90%
油罐阴影测量 原油储量变化 原油期货 80-85%
工地进度 建筑阶段 地产公司收入确认 70-80%

案例:某对冲基金通过分析沃尔玛停车场卫星图像,在季度财报发布前成功预测同店销售数据,获得超额收益。

论文案例:CNN价格趋势识别

Jiang, Kelly & Xiu (2023) "(Re-)Imag(in)ing Price Trends" Journal of Finance

核心创新:将股票价格历史渲染为图像,用CNN自动发现预测模式

传统方法: 手工定义技术指标(MA交叉、RSI、MACD...)
论文方法: 让CNN"看"价格图表,自动学习什么模式能预测未来收益

图像生成流程

CRSP日频数据 (OHLC + Volume)
    ↓
价格调整(去分红、拆股影响)
    ↓
渲染为灰度图像(PIL Image mode="L")
    ↓
三种样式: bar / pixel / centered_pixel
    ↓
图像尺寸: I5=15×32, I20=60×64, I60=180×96
    ↓
叠加移动均线 + 成交量柱
# 图像渲染示例(参考 trend_code_submit/Data/chart_library.py)
from PIL import Image, ImageDraw

def render_ohlc_image(ohlc_data, width=64, height=60):
    """将OHLC数据渲染为灰度图像"""
    img = Image.new('L', (width, height), color=255)  # 白色背景
    draw = ImageDraw.Draw(img)

    bar_width = width // len(ohlc_data)
    for i, (o, h, l, c) in enumerate(ohlc_data):
        x = i * bar_width
        # 绘制K线(高-低线 + 实体)
        draw.line([(x+1, h), (x+1, l)], fill=0, width=1)
        if c >= o:  # 阳线
            draw.rectangle([(x, c), (x+bar_width-1, o)], fill=0)
        else:       # 阴线
            draw.rectangle([(x, o), (x+bar_width-1, c)], fill=128)

    return img

CNN2D与CNN1D架构详解

CNN2D(图像输入)

# 参考 trend_code_submit/Model/cnn_model.py
import torch.nn as nn

class CNNModel(nn.Module):
    def __init__(self, layers=3):
        super().__init__()
        # 输入: 1通道灰度图像
        self.conv1 = nn.Conv2d(1, 64, kernel_size=(5,3), padding=(2,1))
        self.bn1 = nn.BatchNorm2d(64)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=(5,3), padding=(2,1))
        self.bn2 = nn.BatchNorm2d(128)
        self.conv3 = nn.Conv2d(128, 256, kernel_size=(5,3), padding=(2,1))
        self.bn3 = nn.BatchNorm2d(256)
        self.pool = nn.MaxPool2d((2,1))
        self.dropout = nn.Dropout(0.5)
        self.fc = nn.Linear(256 * (height//8) * (width//4), 2)

    def forward(self, x):
        x = self.pool(F.leaky_relu(self.bn1(self.conv1(x))))
        x = self.pool(F.leaky_relu(self.bn2(self.conv2(x))))
        x = self.pool(F.leaky_relu(self.bn3(self.conv3(x))))
        x = x.view(x.size(0), -1)
        x = self.dropout(x)
        return self.fc(x)  # 2类: 上涨/下跌

CNN1D(时间序列输入)

输入: 6通道 × 窗口长度
  通道1: Open(开盘价)
  通道2: High(最高价)
  通道3: Low(最低价)
  通道4: Close(收盘价)
  通道5: Moving Average(移动均线)
  通道6: Volume(成交量)

架构: Conv1D → BatchNorm → LeakyReLU → MaxPool → ... → FC

模型配置对比

窗口 预测 CNN2D层数 CNN1D层数 图像尺寸
I5 (5天) R5 (周) 2层 1层 15×32
I20 (20天) R20 (月) 3层 2层 60×64
I60 (60天) R60 (季) 4层 3层 180×96

训练配置

  • 集成5个独立模型,取平均预测
  • Adam优化器,学习率1e-5
  • 早停:验证集损失连续2轮未改善则停止
  • 最大50轮训练,batch size 128

CNN投资组合分析与迁移学习

投资组合构建

CNN预测概率排序 → 分10个十分位
  ┌─────────────────────────────────────┐
  │ Decile 1 (最低) → Decile 10 (最高)   │
  │ 预测下跌概率高        预测上涨概率高   │
  └─────────────────────────────────────┘

High-Low多空组合: 做多D10 + 做空D1
→ 年化Sharpe比率约1.5-2.5(等权)

迁移学习实验

实验 训练数据 测试数据 方法
美国基准 US 1993-2000 US 2001-2019 直接训练
国际直接迁移 US 1993-2000 25国 2001-2019 冻结所有层
国际微调 US预训练 25国 冻结底层,训练FC层
时间尺度迁移 I5/R5 I20/R20, I60/R60 冻结底层,训练FC层

关键发现

  • CNN预测能力在25个国际市场普遍存在
  • 美国训练的模型可直接迁移到其他国家(Sharpe > 1)
  • 微调进一步提升国际迁移效果
  • CNN优于7846个传统技术指标中的绝大多数

4.6 实践环节

情感分析+主题建模

  • 数据获取
  • 情感分析实现
  • 主题建模
  • 结果可视化
  • 实践要求

实践项目A:基于新闻文本的经济周期度量

论文:Bybee, Kelly, Manela & Xiu (2024) "Business News and Business Cycles"

目标:从新闻文本中提取经济状态的量化度量,构建主题注意力指标

复现手册:详见 w04_resource/W04_Replication_Manual_Paper1.md

核心步骤(简化版,约1.5小时):

# ========== Step 1: 数据准备 ==========
import pandas as pd
import numpy as np
from gensim.corpora import Dictionary
from gensim.models import LdaModel

# 加载预处理好的文本数据(或使用模拟数据)
# documents = load_wsj_documents()  # 从text_database/加载
# ========== Step 2: 构建DTM ==========
# 分词 + 去停用词
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords

stop_words = set(stopwords.words('english'))
tokenized_docs = [
    [w for w in word_tokenize(doc.lower()) if w not in stop_words and len(w) > 2]
    for doc in documents
]
# 构建词典和语料
dictionary = Dictionary(tokenized_docs)
dictionary.filter_extremes(no_below=5, no_above=0.5)
corpus = [dictionary.doc2bow(doc) for doc in tokenized_docs]
# ========== Step 3: 训练LDA ==========
lda_model = LdaModel(
    corpus=corpus,
    id2word=dictionary,
    num_topics=180,
    passes=10,
    random_state=42
)
# 查看主题
for idx, topic in lda_model.print_topics(-1, topn=5):
    print(f"Topic {idx}: {topic}")
# ========== Step 4: 计算主题注意力 ==========
# 获取每篇文档的主题分布
doc_topics = [lda_model.get_document_topics(doc) for doc in corpus]

# 按月聚合主题注意力(简化版)
# topic_attention_df: 行=月份, 列=180个主题, 值=该月文章中该主题的平均权重
# ========== Step 5: Lasso匹配宏观变量 ==========
from sklearn.linear_model import LassoCV

# 假设已有宏观变量数据
# macro_df: 包含GDP增长、就业、股市回报等
lasso = LassoCV(cv=5)
lasso.fit(topic_attention_df, macro_df['gdp_growth'])
selected = np.where(lasso.coef_ != 0)[0]
print(f"预测GDP的显著主题: {selected}")

思考题

  1. LDA与BERTopic在此场景各有何优劣?为何论文选择LDA?
  2. Online LDA为何能避免前视偏差?如果用标准LDA会有什么问题?
  3. 180个主题中,哪些主题对GDP预测最显著?这与你的经济直觉一致吗?

实践项目B:基于CNN的股票价格趋势预测

论文:Jiang, Kelly & Xiu (2023) "(Re-)Imag(in)ing Price Trends"

目标:用CNN从股票价格图表图像中预测未来收益方向

复现手册:详见 w04_resource/W04_Replication_Manual_Paper2.md

核心步骤(简化版,约1.5小时):

# ========== Step 1: 加载数据 ==========
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from PIL import Image

# 使用预计算的投资组合收益数据(CACHE_DIR中)
# 或从CRSP数据生成图像(需要原始数据)
portfolio_returns = pd.read_csv(
    "CACHE_DIR/cnn1d_and_linear_model_portfolio_returns/cnn2d_i20_r5_ew.csv"
)
# ========== Step 2: 图像生成(简化版) ==========
def generate_chart_image(ohlc_data, window=20, img_width=64, img_height=60):
    """将OHLC数据渲染为灰度图像"""
    img = Image.new('L', (img_width, img_height), color=255)
    pixels = img.load()

    bar_width = img_width // window
    for i in range(min(window, len(ohlc_data))):
        o, h, l, c = ohlc_data[i]
        x = i * bar_width
        # 归一化价格到图像高度
        for y in range(img_height):
            price_level = (y / img_height) * (h - l) + l
            if l <= price_level <= h:
                for dx in range(bar_width):
                    if x + dx < img_width:
                        pixels[x + dx, y] = 0  # 黑色

    return np.array(img)
# ========== Step 3: CNN模型(简化版) ==========
class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=(5,3), padding=(2,1))
        self.bn1 = nn.BatchNorm2d(32)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=(5,3), padding=(2,1))
        self.bn2 = nn.BatchNorm2d(64)
        self.pool = nn.MaxPool2d((2,1))
        self.dropout = nn.Dropout(0.5)
        self.fc = nn.Linear(64 * 15 * 16, 2)  # 2类: 上涨/下跌

    def forward(self, x):
        x = self.pool(torch.relu(self.bn1(self.conv1(x))))
        x = self.pool(torch.relu(self.bn2(self.conv2(x))))
        x = x.view(x.size(0), -1)
        x = self.dropout(x)
        return self.fc(x)
# ========== Step 4: 投资组合分析 ==========
# 使用论文预计算的结果
ew_returns = pd.read_csv("CACHE_DIR/.../cnn2d_i20_r5_ew.csv")
vw_returns = pd.read_csv("CACHE_DIR/.../cnn2d_i20_r5_vw.csv")

# 计算十分位收益
decile_returns = ew_returns.mean(axis=0)
sharpe = decile_returns.mean() / decile_returns.std() * np.sqrt(252)
print(f"H-L组合年化Sharpe: {sharpe:.2f}")

# 可视化累计收益
cumulative = (1 + ew_returns['decile_10'] - ew_returns['decile_0']).cumprod()
cumulative.plot(title='CNN多空组合累计收益')

思考题

  1. CNN2D(图像输入)与CNN1D(时间序列输入)的预测能力有何差异?为什么?
  2. CNN的预测能力与7846个传统技术指标相比如何?这说明了什么?
  3. 迁移学习实验表明CNN模式具有跨市场普遍性,这对行为金融学有什么启示?

实践报告要求

提交内容(二选一完成):

1. Jupyter Notebook
   - 完整的数据处理和模型实现流程
   - 关键结果的可视化(主题时序图/CNN预测分布图)
   - 复现的核心表格或图表

2. 分析报告(1-2页):
   - 论文核心方法的简要介绍
   - 你的复现结果与论文报告结果的对比
   - 复现过程中遇到的问题和解决方案
   - 对论文方法的评价和改进建议

3. 思考题回答(选2-3题)

大数据金融分析实战要点

数据质量框架

维度 定义 检验方法 金融影响
准确性 值与真实一致 交叉验证多数据源 错误数据→错误信号
完整性 无缺失必要字段 缺失率统计 缺失非随机→偏差
及时性 数据可获取的时间 Point-in-Time标记 前视偏差风险
一致性 跨源数据可对齐 MD5/数据指纹 不同源矛盾→无法决策

数据血缘 (Data Lineage) 在金融中的重要性

为什么监管要求数据血缘?
  "这个因子值=0.85是哪来的?"
  → 原始数据(Wind API v3.2) → ETL清洗(v2) → 
    行业中性化 → Z-score → 因子表
  → 每一步都需文档化+可追溯
  → Basel III / SR11-7 / 中国银保监模型风险管理指引均要求

异常检测自动化

# 金融数据异常检测
def detect_anomalies(factor_df, method='iqr'):
    anomalies = {}
    for col in factor_df.columns:
        if method == 'iqr':
            Q1, Q3 = factor_df[col].quantile([0.25, 0.75])
            IQR = Q3 - Q1
            mask = (factor_df[col] < Q1-3*IQR) | (factor_df[col] > Q3+3*IQR)
        elif method == 'mad':
            mad = median_abs_deviation(factor_df[col], nan_policy='omit')
            mask = abs(factor_df[col] - factor_df[col].median()) > 5*mad
        anomalies[col] = factor_df.loc[mask, col]
    return anomalies

BERTopic vs LDA 深度对比

维度 LDA BERTopic
文档表示 词袋 (Bag-of-Words) 预训练Transformer嵌入
语义理解 ❌ 忽略词序和上下文 ✅ 深层语义
主题数量 需预指定 自动确定
主题质量 混合词可能不连贯 语义连贯性高
速度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
适用场景 长文档、大规模 短文本、语义重要
金融适配 研报分类(词分布明确) 新闻/推文(语义依赖)

选择建议

  • 快速探索大量文档的词频模式 → LDA
  • 需要高质量、语义连贯的主题 → BERTopic
  • 短文本(微博/推文/标题) → BERTopic (LDA在短文本上不擅长)
  • 正式研报/财报 → 两者皆可,LDA更经济

论文案例:Online LDA避免前视偏差

Bybee, Kelly, Manela & Xiu (2024) — 股票市场预测应用

问题:标准LDA使用全量数据训练,预测时会"看到"未来信息

标准LDA的前视偏差:
  训练数据: 1984-2017年全部文章
  预测目标: 2010年6月的市场回报
  问题: LDA主题估计已包含2010年之后的信息!

Online LDA解决方案(Hoffman et al., 2010):

# Online LDA:逐月更新,只用历史数据
# 参考 code/prep_oLDA/fit_gensim_oLDA_fout_art_fseed_mstore/

from gensim.models import LdaModel

# 初始化:用burn-in期数据训练基础模型
lda = LdaModel(corpus=burnin_corpus, id2word=dictionary,
               num_topics=180, random_state=153090)

# 逐月更新(关键:每次只用当月数据)
for month in months:
    monthly_docs = get_docs_for_month(month)  # 只取当月文章
    lda.update(monthly_docs)  # 在线更新主题参数
    # 此时模型只"看到"month之前的数据 → 无前视偏差

Online LDA vs 标准LDA对比

特性 标准LDA Online LDA
训练数据 全量语料 逐月累积
前视偏差 ⚠️ 存在 ✅
适用场景 描述性分析 预测性分析
计算效率 低(全量重训) 高(增量更新)
主题稳定性 逐渐收敛

论文案例:主题注意力与经济活动匹配

研究设计:哪些新闻主题能预测宏观经济?

Step 1:构建面板数据

Y (宏观变量): 产出、就业、股市回报、IPO数量、LBO数量等
X (主题注意力): 180个主题的月度文章比例
面板: 月度 × (宏观变量 + 180个主题)

Step 2:Lasso回归选择显著主题

# 使用glmnet进行Lasso回归(论文通过rpy2调用R)
# 参考 code/fit_lasso/fit_glmnet_pval/

from sklearn.linear_model import LassoCV

# 对每个宏观变量分别做Lasso
lasso = LassoCV(cv=10, random_state=42)
lasso.fit(topic_attention, macro_variable)

# 选出非零系数的主题 → 该宏观变量的"信号主题"
selected_topics = [i for i, c in enumerate(lasso.coef_) if c != 0]

关键发现

宏观变量 最强预测主题 新闻解释方差
产出(GDP) 衰退关注(Recession) 25%
就业 劳动力市场 18%
股市回报 综合新闻情绪 25%
IPO数量 新股发行/科技 32%
LBO数量 杠杆收购/私募 58%

Step 3:Group-Lasso VAR

# 标准VAR + 180个主题作为外生变量
# Group-Lasso对主题变量施加群组惩罚
# 参考 code/fit_VAR/fit_glVAR_CV_npen/

# 基准VAR(4个内生变量,3阶滞后)
# y_t = A1*y_{t-1} + A2*y_{t-2} + A3*y_{t-3} + B*x_t + e_t
# x_t = 180个主题注意力(Group-Lasso选择)

# 脉冲响应函数(IRF)
# "衰退关注"↑1标准差 → GDP、就业的动态响应
# 发现:衰退关注冲击对GDP有显著负向持续影响(持续约18个月)

论文案例:叙事检索——从冲击到新闻标题

核心思想:将VAR脉冲响应"翻译"为具体的新闻标题

传统IRF: "衰退关注冲击1个标准差 → GDP下降0.3%"
叙事检索: "2008年9月的哪些新闻触发了衰退关注冲击?"

步骤:
1. 识别衰退关注的正向冲击事件(z-score > 2)
2. 回溯该时刻的具体新闻文章
3. 按主题权重排序,找出贡献最大的文章
4. 输出:具体的新闻标题 + 发布日期

示例输出:
  2008-09-15: "Lehman Files for Bankruptcy" (衰退主题权重: 0.87)
  2008-09-16: "Global Markets Plunge" (衰退主题权重: 0.82)
  2008-09-17: "AIG Seeks Government Rescue" (衰退主题权重: 0.79)

优势:无需施加识别约束(如Cholesky分解),直接从数据中提取经济叙事

本周总结

核心收获

  1. 理解金融大数据分析的全流程
  2. 掌握中文分词、TF-IDF、Word2Vec等NLP技术
  3. 理解情感分析的多种方法及其金融应用
  4. 掌握主题建模在热点发现中的应用
  5. 了解图像分析和另类数据的价值
  6. 理解LLM如何作为NLP的统一接口
  7. 掌握LDA主题模型的金融应用(论文1案例)
  8. 理解CNN在金融图像分析中的应用(论文2案例)

论文案例收获

  • 论文1:LDA可从新闻文本提取经济周期信号,"衰退关注"是GDP最强预测因子
  • 论文2:CNN可从价格图表自动发现预测模式,优于7846个传统技术指标
  • 共同启示:深度学习在金融文本和图像分析中具有强大潜力

预习准备

  • 复习Backtrader回测框架
  • 阅读EMH和行为金融学概念
  • 安装:backtrader, yfinance

下周预告
Week 5: AI时代量化交易

  • Backtrader回测框架
  • TradingAgents多Agent系统
  • ATLAS自适应交易
  • QuantAgent高频信号
  • HFT理论与模型
  • LLM+MCTS因子挖掘

延伸阅读

教材

  • 李航 "统计学习方法"
  • Jurafsky & Martin "Speech and Language Processing"

论文

  • Tetlock (2007): "Giving Content to Investor Sentiment"
  • Loughran & McDonald (2011): "When Is a Liability Not a Liability?"
  • Devlin et al. (2018): "BERT: Pre-training of Deep Bidirectional Transformers"
  • Bybee, Kelly, Manela & Xiu (2024): "Business News and Business Cycles." Journal of Finance, 79(5): 3105-3147
  • Jiang, Kelly & Xiu (2023): "(Re-)Imag(in)ing Price Trends." Journal of Finance, 78(6): 3193-3249

开源金融数据资源

资源 类型 说明
TuShare / AkShare 行情/财务数据 A股全量数据 (免费)
yfinance 全球行情 美股/港股/外汇 (免费)
Wind / Choice 综合金融终端 机构级数据 (付费)
CSMAR / CNRDS 学术数据库 高校常用 (高校采购)
巨潮资讯 (cninfo) 公告/财报 上市公司公告原文
Kaggle金融数据集 竞赛/分析 各类金融NLP/预测任务
SEC EDGAR SEC文件 美股财报全文
新浪财经/东方财富 实时行情 网页抓取 (注意合规)
Quandl / Nasdaq Data 另类数据 宏观经济、替代数据
JHU COVID-19 Data 宏观经济 疫情对经济影响研究

资源

- Grootendorst (2022): "BERTopic: Neural Topic Modeling"