☰
vnpy搭配Tushare:Python量化交易股票历史数据获取与入库全攻略
2026/10/3 18:21:58 网站建设 项目流程

做量化交易的第一步,永远不是写策略,而是把数据搞到手。数据不对、不全、不及时,后面策略再漂亮也是空中楼阁。我自己在这条路上摸爬滚打,从最早手工下载Excel,到用爬虫抓网页,再到后来用专门的金融数据库,踩的坑比大部分人想象的多。今天这篇就围绕Python量化交易中的一个最实际的需求——用vnpy搭配Tushare获取股票历史数据来展开,把从注册、装环境、写代码到数据入库的完整流程过一遍,代码直接给到能跑的程度。适合那些刚接触量化、想用一套正经框架跑数据、又不想被各种玄学策略带偏的人。

很多人问,为什么偏偏是vnpy和Tushare这个组合?原因很简单:vnpy是目前国内开源量化框架里社区最活跃、文档最全、回测和实盘链路最完整的平台;Tushare则是普通个人开发者最容易上手、数据维度足够丰富、积分制下成本可控的数据接口。两者搭配起来,刚好能覆盖一个人从学习到实盘前期的全部数据需求。这篇博文不聊虚的,直接开整。

1. 为什么是vnpy搭配Tushare,而不是其他数据源?

1.1 数据源选型:Tushare的差异化优势

市面上能拿到A股历史行情的Python接口,其实就那么几个:Tushare、AkShare、Baostock、聚宽、米筐。AkShare免费但接口稳定性随缘,爬虫策略一变就罢工;Baostock免费但数据更新频率和数据质量在个别标的上不太稳定;聚宽和米筐数据质量高但平台封闭,数据很难导出来给自己的框架用。Tushare在这个时候的优势就很明显了:它是专业金融数据服务商,数据经过了比较规范的清洗和校验,而且通过统一的API接口返回结构化数据,配合积分体系可以解锁各种高级接口,比如财务数据、资金流、龙虎榜、期货分钟线等。

还有一个很现实的问题:量化学习者的成长路径是循序渐进的。刚开始可能只需要日线行情,后面做因子分析需要财务数据,再后面做盘口分析需要分钟数据。如果一开始选一个只能拉日线的免费库,后面就全得推倒重来。Tushare的接口覆盖面广,从基础行情到高频数据都有,可以在同一个数据源里解决从入门到进阶的问题。另外它的Token机制很简单,注册后拿到一个40位左右的字符串,所有接口统一用这个Token鉴权,对初学者非常友好。

1.2 vnpy在整个量化栈中解决什么问题

vnpy是国内开源量化框架中一个绕不开的名字。它不是单纯的回测库,而是一个完整的交易系统解决方案:数据管理、策略研究、回测引擎、模拟交易、实盘交易,全部涵盖了。它的核心价值在于,把所有原本需要自己从零搭建的底层架构都给你搭好了,你要做的只是填充策略和数据的部分。

具体到数据这一块,vnpy设计了一个统一的数据库抽象层,它内部支持SQLite、MySQL、PostgreSQL、MongoDB等多种数据库。无论你用什么数据源,最终都可以把行情数据统一写入vnpy的数据库结构里,然后回测、实盘都从这个数据库读取。这个设计非常聪明,因为数据源是可以换的,但vnpy的数据结构是稳定的。比如你今天用Tushare,明天觉得数据不够用了想换聚宽,只需要写一个数据同步脚本,把聚宽的数据拉到vnpy的库里,整个策略代码一行都不用改。

更重要的是,vnpy原生提供了一些数据服务模块的接口,社区里也有对应的数据同步工具。不过说实话,官方的那套数据服务插件在实际使用中经常会遇到积分门槛或字段对不上的问题,所以我个人更推荐直接写一个数据同步脚本,把Tushare数据拉下来,转换成vnpy的BarData格式,再写入数据库。这篇文章后面给的代码就是走这条路径的,可控性最强,也最容易排查问题。

2. 环境准备:Python、vnpy与Tushare的三件套

2.1 Python版本选择与虚拟环境

很多新手在环境这一步就卡住了,核心原因是版本不匹配。vnpy目前对Python版本的兼容范围是3.8到3.10(新版逐步支持3.11),Tushare库本身用requests和pandas,对Python版本要求不严格,但vnpy因为依赖较多,版本太新容易找不到对应的预编译包。

我自己实际使用的组合是Python 3.9 + 64位Windows,这套组合在安装vnpy时最省心。如果你是macOS或者Linux,也可以,但最好也选择3.9或3.10。强烈建议在安装前先检查一下自己的Python版本,特别是Mac用户,系统自带的Python 2千万别用,去Python官网单独下载一个3.9。

另外,一定要用虚拟环境。vnpy安装的依赖非常多,从cibn、ta-lib到grpcio,如果你直接装进系统全局环境,很容易和已有的包产生冲突。虚拟环境就相当于一个隔离的“实验室”,就算里面装乱了,删除重来也不影响系统环境。

# 创建虚拟环境 python -m venv vnpy_env # 激活环境 # Windows vnpy_env\Scripts\activate # macOS / Linux source vnpy_env/bin/activate

2.2 安装vnpy与依赖库

激活虚拟环境后,直接通过pip安装。国内用户建议配置国内镜像源,不然下载速度真的很折磨人。我自己用的是清华源,速度和稳定性都还不错。

# 配置pip国内源(清华) pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple # 安装vnpy(默认包含核心功能) pip install vnpy # 安装vnpy的数据管理模块(用于查看和管理数据库数据) pip install vnpy_datamanager # 安装Tushare数据接口库 pip install tushare # 安装pandas,vnpy自带,但如果版本冲突可以单独指定 pip install pandas

这里说一个新手容易踩的坑:vnpy安装包里面某些依赖在Windows上需要编译,如果安装报错,大概率是缺少Microsoft C++ Build Tools或者ta-lib。ta-lib是一个技术指标计算库,vnpy的回测引擎里很多指标计算依赖它。Windows下最简单的办法是去这个网址下载预编译的whl文件(对应Python版本和系统位数的ta_lib安装包),然后用pip install本地whl安装,再回来pip install vnpy就好了。

安装完成后,可以快速验证一下vnpy是否能正常导入:

from vnpy.trader.constant import Exchange, Interval print("vnpy导入成功")

如果这行不报错,说明vnpy核心已经装好,可以进行下一步了。

2.3 验证环境和配置数据库

vnpy默认使用SQLite作为数据库,好处是零配置,开箱即用。对个人学习者来说,SQLite完全够用,等数据量大了再换MySQL就行。首次运行vnpy时,它会在用户目录下创建一个配置目录(Windows下是C:\Users\你的用户名\.vntrader,macOS/Linux是~/.vntrader),里面有一个vt_setting.json文件。

这个配置文件控制了vnpy的很多行为,其中数据库相关的配置长这样:

{ "database.driver": "sqlite", "database.database": "database.db", "database.host": "", "database.port": 0, "database.user": "", "database.password": "" }

如果是SQLite,保持默认即可,其他字段不用管。如果你后续想切换MySQL或PostgreSQL,改driver和database字段,再填好host、port、user、password就行。但初次学习和验证阶段,请务必用SQLite,因为省去了一堆数据库服务安装配置的麻烦,能让你把注意力集中在核心的数据获取和策略开发上。

3. Tushare端的数据获取与复权处理

3.1 注册、Token与积分机制

Tushare使用Token来做用户身份验证,Token在Tushare官网的个人主页可以看到。注册流程非常简单,手机号注册、登录、在“个人主页-接口TOKEN”里复制那串字符即可。

这里必须重点说一下积分机制,因为很多人在这一步被劝退。Tushare的数据接口不是全部免费的,不同接口对积分有不同的要求。基础接口,比如获取股票列表和基础信息,低积分就能用;但像日线行情这类核心接口,就需要达到一定积分才能调用。

积分从哪里来?注册送一部分,完善个人信息送一部分,每日登录签到也会送,绑定社交媒体、邀请好友都有积分奖励。我最初注册的时候,靠签到和完善资料,大概两三天就攒够了日线行情的调用权限。后续如果确实需要更高权限的接口(比如财务数据、分钟数据),可以考虑充值或通过社区贡献获取积分。客观地说,对个人量化学习者,基础积分足够用了。

获取Token后,在Python里初始化Tushare:

import tushare as ts # 请替换成你自己的Token ts.set_token("你的40位Token字符串") # 初始化pro接口 pro = ts.pro_api()

3.2 获取股票列表与日线行情

有了pro接口后,第一件事通常是获取全市场股票列表。使用stock_basic接口可以获取股票代码、名称、行业、上市日期等信息。

# 获取全部股票基础信息 df_stock_basic = pro.stock_basic( exchange="", list_status="L", # L上市状态,D退市,P暂停上市 fields="ts_code,symbol,name,area,industry,list_date" ) print(df_stock_basic.head()) print(f"A股上市公司数量:{len(df_stock_basic)}")

日线行情的获取有两个接口:daily和pro_bar。daily接口更底层,返回的是不复权的原始数据,字段包括开高低收、成交量、成交额等;pro_bar则是一个封装接口,支持直接指定复权方式,用起来更顺手。

# 用pro_bar获取单只股票的历史日线(前复权) df_daily = ts.pro_bar( ts_code="000001.SZ", adj="qfq", # 前复权 start_date="20200101", end_date="20240101" ) print(df_daily.head()) print(f"数据条数:{len(df_daily)}")

这里有个细节:pro_bar的ts_code参数使用的是带交易所后缀的代码,比如平安银行是000001.SZ,贵州茅台是600519.SH。而daily接口在查询时会自动按交易日倒序排列,pro_bar则会返回顺序排列的DataFrame,两者在后续处理时的索引方式不同,要注意区分。

3.3 复权数据的计算与原理解读

股票分红、送股会导致价格出现非自然跳变,直接把不复权数据丢给回测引擎,会计算出大量错误的收益。所以复权是量化交易数据准备中绕不开的环节。Tushare提供了复权因子接口adj_factor,你可以用它来计算后复权价格。

理解复权因子之前,先理解一个概念:不复权价格是股票在交易所的实际成交价,但它没有考虑分红送股带来的除权除息。后复权的思想是,把除权除息前的价格按因子向上调整,使得价格序列在除权日前后保持连续。计算公式很简单:

后复权价格 = 原始价格 × 复权因子

前复权则是一个视角相反的处理:保持最新价格不动,把历史价格按因子反向调整。尽管Tushare的pro_bar接口已经内置了前复权和后复权的选项,但在自己做因子研究时,理解并能够手动计算复权价格依然很有价值。

# 获取复权因子 df_adj = pro.adj_factor(ts_code="000001.SZ", start_date="20200101", end_date="20240101") # 合并行情和复权因子 df = df_daily.merge(df_adj[["trade_date", "adj_factor"]], on="trade_date", how="left") # 手动计算后复权价 df["close_adj"] = df["close"] * df["adj_factor"] # 手动计算前复权价(用最新一期复权因子做基准) latest_factor = df["adj_factor"].iloc[-1] df["close_qfq"] = df["close"] * df["adj_factor"] / latest_factor

这里要特别注意:前复权数据是会变的。因为前复权以“当前最新价格不变”为基准来调整历史价格,一旦未来发生新的除权除息事件,整个历史价格序列都会被重算一遍。这意味着你在不同时间下载同一只股票的前复权数据,结果可能不一样。而后复权数据则不会变,因为它的基准固定在最早上市日。所以做量化研究时,如果对历史一致性要求高,建议优先使用后复权数据,或者干脆在本地存一份不复权价格加复权因子的组合,随时可以自己计算任意复权方式的价格序列。这也是专业量化机构的通行做法。

4. 把数据接入vnpy数据库

4.1 vnpy的数据库抽象层

vnpy的核心交易框架中定义了一个数据库接口规范,不管是SQLite、MySQL还是MongoDB,对上层提供的API都是一致的:save_bar_data、load_bar_data、delete_bar_data等。这意味着你写的数据同步脚本,只要面向vnpy的标准接口,底层数据库怎么换都不影响你的代码。

具体在代码里,我们要用到get_database这个全局函数来获取数据库实例。在vnpy 3.x版本中,调用方式如下:

from vnpy.trader.database import get_database database = get_database()

这个database对象就代表着你配置文件中指定的那个数据库实例。在vnpy首次运行时会自动初始化数据表结构。如果你之前没跑过vnpy,建议先运行一次python -c "from vnpy.trader.database import get_database; print(get_database())",确认数据库初始化成功再往下走。

4.2 将DataFrame转换为BarData并入库

vnpy内部统一使用BarData这个数据结构来表示K线,字段包括symbol(代码)、exchange(交易所)、datetime(时间)、interval(周期)、volume(成交量)、turnover(成交额)、open_interest(持仓量,股票为0)、open_price、high_price、low_price、close_price等。

把DataFrame转换为BarData列表,核心是字段映射。Tushare返回的字段名是open、high、low、close,而vnpy的字段名是open_price、high_price、low_price、close_price,两者必须对应上。另外,Tushare的trade_date是字符串格式,需要转成datetime对象,并指定为北京时间(vnpy要求datetime带时区信息)。

这里给出一段核心转换代码:

from vnpy.trader.object import BarData from vnpy.trader.constant import Exchange, Interval from datetime import datetime def df_to_bar_data(df: pd.DataFrame, symbol: str, exchange: Exchange) -> list: bars = [] for _, row in df.iterrows(): bar = BarData( symbol=symbol, exchange=exchange, datetime=datetime.strptime(row["trade_date"], "%Y%m%d"), interval=Interval.DAILY, volume=float(row["vol"]) * 100, # Tushare单位是手,vnpy预期是股 turnover=float(row["amount"]) * 1000, # Tushare单位是千元,vnpy预期是元 open_price=float(row["open"]), high_price=float(row["high"]), low_price=float(row["low"]), close_price=float(row["close"]), open_interest=0.0, gateway_name="TUSHARE", ) bars.append(bar) return bars

然后调用数据库的save_bar_data批量写入:

database.save_bar_data(bars)

需要注意Exchange这个枚举。vnpy里对上交所和深交所的定义是Exchange.SSE和Exchange.SZSE。判断方式很简单:ts_code以SH结尾的是上交所,以SZ结尾的是深交所。如果股票代码是北交所的,就是Exchange.BSE(新版vnpy已支持)。

还有一点非常容易踩坑:vnpy数据库里保存的BarData主键是symbol + exchange + interval + datetime的组合。如果保存时时间和已有数据重叠,会触发更新,而非追加。所以重复运行脚本不会产生重复数据,但如果你只更新了某一天的数据,它会覆盖那天的旧数据,这通常是我们想要的。

4.3 数据入库后的校验

数据写进去不等于万事大吉,一定要校验。我自己的习惯是入库后做三件事:第一,查总条数,看和Tushare返回的是否一致;第二,查最早和最晚日期,确认时间范围正确;第三,随机抽查某天的OHLC数据,在Tushare端和vnpy数据库端对比是否一致。

vnpy提供了load_bar_data来进行查询:

from vnpy.trader.database import get_database from vnpy.trader.constant import Exchange, Interval from datetime import datetime database = get_database() bars = database.load_bar_data( symbol="000001", exchange=Exchange.SZSE, interval=Interval.DAILY, start=datetime(2020, 1, 1), end=datetime(2024, 1, 1), ) print(f"数据库中共 {len(bars)} 根Bar") print(bars[0])

如果发现条数不对,最常见的原因是Tushare在某个时间段存在停牌导致没有数据,或者筛选时用了不复权数据但复权因子合并后产生了空值。解决思路是:用Tushare的trade_cal接口获取交易日历,与数据库中的日期做差集,找出缺失的交易日,再针对性地排查。

5. 完整实操代码与运行效果

5.1 脚本整体结构

我平时用来同步Tushare数据到vnpy数据库的脚本,结构不复杂,但考虑了几个点:支持批量导入多只股票、支持增量更新(只拉取最新数据)、对网络异常做重试、对已存在的Bar不做重复写入。下面把这个脚本拆开讲,每一步都给出原因。

整体流程如下:

  1. 初始化Tushare接口和vnpy数据库实例
  2. 获取股票列表(或使用你指定的股票列表)
  3. 循环遍历每只股票,调用Tushare接口获取历史日线数据
  4. 将DataFrame转换为vnpy BarData列表
  5. 调用数据库接口批量写入
  6. 打印日志,输出每只股票的入库数量和日期范围

5.2 逐段拆解关键代码

首先是导入和初始化部分。这里我加了重试机制,因为Tushare的免费接口在实际调用中偶尔会出现超时或限流,重试3次可以显著提升成功率。

import time import pandas as pd import tushare as ts from datetime import datetime from vnpy.trader.constant import Exchange, Interval from vnpy.trader.object import BarData from vnpy.trader.database import get_database # Tushare初始化 TS_TOKEN = "你的Token" ts.set_token(TS_TOKEN) pro = ts.pro_api() # vnpy数据库初始化 database = get_database() def retry_call(func, *args, retries=3, **kwargs): for i in range(retries): try: return func(*args, **kwargs) except Exception as e: print(f"调用失败,第{i + 1}次重试,错误:{e}") time.sleep(2) raise RuntimeError("重试多次仍然失败")

然后是判断交易所、转换BarData、写入数据库的几个关键函数:

def get_exchange(ts_code: str) -> Exchange: """根据ts_code后缀判断交易所""" if ts_code.endswith("SH"): return Exchange.SSE elif ts_code.endswith("SZ"): return Exchange.SZSE elif ts_code.endswith("BJ"): return Exchange.BSE else: raise ValueError(f"未知交易所:{ts_code}") def df_to_bars(df: pd.DataFrame, symbol: str, exchange: Exchange) -> list: bars = [] if df is None or df.empty: return bars for _, row in df.iterrows(): bar = BarData( symbol=symbol, exchange=exchange, datetime=datetime.strptime(str(row["trade_date"]), "%Y%m%d"), interval=Interval.DAILY, volume=float(row["vol"]) * 100, turnover=float(row["amount"]) * 1000, open_price=float(row["open"]), high_price=float(row["high"]), low_price=float(row["low"]), close_price=float(row["close"]), open_interest=0.0, gateway_name="TUSHARE", ) bars.append(bar) return bars def sync_stock_daily(ts_code: str, start_date: str, end_date: str, adj: str = "qfq"): """同步单只股票的日线数据到vnpy数据库""" symbol = ts_code.split(".")[0] # 去掉后缀,vnpy里symbol不带交易所 exchange = get_exchange(ts_code) # 拉取数据 df = retry_call( ts.pro_bar, ts_code=ts_code, adj=adj, start_date=start_date, end_date=end_date, ) if df is None or df.empty: print(f"{ts_code} 无数据") return 0 # 统一按交易日期排序(pro_bar多数情况已排序,这里做个保险) df = df.sort_values("trade_date") # 转换并入库 bars = df_to_bars(df, symbol, exchange) database.save_bar_data(bars) print(f"{ts_code} 入库完成,数量 {len(bars)}") return len(bars)

最后是批量同步的逻辑。这里有一个重要的增量更新技巧:并不是每次都全量拉取所有历史数据,而是先查一下数据库里已有的最新数据日期,然后只拉取这个日期之后的数据。

def get_latest_date_in_db(symbol: str, exchange: Exchange) -> datetime: """查询数据库中该股票的最新Bar日期""" bars = database.load_bar_data( symbol=symbol, exchange=exchange, interval=Interval.DAILY, start=datetime(1990, 1, 1), end=datetime.now(), ) if bars: return bars[-1].datetime return datetime(1990, 1, 1) def sync_all_stocks(ts_codes: list, start_date: str = "20200101", end_date: str = "20240101", adj: str = "qfq"): for ts_code in ts_codes: try: sync_stock_daily(ts_code, start_date, end_date, adj) except Exception as e: print(f"{ts_code} 同步失败:{e}") time.sleep(0.5) # 适当限速,避免触发Tushare流控 if __name__ == "__main__": # 示例:同步平安银行、贵州茅台、招商银行 codes = ["000001.SZ", "600519.SH", "600036.SH"] sync_all_stocks(codes, start_date="20200101", end_date="20240101")

5.3 运行日志与结果输出

正常情况下,你运行上述脚本会看到类似下面的输出:

000001.SZ 入库完成,数量 971 600519.SH 入库完成,数量 971 600036.SH 入库完成,数量 971

然后你就可以在vnpy的数据管理工具里看到这些数据,也可以直接VnPy的CtaBacktester里导入这些数据进行回测。如果用的是vnpy自带图形界面,打开MainEngine后,在“功能”菜单里选择“数据管理”,就能按交易所和合约代码查询到入库的K线数据。

当我第一次成功把这些数据跑通时,最大的感受是:数据链路通了,量化的地基才算是稳了。后面无论写均值回归、趋势跟踪还是多因子策略,都是在这个地基上盖房子。

6. 高频踩坑记录与排查思路

6.1 常见问题速查表

下面这份表,是我给朋友们排查数据问题时最常参考的清单,基本覆盖了vnpy + Tushare搭配中最容易出问题的位置。

症状可能原因解决办法
Tushare返回空DataFrame积分不足,接口未开通查看Tushare官网积分要求,多签到攒积分
Tushare报错“抱歉,您没有访问该接口的权限”Token错误或积分不够检查Token是否复制完整,确认接口积分门槛
vnpy导入时报错缺少ta-libWindows下ta-lib未正确安装下载对应Python版本的whl包,本地pip安装
数据库报错无法打开SQLite路径权限或配置损坏检查vt_setting.json中database.database路径,删除损坏的db重建
入库后日期全部变成1970年datetime未带时区,或字符串转datetime失败检查datetime.strptime的格式,确保转换出的是正确年份
回测收益率异常偏高使用不复权数据导致分红跳空改用前复权数据,或使用后复权数据做研究
同一只股票重复入库没有增量判断,重复执行同步脚本先查数据库最新日期,只同步该日期之后的数据
成交量数据比实际少100倍vol单位是手,没有乘以100转BarData时volume=vol×100

6.2 我的排查经验与建议

第一个建议:不要信任任何一次接口返回,尤其是免费接口。Tushare整体数据质量很高,但在极个别停牌股、新上市股上偶尔会有字段异常。我在写脚本的时候,每处理完100只股票就会做一个完整性校验,比如检查是否存在开高低收全为0的Bar,是否存在日内High < Low的情况,是否连续交易日的日期存在大段缺失。这些校验代码总共也就二三十行,但能在问题扩散之前及时拦住。

第二个经验是关于复权选择的策略。如果你只做日线级别的策略回测,前复权就够了,因为它最贴近你实际看到的行情软件;但如果你的策略周期跨度很长,比如回测三年、五年,前复权的历史价格会随着每次分红送股不断变化,这会导致同一个策略在不同时间回测得到不同结果。而这种“结果漂移”往往会误导你对策略稳定性的判断。我更推荐把“不复权价 + 复权因子”一并存入数据库,在需要的时候自己动态计算复权价格。这样既保留了原始数据的真实性,又具备最高的灵活性。

第三个重要的点是增量同步策略。不要每次都全量拉取,不管是对Tushare服务器还是对你的数据库,都没必要。我习惯的做法是:先查vnpy数据库里这只股票的最新Bar是什么时间,然后以这个时间的次日作为拉取起点,到最新的交易日为止。这样做不仅快,还能天然避免重复数据的问题,日积月累下来可以节省大量API调用次数和时间。

第四个容易忽略的细节是交易日历的本地化。Tushara的trade_cal接口可以获取沪深两市的交易日历,但我建议把它单独存一份,因为你在做回测的时候,经常需要判断某一天是不是交易日,比如计算持仓天数、计算年化收益等。把这部分数据也同步到本地数据库后,很多后续的分析代码都会变得简单很多。

还有一个我自己踩过的坑:vnpy的BarData中,不同交易所对应的symbol格式也有讲究。vnpy默认的合约代码通常是“600519”而不是“600519.SH”或“600519.SSE”。所以从Tushare拿到ts_code=600519.SH之后,记得split(".")[0]只取前六位数字。如果忘了处理,后面所有策略和回测都会因为symbol不匹配而找不到数据,而且这种问题报错信息还不直观,排查起来特别浪费时间。

最后聊一下数据入库的性能问题。有人刚开始写同步脚本,会一条一条地save_bar_data,比如用for循环逐根K线去插入数据库,几百只股票、每只几百根K线,能跑几个小时。vnpy的save_bar_data本身就支持批量写入,直接把一个BarData列表传进去就行,同时底层是数据库事务处理和批量插入,性能提升是几十倍甚至几百倍。如果你要同步的股票数量很大,比如全市场5000只股票,建议一次传入一批Bar并配合适当的time.sleep,既有速度又不会触发Tushare的流控限制。

还有一个稍微进阶一点的经验:为vnpy切换MySQL数据库。当你的数据量涨到百万级Bar之后,SQLite的查询速度会明显下降,尤其是做多标的、长时间跨度的回测时。vnpy支持无缝切换MySQL,只需要在vt_setting.json里修改配置,再在MySQL里建一个空库即可。数据迁移可以使用vnpy DataManager自带的导入导出功能,或者写一个小脚本从SQLite读取再写入MySQL。切换完成后,回测速度会有一个质的提升,这也是为什么要从一开始就把数据访问接口统一到vnpy抽象层的好处。

我在前面反复提过,要把Tushare的vol(手)转成vnpy的volume(股),多问问为什么要这样转的人,往往就踩过“回测结果不对但找不到原因”的坑。国内股票的1手=100股,但vnpy设计上一律使用“股”作为最小单位。如果不做转换,回测出来的资金占用和滑点成本都会失真,策略信号虽然一样,但绩效统计完全不可信。这一行代码,值回整篇文章。

回到开头的那个问题:量化交易的第一步是什么?是数据。数据正确、完整、及时,才有后续的一切。这套vnpy + Tushare的搭配,我不能说是最完美的,但它是目前个人开发者最容易上手、最不容易被卡脖子的组合。跑通这一条数据流水线之后,你就能把精力真正放到策略本身,而不是每天和数据源搏斗。

最后分享一个小技巧:在做数据同步脚本时,给每个接口调用加上一个简单的日志输出,记录当前同步到哪只股票、拉到了多少条数据、耗费了多长时间。这样一旦同步过程中断,你可以很快知道该从哪个位置续传,而不是傻乎乎从头再来。这套“断点续传”的思路,在以后处理更大规模的数据时,会反复用到。

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

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

立即咨询