HedgeDocs

Leverage / The mathematics

Leverage

The mathematics

The formulas the contracts run. Integer math truncates. The chain is the source of truth; the trade panel estimate can differ by a unit.

#Units and notation

QuantityRepresentation
MoneyUSDG, 6 decimals. $1.00 is 1e6.
Prices18 decimals, denoted ONE = 1e18. A $0.50 outcome is 0.5e18.
Rates and fractionsBasis points, BPS = 10_000. So 150 is 1.5% and 20_000 is 2.0x.

#Opening a position

Given a margin m, a leverage L in basis points, a spot price s, and a direction:

#Position size

size = (m × L) / BPS

#Entry price

The spread always moves against the trader, and the vault keeps the difference.

long   entry = (s × (BPS + spreadBps)) / BPS
short  entry = (s × (BPS − spreadBps)) / BPS

clamped into (0, ONE): entry of 0 becomes 1, entry >= ONE becomes ONE − 1

#Fee and net margin

fee       = (size × openFeeBps) / BPS
netMargin = m − fee

opening reverts if m <= fee

Note the base: the fee is a percentage of size, not of margin. At 2x a 1.5% fee therefore consumes 3% of the margin posted, and at 5x it consumes 7.5%.

#Shares

shares = (size × ONE) / entry

These are synthetic. They are the unit the position is marked in, not shares held at the venue.

#The vault reservation

The vault locks the worst it could ever owe on the position, which is not the borrowed amount:

long   reserve = shares > size ? shares − size : 0
short  reserve = size

The reasoning is the payoff structure. A long is worth shares if the price reaches $1.00, so the profit owed is shares − size. A short gains the entire position size if the price reaches zero. Reserving only the leverage top-up would let a few winners leave the vault unable to pay.

#Marking a position

value = (shares × s) / ONE

long   pnl = value − size
short  pnl = size − value

Profit and loss is signed and computed against size, which is why a 2x position moves twice as fast as the margin would suggest.

#Carry on borrowed capital

borrowed = size > m ? size − m : 0
owed     = (borrowed × borrowRateBps × elapsed) / (BPS × 3600)

capped:  owed > netMargin  ->  netMargin
  • elapsed is in seconds since the position opened, so carry accrues continuously rather than in hourly steps.
  • The base is size − margin, so a 1x position borrows nothing and pays nothing.
  • borrowRateBps is stored per position at open, so a later change to the global rate does not apply to it.
  • The cap matters: once carry would exceed the margin at risk, the position is already liquidatable and the vault takes the whole margin anyway.

#Liquidation

#The condition

Not a comparison against a stored price. The test is whether losses plus carry have eaten the permitted share of net margin:

loss    = pnl < 0 ? −pnl : 0
charged = loss + owed

liquidatable  when  charged >= (netMargin × liquidationThresholdBps) / BPS

#The liquidation price

Invert the profit and loss expression for the price at which the loss equals a given budget:

maxLoss = (netMargin × liquidationThresholdBps) / BPS

long    loss = size × (1 − P/entry)   ->   P = entry × (size − maxLoss) / size
short   loss = size × (P/entry − 1)   ->   P = entry × (size + maxLoss) / size

long, if maxLoss >= size            ->   P = 0
short, if P >= ONE                  ->   P = ONE − 1

#Two liquidation prices, and which one to trust

The figure stored at open never moves. The live figure subtracts accrued carry from the loss budget first, which pulls it closer as the position ages:

budget  = (netMargin × liquidationThresholdBps) / BPS
maxLoss = budget > owed ? budget − owed : 0

then the same inversion, with this smaller maxLoss

#Closing

A partial close takes a fraction f in basis points and scales every component by it:

closedSize   = (size × f) / BPS
closedShares = (shares × f) / BPS
closedMargin = (margin × f) / BPS
closedNet    = (netMargin × f) / BPS
closedReserve= (reserved × f) / BPS
closedOwed   = (accruedFunding × f) / BPS

exit fee     = (closedSize × closeFeeBps) / BPS

Because both size and margin shrink by the same fraction, the remainder accrues carry from the original timestamp on a smaller base, and the two pieces sum to what the whole position would have owed. Nothing is double charged and nothing is forgiven. Entry price and liquidation price are unchanged, so the remainder is the same trade in miniature.

Passing the full BPS closes everything. Anything less must leave a margin that still clears the minimum.

#The settlement order

What the trader receives is computed by working down from the net margin being closed. The order is fixed, and each deduction is capped by what is still left, which is how a wiped-out position settles without ever going negative:

remaining = closedNet

profit    remaining += pnl              (paid out of the vault)
loss      loss = min(−pnl, remaining)   (absorbed by the vault)
          remaining −= loss

carry     owed = min(owed, remaining)
          remaining −= owed

exit fee  fee = min(fee, remaining)
          remaining −= fee

payout    = remaining
  • Profit is added before anything is deducted, so a winning position pays its carry and exit fee out of the enlarged balance.
  • Each of the three deductions is capped at what remains. A position whose loss consumed the whole margin therefore pays no carry and no exit fee, not because they are waived, but because there is nothing to take.
  • The floor is zero. A trader can lose the entire margin and no more, and the vault cannot claim beyond it.

Losses and liquidated margin are credited to the vault by a different path than fees and carry, which is why the two have separate splits.

#Liquidation and the emergency exit

liquidated       absorbed = netMargin      (trader receives nothing)
emergency close  refund   = netMargin      (trader receives all of it)

The symmetry is the point. Both bypass the profit and loss calculation entirely. One because the margin is spent, the other because no trustworthy price exists to settle against. In both cases the entry fee stays with the vault, having been collected at open, and no exit fee is charged.

#Worked example

A 2x long on a $0.50 outcome with $2.50 of margin, at the default parameters:

QuantityWorkingResult
Size2.50 × 20000 / 10000$5.00
Entry price0.50 × 10100 / 10000$0.505
Entry fee5.00 × 150 / 10000$0.075
Net margin2.50 − 0.075$2.425
Shares5.00 / 0.5059.900990
Vault reservation9.900990 − 5.00$4.900990
Max loss2.425 × 0.90$2.1825
Liquidation price0.505 × (5.00 − 2.1825) / 5.00$0.284568

Worth reading the last row carefully: liquidation is a 43% fall from the $0.50 spot price, not the 20-odd percent that “2x with a 90% threshold” might suggest at a glance. The threshold applies to net margin, and the loss is measured against size.

Note also that the vault reserves $4.90 against a position the trader entered with $2.50, nearly twice the borrowed amount. That is the cost of reserving the true worst case, and it is why a $100 vault at the 30% exposure cap backs about six concurrent $5 positions rather than twelve.

#Capacity

Two independent ceilings, and the binding one is whichever is lower:

used        = lockedAssets
byExposure  = (totalAssets × maxPoolExposureBps) / BPS
byLiquidity = used + freeAssets

ceiling   = min(byExposure, byLiquidity)
available = ceiling > used ? ceiling − used : 0

The exposure ceiling is the risk limit, a deliberate cap on how much of the vault may be committed. The liquidity ceiling is physical: capital that is not there cannot be reserved regardless of what the risk limit permits.

A position is rejected if its reservation exceeds what remains available, and the check runs before any transfer, so hitting a full pool costs the trader nothing but gas and returns a distinguishable error rather than a generic failure.

#Available leverage

Read from the tier schedule against live vault assets, then capped:

walk the tiers while totalAssets >= tier.minTvl,
taking the last matching tier.maxLeverageBps

result = min(tierLeverage, maxLeverageBps)

default schedule
  $0       -> 20_000   (2.0x)
  $1,000   -> 30_000   (3.0x)
  $5,000   -> 40_000   (4.0x)
  $20,000  -> 50_000   (5.0x)

A replacement schedule must start at zero assets, ascend, and contain no row above the ceiling. Lowering the ceiling caps the result immediately, whatever the schedule says, which is the emergency brake.

#Vault share accounting

totalAssets = seniorAssets + juniorAssets
freeAssets  = totalAssets > lockedAssets ? totalAssets − lockedAssets : 0

deposit     shares = totalShares == 0 || seniorAssets == 0
                       ? assets
                       : (assets × totalShares) / seniorAssets

withdraw    assets = (shares × seniorAssets) / totalShares
                     reverts if assets > freeAssets

holding     assetsOf(lp) = (sharesOf[lp] × seniorAssets) / totalShares

#How income is split

fees                toSenior = (amount × feeSeniorBps) / BPS
                    toJunior = amount − toSenior

absorbed margin     toSenior = (amount × liquidationSeniorBps) / BPS
                    toJunior = amount − toSenior

both default to 7_000, i.e. 70% senior

Both add to the tranche totals without minting shares, which is exactly why the share price rises rather than balances changing.

#How losses are paid

fromJunior = amount > juniorAssets ? juniorAssets : amount
fromSenior = amount − fromJunior

reverts if fromSenior > seniorAssets

This is the first-loss rule in three lines: junior is drained to zero before senior is touched at all. The revert should be unreachable while the exposure cap holds, and exists because a silent underflow here would corrupt every depositor’s share price.

#Price feed constraints

RuleDefaultEffect
Maximum price age5 minutesOlder than this is treated as no price; opening halts.
Maximum deviation per update20%A larger gap is walked in over several updates rather than applied at once.
Convergingn/aTrue while the stored price differs from the reported target. Opening reverts; closing and liquidation stay available.

The clamp is computed on-chain from the true reported midpoint, so the contract knows the size of the gap itself. A compromised reporter cannot hide one by flattening it before submission.

#Where rounding falls

All of the above is integer arithmetic with truncating division. The directions are worth knowing:

  • shares truncates down, so a trader receives marginally less exposure than the exact quotient.
  • Fees truncate down, which favours the trader by at most one unit, and is precisely why a minimum margin exists, since below roughly $0.67 a 1.5% fee truncates to zero.
  • The senior share of income truncates down and the junior tranche receives the remainder, so the buffer collects the dust.
  • Deposit shares truncate down, so a depositor never mints more claim than they paid for.

#Panel vs chain

The browser quotes in floating point. The contract uses integer division. If they disagree, the contract is right. Read quoteOpen on-chain for the number you will be held to. Markets are keyed by the hash of the slug. No venue ids on-chain.

Defaults above are deploy-time and admin-settable. Leverage markets for traders, Earning as an LP for the vault.