3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)平台进行回测或模拟交易时,调用 order_target(security, 0) 尝试清仓,并不意味着订单会立即 100% 成交。如果遇到**股票停牌、跌停(无买盘对手方)、超出每日最大成交量限制(order_volume_ratio)**等情况,订单可能会被拒绝(Rejected)、撤销(Canceled)或部分成交(Filled)。
如果策略在下单后立即默认持仓已清空,并更新内部状态,就会导致「策略逻辑认为已空仓,但账户实际仍持有股票被动挨跌」的严重问题。以下是避免该问题的标准解决方案和代码实现。
不要跨交易日缓存持仓状态:
聚宽的 context.portfolio.positions 是只读的实时真实持仓。策略的买卖决策应始终以 context.portfolio.positions[security].closeable_amount(可卖持仓)为准,而不是依赖策略自定义的全局变量(如 g.has_position = False)。
检查 Order 返回值:
调用 order_target 后会返回一个 Order 对象。如果返回 None,说明创建订单失败(例如停牌、代码错误、未上市等),必须立即记录并处理。
监控未完成订单(get_open_orders):
在下一个 Bar(分钟或天)开始时,使用 get_open_orders() 检查上一笔清仓单是否仍在挂单。如果仍在挂单,说明未完全成交。
收盘前强制撤单与状态重置:
未成交的限价单会在收盘后被系统自动撤销。在 after_trading_end 中,应通过 get_trades() 或直接查询 context.portfolio.positions 来确认最终持仓,并修正策略的逻辑状态。
以下是一个实用的双均线/止盈止损策略框架,展示了如何安全地管理清仓订单状态:
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(),若订单状态为 open 或 filled(部分成交),说明未完全清仓。 |
| 跨日状态遗忘 | 策略全局变量与真实持仓不一致 | 严禁使用自定义全局变量记录持仓。每次决策前,必须读取 context.portfolio.positions[security].closeable_amount。 |