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}