For the complete documentation index, see llms.txt. This page is also available as Markdown.

Liquidation Engine

When a leveraged account's equity falls to or below the maintenance margin required by its open positions, the Exchange liquidates: it generates reduce-only IOC orders that close positions at prices anchored to the account's bankruptcy point. This document derives the exact mathematics implemented by the liquidation engine. Equity is always recomputed from fresh mark prices mm rather than cached values, with a non-positive mark treated as missing and replaced by the entry price so an un-oracled market can never fabricate phantom losses. The trigger threshold is inclusive: an account exactly at maintenance liquidates. For cross-margin accounts underwater in several markets at once, the single collateral pool CC is allocated across markets in proportion to each market's loss, so the pool offsets the portfolio shortfall exactly once. All arithmetic is exact decimal arithmetic; rounding enters only where quantities are floored to the lot size and prices are aligned to the tick.

One trigger inequality fans out into two independent per-position computations — a price (bankruptcy, tick-aligned) and a quantity (safe-size, partial-or-full) — that reconverge at the forced close, whose realized outcome feeds equity back into the trigger it came from.

One trigger inequality fans out into two independent per-position computations — a price (bankruptcy, tick-aligned) and a quantity (safe-size, partial-or-full) — that reconverge at the forced close, whose realized outcome feeds equity back into the trigger it came from.

Setting

Symbol
Name
Description
Units
Domain

CC

account_collateral

Collateral backing the positions being evaluated: the account's shared pool for cross margin, or the position's allocated cushion for isolated margin.

USDX

[0, ∞)

pep_e

entry_price

Volume-weighted entry price of the position.

USDX per contract

(0, ∞)

mm

mark_price

Current oracle mark price. A non-positive stored mark means 'no oracle update yet' and is replaced by the entry price before any equity or loss computation.

USDX per contract

(0, ∞)

qq

size

Position size (always positive; direction is carried separately).

contracts

(0, ∞)

dd

direction

Position direction: +1+1 for long, 1-1 for short.

dimensionless

[-1, 1]

rmr_m

maintenance_margin_rate

Maintenance margin rate of the market (2.5% in the reference test parameters).

fraction of notional

(0, 1]

rir_i

initial_margin_rate

Initial margin rate of the market (5% in the reference test parameters).

fraction of notional

(0, 1]

δ\delta

tick_size

Minimum price increment of the market. A non-positive tick disables alignment entirely (the raw price passes through).

USDX

(0, ∞)

λ\lambda

lot_size

Minimum quantity increment of the market.

contracts

(0, ∞)

PP

price

A raw (unaligned) candidate limit price, e.g. an unrounded bankruptcy price, prior to tick alignment.

USDX

unbounded

i\ell_i

loss_i

This market's loss at mark: max(0,uPnLi)\max(0, -\text{uPnL}_i), using the entry-price fallback when the mark is unset.

USDX

[0, ∞)

L\mathcal{L}

total_loss

Sum of losses across all positions being liquidated together.

USDX

(0, ∞)

π\pi

pnl_percent

ADL candidate's unrealized PnL as a fraction of position value.

fraction

unbounded

Λ\Lambda

leverage

ADL candidate's effective leverage.

dimensionless

(0, ∞)

The mechanism

The Liquidation Trigger

Equity is never computed from a cached PnL field, which may be stale between mark updates. Instead the engine recomputes unrealized PnL directly from the current mark price. For a long (d=+1d=+1) the PnL is the mark's excess over entry scaled by size; for a short (d=1d=-1) the sign flips. If the stored mark for a market is zero or negative — the stateless-boot sentinel for 'no oracle push yet' — the entry price is substituted, making the position's fresh PnL exactly zero rather than a catastrophic phantom loss.

uPnL=d(mpe)q(L.1)\text{uPnL} = d\,(m - p_e)\,q \tag{L.1}

Equity is collateral plus the sum of fresh unrealized PnL ((L.1)) over all open positions. For a cross-margin account CC is the shared account collateral; for an isolated position CC is that position's own allocated margin ((L.5)) and the sum runs over that single position only.

E=C+idi(mipe,i)qi(L.2)E = C + \sum_{i} d_i\,(m_i - p_{e,i})\,q_i \tag{L.2}

Each position requires maintenance margin proportional to its notional at the current mark, qmrmq\,m\,r_m, computed by the margin primitive (see (M.3) where published). The account-level requirement is the sum over positions, with the same entry-price fallback for unset marks.

M=qmrm(L.3)M = q\,m\,r_m \tag{L.3}

An account liquidates exactly when its equity ((L.2)) is at or below its total maintenance requirement ((L.3)). The threshold is inclusive: equity exactly equal to maintenance triggers liquidation. An account with no open positions never liquidates. The isolated variant is the single-position specialization with CC replaced by the position's allocated margin ((L.5)), evaluated against that position alone — sibling positions and account-level collateral never enter the isolated decision.

liquidate    EiMi(L.4)\text{liquidate} \iff E \le \sum_i M_i \tag{L.4}

Isolated Positions

An isolated position is backed by the margin allocated to it at fill time. When no allocation was recorded, the engine falls back to the open-time initial margin at market rate — the same cushion the position margins against downstream. The trigger ((L.4)) and the liquidation pricing both use this single value, computed once per trigger, as the position's collateral.

Ciso={Aif allocated margin A is recordedqperiotherwise(L.5)C_{\text{iso}} = \begin{cases} A & \text{if allocated margin } A \text{ is recorded} \\ q\,p_e\,r_i & \text{otherwise} \end{cases} \tag{L.5}

Cross-Margin Collateral Allocation

A cross-margin account holds one collateral pool backing positions in many markets. When several markets liquidate together, cushioning each market with the full pool would offset the portfolio's loss NN times, setting bankruptcy prices too far from entry and understating bad debt by (N1)C(N-1)\,C. Instead each market receives a share proportional to its loss i=max(0,uPnLi)\ell_i = \max(0, -\text{uPnL}_i); flat and winning positions receive nothing. If no position is at a loss (a degenerate case, since such a portfolio would not be liquidating) the pool splits evenly, C/NC/N. The exact-decimal rounding remainder CisiC - \sum_i s_i is folded into the largest-loss position (ties break to the highest market identifier after sorting by market, making the placement replay-deterministic), so the shares always sum to exactly CC. A set of zero or one positions receives the whole pool unchanged, so isolated and single-market paths are byte-identical to the pre-allocation behavior.

si=CiL,L=jj(L.6)s_i = C\,\frac{\ell_i}{\mathcal{L}}, \qquad \mathcal{L} = \sum_j \ell_j \tag{L.6}

Liquidation Order Pricing

The bankruptcy price is the mark at which the position's loss exactly exhausts its collateral share: equity from this position alone reaches zero. For a long it sits below entry by C/qC/q; for a short, above. This is the accounting anchor for the liquidation order's limit price. Note the caveat: liquidation orders are market-type IOC orders, so the book does not enforce this price as a matching bound — fills better than it produce insurance-fund spread profit, fills worse than it surface as bad debt.

pb=pedCq(L.7)p_b = p_e - d\,\frac{C}{q} \tag{L.7}

Bankruptcy prices ((L.7)) are unrounded ratios, and the book rejects limits not aligned to the market tick. The engine rounds toward executability — a sell (closing a long) rounds down — conceding at most one tick beyond the bankruptcy bound. The result is clamped to at least one tick so the limit stays strictly positive; a non-positive tick size disables alignment and passes the raw price through.

P=max ⁣(Pδδ,  δ)(L.8)P^{\downarrow} = \max\!\left(\left\lfloor \frac{P}{\delta} \right\rfloor \delta,\; \delta\right) \tag{L.8}

Symmetrically, a buy (closing a short) rounds up to the tick grid, so the worst-acceptable price stays acceptable. The same one-tick positivity clamp applies, though for a buy it can only bind when the raw price is non-positive.

P=max ⁣(Pδδ,  δ)(L.9)P^{\uparrow} = \max\!\left(\left\lceil \frac{P}{\delta} \right\rceil \delta,\; \delta\right) \tag{L.9}

Liquidation Sizing

The sizing rule is selected by the market's liquidation mode. In Full mode (the current default) the entire position closes. In Partial mode the engine computes the largest size the collateral can support at 1.5×1.5\times the initial margin rate — a buffer above initial margin so the surviving position is comfortably healthy — and floors it to the lot grid. Rounding is downward by design: rounding up would leave the position too large.

qsafe=C1.5mriλλ(L.10)q_{\text{safe}} = \left\lfloor \frac{C}{1.5\, m\, r_i\,\lambda} \right\rfloor \lambda \tag{L.10}

In Partial mode the order closes just the excess above the safe size ((L.10)); if the safe size already meets or exceeds the position, or the denominator is non-positive, the full position closes. In Full mode the quantity is simply qq. A non-positive resulting quantity suppresses the order entirely.

qliq={qqsafeq  or  Pri1.50qqsafeotherwise(L.11)q_{\text{liq}} = \begin{cases} q & q_{\text{safe}} \ge q \;\text{or}\; P\,r_i\,1.5 \le 0 \\ q - q_{\text{safe}} & \text{otherwise} \end{cases} \tag{L.11}

Auto-Deleveraging

When liquidation cannot fully absorb a bankrupt position, profitable counterparties are auto-deleveraged in priority order. The score multiplies profitability by leverage, so the most profitable, most leveraged accounts are selected first. Candidates are ranked descending by score; exact ties break ascending on account identifier bytes, which makes the ordering fully deterministic under replay. The amount a counterparty can absorb in settlement is capped at its unrealized PnL — the profit it gives up to cover bad debt.

S=πΛ(L.12)S = \pi \cdot \Lambda \tag{L.12}

Invariants

  • The trigger threshold is inclusive: EiMiE \le \sum_i M_i liquidates, so an account exactly at maintenance is liquidated ((L.4)).

  • An account with no open positions is never liquidated, and no liquidation order is ever generated against a market whose mark is unset (non-positive) — such marks fall back to the entry price for equity purposes and suppress order generation entirely.

  • For a long, pb<pliq<mp_b < p_{\text{liq}} < m at a healthy mark; for a short the inequalities reverse — the bankruptcy price always sits on the far side of the liquidation price ((L.7)).

  • Cross-collateral shares conserve the pool exactly: isi=C\sum_i s_i = C, with the rounding remainder folded deterministically into the largest-loss market ((L.6)).

  • Isolated liquidation depends only on the position's own allocated margin — account collateral and sibling positions never affect the isolated decision ((L.5)).

  • Liquidation orders are always reduce-only, market-type, IOC, flagged as liquidations, sized at most the position size (qliqqq_{\text{liq}} \le q), and sided opposite the position (long → sell, short → buy).

  • Aligned limit prices are strictly positive and within one tick of the raw bankruptcy price, on the executable side: Pδ<PPP - \delta < P^{\downarrow} \le P for sells and PP<P+δP \le P^{\uparrow} < P + \delta for buys, subject to the one-tick floor ((L.8), (L.9)).

  • ADL ranking is a permutation of its input (no drops, no duplicates) and is deterministic across replays, via the descending-score, ascending-account-id ordering ((L.12)).

Worked example

Consider a 1-contract long opened at pe=50,000p_e = 50{,}000 with collateral C=2,500C = 2{,}500 (5% initial margin), in a market with rm=0.025r_m = 0.025, ri=0.05r_i = 0.05, tick δ=0.5\delta = 0.5. At entry the position is healthy: equity is 2,5002{,}500 against maintenance of 50,000×0.025=1,25050{,}000 \times 0.025 = 1{,}250 ((L.3)). Now the mark drops to m=48,000m = 48{,}000. Fresh PnL is (48,00050,000)×1=2,000(48{,}000 - 50{,}000) \times 1 = -2{,}000 ((L.1)), so equity is 2,5002,000=5002{,}500 - 2{,}000 = 500 ((L.2)), while maintenance rises against the new notional to 48,000×0.025=1,20048{,}000 \times 0.025 = 1{,}200. Since 5001,200500 \le 1{,}200, the trigger fires ((L.4)).

With a single position the collateral share is the whole pool ((L.6)), so the bankruptcy price is 50,0002,500/1=47,50050{,}000 - 2{,}500 / 1 = 47{,}500 ((L.7)). That value is already a multiple of the 0.50.5 tick, so sell-side alignment leaves it unchanged ((L.8)), and the engine emits a reduce-only IOC sell for the full 1 contract (Full mode) with limit price 47,50047{,}500.

In Partial mode with a larger book — a 5-contract long at the same entry, collateral 12,50012{,}500, mark at 50,00050{,}000 — the safe size is 12,500/(50,000×0.05×1.5)=3.3312{,}500 / (50{,}000 \times 0.05 \times 1.5) = 3.3\overline{3}, floored to the 0.0010.001 lot grid to 3.3333.333 contracts ((L.10)). The liquidation order therefore closes 53.333=1.6675 - 3.333 = 1.667 contracts ((L.11)), leaving the survivor margined at one and a half times the initial requirement.

Analysis

Sensitivity

Elasticities ε = (∂y/∂x)·(x/y), computed numerically from the verified expressions at each worked-example point. |ε| > 1 means the output moves more than proportionally with that input.

Expression
Input
Elasticity ε

fresh_unrealized_pnl

entry_price

25

fresh_unrealized_pnl

mark_price

-24

fresh_unrealized_pnl

direction

1

fresh_unrealized_pnl

size

1

account_equity

entry_price

-100

account_equity

mark_price

96

account_equity

account_collateral

5

account_equity

direction

-4

account_equity

size

-4

maintenance_margin

size

1

maintenance_margin

mark_price

1

maintenance_margin

maintenance_margin_rate

1

liquidation_trigger

account_collateral

-5e+05

liquidation_trigger

direction

5e+05

liquidation_trigger

mark_price

-5e+05

liquidation_trigger

entry_price

5e+05

liquidation_trigger

size

5e+05

liquidation_trigger

maintenance_margin_rate

1.25e+04

isolated_margin_fallback

size

1

isolated_margin_fallback

entry_price

1

isolated_margin_fallback

initial_margin_rate

1

cross_collateral_share

account_collateral

1

cross_collateral_share

loss_i

1

cross_collateral_share

total_loss

-1

bankruptcy_price

entry_price

1.053

bankruptcy_price

direction

-0.05263

bankruptcy_price

account_collateral

-0.05263

bankruptcy_price

size

0.05263

aligned_price_sell

tick_size

1

aligned_price_sell

price

0

aligned_price_buy

tick_size

1

aligned_price_buy

price

0

safe_size

lot_size

0.09991

safe_size

account_collateral

0

safe_size

mark_price

0

safe_size

initial_margin_rate

0

partial_liquidation_qty

size

2.999

partial_liquidation_qty

lot_size

-0.1998

partial_liquidation_qty

account_collateral

0

partial_liquidation_qty

mark_price

0

partial_liquidation_qty

initial_margin_rate

0

adl_priority_score

pnl_percent

1

adl_priority_score

leverage

1

Sensitivity tornado — Account equity
Sensitivity tornado — Bankruptcy price
Sensitivity tornado — Liquidation quantity

Response curves

Equity of a 1-contract long at a $50,000 entry as the mark falls, for three collateral levels; the shaded region marks where the thinnest account's equity is at or below maintenance margin (2.5% of notional at mark).

Equity of a 1-contract long at a $50,000 entry as the mark falls, for three collateral levels; the shaded region marks where the thinnest account's equity is at or below maintenance margin (2.5% of notional at mark).

The liquidation order's price anchor falls linearly away from the $50,000 entry as the collateral backing the position grows, with slope inversely proportional to position size.

The liquidation order's price anchor falls linearly away from the $50,000 entry as the collateral backing the position grows, with slope inversely proportional to position size.

How much of a 5-contract position Partial mode closes as collateral varies, holding the mark at 50,000; beyond 18,750 the safe size covers the whole position and the close quantity reaches zero.

How much of a 5-contract position Partial mode closes as collateral varies, holding the mark at 50,000;beyond50,000; beyond 18,750 the safe size covers the whole position and the close quantity reaches zero.

Parameter space

Joint parameter effects evaluated from the verified expressions over 2-D grids.

The liquidation boundary is a straight line in mark-collateral space whose slope reflects the size-weighted price sensitivity, holding a 1-contract long from 50,000 entry at a 2.5% maintenance rate.

The liquidation boundary is a straight line in mark-collateral space whose slope reflects the size-weighted price sensitivity, holding a 1-contract long from 50,000 entry at a 2.5% maintenance rate.

Higher maintenance rates pull the liquidation price toward entry, showing how risk-parameter tightening shrinks the safe zone for a 1-contract long with 2,500 USDX collateral.

Higher maintenance rates pull the liquidation price toward entry, showing how risk-parameter tightening shrinks the safe zone for a 1-contract long with 2,500 USDX collateral.

The liquidated quantity falls in lot-quantized steps as collateral grows, and the full-liquidation cliff shifts outward with mark price since the safe size scales inversely with notional, for a 5-contract position at 5% initial margin.

The liquidated quantity falls in lot-quantized steps as collateral grows, and the full-liquidation cliff shifts outward with mark price since the safe size scales inversely with notional, for a 5-contract position at 5% initial margin.

References

  • Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.

  • Sibling model: funding-rate

  • Sibling model: insurance-fund

  • Sibling model: margin-math

  • Sibling model: oracle

  • Sibling model: order-book

  • Sibling model: position-tracker

  • Sibling model: settlement

Last updated