由于东财的限制,原来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回测马丁格尔补仓法
DAY1我们介绍下如何搭建自己的ETF数据源模块。
我们需要三类数据:ETF代码和名称、实时行情数据以及历史行情数据。
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'''
输入参数说明:
输出参数说明:
获取单只ETF分钟数据案例如下:
批量获取ETF分钟数据案例如下:
接下来,我们介绍下使用fund_etf_hist_sina接口获取ETF的日线数据。这个接口没有 period 参数,固定返回日线数据,也没有 adjust 参数,返回的是不复权数据。
输入输出参数说明如下:
芯片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总交易日数: 14015GETF:数据期间: 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
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组合数据源](https://www.fanshouji.com/wp-content/uploads/2026/01/2031145.png)
[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;}