Skip to main content

finance_query/backtesting/refs/
trend.rs

1use crate::backtesting::strategy::StrategyContext;
2use crate::indicators::Indicator;
3
4use super::IndicatorRef;
5
6/// Average Directional Index reference.
7#[derive(Debug, Clone)]
8pub struct AdxRef {
9    pub period: usize,
10    key: String,
11}
12
13impl IndicatorRef for AdxRef {
14    fn key(&self) -> &str {
15        &self.key
16    }
17
18    fn required_indicators(&self) -> Vec<(String, Indicator)> {
19        vec![(self.key.clone(), Indicator::Adx(self.period))]
20    }
21
22    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
23        ctx.indicator(self.key())
24    }
25
26    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
27        ctx.indicator_prev(self.key())
28    }
29}
30
31/// Create an Average Directional Index reference.
32///
33/// # Example
34///
35/// ```ignore
36/// use finance_query::backtesting::refs::*;
37///
38/// // Strong trend filter
39/// let strong_trend = adx(14).above(25.0);
40/// ```
41#[inline]
42pub fn adx(period: usize) -> AdxRef {
43    AdxRef {
44        period,
45        key: format!("adx_{period}"),
46    }
47}
48
49/// MACD configuration for building MACD-related references.
50#[derive(Debug, Clone, Copy)]
51pub struct MacdConfig {
52    /// Fast EMA period
53    pub fast: usize,
54    /// Slow EMA period
55    pub slow: usize,
56    /// Signal line period
57    pub signal: usize,
58}
59
60impl MacdConfig {
61    /// Get the MACD line reference.
62    pub fn line(&self) -> MacdLineRef {
63        MacdLineRef::new(self.fast, self.slow, self.signal)
64    }
65
66    /// Get the MACD signal line reference.
67    pub fn signal_line(&self) -> MacdSignalRef {
68        MacdSignalRef::new(self.fast, self.slow, self.signal)
69    }
70
71    /// Get the MACD histogram reference.
72    pub fn histogram(&self) -> MacdHistogramRef {
73        MacdHistogramRef::new(self.fast, self.slow, self.signal)
74    }
75}
76
77/// Create a MACD configuration.
78///
79/// # Example
80///
81/// ```ignore
82/// use finance_query::backtesting::refs::*;
83///
84/// let m = macd(12, 26, 9);
85/// let bullish = m.line().crosses_above_ref(m.signal_line());
86/// let histogram_positive = m.histogram().above(0.0);
87/// ```
88#[inline]
89pub fn macd(fast: usize, slow: usize, signal: usize) -> MacdConfig {
90    MacdConfig { fast, slow, signal }
91}
92
93/// MACD Line reference.
94#[derive(Debug, Clone)]
95pub struct MacdLineRef {
96    /// Fast EMA period.
97    pub fast: usize,
98    /// Slow EMA period.
99    pub slow: usize,
100    /// Signal line period.
101    pub signal: usize,
102    key: String,
103}
104
105impl MacdLineRef {
106    fn new(fast: usize, slow: usize, signal: usize) -> Self {
107        Self {
108            fast,
109            slow,
110            signal,
111            key: format!("macd_line_{fast}_{slow}_{signal}"),
112        }
113    }
114}
115
116impl IndicatorRef for MacdLineRef {
117    fn key(&self) -> &str {
118        &self.key
119    }
120
121    fn required_indicators(&self) -> Vec<(String, Indicator)> {
122        vec![(
123            self.key.clone(),
124            Indicator::Macd {
125                fast: self.fast,
126                slow: self.slow,
127                signal: self.signal,
128            },
129        )]
130    }
131
132    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
133        ctx.indicator(self.key())
134    }
135
136    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
137        ctx.indicator_prev(self.key())
138    }
139}
140
141/// MACD Signal Line reference.
142#[derive(Debug, Clone)]
143pub struct MacdSignalRef {
144    /// Fast EMA period.
145    pub fast: usize,
146    /// Slow EMA period.
147    pub slow: usize,
148    /// Signal line period.
149    pub signal: usize,
150    key: String,
151}
152
153impl MacdSignalRef {
154    fn new(fast: usize, slow: usize, signal: usize) -> Self {
155        Self {
156            fast,
157            slow,
158            signal,
159            key: format!("macd_signal_{fast}_{slow}_{signal}"),
160        }
161    }
162}
163
164impl IndicatorRef for MacdSignalRef {
165    fn key(&self) -> &str {
166        &self.key
167    }
168
169    fn required_indicators(&self) -> Vec<(String, Indicator)> {
170        vec![(
171            self.key.clone(),
172            Indicator::Macd {
173                fast: self.fast,
174                slow: self.slow,
175                signal: self.signal,
176            },
177        )]
178    }
179
180    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
181        ctx.indicator(self.key())
182    }
183
184    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
185        ctx.indicator_prev(self.key())
186    }
187}
188
189/// MACD Histogram reference.
190#[derive(Debug, Clone)]
191pub struct MacdHistogramRef {
192    /// Fast EMA period.
193    pub fast: usize,
194    /// Slow EMA period.
195    pub slow: usize,
196    /// Signal line period.
197    pub signal: usize,
198    key: String,
199}
200
201impl MacdHistogramRef {
202    fn new(fast: usize, slow: usize, signal: usize) -> Self {
203        Self {
204            fast,
205            slow,
206            signal,
207            key: format!("macd_histogram_{fast}_{slow}_{signal}"),
208        }
209    }
210}
211
212impl IndicatorRef for MacdHistogramRef {
213    fn key(&self) -> &str {
214        &self.key
215    }
216
217    fn required_indicators(&self) -> Vec<(String, Indicator)> {
218        vec![(
219            self.key.clone(),
220            Indicator::Macd {
221                fast: self.fast,
222                slow: self.slow,
223                signal: self.signal,
224            },
225        )]
226    }
227
228    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
229        ctx.indicator(self.key())
230    }
231
232    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
233        ctx.indicator_prev(self.key())
234    }
235}
236
237/// SuperTrend configuration.
238#[derive(Debug, Clone, Copy)]
239pub struct SupertrendConfig {
240    pub period: usize,
241    pub multiplier: f64,
242}
243
244impl SupertrendConfig {
245    /// Get the SuperTrend value reference.
246    pub fn value(&self) -> SupertrendValueRef {
247        SupertrendValueRef::new(self.period, self.multiplier)
248    }
249
250    /// Get the SuperTrend uptrend indicator (1.0 = uptrend, 0.0 = downtrend).
251    pub fn uptrend(&self) -> SupertrendUptrendRef {
252        SupertrendUptrendRef::new(self.period, self.multiplier)
253    }
254}
255
256/// Create a SuperTrend configuration.
257#[inline]
258pub fn supertrend(period: usize, multiplier: f64) -> SupertrendConfig {
259    SupertrendConfig { period, multiplier }
260}
261
262/// SuperTrend value reference.
263#[derive(Debug, Clone)]
264pub struct SupertrendValueRef {
265    pub period: usize,
266    pub multiplier: f64,
267    key: String,
268}
269
270impl SupertrendValueRef {
271    fn new(period: usize, multiplier: f64) -> Self {
272        Self {
273            period,
274            multiplier,
275            key: format!("supertrend_value_{period}_{multiplier}"),
276        }
277    }
278}
279
280impl IndicatorRef for SupertrendValueRef {
281    fn key(&self) -> &str {
282        &self.key
283    }
284
285    fn required_indicators(&self) -> Vec<(String, Indicator)> {
286        vec![(
287            self.key.clone(),
288            Indicator::Supertrend {
289                period: self.period,
290                multiplier: self.multiplier,
291            },
292        )]
293    }
294
295    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
296        ctx.indicator(self.key())
297    }
298
299    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
300        ctx.indicator_prev(self.key())
301    }
302}
303
304/// SuperTrend uptrend indicator reference.
305/// Returns 1.0 for uptrend, 0.0 for downtrend.
306#[derive(Debug, Clone)]
307pub struct SupertrendUptrendRef {
308    pub period: usize,
309    pub multiplier: f64,
310    key: String,
311}
312
313impl SupertrendUptrendRef {
314    fn new(period: usize, multiplier: f64) -> Self {
315        Self {
316            period,
317            multiplier,
318            key: format!("supertrend_uptrend_{period}_{multiplier}"),
319        }
320    }
321}
322
323impl IndicatorRef for SupertrendUptrendRef {
324    fn key(&self) -> &str {
325        &self.key
326    }
327
328    fn required_indicators(&self) -> Vec<(String, Indicator)> {
329        vec![(
330            self.key.clone(),
331            Indicator::Supertrend {
332                period: self.period,
333                multiplier: self.multiplier,
334            },
335        )]
336    }
337
338    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
339        ctx.indicator(self.key())
340    }
341
342    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
343        ctx.indicator_prev(self.key())
344    }
345}
346
347/// Aroon indicator configuration.
348#[derive(Debug, Clone, Copy)]
349pub struct AroonConfig {
350    pub period: usize,
351}
352
353impl AroonConfig {
354    /// Get the Aroon Up reference.
355    pub fn up(&self) -> AroonUpRef {
356        AroonUpRef::new(self.period)
357    }
358
359    /// Get the Aroon Down reference.
360    pub fn down(&self) -> AroonDownRef {
361        AroonDownRef::new(self.period)
362    }
363}
364
365/// Create an Aroon indicator configuration.
366#[inline]
367pub fn aroon(period: usize) -> AroonConfig {
368    AroonConfig { period }
369}
370
371/// Aroon Up reference.
372#[derive(Debug, Clone)]
373pub struct AroonUpRef {
374    pub period: usize,
375    key: String,
376}
377
378impl AroonUpRef {
379    fn new(period: usize) -> Self {
380        Self {
381            period,
382            key: format!("aroon_up_{period}"),
383        }
384    }
385}
386
387impl IndicatorRef for AroonUpRef {
388    fn key(&self) -> &str {
389        &self.key
390    }
391
392    fn required_indicators(&self) -> Vec<(String, Indicator)> {
393        vec![(self.key.clone(), Indicator::Aroon(self.period))]
394    }
395
396    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
397        ctx.indicator(self.key())
398    }
399
400    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
401        ctx.indicator_prev(self.key())
402    }
403}
404
405/// Aroon Down reference.
406#[derive(Debug, Clone)]
407pub struct AroonDownRef {
408    pub period: usize,
409    key: String,
410}
411
412impl AroonDownRef {
413    fn new(period: usize) -> Self {
414        Self {
415            period,
416            key: format!("aroon_down_{period}"),
417        }
418    }
419}
420
421impl IndicatorRef for AroonDownRef {
422    fn key(&self) -> &str {
423        &self.key
424    }
425
426    fn required_indicators(&self) -> Vec<(String, Indicator)> {
427        vec![(self.key.clone(), Indicator::Aroon(self.period))]
428    }
429
430    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
431        ctx.indicator(self.key())
432    }
433
434    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
435        ctx.indicator_prev(self.key())
436    }
437}
438
439/// Parabolic SAR configuration.
440#[derive(Debug, Clone, Copy)]
441pub struct ParabolicSarConfig {
442    pub step: f64,
443    pub max: f64,
444}
445
446/// Create a Parabolic SAR configuration.
447#[inline]
448pub fn parabolic_sar(step: f64, max: f64) -> ParabolicSarRef {
449    ParabolicSarRef::new(step, max)
450}
451
452/// Parabolic SAR reference.
453#[derive(Debug, Clone)]
454pub struct ParabolicSarRef {
455    pub step: f64,
456    pub max: f64,
457    key: String,
458}
459
460impl ParabolicSarRef {
461    fn new(step: f64, max: f64) -> Self {
462        Self {
463            step,
464            max,
465            key: format!("psar_{step}_{max}"),
466        }
467    }
468}
469
470impl IndicatorRef for ParabolicSarRef {
471    fn key(&self) -> &str {
472        &self.key
473    }
474
475    fn required_indicators(&self) -> Vec<(String, Indicator)> {
476        vec![(
477            self.key.clone(),
478            Indicator::ParabolicSar {
479                step: self.step,
480                max: self.max,
481            },
482        )]
483    }
484
485    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
486        ctx.indicator(self.key())
487    }
488
489    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
490        ctx.indicator_prev(self.key())
491    }
492}
493
494/// Choppiness Index reference.
495#[derive(Debug, Clone)]
496pub struct ChoppinessIndexRef {
497    pub period: usize,
498    key: String,
499}
500
501/// Create a Choppiness Index reference.
502#[inline]
503pub fn choppiness_index(period: usize) -> ChoppinessIndexRef {
504    ChoppinessIndexRef {
505        period,
506        key: format!("chop_{period}"),
507    }
508}
509
510impl IndicatorRef for ChoppinessIndexRef {
511    fn key(&self) -> &str {
512        &self.key
513    }
514
515    fn required_indicators(&self) -> Vec<(String, Indicator)> {
516        vec![(self.key.clone(), Indicator::ChoppinessIndex(self.period))]
517    }
518
519    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
520        ctx.indicator(self.key())
521    }
522
523    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
524        ctx.indicator_prev(self.key())
525    }
526}
527
528#[cfg(test)]
529mod tests {
530    use super::*;
531    use crate::backtesting::refs::{coppock_curve, true_range};
532
533    #[test]
534    fn test_macd_keys() {
535        let m = macd(12, 26, 9);
536        assert_eq!(m.line().key(), "macd_line_12_26_9");
537        assert_eq!(m.signal_line().key(), "macd_signal_12_26_9");
538        assert_eq!(m.histogram().key(), "macd_histogram_12_26_9");
539    }
540
541    #[test]
542    fn test_supertrend_keys() {
543        let st = supertrend(10, 3.0);
544        assert_eq!(st.value().key(), "supertrend_value_10_3");
545        assert_eq!(st.uptrend().key(), "supertrend_uptrend_10_3");
546    }
547
548    #[test]
549    fn test_aroon_keys() {
550        let ar = aroon(25);
551        assert_eq!(ar.up().key(), "aroon_up_25");
552        assert_eq!(ar.down().key(), "aroon_down_25");
553    }
554
555    #[test]
556    fn test_other_keys() {
557        assert_eq!(parabolic_sar(0.02, 0.2).key(), "psar_0.02_0.2");
558        assert_eq!(true_range().key(), "true_range");
559        assert_eq!(coppock_curve(10, 14, 11).key(), "coppock_10_14_11");
560    }
561}