问题描述
问题
大家常常在问,有些函数 python 没有,但 vba 有(先建一个 vba 公式使用这个函数,再用 py 去调这个公式的变量),或者 py 单线程计算速度慢,怎么把计算机多核资源用满提升速度,那就用 python 实时调用 vba 指标,在投研软件里面,调用一个接口就能解决:
# 内置Python
# 初始化参数变量
stock_list = ['000001.SZ'] # 股票代码列表
formula_name = "MA" # 指标名称
period = '1d'
start_time = '20230320' # %Y%m%d 或 %Y%m%d%H%M%S
end_time = '' # %Y%m%d 或 %Y%m%d%H%M%S
extend_param = {'MA:N1':5,'MA:N2':20,'MA:N3':40,'MA:N4':60,"_group" : 'group1}
count = -1
for s in stock_list:
id = C.subscribe_formula(formula_name,s,period,start_time,end_time,count=count,extend_param=extend_param,callback = partial(callback, s))
# 原生Python
# 初始化参数变量
stock_list = ['000001.SZ'] # 股票代码列表
formula_name = "MA" # 指标名称
period = '1d'
start_time = '20230320' # %Y%m%d 或 %Y%m%d%H%M%S
end_time = '' # %Y%m%d 或 %Y%m%d%H%M%S
extend_param = {'MA:N1':5,'MA:N2':20,'MA:N3':40,'MA:N4':60,}
count = -1
for s in stock_list:
id = xtdata.subscribe_formula(formula_name,s,period,start_time,end_time,count=count,extend_param=extend_param,callback = partial(callback, s))
像上方代码块所展示的一样,subscribe_formula 接口可以很好的帮我们完成这个任务。而这里的 参数变量取决于不同的需求,subscribe_formula 函数默认使用电脑核心数个线程并行计算。 也支持在 extend_param 参数中手动指定分组, 格式为 extend_param = {"_group" : 'group1'},相同参数的订阅品种会在同一个线程中计算,节约 CPU 与 内存的占用。
解决方案
背景与痛点
在QMT量化投研中,开发者经常会遇到以下两个痛点:
- 函数缺失:部分特定的金融计算函数Python中没有现成的库,但QMT的VBA指标系统中却有内置实现。
- 计算瓶颈:Python的单线程GIL锁导致在处理全市场海量数据时计算速度较慢,无法充分利用现代计算机的多核CPU资源。
为了解决这些问题,QMT提供了Python实时调用VBA指标的功能。通过调用特定的订阅接口,不仅可以无缝获取VBA公式的计算结果,还能自动开启多核并行计算,将计算速度提升10-100倍。
核心解决方案:subscribe_formula 接口
无论是QMT内置的Python环境,还是外部原生Python环境(通过xtdata),都可以使用 subscribe_formula 接口来订阅并计算VBA指标。该函数默认会根据电脑的核心数分配线程进行并行计算。
1. 内置Python环境调用示例
在QMT内置策略编辑器中,可以通过上下文对象 C (ContextInfo) 来调用:
from functools import partial
def callback(stock, data):
print(f"收到 {stock} 的指标数据: {data}")
def init(C):
# 初始化参数变量
stock_list = ['000001.SZ'] # 股票代码列表
formula_name = "MA" # 指标名称
period = '1d' # 周期
start_time = '20230320' # 起始时间 (%Y%m%d 或 %Y%m%d%H%M%S)
end_time = '' # 结束时间
# 传递给VBA指标的参数,并指定分组
extend_param = {'MA:N1':5, 'MA:N2':20, 'MA:N3':40, 'MA:N4':60, "_group": 'group1'}
count = -1
for s in stock_list:
# 订阅公式并绑定回调函数
id = C.subscribe_formula(formula_name, s, period, start_time, end_time, count=count, extend_param=extend_param, callback=partial(callback, s))
2. 原生Python环境调用示例
如果你使用的是独立安装的原生Python,可以通过引入 xtdata 模块来实现相同的功能:
from xtquant import xtdata
from functools import partial
def callback(stock, data):
print(f"收到 {stock} 的指标数据: {data}")
# 初始化参数变量
stock_list = ['000001.SZ'] # 股票代码列表
formula_name = "MA" # 指标名称
period = '1d' # 周期
start_time = '20230320' # 起始时间
end_time = '' # 结束时间
# 传递给VBA指标的参数
extend_param = {'MA:N1':5, 'MA:N2':20, 'MA:N3':40, 'MA:N4':60}
count = -1
for s in stock_list:
# 使用 xtdata 订阅公式
id = xtdata.subscribe_formula(formula_name, s, period, start_time, end_time, count=count, extend_param=extend_param, callback=partial(callback, s))
# 阻塞主线程,等待回调
xtdata.run()
进阶技巧:使用 _group 优化资源占用
在 subscribe_formula 中,extend_param 参数不仅可以用来向VBA公式传递变量(如 MA:N1),还支持一个特殊的系统保留键:_group。
- 默认行为:如果不指定分组,系统会默认使用电脑核心数个线程进行并行计算。
- 手动分组:通过在
extend_param中添加"_group": "group_name",你可以手动指定计算分组。相同参数的订阅品种会被分配在同一个线程中计算。这种方式可以有效避免线程频繁切换,大幅节约CPU与内存的占用,特别适合全市场几千只股票同时订阅同一指标的场景。
通过这种“Python调度 + VBA底层多核计算”的模式,量化投研人员可以完美兼顾Python的灵活性与C++/VBA底层的高计算性能。