Skip to content

Commit

Permalink
Merge pull request #47 from crescent-network/cherry-pick/328-337
Browse files Browse the repository at this point in the history
docs: add liquidity module white paper (matching) #328 #337
  • Loading branch information
crypin authored Jul 13, 2022
2 parents 2341cfb + 895cf16 commit a9c9dcb
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 30 deletions.
96 changes: 96 additions & 0 deletions docs/whitepapers/liquidity/matching.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions x/liquidity/spec/01_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
33 changes: 3 additions & 30 deletions x/liquidity/spec/03_state_transitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit a9c9dcb

Please sign in to comment.