Skip to content

Risk methodology

Every rule a position lives by, with the numbers.

Margin, mark price, funding, liquidation and the insurance fund as the contracts compute them: formulas, precision, rounding and worked examples. The live panels at the end are read from the vault; the examples are not.

Isolated margin

Each position holds its own margin. Nothing else backs it, and nothing else is at risk when it fails.

A position is one signed quantity per account and market. Its margin is collateral moved from the account’s available bucket into the locked bucket of the vault, and only that margin can be lost on that position. Up to 32 positions per account may be open at once; each is independent.

Initial margin

Opening or increasing a position requires initialMarginFraction (IMF) of the new notional, where IMF = 1 / maximum leverage. The launch markets use 20x for BTC, ETH, SOL and the indices (IMF 5%), 10x for HYPE, equities and metals and oil (10%), and 5x for natural gas (20%).

margin = |qty| × price × IMF
Margin locked when a fill opens or increases exposure. Rounded up.

Maintenance margin

A position may be liquidated when its equity, margin plus unrealized PnL at the mark, falls below maintenanceMarginFraction (MMF) of its notional at the mark. In the launch catalogue MMF is half the IMF.

margin + PnL(mark) < |qty| × mark × MMF
The liquidation condition.

Reservation for resting orders

An order reserves, at placement, the initial margin and the taker fee for its full quantity at its limit price. The reservation moves from available to reserved, returns on cancellation or expiry, and is released in proportion as the order fills (the fill then locks the real margin and charges the real fee). Reduce-only orders reserve nothing.

reserved = notional × IMF + notional × takerFee
orderReservation in @elysta/protocol, mirroring reserveForOrder.

Adding and removing margin

A position’s owner can add margin from available at any time, and remove it as long as the position stays at or above its initial margin requirement at the mark with funding settled first. There is no cross-margin: a healthy position never covers an unhealthy one.

Mark price

The mark is what unrealized PnL, liquidation and funding read. It is derived from the validated index and the live book, never from the last trade.

mark = clamp(mid, index − index × markBandBps, index + index × markBandBps)
markPrice in the risk engine. markBandBps is 100 (1%) on every launch market.
  • Two-sided book: the mid of the best bid and best ask, clamped to the band around the index.
  • One-sided or empty book: the index itself.
  • Invalid index: there is no mark. Liquidation, funding settlement and margin removal revert with OracleInvalid; the market is halted.

Three prices are therefore never conflated: the index (the validated reference from the registry), the mark (above), and the last trade (a fact about the book, used by charts and nothing else). Market orders use the mark too: their protective limit is the mark moved by maxImpactBps (1% at launch), tightened by the trader’s own protection price.

Funding

Funding keeps the mark near the index by moving collateral between longs and shorts at every interval. The rate is computed on chain from a time-weighted premium; no service chooses it.

Premium accrual

On every state change of a market, and whenever anyone calls accruePremium, the engine adds premium × elapsed seconds to the market’s accumulator, where premium is (mark − index) / index. Seconds during which the index was invalid add nothing: a halted market neither pays nor receives funding for the gap.

Settlement

At each interval boundary (every 60 minutes at launch) settleFunding, callable by anyone, turns the average premium into a rate. Premiums are quoted per 8 hours, the industry reference period, and prorated to the market’s interval, so an hourly interval pays one eighth of the 8-hour premium. The rate is clamped to maxFundingRatePerInterval (0.0375% per interval (0.000375)).

rate = clamp(premiumTwap × intervalSeconds / 28,800, −cap, +cap) cumulativeIndex += rate × mark
settleFunding. premiumTwap is the accumulator divided by the window length.

Payment

A position pays or receives qty × (cumulativeIndex now − cumulativeIndex at its last settlement) whenever it is touched: on a fill, a liquidation or a margin change. A positive rate means longs pay shorts; a negative rate means shorts pay longs. Payments owed are taken from the position’s margin (and, should the margin not suffice, from the insurance fund, which halts the market if it cannot cover); payments received are added to margin.

payment = qty × Δ cumulativeIndex
fundingPayment in @elysta/protocol. Positive: the account pays.

Liquidation

Permissionless, at the mark price, partial where a smaller quantity restores health and full otherwise.

Who and when

Anyone except the position’s owner may call liquidate(account, marketId, maxQty) on a position whose equity is below maintenance margin, in a market that is not halted or inactive, with a valid index. maxQty is the most the liquidator is willing to take over; the call reverts with LiquidationQtyTooSmall if that is less than the quantity the formula requires.

How much

A partial liquidation leaves the remaining equity in the position and must bring it back to the initial margin ratio after the fee. The engine solves for the smallest quantity that does so, rounds it up to the lot, and liquidates everything if the result would be the whole position or would leave less than the minimum order quantity.

q = (|Q| × mark × IMF − equity) / (mark × (IMF − fee)), rounded up to a lot full if equity ≤ 0, q ≥ |Q|, or |Q| − q < minOrderQty
_liquidationQty. IMF and fee are WAD fractions; fee is liquidationFeeBps / 10,000.

What happens

  1. Funding is settled for both the position and the liquidator.
  2. PnL on the liquidated quantity is realized at the mark: a gain is added to the margin; a loss is taken from it, and past the margin from the insurance fund.
  3. The penalty, liquidationFeeBps (1%) of the liquidated notional, is taken from the remaining margin (never more than remains) and split: liquidatorShareBps (50%) to the liquidator’s available collateral, the rest to the insurance fund.
  4. The liquidator takes over the quantity at the mark on the same side, locking its own initial margin for it, subject to the market’s position limit.
  5. On a full liquidation whatever margin remains after loss and penalty is returned to the trader’s available collateral.
  6. Liquidated and two PositionChanged events record it all; if any bad debt was recorded the market is halted with reason BadDebt.

Liquidation price

For a long, the mark at which the position becomes liquidatable is (entry − margin / |Q|) / (1 − MMF); for a short, (entry + margin / |Q|) / (1 + MMF). It moves as margin is added or removed and as funding is settled into margin. The terminal shows it from liquidationPrice() on the risk engine.

Insurance fund and the loss waterfall

Losses are paid in a fixed order, and the order is visible on chain at every step.

  1. The position’s margin. A loss, a funding payment or a liquidation penalty is taken from the position’s own locked margin first.
  2. The insurance fund. Whatever the margin could not cover is requested from the fund (coverFromInsurance). The covered amount moves into the PnL pool, where it backs the counterparties’ gains in full.
  3. Bad debt. Whatever the fund could not cover is recorded as bad debt (BadDebtRecorded), the market is halted with reason BadDebt, and the status page shows the total. A bad-debt halt is never lifted automatically: a market manager lifts it after the fund has been replenished.

The fund is credited by 50% of every trading fee, by the insurance share of every liquidation penalty, and by voluntary contributions through fundInsurance. In this version no function pays it out: it can only grow or cover losses. When the collateral is the test dollar, the fund is seeded with it at deployment.

There is no auto-deleveraging. The thesis requires any such mechanism to be separately specified, simulated and tested before it is enabled, and none has been.

Precision and rounding

Every price, quantity, notional, collateral amount and fraction on chain is an 18-decimal integer (WAD). The direction of rounding is fixed per rule and always favours solvency.

Prices are USD per unit of the underlying, quantities are units of the underlying, collateral is USD. Token decimals are normalised once, at the vault’s edge: a deposit of 1,000.000000 tUSD (6 decimals) becomes 1,000 × 1018 in the ledger, and scale = 1012. Basis-point parameters are the only non-WAD numbers and are always named *Bps.

QuantityRoundsWhy
Notional (qty × price)DownNever overstate exposure.
Initial and maintenance marginUpAgainst the trader: slightly more is required than the exact fraction.
Trading feesUpAgainst the trader.
Funding owed by the payerUpAgainst the payer; the receiver’s credit rounds toward zero.
Realized and unrealized gainsDownNever create collateral.
Realized and unrealized lossesUpNever understate a loss.
Margin released on a partial closeDownThe dust stays in the position’s margin until it closes.
Liquidation quantityUp, then up to a lotLiquidate enough to restore health, never too little.
Penalty, incentive and insurance splitDown (basis points of notional)Capped at the margin that remains.

The dust these rules leave between a gain rounded down and the matching loss rounded up stays in the vault’s PnL pool, so rounding can never create collateral. The terminal’s estimates use the same functions (@elysta/protocol’s mulWad, mulWadUp, divWadUp) so that what you see before signing is what the chain computes.

Oracle validation

The registry turns adapter observations into one validated index, and refuses to trade on anything less.

On every refreshIndex the registry reads the market’s primary adapter, falls back to the secondary when the primary is missing or stale, and accepts a price only when all of the following hold:

  1. The price is positive and the observation is no older than heartbeatSeconds (90 s for crypto, 180 s for scheduled markets at launch).
  2. The move from the last valid price is within maxDeviationBps (2%), or within reopenDeviationBps (20%) when more than reopenGapSeconds (2 h) have passed since the last accepted update, because a market reopening after a closed session may gap.
  3. Otherwise the price is held as pending. If later observations stay consistent with it for deviationConfirmSeconds (120 s) the move is confirmed and adopted; if they return to the old level the pending price is discarded.

While a market has no acceptable price its status becomes Halted, the safe state: no orders, no matching, no liquidation, no funding settlement. Open positions keep their margin and can still be cancelled out of resting orders. A fresh valid price lifts an oracle halt automatically (MarketStatusChanged, reason OracleRecovered); an oracle manager can also lift it by hand after review, optionally adopting the pending price. A halt caused by bad debt is never lifted by a price.

Launch oracle

The adapter is fed by Elysta’s own keeper from HyperCore’s published oracle prices. The registry treats it like any adapter and validates every observation; the adapter is isolated behind IOracleAdapter so that a decentralised feed can replace it market by market. A deployment that settles real collateral must not rely on a single-updater adapter, and the roadmap says so; today’s deployments settle in test collateral.

Worked examples

Computed with the bigint functions of @elysta/protocol on the BTC-USD launch parameters (20x, 0.1 tick, 0.0001 lot). They illustrate the rules; they are not live data and no market is at these prices.

Example 1

Placing and filling a long

Worked example · not live data

A trader buys 0.5 BTC with a limit order at 60,000 on a market with 20x maximum leverage (initial margin fraction 5%, taker fee 0.05%).

Inputs

Quantity
0.5000 BTC
Limit price
60,000.0 USD
Initial margin fraction
5.00%
Taker fee
0.05%
  1. Notionalqty × price, rounded down
    30,000.00 USD
  2. Initial marginnotional × IMF, rounded up
    1,500.00 USD
  3. Taker feenotional × takerFee, rounded up
    15.00 USD
  4. Reserved on placementinitial margin + taker fee
    1,515.00 USD
  5. If 0.2 BTC fills firstreservation × 0.2 / 0.5 is released, rounded down
    606.00 USD
  6. Fee split on a full fillinsuranceShareBps 50%
    7.50 USD to the insurance fund, 7.50 USD to protocol fees
  7. As a maker insteadnotional × makerFee
    6.00 USD
  8. Liquidation price at entry(entry − margin / |Q|) / (1 − MMF)
    58,461.53 USD

The position opens with 1,500.00 USD of isolated margin locked and 15.00 USD charged as a taker; it becomes liquidatable if the mark falls to 58,461.53 USD.

Example 2

Mark price from index and book

Worked example · not live data

The mark is the book mid clamped to ±1% (markBandBps 100) around the validated index. It is what unrealized PnL, liquidation and funding read.

Inputs

Index price
60,000.0 USD
Mark band
±600.0 USD (1%)
  1. Book 59,990 / 60,010mid 60,000 is inside the band
    60,000.0 USD
  2. Book 60,900 / 61,000mid 60,950 exceeds index + band, so it is clamped
    60,600.0 USD
  3. Bid only, no askno two-sided quote: the index itself
    60,000.0 USD

A thin or one-sided book cannot move the mark more than 1% from the index, and an empty book cannot move it at all.

Example 3

An hourly funding settlement

Worked example · not live data

Over one hour the mark averaged 0.08% above the index (the time-weighted premium). The interval is 3,600 s; the cap is 0.0375% per interval.

Inputs

Premium TWAP
0.08%
Interval
3,600 s (1 h)
Reference period
28,800 s (8 h)
Mark at settlement
60,000.0 USD
  1. RatepremiumTwap × 3,600 / 28,800, clamped to ±cap
    0.0100%
  2. Payment per unitrate × mark, added to the cumulative funding index
    6.0000 USD per BTC
  3. A long of 0.5 BTCqty × index delta; positive means the account pays
    pays 3.00 USD
  4. A short of 0.5 BTC
    receives 3.00 USD
  5. If the premium had been 1%raw rate 0.1250% exceeds the cap
    clamped to 0.0375%

Longs pay shorts when the premium is positive, shorts pay longs when it is negative, and no single interval can move more than the cap.

Example 4

A partial liquidation

Worked example · not live data

The long from the first example (0.5 BTC at 60,000 with 1,500 USD of margin) is marked at 58,000. Maintenance margin fraction is 2.5%, the liquidation fee 1% of liquidated notional, split half to the liquidator and half to the insurance fund.

Inputs

Position
long 0.5000 BTC at 60,000.0 USD
Margin
1,500.00 USD
Mark
58,000.0 USD
Liquidation price
58,461.53 USD
  1. Unrealized PnLqty × (mark − entry), a loss rounded up
    -1,000.00 USD
  2. Equitymargin + PnL
    500.00 USD
  3. Maintenance margin|Q| × mark × MMF, rounded up
    725.00 USD — equity is below it, so the position is liquidatable
  4. Required initial margin|Q| × mark × IMF
    1,450.00 USD
  5. Quantity to liquidate(required − equity) / (mark × (IMF − fee)), rounded up
    0.409482758620689656 BTC exact
  6. Rounded up to the lotlot 0.0001; the remainder 0.0905 is at least the minimum order
    0.4095 BTC (partial)
  7. Loss realised on that quantity at the mark
    819.00 USD
  8. PenaltyliqQty × mark × 1%, capped at the margin left
    237.51 USD
  9. Liquidator incentive / insurance share50% / 50% of the penalty
    118.755 USD / 118.755 USD
  10. Margin left on the remaining 0.0905 BTC
    443.49 USD against 262.45 USD required
  11. The liquidator's new longtakes over liqQty at the mark and must lock its own initial margin
    0.4095 BTC at 58,000.0 USD, margin 1,187.55 USD

0.4095 BTC is taken over at 58,000.0 USD; the trader keeps 0.0905 BTC with 443.49 USD of margin, back above the initial requirement.

Example 5

The loss waterfall after a gap

Worked example · not live data

The same long is marked at 56,000 after a price gap: the loss exceeds the position's own margin.

Inputs

Position
long 0.5000 BTC at 60,000.0 USD, margin 1,500.00 USD
Mark
56,000.0 USD
  1. Unrealized PnL
    -2,000.00 USD
  2. Equitymargin + PnL, at or below zero
    -500.00 USD — full liquidation
  3. Step 1: the position's marginthe whole margin is debited
    1,500.00 USD
  4. Step 2: the insurance fundcoverFromInsurance(shortfall)
    500.00 USD requested
  5. With a fund of 250,000 USD
    500.00 USD covered, no bad debt; the fund falls to 249,500.00 USD
  6. With a fund of only 200 USDcovered = min(shortfall, fund)
    200.00 USD covered, 300.00 USD recorded as bad debt, market halted (reason BadDebt)
  7. Penalty280.00 USD by formula, capped at the margin left
    0.00 USD: nothing remains, so the liquidator earns no incentive

The counterparties' gains are still paid in full: the covered amount moves from the insurance fund into the PnL pool. Anything the fund cannot cover is bad debt, visible on the status page, and the market stays halted until a market manager lifts it after the fund is replenished.

Example 6

Where rounding goes

Worked example · not live data

Every amount is an 18-decimal integer. When a result does not fit, the direction of rounding is fixed by the rule, never by the caller.

Inputs

WAD
1e18 = 1.000000000000000000
tUSD token unit
1e-6 USD = 0.000001000000000000 in the ledger
  1. Closing 0.1 of a 0.3 BTC position with 1,000 USD marginmargin × 0.1 / 0.3, rounded down
    333.333333333333333333 USD released; the last 1e-18 stays in the position's margin
  2. 950 / 2,320 as a quantitywadDiv rounds down, wadDivUp rounds up
    0.409482758620689655 vs 0.409482758620689656
  3. Gains and lossesa gain rounds down, the matching loss rounds up
    the difference accumulates in the vault's PnL pool
  4. WithdrawalstokenAmount × scale ≤ available
    a balance smaller than one token unit cannot be withdrawn and stays in the ledger

Rounding can only ever favour the vault's solvency: no rule rounds in a direction that could create collateral.

Live figures

Read from the vault through the API and refreshed while you read. Each panel says where its figures came from and at which block; when the API cannot be reached, it says that instead.

Live data arrives with the Elysium mainnet launch

This panel reads the exchange API, which goes live with the Elysium mainnet launch. Until then there is nothing to show, and nothing here is simulated.

Live data arrives with the Elysium mainnet launch

This panel reads the exchange API, which goes live with the Elysium mainnet launch. Until then there is nothing to show, and nothing here is simulated.

Live data arrives with the Elysium mainnet launch

This panel reads the exchange API, which goes live with the Elysium mainnet launch. Until then there is nothing to show, and nothing here is simulated.

The status page shows the same vault figures beside the health of every service and oracle feed, refreshed every ten seconds.