Mark prices
The mark price is the price that the protocol uses for margin, liquidation, funding and triggers. This page describes how the protocol computes it and where to read it.
Stable.
Summary
The mark price is the authoritative price of the protocol per asset. It is the price for margin, liquidation, funding and trigger evaluation. It is a median of three components: the oracle anchor (plus a basis EMA), the internal quote mid, and the external perp median. The protocol recomputes it continuously. It is not the last trade price.
Mark price and last trade
A margin system on the last trade price is open to abuse. A small adversarial trade at a manipulated price can push other users into liquidation. The mark is a smoothed composition of several sources, which is hard to push.
Composition
The implemented mark is the 3-component median below (mark_source: "oracle_median"). It is not a flat median(mid, oracle, ema_mid).
A deployed market priced by a deployer push uses a different rule (mark_source: "deployer_oracle"), since block 44,478,893. On such a market the mark equals the pushed price while the feed is inside its refresh window. The book does not move it. Past the window the market is off-session: the last push is the reference price and the indicative mark is the book mid clamped into one move band around it (±min(10 %, 1/(2L))). The off-session rule is in effect since block 44,563,346. Why: the market has no external perp feed (no C3), so a lone bid and ask with no fill could move the mark and liquidate positions at no cost to the person who quoted. The push move band limits how far each push can move the price (see deploy a perp). Funding on these markets uses the impact-price formula: the premium compares the price to fill a fixed notional on the book with the pushed price. So funding still pulls the book toward the pushed price. A side too thin to fill that notional adds no premium sample, so a lone small quote does not move funding either.
Computation
mark = median( present components of {C1, C2, C3} )
# 3 present → median3 2 present → midpoint 1 present → that value
# a lone C2 (no external anchor) yields no update — see below
The three components are:
| Component | Definition |
|---|---|
| C1 (oracle anchor) | C1 = oracle + EMA(quote_mid − oracle). The oracle plus a fixed-decay EMA of the perp's basis (a half-life of about 150 s at the recompute cadence) |
| C2 (internal book) | mid(best_bid, best_ask). Best bid and ask only. It requires both sides and an uncrossed book, otherwise None. It excludes the last trade deliberately: with the last trade in it, the recompute would refer to itself and could freeze a thin one-sided book |
| C3 (external perps) | median(external perp mids) over the 5 perp venues (Binance, OKX, Bybit, Gate, MEXC). It requires at least 2 venues, otherwise None |
The outer median tolerates one outlier component. Absent components drop out. With two components, the mark is their midpoint. With one, the mark is that value. With no internal book and no external perps, mark = C1 = oracle + EMA(basis). The mark thus moves toward the spot oracle and does not freeze.
The protocol rejects a lone C2. If the only component is the internal quote mid (no oracle, no external perps), the mark does not change. A single resting spread, which an adversary controls, thus cannot set the liquidation and funding price. A lone C1 (external oracle) or a lone C3 (already a median of at least 2 venues) is allowed.
- The C1 EMA is the deterministic
DeterministicEma: fixed-point(num, denom), fixed decay0.9548, which is about a 150 s half-life at the ~10 s recompute cadence. It folds inquote_mid − oracleonly when a usable two-sided quote and an oracle both exist. An empty or one-sided book thus adds no noise to it. A market that never had a usable quote keepsEMA = 0, that is,C1 = the bare oracle. - The EMA is per asset (one for each perp market).
- The protocol writes the computed median to the authoritative mark of the market, which every consumer reads. An ordinary fill that writes the last trade price cannot replace it. The median is authoritative for active markets, not only for quiet ones.
An earlier version of this page modelled C2 as median(bid, ask, last_trade) and C3 as a 7-venue weighted median. The real shapes are as follows. C2 is a two-sided quote mid: best bid and ask only, with the last trade excluded. C3 is the median of 5 external perp venues, with at least 2 required. C1 is the oracle plus a fixed-decay (0.9548) EMA of the perp basis. Absent components drop out of the median (there is no unwrap_or(C1) substitution), and a lone C2 gives no update.
Two price planes
Read this section before you read any number. MTF keeps prices on two distinct numeric planes. This is the most common cause of scale errors.
| Plane | Type | Scale | Used by |
|---|---|---|---|
| Book / order / mark plane | FixedPrice (i128) / price_e8 | 1e8 fixed-point (raw integer = price × 10⁸) | the order book in committed state, last_mark_px, the EVM precompiles (mark_px_e8, entry_px_e8), and the write side: /exchange order limit_px and trigger_px |
| Oracle / notional / collateral plane | rust_decimal::Decimal | whole-USDC (1 unit = 1 USDC) | the oracle aggregator + mark computer (C1/C2/C3 all in Decimal), funding oracle_px, PM scenario engine, margin/health, and the human markets read fields mark_px/oracle_px |
The mark computer and the oracle aggregation run fully in the Decimal
whole-USDC plane. The protocol converts the result to the 1e8 FixedPrice plane
when it writes it to the book and last_mark_px.
The read side is human. The write side is raw. This rule decides the plane of a number:
- Every
/inforead answers in whole units, as a decimal string.marketsreportsmark_pxandoracle_pxscaled back (for example"67042.335", not raw 1e8).l2_booklevelpxandsz, andtick_size, are the same. The node divides by 10⁸ and rounds to the tick before it answers. Do not multiply a read value by 10⁸. /exchangeorder submission fields stay raw 1e8.limit_pxandtrigger_pxareu64tick units, andsizeis raw lots. The caller must convert a read price back for submission.
Funding settlement is not on a third plane. Core settles funding in the
Decimal whole-USDC plane, the same plane as the oracle and margin.
accumulated_funding_e6 is a field of the EVM precompile ABI only. It is in the
encoded input and output of the mark_settle precompile, because a Solidity
caller has no decimal type and needs a fixed integer scale. Core does not settle
on it, and no /info read returns it. Always check the plane of a formula
before you compare magnitudes.
The oracle (C1 anchor)
oracle, the C1 anchor, is the weighted median of up to 10 external spot venues: Binance 3, OKX 2, Bybit 2, Coinbase 2, and Bitget, Kraken, KuCoin, Gate, MEXC and MetaFlux-spot 1 each, for a sum of 15. It is published once per block, and the oracle validators sign it. Governance can set the weights per symbol (SetOracleWeights, ActionId 148). A feed that is stale for more than 60 s, or more than 5 % away from the cross-venue median, is dropped. If less than 50 % of the weight is present, the slot keeps its last good value.
The C3 component of the mark uses a separate set of feeds: perp mids from the 5 perp venues (Binance, OKX, Bybit, Gate, MEXC), not the spot oracle table. See Oracle prices for the full composition, the reliability rules and the per-symbol overrides.
markets_meta returns mark_source (for example "oracle_median"). markets returns the composed mark_px and oracle_px. The individual C1/C2/C3 components and the weighted source list are not wire fields.
Mark and oracle divergence
The mark can be far from the oracle. This is normal. The oracle tracks spot. The mark tracks the price at which the perp trades. A perp can have a persistent basis, a premium or a discount to spot:
- C2 is the mid of the MetaFlux perp book, and C3 is the external perp median. Both reflect the perp, not spot.
- C1 is
oracle + EMA(quote_mid − oracle). The basis EMA pulls the oracle anchor toward the running premium of the perp. Thus all three components track the perp.
For example, when a perp trades at a 30 % discount to its spot index, mark ≈ perp and oracle ≈ spot correctly diverge by about 30 %. Funding exists to close that gap. If the oracle itself is unreliable, funding is gated off and decays to 0, even while the mark-oracle gap is wide. A large gap with funding near 0 thus means that the protocol does not trust the oracle for that market. It does not mean that funding is broken. See funding gating.
Sanity bands
The mark computer runs the 3-component median: absent components drop out, and a lone C2 is rejected. It does not run the per-block clamp that this section describes. The clamp(candidate, prior ± max_step) ramp is a design specification. The structural median is the main defence against manipulation. The nearest guard in the code copies the oracle into the last_mark_px of a stale book, with no ramp. Read the numbers below as the intended band design. Check them against the values that a market reports before you rely on them.
Even with the composition, an adversary can push two of the three sources at the same time. The design thus has the mark enforce bands per block:
prior_mark = mark at block T-1
band_pct = market parameter, default 0.5% per second
max_step = prior_mark * band_pct * (block_time_ms / 1000)
candidate = median(...)
mark_T = clamp(candidate, prior_mark - max_step, prior_mark + max_step)
| Default | Value |
|---|---|
band_pct | 0.5% per second |
block_time_ms | 100 ms |
max_step_per_block | ~0.05% |
At 0.05% per 100 ms, a 5% move takes about 10 seconds. A real fast move catches up over a small number of blocks. An adversarial spike is clamped to a short ramp.
If the candidate exceeds the band repeatedly for band_violation_blocks (default 50, about 5 s), the protocol assumes a real regime change. It widens the band by 2× for one window. This prevents a permanent freeze during a real market dislocation.
What uses mark
| Consumer | Why mark? |
|---|---|
| Margin (init + maint) | Stable basis that resists manipulation |
| Liquidation tier eval | Same |
| Funding (vs oracle) | The funding formula requires it |
| Trigger orders (StopLoss / TakeProfit) | Resists single-trade spikes |
| PnL display (unrealised) | Stable number for users |
| Insurance accounting | Stable |
These consumers use the last trade, not the mark:
- Fill prices on the book. Trades fill at actual book prices.
- Maker and taker fees. They are computed on the fill price, not the mark.
- Realized PnL. It is computed at the exit fill price.
Querying
curl -X POST https://api.testnet.mtf.exchange/info \
-H 'content-type: application/json' \
-d '{"type":"markets","coin":"BTC"}'
The markets read reports mark_px
and oracle_px on the human-decimal plane (for example "67042.335"), and the
mark_source descriptor:
{
"type": "markets",
"data": {
"coin": "BTC",
"mark_source": "oracle_median",
"mark_px": "67042.335",
"oracle_px": "67042.335"
}
}
The three internal components C1/C2/C3 (oracle+EMA anchor, internal-book
median, external-perp weighted median) and the band state (Ok / Banded /
Frozen) are inside the mark computer. They are not markets wire fields
today. Only the composed mark_px is published. Banded means that the band
clamped the candidate in this block. Frozen means that all sources failed and
the protocol holds the prior mark.
A dedicated mark WS channel is on the WS roadmap. It does not stream yet. Until then, poll markets for mark_px.
Edge cases
Show edge cases
- Real 5% move in 1 s. The band clamps the first 10 blocks, and the mark catches up at about 0.05% per block. Then the regime-change detection widens the band, and the mark catches up faster. The total lag is about 1–2 seconds: large but bounded.
- Oracle outage with more than 50 % of the weight missing. When less than 50 % of the spot weight is present in a tick, the oracle slot does not update. The previous good tick stays. C1 keeps the last good oracle plus its EMA, so the mark stays anchored.
- Empty or one-sided MTF book. C2 drops out. The mark is
midpoint(C1, C3), orC1alone if the perps are also down. It tracks the oracle anchor blended with the external perps. Funding still uses the available oracle. Liquidations continue against the oracle-anchored mark. - All external perps down. C3 drops out. The mark is
midpoint(C1, C2): the internal quote mid against the oracle anchor. - No book and no perps. Only C1 remains, so
mark = C1 = oracle + EMA(basis). The mark is anchored on the oracle only. - Only a one-sided or empty quote, with no oracle. The protocol rejects a lone C2, so there is no update. The mark keeps its last value until an external anchor (oracle or perps) returns.
- Stale C1 EMA. The EMA is always defined once at least one internal mid has folded in. It keeps its last value while no new mid arrives, because it updates only when C2 is present.
- Trigger orders during a freeze. Trigger evaluation uses the mark. During a freeze, no trigger fires. Resting orders wait until a real mark resumes.
Mark during a spike
The 3-component median removes a single-source spike before any band applies:
block T-1 C2(book mid)=100.0 C1(oracle+EMA)=100.0 C3(perps)=100.0 → median3 = 100.0
block T: thin MTF book; adversary lifts the internal mid to 110.0
C2 = 110.0, C1 = 100.05, C3 = 100.05 (external perps + oracle unmoved)
mark = median3(100.05, 110.0, 100.05) = 100.05
↑ the adversarial C2 is the OUTLIER → median discards it; mark ≈ oracle anchor
block T+1: adversary persists at 110.0; oracle/perps drift up slowly
C2 = 110.0, C1 = 100.10, C3 = 100.10
mark = median3(100.10, 110.0, 100.10) = 100.10
... mark tracks the (C1, C3) consensus; a single manipulated source never wins the median
The defence is structural. To move the median, an adversary must move at least two of the three components. C1 (oracle) and C3 (the 5-venue external perp median) are the components that are hard to move. The optional per-block sanity band is an extra clamp on top.
See also
- Funding rates: funding uses the mark against the oracle.
- Tiered liquidation: tier evaluation against the mark.
markWS channel (roadmap)- Oracle prices: the full source list, weights and reliability rules.
FAQ
Show FAQ
Q: Why not use the oracle directly? A: With a mark from the oracle only, the oracle operators could liquidate the book by manipulating the feed. A median of three spreads the trust across sources.
Q: Can I see what the band did in the past?
A: Mark history with the band state will be available when the mark WS channel ships (roadmap), and through archive indexer responses. It is not on the current read surface yet.
Q: Will the band cause unfair liquidations? A: The band slows the mark relative to the underlying. During a real crash, your maintenance can stay healthy about 1 second longer than at a venue that uses the last trade. The reverse is also true: the mark unwinds slowly. The net effect is that liquidation behaviour is more deterministic and harder to abuse.