1use std::collections::VecDeque;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq)]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10pub struct Series<T> {
11 capacity: usize,
12 buffer: VecDeque<T>,
13}
14
15impl<T> Series<T> {
16 pub fn new(capacity: usize) -> Self {
18 Self {
19 capacity: capacity.max(1),
20 buffer: VecDeque::with_capacity(capacity.max(1)),
21 }
22 }
23
24 pub fn push(&mut self, value: T) {
26 if self.buffer.len() >= self.capacity {
27 self.buffer.pop_back();
28 }
29 self.buffer.push_front(value);
30 }
31
32 pub fn get(&self, offset: usize) -> Option<&T> {
34 self.buffer.get(offset)
35 }
36
37 pub fn latest(&self) -> Option<&T> {
39 self.buffer.front()
40 }
41
42 pub fn len(&self) -> usize {
44 self.buffer.len()
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.buffer.is_empty()
50 }
51
52 pub fn reset(&mut self) {
54 self.buffer.clear();
55 }
56
57 pub fn barssince<F>(&self, predicate: F) -> Option<usize>
59 where
60 F: Fn(&T) -> bool,
61 {
62 for (i, val) in self.buffer.iter().enumerate() {
63 if predicate(val) {
64 return Some(i);
65 }
66 }
67 None
68 }
69
70 pub fn valuewhen<F>(&self, predicate: F, occurrence: usize) -> Option<&T>
72 where
73 F: Fn(&T) -> bool,
74 {
75 let mut count = 0;
76 for val in self.buffer.iter() {
77 if predicate(val) {
78 if count == occurrence {
79 return Some(val);
80 }
81 count += 1;
82 }
83 }
84 None
85 }
86}
87
88impl Series<f64> {
89 pub fn highest(&self, n: usize) -> Option<f64> {
91 if n == 0 || self.buffer.is_empty() {
92 return None;
93 }
94 self.buffer
95 .iter()
96 .take(n)
97 .copied()
98 .filter(|v| v.is_finite())
99 .fold(None, |max, val| match max {
100 None => Some(val),
101 Some(m) => Some(m.max(val)),
102 })
103 }
104
105 pub fn lowest(&self, n: usize) -> Option<f64> {
107 if n == 0 || self.buffer.is_empty() {
108 return None;
109 }
110 self.buffer
111 .iter()
112 .take(n)
113 .copied()
114 .filter(|v| v.is_finite())
115 .fold(None, |min, val| match min {
116 None => Some(val),
117 Some(m) => Some(m.min(val)),
118 })
119 }
120
121 pub fn highestbars(&self, n: usize) -> Option<usize> {
123 if n == 0 || self.buffer.is_empty() {
124 return None;
125 }
126 let mut max_val = f64::NEG_INFINITY;
127 let mut max_idx = None;
128
129 for (i, &val) in self.buffer.iter().take(n).enumerate() {
130 if val.is_finite() && val > max_val {
131 max_val = val;
132 max_idx = Some(i);
133 }
134 }
135 max_idx
136 }
137
138 pub fn lowestbars(&self, n: usize) -> Option<usize> {
140 if n == 0 || self.buffer.is_empty() {
141 return None;
142 }
143 let mut min_val = f64::INFINITY;
144 let mut min_idx = None;
145
146 for (i, &val) in self.buffer.iter().take(n).enumerate() {
147 if val.is_finite() && val < min_val {
148 min_val = val;
149 min_idx = Some(i);
150 }
151 }
152 min_idx
153 }
154}
155
156pub struct SeriesEvents;
158
159impl SeriesEvents {
160 pub fn change(series: &Series<f64>) -> Option<f64> {
162 if let (Some(&curr), Some(&prev)) = (series.get(0), series.get(1)) {
163 Some(curr - prev)
164 } else {
165 None
166 }
167 }
168
169 pub fn rising(series: &Series<f64>, length: usize) -> bool {
171 if length == 0 || series.len() <= length {
172 return false;
173 }
174 for i in 0..length {
175 if let (Some(&curr), Some(&prev)) = (series.get(i), series.get(i + 1)) {
176 if curr <= prev {
177 return false;
178 }
179 } else {
180 return false;
181 }
182 }
183 true
184 }
185
186 pub fn falling(series: &Series<f64>, length: usize) -> bool {
188 if length == 0 || series.len() <= length {
189 return false;
190 }
191 for i in 0..length {
192 if let (Some(&curr), Some(&prev)) = (series.get(i), series.get(i + 1)) {
193 if curr >= prev {
194 return false;
195 }
196 } else {
197 return false;
198 }
199 }
200 true
201 }
202
203 pub fn cross(a: &Series<f64>, b: &Series<f64>) -> bool {
205 Self::crossover(a, b) || Self::crossunder(a, b)
206 }
207
208 pub fn crossover(a: &Series<f64>, b: &Series<f64>) -> bool {
210 if let (Some(&a0), Some(&a1), Some(&b0), Some(&b1)) =
211 (a.get(0), a.get(1), b.get(0), b.get(1))
212 {
213 a1 <= b1 && a0 > b0
214 } else {
215 false
216 }
217 }
218
219 pub fn crossunder(a: &Series<f64>, b: &Series<f64>) -> bool {
221 if let (Some(&a0), Some(&a1), Some(&b0), Some(&b1)) =
222 (a.get(0), a.get(1), b.get(0), b.get(1))
223 {
224 a1 >= b1 && a0 < b0
225 } else {
226 false
227 }
228 }
229
230 pub fn cum(series: &Series<f64>) -> f64 {
232 (0..series.len())
233 .filter_map(|i| series.get(i).copied())
234 .filter(|v| v.is_finite())
235 .sum()
236 }
237
238 pub fn value_when<'a, T>(
240 condition: &Series<bool>,
241 source: &'a Series<T>,
242 occurrence: usize,
243 ) -> Option<&'a T> {
244 let mut seen = 0;
245 for offset in 0..condition.len().min(source.len()) {
246 if condition.get(offset) == Some(&true) {
247 if seen == occurrence {
248 return source.get(offset);
249 }
250 seen += 1;
251 }
252 }
253 None
254 }
255}
256
257#[derive(Debug, Clone, Copy, PartialEq, Default)]
259pub struct CumulativeSum {
260 value: f64,
261}
262
263impl CumulativeSum {
264 pub fn update(&mut self, value: f64) -> f64 {
265 if value.is_finite() {
266 self.value += value;
267 }
268 self.value
269 }
270
271 pub fn value(&self) -> f64 {
272 self.value
273 }
274
275 pub fn reset(&mut self) {
276 self.value = 0.0;
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_series_basic_lookback() {
286 let mut s = Series::new(5);
287 s.push(10.0);
288 s.push(20.0);
289 s.push(30.0);
290
291 assert_eq!(s.get(0), Some(&30.0));
292 assert_eq!(s.get(1), Some(&20.0));
293 assert_eq!(s.get(2), Some(&10.0));
294 assert_eq!(s.get(3), None);
295
296 assert_eq!(s.highest(3), Some(30.0));
297 assert_eq!(s.lowest(3), Some(10.0));
298 assert_eq!(s.highestbars(3), Some(0));
299 assert_eq!(s.lowestbars(3), Some(2));
300 }
301
302 #[test]
303 fn test_series_events_crossover() {
304 let mut a = Series::new(5);
305 let mut b = Series::new(5);
306
307 a.push(10.0);
308 b.push(15.0);
309
310 a.push(20.0);
311 b.push(15.0);
312
313 assert!(SeriesEvents::crossover(&a, &b));
314 assert!(!SeriesEvents::crossunder(&a, &b));
315 }
316
317 #[test]
318 fn value_when_uses_a_separate_condition_series() {
319 let mut condition = Series::new(4);
320 let mut source = Series::new(4);
321 for (matches, value) in [(true, 10), (false, 20), (true, 30)] {
322 condition.push(matches);
323 source.push(value);
324 }
325 assert_eq!(SeriesEvents::value_when(&condition, &source, 0), Some(&30));
326 assert_eq!(SeriesEvents::value_when(&condition, &source, 1), Some(&10));
327 }
328
329 #[test]
330 fn cumulative_sum_survives_a_small_lookback_capacity() {
331 let mut cumulative = CumulativeSum::default();
332 assert_eq!(cumulative.update(1.0), 1.0);
333 assert_eq!(cumulative.update(2.0), 3.0);
334 assert_eq!(cumulative.update(3.0), 6.0);
335 }
336}