finlib_ta/indicators/
relative_strength_index.rs1use core::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 use alloc::format;
161
162 test_indicator!(RelativeStrengthIndex);
163
164 #[test]
165 fn test_new() {
166 assert!(RelativeStrengthIndex::new(0).is_err());
167 assert!(RelativeStrengthIndex::new(1).is_ok());
168 }
169
170 #[test]
171 fn test_next() {
172 let mut rsi = RelativeStrengthIndex::new(3).unwrap();
173 assert_eq!(rsi.next(10.0), 50.0);
174 assert_eq!(rsi.next(10.5).round(), 86.0);
175 assert_eq!(rsi.next(10.0).round(), 35.0);
176 assert_eq!(rsi.next(9.5).round(), 16.0);
177 }
178
179 #[test]
180 fn test_reset() {
181 let mut rsi = RelativeStrengthIndex::new(3).unwrap();
182 assert_eq!(rsi.next(10.0), 50.0);
183 assert_eq!(rsi.next(10.5).round(), 86.0);
184
185 rsi.reset();
186 assert_eq!(rsi.next(10.0).round(), 50.0);
187 assert_eq!(rsi.next(10.5).round(), 86.0);
188 }
189
190 #[test]
191 fn test_default() {
192 RelativeStrengthIndex::default();
193 }
194
195 #[test]
196 fn test_display() {
197 let rsi = RelativeStrengthIndex::new(16).unwrap();
198 assert_eq!(format!("{}", rsi), "RSI(16)");
199 }
200}