🚀 AI 一键生成 ptrade 策略代码

如何利用 PTrade 的 Tick 数据分析订单簿不平衡(OBI)并进行高频预测?

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

问题描述

如何利用盘口数据计算订单簿不平衡(OBI)并进行交易?

解决方案

这是一个非常经典的微观结构交易策略。订单簿不平衡(Order Book Imbalance, OBI)通过衡量买单和卖单在数量上的差异,来预测短期价格的微小变动方向。

策略逻辑与原理

1. OBI 定义
OBI 是衡量买卖压力的指标。最基础的计算公式如下:
$$ OBI = \frac{V_{bid} - V_{ask}}{V_{bid} + V_{ask}} $$

  • $V_{bid}$:买盘(Bid)前 N 档的总挂单量。
  • $V_{ask}$:卖盘(Ask/Offer)前 N 档的总挂单量。
  • 取值范围:[-1, 1]。
    • 接近 1:买盘远大于卖盘,预示价格可能上涨。
    • 接近 -1:卖盘远大于买盘,预示价格可能下跌。
    • 接近 0:买卖力量均衡。

2. 交易逻辑

  • 买入信号:当 $OBI > \text{阈值}$(例如 0.3),且当前无持仓时,以卖一价(Ask 1)买入。
  • 卖出信号:当 $OBI < -\text{阈值}$(例如 -0.3),且当前有持仓时,以买一价(Bid 1)卖出。

3. PTrade 实现细节

  • 使用 tick_data 函数处理高频 Tick 数据(3秒一次)。
  • 数据源:data[security]['tick'] 中的 bid_grp(买档)和 offer_grp(卖档)。
  • 注意:在 PTrade 的 tick_data 中,bid_grpoffer_grp 返回的是 DataFrame 中的一列,且内容通常是字符串格式的字典,需要使用 eval() 进行解析才能获取具体的量价数据。

策略代码实现

以下是完整的 PTrade 策略代码。该代码选取了前 5 档行情来计算 OBI。

def initialize(context):
    """
    初始化函数
    """
    # 设定关注的股票,这里以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 策略参数设置
    g.obi_threshold = 0.3  # OBI 阈值,超过此值触发交易
    g.trade_amount = 100   # 每次交易的数量
    g.depth_level = 5      # 计算 OBI 使用的档位数(1-5档)

def tick_data(context, data):
    """
    Tick 级别数据处理函数,每 3 秒触发一次
    """
    security = g.security
    
    # 1. 获取 Tick 数据
    # data[security] 包含 'tick', 'order', 'transcation'
    # 我们主要需要 'tick' 数据
    if security not in data:
        return
        
    tick_df = data[security]['tick']
    
    # 检查数据是否为空
    if tick_df is None or len(tick_df) == 0:
        return

    # 2. 解析盘口数据
    # PTrade 的 tick 数据中,bid_grp 和 offer_grp 往往是字符串形式的字典
    # 格式示例: {1: [价格, 量, 笔数], 2: [...], ...}
    try:
        # 获取第一行数据(当前切片)
        bid_grp_str = tick_df['bid_grp'][0]
        offer_grp_str = tick_df['offer_grp'][0]
        
        # 使用 eval 将字符串转换为字典
        bid_grp = eval(bid_grp_str)
        offer_grp = eval(offer_grp_str)
    except Exception as e:
        # 如果解析失败或数据格式异常,直接返回
        return

    # 3. 计算买盘和卖盘的总挂单量 (前 N 档)
    total_bid_vol = 0.0
    total_ask_vol = 0.0
    
    # 遍历前 g.depth_level 档
    for i in range(1, g.depth_level + 1):
        # 字典的 key 是档位 (1, 2, 3, 4, 5)
        # value 是 list: [价格, 数量, 笔数]
        if i in bid_grp:
            total_bid_vol += bid_grp[i][1] # 累加买单数量
        if i in offer_grp:
            total_ask_vol += offer_grp[i][1] # 累加卖单数量

    # 4. 计算 OBI (Order Book Imbalance)
    # 防止除以零
    if (total_bid_vol + total_ask_vol) == 0:
        obi = 0
    else:
        obi = (total_bid_vol - total_ask_vol) / (total_bid_vol + total_ask_vol)
    
    # 打印日志方便调试 (生产环境可注释掉)
    # log.info("Time: %s, OBI: %.4f, BidVol: %s, AskVol: %s" % (
    #     tick_df['hsTimeStamp'][0], obi, total_bid_vol, total_ask_vol))

    # 5. 获取当前持仓
    position = get_position(security)
    curr_amount = position.amount
    
    # 6. 交易逻辑
    
    # 信号:买入
    # 逻辑:OBI 大于阈值 (买压大) 且 当前无持仓
    if obi > g.obi_threshold and curr_amount == 0:
        # 使用 order_tick 下单
        # priceGear='-1' 代表卖一价,即以对手价买入,保证成交速度
        order_tick(security, g.trade_amount, priceGear='-1')
        log.info("买入触发 - OBI: %.2f, 数量: %s" % (obi, g.trade_amount))
        
    # 信号:卖出
    # 逻辑:OBI 小于负阈值 (卖压大) 且 当前有持仓
    elif obi < -g.obi_threshold and curr_amount > 0:
        # priceGear='1' 代表买一价,即以对手价卖出
        # 卖出时数量为负数
        order_tick(security, -curr_amount, priceGear='1')
        log.info("卖出触发 - OBI: %.2f, 数量: %s" % (obi, curr_amount))

def handle_data(context, data):
    """
    必须实现的函数,但在 Tick 策略中通常留空
    """
    pass

代码关键点解析

  1. 数据解析 (eval)

    • PTrade 的 tick_data 返回的 DataFrame 中,bid_grpoffer_grp 字段存储的是字符串形式的字典(例如 "{1:[10.5, 1000, 5], ...}")。
    • 必须使用 eval() 函数将其还原为 Python 字典对象,才能访问具体的档位数据。
    • 字典结构通常为 {档位: [价格, 数量, 笔数]},我们取索引 [1] 来获取数量。
  2. OBI 计算

    • 代码中使用了简单的等权重求和:$\sum_{i=1}^{5} Vol_{bid_i}$。
    • 如果需要更精细的策略,可以给第一档(买一/卖一)更高的权重,例如:$Vol_{total} = 1.0 \times Vol_1 + 0.8 \times Vol_2 + ...$。
  3. 下单方式 (order_tick)

    • tick_data 中必须使用 order_tick
    • priceGear='-1':表示以卖一价(对手方最优价)下单买入,这在追涨策略中很重要,因为我们需要立即成交。
    • priceGear='1':表示以买一价下单卖出。
  4. 异常处理

    • 增加了 try...except 块,防止因为行情数据缺失或格式错误导致策略崩溃。
    • 增加了分母为 0 的判断(虽然在活跃股票中很少见,但在涨跌停或极度不活跃时可能发生)。

常见问题 Q&A

Q: 这个策略适合回测吗?
A: PTrade 支持 Tick 级别的回测,但速度较慢。OBI 策略依赖极短期的微观结构,回测时的撮合逻辑(Match Logic)可能与实盘有差异(回测通常假设你的小单能完全成交,不影响盘口),实盘中需要考虑滑点和冲击成本。

Q: 如何优化 OBI 指标?
A:

  1. 加权 OBI:距离中间价越近的档位,对价格影响越大,可以给予更高的权重衰减(如 $e^{-\alpha x}$)。
  2. 结合 VOI (Volume Order Imbalance):不仅看当前的挂单量,还要看挂单量的变化量(Delta)。
  3. 结合成交流:观察 transcation 数据,看主动买入和主动卖出的成交量。

Q: 为什么使用 eval
A: 这是 PTrade 接口特有的数据格式处理方式。在 tick_data 中,L2 的盘口数据被序列化为字符串存储在 DataFrame 中,必须反序列化才能使用。