rusty_feeder/exchange/upbit/data/
orderbook.rs1use rust_decimal::Decimal;
7use serde::{Deserialize, Serialize};
8use smallvec::SmallVec;
9use smartstring::alias::String;
10
11pub const MAX_ORDERBOOK_LEVELS: usize = 15;
14
15#[derive(Debug, Clone, Deserialize, Serialize)]
17#[repr(align(64))] pub struct OrderbookMessage {
19 #[serde(rename = "type")]
21 pub message_type: String,
22
23 pub code: String,
25
26 pub total_ask_size: Decimal,
28
29 pub total_bid_size: Decimal,
31
32 pub orderbook_units: SmallVec<[OrderbookUnit; MAX_ORDERBOOK_LEVELS]>,
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 ParsedOrderbookData {
62 pub code: String,
64
65 pub asks: SmallVec<[(Decimal, Decimal); MAX_ORDERBOOK_LEVELS]>,
67
68 pub bids: SmallVec<[(Decimal, Decimal); MAX_ORDERBOOK_LEVELS]>,
70
71 pub sequence: u64,
73
74 pub timestamp_ns: u64,
76}
77
78impl ParsedOrderbookData {
79 #[inline(always)]
81 #[must_use]
82 pub fn from_message(msg: &OrderbookMessage) -> Self {
83 let mut asks = SmallVec::with_capacity(MAX_ORDERBOOK_LEVELS);
84 let mut bids = SmallVec::with_capacity(MAX_ORDERBOOK_LEVELS);
85
86 for (i, unit) in msg.orderbook_units.iter().enumerate() {
88 if i >= MAX_ORDERBOOK_LEVELS {
90 break;
91 }
92
93 let ask_price = unit.ask_price;
95 let ask_size = unit.ask_size;
96
97 let ask_pos = match asks
100 .binary_search_by(|existing: &(Decimal, Decimal)| existing.0.cmp(&ask_price))
101 {
102 Ok(pos) => pos, Err(pos) => pos, };
105 asks.insert(ask_pos, (ask_price, ask_size));
106
107 let bid_price = unit.bid_price;
109 let bid_size = unit.bid_size;
110
111 let bid_pos = match bids.binary_search_by(|existing: &(Decimal, Decimal)| {
114 match existing.0.cmp(&bid_price) {
116 std::cmp::Ordering::Less => std::cmp::Ordering::Greater,
117 std::cmp::Ordering::Greater => std::cmp::Ordering::Less,
118 std::cmp::Ordering::Equal => std::cmp::Ordering::Equal,
119 }
120 }) {
121 Ok(pos) => pos, Err(pos) => pos, };
124 bids.insert(bid_pos, (bid_price, bid_size));
125 }
126
127 Self {
128 code: msg.code.clone(),
129 asks,
130 bids,
131 sequence: msg.timestamp, timestamp_ns: msg.timestamp * 1_000_000, }
134 }
135
136 #[inline(always)]
138 pub fn best_ask(&self) -> Option<Decimal> {
139 self.asks.first().map(|ask| ask.0)
140 }
141
142 #[inline(always)]
144 pub fn best_bid(&self) -> Option<Decimal> {
145 self.bids.first().map(|bid| bid.0)
146 }
147
148 #[inline(always)]
150 pub fn spread(&self) -> Option<Decimal> {
151 match (self.best_ask(), self.best_bid()) {
152 (Some(ask), Some(bid)) => Some(ask - bid),
153 _ => None,
154 }
155 }
156
157 #[inline(always)]
159 pub fn mid_price(&self) -> Option<Decimal> {
160 match (self.best_ask(), self.best_bid()) {
161 (Some(ask), Some(bid)) => Some((ask + bid) / Decimal::from(2)),
162 _ => None,
163 }
164 }
165}