Tushare避坑指南:从Token权限到数据清洗的五大实战要点
发布时间:2026/10/2 8:57:52来源:尧图网络
在量化交易和数据挖掘这个圈子里Tushare 大概是国内个人开发者接触最多的一套金融数据接口了。从最开始的攻克积分门槛到后来每天定时拉取行情、清洗入库这套流程几乎成了很多人的数据启蒙课。但恰恰是“启蒙”这两个字让不少人走了一段弯路——很多隐藏规则文档里一句带过只有自己踩进去才会发现坑比想象中深得多。今天我把这几年用 Tushare 攒下的问题集中梳理一下不讲那些官网写清楚的基础用法只挑最常见的 5 个坑来拆。这 5 个坑分布在数据获取和数据清洗两个环节每一个我都见过不止一次也帮人排查过不止一次相信对正在折腾 Tushare 的朋友会有帮助。1. 第一个坑Token 与积分权限数据还没拿到就先被卡在门外1.1 Token 获取的正确姿势别再往代码里硬编码了很多新手第一次接触 Tushare最容易卡在 Token 这一步。其实 Token 就是你的身份凭证注册登录 tushare.pro 官网之后鼠标放到右上角头像上下拉菜单里能看到一个“接口TOKEN”入口点进去就能复制到一串很长的字符串。这个字符串就是调用接口的钥匙初始化的时候用import tushare as ts ts.set_token(你的token字符串) pro ts.pro_api()这里想说句实在话我见过不少人在群里直接把 Token 贴在代码里发出来然后被别人拿去白嫖积分额度。Token 这个东西本质上就是你的账户钥匙泄露之后别人能调用你的接口权限额度耗尽你的程序就废了。建议的做法是放到环境变量里比如在.env文件中配置然后代码里通过os.getenv(TUSHARE_TOKEN)读取这样既不会泄露在代码仓库里换机器也方便。另外一个容易忽略的问题是Token 是有积分绑定的。注册之后的基础积分只有 120而不少接口都有积分门槛比如财务三大报表接口一般要求 2000 积分以上。所以初始化成功了不代表所有接口都能用调用之前先去官网文档里查一下目标接口的积分要求不然会白白踩一脚“抱歉您没有权限”的报错。1.2 积分不够接口再好也白搭Tushare 的积分体系刚开始看会觉得有点绕但核心逻辑其实很简单越核心、越精细的数据对积分的要求越高。120 积分的基础账号能用日线行情、股票列表、交易日历这些基础接口但如果想拿财务数据、衍生指标、分钟数据基本上得往上攒积分。攒积分的方式官网写得很清楚无非是完善个人信息、绑定手机号、关注公众号、参与社区活动这些。这套体系确实有点门槛但换个角度想它也是 Tushare 能持续提供稳定服务的商业逻辑。我的建议是动手之前先去官网文档中心把你需要的接口挨个查一遍确认自己当前积分够不够。如果不够别硬等积分增长可以先用手头能拿到的数据把整个流程跑通等积分够了再补充细节数据。注意Token 泄露之后别人挥霍的是你的调用额度轻则限流重则封号。务必像保管密码一样保管 Token。2. 第二个坑接口参数的隐藏规则字符串和日期格式坑到怀疑人生2.1 trade_date 必须是字符串这个细节卡住了一半新人Tushare 的接口参数对类型有严格的要求最典型的就属trade_date了。官网示例里写的是trade_date20240101看起来平平无奇但如果你图省事直接传一个整数 20240101接口立刻就报错。类似的情况还有start_date、end_date全部要求字符串格式且必须是YYYYMMDD这种八位格式带横杠的2024-01-01也不行。这个坑特别隐蔽的地方在于如果你用 pandas 读入日期列然后再传给接口经常会因为类型已经变成了 Timestamp 或 int64 而报错。所以正确操作是在调接口之前统一做一次字符串转换date_str 20240101 df pro.daily(trade_datedate_str)如果你是从 DataFrame 里取日期强烈建议提前用astype(str)转好不要图一时方便直接塞进去否则报错之后你还得回头排查是哪个环节把类型改了纯属浪费时间。2.2 ts_code 的格式也藏着门道另一个参数坑是ts_code。Tushare 的股票代码格式是“代码 交易所后缀”例如平安银行是000001.SZ贵州茅台是600519.SH。上交所的股票以6开头后缀是.SH深交所的股票以0或3开头后缀是.SZ北交所的股票后缀则是.BJ。这个后缀一旦写错比如把上证股票写成.SZ接口要么返回空数据要么报错。有些朋友从网上爬到的股票列表里只有纯代码没有后缀直接拿去调 Tushare 就会一头雾水。解决办法也很简单用pro.stock_basic()拉一次全市场股票列表从里面拿标准的ts_code字段不要自己拼。2.3 参数优先级的坑trade_date 和 ts_code 同时传会怎样第三个隐藏规则是参数优先级。以pro.daily为例这个接口允许你按股票代码拉历史行情也允许你按交易日拉全市场行情。官方文档的规则是ts_code和trade_date两个参数二选一如果同时传入接口会报错。这其实很好理解按关键字段查还是按时间点扫全市场这是两种完全不同的查询模式混在一起语义就会乱。理解了这一点你就知道为什么有些人的程序跑着跑着就报“参数错误”了——大概率是循环里既给了ts_code又在上一次迭代里漏清了trade_date。排查思路也很简单每次都重新构造参数 dict不要复用同一个 dict。3. 第三个坑循环拉取被限流效率瓶颈不在网速在调用姿势3.1 全市场按股票一个个拉慢到想砸电脑先说一个我特别常见到的场景有人要拿全市场几千只股票的日线数据于是写了一个 for 循环挨个给pro.daily(ts_codexxx)发请求。跑起来之后发现速度慢得离谱几千只股票跑完可能要几个小时跑着跑着还频繁报错。这个姿势的问题在于Tushare 日线接口支持按交易日拉取全市场快照。也就是说你只需要传入一个trade_date就能一次拿到当天所有有交易的股票数据根本不需要一股一股地去查。按交易日循环一年也就两百多个交易日比按股票循环几千次要高效太多了。我整理了一个简单的对比拉取方式请求次数耗时估算按股票循环 5000 只5000 次25~50 分钟频繁限流按交易日循环 250 天250 次2~5 分钟稳定不报错差距就是这么大。做全市场数据首选按交易日拉然后再根据ts_code分组使用。3.2 限流机制与重试策略别再闷头撞墙了Tushare 对接口调用频率是有限制的具体的频次和你的积分等级挂钩。低积分账号如果短时间内请求太密集控制台会直接返回“频率限制”之类的错误。这个机制本身是为了保护服务器但对于不熟悉的人而言往往表现为“跑着跑着程序突然中断”或者“明明刚才还能调现在全在报错”。我的做法是两层防护。第一层是主动降速每次请求之间加一个time.sleep(0.2~0.3)宁可慢一点也别触发限流。第二层是做好失败重试捕获异常后先等几秒再重试连续失败好几次才放弃并打印出错的日期方便排查import time def fetch_with_retry(func, **kwargs): for attempt in range(3): try: return func(**kwargs) except Exception as e: print(f第 {attempt 1} 次请求失败: {e}) time.sleep(2) return None3.3 分段缓存防止拉到一半全部重来还有一个效率相关的经验是拉数据一定要做增量缓存。比如你要拉过去五年的日线数据一次性全拉完当然可以但只要中途网络抖一下、程序崩一下之前拉的全白费了。更稳的做法是按年或按季度分段拉取每一段拉完就落盘成 CSV 或 Parquet 文件最后再统一读出来合并。这样就算中断也只需要补跑缺失的那几段不用从头再来。4. 第四个坑复权计算搞错一次回测结果全得推翻4.1 不复权、前复权、后复权到底该用哪个Tushare 的pro.daily接口返回的是不复权价格这一点很多人在一开始根本没注意到。不复权价格有个很显著的特征遇到股票除权除息的日子价格会突然向下跳空但成交量、市值这些其实没变。如果直接用不复权价格计算收益率回测结果会惨不忍睹尤其遇到分红送股比较多的股票历史收益会被严重扭曲。复权本质上就是把除权除息造成的价格断层修补起来让你看到一条连续的、反映真实涨跌的曲线。前复权是以当前价格为基准把历史价格向下调整后复权是以历史价格为基准把当前价格向上调整。两者的最终计算结果是等价的但有个重要的区别前复权价格会随着时间推移、最新价的变化而不断变化也就是说同一段历史数据你上个月下载的前复权价和这个月下载的前复权价可能是不同的。所以在回测中我更推荐用后复权价格因为它确定性强历史数据一旦算出就是固定的不会因为今天股价涨了就被整体重写。4.2 用复权因子计算前复权和后复权价格新版 Tushare Pro 接口里pro.daily是没有adj参数可以直接返回复权价的。正确做法是再调一次pro.adj_factor拿到复权因子然后自己计算。复权因子的计算逻辑是后复权价格 不复权收盘价 × 当日复权因子前复权价格 不复权收盘价 × 当日的复权因子 / 最新交易日的复权因子代码如下import tushare as ts import pandas as pd pro ts.pro_api() # 1. 拉取不复权行情 df pro.daily(ts_code000001.SZ, start_date20230101, end_date20240101) # 2. 拉取复权因子 adj pro.adj_factor(ts_code000001.SZ, start_date20230101, end_date20240101) # 3. 合并 df df.merge(adj[[ts_code, trade_date, adj_factor]], on[ts_code, trade_date]) df df.sort_values(trade_date).reset_index(dropTrue) # 4. 计算复权价 latest_factor df[adj_factor].iloc[-1] df[close_qfq] df[close] * df[adj_factor] / latest_factor # 前复权 df[close_hfq] df[close] * df[adj_factor] # 后复权这段代码交付出去之后我经常会补一句提醒做因子研究时涉及价格的一定要检查一下复权口径。我遇到过一个把前复权当后复权存库的案例结果某只股票在分红之后的历史收益曲线完全对不上排查了很久才发现是复权因子除以最新因子这一步多算了一次。这种错误一旦发生回测结果就是错的再好看的策略曲线也是空中楼阁。注意如果只需要做收益率计算还有一种更不容易出错的思路——直接用 Tushare 返回的pct_chg字段。但需要注意这个字段同样基于不复权价格计算除权日那天的涨跌幅会异常。稳妥起见还是用复权后的收盘价自己算一遍收益率。5. 第五个坑数据清洗的隐形陷阱trade_date 居然变成了浮点数5.1 读取 CSV 后日期列变成浮点数或者整数数据拉取下来之后很多人习惯直接to_csv保存等下次要用的时候再read_csv读回来。这个操作看起来人畜无害其实埋了一个大雷如果你没有显式指定 dtypepandas 在读 CSV 的时候会把trade_date这种全是数字的列自动解析成 int64而一旦这一列里有空值整个列就会在读取时变成 float64。最终你看到的trade_date不是20240101而是20240101.0。这个坑特别隐蔽原因是程序不会报错但后续所有基于日期的字符串操作、拼接、比较都会出问题甚至你根本察觉不到。解决办法有两个任选其一# 方法一读入时指定列类型 df pd.read_csv(daily.csv, dtype{trade_date: str}) # 方法二读入后统一转换 df[trade_date] df[trade_date].astype(Int64).astype(str).str.replace(.0, )更稳健的做法是不要用 CSV 存中间结果改用 Parquet 格式这样可以在存储时把字段类型固定好避免反复转换带来的数据隐患。刚开始可能不习惯但用一次就回不去了。5.2 数据去重与排序一次到位别拖泥带水Tushare 接口在正常情况下返回的数据不会重复但在自己拼接数据的时候很容易引入重复行尤其是按交易日拉全市场然后多个日期数据做 concat如果中途补跑了一段、重复拉了某一区间最后拼出来的 DataFrame 就会有重复记录。这种重复很难用肉眼发现但一旦进入计算最后统计出来一个离谱的数字你又得回头查数据质量。所以合并之后立刻做一步去重和排序是成本最低的保险df df.sort_values([ts_code, trade_date], ascending[True, True]) df df.drop_duplicates(subset[ts_code, trade_date], keeplast) df df.reset_index(dropTrue)排序和去重的顺序有讲究先排序再去重可以保证keeplast留下的是时间更靠后的记录。如果你先去了重再排序谁会被留下就完全随机了。5.3 缺失值的处理不是所有 NaN 都该用 0 填充最后一个清洗层面的坑是关于缺失值的。Tushare 返回的数据里出现 NaN 是很正常的原因各不相同。比如某只股票当天停牌按交易日拉全市场时就可能没有这一天的数据又比如某些次新股上市较晚在上市之前当然没有行情记录。有些人图省事拿到数据之后直接fillna(0)这种做法非常危险。价格列是 0 的话计算收益率时会直接出现 -100% 的荒谬数值成交量是 0 的话一些流动性指标也会被污染。正确的思路是区分数据是“真的没有”还是“应该是 0”。价格、市值、成交额这些字段出现 NaN大部分情况都应该用前值填充或者剔除该行涨跌幅、成交量这类字段如果是停牌导致的填充为 0 是可以接受的但需要明确记录这一步。另外如果想做面板数据经常需要把宽表转成长表或者反过来。这种转换过程中 pandas 的pivot会自动产生 NaN这时候的 NaN 代表“该股票在该时间点没有交易数据”处理方式和上面类似先想清楚业务含义再动手。6. 可直接抄作业的完整代码框架前面把坑拆开讲了这一节给出一套完整的、可以直接拿来改改就用的流程把获取、清洗、保存串联起来。注意替换成自己的 token并根据实际需要调整起止日期。import tushare as ts import pandas as pd import time import os # ---------- 初始化 ---------- ts.set_token(os.getenv(TUSHARE_TOKEN)) pro ts.pro_api() # ---------- 获取交易日历 ---------- cal pro.trade_cal(exchangeSSE, start_date20240101, end_date20240201, is_open1) trade_dates cal[cal_date].tolist() print(f共 {len(trade_dates)} 个交易日) # ---------- 按交易日拉取全市场日线行情 ---------- all_daily [] for date in trade_dates: try: df pro.daily(trade_datedate) if df is not None and not df.empty: all_daily.append(df) time.sleep(0.3) # 主动降速避免触发限流 except Exception as e: print(f{date} 拉取失败: {e}) time.sleep(2) # ---------- 合并与清洗 ---------- result pd.concat(all_daily, ignore_indexTrue) result[trade_date] result[trade_date].astype(str) result result.sort_values([ts_code, trade_date], ascending[True, True]) result result.drop_duplicates(subset[ts_code, trade_date], keeplast) result result.reset_index(dropTrue) # ---------- 计算后复权价如果需要 ---------- # 说明全市场批量计算复权时建议先按 ts_code 分组再逐组调用 adj_factor # 下面的代码是单只股票的示例批量场景请在此基础上用 groupby 封装 # ---------- 保存 ---------- result.to_csv(daily_data.csv, indexFalse, encodingutf-8-sig) print(f清洗完成共 {len(result)} 条记录)这套代码里每行都有它的作用不是花架子。比如time.sleep(0.3)就是专门用来对抗限流机制的先排序再去重是为了确保留下后面那条数据encodingutf-8-sig是为了在 Excel 里打开 CSV 不乱码。这些细节单拎出来看都不起眼但结合起来就是一套非常耐用的数据拉取管道。7. 常见问题速查表附排查思路最后把上面提到的坑整理成一张速查表方便以后遇到问题直接对号入座。现象原因解决方案调用接口报“抱歉您没有权限”当前积分不满足接口要求先去官网文档查目标接口的积分门槛再确认自己的积分报错提示参数类型错误trade_date传了 int 或 Timestamp统一用YYYYMMDD格式字符串用astype(str)提前转换循环拉取全市场数据太慢按股票循环请求次数爆炸改成按交易日循环一次拿当天全市场数据跑着跑着大量请求失败触发每分钟调用频率限制每次请求之间加time.sleep(0.2~0.3)并做好重试机制收益率计算结果异常出现剧烈跳变除权日数据未复权用adj_factor计算前复权或后复权价格后再计算收益率读回 CSV 后trade_date变成20240101.0列类型被 pandas 推断为 float读取时指定dtype{trade_date: str}或改用 Parquet 格式存储数据量对不上疑似有重复concat 时区间重叠或重复拉取用drop_duplicates(subset[ts_code, trade_date])去重某只股票在某天没有记录停牌或未上市按业务含义决定是前向填充还是剔除不要所有 NaN 一刀切填充为 0这张表不是给你背的而是建议你收藏起来等哪天程序出问题的时候翻一翻。数据获取和数据清洗这个领域很多问题不是逻辑多复杂而是细节太多一步没注意就会绕远路。我这几年用 Tushare 攒下来的经验说到底也就一句话在做任何计算之前先确认数据是怎么来的、经过了哪些转换、类型是什么、有没有重复和缺失。数据管道建得越稳后面做分析和建模就越省心。另外多说一句Tushare 的功能不止日线行情这一块像资金流向、龙虎榜、财务指标这些接口都是好东西等基础流程跑通之后可以慢慢往上加。数据源本身只是一个起点怎么把数据变成可靠、干净的分析素材才是真正需要花时间打磨的核心能力。
网站建设高端定制企业官网