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

JoinQuant 平台 order_target 函数如何确认订单已真正卖出成交?

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

问题描述

order_target如何才能真正知道已经卖出?

def sell_to_zero(context,sy):
try:
r=order_target(sy, 0)
print(r)
if r is not None:
log.info('卖出('+sy+')['+get_security_info(sy).display_name+']成功')
send_msg('卖出:'+sy+','+get_security_info(sy).display_name)
finally:

if sy in rank_pool:
del rank_pool[sy]
2019-02-27 09:30:00 - INFO - UserOrder({'order_id': 1626576144, 'security': '600895.XSHG', 'amount': 2000, 'filled': 2000, 'price': 18.83, 'status': held, 'add_time': datetime.datetime(2019, 2, 27, 9, 30), 'side': 'long', 'action': 'close', 'pindex': 0, 'style': MarketOrderStyle: _limit_price=0.0})

2019-02-27 09:30:00 - INFO - 卖出(600895.XSHG)[张江高科]成功
已经在rank_pool这个字典里面删除了
结果。。。

结果当天又发现还是可以通过在持仓里遍历出来了,我一看虽然当天是跌停,你好说在order_target里给我一个返回状态让我知道是不是卖成功了啊???

019-02-27 15:15:00 - INFO - 603338.XSHG,浙江鼎力,3447.9999999999995

2019-02-27 15:15:00 - INFO - 002157.XSHE,正邦科技,244524.0

2019-02-27 15:15:00 - INFO - 002124.XSHE,天邦股份,311080.0

2019-02-27 15:15:00 - INFO - 002414.XSHE,高德红外,687.0

2019-02-27 15:15:00 - INFO - 600312.XSHG,平高电气,893.0

2019-02-27 15:15:00 - INFO - 600604.XSHG,市北高新,218784.0

2019-02-27 15:15:00 - INFO - 600895.XSHG,张江高科,223884.0

2019-02-27 15:15:00 - INFO - 000917.XSHE,电广传媒,268272.0

2019-02-27 15:15:00 - INFO - 002776.XSHE,ST柏龙,246992.0

2019-02-27 15:15:00 - INFO - 300037.XSHE,新宙邦,2663.0

2019-02-27 15:15:00 - INFO - 601928.XSHG,凤凰传媒,743.0
File "/tmp/jqcore/jqboson/jqboson/core/strategy.py", line 376, in _wrapper
return cb(self._context.strategy_environment.strategy_context, **cb_kwargs)
File "/tmp/strategy/user_code.py", line 71, in select_stocks
sell_it(context)
File "/tmp/strategy/user_code.py", line 296, in sell_it
protfit=rank_pool[sy][0]
KeyError: '600895.XSHG'

解决方案

在 JoinQuant 平台中,您遇到的问题是很多量化开发者常踩的坑。核心误区在于:order_target 返回非 NoneOrder 对象,仅仅代表“委托下单成功”,并不代表“订单已经成交”。

当遇到股票跌停、停牌或流动性不足时,您的卖出委托可能会一直处于挂单状态(open)或者最终被撤单(canceled)。此时股票依然在您的真实持仓中,但您已经在自定义的 rank_pool 中将其删除了,这就导致了后续遍历真实持仓时发生 KeyError

如何真正知道订单已经成交?

要确认订单是否成交,您需要检查返回的 Order 对象的 status(订单状态)或 filled(已成交数量)属性。

JoinQuant 中的订单状态 (OrderStatus) 包括:

  • open: 订单未完成, 无任何成交
  • filled: 订单未完成, 部分成交
  • canceled: 订单完成, 已撤销
  • rejected: 订单完成, 交易所已拒绝
  • held: 订单完成, 全部成交

解决方案与代码优化

方法一:下单后立即检查订单状态(适用于市价单回测)

在回测模式下,市价单通常会立即撮合。您可以直接检查返回的 Order 对象的 status

def sell_to_zero(context, sy):
    try:
        # 下达目标持仓为0的卖出指令
        r = order_target(sy, 0)
        print(r)
        
        if r is not None:
            # 检查订单状态是否为全部成交 (held)
            if str(r.status) == 'held':
                log.info('卖出('+sy+')['+get_security_info(sy).display_name+']成功,全部成交')
                send_msg('卖出:'+sy+','+get_security_info(sy).display_name)
                # 只有真正成交了,才从自定义字典中删除
                if sy in rank_pool:
                    del rank_pool[sy]
            else:
                log.warning('卖出('+sy+')委托成功,但未全部成交,当前状态: ' + str(r.status))
                # 跌停等情况未成交,不应该从 rank_pool 中删除
        else:
            log.error('卖出('+sy+')委托失败(可能停牌或无持仓)')
            
    except Exception as e:
        log.error('卖出异常: ' + str(e))

方法二:以真实持仓为准(最稳妥的架构)

强烈建议不要过度依赖自定义的字典(如 rank_pool)来维护持仓状态。在量化策略中,永远应该以 context.portfolio.positions(账户真实持仓)为唯一事实来源。

如果您必须使用 rank_pool 记录额外信息,在遍历时应该先检查该股票是否真的还在持仓中,或者使用安全的字典访问方法:

# 在后续遍历持仓时,安全地访问 rank_pool
def sell_it(context):
    for sy in context.portfolio.positions:
        # 检查是否在持仓中且可卖数量大于0
        if context.portfolio.positions[sy].closeable_amount > 0:
            # 安全获取,如果不在 rank_pool 中,给一个默认值避免 KeyError
            profit = rank_pool.get(sy, [0])[0] 
            # ... 后续逻辑 ...

方法三:在盘后统一清理未成交的记录

如果您使用了限价单,订单可能在盘中稍后才成交。您可以利用 after_trading_end 函数,在每天收盘后,根据最终的真实持仓来清理您的 rank_pool

def after_trading_end(context):
    # 获取当前真实的持仓标的列表
    current_positions = list(context.portfolio.positions.keys())
    
    # 找出在 rank_pool 中但已经不在真实持仓中的标的,进行清理
    keys_to_delete = []
    for sy in rank_pool:
        if sy not in current_positions:
            keys_to_delete.append(sy)
            
    for sy in keys_to_delete:
        del rank_pool[sy]
        log.info(f'盘后清理:{sy} 已不在持仓中,从 rank_pool 移除')

总结

  1. order_target 返回值不为 None 只代表报单成功
  2. 判断成交成功需要看 Order.status == OrderStatus.heldOrder.filled == Order.amount
  3. 策略逻辑应始终以 context.portfolio.positions 为准,避免自定义状态与真实账户状态脱节。