indexes_rs/v1/momentum/main.rs
1//! # Momentum Indicator Module
2//!
3//! This module implements a Momentum indicator.
4//! The Momentum indicator calculates the difference between the current price and the price
5//! from a specified number of periods ago. In addition, it computes a momentum ratio, defined as
6//! the current price as a percentage of the past price.
7//!
8//! # Examples
9//!
10//! ```rust
11//! use indexes_rs::v1::momentum::main::Momentum;
12//! use indexes_rs::v1::momentum::types::MomentumResult;
13//!
14//! let mut momentum = Momentum::new(14);
15//! let prices = vec![100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0, 108.0, 109.0, 110.0, 111.0, 112.0, 113.0];
16//!
17//! // After feeding in at least 14 prices, calculate() returns a result.
18//! if let Some(result) = momentum.calculate(113.0) {
19//! println!("Momentum: {:.2}", result.value);
20//! println!("Momentum Ratio: {:.2}%", result.ratio);
21//! }
22//! ```
23
24use super::types::MomentumResult;
25use std::collections::VecDeque;
26
27/// A Momentum indicator that calculates the change between the current price and the price
28/// from a given number of periods ago.
29pub struct Momentum {
30 /// The period over which to calculate momentum.
31 period: usize,
32 /// A sliding window of recent prices.
33 values: VecDeque<f64>,
34}
35
36impl Momentum {
37 /// The default period for the Momentum indicator.
38 pub const DEFAULT_PERIOD: usize = 14;
39
40 /// Creates a new `Momentum` indicator with the specified period.
41 ///
42 /// # Arguments
43 ///
44 /// * `period` - The number of periods over which to calculate momentum.
45 ///
46 /// # Example
47 ///
48 /// ```rust
49 /// use indexes_rs::v1::momentum::main::Momentum;
50 ///
51 /// let momentum = Momentum::new(14);
52 /// ```
53 pub fn new(period: usize) -> Self {
54 Momentum {
55 period,
56 values: VecDeque::with_capacity(period),
57 }
58 }
59
60 /// Calculates the current momentum and momentum ratio.
61 ///
62 /// The momentum is computed as the difference between the current price and the price from `period` periods ago.
63 /// The momentum ratio is computed as `(current_price / past_price) * 100.0`.
64 ///
65 /// # Arguments
66 ///
67 /// * `price` - The latest price.
68 ///
69 /// # Returns
70 ///
71 /// * `Some(MomentumResult)` if there are enough data points and the past price is not zero.
72 /// * `None` if there aren't enough values or if the past price is zero (to avoid division by zero).
73 ///
74 /// # Example
75 ///
76 /// ```rust
77 /// use indexes_rs::v1::momentum::main::Momentum;
78 /// use indexes_rs::v1::momentum::types::MomentumResult;
79 ///
80 /// let mut momentum = Momentum::new(3);
81 /// momentum.calculate(100.0); // first value, not enough data
82 /// momentum.calculate(102.0);
83 /// if let Some(result) = momentum.calculate(104.0) {
84 /// // Past price is 100.0, so momentum = 104.0 - 100.0 = 4.0
85 /// // and momentum ratio = (104.0 / 100.0) * 100 = 104.0%
86 /// assert_eq!(result.value, 4.0);
87 /// assert_eq!(result.ratio, 104.0);
88 /// }
89 /// ```
90 pub fn calculate(&mut self, price: f64) -> Option<MomentumResult> {
91 self.values.push_back(price);
92 if self.values.len() > self.period {
93 self.values.pop_front();
94 }
95 if self.values.len() < self.period {
96 return None;
97 }
98 let past_price = *self.values.front()?;
99 if past_price == 0.0 {
100 return None; // Avoid division by zero.
101 }
102 let momentum = price - past_price;
103 let momentum_ratio = (price / past_price) * 100.0;
104 Some(MomentumResult {
105 value: momentum,
106 ratio: momentum_ratio,
107 })
108 }
109}