1use crate::{Error, IndexEntry, IndexEntryLike};
19
20#[derive(Clone)]
21struct Calc {
22 result: f64,
23 prev: IndexEntry,
24 upside: f64,
25 downside: f64,
26}
27
28pub fn rsi<T: IndexEntryLike>(entries: &[T], duration: usize) -> Result<Vec<IndexEntry>, Error> {
30 if duration == 0 || entries.len() < duration {
31 return Ok(vec![]);
32 }
33 IndexEntry::validate_list(entries)?;
34
35 let mut sorted = entries.to_owned();
36 sorted.sort_by_key(|x| x.get_at());
37
38 let first_rsi = calc_first_rsi(&sorted, duration);
39 if first_rsi.is_none() {
40 return Ok(vec![]);
41 }
42 let first_rsi = first_rsi.unwrap();
43 let xs: Vec<&T> = sorted.iter().skip(duration + 1).collect();
44 if xs.is_empty() {
45 return Ok(vec![IndexEntry {
46 at: first_rsi.0,
47 value: first_rsi.1.result,
48 }]);
49 }
50 Ok(xs
51 .iter()
52 .scan(first_rsi, |z, x| {
53 let upside = (z.1.upside * ((duration - 1) as f64)
54 + (x.get_value() - z.1.prev.value).max(0.0))
55 / (duration as f64);
56 let downside = (z.1.downside * ((duration - 1) as f64)
57 + (z.1.prev.value - x.get_value()).max(0.0))
58 / (duration as f64);
59 z.0 = x.get_at();
60 z.1 = Calc {
61 result: upside / (upside + downside) * 100.0,
62 prev: IndexEntry::from(*x),
63 upside,
64 downside,
65 };
66 Some(z.clone())
67 })
68 .map(|(at, calc)| IndexEntry {
69 at,
70 value: calc.result,
71 })
72 .collect())
73}
74
75fn calc_first_rsi<T: IndexEntryLike>(entries: &[T], duration: usize) -> Option<(u64, Calc)> {
76 if duration == 0 {
77 return None;
78 }
79 let xs: Vec<&T> = entries.iter().take(duration + 1).collect();
80 if xs.is_empty() || xs.len() < duration + 1 {
81 return None;
82 }
83
84 let upside = xs
85 .iter()
86 .map(|x| x.get_value())
87 .fold((-1.0, 0.0), |(z, a), b| {
88 if z < 0.0 {
89 (0.0, b)
90 } else if a < b {
91 (z + (b - a).abs(), b)
92 } else {
93 (z, b)
94 }
95 })
96 .0
97 / (duration as f64);
98
99 let downside = xs
100 .iter()
101 .map(|x| x.get_value())
102 .fold((-1.0, 0.0), |(z, a), b| {
103 if z < 0.0 {
104 (0.0, b)
105 } else if a > b {
106 (z + (b - a).abs(), b)
107 } else {
108 (z, b)
109 }
110 })
111 .0
112 / (duration as f64);
113
114 let last = xs.last().unwrap();
115 Some((
116 xs.last().unwrap().get_at(),
117 Calc {
118 result: upside / (upside + downside) * 100.0,
119 prev: IndexEntry::from(*last),
120 upside,
121 downside,
122 },
123 ))
124}