For the complete documentation index, see llms.txt. This page is also available as Markdown.

Position Tracker

Every trade on the Exchange either opens, grows, shrinks, closes, or reverses a position, and the position tracker is the single bookkeeping authority for what that position is worth. Its central design choice is the split between money already banked and money still at risk: when a fill closes part of a position, the profit on the closed quantity is settled immediately at that fill's own price (realized PnL), while the remainder keeps floating with the mark price (unrealized PnL). The entry price is a volume-weighted average that only ever moves when the position grows — reducing a position never rewrites its history.

From the same state the tracker derives the two prices that govern an account's survival. The liquidation price is where the account's collateral plus floating PnL falls to exactly the maintenance-margin requirement — the point where the Exchange must step in. The bankruptcy price is more adverse still: it is where the collateral is fully consumed and equity reaches zero. The gap between the two is the buffer the liquidation engine has to unwind the position before losses spill beyond the account.

Every fill branches on side into either an entry-price update or a realized-PnL close, and both risk prices are a fan-in of the same two state variables — entry price and collateral.

Every fill branches on side into either an entry-price update or a realized-PnL close, and both risk prices are a fan-in of the same two state variables — entry price and collateral.

Setting

A market is described by its parameters: the maintenance margin rate rmr_m, the taker fee βt\beta_t and maker rebate βr\beta_r (both in basis points). A position carries a side σ{+1,1}\sigma \in \{+1, -1\} (long, short), a positive size qq, and a volume-weighted entry price Pe>0P_e > 0. Fills arrive with a price Pf>0P_f > 0 and quantity qf>0q_f > 0, the market publishes a mark price Pmark>0P_{mark} > 0, and the position is backed by account collateral C0C \geq 0. All quantities are exact decimals in the implementation; the formulas below are exact real arithmetic.

Symbol
Name
Description
Units
Domain

qq

size

Absolute position size in units of the underlying asset; direction is carried separately by the side.

units of asset

(0, ∞)

PeP_e

entry_price

Volume-weighted entry price of the position.

USDX per base unit

(0, ∞)

PmarkP_{mark}

mark_price

The Exchange's mark price for the market at valuation time.

USDX per unit of asset

(0, ∞)

σ\sigma

side_sign

Direction indicator for the position: +1+1 for a long position, 1-1 for a short position.

dimensionless

[-1, 1]

PfP_f

fill_price

Execution price of an individual fill applied to the position.

USDX per base unit

(0, ∞)

qfq_f

fill_quantity

Quantity of an individual fill applied to the position (always positive; direction comes from the fill side).

base units

(0, ∞)

qcq_c

closed_qty

The portion of an opposing fill that closes existing position size: the smaller of the position size and the fill quantity.

base units

(0, ∞)

CC

account_collateral

Collateral backing the position being evaluated.

USDX

[0, ∞)

rmr_m

maintenance_margin_rate

Market maintenance margin rate; strictly less than the initial margin rate.

dimensionless ratio

(0, 1]

βt\beta_t

taker_fee_bps

Taker fee for the market, in basis points of fill notional.

basis points

[0, ∞)

βr\beta_r

maker_rebate_bps

Maker rebate for the market, in basis points of fill notional; stored as a signed value whose magnitude is credited.

basis points

[-∞, 0]

The mechanism

Building the position

When a fill lands on the same side as an existing position, the position grows and its entry price becomes the volume-weighted average of the old position and the new fill: the new entry is the total cost of both legs divided by the total quantity. This is the only path on which the entry price changes — reductions, closes, and funding leave it untouched, so PeP_e always answers the question "what did the currently open size cost, on average?" A fill that opens a fresh position is the degenerate case with q=0q = 0: the entry is simply the fill price.

Pe=qPe+qfPfq+qf(T.1)P_e' = \frac{q \, P_e + q_f \, P_f}{q + q_f} \tag{T.1}

Valuing the position

The floating profit of the open position is the signed distance from entry to mark, scaled by size: a long gains when the mark rises above entry (σ=+1\sigma = +1), a short when it falls below (σ=1\sigma = -1). This value is recomputed after every fill that leaves a position open, and again on every mark-price update; it is a pure revaluation and never moves collateral by itself.

uPnL=σ(PmarkPe)q(T.2)\text{uPnL} = \sigma \,(P_{mark} - P_e)\, q \tag{T.2}

Settling reductions

A fill on the opposing side first consumes existing position size. The quantity that closes is capped at the position size: a partial reduce closes the whole fill, a full close consumes exactly the position, and a flip closes the position and carries the excess into a new one. Only this closed portion settles PnL — the excess, if any, opens fresh exposure with no PnL of its own.

qc=min(q,qf)(T.3)q_c = \min(q, \, q_f) \tag{T.3}

Closing qcq_c units at the fill's own price banks the entry-to-fill difference, signed by the position side — the closing analogue of (T.2) with the fill price standing in for the mark. On a partial close this accumulates on the surviving position; on a full close or flip it is returned per fill so the risk layer can settle it into collateral, since the carried position (gone, or newly opened at (T.5)) cannot recover it. Fills that open or increase a position realize exactly zero.

rPnL=σ(PfPe)qc(T.4)\text{rPnL} = \sigma \,(P_f - P_e)\, q_c \tag{T.4}

When an opposing fill exceeds the position size, the position reverses through zero: the old side closes fully (realizing (T.4) on qc=qq_c = q), and the remainder opens a new position on the fill's side with the fill price as its fresh entry. The new position inherits none of the old accumulators.

qnew=qfq(T.5)q_{new} = q_f - q \tag{T.5}

Fees and funding

Each taker fill charges a fee proportional to its notional value, at the market's taker rate in basis points. The tracker debits this amount from the position's fee PnL accumulator so the full P&L breakdown (entry, funding, fee) is visible in one place; the cash movement itself is settled at the collateral layer. When a fill fully closes the position there is no surviving position to accumulate on, and the fee is settled entirely via collateral transfers.

Ft=qfPfβt10000(T.6)F_t = \frac{q_f \, P_f \, \beta_t}{10\,000} \tag{T.6}

Maker fills earn a rebate: the magnitude of the (negatively stored) maker rate, applied to the same fill notional as (T.6), is credited to the position's fee PnL. The absolute value guards against the sign convention — the stored rate is negative, but the credit is always positive.

Fm=qfPfβr10000(T.7)F_m = \frac{q_f \, P_f \, |\beta_r|}{10\,000} \tag{T.7}

Risk prices

The liquidation price is the mark at which the account's equity — collateral plus the unrealized PnL of (T.2) evaluated at that mark — falls to exactly the maintenance-margin requirement PqrmP \cdot q \cdot r_m. Solving C+σ(PPe)q=PqrmC + \sigma(P - P_e)q = P q r_m for PP gives a single closed form covering both sides: for a long (σ=+1\sigma=+1) the price sits below entry, for a short (σ=1\sigma=-1) above it. The formula is meaningful for leveraged positions, where collateral is less than notional; a fully collateralized long yields a non-positive (unreachable) result.

Pliq=PeqσCq(1σrm)(T.8)P_{liq} = \frac{P_e \, q - \sigma\, C}{q\,(1 - \sigma\, r_m)} \tag{T.8}

The bankruptcy price is where equity reaches zero: the entire collateral pool is consumed by the position's loss. Setting C+σ(PPe)q=0C + \sigma(P - P_e)q = 0 and solving gives the entry price shifted adversely by the per-unit collateral C/qC/q. Because the maintenance requirement in (T.8) is strictly positive, the bankruptcy price is always at least as adverse as the liquidation price — the gap is the liquidation engine's buffer to unwind the position before losses exceed the account.

Pbkr=PeσCq(T.9)P_{bkr} = P_e - \sigma\,\frac{C}{q} \tag{T.9}

Invariants

  • Every open position has q>0q > 0 and Pe>0P_e > 0. Why it holds: Opens and flips set size to a positive fill quantity (or positive excess qfqq_f - q) and entry to a positive fill price; increases add positive quantities; a reduction that would take size to zero returns no position at all rather than a zero-size one. So no path produces a live position with non-positive size or entry.

  • Partial closes never change the entry price: after a reducing fill, Pe=PeP_e' = P_e (see (T.1) for the only mutating path). Why it holds: The reduce branch subtracts from size and accumulates realized PnL but does not touch the entry field. Only the same-side (increase) branch recomputes the entry, and full closes and flips discard the old entry entirely.

  • For leveraged positions (C<PeqC < P_e q), the bankruptcy price is at least as adverse as the liquidation price: σPbkrσPliq\sigma P_{bkr} \le \sigma P_{liq}, i.e. PbkrPliqP_{bkr} \le P_{liq} for longs and PbkrPliqP_{bkr} \ge P_{liq} for shorts. Why it holds: At the liquidation price equity equals the maintenance requirement Pliqqrm>0P_{liq}\, q\, r_m > 0; at the bankruptcy price equity is zero. Since equity is monotone in the mark (decreasing for longs, increasing for shorts), the zero-equity price lies strictly further in the adverse direction whenever the maintenance requirement is positive. Verified by the property test prop_p005_bankruptcy_worse_than_liq over randomized sizes, entries, and collateral.

  • A fill realizes PnL only on the quantity that closes existing opposite-side exposure: opens and increases return exactly zero realized PnL, and a flip realizes on qc=qq_c = q, never on the newly opened qnewq_{new} of (T.5). Why it holds: The open and increase branches return a zero realized amount unconditionally. The opposing-side branch computes (T.4) on min(q,qf)\min(q, q_f) before any new position is constructed, so the reopened side starts with fresh, empty accumulators.

  • Funding accrual is additive and orthogonal: applying a funding payment adds its amount to the funding accumulator and changes no other field — in particular fee PnL, entry, and size are preserved. Why it holds: apply_funding performs a single addition to funding_accrued and returns; the cash flow is handled at the collateral layer. Test f005_funding_tick_preserves_fee_pnl confirms fee PnL survives funding ticks unchanged.

Worked example

A trader opens a long with a buy of 11 unit at Pf=45,000P_f = 45{,}000: the position is q=1q = 1, Pe=45,000P_e = 45{,}000. A second buy of 11 unit at 47,00047{,}000 increases the position, and (T.1) blends the two legs: Pe=(145,000+147,000)/2=46,000P_e' = (1 \cdot 45{,}000 + 1 \cdot 47{,}000)/2 = 46{,}000 on a size of q=2q = 2. With the mark at Pmark=50,000P_{mark} = 50{,}000, (T.2) values the position at uPnL=(50,00046,000)2=8,000\text{uPnL} = (50{,}000 - 46{,}000) \cdot 2 = 8{,}000 USDX of floating profit.

The trader now sells 11 unit at 50,00050{,}000. The fill opposes the long, so (T.3) gives qc=min(2,1)=1q_c = \min(2, 1) = 1, and (T.4) banks (50,00046,000)1=4,000(50{,}000 - 46{,}000) \cdot 1 = 4{,}000 USDX. The surviving position has q=1q = 1 at the unchanged entry Pe=46,000P_e = 46{,}000. If instead the trader had sold 33 units, the position would flip: qc=2q_c = 2 closes the long (realizing 8,0008{,}000), and (T.5) opens a short of qnew=1q_{new} = 1 at entry 50,00050{,}000.

Suppose the surviving 11-unit long is backed by C=10,000C = 10{,}000 USDX in a market with rm=0.005r_m = 0.005. (T.8) gives Pliq=(46,00010,000)/(10.995)36,180.90P_{liq} = (46{,}000 - 10{,}000)/(1 \cdot 0.995) \approx 36{,}180.90, and (T.9) gives Pbkr=46,00010,000=36,000P_{bkr} = 46{,}000 - 10{,}000 = 36{,}000. The mark reaches the liquidation threshold about 181181 USDX before equity would hit zero — that gap is the buffer within which the position must be unwound. Each taker fill along the way also cost (T.6): the opening 45,00045{,}000-notional buy, at βt=5\beta_t = 5 bps, debited 22.5022.50 USDX of fee PnL.

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 ε

vwap_entry

fill_price

0.5109

vwap_entry

entry_price

0.4891

vwap_entry

size

-0.01087

vwap_entry

fill_quantity

0.01087

unrealized_pnl

mark_price

12.5

unrealized_pnl

entry_price

-11.5

unrealized_pnl

side_sign

1

unrealized_pnl

size

1

closed_quantity

fill_quantity

1

closed_quantity

size

0

realized_pnl

fill_price

11

realized_pnl

entry_price

-10

realized_pnl

side_sign

1

realized_pnl

closed_qty

1

flip_size

fill_quantity

2

flip_size

size

-1

taker_fee

fill_quantity

1

taker_fee

fill_price

1

taker_fee

taker_fee_bps

1

maker_rebate

fill_quantity

1

maker_rebate

fill_price

1

maker_rebate

maker_rebate_bps

1

liquidation_price

entry_price

1.111

liquidation_price

size

0.1111

liquidation_price

account_collateral

-0.1111

liquidation_price

side_sign

-0.1061

liquidation_price

maintenance_margin_rate

0.005025

bankruptcy_price

entry_price

1.111

bankruptcy_price

side_sign

-0.1111

bankruptcy_price

account_collateral

-0.1111

bankruptcy_price

size

0.1111

Sensitivity tornado — Volume-weighted entry price
Sensitivity tornado — Unrealized PnL
Sensitivity tornado — Liquidation price

Response curves

More collateral pushes a long's liquidation price further below the 100,000 entry, and larger positions dilute each collateral dollar across more units; maintenance rate held at 0.5%.

More collateral pushes a long's liquidation price further below the 100,000 entry, and larger positions dilute each collateral dollar across more units; maintenance rate held at 0.5%.

Floating PnL is linear in the mark and mirror-imaged between long and short around the 46,000 entry, with size held at 2 units.

Floating PnL is linear in the mark and mirror-imaged between long and short around the 46,000 entry, with size held at 2 units.

The volume-weighted entry moves toward the fill price in proportion to the fill's share of the combined size; existing position held at 2 units entered at 46,000.

The volume-weighted entry moves toward the fill price in proportion to the fill's share of the combined size; existing position held at 2 units entered at 46,000.

References

  • Derived from and adversarially verified against the Exchange's Rust implementation and its test suite.

  • Sibling model: funding-rate

  • Sibling model: insurance-fund

  • Sibling model: liquidation-engine

  • Sibling model: margin-math

  • Sibling model: oracle

  • Sibling model: order-book

  • Sibling model: settlement

Last updated