wickra_core/indicators/
signed_volume.rs1use crate::microstructure::Trade;
4use crate::traits::Indicator;
5
6#[derive(Debug, Clone, Default)]
30pub struct SignedVolume {
31 has_emitted: bool,
32}
33
34impl SignedVolume {
35 pub const fn new() -> Self {
37 Self { has_emitted: false }
38 }
39}
40
41impl Indicator for SignedVolume {
42 type Input = Trade;
43 type Output = f64;
44
45 fn update(&mut self, trade: Trade) -> Option<f64> {
46 self.has_emitted = true;
47 Some(trade.size * trade.side.sign())
48 }
49
50 fn reset(&mut self) {
51 self.has_emitted = false;
52 }
53
54 fn warmup_period(&self) -> usize {
55 1
56 }
57
58 fn is_ready(&self) -> bool {
59 self.has_emitted
60 }
61
62 fn name(&self) -> &'static str {
63 "SignedVolume"
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::microstructure::Side;
71 use crate::traits::BatchExt;
72
73 fn trade(size: f64, side: Side, ts: i64) -> Trade {
74 Trade::new(100.0, size, side, ts).unwrap()
75 }
76
77 #[test]
78 fn accessors_and_metadata() {
79 let sv = SignedVolume::new();
80 assert_eq!(sv.name(), "SignedVolume");
81 assert_eq!(sv.warmup_period(), 1);
82 assert!(!sv.is_ready());
83 }
84
85 #[test]
86 fn buy_is_positive() {
87 let mut sv = SignedVolume::new();
88 assert_eq!(sv.update(trade(2.0, Side::Buy, 0)), Some(2.0));
89 assert!(sv.is_ready());
90 }
91
92 #[test]
93 fn sell_is_negative() {
94 let mut sv = SignedVolume::new();
95 assert_eq!(sv.update(trade(3.0, Side::Sell, 0)), Some(-3.0));
96 }
97
98 #[test]
99 fn zero_size_is_zero() {
100 let mut sv = SignedVolume::new();
101 assert_eq!(sv.update(trade(0.0, Side::Buy, 0)), Some(0.0));
102 }
103
104 #[test]
105 fn batch_equals_streaming() {
106 let trades: Vec<Trade> = (0..20)
107 .map(|i| {
108 let side = if i % 2 == 0 { Side::Buy } else { Side::Sell };
109 trade(1.0 + (i % 4) as f64, side, i)
110 })
111 .collect();
112 let mut a = SignedVolume::new();
113 let mut b = SignedVolume::new();
114 assert_eq!(
115 a.batch(&trades),
116 trades.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
117 );
118 }
119
120 #[test]
121 fn reset_clears_state() {
122 let mut sv = SignedVolume::new();
123 sv.update(trade(1.0, Side::Buy, 0));
124 assert!(sv.is_ready());
125 sv.reset();
126 assert!(!sv.is_ready());
127 }
128}