flowsurface_data/panel/
timeandsales.rs1use 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}