Liquidations, Liquidator Vault, and ADL
When an account's equity falls below the maintenance margin requirement, the protocol liquidates positions. The flow differs by margin mode:
- Cross positions liquidate together at the account level. The engine hands severely underwater accounts to the Liquidator Vault, an active backstop primitive. The vault absorbs positions at the user's entry price and unwinds them on the order book. If the vault hits its solvency cap, auto-deleveraging (ADL) tries to close residual positions at the bankruptcy price against the most profitable opposing traders, subject to each trader's break-even clamp.
- Isolated positions liquidate independently, one bucket at a time, but through the same waterfall as cross (orderbook → Liquidator Vault → ADL). Losses can't spill into the account's cross collateral or other isolated positions. See Isolated liquidation below.
Liquidation
When it triggers
The engine checks liquidations at the end of every block. The reference price used for the check depends on the market's asset class:
| Asset class | Liquidation reference price |
|---|---|
| Real estate | Oracle price — strongest manipulation resistance on thin daily-cadence orderbooks |
| Continuous markets | Median mark — median(oracle, impact_mid, last_trade), stays enforceable when the oracle goes stale |
An account is liquidatable when:
account_value < maintenance_margin_requiredThe engine computes both sides at the asset-class-appropriate reference price. See Mark price for why.
Account value
account_value = collateral + Σ unrealized_pnlThe engine computes unrealized_pnl for each open position at the liquidation reference price for that position's market.
Maintenance margin required
maintenance_margin_required = Σ position_mm
position_mm = position_size × liquidation_reference_price × MMRMMR (maintenance margin ratio) is a per-market parameter. Base (smallest-size) devnet values:
| Asset class | Initial margin (max leverage) | Maintenance margin |
|---|---|---|
| Real estate | 2% (50x) | 1% |
These are the base rates. Every live market uses tiers, so MMR is the position's tier rate. Maintenance margin rises and max leverage falls as the position grows. position_mm carries the tier's continuity credit:
position_mm = position_size × liquidation_reference_price × tier_MMR − tier_deductionAuthoritative source: GET /v1/markets, fields initialMarginRatioBps, maintenanceMarginRatioBps, and marginTiers.
How it works
Liquidation follows one ordered waterfall. What happens at each stage depends on how far underwater the estate is.
-
Cancel all open orders. This prevents new fills during liquidation. Order cancellation does not change account value or the maintenance requirement.
-
Close positions down until the account meets maintenance margin again. Each pass:
- If the account is deeply underwater (
account_value < (2/3) × maintenance_margin), the engine hands the position to the Liquidator Vault. The handoff is at the user's entry price, with no book close. - Otherwise the engine closes the minimum needed to restore the maintenance-margin requirement against the live orderbook. It goes largest position first, IOC, bounded at the bankruptcy price, and re-checks after each close. The engine closes a position over $50,000 notional in chunks (see Large positions).
- If the book can't fill within the bankruptcy bound, or slippage drives the account past
(2/3) × maintenance_margin, the position escalates to the vault.
- If the account is deeply underwater (
-
Vault → ADL. The vault absorbs what its solvency allows. The engine auto-deleverages any residual the vault can't take. See Auto-deleveraging.
So the engine works an account in the maintenance-margin band down on the book first. An account already past two-thirds maintenance goes straight to the vault. Every market has a required, routable backstop account.
Liquidation fills on the orderbook are fee-normal. The liquidated account pays the ordinary taker fee at its own volume tier on each fill, and makers on the other side earn their ordinary rebate. Both sides accrue 30-day volume, the same as any other fill. There is no separate liquidation penalty. The taker fee folds into the bankruptcy-price bound: a fill only executes at a price where the account can afford the fee, so the fee can't by itself push the account further underwater. Positions the Liquidator Vault absorbs are off-book transfers and carry no fee. There, the vault's PnL on the inherited inventory is the mechanism.
Partial vs full liquidation
Liquidations are partial by default for accounts in the maintenance-margin band. The engine only closes enough to restore the maintenance-margin requirement. The engine hands accounts that drop below (2/3) × maintenance_margin wholesale to the vault, with no partial-restore step.
Large positions
The engine liquidates a position over $50,000 notional that is not yet deeply underwater in chunks rather than all at once. It closes at most 20% of the position per pass, with a ~30-second cooldown between chunks so the book can refill. This caps the book impact of a large forced close. A position past (2/3) × maintenance_margin skips the cap and closes in full to the vault. Chunking therefore never delays a deeply-underwater position's escalation. The bankruptcy price still bounds every chunk. Isolated positions chunk the same way.
What you see
When the engine liquidates your account, you receive a WebSocket event on the liquidations channel:
{
"type": "liquidation",
"account_id": 12,
"positions_closed": 1,
"positions_remaining": 0
}If the vault absorbed your positions, a BackstopExecuted event will also fire on the events channel.
Isolated liquidation
Isolated positions liquidate one at a time, scoped to a single position and its bucket. This runs as a separate sweep right after the cross liquidation sweep at each block end.
When it triggers
For each open isolated position:
bucket + unrealized_pnl_at_reference_price < notional × MMRThe reference price is the same as for cross liquidations (oracle for real estate, median mark for everything else). When the inequality holds, that one position liquidates. The liquidation does not affect other positions on the account, cross or isolated.
How it closes
The position runs the same waterfall as a cross liquidation, scoped to its bucket. The engine cancels any open orders the account holds on that market. It then closes the position on the orderbook at IOC, bounded at the bucket's bankruptcy price. Realized PnL accrues into the bucket. The account's ordinary taker fee on each fill comes out of the bucket, inside the same bankruptcy bound. When the position fully closes, whatever remains in the bucket returns to the account's cross collateral.
If the book can't absorb it, the position escalates the same block to the Liquidator Vault and then to ADL against opposing profitable traders. The vault inherits the position and its pro-rata bucket. If the vault has no headroom and ADL exhausts profitable counterparties, the remaining position and bucket move to the Parcl Treasury master as bad debt.
Bad debt is confined
If the bucket can't absorb the loss (negative bucket after the close), the engine routes the deficit to the protocol Treasury as bad debt. This is the same sink cross liquidations use. The trader's cross collateral and any other isolated positions on the account stay exactly intact.
One reporting note: a close that takes several fills at the bound can leave a few lamports of rounding dust in the bucket. The engine routes that dust through the same bad-debt path. A Liquidated event can therefore show a lamport-scale bad_debt on an otherwise healthy isolated liquidation.
This is the headline isolated-margin guarantee: a blown-up isolated position can lose only what was in its bucket.
Why a voluntary close can be rejected
A user-initiated close is your PlaceOrder reduce, not the sweep. The validator rejects it if the worst-case fill at the order's price floor would push the bucket negative. The protocol walls off your cross collateral, so the shortfall can't come from there. It would land on the protocol as bad debt, and a voluntary order shouldn't hand the protocol a loss. The engine leaves a position that far underwater to the liquidation sweep.
For example, take an isolated long entered at $100 with a $5 bucket. A reduce of 1 unit against a $94 bid would realize a $6 loss, which is $1 more than the bucket holds. The validator rejects that close. A $95.50 limit would realize only $4.50 and pass.
In practice you'll see IsolatedReduceWouldCreateBadDebt when the close would still realize more loss than the bucket holds. That can be a market close into a deep enough gap, or a limit price below the bucket-survival point. The default cap on a market close is 10% of oracle. Tighten the limit, or let the next liquidation sweep handle it.
Liquidator Vault
The Liquidator Vault is an active backstop: it inherits inventory rather than just absorbing cash. The vault unwinds inherited positions through the orderbook.
Backstop account
The backstop is an ordinary margin account and may be a subaccount of the Parcl Treasury. Each market has one required backstop route. A single backstop account can serve more than one market.
Capacity
There is no fixed per-position cap. A backstop accepts as much inventory as its own solvency allows. Voluntary positions obey the same margin tiers as other accounts. Forced inherited positions may be grandfathered and reduce-only. When a backstop becomes liquidatable, it follows the same waterfall as another account. ADL exhaustion moves the residual estate to the Parcl Treasury master.
What you see
GET /v1/markets returns backstopAccountId for each market. GET /v1/accounts/{id} returns that backstop account's collateral, positions, and open orders.
Auto-deleveraging (ADL)
ADL is the backstop of last resort before Treasury absorption. It tries to close profitable opposing positions at the bankruptcy price, but clamps each match so the counterparty never realizes a loss.
When it triggers
- The vault is at capacity (the common case). The vault takes as much of the position as its own solvency allows and leaves the rest. The engine auto-deleverages that residual. This is the normal path when a large liquidation exceeds the vault's headroom.
- The bankrupt account is the route target. The recursion guard prevents a backstop from inheriting its own estate, so that estate continues directly to ADL. A missing market route or missing route account is invalid state, not a runtime routing mode.
How it works
For each market where the bankrupt account had a residual position:
-
Compute bankruptcy price.
entry - collateral/sizefor longs,entry + collateral/sizefor shorts. This is the price at which the position's PnL exactly equals the user's collateral. That is the loss the system has to socialize. -
Find opposing profitable traders. Liquidated long → ADL targets profitable shorts (and vice versa).
-
Rank by profit ratio. The engine sorts candidates by
unrealized_pnl / position_notional, highest first. -
Shared close price with a counterparty clamp. The engine tries the bankruptcy price, then clamps the pair to the profitable counterparty's entry price if necessary. Both positions close at the same resulting price. The target may forfeit positive unrealized PnL down to break-even, but never principal and never an amount that creates a loss. Open interest decrements symmetrically.
-
Residual to Treasury master. If ADL exhausts profitable counterparties, the unmatched position and its remaining balance move to the Parcl Treasury master. This is bad debt. ADL is best effort; it does not promise equal realized PnL.
What you lose in an ADL event
In an ADL event, the engine forcibly closes a portion of your profitable position at a price no worse than your own entry. You may forfeit positive unrealized PnL on that portion, down to break-even. ADL never turns that forced close into a realized loss.
Minimizing ADL risk
- ADL runs only on residual inventory that the market's backstop cannot absorb. More backstop headroom reduces that residual.
- Lower-leverage positions with modest PnL are further back in the queue.
- ADL cannot affect accounts with no open positions.
ADL events
Each ADL execution emits an AdlExecuted event:
market_id: which marketliquidated_account_id: the account whose position ADL reducedliquidated_side: side of the bankrupt positiontarget_account_id: the counterparty whose position ADL reducedtarget_side: side of the reduced positionclose_size: how much ADL closedclose_price: the bankruptcy pricerealized_pnl_forfeited: PnL the target gave up vs. their unrealized positionremaining_deficit: deficit remaining after this step