Skip to main content

wmjtyd_libstock/data/
orderbook.rs

1//! The orderbook-related operations.
2
3use std::{
4    collections::HashMap,
5    io::{BufRead, BufReader},
6};
7
8use crypto_msg_parser::{Order, OrderBookMsg};
9use rust_decimal::prelude::ToPrimitive;
10
11use super::{
12    fields::{
13        ExchangeTimestampRepr, ExchangeTypeRepr, InfoTypeRepr, MarketTypeRepr, MessageTypeRepr,
14        ReadExt, ReceivedTimestampRepr, StructureError, SymbolPairRepr,
15    },
16    hex::{HexDataError, NumToBytesExt},
17    order::{get_orders, OrderType},
18    types::InfoType,
19};
20
21pub fn generate_diff(old: &OrderBookMsg, latest: &OrderBookMsg) -> OrderBookMsg {
22    let asks = get_orders(&old.asks, &latest.asks, OrderType::Ask);
23    let bids = get_orders(&old.bids, &latest.bids, OrderType::Bid);
24
25    OrderBookMsg {
26        asks,
27        bids,
28        exchange: latest.exchange.clone(),
29        market_type: latest.market_type,
30        symbol: latest.symbol.clone(),
31        pair: latest.pair.clone(),
32        msg_type: latest.msg_type,
33        timestamp: latest.timestamp,
34        snapshot: latest.snapshot,
35        seq_id: latest.seq_id,
36        prev_seq_id: latest.prev_seq_id,
37        json: latest.json.clone(),
38    }
39}
40
41/// Encode a [`OrderBookMsg`] to bytes.
42pub fn encode_orderbook(orderbook: &OrderBookMsg) -> OrderbookResult<Vec<u8>> {
43    // This data should have "at least" 21 bytes.
44    let mut bytes = Vec::<u8>::with_capacity(21);
45
46    // 1. 交易所时间戳: 6 字节
47    bytes.extend_from_slice(&ExchangeTimestampRepr(orderbook.timestamp).to_bytes());
48
49    // 2. 收到时间戳: 6 字节
50    bytes.extend_from_slice(&ReceivedTimestampRepr::try_new_from_now()?.to_bytes());
51
52    // 3. EXCHANGE: 1 字节
53    bytes.extend_from_slice(&ExchangeTypeRepr::try_from_str(&orderbook.exchange)?.to_bytes());
54
55    // 4. MARKET_TYPE: 1 字节信息标识
56    bytes.extend_from_slice(&MarketTypeRepr(orderbook.market_type).to_bytes());
57
58    // 5. MESSAGE_TYPE: 1 字节信息标识
59    bytes.extend_from_slice(&MessageTypeRepr(orderbook.msg_type).to_bytes());
60
61    // 6. SYMBOL: 2 字节信息标识
62    bytes.extend_from_slice(&SymbolPairRepr::from_pair(&orderbook.pair).to_bytes());
63
64    // 7. ask & bid
65    {
66        let markets = {
67            let mut markets = HashMap::new();
68
69            markets.insert("asks", &orderbook.asks);
70            markets.insert("bids", &orderbook.bids);
71
72            markets
73        };
74
75        for (k, order_list) in markets {
76            // 7-1. 字节信息标识
77            bytes.extend_from_slice(&{ InfoTypeRepr::try_from_str(k)?.to_bytes() });
78
79            // 7-2. 字节信息体的长度
80            bytes.extend_from_slice(&{
81                let list_len = (order_list.len() * 10) as u16;
82                list_len.to_be_bytes()
83            });
84
85            // 7-3: data(price(5)、quant(5)) 10*dataLen BYTE[10*dataLen] 信息体
86            for order in order_list {
87                bytes.extend_from_slice(&u32::encode_bytes(&order.price.to_string())?);
88                bytes.extend_from_slice(&u32::encode_bytes(&order.quantity_base.to_string())?);
89            }
90        }
91    }
92
93    // let compressed = compress_to_vec(&bytes, 6);
94    // println!("compressed from {} to {}", data.len(), compressed.len());
95    Ok(bytes)
96}
97
98/// Decode the specified bytes to a [`OrderBookMsg`].
99pub fn decode_orderbook(payload: &[u8]) -> OrderbookResult<OrderBookMsg> {
100    let mut reader = BufReader::new(payload);
101
102    // 1. 交易所时间戳: 6 字节时间戳
103    let exchange_timestamp = ExchangeTimestampRepr::try_from_reader(&mut reader)?.0;
104
105    // 2. 收到时间戳: 6 字节时间戳 (NOT USED)
106    reader.consume(8);
107    // let received_timestamp = ReceivedTimestampRepr::try_from_reader(&mut reader)?;
108
109    // 3. EXCHANGE: 1 字节信息标识
110    let exchange_type = ExchangeTypeRepr::try_from_reader(&mut reader)?.0;
111
112    // 4. MARKET_TYPE: 1 字节信息标识
113    let market_type = MarketTypeRepr::try_from_reader(&mut reader)?.0;
114
115    // 5. MESSAGE_TYPE: 1 字节信息标识
116    let msg_type = MessageTypeRepr::try_from_reader(&mut reader)?.0;
117
118    // 6. SYMBOL_PAIR: 2 字节信息标识
119    let SymbolPairRepr(symbol, pair) = SymbolPairRepr::try_from_reader(&mut reader)?;
120
121    // 7. ask & bid
122    let (asks, bids) = {
123        let mut asks: Vec<Order> = Vec::new();
124        let mut bids: Vec<Order> = Vec::new();
125
126        // Check if the data has left.
127        //
128        // TODO: when `has_data_left` provided, replace the following
129        // to 'reader.has_data_left()`!
130        while reader.fill_buf().map(|b| !b.is_empty()).unwrap_or(false) {
131            // 7-1. 字节信息标识
132            let info_type = InfoTypeRepr::try_from_reader(&mut reader)?.0;
133
134            // 7-2. 字节信息体的长度
135            let info_len = {
136                let data = reader.read_exact_array()?;
137                let info_len_raw = u16::from_be_bytes(data);
138                info_len_raw / 10 // 每 10 bits 為一個資料單位
139            };
140
141            // 7-3: data(price(5)、quant(5)) 10*dataLen BYTE[10*dataLen] 信息体
142            for _ in 0..info_len {
143                let price = {
144                    let raw_bytes = reader.read_exact_array()?;
145                    u32::decode_bytes(&raw_bytes).to_f64().ok_or_else(|| {
146                        OrderbookError::DecimalConvertF64Failed(raw_bytes.to_vec())
147                    })?
148                };
149
150                let quantity_base = {
151                    let raw_bytes = reader.read_exact_array()?;
152                    u32::decode_bytes(&raw_bytes).to_f64().ok_or_else(|| {
153                        OrderbookError::DecimalConvertF64Failed(raw_bytes.to_vec())
154                    })?
155                };
156
157                let order = Order {
158                    price,
159                    quantity_base,
160                    quantity_quote: 0.0,
161                    quantity_contract: None,
162                };
163
164                match info_type {
165                    InfoType::Asks => asks.push(order),
166                    InfoType::Bids => bids.push(order),
167                }
168            }
169        }
170
171        (asks, bids)
172    };
173
174    Ok(OrderBookMsg {
175        exchange: exchange_type.to_string(),
176        market_type,
177        symbol: symbol.to_string(),
178        pair: pair.to_string(),
179        msg_type,
180        timestamp: exchange_timestamp,
181        seq_id: None,
182        prev_seq_id: None,
183        asks,
184        bids,
185        snapshot: true,
186        json: String::new(),
187    })
188}
189
190#[derive(thiserror::Error, Debug)]
191pub enum OrderbookError {
192    #[error("data/hex error: {0}")]
193    HexDataError(#[from] HexDataError),
194
195    #[error("structure error: {0}")]
196    StructureError(#[from] StructureError),
197
198    #[error("failed to convert the following bytes to f64: {0:?}")]
199    DecimalConvertF64Failed(Vec<u8>),
200}
201
202pub type OrderbookResult<T> = Result<T, OrderbookError>;