固定价差回转:成交后反向挂一档(QMT / PTrade)
库里叫「回转交易」「固定价差做 T」。不是选股,也不是动量轮动。先在基准价上下挂限价,整笔满成后再按固定价差反向挂一档,循环。股票常见一档 0.01,ETF 常见 0.001。档位数、股数、价差都是示例,不是操作指令。不构成投资建议,不代操盘。
手册:passorder · order · 委托回调 · get_snapshot
逻辑简述
- 开盘先铺档。基准价优先用昨天记下的回转价,没有就用昨收。向上挂 N 档卖、向下挂 M 档买。
- 满成才回转。
deal_callback/on_trade_response里把剩余量减到 0,才按委托价加减一档反向挂,不要用零碎成交价当下一档。 - 价差分品种。股票两位小数,ETF 三位。混用会废单。
- 状态要落盘。回转价写进 pkl / json,文件名带上价差,多实例才不会互相覆盖。隔夜券商撤单后,盘前按
last_side决定先挂买还是先挂卖。 - 限流。每秒笔数、9:25–9:30 集合竞价窗口、总回转笔数都要卡,否则柜台拒单。
PTrade 还有另一套「固定价差做 T」:每标的只维持一笔,基准价 P 是上一笔整笔成交均价,买限价 min(P-A, 卖一),卖限价 max(P+A, 买一),用快照夹盘。和「铺多档 + 回调回转」是同一类,状态机更严。
QMT 版本
上交所用 .SH。板块当股票池。init 里 set_account,成交走 deal_callback。回转价文件按 stock0_01_etf0_001 这种后缀区分。
#encoding:gbk
def init(C):
try:
g.acct = account
g.acct_type = accountType
except NameError:
g.acct = ''
g.acct_type = 'STOCK'
C.set_account(g.acct)
g.diff_stock = 0.01
g.diff_etf = 0.001
g.mode = 3 # 1 只股票 2 只 ETF 3 都转
g.reversal_price_source = 'order' # 次日铺档用委托价;回转当下仍用委托价±1档
# 回转价落到策略目录,文件名带价差,避免多实例抢同一份 pkl
def order_callback(C, orderInfo):
# 记下 order_id -> 代码、限价、方向、原数量、剩余数量
pass
def deal_callback(C, dealInfo):
deal_id = dealInfo.m_strOrderSysID
# trade_dict[deal_id]['res_vol'] -= 本笔成交量
# 剩余不为 0 就返回
code = trade_dict[deal_id]['code']
is_stock = code[0] in '036984'
diff = g.diff_stock if is_stock else g.diff_etf
digits = 2 if is_stock else 3
src = trade_dict[deal_id]['price']
if trade_dict[deal_id]['flag'] == 49: # 原卖,回转买
px = round(src - diff, digits)
op = 23
else: # 原买,回转卖
px = round(src + diff, digits)
op = 24
passorder(op, 1101, g.acct, code, 11, px, trade_dict[deal_id]['all_vol'],
'策略1', 2, '', C)信用户买入常见 33 担保品 / 27 融资,卖出 34。不是两融标的不要硬走 27。passorder 返回不等于成交。
铺档在 9:31 或启动时跑:有持仓可以跳过「现价远高于基准就不出买」那条,避免空仓时把资金挂死在天上。无仓且现价已经比基准高一截,本轮可以不铺。现价用 get_full_tick,盘前空了再用昨收。
PTrade 版本
上交所用 .SS。要回转手工单,初始化里 set_parameters(receive_other_response='1'),否则 on_trade_response 收不到策略外成交。
def initialize(context):
g.diff_stock = 0.01
g.diff_etf = 0.001
g.mode = 3
set_parameters(receive_other_response='1')
# reversal_prices.json 放 get_research_path()
def on_trade_response(context, trade_list):
for t in trade_list:
if not _is_full_fill(t):
continue
security = normalize_stock_code(t.security)
diff = g.diff_stock if is_stock(security) else g.diff_etf
digits = 2 if is_stock(security) else 3
src = float(t.limit_price) # 回转按委托价,不是成交价
if t.side == 'BUY':
order(security, -t.volume, limit_price=round(src + diff, digits))
else:
order(security, t.volume, limit_price=round(src - diff, digits))「固定价差做 T」那套不铺多档,每标的状态是 空仓可买 → 挂买中 → 可卖出 → 挂卖中。P 用整笔成交均价,不要用持仓成本。夹盘:
buy_px = min(P - A, ask1) # 再和涨跌停比一遍
sell_px = max(P + A, bid1)五档来自 get_snapshot 的 bid_grp / offer_grp。快照大约 3 秒一帧,见 快照 3 秒。回测慎用快照,用 K 线近似盘口。get_order 可能返回对象或 list,满成后柜台有时不再返回该单,要用当日委托簿补链。
两边都容易踩的坑
- 从 QMT 拷到 PTrade,先换
.SH→.SS。见.SS/.SH。 - 回转用委托价加减档,不要用零碎成交价,否则档位会漂。
- 部分成交不要反向。必须整笔数量满成。
- 股票两位、ETF 三位。限价精度错了直接废单。
- 回测几乎验不了成交主推和隔夜撤单。先仿真把委托、成交、持仓对清楚。
- 不要把池外成交默认回转,除非你明确打开开关。
完整源码见知识星球附件,这里只放逻辑和片段。
完整讨论和附件在 知识星球 Kimi量化研究所。
