本文是「Python量化实战」系列第 15 篇(策略实战模块收官篇)。上一篇我们用 backtrader 跑通了第一个策略回测,这一篇换个思路:不看 K 线形态,只看"事件"——业绩预告、公告一落地,股价到底怎么走?
“某某股预增 80%!”——这种消息一弹出来,很多人的第一反应是马上追。但你有没有认真算过:业绩预告发布后第二天买入,平均到底能赚多少? 我们用 15 只大盘股近三年半的 54 条业绩预告真实数据算了一遍,结果可能会让你意外:"预升"公告后追涨,T+1 平均收益只有 0.11%,胜率不足五成。本文带你用 Python 把这套"事件驱动分析"完整跑一遍,让数据代替直觉说话。
本文你将得到什么
- 一套可复用的业绩预告事件抓取代码(含预升/预增/预亏/预降类型解析)
- 一个标准的事件窗口收益分析框架(T+1 / T+5 / T+10 收益与胜率统计)
- 一份基于真实数据的分类型统计结论(附完整数字,可自行复现验证)
- 一个可扩展的公告关键词监控骨架(回购/增持/中标/重大合同)
- 事件驱动策略常见的 4 个坑与规避方法
一、在线体验
想先在线试试接口效果?打开 API Playground 即可直接调用测试,无需安装任何东西:
https://mairuiapi.com/playground
本文用到三个接口:业绩预告(company_earnings_forecast)、个股公告(stock_announcement)、历史 K 线(stock_history),都可以先在 Playground 里输入股票代码看看返回结构。
二、环境准备
本文代码使用 mairui SDK 获取股票数据,安装方法如下:
pip install mairui
SDK 的完整接口文档与使用说明请查阅 GitHub 仓库:https://github.com/MaiRuiApi/mairui
接口的详细参数说明请查阅官网 API 文档:https://mairuiapi.com/hsdata
运行环境:
- Python 3.9+
- mairui SDK 1.0.0
- pandas 2.2+
证书通过环境变量读取,不要写死在代码里:
import os
import mairui
# 证书从环境变量读取(官网注册后获取)
api = mairui.Client(os.environ["MAIRUI_LICENCE"])
本文数据截至 2026-07-28,事件样本区间 2023-01-01 至 2026-06-30,读者复现时数据可能因行情更新而略有差异。
三、什么是事件驱动策略
技术面策略盯的是"价格形态",事件驱动策略盯的是"信息落地":业绩预告、回购公告、重大合同、股东增持……每个事件都可能改变市场对一只股票的定价。事件驱动分析的核心问题只有一个:
某类事件发生后的 N 个交易日里,股价平均表现如何?
回答这个问题需要三步:抓事件 → 对齐事件日之后的行情 → 统计窗口收益。下面逐步实现。
四、第一步:抓业绩预告事件
company_earnings_forecast 接口返回一只股票历史上所有业绩预告,字段包括发布日 pdate(这就是我们的"事件日")、报告期 rdate、预告类型 type(预升/预增/预降/预亏/减亏等)和摘要 abs:
import time
import pandas as pd
# 示例股票池:跨行业大盘股样本(固定池便于复现,实际可换成指数成分股)
STOCK_POOL = {
"600519": "贵州茅台", "000858": "五粮液", "601318": "中国平安",
"600036": "招商银行", "000333": "美的集团", "600900": "长江电力",
"601088": "中国神华", "600030": "中信证券", "000651": "格力电器",
"300750": "宁德时代", "601899": "紫金矿业", "600887": "伊利股份",
"002594": "比亚迪", "601012": "隆基绿能", "600809": "山西汾酒",
}
EVENT_START, EVENT_END = "2023-01-01", "2026-06-30"
def fetch_forecast_events(api):
"""拉取股票池的业绩预告,抽取事件四要素:代码 / 发布日 / 类型 / 摘要。"""
rows = []
for code, name in STOCK_POOL.items():
for item in api.company_earnings_forecast(code) or []:
rows.append({
"code": code, "name": name,
"pdate": item.get("pdate"), # 预告发布日 = 事件日
"type": item.get("type"), # 预升/预增/预降/预亏…
})
time.sleep(0.2) # 控制请求频率
events = pd.DataFrame(rows)
events = events[(events["pdate"] >= EVENT_START) & (events["pdate"] <= EVENT_END)]
return events.sort_values("pdate").reset_index(drop=True)
真实运行结果(2026-07-29 验证):
业绩预告采集: 成功 15 只, 失败 0 只
区间 2023-01-01 ~ 2026-06-30 事件数: 54,类型分布:
预升 27
预增 14
预降 9
预亏 2
减亏 2
关键解释:事件日必须用预告发布日 pdate,而不是报告期 rdate——市场是在发布那一刻才知道消息的,用报告期对齐行情就是典型的"未来函数"。
五、第二步:计算事件窗口收益
事件研究法(Event Study)的标准做法:以事件日后第一个交易日为基准买入,统计其后第 1、5、10 个交易日的累计收益:
import numpy as np
WINDOWS = (1, 5, 10)
def event_window_returns(api, code, pdate):
"""以事件日后首个交易日开盘价买入,算 T+1/T+5/T+10 收盘相对收益(%)。"""
st = pdate.replace("-", "")
et = (pd.Timestamp(pdate) + pd.Timedelta(days=40)).strftime("%Y%m%d")
kline = api.stock_history(code, "d", "n", st=st, et=et) # st/et 格式 YYYYMMDD
if not kline:
return {}
kline_df = pd.DataFrame(kline).sort_values("t").reset_index(drop=True)
entry_open = float(kline_df.iloc[0]["o"]) # 事件后首根K线开盘价买入
result = {}
for n in WINDOWS:
if len(kline_df) > n:
close_n = float(kline_df.iloc[n]["c"])
result[f"ret_t{n}"] = round((close_n / entry_open - 1) * 100, 2)
else:
result[f"ret_t{n}"] = np.nan
return result
关键解释:
- 买入基准用事件日后首个交易日的开盘价——预告多在收盘后或周末发布,事件日当天的收盘价你根本买不到;
st/et参数格式是YYYYMMDD(不带横杠),传2024-01-01这类带横杠格式会报参数错误,这是新手最常踩的格式坑;- 日历天数取 40 天再截取第 N 根 K 线,自动跳过停牌和节假日,比"日历日 +N 天"严谨。
六、真实数据说话:54 个事件的统计结果
按预告类型聚合 54 个事件样本的窗口收益(平均收益单位 %,真实运行输出):
| 预告类型 | 样本数 | T+1 平均 | T+1 胜率 | T+5 平均 | T+5 胜率 | T+10 平均 | T+10 胜率 |
|---|---|---|---|---|---|---|---|
| 预升 | 27 | +0.11 | 44% | +0.50 | 48% | +0.48 | 52% |
| 预增 | 14 | +0.74 | 43% | +2.18 | 64% | +1.02 | 64% |
| 预降 | 9 | +0.70 | 67% | +2.91 | 78% | +4.25 | 89% |
| 预亏 | 2 | +5.00 | 50% | +3.94 | 50% | +5.72 | 100% |
| 减亏 | 2 | -3.52 | 0% | +3.18 | 100% | -0.80 | 50% |
三个值得注意的现象(仅就本样本而言):
- "预升"追涨最不划算:27 个样本 T+1 平均只有 0.11%,胜率 44%——好消息往往早已反映在价格里(“利好出尽”)。
- "预降"反而表现最好:T+10 平均 +4.25%、胜率 89%——大盘蓝筹的利空落地后,市场常出现"靴子落地"式修复。
- 窗口拉长,分化更明显:T+1 噪音大,T+5/T+10 的类型差异才逐渐显现。
⚠️ 统计局限性必须说清楚:本样本仅 15 只大盘股、54 个事件,“预亏”"减亏"各只有 2 条,不具备统计显著性;不同市值、不同行业、不同市场环境下结论可能完全不同。这套框架的价值在于方法可复现,而不是结论可照搬。
七、第三步:公告监控骨架——把事件源扩展到全部公告
业绩预告只是事件之一。回购、增持、中标、重大合同同样是经典事件源。stock_announcement 接口配合关键词过滤,就是一个最小可用的事件监控骨架:
ANNOUNCE_KEYWORDS = ("回购", "增持", "中标", "重大合同", "业绩")
def scan_announcements(api, codes, limit=5):
"""拉最近公告并按关键词过滤,作为盘中事件监控的最小骨架。"""
hits = []
for code in codes:
for a in api.stock_announcement(code, lt=limit) or []:
title = a.get("zt", "") # 公告标题
if any(k in title for k in ANNOUNCE_KEYWORDS):
hits.append({"code": code, "date": a.get("t"), "title": title})
time.sleep(0.2)
return pd.DataFrame(hits)
真实运行输出(节选):
code date title
000858 2026-07-15 五 粮 液:2026年半年度业绩预告
000858 2026-07-16 五 粮 液:关于2025年度分红派息实施后调整回购股份价格上限的公告
000333 2026-07-09 美的集团:关于以集中竞价交易方式回购A股股份达总股本1%的进展公告
把它挂上定时任务(比如每 10 分钟扫一遍自选股池),命中关键词就推送提醒,一个简易的"事件哨兵"就成型了。系列第 19 篇会专门讲盯盘与推送的工程化实现。
八、完整可运行示例
把三步串起来的完整脚本(约 120 行)结构如下,完整代码按文中各节拼装即可直接运行:
import os
import mairui
def main():
api = mairui.Client(os.environ["MAIRUI_LICENCE"]) # 证书从环境变量读取
events = fetch_forecast_events(api) # ① 抓业绩预告事件
study = build_event_study(api, events) # ② 逐事件算窗口收益
study.to_csv("event_study.csv", index=False, encoding="utf-8-sig")
for n in (1, 5, 10): # ③ 按类型聚合统计
agg = study.groupby("type")[f"ret_t{n}"].agg(
样本数="count",
平均收益=lambda s: round(s.mean(), 2),
胜率=lambda s: f"{(s > 0).mean() * 100:.0f}%",
)
print(f"[T+{n}]\n{agg}")
ann = scan_announcements(api, list(STOCK_POOL)[:5]) # ④ 公告监控骨架
print(ann)
if __name__ == "__main__":
main()
九、避坑与进阶
- 坑 1:用报告期当事件日。
rdate是财报对应的期末日期,pdate才是消息落地日,搞混了就是引入未来函数,回测结果会好得离谱。 - 坑 2:用事件日收盘价当买入价。预告常在盘后发布,当天收盘价买不到;事件后首个交易日开盘价(甚至考虑高开幅度)才是可执行的基准。
- 坑 3:样本太少就下结论。类型细分后单类样本很容易只剩个位数,先看样本数再看均值,必要时合并类型(如预升+预增 = 正面事件)。
- 坑 4:忽略整体行情。事件窗口收益里混着大盘涨跌,严谨做法是减去同期沪深 300 收益得到"超额收益",读者可以用
index_history接口自行扩展。 - 进阶方向:把事件类型、预告幅度(从
abs摘要正则提取)、市值分组做成因子,与第 12 篇的多因子框架结合,就是一个事件因子研究的雏形。
十、总结与延伸
本文用真实数据回答了"业绩预告出了要不要追"这个问题:至少在大盘股样本里,追"预升"是期望值很低的行为,而"预降"的靴子落地效应反而值得研究。更重要的是你拿到了一套可复现的事件研究框架——抓事件、对齐窗口、算收益、看胜率,任何事件源(公告/解禁/分红)都能套用。
策略实战模块(#11-#15)到此收官。下一篇进入工程化模块:全市场 5000+ 只股票的数据怎么批量拉才又快又稳——并发、重试、进度、合并,一次讲透。
延伸阅读:
- 在线体验更多接口:https://mairuiapi.com/playground
- 查看完整 API 文档:https://mairuiapi.com/hsdata
- SDK 文档与源码:https://github.com/MaiRuiApi/mairui
- 关注公众号获取本系列更新
本文为技术演示,文中所有统计基于有限历史样本,不构成投资建议。
&spm=1001.2101.3001.5002&articleId=163430145&d=1&t=3&u=e7bccf0155a5452d9a37bde98f6d4066)
1368

被折叠的 条评论
为什么被折叠?



