indexes_rs/v2/support_resistance/
main.rs

1use super::types::{HighLow, SupportResistanceLevels};
2
3/// A stateless struct to encapsulate the support and resistance calculation logic.
4pub struct SupportResistance;
5
6impl SupportResistance {
7    /// Calculates support and resistance levels from a slice of HighLow price data.
8    ///
9    /// This method identifies levels using a fractal-based approach, looking for a candle
10    /// whose high is higher (for resistance) or low is lower (for support) than the
11    /// `period` candles on both sides.
12    ///
13    /// # Arguments
14    ///
15    /// * `price_data` - A slice of `HighLow` structs representing the price history.
16    /// * `period` - The number of candles to check on each side of a potential fractal.
17    ///
18    /// # Returns
19    ///
20    /// A `SupportResistanceLevels` struct containing the identified levels.
21    pub fn calculate(price_data: &[HighLow], period: usize) -> SupportResistanceLevels {
22        // The total window size is `period` candles left, the current candle, and `period` candles right.
23        let window_size = 2 * period + 1;
24
25        // Not enough data to form a single window, so no levels can be found.
26        if period == 0 || price_data.len() < window_size {
27            return SupportResistanceLevels {
28                support: vec![],
29                resistance: vec![],
30            };
31        }
32
33        let mut supports = Vec::new();
34        let mut resistances = Vec::new();
35
36        // Iterate through the data points where a full window can be formed.
37        for i in period..(price_data.len() - period) {
38            let current_high = price_data[i].high;
39            let current_low = price_data[i].low;
40
41            // Check for a resistance fractal (a peak).
42            // The current high must be strictly greater than all other highs in the window.
43            let is_resistance = (i - period..=i + period)
44                .filter(|&j| j != i) // Exclude the current candle from comparison
45                .all(|j| price_data[j].high < current_high);
46
47            // Check for a support fractal (a trough).
48            // The current low must be strictly lower than all other lows in the window.
49            let is_support = (i - period..=i + period)
50                .filter(|&j| j != i) // Exclude the current candle from comparison
51                .all(|j| price_data[j].low > current_low);
52
53            if is_resistance {
54                resistances.push(current_high);
55            }
56            if is_support {
57                supports.push(current_low);
58            }
59        }
60
61        // Sort and remove duplicates to get clean lists of levels.
62        supports.sort_by(|a, b| b.partial_cmp(a).unwrap()); // Descending order
63        supports.dedup();
64
65        resistances.sort_by(|a, b| a.partial_cmp(b).unwrap()); // Ascending order
66        resistances.dedup();
67
68        SupportResistanceLevels {
69            support: supports,
70            resistance: resistances,
71        }
72    }
73}