indexes_rs/v2/adx/
types.rs

1use serde::{Deserialize, Serialize};
2use std::collections::VecDeque;
3
4/// Configuration for ADX calculation
5#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
6pub struct ADXConfig {
7    /// Period for DI and ADX calculation (default: 14)
8    pub period: usize,
9    /// Smoothing period for ADX (default: same as period)
10    pub adx_smoothing: usize,
11    /// Strong trend threshold (default: 25.0)
12    pub strong_trend_threshold: f64,
13    /// Very strong trend threshold (default: 50.0)
14    pub very_strong_trend_threshold: f64,
15}
16
17impl Default for ADXConfig {
18    fn default() -> Self {
19        Self {
20            period: 14,
21            adx_smoothing: 14,
22            strong_trend_threshold: 25.0,
23            very_strong_trend_threshold: 50.0,
24        }
25    }
26}
27
28/// Input data for ADX calculation
29#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
30pub struct ADXInput {
31    /// High price
32    pub high: f64,
33    /// Low price
34    pub low: f64,
35    /// Close price
36    pub close: f64,
37}
38
39/// Trend strength classification
40#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
41pub enum TrendStrength {
42    /// ADX below 25 - weak or no trend
43    Weak,
44    /// ADX between 25-50 - strong trend
45    Strong,
46    /// ADX above 50 - very strong trend
47    VeryStrong,
48    /// Not enough data yet
49    Insufficient,
50}
51
52/// Trend direction based on DI comparison
53#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
54pub enum TrendDirection {
55    /// +DI > -DI (uptrend)
56    Up,
57    /// -DI > +DI (downtrend)
58    Down,
59    /// +DI = -DI (sideways)
60    Sideways,
61}
62
63/// Output from ADX calculation
64#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
65pub struct ADXOutput {
66    /// Average Directional Index (trend strength)
67    pub adx: f64,
68    /// Plus Directional Indicator
69    pub plus_di: f64,
70    /// Minus Directional Indicator
71    pub minus_di: f64,
72    /// Directional Index (|+DI - -DI| / (+DI + -DI) * 100)
73    pub dx: f64,
74    /// True Range for current period
75    pub true_range: f64,
76    /// Trend strength classification
77    pub trend_strength: TrendStrength,
78    /// Trend direction based on DI comparison
79    pub trend_direction: TrendDirection,
80    /// DI spread (+DI - -DI)
81    pub di_spread: f64,
82}
83
84/// Internal calculation data for a single period
85#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
86pub struct ADXPeriodData {
87    /// True Range
88    pub true_range: f64,
89    /// Plus Directional Movement
90    pub plus_dm: f64,
91    /// Minus Directional Movement
92    pub minus_dm: f64,
93    /// Plus DI
94    pub plus_di: f64,
95    /// Minus DI
96    pub minus_di: f64,
97    /// DX value
98    pub dx: f64,
99}
100
101/// ADX calculation state
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct ADXState {
104    /// Configuration
105    pub config: ADXConfig,
106    /// Previous high (for DM calculation)
107    pub previous_high: Option<f64>,
108    /// Previous low (for DM calculation)
109    pub previous_low: Option<f64>,
110    /// Previous close (for TR calculation)
111    pub previous_close: Option<f64>,
112    /// History of period data for smoothing
113    pub period_data: VecDeque<ADXPeriodData>,
114    /// Smoothed True Range sum
115    pub smoothed_tr: Option<f64>,
116    /// Smoothed Plus DM sum
117    pub smoothed_plus_dm: Option<f64>,
118    /// Smoothed Minus DM sum
119    pub smoothed_minus_dm: Option<f64>,
120    /// History of DX values for ADX calculation
121    pub dx_history: VecDeque<f64>,
122    /// Current ADX value (for smoothing)
123    pub current_adx: Option<f64>,
124    /// Whether we have enough data for DI calculation
125    pub has_di_data: bool,
126    /// Whether we have enough data for ADX calculation
127    pub has_adx_data: bool,
128    /// Is first calculation
129    pub is_first: bool,
130}
131
132impl ADXState {
133    pub fn new(config: ADXConfig) -> Self {
134        Self {
135            config,
136            previous_high: None,
137            previous_low: None,
138            previous_close: None,
139            period_data: VecDeque::with_capacity(config.period),
140            smoothed_tr: None,
141            smoothed_plus_dm: None,
142            smoothed_minus_dm: None,
143            dx_history: VecDeque::with_capacity(config.adx_smoothing),
144            current_adx: None,
145            has_di_data: false,
146            has_adx_data: false,
147            is_first: true,
148        }
149    }
150}
151
152/// Error types for ADX calculation
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154pub enum ADXError {
155    /// Invalid input data
156    InvalidInput(String),
157    /// Invalid HLC relationship
158    InvalidHLC,
159    /// Invalid price (NaN or infinite)
160    InvalidPrice,
161    /// Invalid period (must be > 0)
162    InvalidPeriod,
163    /// Invalid threshold values
164    InvalidThresholds,
165    /// Division by zero in calculation
166    DivisionByZero,
167}