Skip to main content

finance_query/backtesting/refs/
computed.rs

1//! Computed indicator references for ALL technical indicators.
2//!
3//! This module provides references to pre-computed technical indicators
4//! that can be used in trading conditions.
5//!
6//! # Categories
7//!
8//! - **Moving Averages**: SMA, EMA, WMA, DEMA, TEMA, HMA, VWMA, ALMA, McGinley Dynamic
9//! - **Oscillators**: RSI, Stochastic, StochasticRSI, CCI, Williams %R, CMO, Awesome Oscillator
10//! - **Trend**: MACD, ADX, Aroon, SuperTrend, Ichimoku, Parabolic SAR
11//! - **Volatility**: ATR, Bollinger Bands, Keltner Channels, Donchian Channels
12//! - **Volume**: OBV, VWAP, MFI, CMF, Chaikin Oscillator, A/D, Balance of Power
13//! - **Momentum**: Momentum, ROC, Coppock Curve, Bull/Bear Power, Elder Ray
14
15// Allow missing docs on struct fields in this file - users interact via
16// fluent API functions (sma(), rsi(), etc.) rather than these internal types.
17#![allow(missing_docs)]
18
19use crate::backtesting::strategy::StrategyContext;
20use crate::indicators::Indicator;
21
22use super::IndicatorRef;
23
24// ============================================================================
25// MOVING AVERAGES
26// ============================================================================
27
28/// Simple Moving Average reference.
29#[derive(Debug, Clone, Copy)]
30pub struct SmaRef(pub usize);
31
32impl IndicatorRef for SmaRef {
33    fn key(&self) -> String {
34        format!("sma_{}", self.0)
35    }
36
37    fn required_indicators(&self) -> Vec<(String, Indicator)> {
38        vec![(self.key(), Indicator::Sma(self.0))]
39    }
40
41    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
42        ctx.indicator(&self.key())
43    }
44
45    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
46        ctx.indicator_prev(&self.key())
47    }
48}
49
50/// Create a Simple Moving Average reference.
51///
52/// # Example
53///
54/// ```ignore
55/// use finance_query::backtesting::refs::*;
56///
57/// let sma_20 = sma(20);
58/// let golden_cross = sma(50).crosses_above_ref(sma(200));
59/// ```
60#[inline]
61pub fn sma(period: usize) -> SmaRef {
62    SmaRef(period)
63}
64
65/// Exponential Moving Average reference.
66#[derive(Debug, Clone, Copy)]
67pub struct EmaRef(pub usize);
68
69impl IndicatorRef for EmaRef {
70    fn key(&self) -> String {
71        format!("ema_{}", self.0)
72    }
73
74    fn required_indicators(&self) -> Vec<(String, Indicator)> {
75        vec![(self.key(), Indicator::Ema(self.0))]
76    }
77
78    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
79        ctx.indicator(&self.key())
80    }
81
82    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
83        ctx.indicator_prev(&self.key())
84    }
85}
86
87/// Create an Exponential Moving Average reference.
88#[inline]
89pub fn ema(period: usize) -> EmaRef {
90    EmaRef(period)
91}
92
93/// Weighted Moving Average reference.
94#[derive(Debug, Clone, Copy)]
95pub struct WmaRef(pub usize);
96
97impl IndicatorRef for WmaRef {
98    fn key(&self) -> String {
99        format!("wma_{}", self.0)
100    }
101
102    fn required_indicators(&self) -> Vec<(String, Indicator)> {
103        vec![(self.key(), Indicator::Wma(self.0))]
104    }
105
106    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
107        ctx.indicator(&self.key())
108    }
109
110    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
111        ctx.indicator_prev(&self.key())
112    }
113}
114
115/// Create a Weighted Moving Average reference.
116#[inline]
117pub fn wma(period: usize) -> WmaRef {
118    WmaRef(period)
119}
120
121/// Double Exponential Moving Average reference.
122#[derive(Debug, Clone, Copy)]
123pub struct DemaRef(pub usize);
124
125impl IndicatorRef for DemaRef {
126    fn key(&self) -> String {
127        format!("dema_{}", self.0)
128    }
129
130    fn required_indicators(&self) -> Vec<(String, Indicator)> {
131        vec![(self.key(), Indicator::Dema(self.0))]
132    }
133
134    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
135        ctx.indicator(&self.key())
136    }
137
138    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
139        ctx.indicator_prev(&self.key())
140    }
141}
142
143/// Create a Double Exponential Moving Average reference.
144#[inline]
145pub fn dema(period: usize) -> DemaRef {
146    DemaRef(period)
147}
148
149/// Triple Exponential Moving Average reference.
150#[derive(Debug, Clone, Copy)]
151pub struct TemaRef(pub usize);
152
153impl IndicatorRef for TemaRef {
154    fn key(&self) -> String {
155        format!("tema_{}", self.0)
156    }
157
158    fn required_indicators(&self) -> Vec<(String, Indicator)> {
159        vec![(self.key(), Indicator::Tema(self.0))]
160    }
161
162    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
163        ctx.indicator(&self.key())
164    }
165
166    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
167        ctx.indicator_prev(&self.key())
168    }
169}
170
171/// Create a Triple Exponential Moving Average reference.
172#[inline]
173pub fn tema(period: usize) -> TemaRef {
174    TemaRef(period)
175}
176
177/// Hull Moving Average reference.
178#[derive(Debug, Clone, Copy)]
179pub struct HmaRef(pub usize);
180
181impl IndicatorRef for HmaRef {
182    fn key(&self) -> String {
183        format!("hma_{}", self.0)
184    }
185
186    fn required_indicators(&self) -> Vec<(String, Indicator)> {
187        vec![(self.key(), Indicator::Hma(self.0))]
188    }
189
190    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
191        ctx.indicator(&self.key())
192    }
193
194    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
195        ctx.indicator_prev(&self.key())
196    }
197}
198
199/// Create a Hull Moving Average reference.
200#[inline]
201pub fn hma(period: usize) -> HmaRef {
202    HmaRef(period)
203}
204
205/// Volume Weighted Moving Average reference.
206#[derive(Debug, Clone, Copy)]
207pub struct VwmaRef(pub usize);
208
209impl IndicatorRef for VwmaRef {
210    fn key(&self) -> String {
211        format!("vwma_{}", self.0)
212    }
213
214    fn required_indicators(&self) -> Vec<(String, Indicator)> {
215        vec![(self.key(), Indicator::Vwma(self.0))]
216    }
217
218    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
219        ctx.indicator(&self.key())
220    }
221
222    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
223        ctx.indicator_prev(&self.key())
224    }
225}
226
227/// Create a Volume Weighted Moving Average reference.
228#[inline]
229pub fn vwma(period: usize) -> VwmaRef {
230    VwmaRef(period)
231}
232
233/// McGinley Dynamic indicator reference.
234#[derive(Debug, Clone, Copy)]
235pub struct McginleyDynamicRef(pub usize);
236
237impl IndicatorRef for McginleyDynamicRef {
238    fn key(&self) -> String {
239        format!("mcginley_{}", self.0)
240    }
241
242    fn required_indicators(&self) -> Vec<(String, Indicator)> {
243        vec![(self.key(), Indicator::McginleyDynamic(self.0))]
244    }
245
246    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
247        ctx.indicator(&self.key())
248    }
249
250    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
251        ctx.indicator_prev(&self.key())
252    }
253}
254
255/// Create a McGinley Dynamic indicator reference.
256#[inline]
257pub fn mcginley(period: usize) -> McginleyDynamicRef {
258    McginleyDynamicRef(period)
259}
260
261// ============================================================================
262// OSCILLATORS
263// ============================================================================
264
265/// Relative Strength Index reference.
266#[derive(Debug, Clone, Copy)]
267pub struct RsiRef(pub usize);
268
269impl IndicatorRef for RsiRef {
270    fn key(&self) -> String {
271        format!("rsi_{}", self.0)
272    }
273
274    fn required_indicators(&self) -> Vec<(String, Indicator)> {
275        vec![(self.key(), Indicator::Rsi(self.0))]
276    }
277
278    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
279        ctx.indicator(&self.key())
280    }
281
282    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
283        ctx.indicator_prev(&self.key())
284    }
285}
286
287/// Create a Relative Strength Index reference.
288///
289/// # Example
290///
291/// ```ignore
292/// use finance_query::backtesting::refs::*;
293///
294/// let oversold = rsi(14).below(30.0);
295/// let overbought = rsi(14).above(70.0);
296/// let exit_oversold = rsi(14).crosses_above(30.0);
297/// ```
298#[inline]
299pub fn rsi(period: usize) -> RsiRef {
300    RsiRef(period)
301}
302
303/// Commodity Channel Index reference.
304#[derive(Debug, Clone, Copy)]
305pub struct CciRef(pub usize);
306
307impl IndicatorRef for CciRef {
308    fn key(&self) -> String {
309        format!("cci_{}", self.0)
310    }
311
312    fn required_indicators(&self) -> Vec<(String, Indicator)> {
313        vec![(self.key(), Indicator::Cci(self.0))]
314    }
315
316    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
317        ctx.indicator(&self.key())
318    }
319
320    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
321        ctx.indicator_prev(&self.key())
322    }
323}
324
325/// Create a Commodity Channel Index reference.
326#[inline]
327pub fn cci(period: usize) -> CciRef {
328    CciRef(period)
329}
330
331/// Williams %R reference.
332#[derive(Debug, Clone, Copy)]
333pub struct WilliamsRRef(pub usize);
334
335impl IndicatorRef for WilliamsRRef {
336    fn key(&self) -> String {
337        format!("williams_r_{}", self.0)
338    }
339
340    fn required_indicators(&self) -> Vec<(String, Indicator)> {
341        vec![(self.key(), Indicator::WilliamsR(self.0))]
342    }
343
344    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
345        ctx.indicator(&self.key())
346    }
347
348    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
349        ctx.indicator_prev(&self.key())
350    }
351}
352
353/// Create a Williams %R reference.
354#[inline]
355pub fn williams_r(period: usize) -> WilliamsRRef {
356    WilliamsRRef(period)
357}
358
359/// Chande Momentum Oscillator reference.
360#[derive(Debug, Clone, Copy)]
361pub struct CmoRef(pub usize);
362
363impl IndicatorRef for CmoRef {
364    fn key(&self) -> String {
365        format!("cmo_{}", self.0)
366    }
367
368    fn required_indicators(&self) -> Vec<(String, Indicator)> {
369        vec![(self.key(), Indicator::Cmo(self.0))]
370    }
371
372    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
373        ctx.indicator(&self.key())
374    }
375
376    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
377        ctx.indicator_prev(&self.key())
378    }
379}
380
381/// Create a Chande Momentum Oscillator reference.
382#[inline]
383pub fn cmo(period: usize) -> CmoRef {
384    CmoRef(period)
385}
386
387// ============================================================================
388// MOMENTUM INDICATORS
389// ============================================================================
390
391/// Momentum indicator reference.
392#[derive(Debug, Clone, Copy)]
393pub struct MomentumRef(pub usize);
394
395impl IndicatorRef for MomentumRef {
396    fn key(&self) -> String {
397        format!("momentum_{}", self.0)
398    }
399
400    fn required_indicators(&self) -> Vec<(String, Indicator)> {
401        vec![(self.key(), Indicator::Momentum(self.0))]
402    }
403
404    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
405        ctx.indicator(&self.key())
406    }
407
408    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
409        ctx.indicator_prev(&self.key())
410    }
411}
412
413/// Create a Momentum indicator reference.
414#[inline]
415pub fn momentum(period: usize) -> MomentumRef {
416    MomentumRef(period)
417}
418
419/// Rate of Change reference.
420#[derive(Debug, Clone, Copy)]
421pub struct RocRef(pub usize);
422
423impl IndicatorRef for RocRef {
424    fn key(&self) -> String {
425        format!("roc_{}", self.0)
426    }
427
428    fn required_indicators(&self) -> Vec<(String, Indicator)> {
429        vec![(self.key(), Indicator::Roc(self.0))]
430    }
431
432    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
433        ctx.indicator(&self.key())
434    }
435
436    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
437        ctx.indicator_prev(&self.key())
438    }
439}
440
441/// Create a Rate of Change reference.
442#[inline]
443pub fn roc(period: usize) -> RocRef {
444    RocRef(period)
445}
446
447// ============================================================================
448// TREND INDICATORS
449// ============================================================================
450
451/// Average Directional Index reference.
452#[derive(Debug, Clone, Copy)]
453pub struct AdxRef(pub usize);
454
455impl IndicatorRef for AdxRef {
456    fn key(&self) -> String {
457        format!("adx_{}", self.0)
458    }
459
460    fn required_indicators(&self) -> Vec<(String, Indicator)> {
461        vec![(self.key(), Indicator::Adx(self.0))]
462    }
463
464    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
465        ctx.indicator(&self.key())
466    }
467
468    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
469        ctx.indicator_prev(&self.key())
470    }
471}
472
473/// Create an Average Directional Index reference.
474///
475/// # Example
476///
477/// ```ignore
478/// use finance_query::backtesting::refs::*;
479///
480/// // Strong trend filter
481/// let strong_trend = adx(14).above(25.0);
482/// ```
483#[inline]
484pub fn adx(period: usize) -> AdxRef {
485    AdxRef(period)
486}
487
488// ============================================================================
489// VOLATILITY INDICATORS
490// ============================================================================
491
492/// Average True Range reference.
493#[derive(Debug, Clone, Copy)]
494pub struct AtrRef(pub usize);
495
496impl IndicatorRef for AtrRef {
497    fn key(&self) -> String {
498        format!("atr_{}", self.0)
499    }
500
501    fn required_indicators(&self) -> Vec<(String, Indicator)> {
502        vec![(self.key(), Indicator::Atr(self.0))]
503    }
504
505    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
506        ctx.indicator(&self.key())
507    }
508
509    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
510        ctx.indicator_prev(&self.key())
511    }
512}
513
514/// Create an Average True Range reference.
515#[inline]
516pub fn atr(period: usize) -> AtrRef {
517    AtrRef(period)
518}
519
520// ============================================================================
521// VOLUME INDICATORS
522// ============================================================================
523
524/// On-Balance Volume reference.
525#[derive(Debug, Clone, Copy)]
526pub struct ObvRef;
527
528impl IndicatorRef for ObvRef {
529    fn key(&self) -> String {
530        "obv".to_string()
531    }
532
533    fn required_indicators(&self) -> Vec<(String, Indicator)> {
534        vec![(self.key(), Indicator::Obv)]
535    }
536
537    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
538        ctx.indicator(&self.key())
539    }
540
541    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
542        ctx.indicator_prev(&self.key())
543    }
544}
545
546/// Create an On-Balance Volume reference.
547#[inline]
548pub fn obv() -> ObvRef {
549    ObvRef
550}
551
552/// Volume Weighted Average Price reference.
553#[derive(Debug, Clone, Copy)]
554pub struct VwapRef;
555
556impl IndicatorRef for VwapRef {
557    fn key(&self) -> String {
558        "vwap".to_string()
559    }
560
561    fn required_indicators(&self) -> Vec<(String, Indicator)> {
562        vec![(self.key(), Indicator::Vwap)]
563    }
564
565    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
566        ctx.indicator(&self.key())
567    }
568
569    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
570        ctx.indicator_prev(&self.key())
571    }
572}
573
574/// Create a Volume Weighted Average Price reference.
575#[inline]
576pub fn vwap() -> VwapRef {
577    VwapRef
578}
579
580/// Money Flow Index reference.
581#[derive(Debug, Clone, Copy)]
582pub struct MfiRef(pub usize);
583
584impl IndicatorRef for MfiRef {
585    fn key(&self) -> String {
586        format!("mfi_{}", self.0)
587    }
588
589    fn required_indicators(&self) -> Vec<(String, Indicator)> {
590        vec![(self.key(), Indicator::Mfi(self.0))]
591    }
592
593    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
594        ctx.indicator(&self.key())
595    }
596
597    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
598        ctx.indicator_prev(&self.key())
599    }
600}
601
602/// Create a Money Flow Index reference.
603#[inline]
604pub fn mfi(period: usize) -> MfiRef {
605    MfiRef(period)
606}
607
608/// Chaikin Money Flow reference.
609#[derive(Debug, Clone, Copy)]
610pub struct CmfRef(pub usize);
611
612impl IndicatorRef for CmfRef {
613    fn key(&self) -> String {
614        format!("cmf_{}", self.0)
615    }
616
617    fn required_indicators(&self) -> Vec<(String, Indicator)> {
618        vec![(self.key(), Indicator::Cmf(self.0))]
619    }
620
621    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
622        ctx.indicator(&self.key())
623    }
624
625    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
626        ctx.indicator_prev(&self.key())
627    }
628}
629
630/// Create a Chaikin Money Flow reference.
631#[inline]
632pub fn cmf(period: usize) -> CmfRef {
633    CmfRef(period)
634}
635
636// ============================================================================
637// MULTI-VALUE INDICATORS (with builders)
638// ============================================================================
639
640// --- MACD ---
641
642/// MACD configuration for building MACD-related references.
643#[derive(Debug, Clone, Copy)]
644pub struct MacdConfig {
645    /// Fast EMA period
646    pub fast: usize,
647    /// Slow EMA period
648    pub slow: usize,
649    /// Signal line period
650    pub signal: usize,
651}
652
653impl MacdConfig {
654    /// Get the MACD line reference.
655    pub fn line(&self) -> MacdLineRef {
656        MacdLineRef {
657            fast: self.fast,
658            slow: self.slow,
659            signal: self.signal,
660        }
661    }
662
663    /// Get the MACD signal line reference.
664    pub fn signal_line(&self) -> MacdSignalRef {
665        MacdSignalRef {
666            fast: self.fast,
667            slow: self.slow,
668            signal: self.signal,
669        }
670    }
671
672    /// Get the MACD histogram reference.
673    pub fn histogram(&self) -> MacdHistogramRef {
674        MacdHistogramRef {
675            fast: self.fast,
676            slow: self.slow,
677            signal: self.signal,
678        }
679    }
680}
681
682/// Create a MACD configuration.
683///
684/// # Example
685///
686/// ```ignore
687/// use finance_query::backtesting::refs::*;
688///
689/// let m = macd(12, 26, 9);
690/// let bullish = m.line().crosses_above_ref(m.signal_line());
691/// let histogram_positive = m.histogram().above(0.0);
692/// ```
693#[inline]
694pub fn macd(fast: usize, slow: usize, signal: usize) -> MacdConfig {
695    MacdConfig { fast, slow, signal }
696}
697
698/// MACD Line reference.
699#[derive(Debug, Clone, Copy)]
700pub struct MacdLineRef {
701    /// Fast EMA period.
702    pub fast: usize,
703    /// Slow EMA period.
704    pub slow: usize,
705    /// Signal line period.
706    pub signal: usize,
707}
708
709impl IndicatorRef for MacdLineRef {
710    fn key(&self) -> String {
711        "macd_line".to_string()
712    }
713
714    fn required_indicators(&self) -> Vec<(String, Indicator)> {
715        vec![(
716            "macd".to_string(),
717            Indicator::Macd {
718                fast: self.fast,
719                slow: self.slow,
720                signal: self.signal,
721            },
722        )]
723    }
724
725    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
726        ctx.indicator("macd_line")
727    }
728
729    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
730        ctx.indicator_prev("macd_line")
731    }
732}
733
734/// MACD Signal Line reference.
735#[derive(Debug, Clone, Copy)]
736pub struct MacdSignalRef {
737    /// Fast EMA period.
738    pub fast: usize,
739    /// Slow EMA period.
740    pub slow: usize,
741    /// Signal line period.
742    pub signal: usize,
743}
744
745impl IndicatorRef for MacdSignalRef {
746    fn key(&self) -> String {
747        "macd_signal".to_string()
748    }
749
750    fn required_indicators(&self) -> Vec<(String, Indicator)> {
751        vec![(
752            "macd".to_string(),
753            Indicator::Macd {
754                fast: self.fast,
755                slow: self.slow,
756                signal: self.signal,
757            },
758        )]
759    }
760
761    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
762        ctx.indicator("macd_signal")
763    }
764
765    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
766        ctx.indicator_prev("macd_signal")
767    }
768}
769
770/// MACD Histogram reference.
771#[derive(Debug, Clone, Copy)]
772pub struct MacdHistogramRef {
773    /// Fast EMA period.
774    pub fast: usize,
775    /// Slow EMA period.
776    pub slow: usize,
777    /// Signal line period.
778    pub signal: usize,
779}
780
781impl IndicatorRef for MacdHistogramRef {
782    fn key(&self) -> String {
783        "macd_histogram".to_string()
784    }
785
786    fn required_indicators(&self) -> Vec<(String, Indicator)> {
787        vec![(
788            "macd".to_string(),
789            Indicator::Macd {
790                fast: self.fast,
791                slow: self.slow,
792                signal: self.signal,
793            },
794        )]
795    }
796
797    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
798        ctx.indicator("macd_histogram")
799    }
800
801    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
802        ctx.indicator_prev("macd_histogram")
803    }
804}
805
806// --- Bollinger Bands ---
807
808/// Bollinger Bands configuration.
809#[derive(Debug, Clone, Copy)]
810pub struct BollingerConfig {
811    /// SMA period
812    pub period: usize,
813    /// Standard deviation multiplier
814    pub std_dev: f64,
815}
816
817impl BollingerConfig {
818    /// Get the upper band reference.
819    pub fn upper(&self) -> BollingerUpperRef {
820        BollingerUpperRef {
821            period: self.period,
822            std_dev: self.std_dev,
823        }
824    }
825
826    /// Get the middle band (SMA) reference.
827    pub fn middle(&self) -> BollingerMiddleRef {
828        BollingerMiddleRef {
829            period: self.period,
830            std_dev: self.std_dev,
831        }
832    }
833
834    /// Get the lower band reference.
835    pub fn lower(&self) -> BollingerLowerRef {
836        BollingerLowerRef {
837            period: self.period,
838            std_dev: self.std_dev,
839        }
840    }
841}
842
843/// Create a Bollinger Bands configuration.
844///
845/// # Example
846///
847/// ```ignore
848/// use finance_query::backtesting::refs::*;
849///
850/// let bb = bollinger(20, 2.0);
851/// let at_lower_band = price().below_ref(bb.lower());
852/// let at_upper_band = price().above_ref(bb.upper());
853/// ```
854#[inline]
855pub fn bollinger(period: usize, std_dev: f64) -> BollingerConfig {
856    BollingerConfig { period, std_dev }
857}
858
859/// Bollinger upper band reference.
860#[derive(Debug, Clone, Copy)]
861pub struct BollingerUpperRef {
862    /// Moving average period.
863    pub period: usize,
864    /// Standard deviation multiplier.
865    pub std_dev: f64,
866}
867
868impl IndicatorRef for BollingerUpperRef {
869    fn key(&self) -> String {
870        "bollinger_upper".to_string()
871    }
872
873    fn required_indicators(&self) -> Vec<(String, Indicator)> {
874        vec![(
875            "bollinger".to_string(),
876            Indicator::Bollinger {
877                period: self.period,
878                std_dev: self.std_dev,
879            },
880        )]
881    }
882
883    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
884        ctx.indicator("bollinger_upper")
885    }
886
887    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
888        ctx.indicator_prev("bollinger_upper")
889    }
890}
891
892/// Bollinger middle band reference.
893#[derive(Debug, Clone, Copy)]
894pub struct BollingerMiddleRef {
895    /// Moving average period.
896    pub period: usize,
897    /// Standard deviation multiplier.
898    pub std_dev: f64,
899}
900
901impl IndicatorRef for BollingerMiddleRef {
902    fn key(&self) -> String {
903        "bollinger_middle".to_string()
904    }
905
906    fn required_indicators(&self) -> Vec<(String, Indicator)> {
907        vec![(
908            "bollinger".to_string(),
909            Indicator::Bollinger {
910                period: self.period,
911                std_dev: self.std_dev,
912            },
913        )]
914    }
915
916    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
917        ctx.indicator("bollinger_middle")
918    }
919
920    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
921        ctx.indicator_prev("bollinger_middle")
922    }
923}
924
925/// Bollinger lower band reference.
926#[derive(Debug, Clone, Copy)]
927pub struct BollingerLowerRef {
928    /// Moving average period.
929    pub period: usize,
930    /// Standard deviation multiplier.
931    pub std_dev: f64,
932}
933
934impl IndicatorRef for BollingerLowerRef {
935    fn key(&self) -> String {
936        "bollinger_lower".to_string()
937    }
938
939    fn required_indicators(&self) -> Vec<(String, Indicator)> {
940        vec![(
941            "bollinger".to_string(),
942            Indicator::Bollinger {
943                period: self.period,
944                std_dev: self.std_dev,
945            },
946        )]
947    }
948
949    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
950        ctx.indicator("bollinger_lower")
951    }
952
953    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
954        ctx.indicator_prev("bollinger_lower")
955    }
956}
957
958// --- Donchian Channels ---
959
960/// Donchian Channels configuration.
961#[derive(Debug, Clone, Copy)]
962pub struct DonchianConfig {
963    pub period: usize,
964}
965
966impl DonchianConfig {
967    /// Get the upper channel reference.
968    pub fn upper(&self) -> DonchianUpperRef {
969        DonchianUpperRef {
970            period: self.period,
971        }
972    }
973
974    /// Get the middle channel reference.
975    pub fn middle(&self) -> DonchianMiddleRef {
976        DonchianMiddleRef {
977            period: self.period,
978        }
979    }
980
981    /// Get the lower channel reference.
982    pub fn lower(&self) -> DonchianLowerRef {
983        DonchianLowerRef {
984            period: self.period,
985        }
986    }
987}
988
989/// Create a Donchian Channels configuration.
990#[inline]
991pub fn donchian(period: usize) -> DonchianConfig {
992    DonchianConfig { period }
993}
994
995/// Donchian upper channel reference.
996#[derive(Debug, Clone, Copy)]
997pub struct DonchianUpperRef {
998    pub period: usize,
999}
1000
1001impl IndicatorRef for DonchianUpperRef {
1002    fn key(&self) -> String {
1003        "donchian_upper".to_string()
1004    }
1005
1006    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1007        vec![(
1008            "donchian".to_string(),
1009            Indicator::DonchianChannels(self.period),
1010        )]
1011    }
1012
1013    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1014        ctx.indicator("donchian_upper")
1015    }
1016
1017    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1018        ctx.indicator_prev("donchian_upper")
1019    }
1020}
1021
1022/// Donchian middle channel reference.
1023#[derive(Debug, Clone, Copy)]
1024pub struct DonchianMiddleRef {
1025    pub period: usize,
1026}
1027
1028impl IndicatorRef for DonchianMiddleRef {
1029    fn key(&self) -> String {
1030        "donchian_middle".to_string()
1031    }
1032
1033    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1034        vec![(
1035            "donchian".to_string(),
1036            Indicator::DonchianChannels(self.period),
1037        )]
1038    }
1039
1040    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1041        ctx.indicator("donchian_middle")
1042    }
1043
1044    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1045        ctx.indicator_prev("donchian_middle")
1046    }
1047}
1048
1049/// Donchian lower channel reference.
1050#[derive(Debug, Clone, Copy)]
1051pub struct DonchianLowerRef {
1052    pub period: usize,
1053}
1054
1055impl IndicatorRef for DonchianLowerRef {
1056    fn key(&self) -> String {
1057        "donchian_lower".to_string()
1058    }
1059
1060    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1061        vec![(
1062            "donchian".to_string(),
1063            Indicator::DonchianChannels(self.period),
1064        )]
1065    }
1066
1067    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1068        ctx.indicator("donchian_lower")
1069    }
1070
1071    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1072        ctx.indicator_prev("donchian_lower")
1073    }
1074}
1075
1076// --- SuperTrend ---
1077
1078/// SuperTrend configuration.
1079#[derive(Debug, Clone, Copy)]
1080pub struct SupertrendConfig {
1081    pub period: usize,
1082    pub multiplier: f64,
1083}
1084
1085impl SupertrendConfig {
1086    /// Get the SuperTrend value reference.
1087    pub fn value(&self) -> SupertrendValueRef {
1088        SupertrendValueRef {
1089            period: self.period,
1090            multiplier: self.multiplier,
1091        }
1092    }
1093
1094    /// Get the SuperTrend uptrend indicator (1.0 = uptrend, 0.0 = downtrend).
1095    pub fn uptrend(&self) -> SupertrendUptrendRef {
1096        SupertrendUptrendRef {
1097            period: self.period,
1098            multiplier: self.multiplier,
1099        }
1100    }
1101}
1102
1103/// Create a SuperTrend configuration.
1104#[inline]
1105pub fn supertrend(period: usize, multiplier: f64) -> SupertrendConfig {
1106    SupertrendConfig { period, multiplier }
1107}
1108
1109/// SuperTrend value reference.
1110#[derive(Debug, Clone, Copy)]
1111pub struct SupertrendValueRef {
1112    pub period: usize,
1113    pub multiplier: f64,
1114}
1115
1116impl IndicatorRef for SupertrendValueRef {
1117    fn key(&self) -> String {
1118        "supertrend_value".to_string()
1119    }
1120
1121    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1122        vec![(
1123            "supertrend".to_string(),
1124            Indicator::Supertrend {
1125                period: self.period,
1126                multiplier: self.multiplier,
1127            },
1128        )]
1129    }
1130
1131    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1132        ctx.indicator("supertrend_value")
1133    }
1134
1135    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1136        ctx.indicator_prev("supertrend_value")
1137    }
1138}
1139
1140/// SuperTrend uptrend indicator reference.
1141/// Returns 1.0 for uptrend, 0.0 for downtrend.
1142#[derive(Debug, Clone, Copy)]
1143pub struct SupertrendUptrendRef {
1144    pub period: usize,
1145    pub multiplier: f64,
1146}
1147
1148impl IndicatorRef for SupertrendUptrendRef {
1149    fn key(&self) -> String {
1150        "supertrend_uptrend".to_string()
1151    }
1152
1153    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1154        vec![(
1155            "supertrend".to_string(),
1156            Indicator::Supertrend {
1157                period: self.period,
1158                multiplier: self.multiplier,
1159            },
1160        )]
1161    }
1162
1163    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1164        ctx.indicator("supertrend_uptrend")
1165    }
1166
1167    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1168        ctx.indicator_prev("supertrend_uptrend")
1169    }
1170}
1171
1172// --- Stochastic ---
1173
1174/// Stochastic Oscillator configuration.
1175#[derive(Debug, Clone, Copy)]
1176pub struct StochasticConfig {
1177    pub k_period: usize,
1178    pub k_slow: usize,
1179    pub d_period: usize,
1180}
1181
1182impl StochasticConfig {
1183    /// Get the %K line reference.
1184    pub fn k(&self) -> StochasticKRef {
1185        StochasticKRef {
1186            k_period: self.k_period,
1187            k_slow: self.k_slow,
1188            d_period: self.d_period,
1189        }
1190    }
1191
1192    /// Get the %D line reference.
1193    pub fn d(&self) -> StochasticDRef {
1194        StochasticDRef {
1195            k_period: self.k_period,
1196            k_slow: self.k_slow,
1197            d_period: self.d_period,
1198        }
1199    }
1200}
1201
1202/// Create a Stochastic Oscillator configuration.
1203#[inline]
1204pub fn stochastic(k_period: usize, k_slow: usize, d_period: usize) -> StochasticConfig {
1205    StochasticConfig {
1206        k_period,
1207        k_slow,
1208        d_period,
1209    }
1210}
1211
1212/// Stochastic %K line reference.
1213#[derive(Debug, Clone, Copy)]
1214pub struct StochasticKRef {
1215    pub k_period: usize,
1216    pub k_slow: usize,
1217    pub d_period: usize,
1218}
1219
1220impl IndicatorRef for StochasticKRef {
1221    fn key(&self) -> String {
1222        "stochastic_k".to_string()
1223    }
1224
1225    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1226        vec![(
1227            "stochastic".to_string(),
1228            Indicator::Stochastic {
1229                k_period: self.k_period,
1230                k_slow: self.k_slow,
1231                d_period: self.d_period,
1232            },
1233        )]
1234    }
1235
1236    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1237        ctx.indicator("stochastic_k")
1238    }
1239
1240    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1241        ctx.indicator_prev("stochastic_k")
1242    }
1243}
1244
1245/// Stochastic %D line reference.
1246#[derive(Debug, Clone, Copy)]
1247pub struct StochasticDRef {
1248    pub k_period: usize,
1249    pub k_slow: usize,
1250    pub d_period: usize,
1251}
1252
1253impl IndicatorRef for StochasticDRef {
1254    fn key(&self) -> String {
1255        "stochastic_d".to_string()
1256    }
1257
1258    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1259        vec![(
1260            "stochastic".to_string(),
1261            Indicator::Stochastic {
1262                k_period: self.k_period,
1263                k_slow: self.k_slow,
1264                d_period: self.d_period,
1265            },
1266        )]
1267    }
1268
1269    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1270        ctx.indicator("stochastic_d")
1271    }
1272
1273    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1274        ctx.indicator_prev("stochastic_d")
1275    }
1276}
1277
1278// --- Aroon ---
1279
1280/// Aroon indicator configuration.
1281#[derive(Debug, Clone, Copy)]
1282pub struct AroonConfig {
1283    pub period: usize,
1284}
1285
1286impl AroonConfig {
1287    /// Get the Aroon Up reference.
1288    pub fn up(&self) -> AroonUpRef {
1289        AroonUpRef {
1290            period: self.period,
1291        }
1292    }
1293
1294    /// Get the Aroon Down reference.
1295    pub fn down(&self) -> AroonDownRef {
1296        AroonDownRef {
1297            period: self.period,
1298        }
1299    }
1300}
1301
1302/// Create an Aroon indicator configuration.
1303#[inline]
1304pub fn aroon(period: usize) -> AroonConfig {
1305    AroonConfig { period }
1306}
1307
1308/// Aroon Up reference.
1309#[derive(Debug, Clone, Copy)]
1310pub struct AroonUpRef {
1311    pub period: usize,
1312}
1313
1314impl IndicatorRef for AroonUpRef {
1315    fn key(&self) -> String {
1316        "aroon_up".to_string()
1317    }
1318
1319    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1320        vec![("aroon".to_string(), Indicator::Aroon(self.period))]
1321    }
1322
1323    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1324        ctx.indicator("aroon_up")
1325    }
1326
1327    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1328        ctx.indicator_prev("aroon_up")
1329    }
1330}
1331
1332/// Aroon Down reference.
1333#[derive(Debug, Clone, Copy)]
1334pub struct AroonDownRef {
1335    pub period: usize,
1336}
1337
1338impl IndicatorRef for AroonDownRef {
1339    fn key(&self) -> String {
1340        "aroon_down".to_string()
1341    }
1342
1343    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1344        vec![("aroon".to_string(), Indicator::Aroon(self.period))]
1345    }
1346
1347    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1348        ctx.indicator("aroon_down")
1349    }
1350
1351    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1352        ctx.indicator_prev("aroon_down")
1353    }
1354}
1355
1356// --- Ichimoku Cloud ---
1357
1358/// Ichimoku Cloud configuration.
1359#[derive(Debug, Clone, Copy)]
1360pub struct IchimokuConfig {
1361    pub conversion: usize,
1362    pub base: usize,
1363    pub lagging: usize,
1364    pub displacement: usize,
1365}
1366
1367impl IchimokuConfig {
1368    /// Get the Tenkan-sen (Conversion Line) reference.
1369    pub fn conversion_line(&self) -> IchimokuConversionRef {
1370        IchimokuConversionRef {
1371            conversion: self.conversion,
1372            base: self.base,
1373            lagging: self.lagging,
1374            displacement: self.displacement,
1375        }
1376    }
1377
1378    /// Get the Kijun-sen (Base Line) reference.
1379    pub fn base_line(&self) -> IchimokuBaseRef {
1380        IchimokuBaseRef {
1381            conversion: self.conversion,
1382            base: self.base,
1383            lagging: self.lagging,
1384            displacement: self.displacement,
1385        }
1386    }
1387
1388    /// Get the Senkou Span A (Leading Span A) reference.
1389    pub fn leading_span_a(&self) -> IchimokuLeadingARef {
1390        IchimokuLeadingARef {
1391            conversion: self.conversion,
1392            base: self.base,
1393            lagging: self.lagging,
1394            displacement: self.displacement,
1395        }
1396    }
1397
1398    /// Get the Senkou Span B (Leading Span B) reference.
1399    pub fn leading_span_b(&self) -> IchimokuLeadingBRef {
1400        IchimokuLeadingBRef {
1401            conversion: self.conversion,
1402            base: self.base,
1403            lagging: self.lagging,
1404            displacement: self.displacement,
1405        }
1406    }
1407
1408    /// Get the Chikou Span (Lagging Span) reference.
1409    pub fn lagging_span(&self) -> IchimokuLaggingRef {
1410        IchimokuLaggingRef {
1411            conversion: self.conversion,
1412            base: self.base,
1413            lagging: self.lagging,
1414            displacement: self.displacement,
1415        }
1416    }
1417}
1418
1419/// Create an Ichimoku Cloud configuration with default periods (9, 26, 52, 26).
1420#[inline]
1421pub fn ichimoku() -> IchimokuConfig {
1422    IchimokuConfig {
1423        conversion: 9,
1424        base: 26,
1425        lagging: 52,
1426        displacement: 26,
1427    }
1428}
1429
1430/// Create an Ichimoku Cloud configuration with custom periods.
1431#[inline]
1432pub fn ichimoku_custom(
1433    conversion: usize,
1434    base: usize,
1435    lagging: usize,
1436    displacement: usize,
1437) -> IchimokuConfig {
1438    IchimokuConfig {
1439        conversion,
1440        base,
1441        lagging,
1442        displacement,
1443    }
1444}
1445
1446/// Ichimoku Conversion Line (Tenkan-sen) reference.
1447#[derive(Debug, Clone, Copy)]
1448pub struct IchimokuConversionRef {
1449    pub conversion: usize,
1450    pub base: usize,
1451    pub lagging: usize,
1452    pub displacement: usize,
1453}
1454
1455impl IndicatorRef for IchimokuConversionRef {
1456    fn key(&self) -> String {
1457        "ichimoku_conversion".to_string()
1458    }
1459
1460    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1461        vec![(
1462            "ichimoku".to_string(),
1463            Indicator::Ichimoku {
1464                conversion: self.conversion,
1465                base: self.base,
1466                lagging: self.lagging,
1467                displacement: self.displacement,
1468            },
1469        )]
1470    }
1471
1472    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1473        ctx.indicator("ichimoku_conversion")
1474    }
1475
1476    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1477        ctx.indicator_prev("ichimoku_conversion")
1478    }
1479}
1480
1481/// Ichimoku Base Line (Kijun-sen) reference.
1482#[derive(Debug, Clone, Copy)]
1483pub struct IchimokuBaseRef {
1484    pub conversion: usize,
1485    pub base: usize,
1486    pub lagging: usize,
1487    pub displacement: usize,
1488}
1489
1490impl IndicatorRef for IchimokuBaseRef {
1491    fn key(&self) -> String {
1492        "ichimoku_base".to_string()
1493    }
1494
1495    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1496        vec![(
1497            "ichimoku".to_string(),
1498            Indicator::Ichimoku {
1499                conversion: self.conversion,
1500                base: self.base,
1501                lagging: self.lagging,
1502                displacement: self.displacement,
1503            },
1504        )]
1505    }
1506
1507    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1508        ctx.indicator("ichimoku_base")
1509    }
1510
1511    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1512        ctx.indicator_prev("ichimoku_base")
1513    }
1514}
1515
1516/// Ichimoku Leading Span A (Senkou Span A) reference.
1517#[derive(Debug, Clone, Copy)]
1518pub struct IchimokuLeadingARef {
1519    pub conversion: usize,
1520    pub base: usize,
1521    pub lagging: usize,
1522    pub displacement: usize,
1523}
1524
1525impl IndicatorRef for IchimokuLeadingARef {
1526    fn key(&self) -> String {
1527        "ichimoku_leading_a".to_string()
1528    }
1529
1530    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1531        vec![(
1532            "ichimoku".to_string(),
1533            Indicator::Ichimoku {
1534                conversion: self.conversion,
1535                base: self.base,
1536                lagging: self.lagging,
1537                displacement: self.displacement,
1538            },
1539        )]
1540    }
1541
1542    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1543        ctx.indicator("ichimoku_leading_a")
1544    }
1545
1546    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1547        ctx.indicator_prev("ichimoku_leading_a")
1548    }
1549}
1550
1551/// Ichimoku Leading Span B (Senkou Span B) reference.
1552#[derive(Debug, Clone, Copy)]
1553pub struct IchimokuLeadingBRef {
1554    pub conversion: usize,
1555    pub base: usize,
1556    pub lagging: usize,
1557    pub displacement: usize,
1558}
1559
1560impl IndicatorRef for IchimokuLeadingBRef {
1561    fn key(&self) -> String {
1562        "ichimoku_leading_b".to_string()
1563    }
1564
1565    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1566        vec![(
1567            "ichimoku".to_string(),
1568            Indicator::Ichimoku {
1569                conversion: self.conversion,
1570                base: self.base,
1571                lagging: self.lagging,
1572                displacement: self.displacement,
1573            },
1574        )]
1575    }
1576
1577    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1578        ctx.indicator("ichimoku_leading_b")
1579    }
1580
1581    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1582        ctx.indicator_prev("ichimoku_leading_b")
1583    }
1584}
1585
1586/// Ichimoku Lagging Span (Chikou Span) reference.
1587#[derive(Debug, Clone, Copy)]
1588pub struct IchimokuLaggingRef {
1589    pub conversion: usize,
1590    pub base: usize,
1591    pub lagging: usize,
1592    pub displacement: usize,
1593}
1594
1595impl IndicatorRef for IchimokuLaggingRef {
1596    fn key(&self) -> String {
1597        "ichimoku_lagging".to_string()
1598    }
1599
1600    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1601        vec![(
1602            "ichimoku".to_string(),
1603            Indicator::Ichimoku {
1604                conversion: self.conversion,
1605                base: self.base,
1606                lagging: self.lagging,
1607                displacement: self.displacement,
1608            },
1609        )]
1610    }
1611
1612    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1613        ctx.indicator("ichimoku_lagging")
1614    }
1615
1616    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1617        ctx.indicator_prev("ichimoku_lagging")
1618    }
1619}
1620
1621// --- Keltner Channels ---
1622
1623/// Keltner Channels configuration.
1624#[derive(Debug, Clone, Copy)]
1625pub struct KeltnerConfig {
1626    pub period: usize,
1627    pub multiplier: f64,
1628    pub atr_period: usize,
1629}
1630
1631impl KeltnerConfig {
1632    /// Get the upper channel reference.
1633    pub fn upper(&self) -> KeltnerUpperRef {
1634        KeltnerUpperRef {
1635            period: self.period,
1636            multiplier: self.multiplier,
1637            atr_period: self.atr_period,
1638        }
1639    }
1640
1641    /// Get the middle channel (EMA) reference.
1642    pub fn middle(&self) -> KeltnerMiddleRef {
1643        KeltnerMiddleRef {
1644            period: self.period,
1645            multiplier: self.multiplier,
1646            atr_period: self.atr_period,
1647        }
1648    }
1649
1650    /// Get the lower channel reference.
1651    pub fn lower(&self) -> KeltnerLowerRef {
1652        KeltnerLowerRef {
1653            period: self.period,
1654            multiplier: self.multiplier,
1655            atr_period: self.atr_period,
1656        }
1657    }
1658}
1659
1660/// Create a Keltner Channels configuration.
1661#[inline]
1662pub fn keltner(period: usize, multiplier: f64, atr_period: usize) -> KeltnerConfig {
1663    KeltnerConfig {
1664        period,
1665        multiplier,
1666        atr_period,
1667    }
1668}
1669
1670/// Keltner upper channel reference.
1671#[derive(Debug, Clone, Copy)]
1672pub struct KeltnerUpperRef {
1673    pub period: usize,
1674    pub multiplier: f64,
1675    pub atr_period: usize,
1676}
1677
1678impl IndicatorRef for KeltnerUpperRef {
1679    fn key(&self) -> String {
1680        "keltner_upper".to_string()
1681    }
1682
1683    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1684        vec![(
1685            "keltner".to_string(),
1686            Indicator::KeltnerChannels {
1687                period: self.period,
1688                multiplier: self.multiplier,
1689                atr_period: self.atr_period,
1690            },
1691        )]
1692    }
1693
1694    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1695        ctx.indicator("keltner_upper")
1696    }
1697
1698    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1699        ctx.indicator_prev("keltner_upper")
1700    }
1701}
1702
1703/// Keltner middle channel reference.
1704#[derive(Debug, Clone, Copy)]
1705pub struct KeltnerMiddleRef {
1706    pub period: usize,
1707    pub multiplier: f64,
1708    pub atr_period: usize,
1709}
1710
1711impl IndicatorRef for KeltnerMiddleRef {
1712    fn key(&self) -> String {
1713        "keltner_middle".to_string()
1714    }
1715
1716    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1717        vec![(
1718            "keltner".to_string(),
1719            Indicator::KeltnerChannels {
1720                period: self.period,
1721                multiplier: self.multiplier,
1722                atr_period: self.atr_period,
1723            },
1724        )]
1725    }
1726
1727    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1728        ctx.indicator("keltner_middle")
1729    }
1730
1731    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1732        ctx.indicator_prev("keltner_middle")
1733    }
1734}
1735
1736/// Keltner lower channel reference.
1737#[derive(Debug, Clone, Copy)]
1738pub struct KeltnerLowerRef {
1739    pub period: usize,
1740    pub multiplier: f64,
1741    pub atr_period: usize,
1742}
1743
1744impl IndicatorRef for KeltnerLowerRef {
1745    fn key(&self) -> String {
1746        "keltner_lower".to_string()
1747    }
1748
1749    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1750        vec![(
1751            "keltner".to_string(),
1752            Indicator::KeltnerChannels {
1753                period: self.period,
1754                multiplier: self.multiplier,
1755                atr_period: self.atr_period,
1756            },
1757        )]
1758    }
1759
1760    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1761        ctx.indicator("keltner_lower")
1762    }
1763
1764    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1765        ctx.indicator_prev("keltner_lower")
1766    }
1767}
1768
1769// --- Parabolic SAR ---
1770
1771/// Parabolic SAR configuration.
1772#[derive(Debug, Clone, Copy)]
1773pub struct ParabolicSarConfig {
1774    pub step: f64,
1775    pub max: f64,
1776}
1777
1778/// Create a Parabolic SAR configuration.
1779#[inline]
1780pub fn parabolic_sar(step: f64, max: f64) -> ParabolicSarRef {
1781    ParabolicSarRef { step, max }
1782}
1783
1784/// Parabolic SAR reference.
1785#[derive(Debug, Clone, Copy)]
1786pub struct ParabolicSarRef {
1787    pub step: f64,
1788    pub max: f64,
1789}
1790
1791impl IndicatorRef for ParabolicSarRef {
1792    fn key(&self) -> String {
1793        format!("psar_{}_{}", self.step, self.max)
1794    }
1795
1796    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1797        vec![(
1798            self.key(),
1799            Indicator::ParabolicSar {
1800                step: self.step,
1801                max: self.max,
1802            },
1803        )]
1804    }
1805
1806    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1807        ctx.indicator(&self.key())
1808    }
1809
1810    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1811        ctx.indicator_prev(&self.key())
1812    }
1813}
1814
1815// --- ALMA ---
1816
1817/// ALMA (Arnaud Legoux Moving Average) configuration.
1818#[derive(Debug, Clone, Copy)]
1819pub struct AlmaConfig {
1820    pub period: usize,
1821    pub offset: f64,
1822    pub sigma: f64,
1823}
1824
1825/// Create an ALMA configuration.
1826#[inline]
1827pub fn alma(period: usize, offset: f64, sigma: f64) -> AlmaRef {
1828    AlmaRef {
1829        period,
1830        offset,
1831        sigma,
1832    }
1833}
1834
1835/// ALMA reference.
1836#[derive(Debug, Clone, Copy)]
1837pub struct AlmaRef {
1838    pub period: usize,
1839    pub offset: f64,
1840    pub sigma: f64,
1841}
1842
1843impl IndicatorRef for AlmaRef {
1844    fn key(&self) -> String {
1845        format!("alma_{}_{}_{}", self.period, self.offset, self.sigma)
1846    }
1847
1848    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1849        vec![(
1850            self.key(),
1851            Indicator::Alma {
1852                period: self.period,
1853                offset: self.offset,
1854                sigma: self.sigma,
1855            },
1856        )]
1857    }
1858
1859    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1860        ctx.indicator(&self.key())
1861    }
1862
1863    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1864        ctx.indicator_prev(&self.key())
1865    }
1866}
1867
1868// --- Stochastic RSI ---
1869
1870/// Stochastic RSI configuration.
1871#[derive(Debug, Clone, Copy)]
1872pub struct StochasticRsiConfig {
1873    pub rsi_period: usize,
1874    pub stoch_period: usize,
1875    pub k_period: usize,
1876    pub d_period: usize,
1877}
1878
1879/// Create a Stochastic RSI configuration.
1880#[inline]
1881pub fn stochastic_rsi(
1882    rsi_period: usize,
1883    stoch_period: usize,
1884    k_period: usize,
1885    d_period: usize,
1886) -> StochasticRsiRef {
1887    StochasticRsiRef {
1888        rsi_period,
1889        stoch_period,
1890        k_period,
1891        d_period,
1892    }
1893}
1894
1895/// Stochastic RSI reference.
1896#[derive(Debug, Clone, Copy)]
1897pub struct StochasticRsiRef {
1898    pub rsi_period: usize,
1899    pub stoch_period: usize,
1900    pub k_period: usize,
1901    pub d_period: usize,
1902}
1903
1904impl IndicatorRef for StochasticRsiRef {
1905    fn key(&self) -> String {
1906        format!(
1907            "stoch_rsi_{}_{}_{}_{}",
1908            self.rsi_period, self.stoch_period, self.k_period, self.d_period
1909        )
1910    }
1911
1912    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1913        vec![(
1914            self.key(),
1915            Indicator::StochasticRsi {
1916                rsi_period: self.rsi_period,
1917                stoch_period: self.stoch_period,
1918                k_period: self.k_period,
1919                d_period: self.d_period,
1920            },
1921        )]
1922    }
1923
1924    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1925        ctx.indicator(&self.key())
1926    }
1927
1928    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1929        ctx.indicator_prev(&self.key())
1930    }
1931}
1932
1933// --- Awesome Oscillator ---
1934
1935/// Awesome Oscillator reference (uses default 5/34 periods).
1936#[derive(Debug, Clone, Copy)]
1937pub struct AwesomeOscillatorRef {
1938    pub fast: usize,
1939    pub slow: usize,
1940}
1941
1942/// Create an Awesome Oscillator reference.
1943#[inline]
1944pub fn awesome_oscillator(fast: usize, slow: usize) -> AwesomeOscillatorRef {
1945    AwesomeOscillatorRef { fast, slow }
1946}
1947
1948impl IndicatorRef for AwesomeOscillatorRef {
1949    fn key(&self) -> String {
1950        format!("ao_{}_{}", self.fast, self.slow)
1951    }
1952
1953    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1954        vec![(
1955            self.key(),
1956            Indicator::AwesomeOscillator {
1957                fast: self.fast,
1958                slow: self.slow,
1959            },
1960        )]
1961    }
1962
1963    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1964        ctx.indicator(&self.key())
1965    }
1966
1967    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1968        ctx.indicator_prev(&self.key())
1969    }
1970}
1971
1972// --- Coppock Curve ---
1973
1974/// Coppock Curve reference.
1975#[derive(Debug, Clone, Copy)]
1976pub struct CoppockCurveRef {
1977    pub wma_period: usize,
1978    pub long_roc: usize,
1979    pub short_roc: usize,
1980}
1981
1982/// Create a Coppock Curve reference (uses default 10/14/11 periods).
1983#[inline]
1984pub fn coppock_curve(wma_period: usize, long_roc: usize, short_roc: usize) -> CoppockCurveRef {
1985    CoppockCurveRef {
1986        wma_period,
1987        long_roc,
1988        short_roc,
1989    }
1990}
1991
1992impl IndicatorRef for CoppockCurveRef {
1993    fn key(&self) -> String {
1994        format!(
1995            "coppock_{}_{}_{}",
1996            self.wma_period, self.long_roc, self.short_roc
1997        )
1998    }
1999
2000    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2001        vec![(
2002            self.key(),
2003            Indicator::CoppockCurve {
2004                wma_period: self.wma_period,
2005                long_roc: self.long_roc,
2006                short_roc: self.short_roc,
2007            },
2008        )]
2009    }
2010
2011    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2012        ctx.indicator(&self.key())
2013    }
2014
2015    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2016        ctx.indicator_prev(&self.key())
2017    }
2018}
2019
2020// --- Choppiness Index ---
2021
2022/// Choppiness Index reference.
2023#[derive(Debug, Clone, Copy)]
2024pub struct ChoppinessIndexRef(pub usize);
2025
2026/// Create a Choppiness Index reference.
2027#[inline]
2028pub fn choppiness_index(period: usize) -> ChoppinessIndexRef {
2029    ChoppinessIndexRef(period)
2030}
2031
2032impl IndicatorRef for ChoppinessIndexRef {
2033    fn key(&self) -> String {
2034        format!("chop_{}", self.0)
2035    }
2036
2037    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2038        vec![(self.key(), Indicator::ChoppinessIndex(self.0))]
2039    }
2040
2041    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2042        ctx.indicator(&self.key())
2043    }
2044
2045    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2046        ctx.indicator_prev(&self.key())
2047    }
2048}
2049
2050// --- True Range ---
2051
2052/// True Range reference.
2053#[derive(Debug, Clone, Copy)]
2054pub struct TrueRangeRef;
2055
2056/// Create a True Range reference.
2057#[inline]
2058pub fn true_range() -> TrueRangeRef {
2059    TrueRangeRef
2060}
2061
2062impl IndicatorRef for TrueRangeRef {
2063    fn key(&self) -> String {
2064        "true_range".to_string()
2065    }
2066
2067    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2068        vec![(self.key(), Indicator::TrueRange)]
2069    }
2070
2071    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2072        ctx.indicator(&self.key())
2073    }
2074
2075    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2076        ctx.indicator_prev(&self.key())
2077    }
2078}
2079
2080// --- Chaikin Oscillator ---
2081
2082/// Chaikin Oscillator reference.
2083#[derive(Debug, Clone, Copy)]
2084pub struct ChaikinOscillatorRef;
2085
2086/// Create a Chaikin Oscillator reference.
2087#[inline]
2088pub fn chaikin_oscillator() -> ChaikinOscillatorRef {
2089    ChaikinOscillatorRef
2090}
2091
2092impl IndicatorRef for ChaikinOscillatorRef {
2093    fn key(&self) -> String {
2094        "chaikin_osc".to_string()
2095    }
2096
2097    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2098        vec![(self.key(), Indicator::ChaikinOscillator)]
2099    }
2100
2101    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2102        ctx.indicator(&self.key())
2103    }
2104
2105    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2106        ctx.indicator_prev(&self.key())
2107    }
2108}
2109
2110// --- Accumulation/Distribution ---
2111
2112/// Accumulation/Distribution reference.
2113#[derive(Debug, Clone, Copy)]
2114pub struct AccumulationDistributionRef;
2115
2116/// Create an Accumulation/Distribution reference.
2117#[inline]
2118pub fn accumulation_distribution() -> AccumulationDistributionRef {
2119    AccumulationDistributionRef
2120}
2121
2122impl IndicatorRef for AccumulationDistributionRef {
2123    fn key(&self) -> String {
2124        "ad".to_string()
2125    }
2126
2127    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2128        vec![(self.key(), Indicator::AccumulationDistribution)]
2129    }
2130
2131    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2132        ctx.indicator(&self.key())
2133    }
2134
2135    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2136        ctx.indicator_prev(&self.key())
2137    }
2138}
2139
2140// --- Balance of Power ---
2141
2142/// Balance of Power reference.
2143#[derive(Debug, Clone, Copy)]
2144pub struct BalanceOfPowerRef(pub Option<usize>);
2145
2146/// Create a Balance of Power reference.
2147#[inline]
2148pub fn balance_of_power(period: Option<usize>) -> BalanceOfPowerRef {
2149    BalanceOfPowerRef(period)
2150}
2151
2152impl IndicatorRef for BalanceOfPowerRef {
2153    fn key(&self) -> String {
2154        match self.0 {
2155            Some(p) => format!("bop_{}", p),
2156            None => "bop".to_string(),
2157        }
2158    }
2159
2160    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2161        vec![(self.key(), Indicator::BalanceOfPower(self.0))]
2162    }
2163
2164    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2165        ctx.indicator(&self.key())
2166    }
2167
2168    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2169        ctx.indicator_prev(&self.key())
2170    }
2171}
2172
2173// --- Bull/Bear Power ---
2174
2175/// Bull Power reference.
2176#[derive(Debug, Clone, Copy)]
2177pub struct BullPowerRef(pub usize);
2178
2179/// Create a Bull Power reference.
2180#[inline]
2181pub fn bull_power(period: usize) -> BullPowerRef {
2182    BullPowerRef(period)
2183}
2184
2185impl IndicatorRef for BullPowerRef {
2186    fn key(&self) -> String {
2187        "bull_power".to_string()
2188    }
2189
2190    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2191        vec![(self.key(), Indicator::BullBearPower(self.0))]
2192    }
2193
2194    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2195        ctx.indicator("bull_power")
2196    }
2197
2198    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2199        ctx.indicator_prev("bull_power")
2200    }
2201}
2202
2203/// Bear Power reference.
2204#[derive(Debug, Clone, Copy)]
2205pub struct BearPowerRef(pub usize);
2206
2207/// Create a Bear Power reference.
2208#[inline]
2209pub fn bear_power(period: usize) -> BearPowerRef {
2210    BearPowerRef(period)
2211}
2212
2213impl IndicatorRef for BearPowerRef {
2214    fn key(&self) -> String {
2215        "bear_power".to_string()
2216    }
2217
2218    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2219        vec![(self.key(), Indicator::BullBearPower(self.0))]
2220    }
2221
2222    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2223        ctx.indicator("bear_power")
2224    }
2225
2226    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2227        ctx.indicator_prev("bear_power")
2228    }
2229}
2230
2231// --- Elder Ray ---
2232
2233/// Elder Ray Bull Power reference.
2234#[derive(Debug, Clone, Copy)]
2235pub struct ElderBullPowerRef(pub usize);
2236
2237/// Create an Elder Ray Bull Power reference.
2238#[inline]
2239pub fn elder_bull_power(period: usize) -> ElderBullPowerRef {
2240    ElderBullPowerRef(period)
2241}
2242
2243impl IndicatorRef for ElderBullPowerRef {
2244    fn key(&self) -> String {
2245        "elder_bull".to_string()
2246    }
2247
2248    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2249        vec![(self.key(), Indicator::ElderRay(self.0))]
2250    }
2251
2252    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2253        ctx.indicator("elder_bull")
2254    }
2255
2256    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2257        ctx.indicator_prev("elder_bull")
2258    }
2259}
2260
2261/// Elder Ray Bear Power reference.
2262#[derive(Debug, Clone, Copy)]
2263pub struct ElderBearPowerRef(pub usize);
2264
2265/// Create an Elder Ray Bear Power reference.
2266#[inline]
2267pub fn elder_bear_power(period: usize) -> ElderBearPowerRef {
2268    ElderBearPowerRef(period)
2269}
2270
2271impl IndicatorRef for ElderBearPowerRef {
2272    fn key(&self) -> String {
2273        "elder_bear".to_string()
2274    }
2275
2276    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2277        vec![(self.key(), Indicator::ElderRay(self.0))]
2278    }
2279
2280    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2281        ctx.indicator("elder_bear")
2282    }
2283
2284    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2285        ctx.indicator_prev("elder_bear")
2286    }
2287}
2288
2289#[cfg(test)]
2290mod tests {
2291    use super::*;
2292
2293    #[test]
2294    fn test_moving_average_keys() {
2295        assert_eq!(sma(20).key(), "sma_20");
2296        assert_eq!(ema(12).key(), "ema_12");
2297        assert_eq!(wma(14).key(), "wma_14");
2298        assert_eq!(dema(21).key(), "dema_21");
2299        assert_eq!(tema(21).key(), "tema_21");
2300        assert_eq!(hma(9).key(), "hma_9");
2301        assert_eq!(vwma(20).key(), "vwma_20");
2302        assert_eq!(mcginley(14).key(), "mcginley_14");
2303        assert_eq!(alma(9, 0.85, 6.0).key(), "alma_9_0.85_6");
2304    }
2305
2306    #[test]
2307    fn test_oscillator_keys() {
2308        assert_eq!(rsi(14).key(), "rsi_14");
2309        assert_eq!(cci(20).key(), "cci_20");
2310        assert_eq!(williams_r(14).key(), "williams_r_14");
2311        assert_eq!(cmo(14).key(), "cmo_14");
2312        assert_eq!(stochastic_rsi(14, 14, 3, 3).key(), "stoch_rsi_14_14_3_3");
2313        assert_eq!(awesome_oscillator(5, 34).key(), "ao_5_34");
2314        assert_eq!(choppiness_index(14).key(), "chop_14");
2315    }
2316
2317    #[test]
2318    fn test_macd_keys() {
2319        let m = macd(12, 26, 9);
2320        assert_eq!(m.line().key(), "macd_line");
2321        assert_eq!(m.signal_line().key(), "macd_signal");
2322        assert_eq!(m.histogram().key(), "macd_histogram");
2323    }
2324
2325    #[test]
2326    fn test_bollinger_keys() {
2327        let bb = bollinger(20, 2.0);
2328        assert_eq!(bb.upper().key(), "bollinger_upper");
2329        assert_eq!(bb.middle().key(), "bollinger_middle");
2330        assert_eq!(bb.lower().key(), "bollinger_lower");
2331    }
2332
2333    #[test]
2334    fn test_donchian_keys() {
2335        let dc = donchian(20);
2336        assert_eq!(dc.upper().key(), "donchian_upper");
2337        assert_eq!(dc.middle().key(), "donchian_middle");
2338        assert_eq!(dc.lower().key(), "donchian_lower");
2339    }
2340
2341    #[test]
2342    fn test_supertrend_keys() {
2343        let st = supertrend(10, 3.0);
2344        assert_eq!(st.value().key(), "supertrend_value");
2345        assert_eq!(st.uptrend().key(), "supertrend_uptrend");
2346    }
2347
2348    #[test]
2349    fn test_stochastic_keys() {
2350        let stoch = stochastic(14, 3, 3);
2351        assert_eq!(stoch.k().key(), "stochastic_k");
2352        assert_eq!(stoch.d().key(), "stochastic_d");
2353    }
2354
2355    #[test]
2356    fn test_aroon_keys() {
2357        let ar = aroon(25);
2358        assert_eq!(ar.up().key(), "aroon_up");
2359        assert_eq!(ar.down().key(), "aroon_down");
2360    }
2361
2362    #[test]
2363    fn test_ichimoku_keys() {
2364        let ich = ichimoku();
2365        assert_eq!(ich.conversion_line().key(), "ichimoku_conversion");
2366        assert_eq!(ich.base_line().key(), "ichimoku_base");
2367        assert_eq!(ich.leading_span_a().key(), "ichimoku_leading_a");
2368        assert_eq!(ich.leading_span_b().key(), "ichimoku_leading_b");
2369        assert_eq!(ich.lagging_span().key(), "ichimoku_lagging");
2370    }
2371
2372    #[test]
2373    fn test_keltner_keys() {
2374        let kc = keltner(20, 2.0, 10);
2375        assert_eq!(kc.upper().key(), "keltner_upper");
2376        assert_eq!(kc.middle().key(), "keltner_middle");
2377        assert_eq!(kc.lower().key(), "keltner_lower");
2378    }
2379
2380    #[test]
2381    fn test_volume_keys() {
2382        assert_eq!(obv().key(), "obv");
2383        assert_eq!(vwap().key(), "vwap");
2384        assert_eq!(mfi(14).key(), "mfi_14");
2385        assert_eq!(cmf(20).key(), "cmf_20");
2386        assert_eq!(chaikin_oscillator().key(), "chaikin_osc");
2387        assert_eq!(accumulation_distribution().key(), "ad");
2388        assert_eq!(balance_of_power(Some(14)).key(), "bop_14");
2389        assert_eq!(balance_of_power(None).key(), "bop");
2390    }
2391
2392    #[test]
2393    fn test_power_keys() {
2394        assert_eq!(bull_power(13).key(), "bull_power");
2395        assert_eq!(bear_power(13).key(), "bear_power");
2396        assert_eq!(elder_bull_power(13).key(), "elder_bull");
2397        assert_eq!(elder_bear_power(13).key(), "elder_bear");
2398    }
2399
2400    #[test]
2401    fn test_other_keys() {
2402        assert_eq!(parabolic_sar(0.02, 0.2).key(), "psar_0.02_0.2");
2403        assert_eq!(true_range().key(), "true_range");
2404        assert_eq!(coppock_curve(10, 14, 11).key(), "coppock_10_14_11");
2405    }
2406
2407    #[test]
2408    fn test_required_indicators() {
2409        let sma_ref = sma(20);
2410        let indicators = sma_ref.required_indicators();
2411        assert_eq!(indicators.len(), 1);
2412        assert_eq!(indicators[0].0, "sma_20");
2413        assert!(matches!(indicators[0].1, Indicator::Sma(20)));
2414    }
2415}