问题描述
好久没有使用Ptrade做分钟级别的回测了。
发现有点蛋疼,记录一下。
因为回测模式下,不能使用run_interval 函数;
而run_daily模式,只能在固定时间运行,无法分钟级别。
解决方案
在 Ptrade 量化平台进行分钟级别的策略回测时,确实会遇到一些与实盘交易不同的 API 限制。正如您所发现的,run_interval 和 get_snapshot 都是仅限交易模块可用的接口,无法在回测环境中使用。而 run_daily 虽然可以在回测中使用,但它是用于指定具体时间点的定时任务,无法实现“每分钟触发一次”的连续逻辑。
为什么会有这些限制?
run_interval:设计用于实盘中按秒级(如每3秒)轮询,回测引擎是基于历史 K 线切片推进的,不支持这种基于真实时间的轮询。get_snapshot:获取的是实盘的实时盘口快照(包含买卖五档等),回测环境中没有这种实时的 L1/L2 快照数据。
正确的解决方案:使用 handle_data
在 Ptrade 中,进行分钟级别回测的标准做法是:
- 在策略配置界面:将“运行周期/频率”设置为分钟级别。
- 在代码中:使用
handle_data(context, data)函数。当频率设置为分钟时,回测引擎会在每个交易日的每一分钟(9:31~11:30, 13:01~15:00)自动调用一次handle_data。 - 获取数据:
- 当前分钟数据:直接通过入参
data[security]['close']等属性获取。 - 历史分钟数据:使用
get_history或get_price,并将frequency参数设置为'1m'。
- 当前分钟数据:直接通过入参
分钟级别回测代码示例
下面是一个完整的分钟级别回测策略框架,展示了如何获取当前分钟价格以及过去 N 分钟的历史数据:
def initialize(context):
# 设置我们要操作的股票池
g.security = '600570.SS'
set_universe(g.security)
# 注意:不需要写 run_interval,只要在界面上选择“分钟频率”,
# handle_data 就会每分钟自动执行一次。
def before_trading_start(context, data):
# 每天开盘前执行一次,可用于初始化每日变量
log.info("新的一天交易开始了")
def handle_data(context, data):
# 当策略频率设置为分钟时,本函数每分钟执行一次
security = g.security
# 1. 获取当前这一分钟的最新价格(相当于这一分钟K线的收盘价)
current_price = data[security]['close']
current_volume = data[security]['volume']
# 2. 获取过去 10 分钟的历史 K 线数据
# 注意 frequency='1m'
hist_data = get_history(10, frequency='1m', field=['close', 'volume'], security_list=security)
if hist_data is None or hist_data.empty:
return
# 计算过去 10 分钟的平均价
ma10_1m = hist_data['close'].mean()
# 打印日志查看数据
log.info(f"当前时间: {context.blotter.current_dt}, 当前价格: {current_price}, 10分钟均价: {ma10_1m}")
# 简单的交易逻辑示例:当前价格突破10分钟均价则买入
cash = context.portfolio.cash
if current_price > ma10_1m and cash > current_price * 100:
order(security, 100)
log.info(f"买入 {security} 100股")
总结与避坑指南
- 频率设置是关键:代码里写了
handle_data还不够,必须在 Ptrade 回测界面的参数设置里,把频率从“日线”改为“分钟”。 - 替代
get_snapshot:在回测中,如果你需要获取最新价,请直接使用data[security]['close']或者data[security]['price'],这代表了当前分钟切片的最新价格,完全可以替代get_snapshot在分钟策略中的作用。 - 避免多线程冲突:在
handle_data中调用get_history时,注意不要在其他定时函数(如run_daily)中同一时刻调用,以免引发底层数据获取冲突。