← 返回博客列表

Python实时盯盘与预警

【Python实时盯盘与预警 #15】收盘了还在手动翻行情软件Python自动生成一页PDF复盘报告

2026年10月15日 08:19 · 麦蕊智数 MyData

每天收盘后,你是不是也这样——打开同花顺切到指数页看一眼,翻涨幅榜数前几名,再翻板块排行找热点,最后在脑子里拼凑出一张"今天行情到底怎么样"的大图……

这个过程少则 10 分钟,多则半小时。而且——每天做的事一模一样,完全可以交给 Python。

这篇文章用不到 200 行 Python,收盘后一键拉取四大指数、涨幅榜、跌幅榜、概念板块数据,自动拼成一张 A4 横向 PDF 复盘报告。

一、本文你将得到什么

  1. 用 index_real_time 拉取上证/深证/创业板/科创 50 四大指数。
  2. 用 stock_ssjy_more 批量拉取个股行情,自动排涨跌幅 Top10。
  3. 用 sector_detail 获取概念板块成分股,手动计算板块涨跌幅。
  4. 用 matplotlib 绘制 6 张子图拼成一页 PDF:指数涨跌、成交额、板块热力、涨幅榜、跌幅榜、盘面综述。
  5. 一个可扩展的盘后复盘脚本,换标的就能复用。

想先在线试试接口效果?打开 API Playground 即可直接调用测试: https://mairuiapi.com/playground

二、环境准备

本文代码使用 mairui SDK 获取指数与个股实时行情,安装方法如下:

pip install mairui

SDK 的完整接口文档与使用说明请查阅官网:https://www.mairuiapi.com/sdk

行情接口的详细参数说明请查阅官网 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="<你的证书>")  # 演示证书,请替换成你自己的正式证书

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="<你的证书>")  # 演示证书,请替换成你自己的正式证书
    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 复盘报告

本系列所有接口均基于麦蕊智数(mairui)SDK。如需获取完整历史行情、实时异动数据,可访问 https://www.mairuiapi.com 了解证书方案。数据持续稳定,一份证书即可打通盘中到盘后的全链路。


免责声明:本文所有代码和数据仅供学习与参考,不构成任何投资建议。股市有风险,投资需谨慎。A 股涨跌幅红色表示上涨、绿色表示下跌。

标签: Python量化
QQ 客服 3826425416 咨询时请提供证书号或订单号,便于快速处理
咨询