问题描述
/
解决方案
问题分析:为什么 get_position 会有延迟?
在 ptrade 交易环境中,策略刚成交一笔订单后,立即调用 get_position 查询持仓,往往无法获取到最新的持仓数据。这是正常现象,主要原因如下:
根据 ptrade 官方 API 文档说明,交易场景中持仓信息同步存在时滞,一般在 6 秒左右。这是因为量化引擎的持仓数据需要从券商柜台系统进行同步,而柜台数据的更新和推送存在物理延迟。甚至在某些柜台配置下,持仓数据可能不体现当日变化(一天只同步一次)。
潜在风险
这种延迟最容易导致的问题是重复下单。如果您的策略使用了 order_target(指定目标数量买卖)或 order_target_value(指定持仓市值买卖),在第一笔委托刚成交但持仓未更新的这 6 秒内,策略再次运行并判断持仓未达标,就会再次发出相同的委托,从而造成严重的重复买入或卖出。
解决方案
为了解决持仓延迟带来的问题,建议采用以下几种方案:
方案一:使用主推事件,在本地维护“虚拟持仓”(推荐)
不要完全依赖 get_position 的实时性。您可以使用 ptrade 提供的交易主推事件 on_trade_response,在订单成交时第一时间捕获数据,并在全局变量 g 中自行维护一个实时的持仓记录。
代码示例:
def initialize(context):
g.security = '600570.SS'
set_universe(g.security)
# 初始化本地维护的持仓量
g.local_position = 0
# 标记是否已经初始化过本地持仓
g.pos_initialized = False
def before_trading_start(context, data):
# 每天盘前同步一次真实的柜台持仓作为基准
pos = get_position(g.security)
if pos:
g.local_position = pos.amount
else:
g.local_position = 0
g.pos_initialized = True
def on_trade_response(context, trade_list):
# 捕获成交主推,实时更新本地持仓
for trade in trade_list:
if trade['stock_code'] == g.security and trade['status'] == '8': # '8'表示已成
if trade['entrust_bs'] == '1': # 买入
g.local_position += trade['business_amount']
elif trade['entrust_bs'] == '2': # 卖出
g.local_position -= trade['business_amount']
log.info(f"实时成交!当前本地维护持仓更新为: {g.local_position}")
def handle_data(context, data):
if not g.pos_initialized:
return
# 使用本地维护的持仓进行逻辑判断,而不是 get_position()
if g.local_position < 1000:
order(g.security, 100)
# 注意:为了防止在途订单(已报未成)导致的重复下单,
# 实际严谨的策略还需要结合 get_open_orders() 或 on_order_response 维护在途数量。
方案二:慎用 order_target 系列接口,改用增量下单
尽量避免使用 order_target 和 order_target_value。这两个接口强依赖于当前持仓的绝对准确性。建议改用 order(按数量买卖)或 order_value(按金额买卖)这种增量下单接口。通过策略逻辑计算出需要买卖的差额,然后直接下达差额订单。
方案三:增加状态锁或时间间隔
如果策略逻辑比较简单,可以在下单后设置一个全局标志位(如 g.is_ordering = True),在接下来的几个 handle_data 周期内(或使用 time.sleep,但不推荐在主线程长 sleep)跳过逻辑执行,等待 6-10 秒让柜台持仓同步完成后,再解锁标志位。
def initialize(context):
g.security = '600570.SS'
set_universe(g.security)
g.last_order_time = None
def handle_data(context, data):
current_time = context.blotter.current_dt
# 如果距离上次下单不足 10 秒,跳过本次判断,等待持仓同步
if g.last_order_time and (current_time - g.last_order_time).total_seconds() < 10:
return
pos = get_position(g.security)
if pos and pos.amount < 1000:
order(g.security, 100)
g.last_order_time = current_time # 记录下单时间
总结
量化交易中,柜台数据的延迟是客观存在的。优秀的量化策略应当具备订单与持仓的本地状态管理能力,通过 on_order_response 和 on_trade_response 结合 get_open_orders(),构建一个闭环的订单状态机,才能彻底解决延迟带来的重复下单问题。