系列进度:AI+量化实战 #05 / 共 12 篇
痛点开场
财报季一到,交易所的公告像洪水一样涌来。你打开股票软件,弹窗里是"预增""预降""扭亏""减亏",眼睛都看花了,却不知道哪条真值得跟进。
更头疼的是:
- 预告类型本身不能直接当信号------市场对同样的"预增"反应可能完全不同;
- 想算"公告后 1 天/5 天涨了多少",要一只一只股票扒日线;
- 样本一多,手动归类和计算几乎不可能。
今天这篇文章,就把这件事自动化:先用 Python 把业绩预告分类,再批量计算事件窗口收益。LLM 负责理解文本里的"弦外之音",规则负责兜底和规模化。
本文你将得到什么
company_earnings_forecast和stock_history两个接口的配合用法。- 一套把预告类型映射为
positive / neutral / negative的规则分类器。 - 一个 LLM 分类 Prompt 模板(未配置 key 时自动回退到规则)。
- 一段已跑通的代码:批量拉预告 → 分类 → 计算 T+0/T+1/T+5 事件窗口收益。
- 一组 6 只样本股的真实运行结果,供你参考数据形态。
一、数据从哪来
业绩预告:company_earnings_forecast
| 字段 | 含义 |
|---|---|
abs |
预告正文(含利润区间和增速) |
type |
预告类型,如预增、预升、预降、预亏、减亏等 |
pdate |
预告发布日期 |
rdate |
报告期,如 2024-12-31 |
old |
上年同期 EPS |
拿到预告类型还不够。比如"减亏"虽然是亏损,但亏损幅度在收窄,市场情绪可能是中性偏正面;"预降"则是实打实的利空。我们需要把 type 和 abs 一起交给规则或 LLM 来判断方向。
日线行情:stock_history
| 字段 | 含义 |
|---|---|
t |
交易日期 |
c |
收盘价 |
o / h / l |
开盘/最高/最低价 |
事件窗口收益就是比较"公告日收盘价"和"公告后第 N 个交易日收盘价"的涨跌幅。
python
import mairui
import os
api = mairui.Client("LICENCE-66D8-9F96-0C7F0FBCD073") # 演示证书,演示证书(读者复制即用,无需替换)
# 业绩预告
forecasts = api.company_earnings_forecast("600519")
for f in forecasts[:3]:
print(f["pdate"], f["type"], f["abs"][:60])
# 日线
hist = api.stock_history("600519")
for row in hist[-3:]:
print(row["t"], row["c"])
二、规则分类:把预告类型翻译成市场方向
规则分类器适合批量跑全量数据,零成本、可解释、速度快。
python
POSITIVE_TYPES = {"预增", "预升", "预盈", "扭亏"}
NEGATIVE_TYPES = {"预减", "预亏", "首亏", "续亏", "大幅下降"}
def rule_label(type_text: str, abs_text: str) -> str:
t = (type_text or "").strip()
if t in POSITIVE_TYPES:
return "positive"
if t in NEGATIVE_TYPES:
return "negative"
# 兜底:从正文中找关键词
txt = abs_text or ""
if any(w in txt for w in ("亏损", "下降", "减少", "大幅下滑")):
return "negative"
if any(w in txt for w in ("增长", "上升", "盈利", "扭亏为盈")):
return "positive"
return "neutral"
三、LLM 分类 Prompt:让 AI 读"弦外之音"
规则再精细也覆盖不了所有表述。比如"业绩同比大幅下降,但环比明显改善"该算什么方向?LLM 更擅长处理这种语义级别的转折。
下面是一个可直接套用的 Prompt:
text
请阅读以下 A 股业绩预告,判断其对股价的短期情绪方向,
只返回三个标签之一:positive(利好)/ negative(利空)/ neutral(中性)。
预告类型:{type_text}
预告文本:{abs_text}
标签:
代码封装:
python
def llm_classify(abs_text: str, type_text: str) -> str:
api_key = os.environ.get("LLM_API_KEY", "")
base_url = os.environ.get("LLM_BASE_URL", "")
if not api_key or not base_url:
# 没配 LLM key 时自动回退到规则分类
return rule_label(type_text, abs_text)
# 否则调用 openai / 兼容接口
...
这样脚本在任何环境下都能跑通,不会因为缺 LLM key 而中断。
四、事件窗口收益怎么算
关键概念:公告日不一定是交易日。如果 pdate 落在周末或假期,事件日要取"公告后的第一个交易日"。
python
import pandas as pd
def compute_event_return(hist_df: pd.DataFrame, pdate: str, offset: int):
"""
offset=0: 事件日相对前一日收盘涨跌幅
offset=1: T+1
offset=5: T+5
"""
hist_df["date"] = pd.to_datetime(hist_df["t"])
hist_df = hist_df.sort_values("date").reset_index(drop=True)
# 找到大于等于 pdate 的第一个交易日
d = pd.to_datetime(pdate)
pos = hist_df.index[hist_df["date"] >= d]
if len(pos) == 0:
return None
base = pos[0]
prev = base - 1
target = base + offset
if prev < 0 or target >= len(hist_df):
return None
base_close = float(hist_df.at[base, "c"])
prev_close = float(hist_df.at[prev, "c"])
target_close = float(hist_df.at[target, "c"])
if offset == 0:
return (base_close - prev_close) / prev_close * 100
return (target_close - base_close) / base_close * 100
对每一只样本股,先拉全部历史日线,然后遍历它的每一条业绩预告,批量计算 T+0、T+1、T+5 收益。
五、真实运行结果
以 6 只样本股为例,共拉取 181 条业绩预告,分类后计算事件窗口收益:
| 分类 | 数量 | 平均 T+0 | 平均 T+1 | 平均 T+5 | T+1 胜率 | T+5 胜率 |
|---|---|---|---|---|---|---|
| positive(利好) | 139 | +1.02% | -0.03% | +0.45% | 51.08% | 50.36% |
| neutral(中性) | 14 | -2.14% | -1.03% | +1.01% | 21.43% | 64.29% |
| negative(利空) | 28 | -0.48% | +1.19% | +1.76% | 60.71% | 60.71% |
这里有两个反直觉的点:
- "利好"预告的 T+1 平均收益接近 0,并不是预增就一定涨。原因是市场对业绩预期已经部分 price in。
- "利空"预告的短期收益反而为正,可能是因为跌幅已在前几日释放,公告日出现超跌反弹。
这些现象说明:预告类型只是事件研究的一个维度,不能直接当交易策略。 把它当作"信号库"中的一条线索,配合仓位和择时才有意义。
正向样本中 T+1 涨幅较高的几条:
- 比亚迪 2020-08-29 预增:T0=-0.46%,T1=+10.00%,T5=+11.46%
- 招商银行 2013-03-02 预升:T0=-6.12%,T1=+8.88%,T5=+6.93%
- 五粮液 2009-10-19 预升:T0=+2.66%,T1=+8.42%,T5=+5.00%
负向样本中 T+1 跌幅较大的几条:
- 比亚迪 2011-07-13 预减:T0=-1.02%,T1=-3.82%,T5=-2.53%
- 比亚迪 2011-08-23 预减:T0=+1.22%,T1=-3.34%,T5=-3.42%
- 隆基绿能 2026-07-15 预亏:T0=+2.30%,T1=-1.17%,T5=+4.75%
六、常见坑
- 预告发布日 vs 事件日 :
pdate可能落在非交易日,直接按自然日偏移会算错。必须映射到交易日历。 - 同一报告期多条预告 :有些公司会发"首次预告"再发"修正预告",事件研究时要按
pdate取最新一条。 - 区间预告的收益计算 :预告里给的是利润区间,本文主要用
type分类;如果要更精细,可以把区间中值与市场预期对比。 - 样本偏倚:本文只用 6 只大盘股做演示,不能代表全市场。扩大样本时建议使用批量接口减少 API 调用次数。
- LLM 输出不稳定:生产环境要对 LLM 返回做格式校验和重试,未配置 key 时务必保留规则 fallback。
小结与下篇预告
今天我们做了一套"业绩预告事件研究"流水线:
- 用规则 + LLM 把业绩预告分类为利好/中性/利空;
- 把
pdate映射到交易日,计算 T+0/T+1/T+5 窗口收益; - 用真实数据说明:预告方向 ≠ 股价方向,事件研究需要大样本和严格归因。
下篇(#06)进入技术指标的"人话翻译":让 LLM 把金叉、死叉、MACD 红绿柱解释成普通人能听懂的交易语言。
风险提示:本文仅供技术学习交流,不构成投资建议。业绩预告分析存在数据覆盖、分类偏差和样本偏倚等风险,历史结果不代表未来收益。
API 文档与证书获取:https://github.com/MaiRuiApi