VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 3
声望: 0

为什么隔夜持仓,第二天的持仓价格永远是错的,是柜台的问题吗?实盘帐号。

Member
avatar
加入于:
帖子: 6109
声望: 379

持仓价格是错的具体指的是什么呢?

Member
avatar
加入于:
帖子: 3
声望: 0

比如第一天,开仓的价格是7301,第二天获取到的价格不是7301,貌似是昨日结算价。
description

Member
avatar
加入于:
帖子: 3
声望: 0

只要是过夜的单子,持仓价格和实际不一致,但手机上查询的持仓价格没问题,不知道是不是CTP柜台的问题。

Super Moderator
avatar
加入于:
帖子: 133
声望: 15

这是一个关于 VeighNa 持仓价格显示机制的问题。让我先查询相关文档和代码来了解具体情况。

让我进一步查询 CTP 接口和持仓数据相关的文档:

让我查看 CTP 接口模块的源代码,了解持仓价格是如何处理的:

我找到了问题的根源。让我再查询一下 PositionData 数据结构来确认:

根据我查询的 CTP 接口源代码,现在可以为您解答这个问题。

问题原因

这不是柜台的问题,也不是 VeighNa 的 bug,而是期货市场的标准结算机制导致的。

核心原因:期货每日无负债结算制度

在 CTP 接口的持仓查询回报处理代码中(ctp_gateway.py 第 317-342 行),持仓均价的计算逻辑如下:

# 计算之前已有仓位的持仓总成本
cost: float = position.price * position.volume * size

# 累加更新持仓数量和盈亏
position.volume += data["Position"]
position.pnl += data["PositionProfit"]

# 计算更新后的持仓总成本和均价
if position.volume and size:
    cost += data["PositionCost"]
    position.price = cost / (position.volume * size)

这里的 data["PositionCost"]CTP 柜台返回的持仓成本

为什么第二天价格会变成结算价?

中国期货市场实行每日无负债结算制度(逐日盯市):

时间点 持仓成本的处理方式
开仓当天 CTP 返回的成本基于开仓价格
每日结算后 交易所将持仓成本调整为当日结算价

具体流程:

  1. 第一天您以 7301 的价格开仓
  2. 收盘后交易所进行结算,假设当日结算价为 7280
  3. 结算时,您的账户会实现 (7301 - 7280) × 手数 × 乘数 的盈亏
  4. 结算后,CTP 系统中该持仓的成本价被调整为 7280(结算价)
  5. 第二天登录时,CTP 返回的 PositionCost 已经是基于 7280 计算的

这与股票市场的区别

  • 股票市场:持仓成本保持买入价格不变,直到卖出
  • 期货市场:每日结算后,持仓成本调整为结算价,已实现盈亏结算到账户

解决方案

如果您需要在策略中记录真实的开仓价格,有以下几种方式:

方案 1:策略中自行维护成交记录

from vnpy.trader.object import TradeData

class MyStrategy(CtaTemplate):
    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)
        # 记录开仓价格的字典 {vt_symbol: open_price}
        self.open_prices: dict[str, float] = {}

    def on_trade(self, trade: TradeData) -> None:
        """成交回调"""
        if trade.offset == Offset.OPEN:
            # 记录开仓价格
            self.open_prices[trade.vt_symbol] = trade.price
        elif trade.offset in {Offset.CLOSE, Offset.CLOSETODAY, Offset.CLOSEYESTERDAY}:
            # 平仓时清除记录
            self.open_prices.pop(trade.vt_symbol, None)

    def get_open_price(self, vt_symbol: str) -> float:
        """获取指定合约的开仓价格"""
        return self.open_prices.get(vt_symbol, 0)

方案 2:使用成交数据计算成本

def calculate_avg_open_price(trades: list[TradeData]) -> float:
    """根据成交列表计算加权平均开仓价"""
    total_cost: float = sum(t.price * t.volume for t in trades)
    total_volume: float = sum(t.volume for t in trades)

    if total_volume > 0:
        return total_cost / total_volume
    return 0

方案 3:使用策略内置的变量(推荐)

CTA 策略模板本身会维护 pos 变量和成交回调,您可以直接利用:

def on_trade(self, trade: TradeData) -> None:
    """成交回调"""
    super().on_trade(trade)  # 调用父类方法更新 pos

    # 自定义逻辑记录开仓价
    if trade.offset == Offset.OPEN:
        # 保存到策略状态,会被持久化
        self.set_variable("my_open_price", trade.price)

总结

  • 现象:隔夜持仓价格显示为昨日结算价,而非开仓价
  • 原因:期货市场每日结算制度导致 CTP 柜台将持仓成本调整为结算价
  • 建议:如需真实开仓价,策略中自行维护,不要依赖 position.price

参考资料:vnpy_ctp/gateway/ctp_gateway.py 中的 onRspQryInvestorPosition 函数

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】