egui_charts/studies/builtin/
know_sure_thing.rs1use super::over_finite_bars;
2use crate::model::Bar;
3use crate::studies::helpers::calculate_roc;
4use crate::studies::stepper::rolling_mean;
5use crate::studies::stepper::{update_window, usable_behind};
6use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
7use egui::Color32;
8
9#[derive(Clone)]
13pub struct KnowSureThing {
14 roc_periods: [usize; 4],
16 sma_periods: [usize; 4],
18 signal_period: usize,
20 values: IndicatorValues,
21 colors: Vec<Color32>,
23 visible: bool,
24}
25
26impl KnowSureThing {
27 pub fn new() -> Self {
29 Self {
30 roc_periods: [10, 15, 20, 30],
31 sma_periods: [10, 10, 10, 15],
32 signal_period: 9,
33 values: IndicatorValues::new(),
34 colors: vec![
35 crate::studies::palette::DEEP_TEAL, crate::studies::palette::BRIGHT_RED, ],
38 visible: true,
39 }
40 }
41
42 pub fn with_params(
44 mut self,
45 roc_periods: [usize; 4],
46 sma_periods: [usize; 4],
47 signal_period: usize,
48 ) -> Self {
49 self.roc_periods = roc_periods.map(|p| p.max(1));
50 self.sma_periods = sma_periods.map(|p| p.max(1));
51 self.signal_period = signal_period.max(1);
52 self
53 }
54
55 pub fn with_colors(mut self, colors: Vec<Color32>) -> Self {
57 self.colors = colors;
58 self
59 }
60}
61
62impl Default for KnowSureThing {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl KnowSureThing {
69 fn compute(&self, data: &[Bar]) -> IndicatorValues {
71 let mut values = IndicatorValues::new();
72 let closes: Vec<f64> = data.iter().map(|b| b.close).collect();
74
75 let roc1 = calculate_roc(&closes, self.roc_periods[0]);
77 let roc2 = calculate_roc(&closes, self.roc_periods[1]);
78 let roc3 = calculate_roc(&closes, self.roc_periods[2]);
79 let roc4 = calculate_roc(&closes, self.roc_periods[3]);
80
81 let sroc1 = rolling_mean(&roc1, self.sma_periods[0]);
83 let sroc2 = rolling_mean(&roc2, self.sma_periods[1]);
84 let sroc3 = rolling_mean(&roc3, self.sma_periods[2]);
85 let sroc4 = rolling_mean(&roc4, self.sma_periods[3]);
86
87 let mut kst = Vec::with_capacity(data.len());
89 for i in 0..data.len() {
90 if sroc1[i].is_nan() || sroc2[i].is_nan() || sroc3[i].is_nan() || sroc4[i].is_nan() {
91 kst.push(f64::NAN);
92 } else {
93 let value = sroc1[i] * 1.0 + sroc2[i] * 2.0 + sroc3[i] * 3.0 + sroc4[i] * 4.0;
94 kst.push(value);
95 }
96 }
97
98 let signal = rolling_mean(&kst, self.signal_period);
100
101 for i in 0..data.len() {
103 if kst[i].is_nan() {
104 values.push(IndicatorValue::None);
105 } else {
106 values.push_lines(&[kst[i], signal[i]]);
107 }
108 }
109
110 values
111 }
112}
113
114impl Indicator for KnowSureThing {
115 fn source(&self) -> Option<crate::studies::StudySource> {
116 Some(crate::studies::StudySource::Price(
117 crate::model::PriceSource::Close,
118 ))
119 }
120
121 crate::studies::inputs::study_inputs!(self;
122 roc_periods[0]: length("Roc periods 1", 1, usize::MAX),
123 roc_periods[1]: length("Roc periods 2", 1, usize::MAX),
124 roc_periods[2]: length("Roc periods 3", 1, usize::MAX),
125 roc_periods[3]: length("Roc periods 4", 1, usize::MAX),
126 sma_periods[0]: length("SMA periods 1", 1, usize::MAX),
127 sma_periods[1]: length("SMA periods 2", 1, usize::MAX),
128 sma_periods[2]: length("SMA periods 3", 1, usize::MAX),
129 sma_periods[3]: length("SMA periods 4", 1, usize::MAX),
130 signal_period: length("Signal period", 1, usize::MAX)
131 ; {});
132
133 fn name(&self) -> &str {
134 "KST"
135 }
136
137 fn desc(&self) -> &str {
138 "Know Sure Thing - Multi-timeframe momentum"
139 }
140
141 fn calculate(&mut self, data: &[Bar]) {
142 self.update(data, 0);
143 }
144
145 fn update(&mut self, data: &[Bar], from: usize) {
146 let lookback = self
147 .roc_periods
148 .iter()
149 .zip(self.sma_periods)
150 .map(|(roc, sma)| roc.saturating_add(sma - 1))
151 .max()
152 .unwrap()
153 .saturating_add(self.signal_period - 1);
154 let mut values = std::mem::take(&mut self.values);
155 update_window(
156 &mut values,
157 data,
158 from,
159 usable_behind(lookback, |b| b.close.is_finite()),
160 |data| over_finite_bars(data, |b| b.close.is_finite(), |data| self.compute(data)),
161 );
162 self.values = values;
163 }
164
165 fn is_incremental(&self) -> bool {
166 true
167 }
168
169 fn values(&self) -> &IndicatorValues {
170 &self.values
171 }
172
173 fn colors(&self) -> Vec<Color32> {
174 self.colors.clone()
175 }
176
177 fn set_colors(&mut self, colors: Vec<Color32>) {
178 if colors.len() >= 2 {
179 self.colors = colors;
180 }
181 }
182
183 fn is_overlay(&self) -> bool {
184 false
185 }
186
187 fn is_visible(&self) -> bool {
188 self.visible
189 }
190
191 fn set_visible(&mut self, visible: bool) {
192 self.visible = visible;
193 }
194
195 fn clone_box(&self) -> Box<dyn Indicator> {
196 Box::new(self.clone())
197 }
198
199 fn line_names(&self) -> Vec<String> {
200 vec!["KST".to_string(), format!("Signal({})", self.signal_period)]
201 }
202}