【Python实时盯盘与预警 #15】收盘了还在手动翻行情软件?Python 自动生成一页 PDF 复盘报告
每天收盘后,你是不是也这样——打开同花顺切到指数页看一眼,翻涨幅榜数前几名,再翻板块排行找热点,最后在脑子里拼凑出一张"今天行情到底怎么样"的大图……
这个过程少则 10 分钟,多则半小时。而且——每天做的事一模一样,完全可以交给 Python。
这篇文章用不到 200 行 Python,收盘后一键拉取四大指数、涨幅榜、跌幅榜、概念板块数据,自动拼成一张 A4 横向 PDF 复盘报告。
一、本文你将得到什么
- 用 index_real_time 拉取上证/深证/创业板/科创 50 四大指数。
- 用 stock_ssjy_more 批量拉取个股行情,自动排涨跌幅 Top10。
- 用 sector_detail 获取概念板块成分股,手动计算板块涨跌幅。
- 用 matplotlib 绘制 6 张子图拼成一页 PDF:指数涨跌、成交额、板块热力、涨幅榜、跌幅榜、盘面综述。
- 一个可扩展的盘后复盘脚本,换标的就能复用。
想先在线试试接口效果?打开 API Playground 即可直接调用测试:
https://mairuiapi.com/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(用于表格展示与排序)
- matplotlib(用于图表绘制和 PDF 输出)
本文数据截至 2026-08-08(周五收盘),读者复现时数据可能因行情更新而略有差异。
证书直接写在代码里(文中使用演示证书,演示证书(读者复制即用,无需替换))。
三、核心思路:一张纸装下六张图
复盘报告的"一页 PDF"骨架是 matplotlib 的 subplot 网格。我们在一张 A4 横向画布上摆 6 张子图:
位置 内容 接口
左上 四大指数涨跌幅柱状图 index_real_time × 4
右上 四大指数成交额柱状图 同上
左下 概念板块涨跌幅水平柱状图 sector_detail + stock_ssjy_more
中左 涨幅 Top10(水平柱状图) stock_ssjy_more
中右 跌幅 Top10(水平柱状图) 同上
右下 盘面综述文字框 汇总以上数据
四、代码实现
4.1 拉取四大指数
from mairui import Client
api = Client(licence="LICENCE-66D8-9F96-0C7F0FBCD073") # 演示证书,演示证书(读者复制即用,无需替换)
idx_codes = {
"上证指数": "000001.SH", "深证成指": "399001.SZ",
"创业板指": "399006.SZ", "科创50": "000688.SH",
}
idx_data = []
for name, code in idx_codes.items():
r = api.index_real_time(code=code)
item = r[0] if isinstance(r, list) else r
idx_data.append({
"名称": name,
"现价": item.get("p", 0),
"涨跌幅": item.get("pc", 0),
"成交额(亿)": round(item.get("cje", 0) / 1e8, 2),
})
注意:index_real_time 只支持单指数查询,逗号分隔会静默丢失后续数据,需要循环逐只调用。
4.2 批量拉取个股行情并排涨跌幅 Top10
用批量接口一次拉 20 只(免费版 500 次/日,批量 1 次算 1 次额度)
hot_stocks = [
"000001", "000002", "000858", "002594", "300750", "601318", "600519",
"601166", "600036", "000333", "002415", "300059", "600030", "601888",
"000651", "002475", "300274", "601012", "600276", "603259",
]
stocks = api.stock_ssjy_more(stock_codes=",".join(hot_stocks))
stock_ssjy_more 返回字段:dm(代码)/p(现价)/pc(涨跌幅)/cje(成交额) 等
注意:批量接口返回无 mc(名称),需要自己维护一个代码→名称映射表
stock_name_map = {
"000001": "平安银行", "000002": "万科A", "000858": "五粮液",
"002594": "比亚迪", "300750": "宁德时代", "601318": "中国平安",
# ... 其余略
}
关键坑:stock_ssjy_more 的参数名是 stock_codes(字符串,逗号分隔),不是 dc 或 code;且返回字段里没有 mc(股票名称),只有 dm(代码)。如果需要显示名称,需要自行维护映射表。
4.3 概念板块涨跌幅(手动计算)
stock_ssjy_more 单次最多 20 只。对于半导体这种 500 只成分股的大板块,我们取前 20 只成分股计算平均涨跌幅作为板块代理指标——实践中足够反映板块方向:
取板块成分股
det = api.sector_detail(sector_name="GN半导体")
# 返回 {count, sector_name, stocks: [{dm, mc, jys}, ...]}
components = [s["dm"] for s in det["stocks"][:20]]
批量拉取行情
comp_data = api.stock_ssjy_more(stock_codes=",".join(components))
sector_pc = sum(s["pc"] for s in comp_data) / len(comp_data)
4.4 matplotlib 六子图拼成一页 PDF
这是整篇文章的核心——用 plt.subplot(2, 3, n) 把六张图摆到 A4 横向画布上:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
中文字体
font_cn = FontProperties(fname="C:/Windows/Fonts/msyh.ttc", size=10)
fig = plt.figure(figsize=(11.69, 8.27), dpi=150) # A4 landscape
fig.suptitle("盘后复盘报告 — 2026-08-08", fontsize=20, fontweight="bold")
子图 1:四大指数涨跌幅
ax1 = fig.add_subplot(2, 3, 1)
colors = ["#e74c3c" if v >= 0 else "#2ecc71" for v in df_idx["涨跌幅"]]
ax1.bar(range(len(df_idx)), df_idx["涨跌幅"], color=colors)
# ... (设置标签、标题)
... 子图 2~5 类似
子图 6:盘面综述文字
ax6 = fig.add_subplot(2, 3, 6)
ax6.axis("off")
ax6.text(0.05, 0.95, "复盘日期:2026-08-08\n"
"四大指数:4涨0跌,整体偏强\n"
"最强指数:科创50 +2.51%\n"
"总成交额:35355 亿\n"
"...",
transform=ax6.transAxes, fontproperties=font_cn)
plt.tight_layout()
fig.savefig("report_2026-08-08.pdf", dpi=150, bbox_inches="tight")
打开生成的 PDF,你会看到一张完整的复盘报告——所有关键信息都在一页纸上。
五、完整脚本
下面是可直接运行的完整脚本。将以下代码保存为 posteod_report.py:
-- coding: utf-8 --
"""盘后复盘 PDF 生成器:收盘后一键生成一页复盘报告。"""
import sys, os
from datetime import datetime
_proj_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(file))))
from mairui import Client
import pandas as pd
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
font_cn = FontProperties(fname="C:/Windows/Fonts/msyh.ttc", size=10)
font_title = FontProperties(fname="C:/Windows/Fonts/msyh.ttc", size=14)
plt.rcParams["axes.unicode_minus"] = False
def build_report(api):
date_str = datetime.now().strftime("%Y-%m-%d")
── ① 四大指数 ──
idx_codes = {"上证指数": "000001.SH", "深证成指": "399001.SZ",
"创业板指": "399006.SZ", "科创50": "000688.SH"}
idx_data = []
for name, code in idx_codes.items():
r = api.index_real_time(code=code)
item = r[0] if isinstance(r, list) else r
idx_data.append({"名称": name, "现价": item.get("p", 0),
"涨跌幅": item.get("pc", 0),
"成交额(亿)": round(item.get("cje", 0) / 1e8, 2)})
df_idx = pd.DataFrame(idx_data)
── ② 个股涨跌榜 ──
stock_name_map = {
"000001": "平安银行", "000002": "万科A", "000858": "五粮液",
"002594": "比亚迪", "300750": "宁德时代", "601318": "中国平安",
"600519": "贵州茅台", "601166": "兴业银行", "600036": "招商银行",
"000333": "美的集团", "002415": "海康威视", "300059": "东方财富",
"600030": "中信证券", "601888": "中国中免", "000651": "格力电器",
"002475": "立讯精密", "300274": "阳光电源", "601012": "隆基绿能",
"600276": "恒瑞医药", "603259": "药明康德",
}
codes = list(stock_name_map.keys())
stocks = api.stock_ssjy_more(stock_codes=",".join(codes))
df_stocks = pd.DataFrame(stocks)
df_stocks["名称"] = df_stocks["dm"].map(stock_name_map).fillna(df_stocks["dm"])
df_stocks = df_stocks.sort_values("pc", ascending=False)
top_gainers = df_stocks.head(10)[["名称", "pc"]]
top_losers = df_stocks.tail(10)[["名称", "pc"]].sort_values("pc")
── ③ 概念板块(取样计算) ──
sec_names = ["GN半导体", "GN人工智能", "GN低空经济", "GN机器人",
"GN新能源", "GN光伏", "GN创新药", "GN军工"]
sec_data = []
for sn in sec_names:
det = api.sector_detail(sector_name=sn)
comps = [s["dm"] for s in det["stocks"][:20]]
comp_stocks = api.stock_ssjy_more(stock_codes=",".join(comps))
avg_pc = sum(s["pc"] for s in comp_stocks) / len(comp_stocks) if comp_stocks else 0
sec_data.append({"名称": sn.replace("GN", ""), "涨跌幅": avg_pc})
df_sec = pd.DataFrame(sec_data)
── ④ 绘制 PDF ──
fig = plt.figure(figsize=(11.69, 8.27), dpi=150)
fig.suptitle(f"盘后复盘报告 — {date_str}", fontsize=20,
fontweight="bold", fontproperties=font_title, y=0.98)
子图 1:指数涨跌
ax1 = fig.add_subplot(2, 3, 1)
colors = ["#e74c3c" if v >= 0 else "#2ecc71" for v in df_idx["涨跌幅"]]
ax1.bar(range(len(df_idx)), df_idx["涨跌幅"], color=colors, edgecolor="white")
ax1.set_xticks(range(len(df_idx)))
ax1.set_xticklabels(df_idx["名称"], fontproperties=font_cn, fontsize=8)
ax1.set_title("四大指数涨跌幅(%)", fontproperties=font_title)
ax1.axhline(y=0, color="gray", linewidth=0.5)
子图 2:成交额
ax2 = fig.add_subplot(2, 3, 2)
ax2.bar(range(len(df_idx)), df_idx["成交额(亿)"], color="#3498db", edgecolor="white")
ax2.set_xticks(range(len(df_idx)))
ax2.set_xticklabels(df_idx["名称"], fontproperties=font_cn, fontsize=8)
ax2.set_title("成交额(亿元)", fontproperties=font_title)
for i, v in enumerate(df_idx["成交额(亿)"]):
ax2.text(i, v + 50, f"{v:.0f}亿", ha="center", fontsize=8, fontproperties=font_cn)
子图 3:板块涨跌
ax3 = fig.add_subplot(2, 3, 3)
sc_colors = ["#e74c3c" if v >= 0 else "#2ecc71" for v in df_sec["涨跌幅"]]
ax3.barh(range(len(df_sec)), df_sec["涨跌幅"], color=sc_colors, edgecolor="white")
ax3.set_yticks(range(len(df_sec)))
ax3.set_yticklabels(df_sec["名称"], fontproperties=font_cn, fontsize=8)
ax3.set_title("概念板块涨跌幅(%)", fontproperties=font_title)
ax3.axvline(x=0, color="gray", linewidth=0.5)
子图 4:涨幅 Top10
ax4 = fig.add_subplot(2, 3, 4)
g_names = [str(n)[:4] for n in top_gainers["名称"]]
g_vals = list(top_gainers["pc"])
ax4.barh(range(len(g_names)), g_vals[::-1], color="#e74c3c", edgecolor="white")
ax4.set_yticks(range(len(g_names)))
ax4.set_yticklabels(g_names[::-1], fontproperties=font_cn, fontsize=7)
ax4.set_title("涨幅 Top10 (%)", fontproperties=font_title)
for i, v in enumerate(g_vals[::-1]):
ax4.text(v + 0.1, i, f"{v:+.2f}%", va="center", fontsize=7, fontproperties=font_cn)
子图 5:跌幅 Top10
ax5 = fig.add_subplot(2, 3, 5)
l_names = [str(n)[:4] for n in top_losers["名称"]]
l_vals = list(top_losers["pc"])
ax5.barh(range(len(l_names)), l_vals, color="#2ecc71", edgecolor="white")
ax5.set_yticks(range(len(l_names)))
ax5.set_yticklabels(l_names, fontproperties=font_cn, fontsize=7)
ax5.set_title("跌幅 Top10 (%)", fontproperties=font_title)
for i, v in enumerate(l_vals):
ax5.text(v - 0.6, i, f"{v:+.2f}%", va="center", fontsize=7, fontproperties=font_cn)
子图 6:盘面综述
ax6 = fig.add_subplot(2, 3, 6)
ax6.axis("off")
up_count = (df_idx["涨跌幅"] > 0).sum()
strongest = df_idx.loc[df_idx["涨跌幅"].idxmax()]
lines = [
f"复盘日期:{date_str}",
f"四大指数:{int(up_count)}涨{int(4-up_count)}跌",
f"最强指数:{strongest['名称']} {strongest['涨跌幅']:+.2f}%",
f"总成交额:{df_idx['成交额(亿)'].sum():.0f} 亿",
f"概念板块:{int((df_sec['涨跌幅']>0).sum())}/{len(df_sec)} 收红",
f"\n数据源:行情数据 API",
f"接口调用:4次指数+1次批量+8次板块≈13次",
f"免费版 500 次/日,盘后复盘绰绰有余",
f"\n[免责] 本报告仅供学习参考,不构成投资建议",
]
for i, line in enumerate(lines):
ax6.text(0.05, 0.95 - i * 0.08, line, transform=ax6.transAxes,
fontsize=9, fontproperties=font_cn, va="top")
plt.tight_layout(rect=[0, 0, 1, 0.94])
pdf_path = f"report_{date_str}.pdf"
fig.savefig(pdf_path, dpi=150, bbox_inches="tight")
plt.close(fig)
print(f"[ok] 复盘 PDF 已生成: {pdf_path}")
return pdf_path
if name == "main":
api = Client(licence="LICENCE-66D8-9F96-0C7F0FBCD073") # 演示证书,演示证书(读者复制即用,无需替换)
build_report(api)
运行结果(真实自验):
============================================================
盘后复盘 PDF 生成器 - 自验
============================================================
[ok] 证书已加载 (len=36)
[info] 复盘日期: 2026-08-09
[ok] PDF 已生成: report_2026-08-09.pdf
[ok] 文件大小: 94,467 bytes
[PASS] 盘后复盘 PDF 生成成功
生成的一页 PDF 包含:四大指数全部收红(科创 50 最强 +2.51%)、成交额总计约 3.5 万亿、概念板块涨跌、个股涨跌幅 Top10、完整盘面综述——一页纸讲完一天行情。
六、常见坑与注意事项
坑 1:stock_ssjy_more 参数名是 stock_codes
不是 dc、不是 code、不是 codes。是 stock_codes,传逗号分隔字符串,最多 20 只。
坑 2:批量接口不返回股票名称
stock_ssjy_more 返回的字段是 dm/p/pc/cje/zf/yc/...,没有 mc(名称)。如果需要显示名称,要自己维护代码→名称映射,或者先调单股接口获取名称再批量拉行情。
坑 3:sector_detail 不含板块涨跌幅
这个接口返回的是板块成分股列表({count, sector_name, stocks}),不是板块级别的涨跌幅数据。如果需要板块涨跌幅,取样成分股手动计算均价即可。
坑 4:中文显示
matplotlib 默认不支持中文,需要指定中文字体路径(Windows 下通常是 C:/Windows/Fonts/msyh.ttc)。Linux 下需安装中文字体或改用英文标签。
坑 5:非交易日数据
周六、周日、节假日调用接口,返回的是最近一个交易日的收盘数据。复盘脚本在工作日盘后运行即可。
七、小结与下篇预告
这篇文章我们完成了盯盘系列的收尾一环——盘后自动生成复盘 PDF。加上前面 14 篇的盘中实时监控能力,现在你拥有了一套完整的「盘中盯盘 → 收盘复盘」自动化工具链。
下篇预告:盯盘脚本总不能每次手动 python run.py 吧?下一篇我们把这整套盯盘做成 systemd 常驻服务,开机自启、日志自动轮转、挂了自动重启——让你的盯盘脚本跑成 7×24 的生产级守护进程。
八、系列文章
本系列已发布文章:
-
01 自选股实时报价+涨跌幅一屏看完
-
02 涨跌幅阈值自动告警+本地桌面通知
-
03 涨停跌停一字板,盘中第一时间拉出涨停池
-
04 炸板预警,刚封涨停又开板的票别错过
-
05 五档盘口藏了多少秘密?读懂盘口大单5个信号
-
06 批量接口一次拉20只省95%额度
-
07 3分钟涨幅最猛的30只用Python算涨速排行榜
-
08 成交额突然放大2倍Python窗口比较抓异动
-
09 4个条件任意组合的异动雷达
-
10 把盯盘结果落SQLite一天全留痕
-
11 把告警推送到微信Server酱企业微信钉钉
-
12 盘中指数快照一屏看全市场温度
-
13 ETF溢价率异动Python实时监控
-
14 概念板块异动Python批量拉成分股算板块涨幅榜
-
15 本文 — Python 自动生成一页 PDF 复盘报告
本系列所有接口均基于行情数据 SDK。如需获取完整历史行情、实时异动数据,可访问 https://www.mairuiapi.com 了解证书方案。数据持续稳定,一份证书即可打通盘中到盘后的全链路。
免责声明:本文所有代码和数据仅供学习与参考,不构成任何投资建议。股市有风险,投资需谨慎。A 股涨跌幅红色表示上涨、绿色表示下跌。

浙公网安备 33010602011771号