🚀 AI 一键生成 ptrade 策略代码
立即体验

ptrade 平台如何利用 on_trade_response 增量更新本地持仓缓存?(规避 get_position 延迟)

ptrade | 发布时间: 2026/8/13
以下内容由 EasyQuant 生成。

问题描述

ptrade 实时持仓管理:利用 on_trade_response 增量更新本地仓位缓存
当成交主推发生变化时,如何利用 on_trade_response 回调函数增量更新本地的全局变量持仓,以规避 get_position 持仓数据延迟的问题?

解决方案

为什么需要利用 on_trade_response 管理持仓?

在 ptrade 实盘交易环境中,柜台返回的持仓数据同步通常存在一定的时滞(一般在 6 秒左右)。如果策略属于高频、Tick 级交易或需要在极短时间内连续下单(例如双均线快速翻转、网格交易等),直接调用 get_position()context.portfolio.positions 可能会因为数据未及时同步而导致重复下单超卖/超买

on_trade_response 是成交主推回调函数,当柜台有订单成交时,会第一时间向策略推送成交信息。通过在本地维护一个全局变量持仓字典 g.local_positions,并在 on_trade_response 中进行增量更新,可以实现毫秒级的持仓状态同步。


核心实现逻辑

  1. 初始化同步:在 before_trading_start(每日盘前)获取当前账户的真实持仓,初始化本地缓存 g.local_positions
  2. 成交主推更新:在 on_trade_response 中解析推送的 trade_list
    • 若为买入entrust_bs == '1'),本地持仓数量增加。
    • 若为卖出entrust_bs == '2'),本地持仓数量减少,可用持仓数量相应减少。
  3. 策略下单引用:在 handle_datatick_data 中,不再调用 get_position(),而是直接读取 g.local_positions 获取最新持仓。

Python 策略源码示例

# -*- coding: utf-8 -*-

def initialize(context):
    # 设置操作的股票池
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 初始化本地持仓缓存字典
    # 格式:{ '股票代码': {'amount': 总持仓, 'enable_amount': 可用持仓} }
    g.local_positions = {}
    
    # 设置策略参数:接收非本策略产生的成交主推(可选,根据实际需求配置)
    set_parameters(receive_other_response="1")

def before_trading_start(context, data):
    log.info("--- 盘前初始化持仓缓存 ---")
    g.local_positions.clear()
    
    # 获取当前账户的所有真实持仓
    all_positions = get_positions()
    for code, pos in all_positions.items():
        g.local_positions[code] = {
            'amount': pos.amount,
            'enable_amount': pos.enable_amount
        }
    log.info("初始本地持仓缓存: %s" % g.local_positions)

def on_trade_response(context, trade_list):
    """
    成交主推回调函数:当有订单成交时,柜台会第一时间推送
    """
    for trade in trade_list:
        code = trade['stock_code']
        entrust_bs = trade['entrust_bs']  # '1'-买,'2'-卖
        filled_amount = int(trade['business_amount'])
        status = trade['status']
        
        # 仅处理已成('8')或部成('7')的推送
        if status in ['7', '8']:
            # 如果本地缓存中没有该股,先初始化
            if code not in g.local_positions:
                g.local_positions[code] = {'amount': 0, 'enable_amount': 0}
            
            if entrust_bs == '1':  # 买入成交
                g.local_positions[code]['amount'] += filled_amount
                # 注意:A股 T+1 机制下,当天买入的股票可用资金减少,但可用持仓不增加(次日才可用)
                # 如果是可转债或T+0品种,可在此处同时增加 enable_amount
                if code.startswith('11') or code.startswith('12'): # 简单判断可转债
                    g.local_positions[code]['enable_amount'] += filled_amount
                
                log.info("【买入主推】%s 成交 %d 股,最新本地持仓: %s" % (code, filled_amount, g.local_positions[code]))
                
            elif entrust_bs == '2':  # 卖出成交
                g.local_positions[code]['amount'] -= filled_amount
                g.local_positions[code]['enable_amount'] -= filled_amount
                
                # 规避减至负数边界情况
                if g.local_positions[code]['amount'] < 0:
                    g.local_positions[code]['amount'] = 0
                if g.local_positions[code]['enable_amount'] < 0:
                    g.local_positions[code]['enable_amount'] = 0
                    
                log.info("【卖出主推】%s 成交 %d 股,最新本地持仓: %s" % (code, filled_amount, g.local_positions[code]))

def handle_data(context, data):
    # 示例:获取本地缓存持仓
    code = g.security
    pos_info = g.local_positions.get(code, {'amount': 0, 'enable_amount': 0})
    
    current_amount = pos_info['amount']
    enable_amount = pos_info['enable_amount']
    
    # 策略逻辑中使用本地持仓判断,完美规避 6 秒延迟
    if current_amount == 0:
        # 执行买入逻辑
        order(code, 100)
        # 为了防止在主推未到达的极短间隙重复下单,可在下单后立即虚拟占扣本地可用仓位
        # g.local_positions[code] = {'amount': 100, 'enable_amount': 0}
    elif enable_amount >= 100:
        # 执行卖出逻辑
        order(code, -100)
        # 模拟扣减可用持仓,防止重复报单
        g.local_positions[code]['enable_amount'] -= 100

注意事项

  1. T+1 交易限制:A股股票实行 T+1 交易,当天买入的持仓 amount 增加,但 enable_amount(可用持仓)当天不能增加。可转债、ETF等 T+0 品种则可以同步增加 enable_amount
  2. 下单瞬间的“虚拟占扣”:在发出 order 指令后到 on_trade_response 接收到主推之间,仍有微小的延迟。为了极致防重,建议在发出 order 指令的下一行代码,直接在本地缓存中提前扣减 enable_amount(即虚拟占扣),待主推到达时再进行精确对账。