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