炒股软件弹出一条公告,你第一反应是不是「要涨」?
但问题是: announcement 本身不等于 profit。公告后的股价怎么走,和公告类型、市场预期、大盘情绪都有关系,直觉往往靠不住。做量化研究的人都知道,真正有用的问法是:公告前后 N 个交易日,这只股票的平均累计收益是多少?胜率是多少?
这就是「事件窗口分析」(Event Window Study)——把公告日当作 t=0,向前看 5 天、向后看 5 天,把多只股票、多次公告的窗口收益拼在一起,看有没有稳定的统计规律。
今天这篇文章就教你用 Python 跑一次完整的事件窗口分析。读完之后,你不仅能复现这个方法,还能把它套到分红、龙虎榜、涨停炸板等其他事件上。
本文你将得到什么
核心思路
事件窗口分析只做三件事:
把多次事件的结果平均,就能看出公告这个事件本身是否带来了超额收益。
可运行代码
下面的代码使用公开演示证书,方便你直接复制运行。如果你没有安装 mairui 包,可以先执行:
pip install mairui pandas numpy
from mairui import Client
import pandas as pd
import numpy as np
LICENCE = "LICENCE-66D8-9F96-0C7F0FBCD073" # 演示证书;换成你自己的正式证书
client = Client(LICENCE)
# 关注股票与对应业绩预告事件日期(示例)
EVENTS = [
{"code": "600519", "date": "2026-04-15", "name": "贵州茅台"},
{"code": "600519", "date": "2026-07-20", "name": "贵州茅台"},
{"code": "000001", "date": "2026-03-10", "name": "平安银行"},
{"code": "000001", "date": "2026-08-12", "name": "平安银行"},
{"code": "300750", "date": "2026-05-18", "name": "宁德时代"},
{"code": "300750", "date": "2026-09-08", "name": "宁德时代"},
]
WINDOW_BEFORE, WINDOW_AFTER = 5, 5
def load_prices(code, st="20260101", et="20260925"):
"""尝试从 API 拉行情;演示证受限时回退到教学样例数据。"""
try:
bars = client.stock_history(code, "d", "n", st=st, et=et)
if bars:
df = pd.DataFrame(bars)
df = df.rename(columns={"t": "date", "c": "close"})
df["date"] = pd.to_datetime(df["date"])
return df[["date", "close"]].sort_values("date")
except Exception as e:
print(f"演示证拉取受限,回退到样例数据:{e}")
# 教学样例:生成固定 seed 的随机游走
np.random.seed(int(code) if code.isdigit() else 42)
dates = pd.bdate_range("2026-01-06", "2026-09-25")
close = 100 * np.exp(np.cumsum(np.random.randn(len(dates)) * 0.02))
return pd.DataFrame({"date": dates, "close": np.round(close, 2)})
def event_window(prices, event_date, before=5, after=5):
prices = prices.sort_values("date").reset_index(drop=True)
idx = prices[prices["date"] >= pd.to_datetime(event_date)].index.min()
if pd.isna(idx):
return None
idx = int(idx)
start = max(0, idx – before)
end = min(len(prices), idx + after + 1)
if end – start < before + after + 1:
return None
base = prices.iloc[idx – 1]["close"]
window = prices.iloc[start:end].copy()
window["relative_day"] = np.arange(–(idx – start), end – idx)
window["cum_return"] = (window["close"] / base – 1) * 100
return window[["date", "relative_day", "close", "cum_return"]]
results = []
for ev in EVENTS:
prices = load_prices(ev["code"])
w = event_window(prices, ev["date"])
if w is not None:
w["code"] = ev["code"]
w["name"] = ev["name"]
results.append(w)
agg = pd.concat(results, ignore_index=True)
stats = agg.groupby("relative_day")["cum_return"].agg(["mean", "median", "count"]).reset_index()
print(stats.round(2).to_string(index=False))
运行后你会看到类似下面的窗口收益表:
relative_day mean median count
-5 -0.01 -0.31 5
-4 0.03 -1.00 5
-3 -1.61 -2.31 5
-2 -0.73 -0.30 5
-1 0.00 0.00 5
0 0.00 1.76 5
1 -1.19 -0.95 5
2 -2.47 -3.01 5
3 -2.71 -2.35 5
4 -3.46 -1.83 5
5 -2.63 -2.26 5
(示例数据为教学演示,换正式证书后可跑全量真实数据。)
怎么读这张表
- relative_day = 0:公告当天。如果当天已经大涨,说明公告前市场预期已经打满,追进去风险高。
- relative_day = -1:公告前一天。经常会出现「信息提前泄漏」带来的异动,如果这一天收益显著为正,说明内幕交易或机构提前布局的概率偏高。
- relative_day = +1:公告后第一个交易日。如果当天收益为负,说明「利好出尽是利空」,追高容易被套。
注意:单只股票、单次公告没有任何说服力。事件窗口分析的价值在于多次事件平均,样本量越大,结论越稳。
三个容易踩的坑
小结
事件窗口分析不是什么神秘模型,它就是把公告当做一个时间节点,看股价在这个节点前后的统计反应。这个方法的最大好处是可复制:换一只股票、换一类公告,代码结构几乎不变。
下一步你可以把它扩展到:分红除息、龙虎榜、涨停炸板、互动易舆情……我们下一篇先拿「分红除权除息」开刀。
下篇预告:【Python量化事件驱动实战 02】分红公告前后股价怎么走?除权除息事件的 Python 分析。
免责声明
本文仅为技术方法演示,不构成任何投资建议。文中示例数据仅用于教学,历史表现不代表未来收益。投资有风险,决策需谨慎。
代码与文档:https://github.com/MaiRuiApi





