Skip to main content

egui_charts/studies/builtin/
know_sure_thing.rs

1use 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/// Know Sure Thing (KST) Oscillator
10/// Developed by Martin Pring
11/// Combines multiple ROC values with different smoothing periods
12#[derive(Clone)]
13pub struct KnowSureThing {
14    /// ROC periods (typically 10, 15, 20, 30)
15    roc_periods: [usize; 4],
16    /// SMA periods for smoothing (typically 10, 10, 10, 15)
17    sma_periods: [usize; 4],
18    /// Signal line SMA period (typically 9)
19    signal_period: usize,
20    values: IndicatorValues,
21    /// Colors: [KST, Signal]
22    colors: Vec<Color32>,
23    visible: bool,
24}
25
26impl KnowSureThing {
27    /// Create with ROC periods 10/15/20/30, SMA periods 10/10/10/15 and signal period 9.
28    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,  // KST
36                crate::studies::palette::BRIGHT_RED, // Signal
37            ],
38            visible: true,
39        }
40    }
41
42    /// Set the four rate-of-change periods, their SMA periods and the signal period, each at least one.
43    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    /// Set plot colors in the order returned by [`Indicator::line_names`].
56    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    /// The values over `data`, every bar of which has finite inputs.
70    fn compute(&self, data: &[Bar]) -> IndicatorValues {
71        let mut values = IndicatorValues::new();
72        // Get closes
73        let closes: Vec<f64> = data.iter().map(|b| b.close).collect();
74
75        // Calculate ROC for each period
76        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        // Smooth each ROC
82        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        // Calculate KST: (SROC1 * 1) + (SROC2 * 2) + (SROC3 * 3) + (SROC4 * 4)
88        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        // Calculate signal line
99        let signal = rolling_mean(&kst, self.signal_period);
100
101        // Store results
102        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}