尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Tushare量化数据接口实战:从注册到复权计算的完整指南

Tushare量化数据接口实战:从注册到复权计算的完整指南 我玩了几年量化踩过数据源的不少坑回头一看用得最顺手的还是Tushare。今天就以一个老用户的身份把这套工具从注册、取数到复权计算完整捋一遍。不管你是刚接触量化投资的新手还是已经写了几年策略的老手只要跟股票数据打交道这篇文章应该都能帮你少走点弯路。先说结论Tushare是目前国内用得最广的Python财经数据接口包它把A股、基金、期货、宏观经济等一大堆数据统一封装好了你不用自己去爬网页、解析HTML一条pip install tushare装完写几行代码就能把数据拉到DataFrame里。尤其做量化回测、选股分析、财务因子研究的人几乎绕不开它。但网上教程大多停留在“怎么安装、怎么调用daily接口”的层面真正把积分机制、复权算法、数据坑点讲透的很少所以我打算一次补齐。1. Tushare到底是个什么工具——先用大白话把概念捋清楚1.1 Tushare能做什么Tushare本质上是一个数据服务中间件你通过Python调用它的接口它能帮你拿到几大类数据股票行情日线、周线、月线、分钟线实时行情快照。基础信息上市公司基本信息、股本结构、行业分类、高管信息。财务数据资产负债表、利润表、现金流量表、财务指标、业绩预告。参考数据分红送股、复权因子、融资融券、限售解禁。宏观经济GDP、CPI、PPI、货币供应量、利率汇率。期货期权期货日线、合约信息、期权行情。基金数据基金净值、基金持仓、ETF行情。老实说单靠这一个库基本能覆盖一个散户量化研究者80%以上的数据需求。我最早是用爬虫自己抓新浪财经、东方财富的接口那时候网页结构一改代码就废一片维护成本高得离谱。后来换了Tushare整个人都轻松了。它的数据格式统一返回Pandas DataFrame做数据分析、回测、可视化都是无缝衔接。1.2 为什么量化分析会选它而不是其他数据源市面上的数据源不少各有各的脾气。我简单对比一下自己用过的几个方便你理解为什么Tushare能成为很多人的默认选择。数据源优点缺点适合人群Tushare Pro数据全、文档清晰、社区活跃Python直接集成新用户积分限制较多大多数个人量化研究者AkShare完全免费、接口多靠爬虫聚合稳定性一般数据来源变更会导致接口失效预算为零、能接受偶尔修bug的人Baostock免费、无需注册数据覆盖面较窄更新频率一般做基础教学、简单回测Wind/Choice数据质量极高、更新及时贵个人用户基本不划算机构或专业投资者我自己最早用的其实是AkShare但它有个问题数据源分散经常今天能用明天就挂。Tushare Pro虽然要积分但接口稳定、改版频率低数据一致性做得好对做量化回测的人来说“稳定”比“免费”值钱太多。2. 上手准备注册、积分与Token获取全流程2.1 官网注册与实名认证Tushare的官网是tushare.pro第一次去先注册账号。注册流程很简单手机号加验证码就行但有一件事必须做实名认证。别嫌麻烦不完成实名认证很多接口根本不给权限。注册登录后在个人主页里能看到“接口TOKEN”这个选项生成一串以字母数字组成的Token。这就是你调用数据接口的钥匙后面所有pro_api()都要用到它。我建议把这串Token放到环境变量或者独立的配置文件里别直接写死在代码里尤其是你的代码要传到GitHub上的时候Token泄露了别人就能白嫖你的接口额度。2.2 积分机制深度解读Tushare Pro从2.0版本起改成积分制这是很多新手最容易懵的地方。点开接口文档每个接口旁边会标明需要的积分门槛比如stock_basic基础接口120积分可用。daily日线行情120积分可调但有频率限制。adj_factor复权因子120积分可调。income、balancesheet、cashflow三大财务报表需要2000积分。分钟数据通常需要5000积分以上。新注册用户默认给120积分意味着你可以跑通基本流程但做深度研究就会碰壁。提高积分的方法有几种注册完善资料、关注公众号能拿一点。社区发帖、分享接口使用心得官方会奖励积分。直接捐赠相当于付费解锁更高级别的接口和更高频率。我个人的建议是如果你只是学习、做做简单回测120积分够用但如果你想认真做财务因子研究、分钟级别策略别纠结了直接充个最低档的捐赠省下来的时间比那点钱值钱多了。这和买软件一样核心价值是省时间而不是省钱。2.3 安装与首次连接测试安装没什么好说的pip install tushare如果你在中国大陆建议直接用清华镜像源pip install tushare -i https://pypi.tuna.tsinghua.edu.cn/simple装好后验证一下连接是否正常。这里有一个很关键的小细节pro_api()只是建立了客户端并不会真正校验Token有效性只有实际调用接口才会返回结果。所以首次测试一定要真的拉一次数据。import tushare as ts # 设置token并初始化pro接口 pro ts.pro_api(你的token填在这里) # 尝试拉取平安银行最近5个交易日数据 df pro.daily(ts_code000001.SZ, start_date20250101, end_date20250201) print(df.head())ts_code是Tushare的股票代码格式深市股票在6位数字后加.SZ沪市加.SH。比如平安银行是000001.SZ贵州茅台是600519.SH。这个格式跟你在行情软件里看到的纯数字不一样刚用的人经常搞错一调就是空数据还以为是自己Token有问题。3. 核心实操用Tushare拉取数据的完整姿势3.1 先从最简单的接口开始股票列表做任何量化研究第一步永远是拉一份全市场的股票列表。stock_basic这个接口返回的就是A股上市公司的基本信息包括代码、名称、行业、上市日期、退市日期等。# 获取全部A股股票列表 stock_list pro.stock_basic(exchange, list_statusL, fieldsts_code,symbol,name,area,industry,list_date) print(stock_list.head()) print(f总共 {len(stock_list)} 只股票)参数说明一下exchange交易所代码空字符串代表上海和深圳都包含。list_status上市状态L代表仍在上市D代表已退市P代表暂停上市。fields你要哪些字段不传就返回所有默认字段。建议按需指定字段能少传不少流量。这里有个小坑list_statusL只包含当前还在交易的股票如果你做回测的时间区间覆盖了2018年而某只股票2020年退市了那么用L过滤后这只股票就不会出现回测就会产生严重的幸存者偏差。正确的做法是拉取list_statusD的退市股票和L合并起来再按list_date筛选出你回测区间内的股票。3.2 日线行情获取实战日线是几乎所有策略的基础数据。daily接口返回的是不复权的价格这是很多新手理解偏差最大的地方后面我专门讲复权先看基本用法。# 获取单只股票某个时间段日线数据 df pro.daily(ts_code000001.SZ, start_date20240101, end_date20241231) print(df.columns.tolist()) # [ts_code, trade_date, open, high, low, close, pre_close, change, pct_chg, vol, amount]字段含义分别是股票代码、交易日期、开盘价、最高价、最低价、收盘价、昨收价、涨跌额、涨跌幅、成交量手、成交额千元。注意两点返回结果是按trade_date倒序排列的最新的日期在最前面。很多新手直接拿这个DataFrame去算移动平均结果顺序是反的指标全算错。vol单位是手如果你要算换手率或结合流通股本计算成交量需要做单位换算。amount单位是千元不是元。正确做法是用sort_values排一下序df df.sort_values(trade_date).reset_index(dropTrue)调daily接口一次最多返回多少行官方文档没有严格写死但实测一次拉一只股票整年的数据是没问题的不过如果拉全市场某一天的数据daily是按trade_date维度返回的你需要先拿到交易日历再去循环每一天不能一把梭。# 获取2024年全年交易日历 cal pro.trade_cal(exchangeSSE, start_date20240101, end_date20241231, is_open1) trade_days cal[cal_date].tolist() # 逐日拉取全市场日线数据示例只拉前3天 for day in trade_days[:3]: df pro.daily(trade_dateday) print(f{day}: {len(df)} 只股票)这个循环逻辑非但适用于日线也适用于后面很多按日期做快照的接口。核心思路就是Tushare的很多接口是按“单只股票”或“单日”维度返回的自己组装时要有意识地做循环。3.3 把数据存下来本地落盘与增量更新数据拉到内存里不算完做研究时肯定要存到本地。我个人比较推荐的方案是存到SQLite或Parquet文件不推荐直接用CSV因为CSV在追加写、类型推断上有太多坑。如果你的环境允许装数据库用SQLite最轻量。我用一个最简单的例子说明怎么持久化import sqlite3 import pandas as pd def store_daily(df, db_pathstock_data.db): conn sqlite3.connect(db_path) df df[[ts_code, trade_date, open, high, low, close, pre_close, vol]].copy() df.to_sql(daily_data, conn, if_existsappend, indexFalse) conn.close()因为daily接口返回的数据是倒序的存入SQLite后每次读取时都按trade_date排序或者建表时加一个UNIQUE(ts_code, trade_date)约束这样重复拉取数据时不会产生重复记录。增量更新的思路是记录每只股票本地最新的trade_date下次拉取时从那个日期往后拉几天做一个“补数”。这样既省接口次数又能保证数据不断档。4. 复权计算搞懂前复权、后复权与不复权的区别4.1 为什么一定要复权我见过不少新手直接拿daily接口的收盘价去算收益率曲线画出来一看突然某天价格暴跌50%还以为是股灾结果一看是除权除息。股票分红送股后价格会做一个“除权调整”如果不复权历史价格就不连续算出来的收益率、技术指标全部失真。举个例子某股票10元10送10除权后股价变成5元。如果你拿原始价格算这一天收益率是-50%但你的实际资产没有任何变化因为你持有的股数翻倍了。所以做量化回测必须复权。4.2 前复权与后复权的计算逻辑复权分两种前复权和后复权。我用最通俗的话解释后复权以上市首日价格为基准把之后所有的价格都做调整保持历史价格不变当前价格被“放大”。后复权价格走势代表买入并一直持有不动分红再投入的真实收益曲线。前复权以最新交易日价格为基准把历史价格做调整让历史价格看起来像“被压缩”过。当前价格就是现实的成交价而历史价格是经过复权调整的虚拟价。Tushare官方给了一个adj_factor接口返回的是累积复权因子。计算方式为后复权价 不复权价 × 复权因子 前复权价 不复权价 × 复权因子 / 最新复权因子这里的“最新复权因子”指的是你研究区间内最后一个交易日的复权因子。如果一只股票从未分红送股复权因子恒为1前后复权和不复权都一样。但A股大部分股票长期都有分红或送转复权因子基本都是大于1的而且随交易日在缓慢增长。4.3 用Tushare数据实际算一遍光说理论不够我来演示真实操作。以平安银行000001.SZ为例我用Tushare拉它的日线数据和复权因子然后手动计算前复权和后复权价格。import tushare as ts import pandas as pd pro ts.pro_api(你的token) # 拉取不复权日线数据和复权因子 df pro.daily(ts_code000001.SZ, start_date20240101, end_date20241231) adj pro.adj_factor(ts_code000001.SZ, start_date20240101, end_date20241231) # 合并 df df.merge(adj[[trade_date, adj_factor]], ontrade_date, howleft) df df.sort_values(trade_date).reset_index(dropTrue) # 最新复权因子 latest_adj df[adj_factor].iloc[-1] # 计算前复权价格 df[close_qfq] df[close] * df[adj_factor] / latest_adj # 计算后复权价格 df[close_hfq] df[close] * df[adj_factor] print(df[[trade_date, close, adj_factor, close_qfq, close_hfq]].head())注意看结果最后一天的前复权价格等于当天的收盘价因为当天的复权因子除以自己等于1。而后复权价格会比不复权价格大很多这是因为平安银行上市以来多次分红送股叠加了大量复利效应。如果你不想自己算Tushare老版本接口ts.pro_bar可以直接返回复权数据# 直接用pro_bar拉前复权数据 df_qfq ts.pro_bar(ts_code000001.SZ, adjqfq, start_date20240101, end_date20241231)但我的建议是先理解adj_factor的计算逻辑再用pro_bar偷懒。因为pro_bar虽然方便但它只返回价格、成交量这些基础字段且不会返回复权因子本身。做因子研究时很多时候你需要自己保留复权因子做对齐计算只用pro_bar反而限制了你对数据的掌控力。5. 常见问题与避坑指南5.1 积分不够很多接口调不了怎么办这是Tushare社区里被问烂了的问题。我的答案分两种情况如果只是学习和验证想法120积分其实能覆盖不少核心数据比如daily、adj_factor、stock_basic、trade_cal都够用。财务数据、分钟数据这些高门槛接口用不了就用AkShare来回补或者直接问社区要别人整理好的样例数据。很多人的策略研究根本用不到全市场分钟线日线级别的数据已经能做大量有效分析了。如果你确认自己就是要做深度研究财务三表、分钟线都是刚需那就别浪费时间在攒积分上了。看看Tushare的捐赠方案花点小钱换取稳定数据服务这笔账怎么算都划算。你自己的时间成本远比这点投入高。5.2 数据频率限制与超时处理Tushare Pro对接口调用频率有严格限制即使积分达标也有每分钟多少次的上限。实测常见限制是每分钟调用不超过几百次批量拉取时特别容易触顶然后返回“抱歉您每分钟最多访问该接口X次”的错误。解决方案就是加限速和重试机制。我在批量拉数据时一般会写个带time.sleep的循环import time import random def fetch_with_retry(func, retries5, *args, **kwargs): for i in range(retries): try: return func(*args, **kwargs) except Exception as e: if 频率 in str(e): wait_time 2 ** i random.uniform(0, 1) print(f触发频率限制等待 {wait_time:.2f} 秒) time.sleep(wait_time) else: raise e raise Exception(重试多次仍失败)这里用了指数退避策略第一次等待2秒左右第二次4秒第三次8秒依此类推。实际批量拉取几千只股票时可以配合time.sleep(0.2)这种基础的均匀限速避免请求集中在同一秒内发出。5.3 缺失值与空值的处理技巧daily接口对停牌股票的处理是直接返回空也就是说某只股票当天停牌这个交易日就不会出现在返回结果里。做面板数据时你需要自己把缺失的日期补上填充方式要看具体研究场景做收益率计算时停牌日不应该被当作收益率为0因为停牌期间你无法按市价交易更合理的做法是往后顺延直到复盘日再计算。做技术指标时如果是指数平滑类指标可以用前向填充ffill把停牌前的收盘价延续到复牌前保证指标连续性。基本面数据本身不要求连续净利润、营收等是季度维度的合并到日线时前向填充是常规操作。我用一个比较规范的填充示例# 生成完整交易日序列 trade_cal pro.trade_cal(exchangeSSE, start_date20240101, end_date20241231, is_open1) all_days trade_cal[cal_date].tolist() # 假设df是某只股票的日线 df df.set_index(trade_date).reindex(all_days).reset_index() df[close] df[close].fillna(methodffill) # 前向填充收盘价 df[vol] df[vol].fillna(0) # 停牌日成交量填0注意填充和填0要区分开。价格类字段适合填前值成交量这类适合填0因为停牌确实没有成交。如果把价格填0后面的收益计算会直接爆掉。这些细节都是实打实踩坑后总结出的经验。6. 我的几个独家使用心得最后再分享几个我用Tushare几年下来沉淀的小技巧这些技巧未必写在官方文档里但对日常研究很有帮助。第一拉数据前先想清楚字段。daily接口如果不指定fields会返回全部字段这看起来没什么但批量拉取大量股票时多传的字段会明显拖慢接口响应速度。我习惯每次调用前都明确fields参数只取自己需要的列。数据量大了以后这些细节就是性能瓶颈。第二财务数据日期对齐别用公告日期要用报告期。Tushare的财务接口里通常有ann_date公告日期和f_ann_date实际披露日期做事件研究时要用f_ann_date因为只有实际披露后数据才可获取。如果拿公告日期做对齐容易出现未来数据泄露回测成绩虚高。第三如果你在写稳定的策略框架建议在Tushare外面再包一层自己的数据层。这样哪天换数据源或者接口改了字段你只需要修改这一层而不必改策略逻辑。我之前就吃过一次亏直接在策略代码里调pro.daily()结果有段时间官方改了频率限制我的自动更新脚本跑一半全挂了连夜改代码。后来把数据访问统一封装到底层再也没操心过这类问题。Tushare这个工具说简单也简单就是一个Python库说复杂也复杂里面积分规则、复权算法、接口频率、数据对齐这些细节都值得认真研究。希望这篇文章能让你少踩一些我踩过的坑把时间真正花在策略研究上而不是跟数据较劲。
返回列表