@somnia-chain/markets-sdk


@somnia-chain/markets-sdk / index / perpLiquidationPrice

Function: perpLiquidationPrice()

perpLiquidationPrice(p): bigint | null

Defined in: packages/sdk/src/perp/margin.ts:437

Solve for the mark price at which one market's position trips maintenance margin — the shared kernel behind client.getLiquidationPrice and client.previewPerpLiquidationPrice, which is why a current and a projected price cannot disagree on identical inputs.

Pure: no client, no block, no I/O. Feed it a consistent snapshot and it is exact arithmetic.

Both sides of the inequality move with the price. Liquidation begins where equity == mmReqMarginBank._classify returns PartialLiquidation the moment equity < mmReq. Equity moves with the mark through unrealized PnL, at size / oneBase per unit of price. But mmReq moves too, because _marketHealthFromSnapshot recomputes it as ceil(|size| × mark × mmBps / (oneBase × 10000)) against the CURRENT mark. Solving equity(p) == mmReq(p) therefore carries a factor a fixed-mmReq estimate drops:

text
long    p = mark − (equity − mmReq) × oneBase × 10000 / (|size| × (10000 − mmBps))
short   p = mark + (equity − mmReq) × oneBase × 10000 / (|size| × (10000 + mmBps))

Dropping that factor errs conservative on a long and optimistic on a short — reporting the liquidation further away than it is, on the side whose loss is unbounded. At a 250 bps maintenance threshold it misplaces a short's liquidation by ~2.5% of the distance to it.

A single-market solve, deliberately. Only this market's mark is varied; every other market's contribution stays at the value baked into equity and mmReq. So the answer is "the price at which THIS market's move alone trips maintenance", which is what a per-position liquidation price means under cross margin. A correlated move across several markets liquidates sooner, and nothing here claims otherwise.

Also held constant: pending funding (already netted into equity), the mark's own EMA lag, and any fill landing between the read and the move.

Details

  • Returns: The liquidation price in raw quote units per whole base, floored at 0 (a price cannot go negative) — or null when the position is flat, or when mmBps is exactly 10000 on a long, where price cancels out of the inequality entirely and no price triggers liquidation. That last case is unreachable on a real pool: PerpPool._validatePerpPoolParameters enforces initialMarginBps ≤ 10000 and maintenanceMarginBps < initialMarginBps, so mmBps < 10000 on every market.

Parameters

p

PerpLiquidationPriceInputs

Returns

bigint | null