rust_ti/
basic_indicators.rs

1//! # Basic Indicators
2//!
3//! `basic_indicators` are simple functions that perform simple calculations on prices.
4//!
5//! The primary purpose of these functions is to be reused by other functions.
6//!
7//! ## Bulk
8//!
9//! * [`absolute_deviation`](bulk::absolute_deviation) - Calculates the absolute deviation
10//! * [`log`](bulk::log) - Calculates the natural logrithm of slice of prices
11//! * [`log_difference`](bulk::log_difference) - Calculates the difference between the natural logarithm
12//! at t and t-1
13//! * [`mean`](bulk::mean) - Calculates the mean (average) of a slice of prices
14//! * [`median`](bulk::median) - Calculates the median (middle value) of a slice of prices
15//! * [`mode`](bulk::mode) - Calculates the mode (most common price) of a slice of prices
16//! * [`standard_deviation`](bulk::standard_deviation) - Calculates the standard deviation of a slice of prices
17//! * [`variance`](bulk::variance) - Calculates the variance of slice of prices
18//!
19//! ## Single
20//!
21//! * [`absolute_deviation`](single::absolute_deviation) - Calculates the absolute deviation
22//! * [`log_difference`](single::log_difference) - Calculates the difference between the natural logarithm
23//! at t and t-1
24//! * [`max`](single::max) - Calculates the maximum of a slice of prices
25//! * [`mean`](single::mean) - Calculates the mean (average) of a slice of prices
26//! * [`median`](single::median) - Calculates the median (middle value) of a slice of prices
27//! * [`min`](single::min) - Calculates the minimum of a slice of prices
28//! * [`mode`](single::mode) - Calculates the mode (most common price) of a slice of prices
29//! * [`standard_deviation`](single::standard_deviation) - Calculates the standard deviation of a slice of prices
30//! * [`variance`](single::variance) - Calculates the variance of slice of prices
31//!
32
33/// `single` module holds functions that return a singular value
34pub mod single {
35    use std::cmp::Ordering;
36    use std::collections::HashMap;
37    /// Calculates the mean (average) of a slice of prices and returns it as an `f64`
38    ///
39    /// # Arguments
40    ///
41    /// * `prices` - Slice of prices
42    ///
43    /// # Panics
44    ///
45    /// The fuction will panic if given an empty `prices` slice
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
51    /// let mean = rust_ti::basic_indicators::single::mean(&prices);
52    /// assert_eq!(101.5, mean);
53    /// ```
54    pub fn mean(prices: &[f64]) -> f64 {
55        if prices.is_empty() {
56            panic!("Prices ({:?}) is empty", prices);
57        };
58        let sum: f64 = prices.iter().sum();
59        return sum / prices.len() as f64;
60    }
61
62    /// Calculates the median (middle value) of a slice of prices and returns it as an `f64`.
63    ///
64    /// `median` orders the numbers and takes the middle value. If the number of prices is even it
65    /// will take the average of the two middle values.
66    ///
67    /// # Argument
68    ///
69    /// * `prices` - Slice of prices
70    ///
71    /// # Panics
72    ///
73    /// The fuction will panic if given an empty `prices` slice
74    ///
75    /// # Examples
76    ///
77    /// ```rust
78    /// // Odd number of prices
79    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
80    /// let median = rust_ti::basic_indicators::single::median(&prices);
81    /// assert_eq!(101.0, median);
82    ///
83    /// // Even number of prices
84    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
85    /// let median = rust_ti::basic_indicators::single::median(&prices);
86    /// assert_eq!(101.5, median);
87    /// ```
88    pub fn median(prices: &[f64]) -> f64 {
89        if prices.is_empty() {
90            panic!("Prices ({:?}) is empty", prices);
91        };
92
93        let mut ordered_prices = prices
94            .iter()
95            .filter_map(|f| if f.is_nan() { None } else { Some(*f) })
96            .collect::<Vec<f64>>();
97        ordered_prices.sort_by(cmp_f64);
98        let middle: usize = prices.len() / 2;
99        if prices.len() % 2 == 0 {
100            return mean(&ordered_prices[middle - 1..middle + 1]);
101        };
102
103        return ordered_prices[middle];
104    }
105
106    /// Calculates the mode (most common price) of a slice of prices.
107    ///
108    /// `mode` will round the numbers to get most frequently occuring integer.
109    ///
110    /// If it finds multiple prices that occur an equal number of times it will return the average of those
111    /// numbers.
112    ///
113    /// # Arguments
114    ///
115    /// * `prices` - Slice of prices
116    ///
117    /// # Panics
118    ///
119    /// The fuction will panic if given an empty `prices` slice
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// let prices = vec![100.0, 102.0, 101.0, 101.0, 100.0];
125    /// let mode = rust_ti::basic_indicators::single::mode(&prices);
126    /// assert_eq!(100.5, mode);
127    ///
128    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
129    /// let mode = rust_ti::basic_indicators::single::mode(&prices);
130    /// assert_eq!(100.0, mode);
131    /// ```
132    pub fn mode(prices: &[f64]) -> f64 {
133        if prices.is_empty() {
134            panic!("Prices ({:?}) is empty", prices);
135        };
136
137        let rounded_prices = prices.iter().map(|x| x.round() as i64).collect();
138        return most_frequent(rounded_prices);
139    }
140
141    /// Calculates the difference between the natural logarithm at t and t-1
142    ///
143    /// # Arguments
144    ///
145    /// * `price_t` - `&f64` price at t
146    /// * `price_t_1` - `&f64` price at t-1
147    ///
148    /// # Panics
149    ///
150    /// If `price_t` or `price_t_1` is less or equal to 0.0
151    ///
152    /// # Examples
153    ///
154    /// ```rust
155    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
156    /// let log_difference = rust_ti::basic_indicators::single::log_difference(&prices[3], &prices[2]);
157    /// assert_eq!(-0.01960847138837618, log_difference);
158    /// ```
159    pub fn log_difference(price_t: &f64, price_t_1: &f64) -> f64 {
160        if price_t <= &0.0 || price_t_1 <= &0.0 {
161            panic!(
162                "price_t ({}) and price_t_1 ({}) need to be greater than 0.0",
163                price_t, price_t_1
164            );
165        }
166        return price_t.ln() - price_t_1.ln();
167    }
168
169    /// Calculates the variance of slice of prices
170    ///
171    /// Assumes a normal distribution
172    ///
173    /// # Arguments
174    ///
175    /// * `prices` - Slice of prices
176    ///
177    /// # Panics
178    ///
179    /// The function will panic if `prices` is empty
180    ///
181    /// # Examples
182    ///
183    /// ```rust
184    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
185    /// let variance = rust_ti::basic_indicators::single::variance(&prices);
186    /// assert_eq!(1.25, variance);
187    /// ```
188    // TODO: Allow for distributions other than normal distributions
189    pub fn variance(prices: &[f64]) -> f64 {
190        if prices.is_empty() {
191            panic!("Prices ({:?}) is empty", prices);
192        }
193        let prices_mean = mean(prices);
194        let mean_diff_sq: Vec<f64> = prices.iter().map(|x| (x - prices_mean).powi(2)).collect();
195        return mean(&mean_diff_sq);
196    }
197
198    /// Calculates the standard deviation of slice of prices
199    ///
200    /// Assumes a normal distribution
201    ///
202    /// # Arguments
203    ///
204    /// * `prices` - Slice of prices
205    ///
206    /// # Panics
207    ///
208    /// The function will panic if `prices` is empty
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
214    /// let standard_deviation = rust_ti::basic_indicators::single::standard_deviation(&prices);
215    /// assert_eq!(1.118033988749895, standard_deviation);
216    /// ```
217    // TODO: Allow for distributions other than normal distributions
218    pub fn standard_deviation(prices: &[f64]) -> f64 {
219        let variance = variance(prices);
220        return variance.sqrt();
221    }
222
223    /// Calculates the absolute deviation from the mean, median, or mode.
224    ///
225    /// # Arguments
226    ///
227    /// * `prices` - A `f64` slice of prices
228    /// * `central_point` - A variant of the [`CentralPoint`](crate::CentralPoint) enum
229    ///
230    /// # Panics
231    ///
232    /// The function will panic if `prices` is empty
233    ///
234    /// # Examples
235    ///
236    /// ```rust
237    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
238    /// let mean_absolute_deviation = rust_ti::basic_indicators::single::absolute_deviation(&prices, &rust_ti::CentralPoint::Mean);
239    /// // The answer is `1.04` but due to how Rust implements `f64` `1.0400000000000005` gets
240    /// // returned which should be negligible.
241    /// assert_eq!(1.0400000000000005, mean_absolute_deviation);
242    ///
243    /// let median_absolute_deviation = rust_ti::basic_indicators::single::absolute_deviation(&prices, &rust_ti::CentralPoint::Median);
244    /// assert_eq!(1.0, median_absolute_deviation);
245    ///
246    /// let mode_absolute_deviation = rust_ti::basic_indicators::single::absolute_deviation(&prices, &rust_ti::CentralPoint::Mode);
247    /// assert_eq!(1.2, mode_absolute_deviation);
248    /// ```
249    pub fn absolute_deviation(prices: &[f64], central_point: &crate::CentralPoint) -> f64 {
250        if prices.is_empty() {
251            panic!("Prices is empty")
252        };
253        let mid_point = match central_point {
254            crate::CentralPoint::Mean => mean(prices),
255            crate::CentralPoint::Median => median(prices),
256            crate::CentralPoint::Mode => mode(prices),
257            _ => panic!("Unsupported central_point provided"), // TODO: add debug to Central point
258                                                               // so the panic can provide it
259        };
260        let deviation: f64 = prices.iter().map(|x| (x - mid_point).abs()).sum();
261        return deviation / prices.len() as f64;
262    }
263
264    /// Calculates the maximum of a slice of prices
265    ///
266    /// Max doesn't currently exist in Rust for `f64`
267    ///
268    /// # Arguments
269    ///
270    /// * `prices` - Slice of prices
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
276    /// let max = rust_ti::basic_indicators::single::max(&prices);
277    /// assert_eq!(103.0, max);
278    /// ```
279    pub fn max(prices: &[f64]) -> f64 {
280        if prices.is_empty() {
281            panic!("Prices is empty")
282        };
283        let mut ordered_prices = prices
284            .iter()
285            .filter_map(|f| if f.is_nan() { None } else { Some(*f) })
286            .collect::<Vec<f64>>();
287        ordered_prices.sort_by(cmp_f64);
288        return ordered_prices[ordered_prices.len() - 1];
289    }
290
291    /// Calculates the minimum of a slice of prices
292    ///
293    /// Min doesn't currently exist in Rust for `f64`
294    ///
295    /// # Arguments
296    ///
297    /// * `prices` - Slice of prices
298    ///
299    /// # Examples
300    ///
301    /// ```rust
302    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
303    /// let min = rust_ti::basic_indicators::single::min(&prices);
304    /// assert_eq!(100.0, min);
305    /// ```
306    pub fn min(prices: &[f64]) -> f64 {
307        if prices.is_empty() {
308            panic!("Prices is empty")
309        };
310        let mut ordered_prices = prices
311            .iter()
312            .filter_map(|f| if f.is_nan() { None } else { Some(*f) })
313            .collect::<Vec<f64>>();
314        ordered_prices.sort_by(cmp_f64);
315        return ordered_prices[0];
316    }
317
318    fn cmp_f64(a: &f64, b: &f64) -> Ordering {
319        if a < b {
320            return Ordering::Less;
321        } else if a > b {
322            return Ordering::Greater;
323        }
324        return Ordering::Equal;
325    }
326
327    // TODO: Surely this can be improved
328    //     sorting could eventually be done by f64 sort_floats method once it is no longer
329    //     experimental
330    fn most_frequent(vector: Vec<i64>) -> f64 {
331        let mut map: HashMap<i64, usize> = HashMap::new();
332        for x in vector {
333            *map.entry(x as i64).or_default() += 1;
334        }
335        let mut max_price = vec![0];
336        let mut max_count = usize::MIN;
337        for (key, value) in map {
338            if value > max_count {
339                max_count = value;
340                max_price = vec![key];
341            } else if value == max_count {
342                max_price.push(key);
343            } else {
344                continue;
345            }
346        }
347        if max_price.len() > 1 {
348            return max_price.iter().sum::<i64>() as f64 / max_price.len() as f64;
349        }
350        return max_price[0] as f64;
351    }
352}
353
354/// `bulk` module holds functions that return multiple values
355pub mod bulk {
356    use crate::basic_indicators::single;
357    /// Calculates the mean (averages) of a slice of prices for a provided period and returns
358    /// them as a `vector` of `f64`
359    ///
360    /// # Arguments
361    ///
362    /// * `prices` - Slice of prices
363    /// * `period` - Period over which to calculate the mean
364    ///
365    /// # Panics
366    ///
367    /// The fuction will panic if `period` is greater than length of `prices`
368    ///
369    /// # Examples
370    ///
371    /// ```rust
372    /// let prices = vec![101.0, 102.0, 103.0, 101.0];
373    /// let period: usize = 3;
374    /// let mean = rust_ti::basic_indicators::bulk::mean(&prices, &period);
375    /// assert_eq!(vec![102.0, 102.0], mean);
376    /// ```
377    pub fn mean(prices: &[f64], period: &usize) -> Vec<f64> {
378        let length = prices.len();
379
380        if period > &length {
381            panic!(
382                "Period ({}) cannot be longer than the length of provided prices ({})",
383                period, length
384            );
385        };
386
387        let mut means = Vec::new();
388        for i in 0..length {
389            let end_index = period + i;
390            if end_index > length {
391                break;
392            }
393            means.push(single::mean(&prices[i..end_index]));
394        }
395        return means;
396    }
397
398    /// Calculates the median (middle value) of a slice of prices and returns it as an f64.
399    ///
400    /// `median` orders the numbers and takes the middle value. If the number of prices is even it will take the average of the two middle values.
401    ///
402    /// # Arguments
403    ///
404    /// * `prices` - Slice of prices
405    /// * `period` - Period over which to calculate the median
406    ///
407    /// # Panics
408    ///
409    /// The fuction will panic if `period` is greater than length of `prices`
410    ///
411    /// # Examples
412    ///
413    /// ```rust
414    /// let prices = vec![101.0, 102.0, 103.0, 101.0];
415    /// let period: usize = 3;
416    /// let median = rust_ti::basic_indicators::bulk::median(&prices, &period);
417    /// assert_eq!(vec![102.0, 102.0], median);
418    /// ```
419    pub fn median(prices: &[f64], period: &usize) -> Vec<f64> {
420        let length = prices.len();
421
422        if period > &length {
423            panic!(
424                "Period ({}) cannot be longer than the length of provided prices ({})",
425                period, length
426            );
427        };
428
429        let mut medians = Vec::new();
430        for i in 0..length {
431            let end_index = period + i;
432            if end_index > length {
433                break;
434            }
435            medians.push(single::median(&prices[i..end_index]));
436        }
437        return medians;
438    }
439
440    /// Calculates the mode (most common price) of a slice of prices.
441    ///
442    /// `mode` will round the numbers to get most frequently occuring integer.
443    ///
444    /// If it finds multiple prices that occur an equal number of times it will the average of those
445    /// numbers.
446    ///
447    /// # Arguments
448    ///
449    /// * `prices` - Slice of prices
450    /// * `period` - Period over which to calculate the mode
451    ///
452    /// # Panics
453    ///
454    /// The fuction will panic if `period` is greater than length of `prices`
455    ///
456    /// # Examples
457    ///
458    /// ```rust
459    /// let prices = vec![101.0, 102.0, 101.0, 102.0];
460    /// let period: usize = 3;
461    /// let mode = rust_ti::basic_indicators::bulk::mode(&prices, &period);
462    /// assert_eq!(vec![101.0, 102.0], mode);
463    /// ```
464    pub fn mode(prices: &[f64], period: &usize) -> Vec<f64> {
465        let length = prices.len();
466
467        if period > &length {
468            panic!(
469                "Period ({}) cannot be longer than the length of provided prices ({})",
470                period, length
471            );
472        };
473
474        let mut modes = Vec::new();
475        for i in 0..length {
476            let end_index = period + i;
477            if end_index > length {
478                break;
479            }
480            modes.push(single::mode(&prices[i..end_index]));
481        }
482        return modes;
483    }
484
485    /// Calculates the natural logrithm of slice of prices
486    ///
487    /// # Arguments
488    ///
489    /// * `prices` - Slice of prices
490    ///
491    /// # Panics
492    ///
493    /// The function will panic if passed in an empty `prices` slice
494    ///
495    /// # Examples
496    ///
497    /// ```rust
498    /// let prices = vec![101.0, 102.0, 103.0, 101.0];
499    /// let log = rust_ti::basic_indicators::bulk::log(&prices);
500    /// assert_eq!(vec![4.61512051684126, 4.624972813284271, 4.634728988229636, 4.61512051684126], log);
501    /// ```
502    pub fn log(prices: &[f64]) -> Vec<f64> {
503        if prices.len() < 1 {
504            panic!("Prices ({:?}) is empty", prices);
505        }
506
507        let mut logs = Vec::new();
508        for price in prices {
509            logs.push(price.ln());
510        }
511        return logs;
512    }
513
514    /// Calculates the difference between the natural logarithm at t and t-1
515    ///
516    /// # Arguments
517    ///
518    /// * `prices` - Slice of prices
519    ///
520    /// # Panics
521    ///
522    /// The function will panic if passed an empty slice
523    ///
524    /// # Examples
525    ///
526    /// ```rust
527    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
528    /// let log_difference = rust_ti::basic_indicators::bulk::log_difference(&prices);
529    /// assert_eq!(vec![0.019802627296178876, 0.009756174945365181, -0.01960847138837618], log_difference);
530    /// ```
531    pub fn log_difference(prices: &[f64]) -> Vec<f64> {
532        let length = prices.len();
533        if length < 1 {
534            panic!("Prices ({:?}) is empty", prices);
535        }
536
537        let mut log_diffs = Vec::new();
538        for i in 0..length {
539            let end_index = i + 1;
540            if end_index >= length {
541                break;
542            }
543            log_diffs.push(single::log_difference(&prices[end_index], &prices[i]));
544        }
545        return log_diffs;
546    }
547
548    /// Calculates the variance of slice of prices
549    ///
550    /// Assumes a normal distribution
551    ///
552    /// # Arguments
553    ///
554    /// * `prices` - Slice of prices
555    /// * `period` - Period over which to calculate the variance
556    ///
557    /// # Panics
558    ///
559    /// The function will panic if `period` is greater than the length of `prices`
560    ///
561    /// # Examples
562    ///
563    /// ```rust
564    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
565    /// let period: usize = 3;
566    /// let variance = rust_ti::basic_indicators::bulk::variance(&prices, &period);
567    /// assert_eq!(vec![1.5555555555555556, 0.6666666666666666], variance);
568    /// ```
569    // TODO: Allow for distributions other than normal distributions
570    pub fn variance(prices: &[f64], period: &usize) -> Vec<f64> {
571        let length = prices.len();
572        if period > &length {
573            panic!(
574                "Period ({}) cannot be longer than the length of provided prices ({})",
575                period, length
576            );
577        };
578        let mut variances = Vec::new();
579        for i in 0..length {
580            let end_index = period + i;
581            if end_index > length {
582                break;
583            }
584            variances.push(single::variance(&prices[i..end_index]));
585        }
586        return variances;
587    }
588
589    /// Calculates the standard deviation of a slice of prices
590    ///
591    /// Assumes a normal distribution
592    ///
593    /// # Arguments
594    ///
595    /// * `prices` - Slice of prices
596    /// * `period` - Period over which to calculate the standard deviation
597    ///
598    /// # Panics
599    ///
600    /// The function will panic if `period` is greater than the length of `prices`
601    ///
602    /// # Examples
603    ///
604    /// ```rust
605    /// let prices = vec![100.0, 102.0, 103.0, 101.0];
606    /// let period: usize = 3;
607    /// let standard_deviation = rust_ti::basic_indicators::bulk::standard_deviation(&prices, &period);
608    /// assert_eq!(vec![1.247219128924647, 0.816496580927726], standard_deviation);
609    /// ```
610    // TODO: Allow for distributions other than normal distributions
611    pub fn standard_deviation(prices: &[f64], period: &usize) -> Vec<f64> {
612        let length = prices.len();
613        if period > &length {
614            panic!(
615                "Period ({}) cannot be longer than the length of provided prices ({})",
616                period, length
617            );
618        };
619        let mut stddevs = Vec::new();
620        for i in 0..length {
621            let end_index = period + i;
622            if end_index > length {
623                break;
624            }
625            stddevs.push(single::standard_deviation(&prices[i..end_index]));
626        }
627        return stddevs;
628    }
629
630    /// Calculates the absolute deviation from the mean, median, or mode over a provided period.
631    ///
632    /// # Arguments
633    ///
634    /// * `prices` - Slice of prices
635    /// * `period` - Period over which to calculate the standard deviation
636    /// * `central_point` - A variant of the [`CentralPoint`](crate::CentralPoint) enum
637    ///
638    /// # Panics
639    ///
640    /// The function will panic if `period` is longer than the length of `prices`
641    ///
642    /// # Examples
643    ///
644    /// ```rust
645    /// let prices = vec![100.0, 102.0, 103.0, 101.0, 100.0];
646    /// let period: usize = 3;
647    ///
648    /// let mean_absolute_deviation = rust_ti::basic_indicators::bulk::absolute_deviation(&prices, &period, &rust_ti::CentralPoint::Mean);
649    /// assert_eq!(vec![1.1111111111111096, 0.6666666666666666, 1.1111111111111096], mean_absolute_deviation);
650    ///
651    /// let median_absolute_deviation = rust_ti::basic_indicators::bulk::absolute_deviation(&prices, &period, &rust_ti::CentralPoint::Median);
652    /// assert_eq!(vec![1.0, 0.6666666666666666, 1.0], median_absolute_deviation);
653    ///
654    /// let mode_absolute_deviation = rust_ti::basic_indicators::bulk::absolute_deviation(&prices, &period, &rust_ti::CentralPoint::Mode);
655    /// assert_eq!(vec![1.1111111111111096, 0.6666666666666666, 1.1111111111111096], mode_absolute_deviation);
656    /// ```
657    pub fn absolute_deviation(
658        prices: &[f64],
659        period: &usize,
660        central_point: &crate::CentralPoint,
661    ) -> Vec<f64> {
662        let length = prices.len();
663        if period > &length {
664            panic!(
665                "Period ({}) cannot be longer than the length of provided prices ({})",
666                period, length
667            );
668        };
669        let mut absolute_deviations = Vec::new();
670        for i in 0..length {
671            let end_index = period + i;
672            if end_index > length {
673                break;
674            }
675            absolute_deviations.push(single::absolute_deviation(
676                &prices[i..end_index],
677                central_point,
678            ));
679        }
680        return absolute_deviations;
681    }
682}
683
684#[cfg(test)]
685mod tests {
686    use super::*;
687
688    #[test]
689    fn single_mean() {
690        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
691        assert_eq!(100.352, single::mean(&prices));
692    }
693
694    #[test]
695    fn single_mean_identical_prices() {
696        let prices = vec![100.0, 100.0, 100.0];
697        assert_eq!(100.0, single::mean(&prices));
698    }
699
700    #[test]
701    #[should_panic]
702    fn single_mean_empty_prices() {
703        let prices = Vec::new();
704        single::mean(&prices);
705    }
706
707    #[test]
708    fn bulk_mean() {
709        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
710        let period: usize = 3;
711        assert_eq!(
712            vec![100.39666666666666, 100.45666666666666, 100.36666666666667],
713            bulk::mean(&prices, &period)
714        );
715    }
716
717    #[test]
718    #[should_panic]
719    fn bulk_mean_panic() {
720        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
721        let period: usize = 30;
722        bulk::mean(&prices, &period);
723    }
724
725    #[test]
726    fn single_median_odd() {
727        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
728        assert_eq!(100.38, single::median(&prices));
729    }
730
731    #[test]
732    fn single_median_even() {
733        let prices = vec![100.2, 100.46, 100.53, 100.38];
734        // Should be
735        // assert_eq!(100.42, single::median(&prices));
736        // but due to how floating points are calculated we have to assert on
737        assert_eq!(100.41999999999999, single::median(&prices));
738    }
739
740    #[test]
741    #[should_panic]
742    fn single_median_panic() {
743        let prices = Vec::new();
744        single::median(&prices);
745    }
746
747    #[test]
748    fn bulk_median() {
749        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
750        let period: usize = 3;
751        assert_eq!(vec![100.46, 100.46, 100.38], bulk::median(&prices, &period));
752    }
753
754    #[test]
755    #[should_panic]
756    fn bulk_median_panic() {
757        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
758        let period: usize = 30;
759        bulk::median(&prices, &period);
760    }
761
762    #[test]
763    fn single_mode_round_up() {
764        let prices = vec![100.2, 100.46, 100.53, 101.08, 101.19];
765        assert_eq!(101.0, single::mode(&prices));
766    }
767
768    #[test]
769    fn single_mode_round_down() {
770        let prices = vec![100.2, 100.46, 100.35, 101.08, 101.19];
771        assert_eq!(100.0, single::mode(&prices));
772    }
773
774    #[test]
775    fn single_mode_average() {
776        let prices = vec![100.46, 100.35, 101.08, 101.19];
777        assert_eq!(100.5, single::mode(&prices));
778    }
779
780    #[test]
781    #[should_panic]
782    fn single_mode_panic() {
783        let prices = Vec::new();
784        single::mode(&prices);
785    }
786
787    #[test]
788    fn bulk_mode() {
789        let prices = vec![100.2, 100.46, 100.53, 101.08, 101.19];
790        let period: usize = 3;
791        assert_eq!(vec![100.0, 101.0, 101.0], bulk::mode(&prices, &period));
792    }
793
794    #[test]
795    #[should_panic]
796    fn bulk_mode_panic() {
797        let prices = vec![100.2, 100.46, 100.53, 101.08, 101.19];
798        let period: usize = 30;
799        bulk::mode(&prices, &period);
800    }
801
802    #[test]
803    fn bulk_log() {
804        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
805        assert_eq!(
806            vec![
807                4.607168188650764,
808                4.609759638321899,
809                4.610456190417329,
810                4.608962984226787,
811                4.607068383271171
812            ],
813            bulk::log(&prices)
814        );
815    }
816
817    #[test]
818    #[should_panic]
819    fn bulk_log_panic() {
820        let prices = Vec::new();
821        bulk::log(&prices);
822    }
823
824    #[test]
825    fn single_log_difference() {
826        assert_eq!(
827            -0.0018946009556159993,
828            single::log_difference(&100.19, &100.38)
829        );
830    }
831
832    #[test]
833    #[should_panic]
834    fn single_log_difference_panic() {
835        single::log_difference(&0.0, &100.38);
836    }
837
838    #[test]
839    #[should_panic]
840    fn single_log_difference_panic_2() {
841        single::log_difference(&100.19, &-100.38);
842    }
843
844    #[test]
845    fn bulk_log_difference() {
846        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
847        assert_eq!(
848            vec![
849                0.0025914496711347823,
850                0.0006965520954302917,
851                -0.0014932061905419403,
852                -0.0018946009556159993
853            ],
854            bulk::log_difference(&prices)
855        );
856    }
857
858    #[test]
859    #[should_panic]
860    fn bulk_log_difference_difference() {
861        bulk::log_difference(&Vec::new());
862    }
863
864    #[test]
865    fn single_variance() {
866        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
867        assert_eq!(0.018695999999999734, single::variance(&prices));
868    }
869
870    #[test]
871    #[should_panic]
872    fn single_variance_panic() {
873        let prices = Vec::new();
874        single::variance(&prices);
875    }
876
877    #[test]
878    fn bulk_variance() {
879        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
880        let period = 3;
881        assert_eq!(
882            vec![
883                0.02015555555555502,
884                0.0037555555555558295,
885                0.019355555555555907
886            ],
887            bulk::variance(&prices, &period)
888        );
889    }
890
891    #[test]
892    #[should_panic]
893    fn bulk_variance_panic() {
894        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
895        let period = 30;
896        bulk::variance(&prices, &period);
897    }
898
899    #[test]
900    fn single_standard_deviation() {
901        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
902        assert_eq!(0.1367333170810967, single::standard_deviation(&prices));
903    }
904
905    #[test]
906    #[should_panic]
907    fn single_standard_deviation_panic() {
908        let prices = Vec::new();
909        single::standard_deviation(&prices);
910    }
911
912    #[test]
913    fn bulk_standard_deviation() {
914        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
915        let period = 3;
916        assert_eq!(
917            vec![
918                0.14197026292697715,
919                0.06128258770283635,
920                0.13912424503139598
921            ],
922            bulk::standard_deviation(&prices, &period)
923        );
924    }
925
926    #[test]
927    #[should_panic]
928    fn bulk_standard_deviation_panic() {
929        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
930        let period = 30;
931        bulk::standard_deviation(&prices, &period);
932    }
933
934    #[test]
935    fn single_absolute_deviation() {
936        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
937        assert_eq!(
938            0.12559999999999719,
939            single::absolute_deviation(&prices, &crate::CentralPoint::Mean)
940        );
941        assert_eq!(
942            0.11999999999999886,
943            single::absolute_deviation(&prices, &crate::CentralPoint::Median)
944        );
945        assert_eq!(
946            0.3519999999999982,
947            single::absolute_deviation(&prices, &crate::CentralPoint::Mode)
948        );
949    }
950
951    #[test]
952    #[should_panic]
953    fn singe_absolute_deviation_panic() {
954        let prices = Vec::new();
955        single::absolute_deviation(&prices, &crate::CentralPoint::Mean);
956    }
957
958    #[test]
959    fn bulk_absolute_deviation() {
960        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
961        let period: usize = 3;
962
963        assert_eq!(
964            vec![
965                0.1311111111111103,
966                0.051111111111111995,
967                0.11777777777777487
968            ],
969            bulk::absolute_deviation(&prices, &period, &crate::CentralPoint::Mean)
970        );
971        assert_eq!(
972            vec![0.10999999999999943, 0.0500000000000019, 0.11333333333333447],
973            bulk::absolute_deviation(&prices, &period, &crate::CentralPoint::Median)
974        );
975        assert_eq!(
976            vec![0.3966666666666659, 0.45666666666666345, 0.36666666666666475],
977            bulk::absolute_deviation(&prices, &period, &crate::CentralPoint::Mode)
978        );
979    }
980
981    #[test]
982    #[should_panic]
983    fn bulk_absolute_deviation_panic() {
984        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
985        let period: usize = 30;
986        bulk::absolute_deviation(&prices, &period, &crate::CentralPoint::Mean);
987    }
988
989    #[test]
990    fn test_single_max() {
991        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
992        assert_eq!(100.53, single::max(&prices));
993    }
994
995    #[test]
996    #[should_panic]
997    fn test_single_max_panic() {
998        let prices = Vec::new();
999        single::max(&prices);
1000    }
1001
1002    #[test]
1003    fn test_single_min() {
1004        let prices = vec![100.2, 100.46, 100.53, 100.38, 100.19];
1005        assert_eq!(100.19, single::min(&prices));
1006    }
1007
1008    #[test]
1009    #[should_panic]
1010    fn test_single_min_panic() {
1011        let prices = Vec::new();
1012        single::min(&prices);
1013    }
1014}