☰
Tushare数据源实战:复权因子与交易日历如何决定回测真实性
2026/10/14 6:19:52 网站建设 项目流程

1. 从“随便拉个行情”到“认真对待数据源”

先说个实话:我最早用Tushare,纯粹是因为免费、不用装数据库、pip install 一下就能跑。那时候我连“量化”都谈不上,就是写个python脚本查K线,看某只股票最近涨了多少,然后对着屏幕感慨一句“卧槽,又错过了”。

但真正让我开始认真对待它的,是一次让我印象很深的翻车经历。当时我在做一个简单的动量回测,用的还是某免费行情接口,结果跑出来某只股票连续三天涨停,策略显示收益爆炸。我当时还挺兴奋,后来一核对原始数据才发现,那个接口把前复权处理错了,分红除权那天的价格直接跳了个大坑,动量因子算出来全是假的。从那天起我才意识到:行情数据这玩意儿,不是“能拿到”就行,而是“拿得对”才行。

于是我开始认真用Tushare,不只是拉K线,而是把它当作一套完整的数据基础设施来对待。这套工具解决的不只是“有没有数据”的问题,更是“数据能不能直接用于分析”的问题。它有相对完整的接口体系,覆盖了行情、财务、资金、指数、期货、基金等一大堆数据域,而且返回的是干净的 pandas DataFrame,接进分析流程几乎是零成本。

这篇文章我不打算写成文档翻译,也不打算把所有接口都列一遍。我想从一个实际做数据分析的人的角度出发,分享几个我真正高频在用的用法,以及我在用的时候踩过的坑、总结出的经验。

无论你刚接触量化,还是已经在写策略但总感觉数据哪里不对,这篇文章应该都能给你一些参考。我不劝你非得用Tushare,但如果你已经在用它,希望你把它用得更明白一点。

2. 为什么数据源会成为策略的隐形天花板

大多数人刚开始做量化的时候,最容易忽略的问题就是:策略逻辑写得再漂亮,数据不对,一切都是负功。我甚至见过有人花了两周调参数,最后发现是复权方式搞错了,整个回测曲线都是在“幻觉”上建立的。

2.1 行情数据不只是“价格”,而是“价格+时间+规则”三维结构

很多人以为拿到的日线数据就是每天的开高低收量,其实远远不止。它背后包含了几套时间规则:交易日历、停复牌、除权除息、涨跌停限制,这些东西没有一个直接体现在K线图里,但每一个都会扭曲你的分析结果。

举个例子,如果一只股票在5月10日除权,每10股派5元,那么5月9日的收盘价和5月10日的开盘价之间存在一个人为的“断层”。如果你直接拿这两个价格算收益率,会得到一个根本不存在的跌幅。Tushare的复权因子接口(adj_factor)就能解决这个问题,你可以在拉日线数据时直接指定复权方式,也可以在拿到原始数据之后自己通过复权因子换算。

这里我建议你形成一套自己的数据规范,而不是每次临时选参数。我自己的标准是:

  • 做因子计算和策略回测时,统一用前复权价格;
  • 做事件分析、计算实际涨跌幅、统计涨停时,用不复权价格;
  • 做长周期估值比较时,用后复权价格或复权因子。

这三种复权方式的应用场景完全不同,混用是新手最容易犯的错误。

2.2 Tushare 的数据分层:接口权限与积分体系是怎么回事

第一次用Tushare的人可能会困惑:为什么有些接口能调通,有些接口报错说权限不够?这就要说到它的积分体系了。

简单理解,Tushare把接口按数据质量和频率分成了几个等级。基础的日线行情、交易日历,注册后默认就能用;而财务数据、资金流向、分钟线数据,则需要一定积分才能调用。积分可以通过完善个人信息、参与社区活动、或者做贡献来获取,不是只能用钱买。

我用下来的感受是:积分体系虽然多了一道门槛,但客观上也保证了数据服务的稳定性。如果所有接口都完全免费无限制开放,服务大概率撑不住高并发,最后大家都没得用。所以我的建议是:

  • 新手期先把免费的日线、复权因子、交易日历、基础财务用熟;
  • 每个接口调通后,先打印前几行数据,确认格式和预期一致;
  • 项目跑通后,再去申请更高权限的接口,避免一上来就卡在权限上。

2.3 数据本地化:为什么我强烈建议你把数据落到本地

Tushare的接口虽然好用,但我从来不在策略回测里直接一遍遍实时请求数据。原因很简单:慢、不稳定、还可能被限频。

我现在的做法是:写一个本地数据维护脚本,定时从Tushare拉取增量数据,存到本地(我用的SQLite,轻型项目完全够用),回测时候只读本地数据,干净又快速。

这个过程有点像你做饭之前先把菜洗好切好,而不是每次炒菜的时候临时去菜市场买菜。虽然多做了一步准备,但后续所有环节都顺畅多了。

3. 几个我高频在用的 Tushare 实用用法

3.1 获取交易日历:做时间序列分析的地基

做数据分析的人最容易忽略的就是交易日历。多数人直接在行情数据里取trade_date字段,然后当日期索引用。但这种做法在涉及跨市场对比、长假前后、调休补班的时候就会出问题。

Tushare 的trade_cal接口提供了完整的交易日历,包含沪深交易所的开市闭市状态。我的用法是:

import tushare as ts import pandas as pd ts.set_token('你的token') pro = ts.pro_api() # 获取2020年至2024年的交易日历 cal = pro.trade_cal(exchange='SSE', start_date='20200101', end_date='20241231') cal = cal[cal['is_open'] == 1]['cal_date'].tolist()

拿到这个列表之后,我做所有时间序列重采样、节假日过滤、窗口计算,都以它为准,而不是靠pd.bdate_range()硬猜。A股有调休、有临时休市,交易日历这个东西,看起来不起眼,却是最容易埋坑的地方。

3.2 日线行情的两种姿势:单只股票拉取 vs 批量获取

Tushare 的daily接口是大多数人的入门接口,但它默认是单只股票单次拉取,如果你要分析全市场几千只股票,一个个循环请求又慢又容易被限频。

我分享两个我经常用的姿势:

姿势一:单标的精细分析

df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')

这个方法适合你只需要分析某几只股票的时候,比如你盯紧了某只票准备建仓,或者对某个行业里的龙头做标的对比研究时用。

姿势二:按交易日批量拉全市场

df = pro.daily(trade_date='20241201')

这是我觉得Tushare最有价值的用法之一:按交易日拉取全市场当天所有股票的交易数据,一天一天地拉,然后落库。这样你能拿到一个整齐的截面数据,特别适合做横截面因子分析。

我会把每次拉到的数据做增量更新,存成本地表格,然后按trade_date分区管理。这样即使后续需要回溯历史某一天的全市场数据,也只从本地读,不需要重新请求接口。

3.3 复权因子:决定你回测真实性的“隐形开关”

前面提过,复权因子很重要。Tushare 提供了专门的adj_factor接口,用法也很简单:

adj = pro.adj_factor(ts_code='000001.SZ', start_date='20230101', end_date='20231231')

拿到复权因子之后,你可以在本地计算出前复权价格。计算公式非常简单:

前复权价 = 不复权价 × 当前最新复权因子 ÷ 当日复权因子

这个公式网上很多地方都有,但真正去自己算一遍的人不多。我建议你哪怕用现成接口,也自己在本地跑一遍这个计算流程,因为只有自己写过一遍,你才能真正理解复权是怎么回事,以后遇到数据异常才能有排查方向。

3.4 财务指标获取:别再用爬虫去抓财报数据了

早期我为了拿财务数据,写爬虫去财经网站上抓。结果财务报表页面三天两头改版,抓下来的数据字段经常错位,清洗起来极其痛苦。后来换到Tushare的财务接口,同样是用python,代码量少了几十倍,数据字段规范,直接就是结构化表格。

# 获取某公司的基础财务指标 income = pro.income(ts_code='000001.SZ', period='20231231')

我用得比较多的几个财务接口是:

  • income:利润表核心数据,适合做盈利能力分析;
  • balancesheet:资产负债表,适合做偿债能力和资产结构分析;
  • cashflow:现金流量表,适合做经营质量分析;
  • fina_indicator:整理好的财务指标,比如ROE、毛利率、负债率等,直接可用于选股因子。

我的经验是:直接用fina_indicator做初筛,再回到三张报表去验证细节。比如fina_indicator显示ROE很高,我会去利润表里看这个ROE是靠主营业务利润撑起来的,还是靠投资收益、资产处置收益撑起来的。这个验证步骤很重要,能够帮你避开很多高ROE陷阱。

3.5 资金流向与龙虎榜:追踪异常波动背后的资金动向

资金流向数据是很多做短线或者事件驱动策略的人很依赖的数据。Tushare提供了moneyflow接口,可以查看个股的主力资金净流入流出情况。还有一个我非常喜欢用的接口是top_list,也就是龙虎榜数据。

龙虎榜数据能帮你看到一只股票异常放量或涨停背后,到底是哪些营业部在买、哪些在卖。虽然这个数据不能直接告诉你明天涨不涨,但能帮你理解当前市场参与者的结构,尤其当你的策略跑出来一个异常信号时,龙虎榜经常能解释“为什么”。

我一般这样结合使用:

  • 先用资金流向接口看连续几天的主力资金净流入,标记资金异动;
  • 再用龙虎榜确认是否有知名游资或机构席位参与;
  • 最后结合基本面做判断,不盲目跟风。

4. 实操细节:把 Tushare 用得更稳更顺的经验

4.1 自定义本地数据仓库的搭建思路

既然前面提到本地化,我就分享一下我的具体操作方案。我用SQLite存数据,表格结构按使用习惯设计成下面这样:

import sqlite3 import tushare as ts import pandas as pd ts.set_token('你的token') pro = ts.pro_api() # 初始化本地数据库 conn = sqlite3.connect('market_data.db') # 拉取单日数据并写入本地 df = pro.daily(trade_date='20241201') df.to_sql('daily_data', conn, if_exists='append', index=False)

这里有几个关键设计:

  • 每个交易日跑一次增量更新脚本,只拉当天数据;
  • 主键设置为ts_code + trade_date,防止重复入库;
  • 定期清理无用字段,节省存储空间;
  • 回测的时候只读取daily_data表,不依赖网络。

4.2 限频问题怎么破

Tushare接口对单次调用频率有限制,具体频率和你积分相关。我最开始不懂,写了个循环一次性拉500只股票的历史数据,结果跑到一半就被限制了,后面全部失败。

后来我总结了两个破解思路:

  • 批量接口优先。能用trade_date参数一次拉全市场的,就绝不循环单股拉取;
  • 控制请求节奏。在批量循环里加入time.sleep(),给接口留出缓冲时间。
import time ts_codes = ['000001.SZ', '600000.SH', '300750.SZ'] for code in ts_codes: df = pro.daily(ts_code=code, start_date='20240101', end_date='20241231') print(f'{code}: {len(df)} 条') time.sleep(0.5) # 控制请求频率

4.3 数据质量校验:拿到数据先别急着分析

这是我个人吃过不少亏之后养成的习惯。任何数据接口都可能出问题,可能是网络传输丢失、可能是源站数据未更新、也可能是字段单位不同。所以我每次拉完数据,都会写几个简单的校验规则:

  • 检查日期范围是否完整;
  • 检查是否出现重复索引;
  • 检查价格字段是否有空值或异常负值;
  • 检查涨停跌停数据是否与当日涨跌幅限制吻合。
# 简单数据质量校验 def check_daily_data(df): assert df['trade_date'].is_monotonic_increasing, '日期未按顺序排列' assert df['ts_code'].nunique() >= 1, '股票代码缺失' assert (df['close'] > 0).all(), '存在非正收盘价' assert df['close'].isna().sum() == 0, '存在空值' print('数据质量校验通过')

5. 常见问题排查:实操中我踩过的坑

5.1 数据接口突然返回空数据

这种情况多半不是Tushare挂了,而是你的查询时间范围/data权限出了问题。有次我调某个财务接口,明明前几天还能用,突然返回空,后来发现是查询的季度数据源还没更新。所以遇到空数据,我建议按顺序排查:

  • 先检查时间范围和周期是否合法;
  • 再检查接口积分权限是否变化;
  • 最后考虑股票本身停牌或退市状态。

5.2 复权价格计算结果与预期不一致

这个问题我遇到过好几次。比如某只股票我看它在某一天公布分红方案,当天股价跌了很多,但我用前复权数据去看,好像K线上没有明显向下的跳空。这个是因为前复权会把历史上所有价格统一调整到“当前视角”,所以过去的K线已经“被重塑”了。

如果你发现复权计算结果不一致,建议直接打印复权因子,手工计算一天的对照价格,逐步定位问题。

5.3 批量拉取时中断

批量拉数据的时候,最常见的问题就是网络波动或者达到接口限制导致脚本中断。我的处理方案是做断点续传:

  • 记录每个标的最后的下载日期;
  • 每次启动脚本时只下载缺失部分;
  • 下载完成后更新状态表。

这样即使中断了,重新运行只需要补齐缺失区间,不需要从头再来。

5.4 常用问题速查表

问题现象可能原因排查思路
接口返回权限错误积分不足检查当前积分与接口要求是否匹配
返回数据量明显偏少查询范围有误检查起止日期和交易状态
行情字段出现NaN停牌或无交易结合交易日历判断是否实际停牌
复权后价格异常复权方式用错打印复权因子,手工复核
本地数据库重复数据未设置主键去重使用INSERT OR REPLACE或先查后插

5.5 我的一点“笨办法”:先手工验证,再信任接口

无论接口文档写得多清楚,我始终保留一个习惯:拿到一批数据,先手工抽样验证几个点。比如随便挑一只熟悉的股票,找到某一天的收盘价,回到行情软件里对照一下;或者拿某家公司上一年的ROE,跟财报原文对照一下。

这个习惯看起来很笨,但它能帮你过滤掉绝大多数数据源层面的坑。数据这东西,一旦错误进入你的分析流程,后面每一步都是错上加错,而且越复杂的策略,越难察觉到底哪一步开始错的。所以,宁愿前面多花十分钟验证,也别后面花十个小时排查。

6. 让 Tushare 真正成为你的数据分析底座

Tushare用久了,我最大的感受是:它不仅仅是一个数据接口,更像是一套标准化的数据服务框架。你跟它磨合好了,整个数据分析链路会顺畅很多。

从最早的“拉个K线画个图”,到现在我会用财务接口做基本面初筛,用资金流向接口追踪异动,用龙虎榜理解市场情绪,再结合复权因子把回测建立在真实价格上。整个过程不是一蹴而就的,而是一步步踩坑、一次次校验,才形成了一套稳定的数据工作流。

这里我分享一个个人经验:不要急着把所有接口都试一遍,把你的核心分析场景拆开,想清楚每个环节需要什么数据,再针对性地去了解Tushare对应的能力。这样学习效率最高,也最容易把工具用得顺手。

比如你做量化选股,那你的重点是:行情、财务指标、复权因子、交易日历。你做事件驱动策略,那重点是:公告、龙虎榜、资金流、涨跌停。你做资产配置,那重点可能是指数行情、行业分类、宏观数据。场景不同,数据优先级完全不同,没必要什么都学。

如果非要给刚接触Tushare的人一个建议,我会说:先从日线行情+复权因子+交易日历这三个接口开始,把它们彻底用透,比你囫囵吞枣看三十个接口更有价值。数据工作的本质不是“拿到”,而是“拿对”“拿到能用”“拿到可持续更新”。这一点,我在反复踩坑之后,体会特别深。

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

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

立即咨询