Skip to main content

o2_tools/market_data/
order_book.rs

1use crate::{
2    helpers::get_total_amount,
3    market_data::OrderData,
4    order_book::Side,
5};
6use blart::{
7    AsBytes,
8    NoPrefixesBytes,
9    TreeMap,
10};
11use fuels::{
12    tx::TxId,
13    types::{
14        AssetId,
15        Identity,
16    },
17};
18use indexmap::IndexMap;
19use slotmap::{
20    DefaultKey,
21    SlotMap,
22};
23use std::fmt::Debug;
24
25pub type Price = u64;
26pub type Quantity = u64;
27pub type OrderId = DefaultKey;
28pub type Balances = IndexMap<Identity, (u64, u64)>;
29
30/// big endian representation of price for use as radix key
31#[derive(Debug, Clone)]
32struct PriceKey([u8; 8]);
33
34impl From<Price> for PriceKey {
35    fn from(value: Price) -> Self {
36        Self(value.to_be_bytes())
37    }
38}
39
40impl From<&Price> for PriceKey {
41    fn from(value: &Price) -> Self {
42        Self(value.to_be_bytes())
43    }
44}
45
46impl From<&mut Price> for PriceKey {
47    fn from(value: &mut Price) -> Self {
48        Self(value.to_be_bytes())
49    }
50}
51
52impl From<PriceKey> for Price {
53    fn from(value: PriceKey) -> Self {
54        u64::from_be_bytes(value.0)
55    }
56}
57
58impl From<&PriceKey> for Price {
59    fn from(value: &PriceKey) -> Self {
60        u64::from_be_bytes(value.0)
61    }
62}
63
64impl From<&mut PriceKey> for Price {
65    fn from(value: &mut PriceKey) -> Self {
66        u64::from_be_bytes(value.0)
67    }
68}
69
70impl AsBytes for PriceKey {
71    fn as_bytes(&self) -> &[u8] {
72        &self.0
73    }
74}
75
76unsafe impl NoPrefixesBytes for PriceKey {}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct Fill {
80    pub order_id: u64,
81    pub quantity: u64,
82    pub price: u64,
83}
84
85#[derive(Debug)]
86pub struct OrderParams {
87    pub side: Side,
88    pub price: u64,
89    pub quantity: u64,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct Order {
94    pub trader_id: Identity,
95    pub order_id: u64,
96    pub side: Side,
97    pub price: u64,
98    pub quantity: Quantity,
99    pub fill: Vec<Fill>,
100    pub tx_id: Option<TxId>,
101}
102
103impl Order {
104    pub fn new(
105        order_id: u64,
106        OrderData {
107            trader_id,
108            side,
109            price,
110            quantity,
111        }: OrderData,
112    ) -> Self {
113        Self {
114            order_id,
115            trader_id,
116            price,
117            quantity,
118            side,
119            fill: vec![],
120            tx_id: None,
121        }
122    }
123
124    pub fn fill(&mut self, order_id: u64, fill_quantity: Quantity, price: Price) -> bool {
125        let filled_order = Fill {
126            quantity: fill_quantity,
127            price,
128            order_id,
129        };
130        self.fill.push(filled_order);
131        self.quantity -= fill_quantity;
132        self.quantity == 0
133    }
134
135    pub fn total(&self, decimals: u64) -> u64 {
136        get_total_amount(self.quantity, self.price, decimals)
137    }
138
139    pub fn filled_total(&self, decimals: u64) -> u64 {
140        self.fill
141            .iter()
142            .map(|f| get_total_amount(f.quantity, f.price, decimals))
143            .sum()
144    }
145
146    pub fn remaining_total(&self, decimals: u64) -> Quantity {
147        match self.side {
148            Side::Buy => {
149                get_total_amount(self.total_quantity(), self.price, decimals)
150                    - self.filled_total(decimals)
151            }
152            Side::Sell => 0,
153        }
154    }
155
156    pub fn total_quantity(&self) -> Quantity {
157        self.quantity + self.filled_quantity()
158    }
159
160    pub fn filled_quantity(&self) -> Quantity {
161        self.fill.iter().map(|q| q.quantity).sum::<Quantity>()
162    }
163
164    pub fn filled_price(&self) -> Price {
165        let total_price: Price = self.fill.iter().map(|q| q.price).sum();
166        let count = self.fill.len();
167
168        if count == 0 {
169            0
170        } else {
171            total_price / count as Price
172        }
173    }
174
175    pub fn is_closed(&self) -> bool {
176        self.quantity == 0
177    }
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct MatchedEvent {
182    pub direction: Side,
183    pub taker_id: u64,
184    pub maker_id: u64,
185    pub quantity: u64,
186    pub price: u64,
187    pub total: u64,
188}
189
190#[derive(Clone, Default)]
191pub struct BookConfig {
192    pub base_asset: AssetId,
193    pub base_decimals: u64,
194    pub quote_asset: AssetId,
195    pub quote_decimals: u64,
196    pub taker_fee: u64,
197    pub maker_fee: u64,
198}
199
200#[derive(Clone)]
201pub struct Book {
202    pub order_id: u64,
203    pub base_asset: AssetId,
204    pub base_decimals: u64,
205    pub quote_asset: AssetId,
206    pub quote_decimals: u64,
207    pub taker_fee: u64,
208    pub maker_fee: u64,
209    pub all_orders: Vec<Order>,
210    pub trades: Vec<MatchedEvent>,
211    pub cancels: Vec<Order>,
212    pub balances: Balances,
213    buys: TreeMap<PriceKey, Vec<OrderId>>,
214    sells: TreeMap<PriceKey, Vec<OrderId>>,
215    orders: SlotMap<OrderId, Order>,
216}
217
218impl Book {
219    pub fn new(config: BookConfig) -> Self {
220        Self {
221            order_id: 0,
222            base_asset: config.base_asset,
223            base_decimals: config.base_decimals,
224            quote_asset: config.quote_asset,
225            quote_decimals: config.quote_decimals,
226            taker_fee: config.taker_fee,
227            maker_fee: config.taker_fee,
228            buys: TreeMap::new(),
229            sells: TreeMap::new(),
230            orders: SlotMap::new(),
231            trades: Vec::new(),
232            cancels: Vec::new(),
233            all_orders: Vec::new(),
234            balances: Balances::new(),
235        }
236    }
237
238    pub fn subtract_fee(&self, is_maker: bool, amount: u64) -> u64 {
239        let fee: u64 = if is_maker {
240            self.maker_fee
241        } else {
242            self.taker_fee
243        };
244        if fee == 0 {
245            return amount;
246        }
247        amount - ((amount * fee) / 1_000_000)
248    }
249
250    pub fn subtract_taker_fee(&self, amount: u64) -> u64 {
251        self.subtract_fee(false, amount)
252    }
253
254    pub fn subtract_maker_fee(&self, amount: u64) -> u64 {
255        self.subtract_fee(true, amount)
256    }
257
258    pub fn set_address_balance(
259        &mut self,
260        trader_id: &Identity,
261        base_balance: u64,
262        quote_balance: u64,
263    ) {
264        self.balances
265            .insert(*trader_id, (base_balance, quote_balance));
266    }
267
268    pub fn set_balances(&mut self, balances: Balances) {
269        self.balances = balances.clone();
270    }
271
272    pub fn change_balance_insert(&mut self, order: &Order) {
273        if let Some(balance) = self.balances.get_mut(&order.trader_id) {
274            if order.side == Side::Buy {
275                balance.1 -= get_total_amount(
276                    order.total_quantity(),
277                    order.price,
278                    self.base_decimals,
279                );
280            } else {
281                balance.0 -= order.total_quantity();
282            };
283        }
284    }
285
286    pub fn change_balance_cancel(&mut self, order: &Order) {
287        if let Some(balance) = self.balances.get_mut(&order.trader_id) {
288            if order.side == Side::Buy {
289                balance.1 +=
290                    get_total_amount(order.quantity, order.price, self.base_decimals);
291            } else {
292                balance.0 += order.quantity;
293            };
294        }
295    }
296
297    pub fn change_balance_match(
298        &mut self,
299        maker: &Order,
300        taker: &Order,
301        fill_quantity: u64,
302    ) {
303        let maker_balances = self.balances.get(&maker.trader_id);
304        let taker_balances = self.balances.get(&taker.trader_id);
305
306        if maker_balances.is_none() || taker_balances.is_none() {
307            return;
308        }
309        let mut maker_balances = maker_balances.cloned().unwrap();
310        let mut taker_balances = taker_balances.cloned().unwrap();
311
312        if maker.side == Side::Buy {
313            maker_balances.0 += self.subtract_maker_fee(fill_quantity);
314            taker_balances.1 += self.subtract_taker_fee(get_total_amount(
315                fill_quantity,
316                maker.price,
317                self.base_decimals,
318            ));
319        } else {
320            taker_balances.0 += self.subtract_taker_fee(fill_quantity);
321            maker_balances.1 += self.subtract_maker_fee(get_total_amount(
322                fill_quantity,
323                maker.price,
324                self.base_decimals,
325            ));
326        }
327
328        if taker.is_closed() && taker.remaining_total(self.base_decimals) > 0 {
329            taker_balances.1 += taker.remaining_total(self.base_decimals);
330        }
331        if maker.is_closed() && maker.remaining_total(self.base_decimals) > 0 {
332            taker_balances.1 += taker.remaining_total(self.base_decimals);
333        }
334
335        self.balances.insert(maker.trader_id, maker_balances);
336        self.balances.insert(taker.trader_id, taker_balances);
337    }
338
339    pub fn load(&mut self, orders: Vec<Order>) {
340        for order in orders {
341            if order.quantity == 0 {
342                continue;
343            }
344            self.insert_order(&order);
345        }
346    }
347
348    pub fn get_buys_count(&mut self) -> usize {
349        self.buys.len()
350    }
351
352    pub fn get_sells_count(&mut self) -> usize {
353        self.sells.len()
354    }
355
356    fn insert_order(&mut self, order: &Order) -> OrderId {
357        let price = order.price;
358        let id = self.orders.insert(order.clone());
359        self.change_balance_insert(order);
360
361        match order.side {
362            Side::Sell => {
363                self.sells.entry(price.into()).or_default().push(id);
364            }
365            Side::Buy => {
366                self.buys.entry(price.into()).or_default().push(id);
367            }
368        }
369        self.execute();
370        id
371    }
372
373    pub fn get_orders(&self) -> Vec<Order> {
374        self.orders.values().cloned().collect()
375    }
376
377    pub fn cancel(&mut self, order_id: u64) {
378        if let Some((order_key, order)) =
379            self.orders.iter().find(|o| o.1.order_id == order_id)
380        {
381            self.cancels.push(order.clone());
382            self.change_balance_cancel(&order.clone());
383            self.remove_order(order_key);
384        }
385    }
386
387    fn remove_order(&mut self, order_id: OrderId) {
388        let order = &self.orders[order_id];
389        match order.side {
390            Side::Sell => {
391                if let Some(ref_vec) = self.sells.get_mut(&order.price.into()) {
392                    ref_vec.retain(|id| *id != order_id);
393                    if ref_vec.is_empty() {
394                        self.sells.remove(&order.price.into());
395                    }
396                }
397            }
398            Side::Buy => {
399                if let Some(ref_vec) = self.buys.get_mut(&order.price.into()) {
400                    ref_vec.retain(|id| *id != order_id);
401                    if ref_vec.is_empty() {
402                        self.buys.remove(&order.price.into());
403                    }
404                }
405            }
406        }
407        self.orders.remove(order_id);
408    }
409
410    fn lowest_sell(&self) -> Option<(&PriceKey, &OrderId)> {
411        match self.sells.first_key_value().map(|(k, v)| (k, v.first())) {
412            Some((k, Some(v))) => Some((k, v)),
413            _ => None,
414        }
415    }
416
417    fn highest_buy(&self) -> Option<(&PriceKey, &OrderId)> {
418        match self.buys.last_key_value().map(|(k, v)| (k, v.first())) {
419            Some((k, Some(v))) => Some((k, v)),
420            _ => None,
421        }
422    }
423
424    pub fn create_order_matched_event(&self, maker_order: &Order) -> MatchedEvent {
425        let last_filled_order = maker_order.fill.last().unwrap();
426        let total = get_total_amount(
427            last_filled_order.quantity,
428            last_filled_order.price,
429            self.base_decimals,
430        );
431        MatchedEvent {
432            direction: maker_order.side,
433            taker_id: last_filled_order.order_id,
434            maker_id: maker_order.order_id,
435            quantity: last_filled_order.quantity,
436            price: last_filled_order.price,
437            total,
438        }
439    }
440
441    pub fn execute(&mut self) {
442        while self
443            .lowest_sell()
444            .zip(self.highest_buy())
445            .map(|((sell, _), (buy, _))| Price::from(buy) >= Price::from(sell))
446            .unwrap_or(false)
447        {
448            let sell_id = *self.lowest_sell().unwrap().1;
449            let sell_quantity = self.orders[sell_id].quantity;
450            let sell_order_id = self.orders[sell_id].order_id;
451            let buy_id = *self.highest_buy().unwrap().1;
452            let buy_quantity = self.orders[buy_id].quantity;
453            let buy_order_id = self.orders[buy_id].order_id;
454            let fill_quantity = sell_quantity.min(buy_quantity);
455
456            let maker_id = if sell_order_id < buy_order_id {
457                sell_id
458            } else {
459                buy_id
460            };
461            let taker_id = if sell_order_id > buy_order_id {
462                sell_id
463            } else {
464                buy_id
465            };
466            let maker_price = self.orders[maker_id].price;
467
468            let sell_is_full =
469                self.orders[sell_id].fill(buy_order_id, fill_quantity, maker_price);
470            let buy_is_full =
471                self.orders[buy_id].fill(sell_order_id, fill_quantity, maker_price);
472
473            self.change_balance_match(
474                &self.orders[maker_id].clone(),
475                &self.orders[taker_id].clone(),
476                fill_quantity,
477            );
478            self.trades
479                .push(self.create_order_matched_event(&self.orders[maker_id]));
480
481            if sell_is_full {
482                self.remove_order(sell_id);
483            }
484            if buy_is_full {
485                self.remove_order(buy_id);
486            }
487        }
488    }
489
490    pub fn create_order(&mut self, order_data: OrderData) -> Order {
491        self.order_id += 1;
492        let order = Order::new(self.order_id, order_data);
493        self.insert_order(&order);
494        self.all_orders.push(order.clone());
495        order
496    }
497
498    pub fn create_orders(&mut self, orders_data: Vec<OrderData>) -> Vec<Order> {
499        let mut orders = Vec::new();
500        for order_data in orders_data {
501            orders.push(self.create_order(order_data));
502        }
503        orders
504    }
505}
506
507#[derive(Clone, Debug)]
508pub struct TestOrderBookState {
509    pub orders: Vec<OrderData>,
510    pub matches: Vec<MatchedEvent>,
511    pub cancels: Vec<Order>,
512}
513
514pub fn get_default_orders_test(
515    base_asset: AssetId,
516    quote_asset: AssetId,
517    initial_balances: Balances,
518) -> (Book, TestOrderBookState) {
519    let mut book: Book = Book::new(BookConfig {
520        base_asset,
521        base_decimals: 9,
522        quote_asset,
523        quote_decimals: 6,
524        taker_fee: 0,
525        maker_fee: 0,
526    });
527    let traders = initial_balances.keys().cloned().collect::<Vec<_>>();
528
529    // Set initial balances
530    book.set_balances(initial_balances);
531
532    let mut orders = vec![
533        OrderData {
534            side: Side::Buy,
535            price: 9_500_000,
536            quantity: 20_000_000,
537            trader_id: Identity::default(),
538        },
539        OrderData {
540            side: Side::Sell,
541            price: 20_000_000,
542            quantity: 20_000_000,
543            trader_id: Identity::default(),
544        },
545        OrderData {
546            side: Side::Sell,
547            price: 10_000_000,
548            quantity: 20_000_000,
549            trader_id: Identity::default(),
550        },
551        OrderData {
552            side: Side::Buy,
553            price: 10_000_000,
554            quantity: 40_000_000,
555            trader_id: Identity::default(),
556        },
557        OrderData {
558            side: Side::Sell,
559            price: 9_900_000,
560            quantity: 40_000_000,
561            trader_id: Identity::default(),
562        },
563        OrderData {
564            side: Side::Buy,
565            price: 10_000_000,
566            quantity: 20_000_000,
567            trader_id: Identity::default(),
568        },
569    ];
570
571    for (i, order) in orders.iter_mut().enumerate() {
572        let trader_id = traders.get(i % traders.len()).unwrap();
573        order.trader_id = *trader_id;
574    }
575
576    let matches = vec![
577        MatchedEvent {
578            direction: Side::Sell,
579            maker_id: 3,
580            taker_id: 4,
581            quantity: 20_000_000,
582            price: 10_000_000,
583            total: 200_000,
584        },
585        MatchedEvent {
586            direction: Side::Buy,
587            maker_id: 4,
588            taker_id: 5,
589            quantity: 20_000_000,
590            price: 10_000_000,
591            total: 200_000,
592        },
593        MatchedEvent {
594            direction: Side::Sell,
595            maker_id: 5,
596            taker_id: 6,
597            quantity: 20_000_000,
598            price: 9_900_000,
599            total: 198_000,
600        },
601    ];
602    let cancels = vec![Order::new(
603        1,
604        OrderData {
605            side: Side::Buy,
606            price: 9_500_000,
607            quantity: 20_000_000,
608            trader_id: *traders.first().unwrap(),
609        },
610    )];
611
612    // Create book with states
613    book.create_orders(orders.clone());
614
615    let test_order_book_state = TestOrderBookState {
616        orders,
617        matches,
618        cancels,
619    };
620
621    (book, test_order_book_state)
622}
623
624pub fn get_default_orders_with_fees_test(
625    base_asset: AssetId,
626    quote_asset: AssetId,
627    initial_balances: Balances,
628) -> (Book, TestOrderBookState) {
629    let mut book: Book = Book::new(BookConfig {
630        base_asset,
631        base_decimals: 9,
632        quote_asset,
633        quote_decimals: 6,
634        taker_fee: 2_000,
635        maker_fee: 1_000,
636    });
637    let traders = initial_balances.keys().cloned().collect::<Vec<_>>();
638
639    // Set initial balances
640    book.set_balances(initial_balances);
641
642    let mut orders = vec![
643        OrderData {
644            side: Side::Buy,
645            price: 9_500_000,
646            quantity: 20_000_000,
647            trader_id: Identity::default(),
648        },
649        OrderData {
650            side: Side::Sell,
651            price: 20_000_000,
652            quantity: 20_000_000,
653            trader_id: Identity::default(),
654        },
655        OrderData {
656            side: Side::Sell,
657            price: 10_000_000,
658            quantity: 20_000_000,
659            trader_id: Identity::default(),
660        },
661        OrderData {
662            side: Side::Buy,
663            price: 10_000_000,
664            quantity: 40_000_000,
665            trader_id: Identity::default(),
666        },
667        OrderData {
668            side: Side::Sell,
669            price: 9_900_000,
670            quantity: 40_000_000,
671            trader_id: Identity::default(),
672        },
673        OrderData {
674            side: Side::Buy,
675            price: 10_000_000,
676            quantity: 20_000_000,
677            trader_id: Identity::default(),
678        },
679    ];
680
681    for (i, order) in orders.iter_mut().enumerate() {
682        let trader_id = traders.get(i % traders.len()).unwrap();
683        order.trader_id = *trader_id;
684    }
685
686    let matches = vec![
687        MatchedEvent {
688            direction: Side::Sell,
689            maker_id: 3,
690            taker_id: 4,
691            quantity: 20_000_000,
692            price: 10_000_000,
693            total: 200_000,
694        },
695        MatchedEvent {
696            direction: Side::Buy,
697            maker_id: 4,
698            taker_id: 5,
699            quantity: 20_000_000,
700            price: 10_000_000,
701            total: 200_000,
702        },
703        MatchedEvent {
704            direction: Side::Sell,
705            maker_id: 5,
706            taker_id: 6,
707            quantity: 20_000_000,
708            price: 9_900_000,
709            total: 198_000,
710        },
711    ];
712    let cancels = vec![Order::new(
713        1,
714        OrderData {
715            side: Side::Buy,
716            price: 9_500_000,
717            quantity: 20_000_000,
718            trader_id: *traders.first().unwrap(),
719        },
720    )];
721
722    // Create book with states
723    book.create_orders(orders.clone());
724
725    let test_order_book_state = TestOrderBookState {
726        orders,
727        matches,
728        cancels,
729    };
730
731    (book, test_order_book_state)
732}
733
734#[tokio::test]
735async fn test_order_book_data() {
736    use fuels::types::{
737        Address,
738        Identity,
739    };
740    use std::str::FromStr;
741    const USDC: &str =
742        "0x8288f40f7c9ab3b5bc36783b692f4ae96975c5a04d8f0da258886fdada0b26af";
743    const BTC: &str =
744        "0xd0cef4b9cb0706cfa4aa174e75fb006b8ddd2a0ecd6296a27c0a05dcb856f32f";
745    let address_1 = Identity::Address(Address::from([5u8; 32]));
746    let address_2 = Identity::Address(Address::from([6u8; 32]));
747
748    let balances = Balances::from_iter(vec![
749        (address_1, (100_000_000_000_000, 100_000_000_000_000)),
750        (address_2, (100_000_000_000_000, 100_000_000_000_000)),
751    ]);
752
753    let (mut book, test_order_book_state) = get_default_orders_test(
754        AssetId::from_str(BTC).unwrap(),
755        AssetId::from_str(USDC).unwrap(),
756        balances,
757    );
758    book.cancel(1);
759    let balances = Balances::from_iter(vec![
760        (address_1, (99999940000000, 100000000598000)),
761        (address_2, (100000040000000, 99999999402000)),
762    ]);
763
764    assert_eq!(test_order_book_state.matches, book.trades);
765    assert_eq!(test_order_book_state.cancels, book.cancels);
766    assert_eq!(balances, book.balances);
767}
768
769#[tokio::test]
770async fn test_order_book_data_fees() {
771    use fuels::types::{
772        Address,
773        Identity,
774    };
775    use std::str::FromStr;
776    const USDC: &str =
777        "0x8288f40f7c9ab3b5bc36783b692f4ae96975c5a04d8f0da258886fdada0b26af";
778    const BTC: &str =
779        "0xd0cef4b9cb0706cfa4aa174e75fb006b8ddd2a0ecd6296a27c0a05dcb856f32f";
780    let address_1 = Identity::Address(Address::from([5u8; 32]));
781    let address_2 = Identity::Address(Address::from([6u8; 32]));
782
783    let balances = Balances::from_iter(vec![
784        (address_1, (100_000_000_000_000, 100_000_000_000_000)),
785        (address_2, (100_000_000_000_000, 100_000_000_000_000)),
786    ]);
787
788    let (mut book, test_order_book_state) = get_default_orders_with_fees_test(
789        AssetId::from_str(BTC).unwrap(),
790        AssetId::from_str(USDC).unwrap(),
791        balances,
792    );
793    book.cancel(1);
794    let balances = Balances::from_iter(vec![
795        (address_1, (99999940000000, 100000000596804)),
796        (address_2, (100000039880000, 99999999402000)),
797    ]);
798
799    assert_eq!(test_order_book_state.matches, book.trades);
800    assert_eq!(test_order_book_state.cancels, book.cancels);
801    assert_eq!(balances, book.balances);
802}