发布于VeighNa社区公众号【vnpy-community】
 
原文作者:VeighNa小助手 | 发布时间:2025-06-15
 
VeighNa 4.0.0 版本新增了面向 AI 量化策略的 vnpy.alpha 模块,为专业量化交易员提供从多因子机器学习(ML)策略研发到实盘交易的一站式解决方案。

在股票多因子策略中,精准的成分股数据至关重要。在 A 股市场,成分股数据不仅能明确投资范围、控制样本特征,还可通过追踪历史成分变动,避免回测中的幸存者偏差。成分股通常流动性较好,可降低实盘执行风险;同时,以指数作为业绩基准,可更客观地评估策略表现。需要注意的是,本文方法主要适用于股票市场,期货等其他资产类别在数据需求和处理流程上有所不同。

本文将演示如何借助迅投研数据服务与 VeighNa,完整下载 A 股指数成分股数据,并实现成分变动跟踪与历史行情获取。

 

环境准备与模块导入

 

from datetime import datetime                           # 日期时间处理
from tqdm import tqdm                                   # 进度条显示
from xtquant import xtdata                              # 迅投研数据API

# vnpy相关模块
from vnpy.trader.database import DB_TZ                  # 数据库时区
from vnpy.trader.datafeed import get_datafeed           # 数据服务接口
from vnpy.trader.constant import Exchange, Interval     # 交易所和时间周期常量
from vnpy.trader.object import HistoryRequest           # 历史数据请求对象
from vnpy.alpha import AlphaLab, logger                 # Alpha研究实验室和日志

上述模块涵盖了数据下载、处理与存储的核心功能。其中,xtquant 是迅投研提供的 Python API,而 vnpy.alpha 则是 VeighNa 专为因子策略投研推出的组件。

 

下载参数配置

 

task_name = "csi300"                # 任务名称,用于标识数据文件夹
index_symbol = "000300.SSE"         # vnpy格式的指数代码
xt_index_symbol = "000300.SH"       # 迅投研格式的指数代码

start_date = "20070101"             # 开始日期
end_date = "20231231"               # 结束日期

intervals = [
    Interval.DAILY,                 # 设置下载日线数据
]

示例中选取沪深 300 指数(csi300)作为研究对象,时间范围为 2007 年初至 2023 年底,数据周期为日线。可以按需扩展至其他指数(如中证 500、创业板指等)或其他周期(如分钟线)。

 

初始化研究环境

 

# 创建投研实验室
lab = AlphaLab(f"./lab/{task_name}")    # 指定数据文件夹

# 初始化数据服务(迅投研数据服务)
datafeed = get_datafeed()               # 获取配置的数据服务
datafeed.init()                         # 初始化数据服务连接

AlphaLab 是 VeighNa 的因子投研流程管理组件,集成了数据管理、模型训练、信号生成和策略回测等完整环节。此处创建其实例,并指定数据存储目录。

 

下载成分股变动数据

 

# 下载历史成分股信息
xtdata.download_sector_data()                                # 下载板块分类数据
xtdata.download_history_data("", "stocklistchange", "", "")  # 下载股票列表变更历史

在获取指数板块信息前,需要先执行 download_sector_data 下载板块分类;而获取板块成分股列表前,同样需通过 download_history_data 下载股票列表变更历史。

首次下载耗时较长,请耐心等待。如需查看进度,可在 xtquant 自动创建的 data/log 目录中查看日志,其中会出现 "download done [xxx/xxx]" 等提示。

 

获取指数成分股变动

 

# 查询交易日历
days = xtdata.get_trading_calendar(market="SZ", start_time=start_date, end_time=end_date)

# 轮询获取指数成分股
index_components = {}
end_datetime = datetime.strptime(end_date, "%Y%m%d")
for i in days:
    dt = datetime.strptime(i, "%Y%m%d")
    if dt > end_datetime:
        continue

    # 获取特定日期的指数成分股
    xt_symbols = xtdata.get_stock_list_in_sector(xt_index_symbol, i)

    # 将迅投研格式转换为vnpy格式
    vt_symbols: list = []
    for xt_symbol in xt_symbols:
        vt_symbol = xt_symbol.replace("SH", "SSE").replace("SZ", "SZSE")
        vt_symbols.append(vt_symbol)

    # 以日期为键,成分股列表为值,存储在字典中
    index_components[dt.strftime("%Y-%m-%d")] = vt_symbols

# 保存到数据中心
lab.save_component_data(index_symbol, index_components)

下载完毕后,先使用 get_trading_calendar 获取交易日历,再对每个交易日调用 get_stock_list_in_sector 取得当天的成分股列表,并将代码从迅投研格式(如 000001.SZ)转换为 VeighNa 格式(如 000001.SZSE)。

最终得到的字典以日期为键、成分股列表为值,并通过 save_component_data 存入 AlphaLab 数据中心,为后续因子计算与策略回测奠定基础。

 

加载成分股列表

 

# 加载指数成分股代码
component_symbols = lab.load_component_symbols(index_symbol, start_date, end_date)

调用 load_component_symbols 即可从数据中心加载成分股数据。该方法会在指定日期区间内自动去重,返回所有历史成分股的列表,便于后续行情数据下载。

 

下载历史行情数据

 

# 加载指数成分股代码(去重后的所有历史成分股)
component_symbols = lab.load_component_symbols(index_symbol, start_date, end_date)

# 转换时间格式
start = datetime.strptime(start_date, "%Y%m%d")
start.replace(tzinfo=DB_TZ)  # 设置时区

end = datetime.strptime(end_date, "%Y%m%d")
end.replace(tzinfo=DB_TZ)    # 设置时区

# 除了成分股,还要下载指数数据
task_symbols = component_symbols + [index_symbol]

# 遍历下载数据,使用tqdm显示进度
for vt_symbol in tqdm(task_symbols):
    symbol, exchange_str = vt_symbol.split(".")  # 分离代码和交易所

    for interval in intervals:
        # 创建历史数据请求
        req = HistoryRequest(
            symbol=symbol, 
            exchange=Exchange(exchange_str), 
            start=start, 
            end=end, 
            interval=interval
        )

        # 查询历史数据
        bars = datafeed.query_bar_history(req)

        # 保存数据到数据库
        if bars:
            lab.save_bar_data(bars)
        else:
            logger(interval, vt_symbol)  # 记录下载失败的情况

拿到成分股列表后,需要进一步下载它们的K线历史行情数据。上述代码将去重后的成分股与指数本身合并为下载任务,并使用 tqdm 展示进度。遍历过程中,通过 query_bar_history 拉取历史行情并存储于 AlphaLab 数据库;若查询失败,则记录日志以便后续排查。

 

配置回测参数

 

# 添加回测参数配置
for vt_symbol in component_symbols:
    lab.add_contract_setting(
        vt_symbol,
        long_rate=5/10000,      # 多头费率:万分之五
        short_rate=10/10000,    # 空头费率:万分之十
        size=1,                 # 合约乘数:1
        pricetick=0.0001,       # 价格变动:0.0001
    )

为每只成分股配置回测参数。交易成本的合理设置对回测结果的准确性至关重要。本示例在 A 股市场中采用万分之五的多头费率和万分之十的空头费率,综合考虑了佣金、印花税及滑点。

 

完整代码

 

在文章的结尾还是老规矩附上完整的程序代码:

# 加载模块
from datetime import datetime

from tqdm import tqdm
from xtquant import xtdata

from vnpy.trader.database import DB_TZ
from vnpy.trader.datafeed import get_datafeed
from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.object import HistoryRequest

from vnpy.alpha import AlphaLab, logger


# 设置下载参数
task_name = "csi300"
index_symbol = "000300.SSE"
xt_index_symbol = "000300.SH"

start_date = "20070101"
end_date = "20231231"

intervals = [
    Interval.DAILY,
]

# 创建投研实验室
lab = AlphaLab(f"./lab/{task_name}")    # 指定数据文件夹

# 初始化数据服务(这里配置使用的迅投研)
datafeed = get_datafeed()
datafeed.init()

# 下载历史成分股信息
xtdata.download_sector_data()

xtdata.download_history_data("", "stocklistchange", "", "")

# 查询交易日历
days = xtdata.get_trading_calendar(market="SZ", start_time=start_date, end_time=end_date)

# 轮询获取指数成本股
index_components = {}
end_datetime = datetime.strptime(end_date, "%Y%m%d")
for i in days:
    dt = datetime.strptime(i, "%Y%m%d")
    if dt > end_datetime:
        continue

    xt_symbols = xtdata.get_stock_list_in_sector(xt_index_symbol, i)

    vt_symbols: list = []
    for xt_symbol in xt_symbols:
        vt_symbol = xt_symbol.replace("SH", "SSE").replace("SZ", "SZSE")
        vt_symbols.append(vt_symbol)

    index_components[dt.strftime("%Y-%m-%d")] = vt_symbols

# 保存到数据中心
lab.save_component_data(index_symbol, index_components)

# 加载指数成分股代码
component_symbols = lab.load_component_symbols(index_symbol, start_date, end_date)

# 转换时间格式
start = datetime.strptime(start_date, "%Y%m%d")
start.replace(tzinfo=DB_TZ)

end = datetime.strptime(end_date, "%Y%m%d")
end.replace(tzinfo=DB_TZ)

# 除了成分股,还要下载指数数据
task_symbols = component_symbols + [index_symbol]

# 遍历下载数据
for vt_symbol in tqdm(task_symbols):
    symbol, exchange_str = vt_symbol.split(".")

    for interval in intervals:
        req = HistoryRequest(symbol, Exchange(exchange_str), start, end, interval)
        bars = datafeed.query_bar_history(req)

        if bars:
            lab.save_bar_data(bars)
        else:
            logger(interval, vt_symbol)

# 添加回测参数配置
for vt_symbol in component_symbols:
    lab.add_contract_setting(
        vt_symbol,
        long_rate=5/10000,
        short_rate=10/10000,
        size=1,
        pricetick=0.0001,
    )