用MOOTDX抓取A股分时数据,并基于matplotlib绘制专业分时图
2026/9/19 4:56:59 网站建设 项目流程

很多人第一次接触行情数据可视化,最先想画的就是分时图——那种券商软件里红红绿绿的日内价格走势。以前我做这个需求时,换来换去试了一堆数据源,要么注册门槛高,要么历史分时取不到,后来锁定MOOTDX这个Python库,配合matplotlib画出来的效果可以做到和商业软件非常接近。这篇文章我会把完整的抓取逻辑、绘图细节和中间踩过的坑都写出来,配置好环境之后,复制代码就能直接跑,不管是做量化研究还是日常复盘盯盘,都能用得上。

做技术选型时我也纠结过一阵,这个库到底值不值得上手,下面先把它和常见方案放在一起对比着说清楚。

1. MOOTDX是什么,为什么我最终选了它

1.1 一次真实的选型过程

先说结论:MOOTDX是国内开源社区基于通达信行情协议封装的一个Python库,核心定位就是“免费、免注册、实时性尚可”。它的接口风格比较简洁,Quotes.factory()建立连接之后,调用client.minute()就能拿分时数据,调用client.quotes()能拿实时五档报价,不需要申请token,也不存在积分限制。

我当时候选的有三套方案,各有各的毛病:

  • Tushare:数据质量确实稳,但分时这种明细数据对积分要求不低,注册、积分、限流一套组合拳下来,个人开发者直接劝退。
  • AkShare:接口很丰富,聚合了很多数据源,但历史分时数据经常受上游源限制,盘中请求偶尔会被限流,接口报错信息也不够友好。
  • pytdx:底层协议是好的,可是直接用pytdx写代码,连接、打包、解包、字段映射全都得自己处理,开发成本高,不适合只想快速拿到数据的人。

MOOTDX相当于把pytdx那层繁琐的东西重新封装了一遍,底层走的是通达信行情服务器,数据实时性比大多数免费网页接口要强,尤其适合做盘中监控和当日分时复盘。

我实测下来,正常交易日盘中请求分时数据的延迟在毫秒级,基本满足个人研究需要。它还有一个好处:分时数据返回的就是干净的Pandas DataFrame,不需要自己拼结构,拿到手直接就能开始分析绘图。

补充一句:任何免费行情源都有服务器不稳定的可能,MOOTDX也一样。网上偶尔能看到“mootdx不能用了吗”这样的讨论,多半是通达信的行情节点变动导致的,这个在第4章我会重点讲怎么解决。

1.2 安装与基础配置:别踩这些坑

安装本身不复杂,用pip一行就搞定:

pip install mootdx

但我建议在动手之前先做一个隔离环境,尤其是你机器上同时装了很多Python项目依赖的时候。我遇到过因为numpy版本冲突导致matplotlib绘图报错的情况,排查起来纯属浪费时间。用venv或者conda建一个专门的行情分析环境,后面会省心很多:

python -m venv stock_env source stock_env/bin/activate # Windows下执行 stock_env\Scripts\activate pip install mootdx pandas matplotlib

安装完成之后,先别急着写业务代码,跑一下下面这个验证脚本,确保库本身能正常工作:

python -c "from mootdx.quotes import Quotes; print('OK')"

如果输出OK,说明安装没有问题。如果在这一步就报错,大概率是网络环境访问不了通达信的行情服务器,或者库版本过旧。这时可以先升级库:

pip install -U mootdx

再强调一个容易被忽略的点:分时数据抓取和绘图不是MOOTDX一个库能搞定的,你的环境里至少还要有pandas和matplotlib。有些教程默认你已经装好了,但新手往往在这一步卡很久。pandas负责数据处理,matplotlib负责最后的图表输出,三者的关系就是一个流水线,缺一环都会导致最终代码跑不通。

2. 分时数据抓取的原理与核心代码

2.1 分时数据长什么样,看懂字段再动手

分时数据和传统的K线数据思路很不一样。K线数据描述的是一个周期内的四个价格(开高低收),而分时数据是交易时间内每一分钟产生的“切片”。

A股一天交易4小时,上午9:30到11:30共120分钟,下午13:00到15:00共120分钟,理论上每个交易日有240根分钟记录。拿到的分时数据通常包含以下核心字段:

字段含义说明
time时间字符串格式,如"09:31"
price当前价格该分钟最后一笔成交价
avg_price当日分时均价从开盘到该分钟的累计成交金额 ÷ 累计成交量
volume成交量该分钟内的成交量,单位通常是手
last_close昨收价用于计算涨跌幅基准,不是所有版本都有

这里最关键的是avg_price字段,它是画分时均价线(就是券商软件里那条黄线)的数据基础。分时均价的本质是“当日持仓者的平均成本”,它的实时走势能很直观地反映资金是愿意在盘中高位承接还是低位吸筹。

不同版本的MOOTDX返回字段可能有细微差异,建议拿到数据后先执行一次print(df.head())看看实际列名,再往下处理,不要硬编码字段。

2.2 MOOTDX抓取分时数据的完整实现

下面这段代码演示了最基础的抓取流程,我加了重试机制和空数据保护,这两种情况在实际使用中实在太常见了:

from mootdx.quotes import Quotes def get_stock_minute(symbol, date, max_retry=3): """ 获取个股分时数据 :param symbol: 股票代码,如 '600036' :param date: 交易日期,字符串格式 'YYYYMMDD' :param max_retry: 最大重试次数 :return: (DataFrame, Quotes客户端) """ client = Quotes.factory(market='std') for attempt in range(1, max_retry + 1): try: df = client.minute(symbol=symbol, date=date) if df is not None and not df.empty: return df, client print(f'返回数据为空,第 {attempt} 次重试...') except Exception as e: print(f'第 {attempt} 次请求失败: {e}') time.sleep(0.5) raise RuntimeError(f'连续 {max_retry} 次无法获取 {symbol} 的分时数据')

调用方法很简单:

df, client = get_stock_minute('600036', '20240115') print(df.head())

有几个细节需要特别提醒:

  • date参数格式必须是YYYYMMDD。我一开始传成2024-01-15这种带横杠的格式,结果返回空数据,查了半天才发现是格式问题。
  • 如果传了非交易日或者当天还没开盘,返回的DataFrame基本上是空的。最好先确认目标日期确实是交易日,否则后续绘图会拿到一个没有任何数据可画的空表。
  • 网络请求偶尔会超时,所以重试机制不是可有可无的装饰,而是实际使用中真的能救命。

抓回分时数据之后,我顺手还会用client.quotes()拿到当天的昨收价,后面画基准线要用:

quotes_df = client.quotes(symbol=['600036']) prev_close = float(quotes_df.iloc[0]['last_close']) print(f'昨收价: {prev_close}')

注意quotes()接口入参是列表,返回的是DataFrame。不同库版本里昨收字段名可能叫last_close,也可能叫pre_close,建议打印列名确认一下。

3. 用matplotlib绘制一张专业的分时图

3.1 分时图的信息架构设计

拿到分时数据之后,最核心的问题是:一张专业的分时图应该包含哪些信息?我复盘了市面上主流券商软件的分时图,总结出四个必备元素:

  • 价格线:每根分钟柱的收盘价连成的曲线,通常用红色表示上涨、绿色表示下跌,和A股的红涨绿跌惯例保持一致。
  • 分时均价线:当日持仓均价,用黄色或白色绘制,反映市场整体的成本重心。
  • 昨收基准线:一条水平虚线,用来快速判断当前价格处于昨日收盘价上方还是下方。
  • 成交量柱:在副图区域展示,颜色同样遵循红涨绿跌,直观表现每一分钟的多空交战力度。

布局上,主流做法是上下结构:主图放价格线和均价线,副图放成交量,两个子图共享X轴,高度比例大约3:1。

有人会问,为什么不用X轴直接放时间字符串?因为A股交易时间有午间休市,上午11:30到下午13:00之间没有交易数据。如果直接用真实时间轴,中间会空出一大段空白,不仅难看,还会让价格走势在视觉上被拉伸变形。所以专业做法是用数据索引作为X轴,然后在固定位置上标注对应的时间点,这样上午和下午的走势在视觉宽度上是对称的。

3.2 完整绘图代码与逐段解析

下面这段是我在实际项目中打磨过的完整绘图代码,参数都按“券商软件接近的显示效果”调过:

import matplotlib.pyplot as plt import matplotlib.ticker as mticker import pandas as pd import numpy as np from mootdx.quotes import Quotes # matplotlib 默认字体不支持中文,必须先设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False def plot_minute_chart(df, symbol, date, prev_close=None): """ 绘制专业分时图 :param df: 分时数据 DataFrame,必须包含 time/price/avg_price/volume 列 :param symbol: 股票代码,用于标题显示 :param date: 交易日期,用于标题显示 :param prev_close: 昨收价,如果为空则取数据第一行价格近似代替 """ df = df.copy() df['time_str'] = df['time'].astype(str).str[:5] n = len(df) # 如果没有昨收价,用第一分钟价格近似替代(仅作兜底) if prev_close is None: prev_close = float(df['price'].iloc[0]) # 动态寻找上午/下午的分界点 pm_mask = df['time_str'] >= '13:00' split_idx = int(pm_mask.idxmax()) if pm_mask.any() else n # 构造X轴刻度位置:上午取5个点,下午取5个点 def tick_points(start, end): if end - start <= 0: return [start] return [int(start + (end - start) * i / 4) for i in range(5)] positions = tick_points(0, split_idx) + tick_points(split_idx, n - 1) labels = [df['time_str'].iloc[i] for i in positions] # 创建画布:主图+成交量副图 fig, (ax1, ax2) = plt.subplots( 2, 1, figsize=(14, 9), sharex=True, gridspec_kw={'height_ratios': [3, 1]} ) fig.subplots_adjust(hspace=0.08) x = np.arange(n) # === 主图:价格线 + 均价线 + 昨收线 === price_color = '#d33e43' # 红色用于上涨主基调 ax1.plot(x, df['price'], color=price_color, linewidth=1.6, label='价格') ax1.plot(x, df['avg_price'], color='#f0a42e', linewidth=1.2, label='分时均价') ax1.axhline(y=prev_close, color='#555555', linestyle='--', linewidth=0.9, label='昨收') # 涨跌停参考线(按主板10%计算) limit_up = round(prev_close * 1.1, 2) limit_down = round(prev_close * 0.9, 2) ax1.axhline(y=limit_up, color='#d33e43', linestyle=':', linewidth=0.7, alpha=0.5) ax1.axhline(y=limit_down, color='#2f9e6e', linestyle=':', linewidth=0.7, alpha=0.5) # 价格与昨收之间的填充区域,直观呈现盘中浮盈/浮亏区间 ax1.fill_between( x, df['price'], prev_close, where=(df['price'] >= prev_close), color='#d33e43', alpha=0.12 ) ax1.fill_between( x, df['price'], prev_close, where=(df['price'] < prev_close), color='#2f9e6e', alpha=0.12 ) ax1.set_ylabel('价格 (元)') ax1.grid(True, alpha=0.35, linestyle='--') ax1.legend(loc='upper left', fontsize=9) ax1.set_title(f'{symbol} 分时图 {date}', fontsize=14, fontweight='bold') # === 副图:成交量柱状图 === bar_colors = ['#d33e43' if p >= prev_close else '#2f9e6e' for p in df['price']] ax2.bar(x, df['volume'], color=bar_colors, width=0.7, alpha=0.8) ax2.set_ylabel('成交量 (手)') ax2.grid(True, alpha=0.25, linestyle='--') # === X轴时间刻度 === ax2.set_xticks(positions) ax2.set_xticklabels(labels, rotation=0) return fig

这段代码里有两个设计细节我特意做了处理:

第一,时间轴刻度没有写死成“9:30、10:00、10:30……”,而是根据实际数据条数动态计算。这样可以兼容盘中数据不完整、个股停牌时段缺失等情况,不会因为索引越界导致绘图失败。

第二,涨跌停线的计算先按主板10%的规则处理。如果你用的是创业板或科创板标的,需要把比例改成20%,计算方式是一样的。图中用虚线做了边界提示,视觉上能快速看出股价是贴着涨停板运行还是在跌停板附近挣扎。

画完之后的保存操作不要用plt.show()直接弹窗就完事,我建议输出成高分辨率图片:

fig = plot_minute_chart(df, '600036', '20240115', prev_close) fig.savefig('600036_20240115分时图.png', dpi=150, bbox_inches='tight') plt.close(fig)

dpi调到150以上,图片放到复盘笔记或者发到手机上都能看清细节,bbox_inches='tight'可以自动裁掉边缘空白,不让图表四周留太多没用区域。

3.3 输出与保存:海报级效果的小细节

在画图的实际操作中,有几个小细节很影响最终效果,值得单独拿出来说一下。

中文字体是第一优先级。matplotlib的默认字体里没有中文字形,如果你不设置rcParams['font.sans-serif'],标题和图例里的中文就会显示成一个个方框。在Windows上我一般用SimHei(黑体),在macOS上可以用PingFang SC,Linux上如果没有中文字体,需要先安装fonts-wqy-microhei之类的字体包。

颜色规范千万别搞反。A股的红涨绿跌与国际市场的绿涨红跌是相反的。如果习惯写美股技术指标的颜色逻辑,很容易把上涨画成绿色、下跌画成红色,这在看盘语境下会引发严重误读。我在代码里用#d33e43作为上涨色、#2f9e6e作为下跌色,你可以直接复用。

成交量柱的宽度不要太宽。默认宽度为1时柱子会黏在一起,视觉上很拥挤,我调到0.7之后清爽很多。这个值也可以根据数据条数微调,数据量少于200根时适当放宽到0.8。

画完记得关掉图形对象。在批量导出多张分时图时,不执行plt.close(fig)会累积内存,轻则拖慢脚本,重则导致内存溢出,这是一个在批量任务里特别常见的隐性坑。

4. 高频问题与避坑指南

4.1 服务器地址不可用怎么办

这是MOOTDX使用者遇到率最高的问题,也是网上“mootdx不能用了吗”这类讨论的根源。通达信行情协议依赖公网服务器节点,而节点并不是永久稳定的——机房维护、策略调整、流量过载都可能导致某个IP地址失联,遇到这种情况才会出现connect timeout或者address unavailable之类的报错。

我实测有效的排查顺序如下:

第一,先执行自动优选的bestip()方法,让库自动测速并选择可用的服务器节点:

client = Quotes.factory(market='std') client.bestip()

这个方法会遍历内置的服务器列表,逐个测试连接延迟,然后把客户端切换到一个最快最稳的节点上。绝大多数“昨天还能用今天不行了”的情况,通过这一步就能解决。

第二,如果bestip()也救不回来,试着升级库版本:

pip install -U mootdx

服务器地址列表会随版本更新,新版本通常会修正失效的节点。

第三,还是不行时,检查本机到行情服务器的网络连通性。拿Windows系统举例,在命令行里执行:

ping 你的行情服务器IP

不同库版本内置的服务器地址不一样,可以通过client.configclient.addr之类的属性查看当前使用的地址。如果ping不通,说明是网络层面的问题,这时候可以考虑切换网络环境,或者稍微等一段时间再试,服务器恢复后自然就好了。

第四,如果急需用数据,又暂时没有可用的通达信节点,可以临时换数据源过渡:AkShare里的stock_zh_a_minute接口,或者新浪、腾讯的公开行情接口,虽然实时性和稳定性各有差异,但作为备选方案没有问题。等MOOTDX节点恢复后再切回来。

重点:免费库的稳定性不能和商业数据源比。做个人研究可以容忍偶发故障,但如果你的程序要7x24小时跑监控告警,我建议在架构上做好多数据源自动切换,不要在一棵树上吊死。

4.2 数据异常排查:非交易日、停牌、字段缺失

除了服务器问题之外,我整理了一个高频异常速查表,基本都是实战里验证过的:

问题现象根本原因解决办法
返回空DataFrame传入了非交易日或节假日先通过交易日历接口确认日期是否为交易日
返回条数明显少于240个股停牌/盘中数据未完整按实际数据条数动态计算刻度位置
KeyError: 'last_close'库版本字段名不一致打印df.columns确认字段名,改用pre_close
中文标题显示方框matplotlib缺少中文字体设置rcParams['font.sans-serif']
绘图时索引越界时间轴刻度写死导致改用数据条数动态生成刻度位置

这里我特别想提一下“字段名不一致”这个坑。不同版本的MOOTDX在返回字段命名上确实存在过差异,早期版本里last_close可能叫pre_closeavg_price可能叫average。所以最保险的做法是在拿到数据后先看列名,再决定后续字段怎么取值,我上面写的代码也特意保留了对last_closepre_close的双重兜底逻辑。

历史分时数据还有一个隐性限制:通达信节点一般只保留最近几个交易日(有时只有最近5个交易日)的分钟级数据,更早的数据很难通过MOOTDX拿到。如果要做多日分时对比或者历史因子回测,最好每天收盘后定时抓取并落地存储,把数据积累从“临时调用”变成“持续沉淀”。

4.3 进阶玩法:交互式图表与定时任务加持

matplotlib画出来的分时图是静态图片,适合嵌入研究报告或复盘文档。但如果你想在浏览器里查看鼠标悬停的价格明细,或者想在手机上随时打开看当日走势,我建议试试pyecharts。

pyecharts是一个生成JavaScript图表的Python库,输出的是HTML文件,天然适配浏览器和手机端。画分时图的核心思路是:把同样的价格序列和成交量序列传入LineBar组件,最后拼接成一个Grid布局:

from pyecharts.charts import Line, Bar, Grid from pyecharts import options as opts # 将df中的价格、均价、时间转为列表 times = df['time'].astype(str).str[:5].tolist() prices = df['price'].round(2).tolist() avg_prices = df['avg_price'].round(2).tolist() line = Line() line.add_xaxis(times) line.add_yaxis('价格', prices, is_smooth=True) line.add_yaxis('均价', avg_prices, is_smooth=True) bar = Bar() bar.add_xaxis(times) bar.add_yaxis('成交量', df['volume'].tolist()) grid = Grid() grid.add(line, grid_opts=opts.GridOpts(pos_bottom='25%')) grid.add(bar, grid_opts=opts.GridOpts(pos_bottom='5%')) grid.render('minute_chart.html')

这样生成的HTML文件可以直接用浏览器打开,也可以部署到内网服务器上,随时随地查看。手机端浏览器访问时,交互式图表的缩放和滑动体验比静态图片好很多。

如果你的数据抓取任务是固定每天收盘后执行,建议再用系统定时任务把它自动化:

  • Windows下用“任务计划程序”,指定每天15:05运行Python脚本。
  • Linux/macOS下用crontab,比如5 15 * * 1-5表示周一到周五下午15:05执行。

自动化的另一个好处是能把数据保存成CSV或Parquet格式,长期积累下来就是一份自己的分时数据库。有了历史数据之后,你可以在上面做日内波动统计、分时均价偏离度分析、尾盘异动识别等更深度的研究,这就远远不止“画一张图”那么浅了。

我在实际使用中还有一个体会:无论是做盘中实时的价格监控,还是做盘后的复盘分析,工具始终是辅助,关键还是看你怎么解读这些数据。分时图和K线最大的不同在于它保留了日内交易节奏的细节,价格什么时段走强、什么时段走弱,成交量在哪些关键点位放大,这些信息配合大盘指数和板块走势一起看,往往能帮你发现很多闭着眼睛看日线图发现不了的机会。这套抓取和绘图方案,我是建议你直接拿来改一改跑一跑的,反正代码都在这,跑通了再按自己的标的和风格调整细节就行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询