rust_order_book/
builder.rs1use crate::{
15    journal::{JournalLog, Snapshot},
16    OrderBook, OrderBookOptions,
17};
18
19pub struct OrderBookBuilder {
24    symbol: String,
25    options: OrderBookOptions,
26}
27
28impl OrderBookBuilder {
29    pub fn new(symbol: impl Into<String>) -> Self {
34        Self { symbol: symbol.into(), options: OrderBookOptions::default() }
35    }
36
37    pub fn with_options(mut self, options: OrderBookOptions) -> Self {
41        self.options = options;
42        self
43    }
44
45    pub fn with_snapshot(mut self, snapshot: Snapshot) -> Self {
56        self.options.snapshot = Some(snapshot);
57        self
58    }
59
60    pub fn with_replay_logs(mut self, logs: Vec<JournalLog>) -> Self {
72        self.options.replay_logs = Some(logs);
73        self
74    }
75
76    pub fn with_journaling(mut self, enabled: bool) -> Self {
81        self.options.journaling = enabled;
82        self
83    }
84
85    pub fn build(self) -> OrderBook {
90        let mut ob = OrderBook::new(self.symbol.as_str(), self.options.clone());
91        if let Some(snapshot) = &self.options.snapshot {
92            ob.restore_snapshot(snapshot.clone());
93        }
94
95        if let Some(logs) = self.options.replay_logs {
96            ob.replay_logs(logs).unwrap(); }
98
99        ob
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use std::collections::{BTreeMap, HashMap};
106
107    use crate::{
108        enums::{JournalOp, OrderOptions},
109        utils::current_timestamp_millis,
110        LimitOrderOptions, MarketOrderOptions, Side,
111    };
112
113    use super::*;
114
115    #[test]
116    fn test_builder_with_defaults() {
117        let ob = OrderBookBuilder::new("BTCUSD").build();
118        assert_eq!(ob.symbol(), "BTCUSD");
119        assert!(!ob.journaling);
120    }
121
122    #[test]
123    fn test_builder_with_journaling_enabled() {
124        let ob = OrderBookBuilder::new("ETHUSD").with_journaling(true).build();
125
126        assert_eq!(ob.symbol(), "ETHUSD");
127        assert!(ob.journaling);
128    }
129
130    #[test]
131    fn test_builder_with_options_struct() {
132        let opts = OrderBookOptions { journaling: true, ..Default::default() };
133
134        let ob = OrderBookBuilder::new("DOGEUSD").with_options(opts.clone()).build();
135
136        assert_eq!(ob.symbol(), "DOGEUSD");
137        assert_eq!(ob.journaling, opts.journaling);
138    }
139
140    #[test]
141    fn test_builder_with_snapshot() {
142        let snap = Snapshot {
144            orders: HashMap::new(),
145            bids: BTreeMap::new(),
146            asks: BTreeMap::new(),
147            last_op: 42,
148            next_order_id: 100,
149            ts: current_timestamp_millis(),
150        };
151
152        let book = OrderBookBuilder::new("BTCUSD").with_snapshot(snap).build();
153
154        assert_eq!(book.last_op, 42);
155        assert_eq!(book.next_order_id, 100);
156        assert_eq!(book.orders.len(), 0);
157    }
158
159    #[test]
160    fn test_builder_with_replay_logs() {
161        let logs = vec![
163            JournalLog {
164                op_id: 1,
165                ts: 123457,
166                op: JournalOp::Limit,
167                o: OrderOptions::Limit(LimitOrderOptions {
168                    quantity: 10,
169                    price: 1100,
170                    side: Side::Sell,
171                    post_only: None,
172                    time_in_force: None,
173                }),
174            },
175            JournalLog {
176                op_id: 2,
177                ts: 123457,
178                op: JournalOp::Limit,
179                o: OrderOptions::Limit(LimitOrderOptions {
180                    quantity: 10,
181                    price: 1000,
182                    side: Side::Buy,
183                    post_only: None,
184                    time_in_force: None,
185                }),
186            },
187            JournalLog {
188                op_id: 3,
189                ts: 123456,
190                op: JournalOp::Market,
191                o: OrderOptions::Market(MarketOrderOptions { quantity: 5, side: Side::Buy }),
192            },
193        ];
194
195        let ob = OrderBookBuilder::new("BTCUSD").with_replay_logs(logs.clone()).build();
197
198        assert_eq!(ob.orders.len(), 2);
200
201        assert_eq!(ob.get_order(0).unwrap().remaining_qty, 5);
203        assert_eq!(ob.get_order(1).unwrap().remaining_qty, 10);
204    }
205}