This document provides detailed specs and math behind critical protocol functions.
<aside>
🚧 August 25, 2021 revised freeCollateral
and buyingPower
. Modeled all three configurations for different insolvency risk tolerance: conservative, moderate, aggressive. Currently we are using the conservative one
September 3, 2021 added pending funding payment to the model. Fixed realizedPnl
should not use unrealizedPnl
as its dependency. Minor fixes to all vQuote, vBase formulas
September 5, 2021 renamed vQuotePoolDebt
to openNotionalFraction
to match the naming in source codes. Added missing definitions for variables vQuoteLiquidity
and vQuoteOwedFee
. Revised market spec descriptions. Add references to Uniswap v3 whitepaper
September 14, 2021 merged avail
and debt
into balance
. You can find the older version here
September 17, 2021 renamed totalImReq
to openOrderMarginReq
. Removed positionValue
from openOrderMarginReq
so traders could open new position when his existing position value increases in his favor. Revise criteria for cancelExcessOrders()
and introduced two types of freeCollateral
September 27, 2021 revised cancelExcessOrder
so a maker's open orders can always be cancelled when he is about to be liquidated
October 3, 2021 added an interactive chart for learning margin specs
October 19, 2021 add specs for liquidation fee, exchange fee, insurance fund fee and accounting
November 2, 2021 update quote debt spec to current implementation (calculate account debt instead of individual market debt)
November 4, 2021 added maker/taker position specs
November 9, 2021 update maker/taker position specs with detailed states during add/remove liquidity and swap
November 10, 2021 update buying power specs. It should depend on market and the direction of open positions
November 11, 2021 update realizedPnl spec to be based on taker position. Add missing specs for reversing position
April 21, 2023 update the spec to MarkPrice and update PositionValue
is calculated based on Mark Price
</aside>
$$ \begin{aligned}
t &:= \text{epoch time in seconds} \\
d &:= \text{duration of time segment} \\\\
iTwap(t, d) &:= \text{index TWAP at the time point }t \text{ with duration } d\\\\
mTwap(t, d) &:= \text{market TWAP at the time point }t \text{ with duration } d\\\\
iP(t) &:= \text{index price at the time point }t \\\\
mP(t) &:= \text{market price at the time point }t \\\\
premium(t, d) &:= \text{premium at the time point }t \\ &= mTwap(t, d) - iTwap(t, d) \\\\
indexPrem(t, d) &:= iP(t) + premium(t, d) \\\\
markPrice(t) &:= median \space of \begin{cases} mTwap(t, 30\space mins) \\ indexPrem(t, 15 \space mins)\\ mP(t) \\ \end{cases}
\end{aligned} $$
$$ \begin{aligned} collateral &:= \text{balance of USDC in vault} \\\\ \text{settling } collateral &:= \begin{cases} collateral &\leftarrow collateral + owedRealizedPnl \\owedRealizedPnl &\leftarrow 0 \end{cases} \\\\ owedRealizedPnl &:= \text{realized PnL (in USD) that hasn't been settled(include funding payment and collected maker fees)} \\\\ \text{settling } funding &:= \begin{cases} owedRealizedPnl &\leftarrow owedRealizedPnl + pendingFundingPayment \\pendingFundingPayment &\leftarrow 0 \end{cases} \\\\ pendingFundingPayment &:= \text{funding payment (in USD) that hasn't been settled} \\\\ accountValue &= \underbrace{collateral + owedRealizedPnl + pendingFundingPayment + pendingFee}{totalCollateralValue} + \underbrace{\sum{market}{unrealizedPnl_{market}}}{totalUnrealizedPnl} \\&= collateral + owedRealizedPnl + pendingFundingPayment + pendingFee + \underbrace{\sum{market}{positionValue_{market}}}{totalPositionValue} + netQuoteBalance \\\\ netQuoteBalance &:= \text{quote position/exposure of this account(does not include pending fee or collected fees)} \\&= vQuote{balance} + vQuote_{inPool} \\ vQuote_{balance} &:= \text{unused (positive) or owed(negative) quote token of this account in a specific market} \\&= \sum_{market}{vQuote_{balance,market}} \\vQuote_{inPool} &= \sum_{market}{vQuote_{liquidity,market} + vQuote_{owedFee,market}} \\vQuote_{liquidity,market} &:= \text{amount of quote tokens the trader added to the market pool} \\vQuote_{owedFee,market} &:= \text{amount of the trader's unclaimed quote tokens as pool fees} \\\\ openOrderMarginReq(ratio) &:= \text{open order margin requirement (total collateral locked due to open orders)} \\&= (\sum_{market}{vBaseValue_{debt,market}} + vQuoteValue_{debt}) \times ratio \\\\ vBaseValue_{debt,market} &= vBase_{debt,market} \times vBaseMarkPrice_{market} \\vQuoteValue_{debt} &= vQuote_{debt} \times 1 \\vQuote_{debt} &= abs(min(0, \sum_{market}{vQuote_{balance,market}}))\\\\ imRatio &:= \text{initial margin requirement ratio} \\\\ freeCollateral_{withdrawal} &:= \text{amount of collateral available for withdrawal or opening new positions/orders} \\ &= freeCollateral(imRatio) \\\\ freeCollateral_{cancelOrder} &:= \text{negative means excess orders and can be cancelled} \\ &= freeCollateral(mmRatio) \\\\ freeCollateral(ratio) &= \begin{cases} min(totalCollateralValue, accountValue) - openOrderMarginReq(ratio), &\text{conservative, currently in use}\\ min(totalCollateralValue, accountValue - openOrderMarginReq(ratio)), &\text{moderate, Perp v1}\\ accountValue - openOrderMarginReq(ratio), &\text{aggresive, FTX}\end{cases} \\\\ buyingPower_{market,increasePosition} &= \frac{freeCollateral_{withdrawal}}{imRatio} \\buyingPower_{market,reversePosition} &= |positionValue_{market}| + buyingPower_{market,closed} \\buyingPower_{market,closed} &:= \text{estimated buying power after the current position is closed} \end{aligned} $$
$$ \begin{aligned}
accountMarginRatio &= \frac{accountValue}{\sum_{market}{|positionValue_{market}|}}\\\\
mmRatio &:= \text{maintenance margin ratio (account will be liquidated if its margin ratio falls below)} \\ accountMarginRatio &< accountMaintenanceMarginRatio \implies \text{liquidation (any market)!} \\\\ accountMaintenanceMarginRatio &= \frac{\sum_{market}{mmRatio_{market} \times |positionValue_{market}|}}{\sum_{market}{|positionValue_{market}|}} \\ &= \frac{mmRatio \times \sum_{market}{|positionValue_{market}|}}{\sum_{market}{|positionValue_{market}|}}, \text{ since currently all markets share the same mmRatio} \\ &= mmRatio \\\\
liquidationPenaltyRatio &:= \text{percentage paid by the trader to the liquidator when being liquidated} \\ liquidationFee &:= \text{actual amount paid by the trader to the liquidator} \\ &= exchangePositionNotional \times liquidationPenaltyRatio
\end{aligned} $$
<aside> 💡 Note regarding cross-margin:
Currently our system has a unified mmRatio
for all markets (similarly to imRatio
as mentioned below) so the calculation is relatively simpler. In the future, when mmRatio
differs from market to market, consider the following:
When you have multiple positions in different markets (ex. ETH & BTC), your accountMaintenanceMarginRatio
is based on the average of each market's mmRatio
weighted by your positionValue
. This means estimating liquidation prices might be tricky if you have many positions on different markets that performs differently. In other words, your specific position might get liquidated below or above its market's own mmRatio
because it is influenced by the performance of your other positions.
Refer to our simulations below for more examples (look for the "cross-margin" tab).
Since our liquidation specs are similar to FTX's, you can refer to their article "How can I determine my liquidation risk?" for more details.
</aside>
<aside> 💡 TODO:
verify if we switched to use taker position for close ratio calculation
need to revisit imRatio
's relationship with quoteToken
and if we can support customized imRatio
for each market
</aside>