finance_solution/stocks/ta/
ring.rs1use std::collections::VecDeque;
7
8#[derive(Clone, Debug)]
10pub(crate) struct RingF64 {
11 buf: Vec<f64>,
12 head: usize,
14 len: usize,
16 sum: f64,
17}
18
19impl RingF64 {
20 pub(crate) fn with_capacity(cap: usize) -> Self {
21 debug_assert!(cap >= 1);
22 Self {
23 buf: vec![0.0; cap],
24 head: 0,
25 len: 0,
26 sum: 0.0,
27 }
28 }
29
30 pub(crate) fn capacity(&self) -> usize {
31 self.buf.len()
32 }
33
34 pub(crate) fn len(&self) -> usize {
35 self.len
36 }
37
38 pub(crate) fn is_full(&self) -> bool {
39 self.len == self.buf.len()
40 }
41
42 pub(crate) fn sum(&self) -> f64 {
43 self.sum
44 }
45
46 pub(crate) fn oldest(&self) -> Option<f64> {
50 if self.len == 0 {
51 return None;
52 }
53 let cap = self.buf.len();
54 let start = if self.len < cap { 0 } else { self.head };
55 Some(self.buf[start])
56 }
57
58 pub(crate) fn clear(&mut self) {
59 self.head = 0;
60 self.len = 0;
61 self.sum = 0.0;
62 }
63
64 pub(crate) fn push(&mut self, value: f64) -> Option<f64> {
68 let cap = self.buf.len();
69 if self.len < cap {
70 self.buf[self.head] = value;
71 self.sum += value;
72 self.head = (self.head + 1) % cap;
73 self.len += 1;
74 None
75 } else {
76 let old = self.buf[self.head];
77 self.sum += value - old;
78 self.buf[self.head] = value;
79 self.head = (self.head + 1) % cap;
80 Some(old)
81 }
82 }
83
84 pub(crate) fn copy_ordered(&self, out: &mut Vec<f64>) {
86 out.clear();
87 if self.len == 0 {
88 return;
89 }
90 let cap = self.buf.len();
91 let start = if self.len < cap { 0 } else { self.head };
92 for i in 0..self.len {
93 out.push(self.buf[(start + i) % cap]);
94 }
95 }
96
97 pub(crate) fn mean(&self) -> Option<f64> {
98 if self.len == 0 {
99 None
100 } else {
101 Some(self.sum / self.len as f64)
102 }
103 }
104
105 pub(crate) fn max(&self) -> Option<f64> {
106 if self.len == 0 {
107 return None;
108 }
109 let cap = self.buf.len();
110 let start = if self.len < cap { 0 } else { self.head };
111 let mut m = f64::NEG_INFINITY;
112 for i in 0..self.len {
113 m = m.max(self.buf[(start + i) % cap]);
114 }
115 Some(m)
116 }
117
118 pub(crate) fn min(&self) -> Option<f64> {
119 if self.len == 0 {
120 return None;
121 }
122 let cap = self.buf.len();
123 let start = if self.len < cap { 0 } else { self.head };
124 let mut m = f64::INFINITY;
125 for i in 0..self.len {
126 m = m.min(self.buf[(start + i) % cap]);
127 }
128 Some(m)
129 }
130}
131
132#[derive(Clone, Debug)]
134pub(crate) struct RingPv {
135 pv: RingF64,
136 vol: RingF64,
137}
138
139impl RingPv {
140 pub(crate) fn with_capacity(cap: usize) -> Self {
141 Self {
142 pv: RingF64::with_capacity(cap),
143 vol: RingF64::with_capacity(cap),
144 }
145 }
146
147 pub(crate) fn clear(&mut self) {
148 self.pv.clear();
149 self.vol.clear();
150 }
151
152 pub(crate) fn is_full(&self) -> bool {
153 self.pv.is_full()
154 }
155
156 pub(crate) fn len(&self) -> usize {
157 self.pv.len()
158 }
159
160 pub(crate) fn push(&mut self, price: f64, volume: f64) {
161 let _ = self.pv.push(price * volume);
162 let _ = self.vol.push(volume);
163 }
164
165 pub(crate) fn vwap(&self) -> Option<f64> {
166 let v = self.vol.sum();
167 if v > 0.0 {
168 Some(self.pv.sum() / v)
169 } else {
170 None
171 }
172 }
173}
174
175#[derive(Clone, Debug)]
184pub(crate) struct SlidingMax {
185 dq: VecDeque<(u64, f64)>,
187 next_id: u64,
188 window: usize,
189 count: usize,
191}
192
193impl SlidingMax {
194 pub(crate) fn with_window(window: usize) -> Self {
195 debug_assert!(window >= 1);
196 Self {
197 dq: VecDeque::with_capacity(window),
198 next_id: 0,
199 window,
200 count: 0,
201 }
202 }
203
204 pub(crate) fn clear(&mut self) {
205 self.dq.clear();
206 self.next_id = 0;
207 self.count = 0;
208 }
209
210 pub(crate) fn is_full(&self) -> bool {
211 self.count == self.window
212 }
213
214 pub(crate) fn max(&self) -> Option<f64> {
215 self.dq.front().map(|(_, v)| *v)
216 }
217
218 pub(crate) fn push(&mut self, value: f64) -> Option<f64> {
222 if self.count == self.window {
223 let drop_id = self.next_id - self.window as u64;
224 if let Some(&(id, _)) = self.dq.front() {
225 if id == drop_id {
226 self.dq.pop_front();
227 }
228 }
229 } else {
230 self.count += 1;
231 }
232 while let Some(&(_, back_v)) = self.dq.back() {
233 if back_v <= value {
234 self.dq.pop_back();
235 } else {
236 break;
237 }
238 }
239 self.dq.push_back((self.next_id, value));
240 self.next_id += 1;
241 self.max()
242 }
243}
244
245#[derive(Clone, Debug)]
247pub(crate) struct SlidingMin {
248 dq: VecDeque<(u64, f64)>,
249 next_id: u64,
250 window: usize,
251 count: usize,
252}
253
254impl SlidingMin {
255 pub(crate) fn with_window(window: usize) -> Self {
256 debug_assert!(window >= 1);
257 Self {
258 dq: VecDeque::with_capacity(window),
259 next_id: 0,
260 window,
261 count: 0,
262 }
263 }
264
265 pub(crate) fn clear(&mut self) {
266 self.dq.clear();
267 self.next_id = 0;
268 self.count = 0;
269 }
270
271 pub(crate) fn is_full(&self) -> bool {
272 self.count == self.window
273 }
274
275 pub(crate) fn min(&self) -> Option<f64> {
276 self.dq.front().map(|(_, v)| *v)
277 }
278
279 pub(crate) fn push(&mut self, value: f64) -> Option<f64> {
280 if self.count == self.window {
281 let drop_id = self.next_id - self.window as u64;
282 if let Some(&(id, _)) = self.dq.front() {
283 if id == drop_id {
284 self.dq.pop_front();
285 }
286 }
287 } else {
288 self.count += 1;
289 }
290 while let Some(&(_, back_v)) = self.dq.back() {
291 if back_v >= value {
292 self.dq.pop_back();
293 } else {
294 break;
295 }
296 }
297 self.dq.push_back((self.next_id, value));
298 self.next_id += 1;
299 self.min()
300 }
301}
302
303#[cfg(test)]
304mod sliding_tests {
305 use super::*;
306
307 #[test]
308 fn sliding_max_matches_scan() {
309 let data = [1.0, 3.0, 2.0, 5.0, 4.0, 0.0, 6.0, 1.0];
310 let w = 3usize;
311 let mut sm = SlidingMax::with_window(w);
312 for (i, &v) in data.iter().enumerate() {
313 let got = sm.push(v).unwrap();
314 let start = i.saturating_sub(w - 1);
315 let exp = data[start..=i]
316 .iter()
317 .cloned()
318 .fold(f64::NEG_INFINITY, f64::max);
319 assert!((got - exp).abs() < 1e-15, "i={i} got={got} exp={exp}");
320 assert_eq!(sm.is_full(), i + 1 >= w);
321 }
322 }
323
324 #[test]
325 fn sliding_min_matches_scan() {
326 let data = [4.0, 2.0, 3.0, 1.0, 5.0, 0.5, 2.0];
327 let w = 4usize;
328 let mut sm = SlidingMin::with_window(w);
329 for (i, &v) in data.iter().enumerate() {
330 let got = sm.push(v).unwrap();
331 let start = i.saturating_sub(w - 1);
332 let exp = data[start..=i]
333 .iter()
334 .cloned()
335 .fold(f64::INFINITY, f64::min);
336 assert!((got - exp).abs() < 1e-15, "i={i} got={got} exp={exp}");
337 }
338 }
339}