ta/indicators/
relative_strength_index.rs1use 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#[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 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}