本文以只做多网格策略为例,展示一个完整的期魔方期货策略写法:网格管理、分笔止盈、加仓控制、整体止损。
策略逻辑
- 首次开仓:订单列表为空时,按每笔开仓手数开多
- 加仓:价格跌破「最小开仓价 − 加仓距离」时继续加仓开多,直至达到最大持仓手数
- 分笔止盈:价格涨破「最小开仓价 + 分笔止盈」时,平仓一单位
- 整体止损:加权均价跌破「均价 − 整体止损」时,全部清仓并退出
- 网格基准:每次成交后更新订单列表,重新计算最小开仓价与总持仓
完整代码
"""
文件类型: 量化策略
帮助文档: https://jukequant.com/knowledge/category/strategy
期魔方,为您提供专业的量化服务
"""
# 整体止损清仓函数,当触发整体止损条件时,将所有持仓进行平仓操作,并标记退出标志为 True
def clear_position(context):
# 遍历订单列表
for order in context.order_list:
# 对每个订单的持仓数量进行平仓操作
sell_close(order["volume"])
# 将退出标志设置为 True,表示已经进行了清仓止损操作
context.quit_flag = True
# 整体止损处理函数,计算持仓的平均开仓价格,根据当前价格判断是否触发整体止损条件
def over_all_stop_loss(context):
# 初始化总持仓数量为 0
total_vol = 0
# 初始化加权开仓价格为 0,用于计算平均开仓价格
weighted_open_price = 0
# 遍历订单列表
for order in context.order_list:
# 累加每个订单的持仓数量到总持仓数量
total_vol += order["volume"]
# 计算每个订单的开仓价格乘以持仓数量,并累加到加权开仓价格
weighted_open_price += order["open_price"] * order["volume"]
# 计算平均开仓价格,即加权开仓价格除以总持仓数量
avg_open_price = weighted_open_price / total_vol
# 判断当前价格是否小于等于平均开仓价格减去整体止损点数乘以价格跳动单位
if context.last_price <= avg_open_price - context.overall_stop_loss_point * context.price_tick:
# 如果触发整体止损条件,调用清仓函数进行平仓操作
clear_position(context)
# 返回 True 表示已经触发整体止损
return True
else:
# 未触发整体止损条件,返回 False
return False
# 判断网格是否发生改变,并进行相应的处理,包括分笔止盈和加仓操作
def wait_grid_change(context):
# 判断当前价格是否大于等于最小开仓价格加上分笔止盈点数乘以价格跳动单位
if context.last_price >= context.min_open_price + context.take_tick * context.price_tick:
# 若向上突破网格,进行分笔止盈操作,平仓指定数量的持仓
sell_close(context.volume) # 向上突破网格,分笔止盈
# 判断当前价格是否小于等于最小开仓价格减去加仓距离点数乘以价格跳动单位
elif context.last_price <= context.min_open_price - context.plus_tick * context.price_tick:
# 若向下突破网格,且当前总持仓数量小于最大持仓手数
if context.total_position < context.max_volume:
# 进行加仓操作,开仓指定数量的持仓
buy_open(context.volume) # 向下突破网格,加仓
# 更新最小开仓价格等变量,遍历订单列表,找出最小开仓价格及其索引,并更新总持仓数量
def update_variables(context):
# 初始化订单索引为 0
index = 0
# 初始化总持仓数量为 0
total_position = 0
# 初始化最小开仓价格为 -1,表示尚未找到有效价格
context.min_open_price = -1
# 初始化最小开仓价格对应的订单索引为 -1,表示尚未找到有效索引
context.min_open_price_index = -1
# 遍历订单列表
for order in context.order_list:
# 如果最小开仓价格仍为 -1,说明是第一次遍历,直接将当前订单的开仓价格作为最小开仓价格
if context.min_open_price == -1:
context.min_open_price = order["open_price"]
context.min_open_price_index = index
# 如果当前订单的开仓价格小于最小开仓价格,更新最小开仓价格及其索引
elif order["open_price"] < context.min_open_price:
context.min_open_price = order["open_price"]
context.min_open_price_index = index
# 订单索引加 1
index += 1
# 累加每个订单的持仓数量到总持仓数量
total_position += order["volume"]
# 更新总持仓数量
context.total_position = total_position
# 订单成交处理回调函数,处理订单成交后的相关操作,包括更新订单列表和变量
def on_trade_callback(context, trade):
# 从成交信息中获取开平标志
offset = trade.get("OffsetFlag")
# 从成交信息中获取成交价格
price = trade.get("Price")
# 从成交信息中获取成交数量
volume = trade.get("Volume")
# 判断开平标志是否为开仓(这里 "0" 表示开仓)
if offset == "0": # 开仓就增加订单列表
# 创建一个新的订单字典,包含开仓价格和成交数量
order = {"open_price": price, "volume": volume}
# 将新订单添加到订单列表
context.order_list.append(order)
else: # 平仓单就删除订单列表
# 从订单列表中移除最小开仓价格对应的订单
context.order_list.pop(context.min_open_price_index)
# 调用更新变量函数,更新最小开仓价格等相关变量
update_variables(context)
# 将首笔订单是否成交的标志设置为 False,表示首笔订单已经成交
context.weit_trade = False # 首笔订单已经成交
################################## 主程序部分 ##################################
# 从 pydantic 库中导入 BaseModel 和 Field 类,用于定义参数映射模型
from pydantic import BaseModel, Field
# 定义一个参数映射模型类
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
"""
参数1:每笔开仓手数:1手
参数2:加仓距离:10个点
参数3:最大持仓手数:3手
参数4:分笔止盈:10个点
参数5:止损风控:30个点(相对于均价)
"""
# 定义每笔开仓手数,默认值为 1
volume: int = Field(default=1, title="每笔开仓手数")
# 定义加仓距离,单位为跳,默认值为 10
plus_tick: int = Field(default=10, title="加仓距离(跳)")
# 定义最大持仓手数,默认值为 3
max_volume: int = Field(default=3, title="最大持仓手数")
# 定义分笔止盈点数,单位为跳,默认值为 10
take_tick: int = Field(default=10, title="分笔止盈(跳)")
# 定义整体止损点数,单位为跳,默认值为 30
overall_stop_loss_point: int = Field(default=30, title="整体止损(跳)")
# 初始化函数,在策略启动时进行一些初始化操作
def on_init(context):
# 初始化订单列表,用于保存开仓订单
context.order_list = [] # 保存开仓订单
# 从基础设置中获取合约 ID
context.base_instrument_id = BASE_SETTING.get("Instrument")
# 初始化最新价格为 -1,表示尚未获取到有效价格
context.last_price = -1 # 保存最新价格
# 获取合约的价格跳动单位
context.price_tick = get_symbolinfo(context.base_instrument_id)["price_tick"]
# 初始化退出标志为 False,表示尚未进行清仓止损操作
context.quit_flag = False
# 初始化最小开仓价格为 -1,表示尚未找到有效价格
context.min_open_price = -1
# 初始化最小开仓价格对应的订单索引为 0
context.min_open_price_index = 0
# 初始化总持仓数量为 0
context.total_position = 0
# 初始化首笔订单是否成交的标志为 False,表示首笔订单尚未发送或成交
context.weit_trade = False
# 处理 tick 数据的函数,根据当前状态和价格信息进行相应的操作
def on_tick(context):
# 判断是否已经进行了清仓止损操作,如果是则直接返回,不再进行后续操作
if context.quit_flag: # 已经清仓止损过了
return
# 判断首笔订单是否已经发送但还未成交,如果是则直接返回,不再进行后续操作
if context.weit_trade: # 首笔订单已经发送了,还未成交
return
# 从基础 tick 数据中获取最新价格,并转换为浮点数保存
context.last_price = float(BASE_SYMBOL_TICK.get("LastPrice"))
# 判断订单列表是否为空,如果为空说明还没有开仓
if len(context.order_list) == 0:
# 进行开仓操作,开仓指定数量的持仓,并将首笔订单是否成交的标志设置为 True
buy_open(context.volume, context.base_instrument_id)
context.weit_trade = True
else:
# 调用整体止损处理函数,判断是否触发整体止损条件
ret = over_all_stop_loss(context)
# 如果没有触发整体止损条件
if ret == False: # 没有整体止损,就监控网格变化
# 调用判断网格是否改变的函数,进行相应的处理
wait_grid_change(context)
# 处理订单状态变化的函数,这里暂时不做任何处理
def on_order(context, order):
pass
# 处理订单成交的函数,调用订单成交处理回调函数进行具体操作
def on_trade(context, trade):
on_trade_callback(context, trade)
# 处理错误信息的函数,这里暂时不做任何处理
def on_error(context, data):
pass
# 处理策略停止的函数,这里暂时不做任何处理
def on_stop(context):
pass
关键要点
- 订单列表管理:
context.order_list保存所有开仓订单,记录每笔开仓价格和数量;开仓时append追加,分笔止盈时pop移除最小开仓价对应订单 - 网格基准价:
update_variables实时找出当前持仓中开仓价最低的一笔min_open_price,作为网格判断上下沿的基准 - tick 驱动:在
on_tick中读取最新价BASE_SYMBOL_TICK["LastPrice"],先判断整体止损,再判断网格突破方向 - 分笔止盈与加仓:向上突破
min_open_price + take_tick平仓一单位;向下突破min_open_price - plus_tick且未达max_volume时加仓 - 整体止损:以加权平均开仓价为基础,跌破
均价 - overall_stop_loss_point * price_tick即清仓退出并置quit_flag - 首笔防重复:
weit_trade标志用于防止首笔开仓未成交时重复下单,成交回调中重置为False
相关阅读
- [核心API 实时行情数据]
- [核心API 历史K线数据]
- [核心API 账户持仓查询]
- [核心API 合约信息查询]
- [案例:日内清仓与套利]

评论
登录后参与讨论,与站内账号体系共用。