wickra_core/indicators/
oi_weighted.rs1use crate::derivatives::DerivativesTick;
5use crate::traits::Indicator;
6
7#[derive(Debug, Clone, Default)]
41pub struct OIWeighted {
42 sum_weighted: f64,
43 sum_oi: f64,
44 has_emitted: bool,
45}
46
47impl OIWeighted {
48 #[must_use]
50 pub const fn new() -> Self {
51 Self {
52 sum_weighted: 0.0,
53 sum_oi: 0.0,
54 has_emitted: false,
55 }
56 }
57}
58
59impl Indicator for OIWeighted {
60 type Input = DerivativesTick;
61 type Output = f64;
62
63 fn update(&mut self, tick: DerivativesTick) -> Option<f64> {
64 self.has_emitted = true;
65 self.sum_weighted += tick.mark_price * tick.open_interest;
66 self.sum_oi += tick.open_interest;
67 if self.sum_oi == 0.0 {
68 return Some(tick.mark_price);
70 }
71 Some(self.sum_weighted / self.sum_oi)
72 }
73
74 fn reset(&mut self) {
75 self.sum_weighted = 0.0;
76 self.sum_oi = 0.0;
77 self.has_emitted = false;
78 }
79
80 fn warmup_period(&self) -> usize {
81 1
82 }
83
84 fn is_ready(&self) -> bool {
85 self.has_emitted
86 }
87
88 fn name(&self) -> &'static str {
89 "OIWeighted"
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::traits::BatchExt;
97
98 fn tick(mark: f64, oi: f64) -> DerivativesTick {
99 DerivativesTick::new_unchecked(0.0, mark, mark, mark, oi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)
100 }
101
102 #[test]
103 fn accessors_and_metadata() {
104 let oiw = OIWeighted::new();
105 assert_eq!(oiw.name(), "OIWeighted");
106 assert_eq!(oiw.warmup_period(), 1);
107 assert!(!oiw.is_ready());
108 }
109
110 #[test]
111 fn weights_by_open_interest() {
112 let mut oiw = OIWeighted::new();
113 assert_eq!(oiw.update(tick(100.0, 10.0)), Some(100.0));
114 assert_eq!(oiw.update(tick(110.0, 30.0)), Some(107.5));
116 assert!(oiw.is_ready());
117 }
118
119 #[test]
120 fn zero_open_interest_falls_back_to_mark() {
121 let mut oiw = OIWeighted::new();
122 assert_eq!(oiw.update(tick(123.0, 0.0)), Some(123.0));
123 assert_eq!(oiw.update(tick(125.0, 0.0)), Some(125.0));
125 }
126
127 #[test]
128 fn batch_equals_streaming() {
129 let ticks: Vec<DerivativesTick> = (0..20)
130 .map(|i| tick(100.0 + f64::from(i % 5), 1.0 + f64::from(i % 4)))
131 .collect();
132 let mut a = OIWeighted::new();
133 let mut b = OIWeighted::new();
134 assert_eq!(
135 a.batch(&ticks),
136 ticks.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
137 );
138 }
139
140 #[test]
141 fn reset_re_anchors() {
142 let mut oiw = OIWeighted::new();
143 oiw.update(tick(100.0, 10.0));
144 oiw.update(tick(110.0, 30.0));
145 assert!(oiw.is_ready());
146 oiw.reset();
147 assert!(!oiw.is_ready());
148 assert_eq!(oiw.update(tick(200.0, 5.0)), Some(200.0));
150 }
151}