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::single_order_book

This module provides a core order book functionality for a trading system. On a high level, it has three major components

  1. 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.
  2. PendingOrderBookIndex: This keeps track of pending orders. The pending orders are those that are not active yet. Three types of pending orders are supported.
  • Price move up - Triggered when the price moves above a certain price level
  • Price move down - Triggered when the price moves below a certain price level
  • Time based - Triggered when a certain time has passed
  1. Orders: This is a BigOrderedMap of order id to order details.
use 0x1::big_ordered_map;
use 0x1::error;
use 0x1::option;
use 0x1::string;
use 0x1::vector;
use 0x5::order_book_types;
use 0x5::order_match_types;
use 0x5::single_order_types;
use 0x7::order_book_utils;
use 0x7::pending_order_book_index;
use 0x7::price_time_index;

Enum SingleOrderBook

enum SingleOrderBook<M: copy, drop, store> has store
Variants
V1
Fields
orders: big_ordered_map::BigOrderedMap<order_book_types::OrderId, single_order_types::OrderWithState<M>>
client_order_ids: big_ordered_map::BigOrderedMap<order_book_types::AccountClientOrderId, order_book_types::OrderId>
pending_orders: pending_order_book_index::PendingOrderBookIndex

Constants

const E_REINSERT_ORDER_MISMATCH: u64 = 8;

const EINVALID_ADD_SIZE_TO_ORDER: u64 = 6;

const EINVALID_INACTIVE_ORDER_STATE: u64 = 5;

const EORDER_ALREADY_EXISTS: u64 = 1;

const EORDER_CREATOR_MISMATCH: u64 = 9;

const EORDER_NOT_FOUND: u64 = 4;

const EPOST_ONLY_FILLED: u64 = 2;

const E_NOT_ACTIVE_ORDER: u64 = 7;

const ENOT_SINGLE_ORDER_BOOK: u64 = 10;

const ETRIGGER_COND_NOT_FOUND: u64 = 11;

Function new_single_order_book

public(friend) fun new_single_order_book<M: copy, drop, store>(): single_order_book::SingleOrderBook<M>
Implementation
public(friend) fun new_single_order_book<M: store + copy + drop>(): SingleOrderBook<M> {
    SingleOrderBook::V1 {
        orders: order_book_utils::new_default_big_ordered_map(),
        client_order_ids: order_book_utils::new_default_big_ordered_map(),
        pending_orders: new_pending_order_book_index()
    }
}

Function cancel_order

Cancels an order from the order book. If the order is active, it is removed from the active order book else it is removed from the pending order book. If order doesn’t exist, it aborts with EORDER_NOT_FOUND.

order_creator is passed to only verify order cancellation is authorized correctly

public(friend) fun cancel_order<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_creator: address, order_id: order_book_types::OrderId): single_order_types::SingleOrder<M>
Implementation
public(friend) fun cancel_order<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_creator: address,
    order_id: OrderId
): SingleOrder<M> {
    let order_with_state_option = self.orders.remove_or_none(&order_id);
    assert!(order_with_state_option.is_some(), EORDER_NOT_FOUND);
    let order_with_state = order_with_state_option.destroy_some();
    let (order, is_active) = order_with_state.destroy_order_from_state();
    let order_request = order.get_order_request();
    assert!(order_creator == order_request.get_account(), EORDER_CREATOR_MISMATCH);
    if (is_active) {
        price_time_idx.cancel_active_order(
            order_request.get_price(),
            order.get_unique_priority_idx(),
            order_request.is_bid()
        );
        if (order_request.get_client_order_id().is_some()) {
            self.client_order_ids.remove(
                &new_account_client_order_id(
                    order_request.get_account(),
                    order_request.get_client_order_id().destroy_some()
                )
            );
        };
    } else {
        self.pending_orders.cancel_pending_order(
            order_request.get_trigger_condition().destroy_some(),
            order.get_unique_priority_idx()
        );
        if (order_request.get_client_order_id().is_some()) {
            self.client_order_ids.remove(
                &new_account_client_order_id(
                    order_request.get_account(),
                    order_request.get_client_order_id().destroy_some()
                )
            );
        };
    };
    return order
}

Function try_cancel_order_with_client_order_id

public(friend) fun try_cancel_order_with_client_order_id<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_creator: address, client_order_id: string::String): option::Option<single_order_types::SingleOrder<M>>
Implementation
public(friend) fun try_cancel_order_with_client_order_id<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_creator: address,
    client_order_id: String
): Option<SingleOrder<M>> {
    let account_client_order_id =
        new_account_client_order_id(order_creator, client_order_id);
    let order_id = self.client_order_ids.get(&account_client_order_id);
    if (order_id.is_none()) {
        return option::none();
    };
    option::some(
        self.cancel_order(price_time_idx, order_creator, order_id.destroy_some())
    )
}

Function try_cancel_order

public(friend) fun try_cancel_order<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_creator: address, order_id: order_book_types::OrderId): option::Option<single_order_types::SingleOrder<M>>
Implementation
public(friend) fun try_cancel_order<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_creator: address,
    order_id: OrderId
): Option<SingleOrder<M>> {
    let is_creator =
        self.orders.get_and_map(
            &order_id,
            |order| order.get_order_from_state().get_order_request().get_account()
                == order_creator
        );

    if (is_creator.is_none() || !is_creator.destroy_some()) {
        return option::none();
    };

    option::some(self.cancel_order(price_time_idx, order_creator, order_id))
}

Function client_order_id_exists

public(friend) fun client_order_id_exists<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_creator: address, client_order_id: string::String): bool
Implementation
public(friend) fun client_order_id_exists<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_creator: address, client_order_id: String
): bool {
    let account_client_order_id =
        new_account_client_order_id(order_creator, client_order_id);
    self.client_order_ids.contains(&account_client_order_id)
}

Function place_maker_or_pending_order

Places a maker order to the order book. If the order is a pending order, it is added to the pending order book else it is added to the active order book. The API aborts if it’s not a maker order or if the order already exists

public(friend) fun place_maker_or_pending_order<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_req: single_order_types::SingleOrderRequest<M>)
Implementation
public(friend) fun place_maker_or_pending_order<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_req: SingleOrderRequest<M>
) {
    let ascending_idx = next_increasing_idx_type();
    if (order_req.get_trigger_condition().is_some()) {
        return self.place_pending_order_internal(order_req, ascending_idx);
    };
    self.place_ready_maker_order_with_unique_idx(
        price_time_idx, order_req, ascending_idx
    );
}

Function place_ready_maker_order_with_unique_idx

fun place_ready_maker_order_with_unique_idx<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_req: single_order_types::SingleOrderRequest<M>, ascending_idx: order_book_types::IncreasingIdx)
Implementation
fun place_ready_maker_order_with_unique_idx<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_req: SingleOrderRequest<M>,
    ascending_idx: IncreasingIdx
) {
    let order = new_single_order(order_req, ascending_idx);
    assert!(
        self.orders.upsert(
            order_req.get_order_id(), new_order_with_state(order, true)
        ).is_none(),
        error::invalid_argument(EORDER_ALREADY_EXISTS)
    );
    if (order_req.get_client_order_id().is_some()) {
        self.client_order_ids.add(
            new_account_client_order_id(
                order_req.get_account(),
                order_req.get_client_order_id().destroy_some()
            ),
            order_req.get_order_id()
        );
    };
    price_time_idx.place_maker_order(
        order_req.get_order_id(),
        single_order_type(),
        order_req.get_price(),
        ascending_idx,
        order_req.get_remaining_size(),
        order_req.is_bid()
    );
}

Function reinsert_order

Reinserts a maker order to the order book. This is used when the order is removed from the order book but the clearinghouse fails to settle all or part of the order. If the order doesn’t exist in the order book, it is added to the order book, if it exists, its size is updated.

public(friend) fun reinsert_order<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, reinsert_order: order_match_types::OrderMatchDetails<M>, original_order: &order_match_types::OrderMatchDetails<M>)
Implementation
public(friend) fun reinsert_order<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    reinsert_order: OrderMatchDetails<M>,
    original_order: &OrderMatchDetails<M>
) {
    assert!(
        reinsert_order.validate_single_order_reinsertion_request(original_order),
        E_REINSERT_ORDER_MISMATCH
    );
    let order_id = reinsert_order.get_order_id_from_match_details();
    let unique_idx = reinsert_order.get_unique_priority_idx_from_match_details();

    let reinsert_remaining_size =
        reinsert_order.get_remaining_size_from_match_details();
    let present =
        self.orders.modify_if_present(
            &order_id,
            |order_with_state| {
                order_with_state.increase_remaining_size_from_state(
                    reinsert_remaining_size
                );
            }
        );
    if (!present) {
        return self.place_ready_maker_order_with_unique_idx(
            price_time_idx,
            new_order_request_from_match_details(reinsert_order),
            unique_idx
        );
    };

    price_time_idx.increase_order_size(
        reinsert_order.get_price_from_match_details(),
        unique_idx,
        reinsert_order.get_remaining_size_from_match_details(),
        reinsert_order.is_bid_from_match_details()
    );
}

Function place_pending_order_internal

fun place_pending_order_internal<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, order_req: single_order_types::SingleOrderRequest<M>, ascending_idx: order_book_types::IncreasingIdx)
Implementation
fun place_pending_order_internal<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    order_req: SingleOrderRequest<M>,
    ascending_idx: IncreasingIdx
) {
    let order_id = order_req.get_order_id();
    let order = new_single_order(order_req, ascending_idx);
    self.orders.add(order_id, new_order_with_state(order, false));

    if (order_req.get_client_order_id().is_some()) {
        self.client_order_ids.add(
            new_account_client_order_id(
                order_req.get_account(),
                order_req.get_client_order_id().destroy_some()
            ),
            order_req.get_order_id()
        );
    };

    self.pending_orders.place_pending_order(
        order_id,
        order_req.get_trigger_condition().destroy_some(),
        ascending_idx
    );
}

Function get_single_match_for_taker

Returns a single match for a taker order. It is responsibility of the caller to first call the is_taker_order API to ensure that the order is a taker order before calling this API, otherwise it will abort.

public(friend) fun get_single_match_for_taker<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, active_matched_order: order_match_types::ActiveMatchedOrder): order_match_types::OrderMatch<M>
Implementation
public(friend) fun get_single_match_for_taker<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>, active_matched_order: ActiveMatchedOrder
): OrderMatch<M> {
    let (order_id, matched_size, remaining_size, order_book_type) =
        active_matched_order.destroy_active_matched_order();
    assert!(order_book_type == single_order_type(), ENOT_SINGLE_ORDER_BOOK);

    let order_with_state =
        if (remaining_size == 0) {
            let order = self.orders.remove(&order_id);
            order.set_remaining_size_from_state(0);
            order
        } else {
            self.orders.modify_and_return(
                &order_id,
                |order_with_state| {
                    aptos_trading::single_order_types::set_remaining_size_from_state(
                        order_with_state, remaining_size
                    );
                    // order_with_state.set_remaining_size_from_state(remaining_size);
                    *order_with_state
                }
            )
        };

    let (order, is_active) = order_with_state.destroy_order_from_state();
    assert!(is_active, EINVALID_INACTIVE_ORDER_STATE);

    let (order_request, unique_priority_idx) = order.destroy_single_order();
    let (
        account,
        order_id,
        client_order_id,
        price,
        orig_size,
        size,
        is_bid,
        _trigger_condition,
        time_in_force,
        creation_time_micros,
        metadata
    ) = order_request.destroy_single_order_request();

    if (remaining_size == 0 && client_order_id.is_some()) {
        self.client_order_ids.remove(
            &new_account_client_order_id(
                order.get_order_request().get_account(),
                client_order_id.destroy_some()
            )
        );
    };
    new_order_match(
        new_single_order_match_details(
            order_id,
            account,
            client_order_id,
            unique_priority_idx,
            price,
            orig_size,
            size,
            is_bid,
            time_in_force,
            creation_time_micros,
            metadata
        ),
        matched_size
    )
}

Function decrease_order_size

Decrease the size of the order by the given size delta. The API aborts if the order is not found in the order book or if the size delta is greater than or equal to the remaining size of the order. Please note that the API will abort and not cancel the order if the size delta is equal to the remaining size of the order, to avoid unintended cancellation of the order. Please use the cancel_order API to cancel the order.

order_creator is passed to only verify order cancellation is authorized correctly

public(friend) fun decrease_order_size<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, price_time_idx: &mut price_time_index::PriceTimeIndex, order_creator: address, order_id: order_book_types::OrderId, size_delta: u64)
Implementation
public(friend) fun decrease_order_size<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>,
    price_time_idx: &mut PriceTimeIndex,
    order_creator: address,
    order_id: OrderId,
    size_delta: u64
) {
    let order_opt =
        self.orders.modify_if_present_and_return(
            &order_id,
            |order_with_state| {
                assert!(
                    order_creator
                        == order_with_state.get_order_from_state().get_order_request()
                        .get_account(),
                    EORDER_CREATOR_MISMATCH
                );
                // TODO should we be asserting that remaining size is greater than 0?
                aptos_trading::single_order_types::decrease_remaining_size_from_state(
                    order_with_state, size_delta
                );
                // order_with_state.decrease_remaining_size(size_delta);
                *order_with_state
            }
        );

    assert!(order_opt.is_some(), EORDER_NOT_FOUND);
    let order_with_state = order_opt.destroy_some();

    if (order_with_state.is_active_order()) {
        let order = order_with_state.get_order_from_state();
        price_time_idx.decrease_order_size(
            order.get_order_request().get_price(),
            order_with_state.get_unique_priority_idx_from_state(),
            size_delta,
            order.get_order_request().is_bid()
        );
    };
}

Function get_order_id_by_client_id

public(friend) fun get_order_id_by_client_id<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_creator: address, client_order_id: string::String): option::Option<order_book_types::OrderId>
Implementation
public(friend) fun get_order_id_by_client_id<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_creator: address, client_order_id: String
): Option<OrderId> {
    let account_client_order_id =
        new_account_client_order_id(order_creator, client_order_id);
    self.client_order_ids.get(&account_client_order_id)
}

Function get_order_metadata

public(friend) fun get_order_metadata<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_id: order_book_types::OrderId): option::Option<M>
Implementation
public(friend) fun get_order_metadata<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_id: OrderId
): Option<M> {
    self.orders.get_and_map(&order_id, |order| order.get_metadata_from_state())
}

Function set_order_metadata

public(friend) fun set_order_metadata<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, order_id: order_book_types::OrderId, metadata: M)
Implementation
public(friend) fun set_order_metadata<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>, order_id: OrderId, metadata: M
) {
    let present =
        self.orders.modify_if_present(
            &order_id,
            |order_with_state| {
                order_with_state.set_metadata_in_state(metadata);
            }
        );
    assert!(present, EORDER_NOT_FOUND);
}

Function is_active_order

public(friend) fun is_active_order<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_id: order_book_types::OrderId): bool
Implementation
public(friend) fun is_active_order<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_id: OrderId
): bool {
    self.orders.get_and_map(&order_id, |order| order.is_active_order()).destroy_with_default(
        false
    )
}

Function get_order

public(friend) fun get_order<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_id: order_book_types::OrderId): option::Option<single_order_types::OrderWithState<M>>
Implementation
public(friend) fun get_order<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_id: OrderId
): Option<OrderWithState<M>> {
    self.orders.get(&order_id)
}

Function get_remaining_size

public(friend) fun get_remaining_size<M: copy, drop, store>(self: &single_order_book::SingleOrderBook<M>, order_id: order_book_types::OrderId): u64
Implementation
public(friend) fun get_remaining_size<M: store + copy + drop>(
    self: &SingleOrderBook<M>, order_id: OrderId
): u64 {
    self.orders.get_and_map(
        &order_id, |order| order.get_remaining_size_from_state()
    ).destroy_with_default(0)
}

Function take_ready_price_based_orders

Removes and returns the orders that are ready to be executed based on the current price.

public(friend) fun take_ready_price_based_orders<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, current_price: u64, order_limit: u64): vector<single_order_types::SingleOrder<M>>
Implementation
public(friend) fun take_ready_price_based_orders<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>, current_price: u64, order_limit: u64
): vector<SingleOrder<M>> {
    let self_orders = &mut self.orders;
    let self_client_order_ids = &mut self.client_order_ids;
    let order_ids =
        self.pending_orders.take_ready_price_based_orders(
            current_price, order_limit
        );
    let orders = vector::empty();

    order_ids.for_each(
        |order_id| {
            let order_with_state = self_orders.remove(&order_id);
            let (order, _) = order_with_state.destroy_order_from_state();
            let client_order_id = order.get_order_request().get_client_order_id();
            if (client_order_id.is_some()) {
                self_client_order_ids.remove(
                    &new_account_client_order_id(
                        order.get_order_request().get_account(),
                        client_order_id.destroy_some()
                    )
                );
            };
            orders.push_back(order);
        }
    );
    orders
}

Function take_ready_time_based_orders

Removes and returns the orders that are ready to be executed based on the time condition.

public(friend) fun take_ready_time_based_orders<M: copy, drop, store>(self: &mut single_order_book::SingleOrderBook<M>, order_limit: u64): vector<single_order_types::SingleOrder<M>>
Implementation
public(friend) fun take_ready_time_based_orders<M: store + copy + drop>(
    self: &mut SingleOrderBook<M>, order_limit: u64
): vector<SingleOrder<M>> {
    let self_orders = &mut self.orders;
    let self_client_order_ids = &mut self.client_order_ids;
    let order_ids = self.pending_orders.take_ready_time_based_orders(order_limit);
    let orders = vector::empty();

    order_ids.for_each(
        |order_id| {
            let order_with_state = self_orders.remove(&order_id);
            let (order, _) = order_with_state.destroy_order_from_state();
            let client_order_id = order.get_order_request().get_client_order_id();
            if (client_order_id.is_some()) {
                self_client_order_ids.remove(
                    &new_account_client_order_id(
                        order.get_order_request().get_account(),
                        client_order_id.destroy_some()
                    )
                );
            };
            orders.push_back(order);
        }
    );
    orders
}

Function __lambda__1__get_single_match_for_taker

fun __lambda__1__get_single_match_for_taker<M: copy, drop, store>(remaining_size: u64, v: &mut single_order_types::OrderWithState<M>): single_order_types::OrderWithState<M>
Implementation
|v| f(v)

Function __lambda__1__decrease_order_size

fun __lambda__1__decrease_order_size<M: copy, drop, store>(order_creator: address, size_delta: u64, v: &mut single_order_types::OrderWithState<M>): single_order_types::OrderWithState<M>
Implementation
|v| modify_f(v)

Function __lambda__1__reinsert_order

fun __lambda__1__reinsert_order<M: copy, drop, store>(reinsert_remaining_size: u64, v: &mut single_order_types::OrderWithState<M>): bool
Implementation
|v| modify_f(v)

Function __lambda__1__set_order_metadata

fun __lambda__1__set_order_metadata<M: copy, drop, store>(metadata: M, v: &mut single_order_types::OrderWithState<M>): bool
Implementation
|v| modify_f(v)