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}