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 #[inline]
64 fn update(&mut self, tick: DerivativesTick) -> Option<f64> {
65 self.has_emitted = true;
66 self.sum_weighted += tick.mark_price * tick.open_interest;
67 self.sum_oi += tick.open_interest;
68 if self.sum_oi == 0.0 {
69 return Some(tick.mark_price);
71 }
72 Some(self.sum_weighted / self.sum_oi)
73 }
74
75 fn reset(&mut self) {
76 self.sum_weighted = 0.0;
77 self.sum_oi = 0.0;
78 self.has_emitted = false;
79 }
80
81 #[inline]
82 fn warmup_period(&self) -> usize {
83 1
84 }
85
86 #[inline]
87 fn is_ready(&self) -> bool {
88 self.has_emitted
89 }
90
91 #[inline]
92 fn name(&self) -> &'static str {
93 "OIWeighted"
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::traits::BatchExt;
101
102 fn tick(mark: f64, oi: f64) -> DerivativesTick {
103 DerivativesTick::new_unchecked(0.0, mark, mark, mark, oi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)
104 }
105
106 #[test]
107 fn accessors_and_metadata() {
108 let oiw = OIWeighted::new();
109 assert_eq!(oiw.name(), "OIWeighted");
110 assert_eq!(oiw.warmup_period(), 1);
111 assert!(!oiw.is_ready());
112 }
113
114 #[test]
115 fn weights_by_open_interest() {
116 let mut oiw = OIWeighted::new();
117 assert_eq!(oiw.update(tick(100.0, 10.0)), Some(100.0));
118 assert_eq!(oiw.update(tick(110.0, 30.0)), Some(107.5));
120 assert!(oiw.is_ready());
121 }
122
123 #[test]
124 fn zero_open_interest_falls_back_to_mark() {
125 let mut oiw = OIWeighted::new();
126 assert_eq!(oiw.update(tick(123.0, 0.0)), Some(123.0));
127 assert_eq!(oiw.update(tick(125.0, 0.0)), Some(125.0));
129 }
130
131 #[test]
132 fn batch_equals_streaming() {
133 let ticks: Vec<DerivativesTick> = (0..20)
134 .map(|i| tick(100.0 + f64::from(i % 5), 1.0 + f64::from(i % 4)))
135 .collect();
136 let mut a = OIWeighted::new();
137 let mut b = OIWeighted::new();
138 assert_eq!(
139 a.batch(&ticks),
140 ticks.iter().map(|x| b.update(*x)).collect::<Vec<_>>()
141 );
142 }
143
144 #[test]
145 fn reset_re_anchors() {
146 let mut oiw = OIWeighted::new();
147 oiw.update(tick(100.0, 10.0));
148 oiw.update(tick(110.0, 30.0));
149 assert!(oiw.is_ready());
150 oiw.reset();
151 assert!(!oiw.is_ready());
152 assert_eq!(oiw.update(tick(200.0, 5.0)), Some(200.0));
154 }
155}