diff --git a/docs/whitepapers/liquidity/matching.md b/docs/whitepapers/liquidity/matching.md new file mode 100644 index 000000000..5eda8df93 --- /dev/null +++ b/docs/whitepapers/liquidity/matching.md @@ -0,0 +1,96 @@ +# Matching engine + +This document describes the matching algorithm of the liquidity module. +Basically, matching is done in batch-style in the liquidity module, +which means the timing of the order is not important like in other exchanges. + +## Matching type + +There are two types of order matching: *Single price auction* and +*Two-stage matching*. + +### Single price auction + +When there's no last price in the pair, *Single price auction* matching is done +for that pair. +With this type of matching, pools don't place orders on every tick but place orders at +single matching price. +The matching price is the best price where the largest portion of orders can be matched, +including pools. + +### Two-stage matching + +When there's last price in the pair, *Two-stage matching* is done for that pair. + +The first stage is like *Single price auction*, but with the matching price set to +the last price. +If there are no orders to match at the last price, this stage is skipped. + +In the second stage, buy orders with higher price are matched with sell orders with +lower price, which is typical. +The matching price depends on the direction of the price. +If the next price(last price of the batch after matching) is going to be higher than +the current last price(price increasing case), orders are matched at each sell order's price. +If the price is going to be decreased, then orders are matched at each buy order's price. +Otherwise, this stage is skipped since matching should have done in the first stage. + +#### Price range + +Order books are constructed from orders above and below 10%(the percentage can be +changed through a parameter change proposal) based on the pair's last price. +This restriction makes lower limit and upper limit of price within a pair. + +#### Price direction + +In the second stage of *Two-stage matching*, the direction of price is estimated and used +to decide matching price. +To estimate the price direction, following variables are calculated. + +- $B_o$: Sum of buy orders amount over the last price +- $S_u$: Sum of sell orders amount under the last price +- $B_e$: Sum of buy orders amount at the last price +- $S_e$: Sum of sell orders amount at the last price + +Then, price direction is determined as below: + +- If $B_o$ > $S_u + S_e$, price is **increasing** +- If $S_u$ > $B_o + B_e$, price is **decreasing** +- Otherwise, price is **staying** + +## Proportion based partial matching + +When matching orders, orders in the same tick takes amount from opposite side +fairly based on their proportion and priority. +Since there is no time priority in matching, orders with larger amount have +more amount to be matched. + +After distributing order amount to a tick, there can be small amount of orders +remaining because the amount isn't always divided into integers exactly. +These remaining amount is then distributed to the order with the highest priority. +If there's still a remaining amount, the next highest priority orders +take the amount. + +For sell orders, if the matching amount is too small so that the order receives +zero quote coin after matching, the order is excluded from matching to prevent +unfair trade. +The proportion of other orders is re-calculated after dropping the orders. + +### Order priority + +There should be priorities within orders in the same tick to distribute order +amount as described as above. +There are three criteria to decide the order's priority: + +1. Order amount +2. Type of orderer +3. Order(or pool) ID + +The order with larger amount gets higher priority than other orders. +If the amount was same, then the type of the orderer determines the precedence. +In the liquidity module user orders always gets higher priority than pool orders. +If even the type of the orderer was same, then time priority takes part in. +It is inevitable to consider time priority even if the liquidity module tries to be +as fair as possible, because there must be a precedence between orders when distributing +remaining order amount after dividing orders based on their proportion. +If two orders are from users then the order with lower order ID takes precedence, +and if two orders are pool orders, the order from pool with lower pool ID takes precedence. diff --git a/x/liquidity/spec/01_concepts.md b/x/liquidity/spec/01_concepts.md index 1d851a789..33280b446 100644 --- a/x/liquidity/spec/01_concepts.md +++ b/x/liquidity/spec/01_concepts.md @@ -22,6 +22,8 @@ and participate in the transaction as a single entity. The matchable orders stacked in the orderbook are matched to the most reasonable price by mathematical logic and unmatched requests are removed or remained according to user’s choice. +Read more about matching process in the [Liquidity pool white paper](../../../docs/whitepapers/liquidity/matching.md). + ## Tick System We introduce tick system in DEX, alongside with enabling order book feature. diff --git a/x/liquidity/spec/03_state_transitions.md b/x/liquidity/spec/03_state_transitions.md index bb4883674..9073281b4 100644 --- a/x/liquidity/spec/03_state_transitions.md +++ b/x/liquidity/spec/03_state_transitions.md @@ -70,36 +70,9 @@ of the targeted `Pool` and new pool coins are minted and sent to the depositor. After a successful withdraw transaction, escrowed pool coins are burned and corresponding amount of reserve coins are sent to the withdrawer from the liquidity `Pool`. -## Swap Process - -### Matching Price Calculation - -- CX(i): Sum of all buy amount of orders with tick equal to or higher than tick(i) -- CY(i): Sum of all sell amount of orders with tick equal to or lower than tick(i) -- Check matchability - - for all tick i, if CX(i) == 0 or CY(i) == 0, there is no matchable order - - If i < j, for highest tick i with CX(i) > 0 and lowest tick j with CY(j) > 0, there is no matchable order -- price discovery logic - - find tick(i) with CX(i+1) ≤ CY(i) and CX(i) ≥ CY(i-1) - - if there are two or more ticks that satisfies above condition - - tick(l): lowest tick with both conditions hold - - tick(h): highest tick with both conditions hold - - result = PriceToRoundedTick((tick(l)+tick(h))/2) - -### Matching Execution - -- Priority - 1. price: buy order with high order price, sell order with low order price - 2. amount: large amount → small amount - 3. orderbook type: order from pool → order from user - 4. id: small number id → large number id(pool id, order id) -- Matching - 1. line up buy orders with price greater than or equal to matching price and - sell orders with price less than or equal to matching price. - both are sorted by above priority, respectively. - 2. Then match each other based on the shorter side. - Orders at the end of each side may be partially matched. - Sell orders which receive no quote coin due to decimal truncation will be dropped during this process. +## Matching Process + +Read more about matching process in the [Liquidity pool white paper](../../../docs/whitepapers/liquidity/matching.md). ## Change states of orders with expired lifespan