3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 QMT(Quant Trader)平台中,策略运行缓慢通常是由数据获取方式不当、循环计算过多或平台参数设置不合理导致的。
以下是针对 QMT Python 策略性能优化的完整指南,分为平台设置优化、数据接口优化和代码逻辑优化三个维度。
在修改代码之前,首先检查策略编辑器的“基本信息”设置,这是最常见的性能瓶颈来源。
handlebar 函数。如果历史数据很长,启动会非常慢。100 或 200)。handlebar 就会被触发一次。对于高频策略这是必须的,但对于分钟级或日线级策略,这会造成大量无用计算。3000 毫秒)。或者在代码中控制逻辑执行频率。QMT 的底层是 C++,Python API 是通过层级调用的。频繁、细碎的数据交互会严重拖慢速度。
get_market_data_ex 替代旧接口ContextInfo.get_market_data_ex()。get_market_data 已不建议使用。新版接口在处理批量数据时效率更高,且返回格式更统一(Pandas DataFrame)。# ❌ 低效写法
for stock in stock_list:
data = ContextInfo.get_market_data_ex(..., [stock], ...)
# 处理逻辑...
# ✅ 高效写法
data_map = ContextInfo.get_market_data_ex(..., stock_list, ...)
# 此时 data_map 包含所有股票数据,直接处理
handlebar 中重复下载历史数据get_history_data 或 download_history_data 如果放在 handlebar 中且没有条件限制,会在每个 Tick 都执行下载/读取 IO 操作。init() 中一次性获取。ContextInfo.is_new_bar() 为 True 时获取。handlebar 的计算负载handlebar 是高频回调函数。
is_last_bar:如果是实盘运行,历史 K 线的 handlebar 回放通常不需要执行下单逻辑或复杂的信号计算。def handlebar(ContextInfo):
# 如果不是最后一根K线(即正在回放历史),直接跳过复杂逻辑
if not ContextInfo.is_last_bar():
return
# 下面写实盘逻辑
...
Python 的原生循环非常慢。凡是涉及价格计算(如均线、RSI、相关性),务必使用 pandas 或 numpy 的内置函数。
df['close'].rolling(window=10).mean()。print() 函数是同步 IO 操作,在大量 Tick 推送时,频繁打印会显著阻塞策略运行。logging 模块并设置较高的日志级别。init(ContextInfo) 中,而不是 handlebar(ContextInfo) 中。以下是一个应用了上述优化技巧的框架示例:
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
# 1. 在 init 中做一次性设置,避免在 handlebar 中重复操作
ContextInfo.stock_list = ['600000.SH', '000001.SZ', '600519.SH']
ContextInfo.set_universe(ContextInfo.stock_list)
# 2. 预先定义好需要的字段,避免反复创建列表
ContextInfo.fields = ['close', 'open', 'high', 'low']
# 3. 账号设置
ContextInfo.account_id = '您的账号'
ContextInfo.set_account(ContextInfo.account_id)
def handlebar(ContextInfo):
# 4. 【关键优化】如果是回测模式或历史K线回放,且不需要逐K线计算信号,可以直接跳过
# 如果设置了“快速计算”,这里的负担会小很多
if not ContextInfo.is_last_bar():
return
# 5. 【关键优化】使用 get_market_data_ex 批量获取数据
# count=-1 表示获取所有订阅的数据,或者指定 count=200 用于计算指标
data_map = ContextInfo.get_market_data_ex(
ContextInfo.fields,
ContextInfo.stock_list,
period='1d',
count=20,
dividend_type='front'
)
# 6. 【关键优化】避免循环,尽量使用逻辑判断
for stock in ContextInfo.stock_list:
if stock not in data_map:
continue
df = data_map[stock]
if df.empty:
continue
# 使用 pandas 快速计算均线,而不是手写循环
ma5 = df['close'].rolling(5).mean().iloc[-1]
current_price = df['close'].iloc[-1]
# 简单的交易逻辑
if current_price > ma5:
# 检查是否已有持仓等逻辑...
pass
Q: 为什么我的策略在回测时很快,一到实盘就卡顿?
A: 回测时数据是本地读取的,速度极快。实盘时 handlebar 是由实时 Tick 驱动的。如果你的策略逻辑处理时间超过了两个 Tick 之间的间隔(例如 500ms),就会导致积压和卡顿。请检查是否在 handlebar 里做了耗时的网络请求或复杂的数学运算。
Q: get_market_data 和 get_market_data_ex 真的有很大区别吗?
A: 是的。get_market_data 是旧版接口,在处理多标的数据时性能较差。get_market_data_ex 经过优化,且直接对接 Pandas 格式,减少了数据转换的开销。
Q: “快速计算”设置为 0 会发生什么?
A: 设置为 0 意味着全量计算。假设你在日线上运行,且主图品种有 10 年历史,策略启动时会从 10 年前第一天开始,把每一天的 handlebar 都跑一遍。这通常是策略启动慢的罪魁祸首。
Q: 我可以使用多线程来加速吗?
A: 在 QMT 的 Python 环境中,不建议使用 threading 或 multiprocessing 进行复杂的计算或下单操作。因为 QMT 的底层 API 并非完全线程安全,且 Python 的 GIL 锁限制了多线程的计算效率。对于计算密集型任务,优先考虑向量化(Numpy);对于 IO 密集型任务,尽量减少频率。