Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module 0x7::price_time_index

ActiveOrderBook: This is the main order book that keeps track of active orders and their states. The active order book is backed by a BigOrderedMap, which is a data structure that allows for efficient insertion, deletion, and matching of the order The orders are matched based on price-time priority.

This is internal module, which cannot be used directly, use OrderBook instead.

use 0x1::big_ordered_map;
use 0x1::error;
use 0x1::option;
use 0x5::order_book_types;
use 0x5::order_match_types;
use 0x7::order_book_utils;

Struct PriceAscTime

struct PriceAscTime has copy, drop, store
Fields
price: u64
tie_breaker: order_book_types::IncreasingIdx

Struct PriceDescTime

struct PriceDescTime has copy, drop, store
Fields
price: u64
tie_breaker: order_book_types::DecreasingIdx

Struct OrderData

struct OrderData has copy, drop, store
Fields
order_id: order_book_types::OrderId
order_book_type: order_book_types::OrderType
size: u64

Enum PriceTimeIndex

OrderBook tracking active (i.e. unconditional, immediately executable) limit orders.

  • invariant - all buys are smaller than sells, at all times.
  • tie_breaker in sells is U128_MAX-value, to make sure largest value in the book that is taken first, is the one inserted first, amongst those with same bid price.
enum PriceTimeIndex has store
Variants
V1
Fields
buys: big_ordered_map::BigOrderedMap<price_time_index::PriceDescTime, price_time_index::OrderData>
sells: big_ordered_map::BigOrderedMap<price_time_index::PriceAscTime, price_time_index::OrderData>

Constants

There is a code bug that breaks internal invariant

const EINTERNAL_INVARIANT_BROKEN: u64 = 2;

const EINVALID_MAKER_ORDER: u64 = 1;

const EINVALID_SLIPPAGE_BPS: u64 = 3;

const SLIPPAGE_PCT_PRECISION: u64 = 100;

========= Active OrderBook ===========

const U64_MAX: u64 = 18446744073709551615;

Function get_slippage_pct_precision

public fun get_slippage_pct_precision(): u64
Implementation
public fun get_slippage_pct_precision(): u64 {
    SLIPPAGE_PCT_PRECISION
}

Function new_price_time_idx

public(friend) fun new_price_time_idx(): price_time_index::PriceTimeIndex
Implementation
public(friend) fun new_price_time_idx(): PriceTimeIndex {
    // potentially add max value to both sides (that will be skipped),
    // so that max_key never changes, and doesn't create conflict.
    PriceTimeIndex::V1 {
        buys: order_book_utils::new_default_big_ordered_map(),
        sells: order_book_utils::new_default_big_ordered_map()
    }
}

Function best_bid_price

Picks the best (i.e. highest) bid (i.e. buy) price from the active order book. Returns None if there are no buys

public(friend) fun best_bid_price(self: &price_time_index::PriceTimeIndex): option::Option<u64>
Implementation
public(friend) fun best_bid_price(self: &PriceTimeIndex): Option<u64> {
    if (self.buys.is_empty()) {
        option::none()
    } else {
        let (back_key, _) = self.buys.borrow_back();
        option::some(back_key.price)
    }
}

Function best_ask_price

Picks the best (i.e. lowest) ask (i.e. sell) price from the active order book. Returns None if there are no sells

public(friend) fun best_ask_price(self: &price_time_index::PriceTimeIndex): option::Option<u64>
Implementation
public(friend) fun best_ask_price(self: &PriceTimeIndex): Option<u64> {
    if (self.sells.is_empty()) {
        option::none()
    } else {
        let (front_key, _) = self.sells.borrow_front();
        option::some(front_key.price)
    }
}

Function get_mid_price

Returns the mid price (i.e. the average of the highest bid (buy) price and the lowest ask (sell) price. If there are o buys / sells, returns None.

public(friend) fun get_mid_price(self: &price_time_index::PriceTimeIndex): option::Option<u64>
Implementation
public(friend) fun get_mid_price(self: &PriceTimeIndex): Option<u64> {
    if (self.sells.is_empty() || self.buys.is_empty()) {
        return option::none();
    };

    let (front_key, _) = self.sells.borrow_front();
    let best_ask = front_key.price;
    let (back_key, _) = self.buys.borrow_back();
    let best_bid = back_key.price;
    option::some((best_bid + best_ask) / 2)
}

Function get_slippage_price

public(friend) fun get_slippage_price(self: &price_time_index::PriceTimeIndex, is_bid: bool, slippage_bps: u64): option::Option<u64>
Implementation
public(friend) fun get_slippage_price(
    self: &PriceTimeIndex, is_bid: bool, slippage_bps: u64
): Option<u64> {
    if (!is_bid) {
        assert!(
            slippage_bps <= get_slippage_pct_precision() * 100,
            EINVALID_SLIPPAGE_BPS
        );
    };
    let mid_price = self.get_mid_price();
    if (mid_price.is_none()) {
        return option::none();
    };
    let mid_price = mid_price.destroy_some();
    let slippage = mul_div(
        mid_price, slippage_bps, get_slippage_pct_precision() * 100
    );
    if (is_bid) {
        option::some(mid_price + slippage)
    } else {
        option::some(mid_price - slippage)
    }
}

Function cancel_active_order

public(friend) fun cancel_active_order(self: &mut price_time_index::PriceTimeIndex, price: u64, unique_priority_idx: order_book_types::IncreasingIdx, is_bid: bool): u64
Implementation
public(friend) fun cancel_active_order(
    self: &mut PriceTimeIndex,
    price: u64,
    unique_priority_idx: IncreasingIdx,
    is_bid: bool
): u64 {
    if (is_bid) {
        let key = PriceDescTime {
            price,
            tie_breaker: unique_priority_idx.into_decreasing_idx_type()
        };
        self.buys.remove(&key).size
    } else {
        let key = PriceAscTime { price, tie_breaker: unique_priority_idx };
        self.sells.remove(&key).size
    }
}

Function is_taker_order

Check if the order is a taker order - i.e. if it can be immediately matched with the order book fully or partially.

public(friend) fun is_taker_order(self: &price_time_index::PriceTimeIndex, price: u64, is_bid: bool): bool
Implementation
public(friend) fun is_taker_order(
    self: &PriceTimeIndex, price: u64, is_bid: bool
): bool {
    if (is_bid) {
        let best_ask_price = self.best_ask_price();
        best_ask_price.is_some() && price >= best_ask_price.destroy_some()
    } else {
        let best_bid_price = self.best_bid_price();
        best_bid_price.is_some() && price <= best_bid_price.destroy_some()
    }
}

Function single_match_with_current_active_order

fun single_match_with_current_active_order<K: copy, drop, store>(remaining_size: u64, cur_key: K, cur_value: price_time_index::OrderData, orders: &mut big_ordered_map::BigOrderedMap<K, price_time_index::OrderData>): order_match_types::ActiveMatchedOrder
Implementation
inline fun single_match_with_current_active_order<K: drop + copy + store>(
    remaining_size: u64,
    cur_key: K,
    cur_value: OrderData,
    orders: &mut BigOrderedMap<K, OrderData>
): ActiveMatchedOrder {
    let is_cur_match_fully_consumed = cur_value.size <= remaining_size;

    let matched_size_for_this_order =
        if (is_cur_match_fully_consumed) {
            orders.remove(&cur_key);
            cur_value.size
        } else {
            modify_order_data(
                orders,
                &cur_key,
                |order_data| {
                    order_data.size -= remaining_size;
                }
            );
            remaining_size
        };

    new_active_matched_order(
        cur_value.order_id,
        matched_size_for_this_order, // Matched size on the maker order
        cur_value.size - matched_size_for_this_order, // Remaining size on the maker order
        cur_value.order_book_type
    )
}

Function get_single_match_for_buy_order

fun get_single_match_for_buy_order(self: &mut price_time_index::PriceTimeIndex, price: u64, size: u64): order_match_types::ActiveMatchedOrder
Implementation
fun get_single_match_for_buy_order(
    self: &mut PriceTimeIndex, price: u64, size: u64
): ActiveMatchedOrder {
    let (smallest_key, smallest_value) = self.sells.borrow_front();
    assert!(price >= smallest_key.price, EINTERNAL_INVARIANT_BROKEN);
    single_match_with_current_active_order(
        size,
        smallest_key,
        *smallest_value,
        &mut self.sells
    )
}

Function get_single_match_for_sell_order

fun get_single_match_for_sell_order(self: &mut price_time_index::PriceTimeIndex, price: u64, size: u64): order_match_types::ActiveMatchedOrder
Implementation
fun get_single_match_for_sell_order(
    self: &mut PriceTimeIndex, price: u64, size: u64
): ActiveMatchedOrder {
    let (largest_key, largest_value) = self.buys.borrow_back();
    assert!(price <= largest_key.price, EINTERNAL_INVARIANT_BROKEN);
    single_match_with_current_active_order(
        size,
        largest_key,
        *largest_value,
        &mut self.buys
    )
}

Function modify_order_data

fun modify_order_data<K: copy, drop, store>(orders: &mut big_ordered_map::BigOrderedMap<K, price_time_index::OrderData>, key: &K, modify_fn: |&mut price_time_index::OrderData|)
Implementation
inline fun modify_order_data<K: drop + copy + store>(
    orders: &mut BigOrderedMap<K, OrderData>,
    key: &K,
    modify_fn: |&mut OrderData|
) {
    let order = orders.borrow_mut(key);
    modify_fn(order);
}

Function get_single_match_result

public(friend) fun get_single_match_result(self: &mut price_time_index::PriceTimeIndex, price: u64, size: u64, is_bid: bool): order_match_types::ActiveMatchedOrder
Implementation
public(friend) fun get_single_match_result(
    self: &mut PriceTimeIndex,
    price: u64,
    size: u64,
    is_bid: bool
): ActiveMatchedOrder {
    if (is_bid) {
        self.get_single_match_for_buy_order(price, size)
    } else {
        self.get_single_match_for_sell_order(price, size)
    }
}

Function increase_order_size

Increase the size of the order in the orderbook without altering its position in the price-time priority.

public(friend) fun increase_order_size(self: &mut price_time_index::PriceTimeIndex, price: u64, unique_priority_idx: order_book_types::IncreasingIdx, size_delta: u64, is_bid: bool)
Implementation
public(friend) fun increase_order_size(
    self: &mut PriceTimeIndex,
    price: u64,
    unique_priority_idx: IncreasingIdx,
    size_delta: u64,
    is_bid: bool
) {
    if (is_bid) {
        let key = PriceDescTime {
            price,
            tie_breaker: unique_priority_idx.into_decreasing_idx_type()
        };
        modify_order_data(
            &mut self.buys,
            &key,
            |order_data| {
                order_data.size += size_delta;
            }
        );
    } else {
        let key = PriceAscTime { price, tie_breaker: unique_priority_idx };
        modify_order_data(
            &mut self.sells,
            &key,
            |order_data| {
                order_data.size += size_delta;
            }
        );
    };
}

Function decrease_order_size

Decrease the size of the order in the order book without altering its position in the price-time priority.

public(friend) fun decrease_order_size(self: &mut price_time_index::PriceTimeIndex, price: u64, unique_priority_idx: order_book_types::IncreasingIdx, size_delta: u64, is_bid: bool)
Implementation
public(friend) fun decrease_order_size(
    self: &mut PriceTimeIndex,
    price: u64,
    unique_priority_idx: IncreasingIdx,
    size_delta: u64,
    is_bid: bool
) {
    if (is_bid) {
        let key = PriceDescTime {
            price,
            tie_breaker: unique_priority_idx.into_decreasing_idx_type()
        };
        modify_order_data(
            &mut self.buys,
            &key,
            |order_data| {
                order_data.size -= size_delta;
            }
        );
    } else {
        let key = PriceAscTime { price, tie_breaker: unique_priority_idx };
        modify_order_data(
            &mut self.sells,
            &key,
            |order_data| {
                order_data.size -= size_delta;
            }
        );
    };
}

Function place_maker_order

public(friend) fun place_maker_order(self: &mut price_time_index::PriceTimeIndex, order_id: order_book_types::OrderId, order_book_type: order_book_types::OrderType, price: u64, unique_priority_idx: order_book_types::IncreasingIdx, size: u64, is_bid: bool)
Implementation
public(friend) fun place_maker_order(
    self: &mut PriceTimeIndex,
    order_id: OrderId,
    order_book_type: OrderType,
    price: u64,
    unique_priority_idx: IncreasingIdx,
    size: u64,
    is_bid: bool
) {
    let value = OrderData { order_id, order_book_type, size };
    // Assert that this is not a taker order
    assert!(!self.is_taker_order(price, is_bid), EINVALID_MAKER_ORDER);
    if (is_bid) {
        let key = PriceDescTime {
            price,
            tie_breaker: unique_priority_idx.into_decreasing_idx_type()
        };
        self.buys.add(key, value);
    } else {
        let key = PriceAscTime { price, tie_breaker: unique_priority_idx };
        self.sells.add(key, value);
    };
}