加密货币交易所七:清算系统
结算、强平、清算是三件事,原文把它们混成了一件。这篇把它们拆开,再用一组能自己按计算器验一遍的数字走完已实现盈亏、开仓均价加权,以及反向合约为什么要用调和平均。
先算一道题。你在 50000 开了 100 张 BTC 永续多单,每张面值 0.01 BTC,也就是 1 个 BTC 的仓位。价格涨到 51000 的时候你平掉 40 张,剩下 60 张挂着不动。
这时候你的「盈亏」是多少?
答案是两个数字,而且都对:已实现 400 USDT,未实现 600 USDT。
平掉的 40 张 (51000 − 50000) × 40 × 0.01 = 400 ← 已实现,落进余额剩下的 60 张 (51000 − 50000) × 60 × 0.01 = 600 ← 未实现,只影响权益这两个数字走的是完全不同的路径:400 那笔已经变成账户余额,可以提走;600 那笔只存在于「按当前价格算一遍」的结果里,价格一动就变,而且下一秒可能变成负的。清算系统要做的事,说到底就是把第二种数字在恰当的时刻变成第一种。
结算、强平、清算是三件事
这三个词在中文材料里几乎总是混着用,但它们指的是流程上三个不同的位置:
| 是什么 | 什么时候发生 | |
|---|---|---|
| 结算 | 把浮动的盈亏和该收该付的费用落账 | 按周期(资金费率通常每 8 小时一次),或者每次成交后 |
| 强平 | 一个判定:保证金余额掉到维持保证金以下 | 价格走到某个位置的那一瞬间 |
| 清算 | 强平判定之后的一串处置动作 | 判定成立之后 |
「爆仓」是这三件事的口语总称,指不清具体哪一步,下面不再用它。
强平那一行要多说一句,因为「保证金率」这个词在中文材料里被两种相反的定义共用,看到数字先确认是哪一种:
写法一(Binance 用的) 保证金率 = 维持保证金 ÷ 保证金余额 涨到 100% 触发写法二(也很常见) 保证金率 = 保证金余额 ÷ 维持保证金 跌到 100% 触发两种写法互为倒数,触发点都在 100%,但方向是反的 —— 一种是越高越危险,另一种是越低越危险。混着用就会写出「保证金率跌破维持保证金率」这种把两个不同量级的东西放在一起比的句子。下面统一用最不容易出错的那种表述:保证金余额掉到维持保证金以下,不带比值。
判定和处置分开,是因为这两件事的失败模式完全不同。判定失败是算错了 —— 该平的没平,或者不该平的平了;处置失败是市场没接住 —— 判定完全正确,但仓位挂出去没人吃。后一种才是交易所真正头疼的那类。
处置这一串按顺序是:接管仓位 → 用市价单在市场上平掉 → 成交价比破产价差的那部分由保险基金吞下 → 保险基金也不够的时候,触发自动减仓(ADL),从对手方那边强行减掉相应的仓位。穿仓指的是最后清出来的价格已经差到把保证金亏光还欠一截,那个缺口就是保险基金要填的洞。
ADL 挑谁不是随机的,也不是「挑盈利最多的」这么简单。Binance 公布的排序是盈利比例乘以有效杠杆:
盈利比例 = 未实现盈利 ÷ |仓位名义价值|有效杠杆 = |仓位名义价值| ÷ (账户余额 + 未实现盈利)
盈利比例 ≥ 0 排序值 = 盈利比例 × 有效杠杆 ← 又赚又用高杠杆的排最前盈利比例 < 0 排序值 = 盈利比例 ÷ 有效杠杆注意亏损那一支是除以杠杆,不是乘以。这不是笔误:盈利时高杠杆会把排序值放大(更靠前),亏损时高杠杆反而把负的排序值往零的方向拉。两支合起来的效果是,队列前端稳定地留给「赚得多而且赚得快」的那批人 —— ADL 的成本精确地落在这一轮行情里赢得最漂亮的账户上。
OKX 和 Bybit 的排序用的是维持保证金率而不是杠杆倍数(盈利仓位除以账户维持保证金率),量不同但意图一样:同样的盈利比例下,仓位相对本金越大的排得越前。
这条链上有一个反直觉的地方:被强平的用户不一定一分钱不剩。 维持保证金率不是零,跌破它的时候账户里还有钱,清算是在这笔钱被亏完之前提前动手。剩下多少取决于处置的成交价和罚金 —— 处置得越快越贵,剩得越少。
链上的做法可以对照着看:Perpetual Protocol 三:ClearingHouse 里,判定写在 internalLiquidate 开头的一行 requireMoreMarginRatio,处置是紧接着的一次反向 swapInput,两段代码挨着,但确实是两件事。
加仓改的是开仓均价,按数量加权
开仓均价是已实现盈亏的减数,算错一次,之后每一笔平仓都跟着错。加仓的时候它按数量加权,不是把两个价格取平均:
第一笔 50000 × 100 张第二笔 52000 × 50 张均价 (50000 × 100 + 52000 × 50) ÷ 150 = 50666.67两价平均 (50000 + 52000) ÷ 2 = 51000 ← 差 333,错在这反向合约(币本位,一张合约代表固定的 100 USD,保证金和盈亏都用币结算)不能这么算。这时候「张」是分子上的美元数,仓位占用的币量是 张数 × 100 ÷ 价格,所以均价得用总美元除以总币量:
第一笔 100 张 @ 50000 → 10000 ÷ 50000 = 0.200000 BTC第二笔 100 张 @ 52000 → 10000 ÷ 52000 = 0.192308 BTC总名义 20000 USD总币量 0.392308 BTC均价 20000 ÷ 0.392308 = 50980.39 ← 不是 51000差了 19.6。数量小的时候看不出来,仓位一大、加仓次数一多,这个偏差会一路串进保证金率和强平价。同一套盈亏代码同时服务正向和反向合约,是这一层最容易埋雷的地方 —— 两种合约连「一张是多少」的方向都是反的。
未实现盈亏用标记价格算,不用最新成交价
未实现盈亏每次价格跳动都要重算一遍,已实现盈亏只在平仓那一刻算一次,两者的计算量差着数量级。但更要紧的是用哪个价格去算。
如果拿最新成交价算,一笔金额不大的市价单就能在薄的盘口上把价格打穿一截,顺带把一批仓位的未实现盈亏打进强平线。所以喂给强平判定的是标记价格 —— 它以指数价格(从多个现货交易所取的加权价)为基准,做过平滑,不跟单个交易所盘口上的瞬时波动走。
由此来的一个结果经常让人困惑:盘面上你的挂单价明明还没被触及,仓位却已经被平了,因为标记价格和最新成交价是两个数。反过来也成立 —— 插针把最新成交价打到你的强平价以下,只要标记价格没跟着走,仓位就还在。
全仓和逐仓在清算这一步分岔
| 特性 | 全仓 | 逐仓 |
|---|---|---|
| 风险评估 | 整个账户合起来算一次 | 每个仓位单独算 |
| 保证金 | 所有仓位共用一个保证金余额 | 每个仓位划一份,互不相通 |
| 强平判定 | 账户级触发,可能牵连多个仓位 | 仓位级触发,只动那一个 |
| 资金利用效率 | 高 —— 盈利仓位的浮盈能顶亏损仓位的保证金 | 低 —— 一边浮盈一边被强平是可能的 |
| 风险隔离 | 低 | 高 |
| 最大亏损 | 整个账户余额 | 划给这个仓位的那一份 |
最后一行是选择这两种模式的真正依据,其余几行都是它的推论。全仓换来的效率和它能亏掉的钱是同一件事的两面。
分岔还体现在可用余额的算法上,这一点第八篇会展开:全仓下可用余额是一个账户级的减法(保证金余额减去所有仓位占用的保证金和挂单冻结),逐仓下是逐仓位算的,某个仓位的浮盈不会变成另一个仓位的下单额度。
全仓触发之后还多一步:平哪个。按风险贡献排序、从贡献最大的开始平,平到保证金率回到阈值以上就停,不是一次清空整个账户。
冻结不是把钱挪走
清算系统的账要能对得上,靠的是复式记账:每一笔变动同时写借贷两侧,任何时刻加起来必须平。这条约束决定了「冻结」的实现方式。
挂一个限价单要冻结保证金,这里发生的不是把钱转到另一个账户,而是在同一个账户里把一部分余额标记成不可用:
保证金余额 = 可用余额 + 仓位占用保证金 + 挂单冻结保证金撤单就是把挂单冻结的那一份挪回可用余额,两个数一增一减,等式左边不变。如果实现成「转到系统账户」,那么每一次撤单、每一次成交、每一次崩溃恢复,都多一个可能对不上的地方 —— 而对不上意味着有人的钱凭空多了或少了。
同样的道理,结算不需要停机。永续合约没有交割日,资金费率到点就结,市场一秒都不停 —— OKX 的文档写得最直白:费用在毫秒级内完成计收,交易不会中断。这也是为什么各家都会补一句「结算时刻前后几秒开的仓,可能被算进这一轮,也可能不被算进」:结算是一个扫过账本的过程,不是一道闸门。
资金费率这笔钱还有一个容易搞错的性质:它在交易者之间直接划转,交易所不抽成。多头付给空头或者反过来,钱不经过交易所的收入账户。所以清算系统在这一步的角色不是收款方,而是一个必须做到「付出总额等于收到总额」的记账者 —— 一方付不出的时候缺口才轮到保险基金。
原文里那张流程图画的是「冻结交易 → 算盈亏 → 解冻交易」,那是传统期货每日无负债结算的模型,套在永续上是错的。
这套设计的代价
标记价格挡住了插针,也让强平价变得不好预测。 用户看到的盘口价格不是判定用的价格,「我明明没到那个价」的争议无法靠盘面截图解决,只能靠交易所公布标记价格的历史值。
保险基金是有限的,而且它的钱不是从穿仓用户那里来的。 这一点常被说反:Binance 的保险基金主要由清算清算费(对未穿仓的被强平仓位收取的那笔费用)注入,另外还包括基金接管穿仓仓位后如果反而赚了的那部分;Bybit 则明确写着来自平台注资加上「强平成交价优于破产价」的那段盈余。共同点是 —— 它靠的是清算得顺利的那些次攒钱,用来赔清算得不顺利的那些次。
它的用途也被限定得很死:只填破产价和实际成交价之间的差额,不用来补交易者的亏损。基金被抽干之后 ADL 就会启动,而 ADL 的成本落在盈利最多的那些账户身上 —— 他们没做错任何事,仓位却被强行减掉。这是整个机制里最难向用户解释的一环。
全仓的资金效率是有代价的。 一个仓位的亏损吃的是整个账户的余量,所以全仓账户被清算时通常一次损失比逐仓大得多。「较低的强平风险」这个说法只在价格小幅波动时成立。
已实现盈亏的算法不是唯一的。 多次部分平仓时,用先进先出还是用加权均价,会从同一串成交里算出不同的已实现盈亏(总额相同,但落在哪一笔上不同)。这件事直接影响用户的报税,所以是要写进规则文档、不能中途改的那一类决定。
这篇是 加密货币交易所的第 7 篇。前一篇是 加密货币交易所六:风险管理系统,后一篇是 加密货币交易所八:账户系统。