[Bug] PositionHolding在持仓推送后未重算已有活动委托的冻结量
Author: ai-yangCreated Jul 31, 2026Updated Sep 8, 2026
环境
- 操作系统: Ubuntu 22.04.4 LTS x86_64
- Python版本: Python 3.10.12、Python 3.13.14
- VeighNa版本: dev branch
4240fee6925313d84649b6f5b86f4f3a7b679ec4(提交日期2026-07-28,vnpy.__version__ == "4.4.0")
Issue类型
Bug
预期程序行为
PositionHolding已经缓存活动平仓委托时,后续收到PositionData持仓快照后,应根据最新持仓和已有的active_orders重新计算冻结量。
以下场景中已有一笔卖出平今5手的活动委托,随后收到多头今仓5手的持仓快照,因此:
long_td_frozen和long_pos_frozen应为5;- 可用多头持仓应为0;
- 再次转换卖出平仓5手的请求时,应返回空列表,避免重复平仓。
实际程序行为
PositionHolding.update_position()只更新long_pos/long_yd/long_td或对应的空头字段,没有重新调用calculate_frozen()。
如果活动委托先到达,此时持仓仍为0,update_order()计算出的冻结量会被限制为0。之后持仓快照到达时冻结量仍保持为0,转换器会错误地再次生成5手平今委托。
在Python 3.10和3.13下分别连续运行3次,结果均为:
{'active_order_count': 1, 'long_pos': 5, 'long_td': 5, 'long_pos_frozen': 0, 'converted': [('CLOSETODAY', 5)]}
AssertionError: 活动平今委托的剩余数量应在持仓快照到达后重新冻结该状态如果没有后续的委托更新,会一直保留,并可能造成重复平仓请求。
重现步骤
以下脚本不连接交易接口或其他外部服务:
from vnpy.trader.constant import Direction, Exchange, Offset, OrderType, Product, Status
from vnpy.trader.converter import PositionHolding
from vnpy.trader.object import ContractData, OrderData, OrderRequest, PositionData
contract = ContractData(
gateway_name="CTP", symbol="rb", exchange=Exchange.SHFE,
name="rb", product=Product.FUTURES, size=10, pricetick=1,
)
holding = PositionHolding(contract)
holding.update_order(OrderData(
gateway_name="CTP", symbol="rb", exchange=Exchange.SHFE,
orderid="1", direction=Direction.SHORT, offset=Offset.CLOSETODAY,
type=OrderType.LIMIT, volume=5, traded=0, status=Status.NOTTRADED,
))
holding.update_position(PositionData(
gateway_name="CTP", symbol="rb", exchange=Exchange.SHFE,
direction=Direction.LONG, volume=5, yd_volume=0,
))
request = OrderRequest(
symbol="rb", exchange=Exchange.SHFE, direction=Direction.SHORT,
type=OrderType.LIMIT, volume=5, offset=Offset.CLOSE,
)
converted = holding.convert_order_request_shfe(request)
print({
"active_order_count": len(holding.active_orders),
"long_pos": holding.long_pos,
"long_td": holding.long_td,
"long_pos_frozen": holding.long_pos_frozen,
"converted": [(item.offset.name, item.volume) for item in converted],
})
assert holding.long_pos_frozen == 5, "活动平今委托的剩余数量应在持仓快照到达后重新冻结"
assert converted == []从仓库根目录连续运行3次,均可观察到long_pos_frozen == 0,且转换器生成新的CLOSETODAY 5请求。
Source: vnpy/vnpy