AI应用 量化交易NLP情感分析择时市场情绪
NLP情感分析与量化择时策略
道
量子道
量子道 · 扫码阅读
微信 · 浏览器扫码
在手机上获得更好的阅读体验
NLP情感分析与量化择时策略
市场由人组成,人由情绪驱动。如果能量化市场情绪,你就能在拥挤的羊群中提前看到转折。
情绪驱动市场的证据
行为金融学的洞见
有效市场假说 行为金融学
价格反映所有信息 价格反映信息 + 情绪
↓ ↓
随机游走 可预测的偏差
- 过度反应 → 恐慌性抛售 → 超卖机会
- 反应不足 → 利好消息缓慢消化 → 趋势延续
- 羊群效应 → 泡沫和崩盘的自增强循环
NLP 让我们有机会量化这些情绪。
NLP情绪分析技术栈
技术路线对比
| 方法 | 复杂度 | 准确率 | 可解释性 | 适用场景 |
|---|---|---|---|---|
| 词典法 | 低 | 60-70% | 高 | 快速原型 |
| 传统ML | 中 | 70-80% | 中 | 中等数据量 |
| FinBERT | 中 | 80-88% | 中 | 金融文本 |
| GPT/Claude | 高 | 85-92% | 低 | 复杂语义 |
| 微调LLM | 高 | 88-95% | 低 | 领域定制 |
数据源分类
结构化数据源:
├── 财经新闻(Bloomberg, Reuters)
├── 公司公告和财报
├── 分析师研报
└── 央行政策声明
非结构化数据源:
├── 社交媒体(Twitter/X, Reddit, 雪球)
├── 论坛讨论(WallStreetBets, 股吧)
├── 搜索引擎趋势
└── 视频/播客转文字
实战一:基于 FinBERT 的情绪因子
FinBERT 快速上手
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import pandas as pd
import numpy as np
# 加载 FinBERT(在金融文本上微调的BERT)
model_name = ProsusAI/finbert
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)
def sentiment_analysis(texts, batch_size=32):
批量情感分析
sentiments = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i+batch_size]
inputs = tokenizer(
batch, padding=True, truncation=True,
max_length=512, return_tensors=pt
)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1)
# FinBERT 标签: 0=负面, 1=中性, 2=正面
sentiments.append(probs.numpy())
return np.concatenate(sentiments, axis=0)
# 示例:分析一组新闻标题
headlines = [
Apple reports record quarterly earnings, beating analyst expectations,
Market tumbles amid rising inflation fears and Fed uncertainty,
Tesla faces regulatory investigation over autopilot safety concerns
]
probs = sentiment_analysis(headlines)
for h, p in zip(headlines, probs):
sentiment = [负面, 中性, 正面][np.argmax(p)]
print(f[{sentiment}] {h})
print(f 负面:{p[0]:.1%} 中性:{p[1]:.1%} 正面:{p[2]:.1%}\n)
构建日频情绪因子
def build_daily_sentiment_factor(news_df, date_col=date,
headline_col=headline):
从新闻数据构建每日情绪因子
# 对每天的所有新闻计算情绪
daily_sentiments = []
for date, group in news_df.groupby(date_col):
texts = group[headline_col].tolist()
probs = sentiment_analysis(texts)
daily_sentiments.append({
date: date,
news_count: len(texts),
sentiment_positive: probs[:, 2].mean(), # 正面均值
sentiment_negative: probs[:, 0].mean(), # 负面均值
sentiment_score: probs[:, 2].mean() - probs[:, 0].mean(), # 净情绪
sentiment_std: probs[:, 2].std(), # 情绪分歧度
max_positive: probs[:, 2].max(),
max_negative: probs[:, 0].max(),
})
sentiment_df = pd.DataFrame(daily_sentiments)
sentiment_df.set_index(date, inplace=True)
# 衍生因子
sentiment_df[sentiment_ma5] = sentiment_df[sentiment_score].rolling(5).mean()
sentiment_df[sentiment_change] = sentiment_df[sentiment_score].diff()
sentiment_df[sentiment_zscore] = (
(sentiment_df[sentiment_score] - sentiment_df[sentiment_score].rolling(20).mean())
/ sentiment_df[sentiment_score].rolling(20).std()
)
return sentiment_df
实战二:用 GPT/Claude 做深度语义分析
Few-Shot 文本分类
import openai
SYSTEM_PROMPT = 你是一个专业的金融情绪分析系统。
分析给定的财经文本,输出JSON格式:
{
sentiment: positive/negative/neutral,
confidence: 0.0-1.0,
market_impact: high/medium/low,
key_factors: [因素1, 因素2],
sentiment_score: -1.0到1.0之间的数值
}
评分标准:
- 1.0: 强烈利好(超预期盈利、重大政策利好)
- 0.5: 温和利好
- 0.0: 中性或信息混杂
- -0.5: 温和利空
- -1.0: 强烈利空(暴雷、监管处罚)
def deep_sentiment_analysis(text, model=gpt-4):
用GPT进行深度情绪分析
response = openai.ChatCompletion.create(
model=model,
messages=[
{role: system, content: SYSTEM_PROMPT},
{role: user, content: text}
],
temperature=0.1, # 低温度保证一致性
response_format={type: json_object}
)
return json.loads(response.choices[0].message.content)
财报电话会议分析
财报电话会议的语气和措辞往往比财报数字本身更能预测未来:
def analyze_earnings_call(transcript):
分析财报电话会议的文本
prompt = f分析以下财报电话会议记录,重点关注:
1. 管理层语气变化(与过往对比)
2. 回避问题的次数和话题
3. 不确定性的语言指标(可能、取决于、具有挑战性等词频)
4. 对未来的具体承诺 vs 模糊表述
电话会议记录:
{transcript}
输出JSON:
{{
tone_score: -1到1(-1=非常消极,1=非常积极),
evasiveness_score: 0到1(越高=越回避问题),
uncertainty_index: 不确定性词汇密度,
concrete_vs_vague_ratio: 具体承诺/模糊表述的比值,
red_flags: [红旗信号列表],
summary: 两句话总结
}}
return call_llm(prompt)
实战三:Reddit/社交媒体情绪
WallStreetBets 情绪数据
import requests
from datetime import datetime, timedelta
def fetch_reddit_sentiment(subreddit, ticker, days=7):
获取Reddit上某股票的情绪
# 使用Pushshift API获取帖子
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=days)
url = fhttps://api.pushshift.io/reddit/search/submission/
params = {
subreddit: subreddit,
q: ticker,
after: int(start_date.timestamp()),
before: int(end_date.timestamp()),
size: 500,
sort: score
}
response = requests.get(url, params=params)
posts = response.json()[data]
# 提取文本和分数
texts = [p.get(title, ) + + p.get(selftext, ) for p in posts]
scores = [p.get(score, 0) for p in posts]
comments = [p.get(num_comments, 0) for p in posts]
# NLP分析
sentiments = sentiment_analysis(texts)
return pd.DataFrame({
text: texts,
score: scores,
comments: comments,
sentiment_positive: sentiments[:, 2],
sentiment_negative: sentiments[:, 0],
})
散户情绪指标
def build_retail_sentiment_index(ticker):
构建散户情绪综合指标
data = fetch_reddit_sentiment(wallstreetbets, ticker)
# 加权情绪(高分帖子权重大)
weighted_sentiment = np.average(
data[sentiment_positive] - data[sentiment_negative],
weights=np.log1p(data[score]) * np.sqrt(data[comments] + 1)
)
# 情绪热度(讨论量)
buzz_score = np.log1p(len(data))
# 情绪一致性(分歧度)
sentiment_dispersion = data[[sentiment_positive, sentiment_negative]].std().mean()
return {
sentiment: weighted_sentiment,
buzz: buzz_score,
dispersion: sentiment_dispersion,
composite: weighted_sentiment * buzz_score / (1 + sentiment_dispersion)
}
情绪因子与交易信号
择时策略框架
def generate_sentiment_signals(sentiment_df):
从情绪因子生成交易信号
signals = pd.DataFrame(index=sentiment_df.index)
# 极端情绪反转信号
# 当情绪过于乐观(>2σ),可能见顶
# 当情绪过于悲观(<-2σ),可能见底
signals[extreme_bearish] = sentiment_df[sentiment_zscore] < -2.0 # 买入机会
signals[extreme_bullish] = sentiment_df[sentiment_zscore] > 2.0 # 卖出警告
# 情绪趋势变化
# 从悲观转为中性/乐观 = 买入信号
signals[sentiment_turnaround] = (
(sentiment_df[sentiment_zscore] > -0.5) &
(sentiment_df[sentiment_zscore].shift(1) < -1.0)
)
# 情绪与价格背离(最强信号之一)
# 价格下跌 + 情绪改善 = 潜在底部
signals[bullish_divergence] = (
(sentiment_df[sentiment_change] > 0) &
(sentiment_df[price_change] < 0)
)
# 情绪一致性过滤
# 情绪分歧过大时,信号可信度降低
signals[high_confidence] = sentiment_df[sentiment_std] < 0.15
# 最终信号
signals[buy_signal] = (
(signals[extreme_bearish] | signals[sentiment_turnaround]) &
signals[high_confidence]
)
signals[sell_signal] = (
signals[extreme_bullish] & signals[high_confidence]
)
return signals
实战建议
关键要点
- 多源融合 — 单一来源的情绪信号噪音太大,多源交叉验证
- 语境重要 — 利润下降在预期下降20%但实际降10%时是利好
- 时间衰减 — 新闻情绪的影响随时间快速衰减(通常1-5天)
- 极端值最有信息量 — 正常情绪波动预测力弱,极端情绪预测力强
- 与价格结合 — 情绪+量价分析 > 单独情绪 > 单独量价
常见陷阱
- 所有新闻都是中性偏正面 → 需要校准
- 机器难以理解讽刺和反讽 → Reddit情绪需要特殊处理
- 重大事件日新闻爆发 → 需要按事件去重而非按篇数
- 回译偏差 → 中文文本用中文模型(如FinBERT-Chinese)
市场情绪是可以量化的。NLP 给了我们一把读取市场心情的钥匙。但记住:当所有人都在用同一把钥匙时,锁就会换掉。不断寻找新的情绪数据源,是在这个领域保持优势的关键。