欢迎光临散文网 会员登陆 & 注册

CTP账户持仓和资金的维护(干货满满)

2023-05-12 17:40 作者:开心秋水  | 我要投稿

对于很多CTP开发者而言,持仓和资金的维护都是比较头疼的事情,这里给大家讲一些维护持仓和资金的一些方法。注意哦,全篇全是干货,记得做笔记~


先声明一下,Up主这里讲的是一个通用的钱和仓的计算规则,适用于大部分交易系统,up主也合理推测CTP柜台内部也是使用类似的更新方法。写得不对的地方,欢迎大家指正。


首先列出几个资金的计算式(本文以逐日盯市而非逐笔对冲的规则来计算,逐日盯市的含义up主不再赘述,读者可通过其他方式了解):


静态权益 = 上次结算准备金(PreBalance,就是昨天结算后的账户权益)+ 入金(Deposit) - 出金(Withdraw)


动态权益(Balance) = 静态权益 + 持仓盈亏(PositionProfit) + 平仓盈亏(CloseProfit)+ 资金差额(CashIn,即权利金收支,期权才会用到)- 手续费 (Commission)


可用资金(Available) = 动态权益(Balance)- 保证金(CurrMargin)- 冻结的保证金(FrozenMargin) - 冻结的手续费(FrozenCommission) - 冻结的资金(FrozenCash,即冻结的权利金,期权才会用到) - 交割保证金(DeliveryMargin)


持仓盈亏 = ∑持仓的持仓盈亏

平仓盈亏 = ∑持仓的平仓盈亏

单个持仓的平仓盈亏 = ∑此持仓各笔平仓成交的平仓盈亏

资金差额 = ∑持仓的资金差额

手续费 = ∑持仓的手续费

保证金 = ∑持仓的占用保证金

冻结的保证金= ∑持仓的冻结的保证金

冻结的手续费= ∑持仓的冻结的手续费

冻结的资金= ∑持仓的冻结的资金


这些按持仓汇总求和的字段,都是表示当天这个交易日的数据,结算后会清空置零。例如,持仓盈亏表示今日的持仓盈亏,手续费表示今日的手续费。


从上面的式子可以看出,账户资金的数据的很多字段都是持仓的相关数据的汇总求和,当持仓的该字段数据发生变化时,账户资金的对应数据也会发生变化。因此,下面的内容主要是关于持仓中各数据的维护,而资金的数据的变动可根据新的持仓数据计算更新,所以一般不再赘述。另外,也不涉及期权的相关计算。


TIPS:期权交易涉及到的资金差额(CashIn,即权利金收支)的更新,参加up主的文章:(传送门)CTP期权交易。本文剩余内容对于期货和期权都是通用的,但不再单独涉及期权的权利金收支的更新。



1.收到行情快照时资金和持仓的更新


接收到合约行情快照时,需要更新这个快照的合约对应的持仓(汇总)的持仓盈亏,然后更新账户的动态权益等资金数据。


多头持仓的持仓盈亏 = (最新价 - 持仓均价) * 合约数量乘数 * 持仓数量

空头持仓的持仓盈亏 = (持仓均价 - 最新价) * 合约数量乘数 * 持仓数量



2. 报单时资金和持仓的更新


报单时,区分开仓和平仓两种类型来更新数据。


  • 开仓报单

    • 需要冻结一部分保证金和冻结一部分手续费,以及持仓中增加等同于报单数量的多(或空)头冻结。

    • 买入开仓则增加多头持仓的多头冻结(LongFrozen)数量,卖出开仓则增加空头持仓的空头冻结(ShortFrozen)数量。

    • 由于冻结手续费计算较为复杂,而且金额较少,因此本文中忽略不计,下同。

    • 不同期货公司的冻结保证金算法不一(可以参见 TThostFtdcMarginPriceTypeType 类型取值说明),有的按报单价格计算,有的按昨结算价计算,有的按最新价计算......为方便管理,开发者可自行选择其中一种价格的类型来统一计算冻结保证金(此处以昨结算价为例进行计算):

      • 增加的冻结保证金 = 昨结算价 * 合约数量乘数 * 合约保证金率 * 报单数量 + 合约保证金(按手数) * 报单数量


注1:目前国内期货的合约保证金(按手数)暂时都为0,多头和空头保证金率二者也是相等的,但有消息称,交易所未来将上线"多头和空头独立设置保证金率"的改动。

注2:期货公司具体使用哪种方式计算冻结资金,可通过查询经纪公司交易参数获得,查询接口是函数ReqQryBrokerTradingParams, 响应函数是OnRspQryBrokerTradingParams

注3:期权买入开仓时,由于多头持仓无需支付保证金,因此也无需占用冻结保证金。下方同理,对期权买入开仓的报单错误相应和成交、报单回报等的处理中,也无需更改保证金或冻结保证金。作为代价,期权买入开仓需要支付权利金(资金差额),权利金的计算方式类似于保证金,计算方式:

权利金增加或减少的数量,等于此次成交的(预期的)成交额,即 成交数量 * 成交价格 * 期权合约乘数。 

当然对于买入下单计算冻结权利金时,这里的成交数量即可替换为报单数量,成交价格即可替换为合约昨结算价,替换方式类似于冻结保证金。其他场景的更新权利金和冻结权利金同理,下方不再赘述。


  • 平仓报单

    • 需要冻结一部分持仓,就是说,未成交的平仓的挂单会冻结一部分持仓,这部分持仓暂时无法平仓。

    • 增加的冻结的持仓的数量等于报单数量。

    • 买入平仓则增加空头持仓的多头冻结(LongFrozen)数量,卖出平仓则增加多头持仓的空头冻结数量(ShortFrozen)数量。


这里给大家讲一下,为啥在平仓的委托在下单时就要更新冻结的持仓等数据呢?因为从委托到收到报单通知是有一个时间差的,如果不在报单时更新而是在收到报单通知中更新,则有一定的延时,这段时间内持仓等数据就会不准确。例如,小明有1手ag2106多头持仓,他报单卖出平仓1手,如果他不在报单时更新冻结持仓数量,则程序会误认为报单后(还没收到报单通知时),自己仍有1手可平的持仓(见下面的式子),从而程序可能会再次下单平仓,产生错误。


持仓可用(即可平)数量计算式:

可用数量(对于多头持仓) = Position - ShortFrozen - CombShortFrozen

可用数量(对于空头持仓) = Position - LongFrozen - CombLongFrozen



3. 报单错误响应时资金和持仓的更新


如果报单有错误,即收到了OnRspOrderInsert响应,说明报单失败,需要将此前冻结的持仓等数据进行解冻。

报单错误响应时,也区分开仓和平仓两种类型来更新数据。

  • 开仓报单的错误响应

    • 解冻(即减少)一部分保证金和一部分手续费,并持仓中减少等同于报单数量的多(或空)头冻结。

    • 买入开仓错误响应则减少多头持仓的多头冻结(LongFrozen)数量,卖出开仓错误响应则减少空头持仓的空头冻结(ShortFrozen)数量。

    • 减少的冻结保证金 = 昨结算价 * 合约数量乘数 * 合约保证金率 * 报单数量 + 合约保证金(按手数) * 报单数量

  • 平仓报单的错误响应

    • 需要解冻一部分持仓,减少的冻结的持仓的数量等于报单数量。

    • 买入平仓错误响应则减少空头持仓的多头冻结(LongFrozen)数量,卖出平仓则减少多头持仓的空头冻结数量(ShortFrozen)数量。



4. 报单通知(OnRtnOrder)中的更新


收到报单通知时,先判断报单是否是已撤单。如果报单通知中的订单状态不是已撤单,则无需根据这个报单通知来更新持仓或资金。

当一个报单被撤单时,无论是被动撤单(被交易所自动撤单,如FAK单)还是主动撤单(主动发起撤单请求导致),都需要更新持仓和资金数据。更新的方法与报单错误响应时的更新类似。二者主要区别在于,报单错误响应时的更新中,计算使用的数量是报单数量,而(已撤单的)报单通知的更新,计算使用的数量是报单的未成交数量,即报单通知数据结构的剩余数量(VolumeTotal)字段。具体更新计算方法不再赘述。而如果报单没有达到被撤单的最终状态,则无需更新持仓和资金数据。


有一点需要注意,如果你的账户可能在多端同时登录且报单,由于私有流OnRtnOrder报单通知会推送给所有登录了同一账户的API,所以API会收到其他API终端报单生的报单通知,这就会有数据同步的问题。因为,在非本端的报单,是没有在报单时更新本端的持仓等数据的,而根据其他端的报单产生的报单通知去更新,就会有解冻还没有冻结(但本应冻结)的持仓等错误。如何解决这个问题,这里有两种方案供参考

  1. 在你的多个程序间,建立同步机制,一端的报单能同步到其他端上面,从而保证各个端的数据是统一的,例如使用分布式服务。

  2. 收到报单通知时,判断是否是本端的报单(例如根据FrontID和SessionID判断),如果不是,而且是第一次收到这个报单的报单通知,则更新冻结的持仓等数据,更新方法类似于报单时的更新,即将本端漏掉的报单时的更新操作给补上。



5.成交通知(OnRtnTrade)中的更新


收到成交通知时,需要更新对应的持仓明细和持仓,以及解冻数量等同于成交数量的持仓,还需要解冻一定数量的冻结保证金和冻结手续费。

成交也区分开仓和平仓两种类型来更新数据。



开仓成交

先增加一条持仓明细:

  1. 持仓明细的开仓日期(OpenDate)是成交的交易日(TradingDay),

  2. 持仓明细的成交编号(TradeID)等于成交的成交编号(TradeID),

  3. 持仓明细的成交类型(TradeType)等于成交的成交类型(TradeType),

  4. 持仓明细的买卖方向(Direction)等于成交的买卖方向(Direction),

  5. 持仓明细的开仓价格(OpenPrice)等于成交的成交价格(Price),

  6. 持仓明细的数量等于成交的成交数量(Volume),

  7. 持仓明细的投机套保标志(HedgeFlag)等于成交的投机套保标志(HedgeFlag)。

  8. 此笔持仓明细是今仓,因此持仓价格等于开仓成交价格。

  9. 持仓明细的保证金(Margin)由以下式子计算得出:

    1. Margin = Volume * Price * 合约数量乘数 * 合约保证金率 + Volume * 合约保证金(按手数)

然后,查找到对应的持仓,并做修改:

  1. 持仓的持仓数量(PositionNum)和开仓量(OpenVolume)和今仓量(TodayPosition)增加,其增加量等于成交数量。

  2. 持仓的保证金(UseMargin)增加,增加量等于上述计算出的持仓明细的保证金。

  3. 持仓的多头冻结(对于多头持仓)或空头冻结(对于空头持仓)减少,其减少量等于成交数量。

  4. 持仓的冻结保证金减少,减少量为按成交数量计算。

  5. 持仓的持仓成本(PositionCost)和开仓成本(OpenCost)增加,增加量都等于成交的成交额,即Volume * Price * 合约数量乘数,并根据持仓成本和开仓成本重新计算持仓均价和开仓均价:

    1. 持仓均价 = 持仓成本 / (持仓数量 * 合约数量乘数)

    2. 开仓均价 = 开仓成本 / (持仓数量 * 合约数量乘数)

  6. 持仓的手续费增加,增加数量即为此笔成交的手续费

    1. 此笔开仓成交的手续费 = 成交价 * 成交数量 * 合约数量乘数 * 合约开仓手续费率 + 成交数量 * 合约开仓手续费(按手数)



平仓成交

  • 首先,按照一定的规则来确定,该合约对应方向的各笔持仓明细的分别平仓的数量。平仓规则详见此前介绍组合合约的文章。

    • 部分交易所不区分今仓和昨仓,例如大商所,但平仓一般是按先开先平来处理,即先平昨仓再平今仓,而上期所和能源中心等交易所,区分平今和平昨,因此,这些交易所根据成交的开平类型(平今还是平仓即平昨),即可判断要更新哪一些持仓明细。搞清楚持仓明细的平仓顺序,才能正确更新持仓明细(主要是为了计算准确手续费和平仓盈亏)。

    • 对于单笔持仓明细,按上述规则确定其在成交中的平仓数量(设为N),再进行更新。更新方法如下:

      • 这笔持仓明细的数量减少,减少量等于N。

      • 按新的数量重新计算这笔持仓明细的保证金。

      • 这笔持仓明细的平仓量(CloseVolume)增加,增加量等于N。

      • 这笔持仓明细的平仓盈亏(CloseProfit)增加,增加量等于此笔持仓明细在此成交中的平仓盈亏:

        • 对于多头持仓明细:此笔持仓明细在此成交中的平仓盈亏 = (成交价 - 持仓价格)* N * 合约数量乘数

        • 对于空头持仓明细:此笔持仓明细在此成交中的平仓盈亏 = (持仓价格 - 成交价)* N * 合约数量乘数

        • 其中,持仓价格是多少:

          • 历史持仓明仓(开仓日期不等于今日交易日)的持仓价格 = 昨结算价

          • 今日持仓明细(开仓日期等于今日交易日)的持仓价格 = 开仓价格

        • 可以看到,今仓和昨仓的持仓价格不一样,因此算出来的持仓盈亏也是不一样的,所以持仓明细的更新需要按正确的平仓顺序来算,如果顺序错了,那么算出来的平仓盈亏也就会和实际值不相符了,下面的平仓成交的的手续费也是同理。

      • 更新持仓明细时,顺便将此笔持仓明细平仓产生的手续费计算出来:

        • 此笔持仓明细平仓产生的手续费 = 成交价 * N * 合约数量乘数 * 合约平仓手续费率 + N * 合约平仓手续费(按手数)

        • 注意:即使是对于大商所等不区分今仓和昨仓的交易所,合约平仓手续费率和合约平仓手续费(按手数),也是区分平今和平昨进行计算的,即分为平今手续费率和平昨手续费率。所以,上面的计算手续费的式子中的费率是需要取用交易对应的实际费率的,而不能为了简单方便而对大商所等交易所的平仓交易统一都用单一的费率(例如,只取用平昨的费率),这样算出来的手续费就会和实际的手续费有较大的偏差。

  • 然后,根据以上数据,可以计算出此笔成交的平仓盈亏手续费

    • 此笔平仓成交的平仓盈亏 = ∑各笔持仓明细在此平仓成交中的平仓盈亏

    • 此笔平仓成交的手续费 = ∑各笔持仓明细在此平仓成交中产生的手续费

  • 持仓明细更新完后,以持仓明细汇总更新持仓,更新持仓的持仓数量,保证金,平仓盈亏,今仓数量,持仓成本,开仓成本等数据,具体更新方法如下。

    • 持仓的持仓数量 = ∑持仓明细的数量

    • 持仓的保证金 = ∑持仓明细的保证金

    • 持仓的平仓量 = ∑持仓明细的平仓量

    • 持仓的平仓盈亏 = ∑持仓明细的平仓盈亏

    • 持仓的今仓量 = ∑开仓日期等于今日交易日的持仓明细的数量

    • 持仓的开仓成本 = ∑ (持仓明细的开仓价格 * 数量 * 合约数量乘数)

      • 根据开仓的开仓成本即可算出开仓均价,注意哦,是均价,即开仓平均价格

    • 持仓的持仓成本 = ∑ (持仓明细的持仓价格 * 数量 * 合约数量乘数)

      • 根据持仓的持仓成本即可算出持仓均价,注意哦,是均价,即持仓平均价格

    • 持仓的手续费 += 此笔平仓成交的手续费

    • 更新后,持仓中再做进一步改动:

      • 持仓的空头冻结(对于多头持仓)或多头冻结(对于空头持仓)减少,其减少量等于成交数量。

注1:期权交易的卖出成交时(无论是开仓或平仓),还额外会有一笔权利金的收入,即权利金收支(资金差额)会增加,计算方式见上文。


至此,根据平仓成交来更新持仓明细和持仓的流程宣告完成。相信看到这里,你已经是对CTP的钱和仓该怎么算,有了一个基础的认识了。




这里举一个平仓的例子,帮助大家理解:

小明有4手c2101多头持仓,分为两笔持仓明细:

第一笔,是2手昨仓,开仓价格3006元;

第二笔,是2手今仓,开仓价格3000元。

假设c2101昨结算价是3005元,合约数量乘数是10吨/手,多头保证金率是5%,平昨手续费是每手1.2元,平今免手续费。他下单卖出平仓3手c2101,全部成交,成交价是3004元。则按平仓顺序可知,平仓的是2手昨仓和1手今仓,交易后剩余1手今仓。交易数据如下:


第一笔持仓明细(昨仓)在本次成交中的平仓盈亏 = (3004 - 3005) * 10 * 2 = -20元

第一笔持仓明细(昨仓)在本次成交中产生的手续 = 2 * 1.2 = 2.4元


第二笔持仓明细(今仓)在本次成交中的平仓盈亏 = (3004 - 3000) * 10 * 1 = 40元

第二笔持仓明细(今仓)在本次成交中产生的手续费 = 1 * 0 = 0元


此次成交的平仓盈亏 = (-20)+ 40 = 20元

此次成交的手续费 = 2.4 + 0 = 2.4元


成交后,新的c2101多头持仓的部分数据如下:

持仓数量 1手,其中今仓量 1手,即持仓全部都是今仓。

平仓量相比此次成交前增加3手,

手续费相比此次成交前增加2.4元,

平仓盈亏相比此次成交前增加20元。

新的开仓成本为30000元,开仓均价为3000元,

新的持仓成本为30000元,持仓均价为3000元。

新的持仓保证金为1500元。



CTP账户持仓和资金的维护(干货满满)的评论 (共 条)

分享到微博请遵守国家法律