升级说明

由于东财的限制,原来DAY1使用AkShare获取ETF分钟和日线数据API已失效,因此之前DAY1的内容我们升级一番,更改有效的接口,同时我们也增加更靠谱的miniqmt接口方式。


搭建过程

这个系列中,我们用Python从0开始一步步搭建出一套ETF量化交易系统选择ETF标的是因为对于普通交易者来说,ETF相对于选强势股难度要小,而且没有退市风险)。大家可以跟随着我们的实现路径来一起学习,从过程中掌握方法。

掌握了方法之后,你可以换成期货系统、比特币系统、美股系统,然后在实战中不断去完善自己的系统了。

DAY1-DAY15链接如下:15天搭建ETF量化交易系统

我们对“15天搭建ETF量化交易系统”系列的学习进行升级!进阶系列链接如下:

[进阶]21天搭建ETF量化交易系统Day16—用DeepSeek学习数据处理

[进阶]21天搭建ETF量化交易系统Day17—Backtrader回测马丁格尔补仓法

[进阶]21天搭建ETF量化交易系统Day18—爆改backtrader升级为实盘交易!

[进阶]21天搭建ETF量化交易系统Day19—基于Dash“平民网页版”量化交易框架!

DAY1我们介绍下如何搭建自己的ETF数据源模块。

我们需要三类数据:ETF代码和名称、实时行情数据以及历史行情数据。


AkShare数据源

AkShare是基于Python的财经数据接口库,AkShare提供的API函数,可以直接获取ETF的实时行情。

我们使用AkShare作为ETF的数据源时,要使用命令安装下AkShare:

pip install akshare

AkShare的fund_etf_category_sina 函数可以获取所有 ETF 基金的实时行情数据

etf_data = ak.fund_etf_category_sina(symbol='ETF基金')# 显示前10行print(etf_data.head(5))'''   代码       名称    最新价    涨跌额    涨跌幅   买入   卖出   昨收   今开    最高    最低    成交量    成交额0  sz159998  计算机ETF  0.714  0.021  3.030  0.714  0.715  0.693  0.695  0.717  0.693   39176900   277509431  sz159997   电子ETF  0.843  0.025  3.056  0.843  0.844  0.818  0.821  0.846  0.819   42597200   356306002  sz159996   家电ETF  1.175  0.012  1.032  1.174  1.175  1.163  1.163  1.181  1.160   77680200   911773083  sz159995   芯片ETF  0.826  0.024  2.993  0.825  0.826  0.802  0.801  0.826  0.801  480305699  3930024194  sz159994   5GETF  0.766  0.030  4.076  0.766  0.767  0.736  0.740  0.771  0.740   56534500   43087108'''# 显示最后3行print(etf_data.tail(3))'''    代码       名称   最新价    涨跌额    涨跌幅    买入   卖出   昨收   今开    最高   最低    成交量    成交额934  sh510030     价值ETF  0.848  0.003  0.355  0.848  0.851  0.845  0.850  0.852  0.842  2808900  2381342935  sh510020    超大盘ETF  2.753  0.040  1.474  2.751  2.753  2.713  2.724  2.756  2.724   653710  1789779936  sh510010  180治理ETF  1.464  0.018  1.245  1.435  1.465  1.446  1.445  1.464  1.445    24900    36290'''

fund_etf_category_sina 接口返回的是一个pandas DataFrame,包含了全部ETF基金的实时行情数据。每行数据代表一个ETF基金,通常包含以下列:

使用fund_etf_category_sina接口还可以返回ETF的代码和名称

# 打印ETF代码和名称for index, item in etf_data.iterrows():    print(f'ETF代码: {item['代码']}, ETF名称: {item['名称']}')'''ETF代码: sz159998, ETF名称: 计算机ETFETF代码: sz159997, ETF名称: 电子ETFETF代码: sz159996, ETF名称: 家电ETFETF代码: sz159995, ETF名称: 芯片ETFETF代码: sz159994, ETF名称: 5GETF'''
由于fund_etf_category_sina接口包含了全市场ETF快照数据,还可以用条件快速筛选出品种以及进行市场的统计。

由于AkShare的fund_etf_hist_min_em 接口获取ETF分钟数据已失效,接下来我们使用stock_zh_a_minute进行代替。单次返回指定股票或指数的指定频率的最近交易日的历史分时行情数据。

输入参数说明:

输出参数说明:

获取单只ETF分钟数据案例如下:

批量获取ETF分钟数据案例如下:


接下来,我们介绍下使用fund_etf_hist_sina接口获取ETF的日线数据。这个接口没有 period 参数,固定返回日线数据,也没有 adjust 参数,返回的是不复权数据。

输入输出参数说明如下:

获取单只ETF日线数据和批量ETF数据的例程如下所示:
芯片ETF数据形状: (1401, 6)芯片ETF前5行数据:         date   open   high    low  close      volume0  2020-02-10  1.048  1.048  1.010  1.027   9955870451  2020-02-11  1.029  1.048  1.025  1.037   7112839952  2020-02-12  1.033  1.073  1.030  1.068   8087915423  2020-02-13  1.071  1.125  1.068  1.099  12825099364  2020-02-14  1.107  1.140  1.081  1.103  1669053893计算机ETF: 获取到 1357 个交易日数据芯片ETF: 获取到 1401 个交易日数据5GETF: 获取到 1387 个交易日数据沪深300ETF: 获取到 3274 个交易日数据创业板ETF: 获取到 3382 个交易日数据
=== ETF数据汇总 ===计算机ETF:  数据期间: 2020-04-13 到 2025-11-14  最新收盘价: 1.015  总交易日数: 1357
芯片ETF:  数据期间: 2020-02-10 到 2025-11-14  最新收盘价: 1.686  总交易日数: 1401
5GETF:  数据期间: 2020-02-28 到 2025-11-14  最新收盘价: 1.605  总交易日数: 1387
沪深300ETF:  数据期间: 2012-05-28 到 2025-11-14  最新收盘价: 4.741  总交易日数: 3274
创业板ETF:  数据期间: 2011-12-09 到 2025-11-14  最新收盘价: 3.093  总交易日数: 3382

miniQMT数据源
QMT(Quantitative Market Trading)是迅投公司开发的量化交易软件,专供券商采购,现在个人投资者也可申请使用。

miniQMT是QMT的简化版,执行完安装过程这两个就都有了。

miniQMT提供了一个XtQuant的Python库,可以import它并调用它的方法获取数据和下单交易。

XtQuant 目前不能通过 pip 安装,可以下载后放在Python第三方库目录下。

from TradeDrv.QMT_Drv import xtdatafrom TradeDrv.QMT_Drv import xtconstantfrom TradeDrv.QMT_Drv.xttype import StockAccountfrom TradeDrv.QMT_Drv.xttrader import XtQuantTrader
我们使用miniQMT的xtdata接口作为新的ETF行情数据源!
在使用时,要先登录QMT交易终端,输入账号和密码,勾选“极简模式”登录。

对于ETF实时数据,这里做一个基础的demo给大家参考,如下所示:

返回的数据格式如下所示:

          代码        名称                 时间    最新价  ...    买一价    买一量    卖一价    卖一量0  159995.SZ     芯片ETF  20251114 15:00:06  1.686  ...  1.686   1032  1.687  184311  159998.SZ    计算机ETF  20251114 15:00:06  1.015  ...  1.015   1394  1.016   19472  159994.SZ     5GETF  20251114 15:00:06  1.605  ...  1.605    933  1.606    1283  510300.SH  沪深300ETF  20251114 15:00:13  4.741  ...  4.742    254  4.743   18094  159915.SZ    创业板ETF  20251114 15:00:06  3.093  ...  3.093  72296  3.094    600
[升级]15天搭建ETF量化交易系统Day1—miniqmt AkShare组合数据源
[5 rows x 15 columns]

xtdata.get_full_tick 获取实时分笔行情数据 – 返回指定品种的最新实时行情快照,包含买卖盘口等详细信息。

的参数是一个列表,这样就可以把整个ETF池子输入

返回一个字典,格式为:

{    '代码1': {字段1: 值1, 字段2: 值2, ...},    '代码2': {字段1: 值1, 字段2: 值2, ...},    ...}

主要字段包括如下内容:

对于ETF行情数据,这里做一个基础的demo给大家参考,更改周期参数就能分别获取日线和不同分钟的ETF行情数据,如下所示:

最关键的三个函数是:

xtdata.subscribe_quote()

订阅实时行情数据 – 建立与行情服务器的连接,开始接收指定品种的实时行情推送。

xtdata.download_history_data()

下载历史数据 – 从服务器下载指定品种的历史K线数据到本地缓存。

xtdata.get_market_data()

获取市场数据 – 从本地缓存中读取已经下载的行情数据。

比如周期参数是15m获取到的数据如下所示:

                时间     开盘     最高     最低     收盘      成交量          成交额0   20251106141500  3.201  3.213  3.201  3.206   632602  202922373.01   20251106143000  3.205  3.209  3.202  3.206   233291   74779183.02   20251106144500  3.206  3.209  3.196  3.197   591590  189295249.03   20251106150000  3.197  3.202  3.196  3.201  1030327  329668105.04   20251107094500  3.184  3.192  3.155  3.191  2166504  687268464.0..             ...    ...    ...    ...    ...      ...          ...95  20251114140000  3.125  3.128  3.121  3.127   248980   77794446.096  20251114141500  3.128  3.134  3.125  3.126   544069  170276065.097  20251114143000  3.127  3.128  3.117  3.118   527072  164520556.098  20251114144500  3.118  3.121  3.108  3.111   836129  260356021.099  20251114150000  3.110  3.110  3.091  3.093  1445268  448003973.0
[100 rows x 7 columns]

比如周期参数是1d获取到的数据如下所示:

          时间     开盘     最高     最低     收盘       成交量           成交额0   20250620  2.005  2.015  1.987  1.988   5926744  1.183215e+091   20250623  1.980  2.001  1.975  1.998   6068657  1.207204e+092   20250624  2.004  2.044  2.003  2.044  12388429  2.518135e+093   20250625  2.050  2.113  2.050  2.109  16381973  3.406557e+094   20250626  2.109  2.121  2.092  2.093  11040739  2.325635e+09..       ...    ...    ...    ...    ...       ...           ...95  20251110  3.200  3.215  3.117  3.158  11746888  3.697770e+0996  20251111  3.175  3.188  3.107  3.113  10573098  3.323336e+0997  20251112  3.095  3.116  3.058  3.108  11814661  3.648763e+0998  20251113  3.094  3.188  3.086  3.182  16018683  5.045455e+0999  20251114  3.146  3.146  3.091  3.093  12217523  3.818689e+09[100 rows x 7 columns]
封装成类

作为一套系统,我们需要创建一个ETF数据源模块,供其他模块调用。于是创建一个ETFDataSource类,这个类提供了三个方法

  • get_etf_codes_and_names:查询ETF代码和名称,返回DataFrame。

  • get_etf_real_time_data:获取指定ETF基金的实时行情数据,返回Series。

  • get_etf_hist_data:获取指定ETF基金在指定日期范围内的历史走势数据,返回DataFrame。

关键例程代码如下所示,完整代码可以前往知识星球下载。

class ETFDataSource:    '''    ETF数据源模块,封装了akshare库的相关接口。    '''
    def __init__(self):        '''        初始化ETF数据源模块。        '''        pass
    def get_etf_codes_and_names(self):        '''        查询ETF代码和名称。        Returns:        pd.DataFrame: 包含ETF代码和名称的DataFrame。        '''        # 省略        return etf_data[['代码''名称']]
    def get_etf_real_time_data(self, symbol):        '''        获取ETF实时行情数据。
        Args:            symbol (str): ETF基金的代码。
        Returns:            pd.Series: 包含ETF实时行情数据的Series。        '''        # 使用akshare的fund_etf_category_sina接口获取ETF实时行情数据        return real_time_data[real_time_data['代码'] == code]
    def get_etf_hist_data(self, symbol, start_date, end_date):        '''        获取ETF历史走势数据。
        Args:            symbol (str): ETF基金的代码。            start_date (str): 开始日期,格式为'YYYYMMDD'。            end_date (str): 结束日期,格式为'YYYYMMDD'。
        Returns:            pd.DataFrame: 包含ETF历史走势数据的DataFrame。        '''        # 省略
        return hist_data
if __name__ == '__main__':    # 创建ETF数据源对象    etf_source = ETFDataSource()
    # 查询ETF代码和名称    etf_codes_and_names = etf_source.get_etf_codes_and_names()    print('ETF代码和名称:')    print(etf_codes_and_names)
    # 假设要查询代码为'510300'的ETF实时行情数据    symbol = '510300'    etf_real_time_data = etf_source.get_etf_real_time_data(symbol)    print(f'nETF {symbol} 实时行情数据:')    print(etf_real_time_data)
    # 假设要查询代码为'510300'的ETF在指定日期范围内的历史走势数据    start_date = '20240401'    end_date = '20240426'    etf_hist_data = etf_source.get_etf_hist_data(symbol, start_date, end_date)    print(f'nETF {symbol}{start_date}{end_date}历史走势数据:')    print(etf_hist_data)

#artContent h1{font-size:16px;font-weight: 400;}#artContent p img{float:none !important;}#artContent table{width:100% !important;}