🚀 AI 一键生成 ptrade 策略代码

PTrade Tick 数据挖掘:简单的订单流不平衡策略

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

问题描述

如何利用Tick数据分析买卖压力?

解决方案

利用Tick数据分析买卖压力是高频交易和日内短线策略中的核心技术。在PTrade中,我们可以通过tick_data回调函数获取高频快照数据(Snapshot),利用其中的**买卖盘口挂单量(Order Book)**来计算买卖压力失衡度(Order Book Imbalance, OBI)。

以下是具体的分析逻辑与策略实现代码。

核心逻辑:盘口压力失衡度 (OBI)

买卖压力通常通过比较买盘(Bid)和卖盘(Ask)的挂单数量来衡量。

  1. 数据来源tick_data 中的 bid_grp(委买档位)和 offer_grp(委卖档位)。
  2. 计算公式
    $$ OBI = \frac{\sum V_{Bid} - \sum V_{Ask}}{\sum V_{Bid} + \sum V_{Ask}} $$
    • $V_{Bid}$:买一到买五(或买十)的总挂单量。
    • $V_{Ask}$:卖一到卖五(或卖十)的总挂单量。
  3. 信号判断
    • OBI > 阈值(如 0.3):买盘显著大于卖盘,买方压力大(推升股价),视为看多信号。
    • OBI < -阈值(如 -0.3):卖盘显著大于买盘,卖方压力大(压低股价),视为看空信号。

PTrade 策略代码实现

以下代码实现了一个基于Tick级别盘口挂单压力的自动交易策略。

def initialize(context):
    """
    策略初始化函数
    """
    # 设置要操作的股票,这里以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 策略参数设置
    g.obi_threshold = 0.4  # 压力失衡阈值,大于0.4买入,小于-0.4卖出
    g.trade_qty = 100      # 每次交易数量
    
    # 初始化持仓标志,防止同一分钟内重复下单(可选)
    g.last_trade_time = None

def tick_data(context, data):
    """
    Tick数据回调函数,每3秒或有行情更新时触发
    """
    security = g.security
    
    # 1. 获取该股票的Tick数据
    # data结构: data[security] -> {'tick': {...}, 'order': {...}, 'transcation': {...}}
    if security not in data:
        return
        
    tick_info = data[security].get('tick')
    if not tick_info:
        return

    # 2. 提取买卖档位数据
    # bid_grp 和 offer_grp 是字典,Key为档位(1-10),Value为列表 [价格, 数量, 笔数]
    # 注意:部分券商环境可能需要使用 eval() 解析字符串,但在标准API中通常直接为对象
    # 这里为了稳健性,增加了类型判断
    bid_grp = tick_info.get('bid_grp', {})
    offer_grp = tick_info.get('offer_grp', {})
    
    # 如果是字符串格式(某些旧版本接口),则进行解析
    if isinstance(bid_grp, str):
        try:
            bid_grp = eval(bid_grp)
            offer_grp = eval(offer_grp)
        except:
            return

    # 3. 计算买卖总挂单量 (取前5档)
    total_bid_vol = 0.0
    total_ask_vol = 0.0
    
    # 遍历前5档 (如果开通L2可能有10档,这里保守取前5档)
    for i in range(1, 6):
        # 获取买档数据
        if i in bid_grp:
            # bid_grp[i] 格式通常为 [price, volume, count]
            # 确保数据有效且有量
            if len(bid_grp[i]) >= 2:
                total_bid_vol += bid_grp[i][1]
        
        # 获取卖档数据
        if i in offer_grp:
            if len(offer_grp[i]) >= 2:
                total_ask_vol += offer_grp[i][1]

    # 防止分母为0
    if total_bid_vol + total_ask_vol == 0:
        return

    # 4. 计算压力失衡度 (Order Book Imbalance)
    # 范围在 [-1, 1] 之间
    obi = (total_bid_vol - total_ask_vol) / (total_bid_vol + total_ask_vol)
    
    # 获取当前最新价,用于日志记录
    current_price = tick_info.get('last_px', 0)

    # 5. 交易逻辑
    # 获取当前持仓
    position = get_position(security)
    curr_amount = position.amount
    
    # 简单的信号过滤:避免频繁开仓,这里仅作演示
    # 实际实盘中可能需要结合资金流向或成交主动性进一步过滤
    
    # 信号:买盘压力显著,且空仓 -> 买入
    if obi > g.obi_threshold and curr_amount == 0:
        log.info("买盘压力大 (OBI: %.2f), 触发买入: %s" % (obi, security))
        # 使用 order_tick 接口下单,以卖一价(对手价)下单以保证成交
        # priceGear: '-1' 代表卖一价
        order_tick(security, g.trade_qty, priceGear='-1')
        
    # 信号:卖盘压力显著,且有持仓 -> 卖出
    elif obi < -g.obi_threshold and curr_amount > 0:
        log.info("卖盘压力大 (OBI: %.2f), 触发卖出: %s" % (obi, security))
        # 以买一价(对手价)下单
        # priceGear: '1' 代表买一价
        order_tick(security, -curr_amount, priceGear='1')

def handle_data(context, data):
    """
    必须实现的函数,但在Tick策略中通常留空或用于每日级别的辅助逻辑
    """
    pass

代码关键点解析

  1. tick_data 入口

    • 这是PTrade专门处理Tick级别数据的函数。与handle_data不同,它在交易时间内只要有新的Tick推送(通常间隔3秒)就会执行一次。
    • 注意:此函数仅在交易模式下有效,回测模式下如果选择分钟级回测,不会触发此函数;必须选择Tick级回测。
  2. 数据提取 (bid_grp/offer_grp)

    • PTrade的Tick数据结构中,买卖档位是一个字典。
    • Key是档位(1, 2, 3...),Value是一个列表 [价格, 数量, 笔数]
    • 代码中累加了前5档的挂单量(Volume),这是衡量压力的直接指标。
  3. OBI 指标计算

    • obi = (BidVol - AskVol) / (BidVol + AskVol)
    • 结果接近 1 表示几乎全是买单(涨停板附近常见)。
    • 结果接近 -1 表示几乎全是卖单(跌停板附近常见)。
    • 结果接近 0 表示买卖力量均衡。
  4. 下单接口 order_tick

    • tick_data 中必须使用 order_tick 而不是普通的 order
    • priceGear 参数非常实用:
      • '1':买一价。
      • '-1':卖一价。
      • 使用对手价(买入用卖一,卖出用买一)可以提高高频交易的成交概率。

进阶分析思路(可扩展)

除了上述的基础盘口压力,你还可以结合以下维度进行更深入的分析:

  1. 加权平均价格 (VWAP) 偏离
    计算盘口挂单的加权均价,如果盘口均价显著高于最新成交价,说明上方抛压重但下方承接力也强,或者买方意愿更强(视具体分布而定)。

  2. 逐笔成交分析 (Transaction Data)
    利用 data[security]['transcation'](注意API文档中的拼写)获取最近一笔成交的 business_direction(成交方向)。

    • 如果 obi > 0 且 成交方向为 主动买入 (Buy),则上涨概率更高。
    • 如果 obi > 0 但 成交方向持续为 主动卖出 (Sell),则可能是“假托单”,即主力在盘口挂大买单诱多,实际在出货。
  3. 撤单率分析
    通过对比连续两个Tick的 order_no 或挂单量变化,判断是否存在频繁撤单(虚假挂单)的行为。


Q&A

Q1: 为什么在回测中 tick_data 没有触发?
A1: 请检查回测频率设置。必须将回测频率设置为 “Tick级别”。如果是日线或分钟线回测,tick_data 函数不会被调用。

Q2: bid_grp 返回的数据有时候是字符串有时候是字典?
A2: 这取决于PTrade的具体部署版本和券商接口。标准的Python对象应该是字典,但部分旧接口可能返回JSON字符串。代码中加入了 isinstance(..., str) 的判断和 eval() 处理以增强兼容性。

Q3: 如何获取 Level-2 的十档行情?
A3: 如果你的账户开通了 Level-2 权限,bid_grpoffer_grp 字典中会包含 Key 为 6 到 10 的数据。你只需将代码中的循环范围 range(1, 6) 改为 range(1, 11) 即可纳入计算。

Q4: order_tick 下单后如何确认成交?
A4: order_tick 返回的是委托流水号。你可以通过 get_orders() 或监听 on_trade_response(交易主推)回调函数来实时获取成交状态。在高频策略中,通常建议使用 on_trade_response 以获得更低的延迟反馈。