3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个基于聚宽(JoinQuant)API 编写的简单 Tick 级价差套利策略。
卖一价 - 买一价 >= 阈值,则以 买一价 挂单买入(试图作为 Maker 吃掉价差的一端)。# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、股票池、参数等
"""
# 1. 设定基准
set_benchmark('000300.XSHG')
# 2. 开启真实价格模式 (Tick级回测必须开启)
set_option('use_real_price', True)
# 3. 设置手续费 (模拟股票交易,印花税等)
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 4. 定义要操作的标的 (这里以平安银行为例)
g.security = '000001.XSHE'
# 5. 定义价差阈值 (单位:元)
# 如果 卖一价 - 买一价 >= 0.02元,则触发交易
g.spread_threshold = 0.02
# 6. 订阅 Tick 事件 (必须步骤)
subscribe(g.security, 'tick')
# 7. 记录上一次操作,防止同一tick重复下单
g.last_order_time = None
def handle_tick(context, tick):
"""
Tick处理函数,每有一个新的Tick数据推送时调用一次
"""
security = g.security
# 获取当前的买一价和卖一价
# a1_p: 卖一价 (Ask 1 Price)
# b1_p: 买一价 (Bid 1 Price)
ask_price = tick.a1_p
bid_price = tick.b1_p
# 过滤无效数据 (如集合竞价阶段可能价格为0)
if ask_price == 0 or bid_price == 0:
return
# 计算价差
spread = ask_price - bid_price
# 获取当前持仓数量 (可卖出的持仓)
position = context.portfolio.positions[security]
current_amount = position.total_amount
# --- 交易逻辑 ---
# 1. 买入逻辑:空仓 且 价差足够大
if current_amount == 0 and spread >= g.spread_threshold:
# 使用可用资金买入
cash = context.portfolio.available_cash
# 预留一点资金防止手续费不足,这里简单计算买入数量(向下取整到100股)
amount = int(cash / bid_price / 100) * 100
if amount >= 100:
# 挂限价单:以买一价买入 (LimitOrderStyle)
order(security, amount, style=LimitOrderStyle(bid_price))
log.info("触发买入: 卖一:%.2f, 买一:%.2f, 价差:%.2f >= 阈值:%.2f, 挂单价:%.2f" % (
ask_price, bid_price, spread, g.spread_threshold, bid_price))
# 2. 卖出逻辑:持有仓位 (这里简化为只要有货就尝试以卖一价卖出获利)
# 在实际套利中,通常是买入成交后,立即挂卖单
elif current_amount > 0:
# 获取可卖持仓
closeable_amount = position.closeable_amount
if closeable_amount > 0:
# 挂限价单:以卖一价卖出
order(security, -closeable_amount, style=LimitOrderStyle(ask_price))
log.info("触发卖出: 卖一:%.2f, 买一:%.2f, 挂单价:%.2f" % (
ask_price, bid_price, ask_price))
def on_strategy_end(context):
"""
策略结束时调用
"""
# 取消订阅
unsubscribe_all()
log.info("策略结束,取消所有订阅")
set_option('use_real_price', True):
subscribe(g.security, 'tick'):
initialize 中调用此函数订阅标的。只有订阅了,handle_tick 函数才会被触发。handle_tick(context, tick):
tick 对象包含了当前的盘口快照数据。tick.a1_p:卖一价。tick.b1_p:买一价。LimitOrderStyle (限价单):
order(..., style=LimitOrderStyle(price)) 明确指定了挂单价格。价差逻辑:
spread = ask_price - bid_price。Q: 为什么我的 Tick 策略回测非常慢?
A: Tick 数据量是分钟数据的 240 倍以上(每天约 4800 个 Tick 快照)。处理每一个 Tick 都需要消耗计算资源。建议在调试阶段只回测 1-3 天的数据,确认逻辑无误后再扩大范围。
Q: handle_tick 和 handle_data 有什么区别?
A: handle_data 是按分钟或按天定时触发的(例如每分钟的第 0 秒)。handle_tick 是由行情驱动的,每当交易所推送一个新的 Tick 快照(有成交或盘口变化)时触发。Tick 策略必须使用 handle_tick。
Q: 如何查看 Tick 对象里还有哪些数据?
A: Tick 对象还包含 current (最新价), volume (累计成交量), money (累计成交额), 以及五档盘口数据 (a1_v~a5_v, b1_v~b5_v 等)。你可以使用 print(tick) 或 log.info(tick) 在日志中查看完整结构。