1use serde::{Deserialize, Serialize};
2
3use crate::{Depth, Liq, Price};
4
5#[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 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}