> For the complete documentation index, see [llms.txt](https://docs.nexus.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexus.xyz/math-engine/liquidation-engine.md).

# 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 $$m$$ 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 $$C$$ 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.](/files/0yObb5920KoDR5ksHraJ)

*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    |
| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | --------- |
| $$C$$           | 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, ∞)   |
| $$p\_e$$        | entry\_price              | Volume-weighted entry price of the position.                                                                                                                 | USDX per contract    | (0, ∞)    |
| $$m$$           | 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, ∞)    |
| $$q$$           | size                      | Position size (always positive; direction is carried separately).                                                                                            | contracts            | (0, ∞)    |
| $$d$$           | direction                 | Position direction: $$+1$$ for long, $$-1$$ for short.                                                                                                       | dimensionless        | \[-1, 1]  |
| $$r\_m$$        | maintenance\_margin\_rate | Maintenance margin rate of the market (2.5% in the reference test parameters).                                                                               | fraction of notional | (0, 1]    |
| $$r\_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, ∞)    |
| $$P$$           | price                     | A raw (unaligned) candidate limit price, e.g. an unrounded bankruptcy price, prior to tick alignment.                                                        | USDX                 | unbounded |
| $$\ell\_i$$     | loss\_i                   | This market's loss at mark: $$\max(0, -\text{uPnL}\_i)$$, using the entry-price fallback when the mark is unset.                                             | USDX                 | \[0, ∞)   |
| $$\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=+1$$) the PnL is the mark's excess over entry scaled by size; for a short ($$d=-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.

$$
\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 $$C$$ is the shared account collateral; for an isolated position $$C$$ is that position's own allocated margin ((L.5)) and the sum runs over that single position only.

$$
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, $$q,m,r\_m$$, computed by the margin primitive (see [(M.3)](/math-engine/margin-math.md) where published). The account-level requirement is the sum over positions, with the same entry-price fallback for unset marks.

$$
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 $$C$$ 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.

$$
\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.

$$
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 $$N$$ times, setting bankruptcy prices too far from entry and understating bad debt by $$(N-1),C$$. Instead each market receives a share proportional to its loss $$\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/N$$. The exact-decimal rounding remainder $$C - \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 $$C$$. 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.

$$
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/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.

$$
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^{\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^{\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\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.

$$
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 $$q$$. A non-positive resulting quantity suppresses the order entirely.

$$
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 = \pi \cdot \Lambda \tag{L.12}
$$

### Position Takeover

On markets where the takeover model is enabled (rolled out per risk class), a liquidation does not close the position against the book inside the liquidation call. Instead, the position transfers **atomically, in whole, to a per-market system liquidation account at the bankruptcy price**, and the venue's liquidation engine unwinds it through the normal order book over the following ticks — one bankruptcy-bounded IOC attempt per tick, with a bounded depth sweep for thin books.

Three things are fixed at the moment of transfer, and nothing that happens afterwards changes them:

1. **Your settlement is final at transfer.** Your post-liquidation equity is set by the bankruptcy contract at the moment the position transfers, irrespective of how the unwind goes afterwards.
2. **Improvement goes to the insurance fund.** If the engine unwinds the position at better than the bankruptcy price, the improvement is added to the insurance fund — it is not returned to the liquidated account.
3. **Deterioration never comes back to you.** If the unwind does worse than the bankruptcy price, you owe nothing more. The shortfall is absorbed by the insurance fund up to a per-takeover budget fixed when the takeover opened; past that budget the remaining inventory is closed by auto-deleveraging against opposing positions. If no opposing positions remain to close against, that inventory stays on the system account and the market halts rather than the position being written off. Either way, any residual cash deficit is recorded as an explicit, auditable system loss with the market halted — never socialized onto other accounts' balances and never clawed back from winners.

The unwind pays no fee, earns no rebate, and gets no queue priority — the system account's orders route through the same book as everyone else's.

## Invariants

* The trigger threshold is inclusive: $$E \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, $$p\_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: $$\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 ($$q\_{\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 - \delta < P^{\downarrow} \le P$$ for sells and $$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 $$p\_e = 50{,}000$$ with collateral $$C = 2{,}500$$ (5% initial margin), in a market with $$r\_m = 0.025$$, $$r\_i = 0.05$$, tick $$\delta = 0.5$$. At entry the position is healthy: equity is $$2{,}500$$ against maintenance of $$50{,}000 \times 0.025 = 1{,}250$$ ((L.3)). Now the mark drops to $$m = 48{,}000$$. Fresh PnL is $$(48{,}000 - 50{,}000) \times 1 = -2{,}000$$ ((L.1)), so equity is $$2{,}500 - 2{,}000 = 500$$ ((L.2)), while maintenance rises against the new notional to $$48{,}000 \times 0.025 = 1{,}200$$. Since $$500 \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{,}000 - 2{,}500 / 1 = 47{,}500$$ ((L.7)). That value is already a multiple of the $$0.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{,}500$$.

In Partial mode with a larger book — a 5-contract long at the same entry, collateral $$12{,}500$$, mark at $$50{,}000$$ — the safe size is $$12{,}500 / (50{,}000 \times 0.05 \times 1.5) = 3.3\overline{3}$$, floored to the $$0.001$$ lot grid to $$3.333$$ contracts ((L.10)). The liquidation order therefore closes $$5 - 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](/files/txrGy0ONz2nNYFWHXBn3)

![Sensitivity tornado — Bankruptcy price](/files/JXS2fd5anwc3f3EAIHgY)

![Sensitivity tornado — Liquidation quantity](/files/859LtPb3MZ71oBO80hGh)

### 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).](/files/axK2vp5JblOoOIgn1h8C)

*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.](/files/E6YwkPuJnhGXgDwIborR)

*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.](/files/vscQHMO78IcPhOfa1fnb)

*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.*

### 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.](/files/ZTqGJZSLq6RktmsYT1t6)

*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.](/files/bDJS9BdxGWvRcu8dYqiE)

*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.](/files/YyiQCNPBcktuR9z2nwNN)

*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](/math-engine/funding-rate.md)
* Sibling model: [insurance-fund](/math-engine/insurance-fund.md)
* Sibling model: [margin-math](/math-engine/margin-math.md)
* Sibling model: [oracle](/math-engine/oracle.md)
* Sibling model: [order-book](/math-engine/order-book.md)
* Sibling model: [position-tracker](/math-engine/position-tracker.md)
* Sibling model: [settlement](/math-engine/settlement.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nexus.xyz/math-engine/liquidation-engine.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
