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 #[inline]
62 fn update(&mut self, tick: DerivativesTick) -> Option<f64> {
63 let oi = tick.open_interest;
64 let delta = self.prev.map(|prev| oi - prev);
65 self.prev = Some(oi);
66 if delta.is_some() {
67 self.has_emitted = true;
68 }
69 delta
70 }
71
72 fn reset(&mut self) {
73 self.prev = None;
74 self.has_emitted = false;
75 }
76
77 #[inline]
78 fn warmup_period(&self) -> usize {
79 2
80 }
81
82 #[inline]
83 fn is_ready(&self) -> bool {
84 self.has_emitted
85 }
86
87 #[inline]
88 fn name(&self) -> &'static str {
89 "OpenInterestDelta"
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::traits::BatchExt;
97
98 fn tick(oi: f64) -> DerivativesTick {
99 DerivativesTick::new_unchecked(
100 0.0, 100.0, 100.0, 100.0, oi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0,
101 )
102 }
103
104 #[test]
105 fn accessors_and_metadata() {
106 let oid = OpenInterestDelta::new();
107 assert_eq!(oid.name(), "OpenInterestDelta");
108 assert_eq!(oid.warmup_period(), 2);
109 assert!(!oid.is_ready());
110 }
111
112 #[test]
113 fn seeds_then_emits_delta() {
114 let mut oid = OpenInterestDelta::new();
115 assert_eq!(oid.update(tick(1_000.0)), None);
116 assert!(!oid.is_ready());
117 assert_eq!(oid.update(tick(1_250.0)), Some(250.0));
118 assert!(oid.is_ready());
119 assert_eq!(oid.update(tick(1_100.0)), Some(-150.0));
120 }
121
122 #[test]
123 fn batch_equals_streaming() {
124 let ticks: Vec<DerivativesTick> = (0..20)
125 .map(|i| tick(1_000.0 + f64::from(i * i % 13) * 10.0))
126 .collect();
127 let mut a = OpenInterestDelta::new();
128 let mut b = OpenInterestDelta::new();
129 assert_eq!(
130 a.batch(&ticks),
131 ticks.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
132 );
133 }
134
135 #[test]
136 fn reset_clears_state() {
137 let mut oid = OpenInterestDelta::new();
138 oid.update(tick(1_000.0));
139 oid.update(tick(1_250.0));
140 assert!(oid.is_ready());
141 oid.reset();
142 assert!(!oid.is_ready());
143 assert_eq!(oid.update(tick(2_000.0)), None);
145 }
146}