🚀 AI 一键生成 joinquant 策略代码

如何用 JoinQuant 编写一个简单的 Tick 级高频交易策略?

joinquant | 发布时间: 2025/11/25
以下内容由 EasyQuant 生成。

问题描述

如何编写一个策略,在每次股票价格变动时都执行一次?

解决方案

要在聚宽(JoinQuant)中实现“每次股票价格变动时都执行一次”的策略,你需要使用 Tick 级频率

在聚宽的架构中,标准的 handle_data 函数通常用于按天或按分钟运行。要捕捉每一次价格变动(快照),必须使用事件驱动的 handle_tick 函数,并配合 subscribe 订阅功能。

以下是实现该策略的关键步骤和完整代码示例:

关键步骤说明

  1. 开启真实价格模式:Tick 级回测必须使用真实价格模式,需要在 initialize 中调用 set_option('use_real_price', True)
  2. 订阅 Tick 数据:使用 subscribe(security, 'tick') 函数订阅你关注的标的。通常在 before_trading_startinitialize 中进行。
  3. 实现 handle_tick 函数:这是核心部分。每当订阅的标的有新的 Tick 数据(价格变动或盘口变化)产生时,系统会自动调用此函数。
  4. 回测设置:在运行回测时,必须在界面上将回测频率设置为 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订阅")

注意事项

  1. 回测速度:Tick 数据量非常大(股票约3秒一个快照,期货约0.5秒),回测速度会比分钟级慢很多。建议先用短时间段(如几天)进行调试。
  2. handle_data 与 handle_tick:在 Tick 回测中,handle_data(分钟/日级回调)通常不会被调用,或者不建议混用,应主要逻辑放在 handle_tick 中。
  3. Tick 对象属性tick 对象包含了丰富的盘口信息,如 tick.a1_v (卖一量), tick.b1_p (买一价) 等,可用于编写更复杂的盘口策略。
  4. 运行频率设置:在聚宽网页端运行回测时,请务必在“运行频率”选项中选择 Tick,否则上述代码中的 handle_tick 不会触发。