问题描述
rebalance的时候,采用order_target_value后,有个股出现了8460股的持仓,还成交了,第二天平仓失败,请问如何解决该问题?
解决方案
在 JoinQuant 平台进行 Rebalance 调仓时,使用 order_target_value 出现非 100 整数倍的持仓(如 8460 股)且第二天平仓失败,通常与交易品种的特殊规则或除权除息有关。以下是具体的原因分析及解决方案:
一、 为什么会出现 8460 股的持仓?
- 科创板股票的交易规则
如果该股票是科创板股票(代码以688开头),根据交易所规则,科创板单笔申报数量为 200 股起,按 1 股递增。因此,order_target_value在计算目标价值对应的股数时,完全可能计算并成交 8460 股。这是符合交易所规则的正常现象。 - 发生了除权除息(送转股)
如果该股票是普通 A 股,买入时必须是 100 的整数倍。但如果在持仓期间发生了送股或转增股本(例如 10 送 4.1),原本 6000 股的持仓会自动变成 8460 股。在开启了真实价格模式(set_option('use_real_price', True))时,系统会根据复权因子自动调整你的持仓数量。
二、 为什么第二天平仓会失败?
- 科创板市价单未设置“保护价”
这是最常见的原因。科创板股票在使用市价单(MarketOrder)进行买卖时,必须指定保护价,否则订单会被直接废单。如果你在平仓时使用了类似order_target(security, 0)且默认是市价单,但没有传入保护价,就会导致平仓失败。 - 零股卖出规则限制
A 股规则规定,卖出时如果数量不足 100 股(零股),必须一次性全部卖出。如果你在平仓时试图只卖出部分零股(例如想卖出 8400 股,留下 60 股),订单会被拒绝。 - 常规原因:停牌或跌停
股票在第二天处于停牌状态,或者开盘即一字跌停,导致市价单无法撮合成交。
三、 如何解决该问题?
方案 1:清仓时统一使用 order_target(security, 0)
无论持仓是多少股(包含零股),只要是想全部平仓,最安全的方式是直接将目标股数设为 0。系统会自动处理全部卖出的逻辑。
# 卖出所有股票, 使这只股票的最终持有量为0
order_target(security, 0)
方案 2:针对科创板股票,添加保护价或使用限价单
如果你的股票池中包含科创板股票,在下单时必须处理保护价,或者改用限价单(LimitOrderStyle)。
# 假设 current_price 是当前价格,设置跌停价或较低的价格作为卖出保护价
protect_price = current_price * 0.8
# 使用带保护价的市价单清仓
order_target(security, 0, style=MarketOrderStyle(limit_price=protect_price))
# 或者使用限价单清仓
order_target(security, 0, style=LimitOrderStyle(current_price * 0.98))
方案 3:手动计算 100 的整数倍进行调仓(替代 order_target_value)
如果你不希望出现非 100 整数倍的持仓(即使是科创板),可以放弃直接使用 order_target_value,改为自己计算目标股数,并向下取整到 100 的倍数,然后使用 order_target 下单:
def my_order_target_value(security, target_value, data):
# 获取当前价格
current_price = data[security].close
if current_price == 0:
return
# 计算目标股数
target_amount = target_value / current_price
# 向下取整到 100 的整数倍
target_amount = int(target_amount / 100) * 100
# 使用 order_target 下单
order_target(security, target_amount)
总结排查步骤:
先检查该股票是否为科创板(688开头),如果是,请务必在下单函数中加入 MarketOrderStyle(保护价);如果不是,请检查日志中是否有除权除息提示,并在平仓时严格使用 order_target(sec, 0)。