问题描述
解决方案
要在聚宽(JoinQuant)中实现“每次股票价格变动时都执行一次”的策略,你需要使用 Tick 级频率。
在聚宽的架构中,标准的 handle_data 函数通常用于按天或按分钟运行。要捕捉每一次价格变动(快照),必须使用事件驱动的 handle_tick 函数,并配合 subscribe 订阅功能。
以下是实现该策略的关键步骤和完整代码示例:
关键步骤说明
- 开启真实价格模式:Tick 级回测必须使用真实价格模式,需要在
initialize中调用set_option('use_real_price', True)。 - 订阅 Tick 数据:使用
subscribe(security, 'tick')函数订阅你关注的标的。通常在before_trading_start或initialize中进行。 - 实现 handle_tick 函数:这是核心部分。每当订阅的标的有新的 Tick 数据(价格变动或盘口变化)产生时,系统会自动调用此函数。
- 回测设置:在运行回测时,必须在界面上将回测频率设置为 Tick。
策略代码示例
这是一个完整的 Tick 级策略示例。它订阅了平安银行(000001.XSHE),并在每次价格变动时打印当前价格。为了演示交易,代码中包含了一个简单的逻辑:如果当前价大于开盘价则买入,否则卖出。
# -*- coding: utf-8 -*-
import jqdata
def initialize(context):
"""
初始化函数,设定基准等等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 【重要】Tick级回测必须开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设定要操作的股票,例如平安银行
g.security = '000001.XSHE'
# 设置手续费(可选,这里设置为股票类默认费率)
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 过滤日志,避免Tick级别日志过多导致卡顿,只显示error级别以上的系统日志
# log.set_level('order', 'error')
def before_trading_start(context):
"""
开盘前运行函数
"""
# 【重要】订阅标的的 tick 事件
# 只有订阅了,handle_tick 才会被触发
subscribe(g.security, 'tick')
log.info(f"已订阅 {g.security} 的Tick数据")
def handle_tick(context, tick):
"""
Tick处理函数
该函数在策略订阅的标的产生 tick 事件时被调用一次。
参数 tick: tick 对象,存放了触发 handle_tick 事件的 tick 数据。
"""
# 获取当前时间
current_time = tick.datetime
# 获取最新价
current_price = tick.current
# 获取当日开盘价
open_price = tick.open
# 打印Tick信息 (注意:Tick级回测日志量巨大,实际策略中建议少打日志)
# log.info(f"时间: {current_time}, 代码: {tick.code}, 最新价: {current_price}")
# --- 简单的交易逻辑示例 ---
# 如果当前价格高于开盘价,且没有持仓,则买入
if current_price > open_price and context.portfolio.positions[tick.code].total_amount == 0:
# 下单买入 100 股
order(tick.code, 100)
log.info(f"Tick触发买入: 价格 {current_price} > 开盘价 {open_price}")
# 如果当前价格低于或等于开盘价,且有持仓,则卖出
elif current_price <= open_price and context.portfolio.positions[tick.code].closeable_amount > 0:
# 卖出所有持仓
order_target(tick.code, 0)
log.info(f"Tick触发卖出: 价格 {current_price} <= 开盘价 {open_price}")
def after_trading_end(context):
"""
收盘后运行函数
"""
# 取消订阅,清理资源
unsubscribe_all()
log.info("收盘,取消所有Tick订阅")
注意事项
- 回测速度:Tick 数据量非常大(股票约3秒一个快照,期货约0.5秒),回测速度会比分钟级慢很多。建议先用短时间段(如几天)进行调试。
- handle_data 与 handle_tick:在 Tick 回测中,
handle_data(分钟/日级回调)通常不会被调用,或者不建议混用,应主要逻辑放在handle_tick中。 - Tick 对象属性:
tick对象包含了丰富的盘口信息,如tick.a1_v(卖一量),tick.b1_p(买一价) 等,可用于编写更复杂的盘口策略。 - 运行频率设置:在聚宽网页端运行回测时,请务必在“运行频率”选项中选择 Tick,否则上述代码中的
handle_tick不会触发。