问题描述
解决方案
这是一个基于 PTrade 平台的简单 Tick 级做市商(Market Maker)策略。
策略逻辑说明
做市商策略的核心在于提供流动性并赚取买卖价差(Spread)。
- 运行频率:使用
tick_data函数,策略会在每一个 Tick(行情快照更新,通常为3秒)触发一次。 - 撤单逻辑:在每次 Tick 开始时,先撤销上一时刻未成交的挂单,防止订单堆积占用资金或持仓。
- 盘口挂单:
- 买单:以当前的**买一价(Bid 1)**挂单买入。
- 卖单:以当前的**卖一价(Ask 1)**挂单卖出。
- 仓位控制:为了防止单边持仓过重,策略中加入了简单的库存管理(Inventory Control),当持仓超过上限时不买,低于下限时不卖。
- 底仓设置:为了在回测开始时就能进行卖出操作,我们在
initialize中设置了底仓。
策略代码
# 简单的 Tick 级做市商策略
def initialize(context):
"""
初始化函数,设置股票池、全局变量和底仓
"""
# 1. 设置要操作的标的(这里以恒生电子为例)
g.security = '600570.SS'
set_universe(g.security)
# 2. 策略参数设置
g.trade_vol = 100 # 每次挂单数量(股)
g.max_position = 20000 # 最大持仓上限(防止买太多)
g.min_position = 1000 # 最小持仓下限(防止卖空)
# 3. 设置回测时的初始底仓(实盘中请忽略或根据实际情况调整)
# 格式:[{'sid':代码, 'amount':数量, 'enable_amount':可用数量, 'cost_basis':成本}]
# 做市商需要有底仓才能挂卖单
pos = {
'sid': g.security,
'amount': 10000,
'enable_amount': 10000,
'cost_basis': 40.0
}
set_yesterday_position([pos])
log.info("策略初始化完成,标的:%s,初始底仓:10000" % g.security)
def tick_data(context, data):
"""
Tick 数据处理函数,由系统每隔约3秒调用一次
"""
security = g.security
# ----------------------------------------------------------------
# 1. 撤单逻辑:撤销当前标的的所有未完成订单
# 做市商需要跟随盘口价格跳动,旧的挂单如果未成交需要及时撤销
# ----------------------------------------------------------------
open_orders = get_open_orders(security)
if open_orders:
for order_obj in open_orders:
# 仅撤销未成交或部分成交的订单
if order_obj.status in ['0', '1', '2', '3', '4', '7']:
cancel_order(order_obj.id)
# ----------------------------------------------------------------
# 2. 获取当前持仓和资金情况
# ----------------------------------------------------------------
position = get_position(security)
current_amount = position.amount # 当前持仓数量
# ----------------------------------------------------------------
# 3. 盘口挂单逻辑 (Market Making)
# 使用 order_tick 接口,priceGear 参数可以直接指定盘口档位
# priceGear='1' 代表买一价,priceGear='-1' 代表卖一价
# ----------------------------------------------------------------
# --- 挂买单逻辑 ---
# 如果当前持仓未达到上限,则在【买一价】挂单买入
if current_amount < g.max_position:
# order_tick: amount为正数代表买入
# priceGear='1': 以当前盘口买一价委托
order_tick(security, g.trade_vol, priceGear='1')
# --- 挂卖单逻辑 ---
# 如果当前持仓大于下限,则在【卖一价】挂单卖出
if current_amount > g.min_position:
# order_tick: amount为负数代表卖出
# priceGear='-1': 以当前盘口卖一价委托
order_tick(security, -g.trade_vol, priceGear='-1')
def handle_data(context, data):
"""
必须实现的函数,但在 Tick 策略中通常留空,
因为主要逻辑在 tick_data 中执行。
"""
pass
代码关键点解析
-
order_tick(sid, amount, priceGear=...):- 这是 PTrade 专门用于 Tick 级交易的函数。
priceGear='1':表示以买一价委托。priceGear='-1':表示以卖一价委托。- 这种方式比手动获取
get_snapshot里的价格再填入limit_price要快且方便,能确保你的挂单始终贴在盘口上。
-
get_open_orders&cancel_order:- 做市策略必须具备高频的撤单能力。如果盘口价格变动(例如买一价从 10.00 变为 10.01),旧的 10.00 的买单就失去了做市意义(变成了买二或更后),因此需要撤单并重新挂在新的买一价上。
-
set_yesterday_position:- 在回测环境中,默认资金账号是空仓的。做市商策略需要双向报价(既买也卖)。如果没有底仓,策略一开始只能挂买单,无法挂卖单。因此使用此函数虚拟建立底仓。
风险提示
这是一个最基础的做市模型,实盘中做市商策略会面临以下风险,需要更复杂的逻辑来处理:
- 单边行情风险:如果股价快速单边上涨,你可能会过早卖光筹码;如果快速下跌,你可能会接满仓位的“飞刀”。
- 成交不平衡:需要动态调整买卖价差或停止单边报价来平衡库存。
- 手续费:高频的买卖会产生大量手续费,策略的价差利润必须覆盖交易成本。
Q&A
Q1: 为什么使用 order_tick 而不是普通的 order 函数?
A1: order_tick 是 PTrade 专门为 Tick 级别高频场景设计的。它支持 priceGear 参数(如 '1', '-1'),可以直接锁定盘口档位(买一、卖一等),无需先查询行情再手动输入价格,执行效率更高,代码更简洁。
Q2: tick_data 函数的运行频率是多少?
A2: 在 PTrade 中,tick_data 通常跟随交易所的行情快照推送频率,大约每 3 秒执行一次。
Q3: 如何避免在单边下跌行情中买入过多股票?
A3: 可以在代码中加入趋势判断逻辑。例如,计算过去 N 个 Tick 的价格均值,如果当前价格显著低于均值(急跌),则暂停挂买单;或者引入库存倾斜逻辑(Inventory Skew),当持仓过多时,大幅降低买单报价(挂在买三、买五)或停止买入,同时积极挂卖单。