flowsurface_data/panel/
timeandsales.rs

1use std::time::Duration;
2
3use exchange::util::Price;
4use serde::{Deserialize, Serialize};
5
6use crate::util::ok_or_default;
7
8const TRADE_RETENTION_MS: u64 = 120_000;
9
10#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
11pub struct Config {
12    pub trade_size_filter: f32,
13    #[serde(default = "default_buffer_filter")]
14    pub trade_retention: Duration,
15    #[serde(deserialize_with = "ok_or_default", default)]
16    pub stacked_bar: Option<StackedBar>,
17}
18
19impl Default for Config {
20    fn default() -> Self {
21        Config {
22            trade_size_filter: 0.0,
23            trade_retention: Duration::from_millis(TRADE_RETENTION_MS),
24            stacked_bar: StackedBar::Compact(StackedBarRatio::default()).into(),
25        }
26    }
27}
28
29fn default_buffer_filter() -> Duration {
30    Duration::from_millis(TRADE_RETENTION_MS)
31}
32
33#[derive(Debug, Clone)]
34pub struct TradeDisplay {
35    pub time_str: String,
36    pub price: Price,
37    pub qty: f32,
38    pub is_sell: bool,
39}
40
41#[derive(Debug, Clone)]
42pub struct TradeEntry {
43    pub ts_ms: u64,
44    pub display: TradeDisplay,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Copy)]
48pub enum StackedBar {
49    Compact(StackedBarRatio),
50    Full(StackedBarRatio),
51}
52
53impl StackedBar {
54    pub fn ratio(self) -> StackedBarRatio {
55        match self {
56            StackedBar::Compact(r) | StackedBar::Full(r) => r,
57        }
58    }
59
60    pub fn with_ratio(self, r: StackedBarRatio) -> Self {
61        match self {
62            StackedBar::Compact(_) => StackedBar::Compact(r),
63            StackedBar::Full(_) => StackedBar::Full(r),
64        }
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default, Copy)]
69pub enum StackedBarRatio {
70    Count,
71    #[default]
72    Volume,
73    AverageSize,
74}
75
76impl std::fmt::Display for StackedBarRatio {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            StackedBarRatio::Count => write!(f, "Count"),
80            StackedBarRatio::AverageSize => write!(f, "Average trade size"),
81            StackedBarRatio::Volume => write!(f, "Volume"),
82        }
83    }
84}
85
86impl StackedBarRatio {
87    pub const ALL: [StackedBarRatio; 3] = [
88        StackedBarRatio::Count,
89        StackedBarRatio::Volume,
90        StackedBarRatio::AverageSize,
91    ];
92}
93
94#[derive(Default)]
95pub struct HistAgg {
96    buy_count: u64,
97    sell_count: u64,
98    buy_sum: f64,
99    sell_sum: f64,
100}
101
102impl HistAgg {
103    pub fn add(&mut self, trade: &TradeDisplay) {
104        let qty = trade.qty as f64;
105
106        if trade.is_sell {
107            self.sell_count += 1;
108            self.sell_sum += qty;
109        } else {
110            self.buy_count += 1;
111            self.buy_sum += qty;
112        }
113    }
114
115    pub fn remove(&mut self, trade: &TradeDisplay) {
116        let qty = trade.qty as f64;
117
118        if trade.is_sell {
119            self.sell_count = self.sell_count.saturating_sub(1);
120            self.sell_sum -= qty;
121        } else {
122            self.buy_count = self.buy_count.saturating_sub(1);
123            self.buy_sum -= qty;
124        }
125    }
126
127    pub fn values_for(&self, ratio_kind: StackedBarRatio) -> Option<(f64, f64, f32)> {
128        match ratio_kind {
129            StackedBarRatio::Count => {
130                let buy = self.buy_count as f64;
131                let sell = self.sell_count as f64;
132                let total = buy + sell;
133
134                if total <= 0.0 {
135                    return None;
136                }
137                let buy_ratio = (buy / total) as f32;
138
139                Some((buy, sell, buy_ratio))
140            }
141            StackedBarRatio::Volume => {
142                let buy = self.buy_sum;
143                let sell = self.sell_sum;
144                let total = buy + sell;
145
146                if total <= 0.0 {
147                    return None;
148                }
149                let buy_ratio = (buy / total) as f32;
150
151                Some((buy, sell, buy_ratio))
152            }
153            StackedBarRatio::AverageSize => {
154                let buy_avg = if self.buy_count > 0 {
155                    self.buy_sum / self.buy_count as f64
156                } else {
157                    0.0
158                };
159                let sell_avg = if self.sell_count > 0 {
160                    self.sell_sum / self.sell_count as f64
161                } else {
162                    0.0
163                };
164
165                let denom = buy_avg + sell_avg;
166                if denom <= 0.0 {
167                    return None;
168                }
169                let buy_ratio = (buy_avg / denom) as f32;
170
171                Some((buy_avg, sell_avg, buy_ratio))
172            }
173        }
174    }
175}