wickra_core/indicators/
oi_delta.rs1use crate::derivatives::DerivativesTick;
4use crate::traits::Indicator;
5
6#[derive(Debug, Clone, Default)]
41pub struct OpenInterestDelta {
42 prev: Option<f64>,
43 has_emitted: bool,
44}
45
46impl OpenInterestDelta {
47 #[must_use]
49 pub const fn new() -> Self {
50 Self {
51 prev: None,
52 has_emitted: false,
53 }
54 }
55}
56
57impl Indicator for OpenInterestDelta {
58 type Input = DerivativesTick;
59 type Output = f64;
60
61 fn update(&mut self, tick: DerivativesTick) -> Option<f64> {
62 let oi = tick.open_interest;
63 let delta = self.prev.map(|prev| oi - prev);
64 self.prev = Some(oi);
65 if delta.is_some() {
66 self.has_emitted = true;
67 }
68 delta
69 }
70
71 fn reset(&mut self) {
72 self.prev = None;
73 self.has_emitted = false;
74 }
75
76 fn warmup_period(&self) -> usize {
77 2
78 }
79
80 fn is_ready(&self) -> bool {
81 self.has_emitted
82 }
83
84 fn name(&self) -> &'static str {
85 "OpenInterestDelta"
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::traits::BatchExt;
93
94 fn tick(oi: f64) -> DerivativesTick {
95 DerivativesTick::new_unchecked(
96 0.0, 100.0, 100.0, 100.0, oi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0,
97 )
98 }
99
100 #[test]
101 fn accessors_and_metadata() {
102 let oid = OpenInterestDelta::new();
103 assert_eq!(oid.name(), "OpenInterestDelta");
104 assert_eq!(oid.warmup_period(), 2);
105 assert!(!oid.is_ready());
106 }
107
108 #[test]
109 fn seeds_then_emits_delta() {
110 let mut oid = OpenInterestDelta::new();
111 assert_eq!(oid.update(tick(1_000.0)), None);
112 assert!(!oid.is_ready());
113 assert_eq!(oid.update(tick(1_250.0)), Some(250.0));
114 assert!(oid.is_ready());
115 assert_eq!(oid.update(tick(1_100.0)), Some(-150.0));
116 }
117
118 #[test]
119 fn batch_equals_streaming() {
120 let ticks: Vec<DerivativesTick> = (0..20)
121 .map(|i| tick(1_000.0 + f64::from(i * i % 13) * 10.0))
122 .collect();
123 let mut a = OpenInterestDelta::new();
124 let mut b = OpenInterestDelta::new();
125 assert_eq!(
126 a.batch(&ticks),
127 ticks.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
128 );
129 }
130
131 #[test]
132 fn reset_clears_state() {
133 let mut oid = OpenInterestDelta::new();
134 oid.update(tick(1_000.0));
135 oid.update(tick(1_250.0));
136 assert!(oid.is_ready());
137 oid.reset();
138 assert!(!oid.is_ready());
139 assert_eq!(oid.update(tick(2_000.0)), None);
141 }
142}