rusty_feeder/exchange/bithumb/data/
orderbook.rs1use rust_decimal::Decimal;
7use rusty_model::data::orderbook::PriceLevel;
8use serde::{Deserialize, Serialize};
9use smallvec::SmallVec;
10use smartstring::alias::String;
11
12#[derive(Debug, Clone, Deserialize, Serialize)]
14#[repr(align(64))] pub struct OrderbookMessage {
16 #[serde(rename = "type")]
18 pub message_type: String,
19
20 pub code: String,
22
23 pub total_ask_size: Decimal,
25
26 pub total_bid_size: Decimal,
28
29 pub orderbook_units: SmallVec<[OrderbookUnit; 32]>,
31
32 pub level: f64,
34
35 pub timestamp: u64,
37
38 pub stream_type: String,
40}
41
42#[derive(Debug, Clone, Deserialize, Serialize)]
44pub struct OrderbookUnit {
45 pub ask_price: Decimal,
47
48 pub bid_price: Decimal,
50
51 pub ask_size: Decimal,
53
54 pub bid_size: Decimal,
56}
57
58#[derive(Debug, Clone)]
60#[repr(align(64))] pub struct Orderbook {
62 pub code: String,
64
65 pub timestamp_ns: u64,
67
68 pub local_time_ns: u64,
70
71 pub best_ask_price: Decimal,
73
74 pub best_bid_price: Decimal,
76
77 pub asks: SmallVec<[PriceLevel; 20]>,
79
80 pub bids: SmallVec<[PriceLevel; 20]>,
82}
83
84impl Orderbook {
85 #[inline(always)]
87 #[must_use]
88 pub fn from_orderbook_message(msg: &OrderbookMessage, local_time_ns: u64) -> Self {
89 let timestamp_ns = msg.timestamp * 1_000_000;
91
92 let capacity = msg.orderbook_units.len();
94 let mut asks = SmallVec::with_capacity(capacity);
95 let mut bids = SmallVec::with_capacity(capacity);
96
97 for unit in &msg.orderbook_units {
99 let ask_entry = PriceLevel {
101 price: unit.ask_price,
102 quantity: unit.ask_size,
103 };
104
105 let ask_pos = match asks
108 .binary_search_by(|existing: &PriceLevel| existing.price.cmp(&ask_entry.price))
109 {
110 Ok(pos) => pos, Err(pos) => pos, };
113 asks.insert(ask_pos, ask_entry);
114
115 let bid_entry = PriceLevel {
117 price: unit.bid_price,
118 quantity: unit.bid_size,
119 };
120
121 let bid_pos = match bids.binary_search_by(|existing: &PriceLevel| {
124 existing.price.cmp(&bid_entry.price).reverse()
125 }) {
126 Ok(pos) => pos, Err(pos) => pos, };
129 bids.insert(bid_pos, bid_entry);
130 }
131
132 let best_ask_price = asks.first().map_or(Decimal::ZERO, |entry| entry.price);
134 let best_bid_price = bids.first().map_or(Decimal::ZERO, |entry| entry.price);
135
136 Self {
137 code: msg.code.clone(),
138 timestamp_ns,
139 local_time_ns,
140 best_ask_price,
141 best_bid_price,
142 asks,
143 bids,
144 }
145 }
146
147 #[inline(always)]
149 pub fn spread(&self) -> Decimal {
150 if self.best_ask_price.is_zero() || self.best_bid_price.is_zero() {
151 return Decimal::ZERO;
152 }
153 self.best_ask_price - self.best_bid_price
154 }
155
156 #[inline(always)]
158 pub fn mid_price(&self) -> Decimal {
159 if self.best_ask_price.is_zero() || self.best_bid_price.is_zero() {
160 return Decimal::ZERO;
161 }
162 (self.best_ask_price + self.best_bid_price) / Decimal::TWO
163 }
164}