ta/indicators/
relative_strength_index.rs

1use std::fmt;
2
3use crate::errors::Result;
4use crate::indicators::ExponentialMovingAverage as Ema;
5use crate::{Close, Next, Period, Reset};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9/// The relative strength index (RSI).
10///
11/// It is a momentum oscillator,
12/// that compares the magnitude of recent gains
13/// and losses over a specified time period to measure speed and change of price
14/// movements of a security. It is primarily used to attempt to identify
15/// overbought or oversold conditions in the trading of an asset.
16///
17/// The oscillator returns output in the range of 0..100.
18///
19/// ![RSI](https://upload.wikimedia.org/wikipedia/commons/6/67/RSIwiki.gif)
20///
21/// # Formula
22///
23/// RSI<sub>t</sub> = EMA<sub>Ut</sub> * 100 / (EMA<sub>Ut</sub> + EMA<sub>Dt</sub>)
24///
25/// Where:
26///
27/// * RSI<sub>t</sub> - value of RSI indicator in a moment of time _t_
28/// * EMA<sub>Ut</sub> - value of [EMA](struct.ExponentialMovingAverage.html) of up periods in a moment of time _t_
29/// * EMA<sub>Dt</sub> - value of [EMA](struct.ExponentialMovingAverage.html) of down periods in a moment of time _t_
30///
31/// If current period has value higher than previous period, than:
32///
33/// U = p<sub>t</sub> - p<sub>t-1</sub>
34///
35/// D = 0
36///
37/// Otherwise:
38///
39/// U = 0
40///
41/// D = p<sub>t-1</sub> - p<sub>t</sub>
42///
43/// Where:
44///
45/// * U = up period value
46/// * D = down period value
47/// * p<sub>t</sub> - input value in a moment of time _t_
48/// * p<sub>t-1</sub> - input value in a moment of time _t-1_
49///
50/// # Parameters
51///
52/// * _period_ - number of periods (integer greater than 0). Default value is 14.
53///
54/// # Example
55///
56/// ```
57/// use ta::indicators::RelativeStrengthIndex;
58/// use ta::Next;
59///
60/// let mut rsi = RelativeStrengthIndex::new(3).unwrap();
61/// assert_eq!(rsi.next(10.0), 50.0);
62/// assert_eq!(rsi.next(10.5).round(), 86.0);
63/// assert_eq!(rsi.next(10.0).round(), 35.0);
64/// assert_eq!(rsi.next(9.5).round(), 16.0);
65/// ```
66///
67/// # Links
68/// * [Relative strength index (Wikipedia)](https://en.wikipedia.org/wiki/Relative_strength_index)
69/// * [RSI (Investopedia)](http://www.investopedia.com/terms/r/rsi.asp)
70///
71#[doc(alias = "RSI")]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73#[derive(Debug, Clone)]
74pub struct RelativeStrengthIndex {
75    period: usize,
76    up_ema_indicator: Ema,
77    down_ema_indicator: Ema,
78    prev_val: f64,
79    is_new: bool,
80}
81
82impl RelativeStrengthIndex {
83    pub fn new(period: usize) -> Result<Self> {
84        Ok(Self {
85            period,
86            up_ema_indicator: Ema::new(period)?,
87            down_ema_indicator: Ema::new(period)?,
88            prev_val: 0.0,
89            is_new: true,
90        })
91    }
92}
93
94impl Period for RelativeStrengthIndex {
95    fn period(&self) -> usize {
96        self.period
97    }
98}
99
100impl Next<f64> for RelativeStrengthIndex {
101    type Output = f64;
102
103    fn next(&mut self, input: f64) -> Self::Output {
104        let mut up = 0.0;
105        let mut down = 0.0;
106
107        if self.is_new {
108            self.is_new = false;
109            // Initialize with some small seed numbers to avoid division by zero
110            up = 0.1;
111            down = 0.1;
112        } else {
113            if input > self.prev_val {
114                up = input - self.prev_val;
115            } else {
116                down = self.prev_val - input;
117            }
118        }
119
120        self.prev_val = input;
121        let up_ema = self.up_ema_indicator.next(up);
122        let down_ema = self.down_ema_indicator.next(down);
123        100.0 * up_ema / (up_ema + down_ema)
124    }
125}
126
127impl<T: Close> Next<&T> for RelativeStrengthIndex {
128    type Output = f64;
129
130    fn next(&mut self, input: &T) -> Self::Output {
131        self.next(input.close())
132    }
133}
134
135impl Reset for RelativeStrengthIndex {
136    fn reset(&mut self) {
137        self.is_new = true;
138        self.prev_val = 0.0;
139        self.up_ema_indicator.reset();
140        self.down_ema_indicator.reset();
141    }
142}
143
144impl Default for RelativeStrengthIndex {
145    fn default() -> Self {
146        Self::new(14).unwrap()
147    }
148}
149
150impl fmt::Display for RelativeStrengthIndex {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        write!(f, "RSI({})", self.period)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::test_helper::*;
160
161    test_indicator!(RelativeStrengthIndex);
162
163    #[test]
164    fn test_new() {
165        assert!(RelativeStrengthIndex::new(0).is_err());
166        assert!(RelativeStrengthIndex::new(1).is_ok());
167    }
168
169    #[test]
170    fn test_next() {
171        let mut rsi = RelativeStrengthIndex::new(3).unwrap();
172        assert_eq!(rsi.next(10.0), 50.0);
173        assert_eq!(rsi.next(10.5).round(), 86.0);
174        assert_eq!(rsi.next(10.0).round(), 35.0);
175        assert_eq!(rsi.next(9.5).round(), 16.0);
176    }
177
178    #[test]
179    fn test_reset() {
180        let mut rsi = RelativeStrengthIndex::new(3).unwrap();
181        assert_eq!(rsi.next(10.0), 50.0);
182        assert_eq!(rsi.next(10.5).round(), 86.0);
183
184        rsi.reset();
185        assert_eq!(rsi.next(10.0).round(), 50.0);
186        assert_eq!(rsi.next(10.5).round(), 86.0);
187    }
188
189    #[test]
190    fn test_default() {
191        RelativeStrengthIndex::default();
192    }
193
194    #[test]
195    fn test_display() {
196        let rsi = RelativeStrengthIndex::new(16).unwrap();
197        assert_eq!(format!("{}", rsi), "RSI(16)");
198    }
199}