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

聚宽平台如何检查 order_target 订单成交状态并同步持仓?(附 Python 源码)

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

问题描述

聚宽止盈止损策略如何避免订单未成交却提前更新持仓状态?
在聚宽中调用 order_target 清仓后,如何检查订单是否成交并同步持仓状态,避免跌停、停牌或卖单失败导致策略遗忘实际持仓?

解决方案

在聚宽(JoinQuant)平台进行回测或模拟交易时,调用 order_target(security, 0) 尝试清仓,并不意味着订单会立即 100% 成交。如果遇到**股票停牌、跌停(无买盘对手方)、超出每日最大成交量限制(order_volume_ratio)**等情况,订单可能会被拒绝(Rejected)、撤销(Canceled)或部分成交(Filled)。

如果策略在下单后立即默认持仓已清空,并更新内部状态,就会导致「策略逻辑认为已空仓,但账户实际仍持有股票被动挨跌」的严重问题。以下是避免该问题的标准解决方案和代码实现。

一、 核心逻辑与 API 机制

  1. 不要跨交易日缓存持仓状态
    聚宽的 context.portfolio.positions 是只读的实时真实持仓。策略的买卖决策应始终以 context.portfolio.positions[security].closeable_amount(可卖持仓)为准,而不是依赖策略自定义的全局变量(如 g.has_position = False)。

  2. 检查 Order 返回值
    调用 order_target 后会返回一个 Order 对象。如果返回 None,说明创建订单失败(例如停牌、代码错误、未上市等),必须立即记录并处理。

  3. 监控未完成订单(get_open_orders)
    在下一个 Bar(分钟或天)开始时,使用 get_open_orders() 检查上一笔清仓单是否仍在挂单。如果仍在挂单,说明未完全成交。

  4. 收盘前强制撤单与状态重置
    未成交的限价单会在收盘后被系统自动撤销。在 after_trading_end 中,应通过 get_trades() 或直接查询 context.portfolio.positions 来确认最终持仓,并修正策略的逻辑状态。


二、 规避「未成交提前更新状态」的 Python 源码示例

以下是一个实用的双均线/止盈止损策略框架,展示了如何安全地管理清仓订单状态:

import jqdata

def initialize(context):
    g.security = '000001.XSHE'
    set_benchmark('000300.XSHG')
    set_option('use_real_price', True)
    
    # 运行函数
    run_daily(market_open, time='every_bar')
    run_daily(check_order_status, time='14:50') # 每天尾盘检查未完成订单

def market_open(context):
    security = g.security
    close_data = attribute_history(security, 5, '1d', ['close'])
    MA5 = close_data['close'].mean()
    current_price = close_data['close'][-1]
    
    # 获取当前真实可卖持仓
    pos = context.portfolio.positions[security]
    closeable_amount = pos.closeable_amount
    
    # 止损信号:当前价格低于5天均线的95%
    if current_price < 0.95 * MA5 and closeable_amount > 0:
        log.info("触发止损信号,尝试清仓 %s" % security)
        
        # 检查是否有该标的未完成的订单,order_target 会自动取消先前的未完成订单
        order_obj = order_target(security, 0)
        
        if order_obj is None:
            log.error("下单失败!可能由于停牌或账户异常。实际持仓未改变。")
        else:
            log.info("清仓单已提交,订单ID: %s,等待撮合..." % order_obj.order_id)

def check_order_status(context):
    """
    每日尾盘(14:50)检查是否有未完成的清仓单,防止因跌停无法成交
    """
    open_orders = get_open_orders()
    security = g.security
    
    for order_id, order_obj in open_orders.items():
        if order_obj.security == security:
            # 发现仍有未成交的清仓挂单
            log.warn("警报:%s 的清仓单未完全成交!当前状态: %s, 已成交: %d/%d" % 
                     (security, str(order_obj.status), order_obj.filled, order_obj.amount))
            
            # 如果是跌停导致无法成交,可以选择撤单,或者在明日开盘继续尝试
            # cancel_order(order_obj)

def after_trading_end(context):
    """
    收盘后清算,以真实持仓数据同步策略状态
    """
    security = g.security
    actual_amount = context.portfolio.positions[security].total_amount
    
    if actual_amount > 0:
        log.warn("收盘清算:%s 仍有实际持仓 %d 股,清仓未成功(可能遭遇跌停或停牌)!" % (security, actual_amount))
    else:
        log.info("收盘清算:%s 已成功空仓。" % security)

三、 关键防错机制总结

异常场景 导致后果 聚宽API应对方案
标的停牌 order_target 返回 None,无法下单 检查 order_target 返回值是否为 None,若为 None 则不触发后续的空仓逻辑。
遭遇跌停 卖单挂单在买一档,无法成交 在下一个 Bar 或尾盘调用 get_open_orders(),若订单状态为 openfilled(部分成交),说明未完全清仓。
跨日状态遗忘 策略全局变量与真实持仓不一致 严禁使用自定义全局变量记录持仓。每次决策前,必须读取 context.portfolio.positions[security].closeable_amount