企业微信

baostock 的并发限制到底卡在哪

不少做股票、期货量化的朋友,从 tushare 或者 akshare 转到 baostock,第一感觉是“免费、数据全、字段规整”。实盘策略还没跑起来,先卡在拉数据这一步——本来想开 10 个线程同时下载全市场历史 K 线,结果程序要么直接抛异常,要么返回一堆空 DataFrame,要么登录就失败。

问题出在 baostock 的并发设计上。它底层是一个 C++ 写的客户端,跟服务端之间维持一条长连接,官方限制非常明确:一个账号同一时间只允许一个连接会话。这条连接一旦被占用,第二个 bs.login() 就会报“网络接收错误”或者“用户未登录”。你在 Python 里开多线程,看起来每个线程都调用了 login,实际上它们共享的是同一个全局 socket 状态,互相踩踏,最后一个都拿不到数据。

baostock并发限制到底怎么破 股票期货量化数据获取有哪些坑

有人试过用 threading.local 给每个线程独立客户端。结论是行不通的,baostock 的状态是模块级的全局变量,不是线程安全的。

为什么很多人误以为可以并发

tushare pro 是按积分给并发额度的,你充了钱,官方允许你开 2 到 5 个并发线程。akshare 本质是爬虫,没有登录态,你开 20 个线程只要别把对方服务器打挂,短期也能跑。习惯了这种模式,再回到 baostock,自然觉得“限制怎么这么多”。

baostock 是免费公益性质的,服务器资源有限。它的设计初衷是给个人投资者做单线程、低频次的历史数据补全,不是给量化平台做高并发数据仓库。

实测出来的三条路

多进程 + 任务队列

最稳的方案是放弃多线程,改用多进程。每个进程独立登录、独立查询、独立登出,进程之间用 multiprocessing.Queue 或者消息队列传递股票代码列表。

控制同时运行的进程数在 2 到 3 个,再多一样会被服务端限流。代码骨架大概长这样,不依赖第三方调度框架:


import baostock as bs

import pandas as pd

from multiprocessing import Pool

def fetch_one(code):

    lg = bs.login()

    if lg.error_code != '0':

        return None

    rs = bs.query_history_k_data_plus(

        code, "date,open,high,low,close,volume",

        start_date='2020-01-01', end_date='2024-12-31',

        frequency="d", adjustflag="2"

    )

    rows = []

    while rs.error_code == '0' and rs.next():

        rows.append(rs.get_row_data())

    bs.logout()

    return pd.DataFrame(rows, columns=rs.fields)

if __name__ == '__main__':

    codes = ['sh.600000', 'sz.000001', 'sh.601318']

    with Pool(2) as p:

        results = p.map(fetch_one, codes)

进程池大小 2 是经验值。开 4 个进程,大概率第三个开始就 login 失败。

分账户错峰

如果你手上有多个 baostock 账号(不同手机号注册),可以给每个进程分配独立账号。服务端限制的是账号维度,不是 IP 维度。同一台机器,两个账号各跑一个进程,互不干扰。

账号切换的时候注意,bs.logout() 要显式调用,否则旧连接会挂在那里占着名额,新账号登录也可能被风控。

本地缓存 + 增量更新

并发限制的本质矛盾是“我要的数据量”和“单连接吞吐量”不匹配。全市场 5000 多只股票,日线数据跑一遍单线程大概 40 分钟到 1 小时。如果你每天都要全量拉一遍,那是自找麻烦。

正确做法是第一次全量下载,存成 parquet 或者 sqlite。之后每天只拉最近 5 个交易日,做增量合并。期货数据同理,主力合约换月频繁,按合约代码加日期做主键,重复数据直接覆盖。

缓存层用 pandas.read_parquet 比读 csv 快 10 倍以上。股票数量多的时候,parquet 的列式存储优势非常明显。

期货数据要注意的额外坑

baostock 对期货的支持比股票弱。它提供的是 query_history_k_data_plus 接口,期货代码格式跟股票不同,比如 sh.000001 是股票,期货要带交易所前缀,且只有日线级别比较稳定。分钟线数据缺失严重,别指望用它做日内策略回测。

做期货量化,数据源建议分层:日线用 baostock 免费拿,分钟线和 tick 数据走 CTP 或者付费数据商。全免费方案里,baostock 的期货字段缺少持仓量和结算价,自己算持仓量变化、基差结构会缺胳膊少腿。

绕开限制的核心思路

baostock 并发限制不是技术故障,是产品定位决定的。你要么接受它的节奏,单线程慢慢跑,用时间换免费。要么把数据获取和策略计算彻底解耦:数据层用多进程 + 队列 + 本地缓存,每天盘后自动更新。策略层直接从本地读 parquet,完全不碰网络。

实盘信号计算对延迟有要求,更不能依赖 baostock 的实时接口。它没有实时行情推送,只能盘后拿日线。盘中做决策的数据必须另找来源。

搞量化,数据管道稳定比策略花哨重要得多。baostock 能用,但要用对姿势。