nautilus_model/orderbook/
display.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Functions related to order book display.
17
18use tabled::{settings::Style, Table, Tabled};
19
20use super::{BookLevel, BookPrice};
21use crate::orderbook::ladder::BookLadder;
22
23#[derive(Tabled)]
24struct BookLevelDisplay {
25    bids: String,
26    price: String,
27    asks: String,
28}
29
30/// Return a [`String`] representation of the order book in a human-readable table format.
31#[must_use]
32pub(crate) fn pprint_book(bids: &BookLadder, asks: &BookLadder, num_levels: usize) -> String {
33    let ask_levels: Vec<(&BookPrice, &BookLevel)> =
34        asks.levels.iter().take(num_levels).rev().collect();
35    let bid_levels: Vec<(&BookPrice, &BookLevel)> = bids.levels.iter().take(num_levels).collect();
36    let levels: Vec<(&BookPrice, &BookLevel)> = ask_levels.into_iter().chain(bid_levels).collect();
37
38    let data: Vec<BookLevelDisplay> = levels
39        .iter()
40        .map(|(book_price, level)| {
41            let is_bid_level = bids.levels.contains_key(book_price);
42            let is_ask_level = asks.levels.contains_key(book_price);
43
44            let bid_sizes: Vec<String> = level
45                .orders
46                .iter()
47                .filter(|_| is_bid_level)
48                .map(|order| format!("{}", order.1.size))
49                .collect();
50
51            let ask_sizes: Vec<String> = level
52                .orders
53                .iter()
54                .filter(|_| is_ask_level)
55                .map(|order| format!("{}", order.1.size))
56                .collect();
57
58            BookLevelDisplay {
59                bids: if bid_sizes.is_empty() {
60                    String::new()
61                } else {
62                    format!("[{}]", bid_sizes.join(", "))
63                },
64                price: format!("{}", level.price),
65                asks: if ask_sizes.is_empty() {
66                    String::new()
67                } else {
68                    format!("[{}]", ask_sizes.join(", "))
69                },
70            }
71        })
72        .collect();
73
74    Table::new(data).with(Style::rounded()).to_string()
75}