🚀 AI 一键生成 ptrade 策略代码

PTrade 高频交易入门:如何编写一个基于 Tick 数据的做市商策略?

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

问题描述

如何编写一个利用 tick 数据进行高频做市的策略?

解决方案

这是一个基于 PTrade 平台 tick_data 接口实现的高频做市(Market Making)策略示例。

策略核心思想

做市商策略(Market Making)的核心在于赚取买卖价差(Spread)

  1. 双边挂单:在买一价(Bid 1)挂买单,在卖一价(Ask 1)挂卖单。
  2. 库存管理:根据当前持仓量调整挂单价格或数量。如果持仓过多,倾向于卖出;如果持仓过少,倾向于买入。
  3. T+0 交易:由于做市需要频繁买卖,此策略最适合 可转债ETF 等支持 T+0 回转交易的品种。如果是普通股票(T+1),当日买入的无法卖出,无法实现日内高频做市。

策略代码实现

import numpy as np

def initialize(context):
    """
    初始化函数
    """
    # 设置标的:这里以南银转债(113050.SS)为例,因为可转债是T+0交易,适合做市
    # 如果是股票,请注意T+1限制
    g.security = '113050.SS'
    set_universe(g.security)
    
    # 策略参数设置
    g.lot_size = 10          # 单笔下单数量(张/股)
    g.max_position = 200     # 最大持仓限制(绝对值)
    g.spread_threshold = 0.02 # 最小价差阈值,只有价差大于此值才交易
    
    # 交易开关
    g.trading_enabled = True

def tick_data(context, data):
    """
    Tick级别行情处理函数,每3秒触发一次(实盘/回测)
    """
    if not g.trading_enabled:
        return

    security = g.security
    
    # 1. 获取Tick数据
    # data[security] 包含 'tick', 'order', 'transcation'
    tick = data[security].get('tick')
    
    if tick is None:
        return
        
    # 获取买一档和卖一档信息
    # bid_grp/offer_grp 结构: {档位: [价格, 数量, 笔数]}
    # 注意:根据券商接口不同,有时需要用 eval() 解析,这里按标准字典处理
    try:
        # 买一价
        bid_1_price = tick['bid_grp'][1][0]
        # 卖一价
        ask_1_price = tick['offer_grp'][1][0]
    except:
        # 数据异常或无盘口时跳过
        return

    # 2. 计算价差
    spread = ask_1_price - bid_1_price
    
    # 如果价差过小,无利可图,则不进行操作
    if spread < g.spread_threshold:
        return

    # 3. 获取当前持仓和未完成订单
    position = get_position(security)
    current_amount = position.amount # 当前持仓量
    
    # 获取当前挂单,避免重复下单造成挂单堆积
    open_orders = get_open_orders(security)
    
    # 简单的撤单逻辑:如果有未成交的旧单,先撤单,跟随最新盘口
    # 在极速高频中,通常会修改订单,但在PTrade标准接口中,撤单再报是常见做法
    if open_orders:
        for _order in open_orders:
            cancel_order(_order.order_id)
        # 撤单后本Tick周期结束,等待下一Tick重新挂单,防止撤单未完成就下单导致资金占用
        return

    # 4. 做市逻辑:根据库存调整报价意愿
    
    # 检查多头库存风险
    # 如果持仓量没有超过上限,且资金充足,挂买单
    if current_amount < g.max_position:
        # 使用 order_tick 接口,priceGear='1' 代表以买一价下单
        # 也可以指定 limit_price = bid_1_price
        order_tick(security, g.lot_size, priceGear='1')
        # log.info("盘口挂买单: 价格 %s, 数量 %s" % (bid_1_price, g.lot_size))

    # 检查空头库存风险(或卖出平仓)
    # 如果持仓量大于 -g.max_position (允许做空) 或者 有持仓 (仅做多)
    # 这里假设是T+0多头做市,有持仓就挂卖单
    if current_amount > -g.max_position: 
        # 如果是纯股票T+1,需要判断 enable_amount > 0
        # 可转债T+0可以直接判断 amount
        
        # 使用 order_tick 接口,priceGear='-1' 代表以卖一价下单
        # 卖出数量为负数
        order_tick(security, -g.lot_size, priceGear='-1')
        # log.info("盘口挂卖单: 价格 %s, 数量 %s" % (ask_1_price, g.lot_size))

def handle_data(context, data):
    """
    必须实现的函数,但在Tick策略中通常留空或用于日间级别的风控检查
    """
    pass

代码详解

  1. initialize (初始化):

    • 设定了交易标的 113050.SS(南银转债)。选择可转债是因为它们支持 T+0 交易,可以在同一天内买入并卖出,这是做市策略的基础。
    • 设定了 lot_size(单笔交易数量)和 max_position(最大持仓),用于控制库存风险,防止单边持仓过重。
  2. tick_data (核心逻辑):

    • 获取盘口:通过 data[security]['tick']['bid_grp']offer_grp 获取当前的买一价和卖一价。
    • 撤单机制:高频交易中盘口变化极快。策略在每次 Tick 触发时,首先检查是否有旧的未成交订单(get_open_orders),如果有则全部撤销。这是为了防止旧的挂单在不利的价格成交(被“摘单”)。
    • 下单接口 order_tick:这是 PTrade 专门为 Tick 级交易设计的接口。
      • priceGear='1':自动跟随当前的买一价下单。
      • priceGear='-1':自动跟随当前的卖一价下单。
    • 库存控制
      • 如果当前持仓少于最大限制,则继续挂买单。
      • 如果当前持仓大于最小限制(或有货),则挂卖单。
      • 这种双边挂单的行为旨在捕捉 ask_1_price - bid_1_price 的价差收益。

注意事项

  1. 运行频率tick_data 在 PTrade 中通常每 3 秒触发一次(取决于券商端的推送频率)。这属于“准高频”,并非纳秒级的硬件高频。
  2. T+1 限制:如果你将标的换成普通股票(如 600570.SS),当日买入的股票无法卖出,会导致策略在一天内只买不卖,最终变成满仓持有。股票做市通常需要底仓(打底仓)来实现 T+0 效果。
  3. 实盘风险
    • 单边行情风险:如果市场发生剧烈单边上涨或下跌,做市商可能会过早卖飞筹码(上涨时)或接住大量下跌的飞刀(下跌时)。需要加入趋势判断或止损逻辑。
    • 手续费:高频交易对佣金非常敏感。请确保你的策略利润能覆盖交易手续费(印花税、佣金等)。

Q&A

Q: 为什么代码中使用 order_tick 而不是 order?
A: order_tick 是 PTrade 专门用于 tick_data 环境的函数。它支持 priceGear 参数(如 '1', '-1'),可以快速指定按“买一”或“卖一”价下单,而不需要手动读取价格并传入 limit_price,执行效率略高且代码更简洁。

Q: 如何在股票上使用这个策略?
A: 股票是 T+1 制度。要在股票上做市,你需要在 initialize 中通过 set_yesterday_position (回测) 或实际账户中持有一定量的“底仓”。逻辑需要修改为:当买入时,持仓增加;当卖出时,卖出的是昨天的底仓(从而变相实现 T+0)。

Q: tick_data 中的 datahandle_data 中的 data 一样吗?
A: 不一样。tick_data 中的 data 包含 Level-2 的高频数据(如逐笔委托、逐笔成交、十档盘口),结构是字典嵌套。而 handle_data 中的 data 通常是分钟级或日级的 Bar 数据对象。混用会导致报错。