trading_types/
spread.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{Depth, Liq, Price};
4
5/// Spread of depth with convininent calcs.
6/// Very suitable for a depth after depth_util::drop_worth
7#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
8pub struct Spread {
9    pub best_ask: Liq,
10    pub best_bid: Liq,
11    delta_abs: Price,
12    delta_rel: f64,
13}
14
15impl Spread {
16    pub fn new(best_ask: Liq, best_bid: Liq) -> Self {
17        let (delta_abs, delta_rel) = Self::deltas(best_ask, best_bid);
18        Self {
19            best_ask,
20            best_bid,
21            delta_abs,
22            delta_rel,
23        }
24    }
25
26    pub fn from_depth(depth: &Depth) -> Option<Self> {
27        if depth.asks.is_empty() || depth.bids.is_empty() {
28            return None;
29        }
30        let best_ask = depth.asks[0];
31        let best_bid = depth.bids[0];
32        Some(Self::new(best_ask, best_bid))
33    }
34
35    pub fn delta_abs(&self) -> Price {
36        self.delta_abs
37    }
38
39    pub fn delta_rel(&self) -> f64 {
40        self.delta_rel
41    }
42
43    // private helpers
44
45    fn deltas(ask: Liq, bid: Liq) -> (Price, f64) {
46        let ask_price = ask.price();
47        let bid_price = bid.price();
48        let delta_abs: Price = ask_price - bid_price;
49        let mid = (ask_price + bid_price) / 2.0;
50        let delta_rel = delta_abs / mid;
51        (delta_abs, delta_rel)
52    }
53}
54
55#[derive(thiserror::Error, Debug)]
56pub enum SpreadError {
57    #[error("empty depth")]
58    EmptyDepth,
59}
60
61impl TryFrom<&Depth> for Spread {
62    type Error = SpreadError;
63
64    fn try_from(depth: &Depth) -> Result<Self, SpreadError> {
65        Spread::from_depth(depth).ok_or(SpreadError::EmptyDepth)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use serde_json::json;
72
73    use super::*;
74    use crate::Amount;
75
76    #[test]
77    fn spread_to_json() {
78        let s = Spread::new(
79            Liq::from_pa(Price(10.0), Amount(1.0)),
80            Liq::from_pa(Price(9.0), Amount(1.0)),
81        );
82        let v = json!(s);
83        let expected = r#"{"best_ask":{"a":1.0,"p":10.0,"w":10.0},"best_bid":{"a":1.0,"p":9.0,"w":9.0},"delta_abs":1.0,"delta_rel":0.10526315789473684}"#;
84        assert_eq!(v.to_string().as_str(), expected);
85    }
86}