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        format!("macd_line_{}_{}_{}", self.fast, self.slow, self.signal)
712    }
713
714    fn required_indicators(&self) -> Vec<(String, Indicator)> {
715        vec![(
716            self.key(),
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(&self.key())
727    }
728
729    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
730        ctx.indicator_prev(&self.key())
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        format!("macd_signal_{}_{}_{}", self.fast, self.slow, self.signal)
748    }
749
750    fn required_indicators(&self) -> Vec<(String, Indicator)> {
751        vec![(
752            self.key(),
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(&self.key())
763    }
764
765    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
766        ctx.indicator_prev(&self.key())
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        format!("macd_histogram_{}_{}_{}", self.fast, self.slow, self.signal)
784    }
785
786    fn required_indicators(&self) -> Vec<(String, Indicator)> {
787        vec![(
788            self.key(),
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(&self.key())
799    }
800
801    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
802        ctx.indicator_prev(&self.key())
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        format!("bollinger_upper_{}_{}", self.period, self.std_dev)
871    }
872
873    fn required_indicators(&self) -> Vec<(String, Indicator)> {
874        vec![(
875            self.key(),
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(&self.key())
885    }
886
887    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
888        ctx.indicator_prev(&self.key())
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        format!("bollinger_middle_{}_{}", self.period, self.std_dev)
904    }
905
906    fn required_indicators(&self) -> Vec<(String, Indicator)> {
907        vec![(
908            self.key(),
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(&self.key())
918    }
919
920    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
921        ctx.indicator_prev(&self.key())
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        format!("bollinger_lower_{}_{}", self.period, self.std_dev)
937    }
938
939    fn required_indicators(&self) -> Vec<(String, Indicator)> {
940        vec![(
941            self.key(),
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(&self.key())
951    }
952
953    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
954        ctx.indicator_prev(&self.key())
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        format!("donchian_upper_{}", self.period)
1004    }
1005
1006    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1007        vec![(self.key(), Indicator::DonchianChannels(self.period))]
1008    }
1009
1010    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1011        ctx.indicator(&self.key())
1012    }
1013
1014    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1015        ctx.indicator_prev(&self.key())
1016    }
1017}
1018
1019/// Donchian middle channel reference.
1020#[derive(Debug, Clone, Copy)]
1021pub struct DonchianMiddleRef {
1022    pub period: usize,
1023}
1024
1025impl IndicatorRef for DonchianMiddleRef {
1026    fn key(&self) -> String {
1027        format!("donchian_middle_{}", self.period)
1028    }
1029
1030    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1031        vec![(self.key(), Indicator::DonchianChannels(self.period))]
1032    }
1033
1034    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1035        ctx.indicator(&self.key())
1036    }
1037
1038    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1039        ctx.indicator_prev(&self.key())
1040    }
1041}
1042
1043/// Donchian lower channel reference.
1044#[derive(Debug, Clone, Copy)]
1045pub struct DonchianLowerRef {
1046    pub period: usize,
1047}
1048
1049impl IndicatorRef for DonchianLowerRef {
1050    fn key(&self) -> String {
1051        format!("donchian_lower_{}", self.period)
1052    }
1053
1054    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1055        vec![(self.key(), Indicator::DonchianChannels(self.period))]
1056    }
1057
1058    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1059        ctx.indicator(&self.key())
1060    }
1061
1062    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1063        ctx.indicator_prev(&self.key())
1064    }
1065}
1066
1067// --- SuperTrend ---
1068
1069/// SuperTrend configuration.
1070#[derive(Debug, Clone, Copy)]
1071pub struct SupertrendConfig {
1072    pub period: usize,
1073    pub multiplier: f64,
1074}
1075
1076impl SupertrendConfig {
1077    /// Get the SuperTrend value reference.
1078    pub fn value(&self) -> SupertrendValueRef {
1079        SupertrendValueRef {
1080            period: self.period,
1081            multiplier: self.multiplier,
1082        }
1083    }
1084
1085    /// Get the SuperTrend uptrend indicator (1.0 = uptrend, 0.0 = downtrend).
1086    pub fn uptrend(&self) -> SupertrendUptrendRef {
1087        SupertrendUptrendRef {
1088            period: self.period,
1089            multiplier: self.multiplier,
1090        }
1091    }
1092}
1093
1094/// Create a SuperTrend configuration.
1095#[inline]
1096pub fn supertrend(period: usize, multiplier: f64) -> SupertrendConfig {
1097    SupertrendConfig { period, multiplier }
1098}
1099
1100/// SuperTrend value reference.
1101#[derive(Debug, Clone, Copy)]
1102pub struct SupertrendValueRef {
1103    pub period: usize,
1104    pub multiplier: f64,
1105}
1106
1107impl IndicatorRef for SupertrendValueRef {
1108    fn key(&self) -> String {
1109        format!("supertrend_value_{}_{}", self.period, self.multiplier)
1110    }
1111
1112    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1113        vec![(
1114            self.key(),
1115            Indicator::Supertrend {
1116                period: self.period,
1117                multiplier: self.multiplier,
1118            },
1119        )]
1120    }
1121
1122    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1123        ctx.indicator(&self.key())
1124    }
1125
1126    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1127        ctx.indicator_prev(&self.key())
1128    }
1129}
1130
1131/// SuperTrend uptrend indicator reference.
1132/// Returns 1.0 for uptrend, 0.0 for downtrend.
1133#[derive(Debug, Clone, Copy)]
1134pub struct SupertrendUptrendRef {
1135    pub period: usize,
1136    pub multiplier: f64,
1137}
1138
1139impl IndicatorRef for SupertrendUptrendRef {
1140    fn key(&self) -> String {
1141        format!("supertrend_uptrend_{}_{}", self.period, self.multiplier)
1142    }
1143
1144    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1145        vec![(
1146            self.key(),
1147            Indicator::Supertrend {
1148                period: self.period,
1149                multiplier: self.multiplier,
1150            },
1151        )]
1152    }
1153
1154    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1155        ctx.indicator(&self.key())
1156    }
1157
1158    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1159        ctx.indicator_prev(&self.key())
1160    }
1161}
1162
1163// --- Stochastic ---
1164
1165/// Stochastic Oscillator configuration.
1166#[derive(Debug, Clone, Copy)]
1167pub struct StochasticConfig {
1168    pub k_period: usize,
1169    pub k_slow: usize,
1170    pub d_period: usize,
1171}
1172
1173impl StochasticConfig {
1174    /// Get the %K line reference.
1175    pub fn k(&self) -> StochasticKRef {
1176        StochasticKRef {
1177            k_period: self.k_period,
1178            k_slow: self.k_slow,
1179            d_period: self.d_period,
1180        }
1181    }
1182
1183    /// Get the %D line reference.
1184    pub fn d(&self) -> StochasticDRef {
1185        StochasticDRef {
1186            k_period: self.k_period,
1187            k_slow: self.k_slow,
1188            d_period: self.d_period,
1189        }
1190    }
1191}
1192
1193/// Create a Stochastic Oscillator configuration.
1194#[inline]
1195pub fn stochastic(k_period: usize, k_slow: usize, d_period: usize) -> StochasticConfig {
1196    StochasticConfig {
1197        k_period,
1198        k_slow,
1199        d_period,
1200    }
1201}
1202
1203/// Stochastic %K line reference.
1204#[derive(Debug, Clone, Copy)]
1205pub struct StochasticKRef {
1206    pub k_period: usize,
1207    pub k_slow: usize,
1208    pub d_period: usize,
1209}
1210
1211impl IndicatorRef for StochasticKRef {
1212    fn key(&self) -> String {
1213        format!(
1214            "stochastic_k_{}_{}_{}",
1215            self.k_period, self.k_slow, self.d_period
1216        )
1217    }
1218
1219    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1220        vec![(
1221            self.key(),
1222            Indicator::Stochastic {
1223                k_period: self.k_period,
1224                k_slow: self.k_slow,
1225                d_period: self.d_period,
1226            },
1227        )]
1228    }
1229
1230    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1231        ctx.indicator(&self.key())
1232    }
1233
1234    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1235        ctx.indicator_prev(&self.key())
1236    }
1237}
1238
1239/// Stochastic %D line reference.
1240#[derive(Debug, Clone, Copy)]
1241pub struct StochasticDRef {
1242    pub k_period: usize,
1243    pub k_slow: usize,
1244    pub d_period: usize,
1245}
1246
1247impl IndicatorRef for StochasticDRef {
1248    fn key(&self) -> String {
1249        format!(
1250            "stochastic_d_{}_{}_{}",
1251            self.k_period, self.k_slow, self.d_period
1252        )
1253    }
1254
1255    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1256        vec![(
1257            self.key(),
1258            Indicator::Stochastic {
1259                k_period: self.k_period,
1260                k_slow: self.k_slow,
1261                d_period: self.d_period,
1262            },
1263        )]
1264    }
1265
1266    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1267        ctx.indicator(&self.key())
1268    }
1269
1270    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1271        ctx.indicator_prev(&self.key())
1272    }
1273}
1274
1275// --- Aroon ---
1276
1277/// Aroon indicator configuration.
1278#[derive(Debug, Clone, Copy)]
1279pub struct AroonConfig {
1280    pub period: usize,
1281}
1282
1283impl AroonConfig {
1284    /// Get the Aroon Up reference.
1285    pub fn up(&self) -> AroonUpRef {
1286        AroonUpRef {
1287            period: self.period,
1288        }
1289    }
1290
1291    /// Get the Aroon Down reference.
1292    pub fn down(&self) -> AroonDownRef {
1293        AroonDownRef {
1294            period: self.period,
1295        }
1296    }
1297}
1298
1299/// Create an Aroon indicator configuration.
1300#[inline]
1301pub fn aroon(period: usize) -> AroonConfig {
1302    AroonConfig { period }
1303}
1304
1305/// Aroon Up reference.
1306#[derive(Debug, Clone, Copy)]
1307pub struct AroonUpRef {
1308    pub period: usize,
1309}
1310
1311impl IndicatorRef for AroonUpRef {
1312    fn key(&self) -> String {
1313        format!("aroon_up_{}", self.period)
1314    }
1315
1316    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1317        vec![(self.key(), Indicator::Aroon(self.period))]
1318    }
1319
1320    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1321        ctx.indicator(&self.key())
1322    }
1323
1324    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1325        ctx.indicator_prev(&self.key())
1326    }
1327}
1328
1329/// Aroon Down reference.
1330#[derive(Debug, Clone, Copy)]
1331pub struct AroonDownRef {
1332    pub period: usize,
1333}
1334
1335impl IndicatorRef for AroonDownRef {
1336    fn key(&self) -> String {
1337        format!("aroon_down_{}", self.period)
1338    }
1339
1340    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1341        vec![(self.key(), Indicator::Aroon(self.period))]
1342    }
1343
1344    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1345        ctx.indicator(&self.key())
1346    }
1347
1348    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1349        ctx.indicator_prev(&self.key())
1350    }
1351}
1352
1353// --- Ichimoku Cloud ---
1354
1355/// Ichimoku Cloud configuration.
1356#[derive(Debug, Clone, Copy)]
1357pub struct IchimokuConfig {
1358    pub conversion: usize,
1359    pub base: usize,
1360    pub lagging: usize,
1361    pub displacement: usize,
1362}
1363
1364impl IchimokuConfig {
1365    /// Get the Tenkan-sen (Conversion Line) reference.
1366    pub fn conversion_line(&self) -> IchimokuConversionRef {
1367        IchimokuConversionRef {
1368            conversion: self.conversion,
1369            base: self.base,
1370            lagging: self.lagging,
1371            displacement: self.displacement,
1372        }
1373    }
1374
1375    /// Get the Kijun-sen (Base Line) reference.
1376    pub fn base_line(&self) -> IchimokuBaseRef {
1377        IchimokuBaseRef {
1378            conversion: self.conversion,
1379            base: self.base,
1380            lagging: self.lagging,
1381            displacement: self.displacement,
1382        }
1383    }
1384
1385    /// Get the Senkou Span A (Leading Span A) reference.
1386    pub fn leading_span_a(&self) -> IchimokuLeadingARef {
1387        IchimokuLeadingARef {
1388            conversion: self.conversion,
1389            base: self.base,
1390            lagging: self.lagging,
1391            displacement: self.displacement,
1392        }
1393    }
1394
1395    /// Get the Senkou Span B (Leading Span B) reference.
1396    pub fn leading_span_b(&self) -> IchimokuLeadingBRef {
1397        IchimokuLeadingBRef {
1398            conversion: self.conversion,
1399            base: self.base,
1400            lagging: self.lagging,
1401            displacement: self.displacement,
1402        }
1403    }
1404
1405    /// Get the Chikou Span (Lagging Span) reference.
1406    pub fn lagging_span(&self) -> IchimokuLaggingRef {
1407        IchimokuLaggingRef {
1408            conversion: self.conversion,
1409            base: self.base,
1410            lagging: self.lagging,
1411            displacement: self.displacement,
1412        }
1413    }
1414}
1415
1416/// Create an Ichimoku Cloud configuration with default periods (9, 26, 52, 26).
1417#[inline]
1418pub fn ichimoku() -> IchimokuConfig {
1419    IchimokuConfig {
1420        conversion: 9,
1421        base: 26,
1422        lagging: 52,
1423        displacement: 26,
1424    }
1425}
1426
1427/// Create an Ichimoku Cloud configuration with custom periods.
1428#[inline]
1429pub fn ichimoku_custom(
1430    conversion: usize,
1431    base: usize,
1432    lagging: usize,
1433    displacement: usize,
1434) -> IchimokuConfig {
1435    IchimokuConfig {
1436        conversion,
1437        base,
1438        lagging,
1439        displacement,
1440    }
1441}
1442
1443/// Ichimoku Conversion Line (Tenkan-sen) reference.
1444#[derive(Debug, Clone, Copy)]
1445pub struct IchimokuConversionRef {
1446    pub conversion: usize,
1447    pub base: usize,
1448    pub lagging: usize,
1449    pub displacement: usize,
1450}
1451
1452impl IndicatorRef for IchimokuConversionRef {
1453    fn key(&self) -> String {
1454        format!(
1455            "ichimoku_conversion_{}_{}_{}_{}",
1456            self.conversion, self.base, self.lagging, self.displacement
1457        )
1458    }
1459
1460    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1461        vec![(
1462            self.key(),
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(&self.key())
1474    }
1475
1476    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1477        ctx.indicator_prev(&self.key())
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        format!(
1493            "ichimoku_base_{}_{}_{}_{}",
1494            self.conversion, self.base, self.lagging, self.displacement
1495        )
1496    }
1497
1498    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1499        vec![(
1500            self.key(),
1501            Indicator::Ichimoku {
1502                conversion: self.conversion,
1503                base: self.base,
1504                lagging: self.lagging,
1505                displacement: self.displacement,
1506            },
1507        )]
1508    }
1509
1510    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1511        ctx.indicator(&self.key())
1512    }
1513
1514    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1515        ctx.indicator_prev(&self.key())
1516    }
1517}
1518
1519/// Ichimoku Leading Span A (Senkou Span A) reference.
1520#[derive(Debug, Clone, Copy)]
1521pub struct IchimokuLeadingARef {
1522    pub conversion: usize,
1523    pub base: usize,
1524    pub lagging: usize,
1525    pub displacement: usize,
1526}
1527
1528impl IndicatorRef for IchimokuLeadingARef {
1529    fn key(&self) -> String {
1530        format!(
1531            "ichimoku_leading_a_{}_{}_{}_{}",
1532            self.conversion, self.base, self.lagging, self.displacement
1533        )
1534    }
1535
1536    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1537        vec![(
1538            self.key(),
1539            Indicator::Ichimoku {
1540                conversion: self.conversion,
1541                base: self.base,
1542                lagging: self.lagging,
1543                displacement: self.displacement,
1544            },
1545        )]
1546    }
1547
1548    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1549        ctx.indicator(&self.key())
1550    }
1551
1552    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1553        ctx.indicator_prev(&self.key())
1554    }
1555}
1556
1557/// Ichimoku Leading Span B (Senkou Span B) reference.
1558#[derive(Debug, Clone, Copy)]
1559pub struct IchimokuLeadingBRef {
1560    pub conversion: usize,
1561    pub base: usize,
1562    pub lagging: usize,
1563    pub displacement: usize,
1564}
1565
1566impl IndicatorRef for IchimokuLeadingBRef {
1567    fn key(&self) -> String {
1568        format!(
1569            "ichimoku_leading_b_{}_{}_{}_{}",
1570            self.conversion, self.base, self.lagging, self.displacement
1571        )
1572    }
1573
1574    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1575        vec![(
1576            self.key(),
1577            Indicator::Ichimoku {
1578                conversion: self.conversion,
1579                base: self.base,
1580                lagging: self.lagging,
1581                displacement: self.displacement,
1582            },
1583        )]
1584    }
1585
1586    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1587        ctx.indicator(&self.key())
1588    }
1589
1590    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1591        ctx.indicator_prev(&self.key())
1592    }
1593}
1594
1595/// Ichimoku Lagging Span (Chikou Span) reference.
1596#[derive(Debug, Clone, Copy)]
1597pub struct IchimokuLaggingRef {
1598    pub conversion: usize,
1599    pub base: usize,
1600    pub lagging: usize,
1601    pub displacement: usize,
1602}
1603
1604impl IndicatorRef for IchimokuLaggingRef {
1605    fn key(&self) -> String {
1606        format!(
1607            "ichimoku_lagging_{}_{}_{}_{}",
1608            self.conversion, self.base, self.lagging, self.displacement
1609        )
1610    }
1611
1612    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1613        vec![(
1614            self.key(),
1615            Indicator::Ichimoku {
1616                conversion: self.conversion,
1617                base: self.base,
1618                lagging: self.lagging,
1619                displacement: self.displacement,
1620            },
1621        )]
1622    }
1623
1624    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1625        ctx.indicator(&self.key())
1626    }
1627
1628    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1629        ctx.indicator_prev(&self.key())
1630    }
1631}
1632
1633// --- Keltner Channels ---
1634
1635/// Keltner Channels configuration.
1636#[derive(Debug, Clone, Copy)]
1637pub struct KeltnerConfig {
1638    pub period: usize,
1639    pub multiplier: f64,
1640    pub atr_period: usize,
1641}
1642
1643impl KeltnerConfig {
1644    /// Get the upper channel reference.
1645    pub fn upper(&self) -> KeltnerUpperRef {
1646        KeltnerUpperRef {
1647            period: self.period,
1648            multiplier: self.multiplier,
1649            atr_period: self.atr_period,
1650        }
1651    }
1652
1653    /// Get the middle channel (EMA) reference.
1654    pub fn middle(&self) -> KeltnerMiddleRef {
1655        KeltnerMiddleRef {
1656            period: self.period,
1657            multiplier: self.multiplier,
1658            atr_period: self.atr_period,
1659        }
1660    }
1661
1662    /// Get the lower channel reference.
1663    pub fn lower(&self) -> KeltnerLowerRef {
1664        KeltnerLowerRef {
1665            period: self.period,
1666            multiplier: self.multiplier,
1667            atr_period: self.atr_period,
1668        }
1669    }
1670}
1671
1672/// Create a Keltner Channels configuration.
1673#[inline]
1674pub fn keltner(period: usize, multiplier: f64, atr_period: usize) -> KeltnerConfig {
1675    KeltnerConfig {
1676        period,
1677        multiplier,
1678        atr_period,
1679    }
1680}
1681
1682/// Keltner upper channel reference.
1683#[derive(Debug, Clone, Copy)]
1684pub struct KeltnerUpperRef {
1685    pub period: usize,
1686    pub multiplier: f64,
1687    pub atr_period: usize,
1688}
1689
1690impl IndicatorRef for KeltnerUpperRef {
1691    fn key(&self) -> String {
1692        format!(
1693            "keltner_upper_{}_{}_{}",
1694            self.period, self.multiplier, self.atr_period
1695        )
1696    }
1697
1698    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1699        vec![(
1700            self.key(),
1701            Indicator::KeltnerChannels {
1702                period: self.period,
1703                multiplier: self.multiplier,
1704                atr_period: self.atr_period,
1705            },
1706        )]
1707    }
1708
1709    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1710        ctx.indicator(&self.key())
1711    }
1712
1713    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1714        ctx.indicator_prev(&self.key())
1715    }
1716}
1717
1718/// Keltner middle channel reference.
1719#[derive(Debug, Clone, Copy)]
1720pub struct KeltnerMiddleRef {
1721    pub period: usize,
1722    pub multiplier: f64,
1723    pub atr_period: usize,
1724}
1725
1726impl IndicatorRef for KeltnerMiddleRef {
1727    fn key(&self) -> String {
1728        format!(
1729            "keltner_middle_{}_{}_{}",
1730            self.period, self.multiplier, self.atr_period
1731        )
1732    }
1733
1734    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1735        vec![(
1736            self.key(),
1737            Indicator::KeltnerChannels {
1738                period: self.period,
1739                multiplier: self.multiplier,
1740                atr_period: self.atr_period,
1741            },
1742        )]
1743    }
1744
1745    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1746        ctx.indicator(&self.key())
1747    }
1748
1749    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1750        ctx.indicator_prev(&self.key())
1751    }
1752}
1753
1754/// Keltner lower channel reference.
1755#[derive(Debug, Clone, Copy)]
1756pub struct KeltnerLowerRef {
1757    pub period: usize,
1758    pub multiplier: f64,
1759    pub atr_period: usize,
1760}
1761
1762impl IndicatorRef for KeltnerLowerRef {
1763    fn key(&self) -> String {
1764        format!(
1765            "keltner_lower_{}_{}_{}",
1766            self.period, self.multiplier, self.atr_period
1767        )
1768    }
1769
1770    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1771        vec![(
1772            self.key(),
1773            Indicator::KeltnerChannels {
1774                period: self.period,
1775                multiplier: self.multiplier,
1776                atr_period: self.atr_period,
1777            },
1778        )]
1779    }
1780
1781    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1782        ctx.indicator(&self.key())
1783    }
1784
1785    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1786        ctx.indicator_prev(&self.key())
1787    }
1788}
1789
1790// --- Parabolic SAR ---
1791
1792/// Parabolic SAR configuration.
1793#[derive(Debug, Clone, Copy)]
1794pub struct ParabolicSarConfig {
1795    pub step: f64,
1796    pub max: f64,
1797}
1798
1799/// Create a Parabolic SAR configuration.
1800#[inline]
1801pub fn parabolic_sar(step: f64, max: f64) -> ParabolicSarRef {
1802    ParabolicSarRef { step, max }
1803}
1804
1805/// Parabolic SAR reference.
1806#[derive(Debug, Clone, Copy)]
1807pub struct ParabolicSarRef {
1808    pub step: f64,
1809    pub max: f64,
1810}
1811
1812impl IndicatorRef for ParabolicSarRef {
1813    fn key(&self) -> String {
1814        format!("psar_{}_{}", self.step, self.max)
1815    }
1816
1817    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1818        vec![(
1819            self.key(),
1820            Indicator::ParabolicSar {
1821                step: self.step,
1822                max: self.max,
1823            },
1824        )]
1825    }
1826
1827    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1828        ctx.indicator(&self.key())
1829    }
1830
1831    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1832        ctx.indicator_prev(&self.key())
1833    }
1834}
1835
1836// --- ALMA ---
1837
1838/// ALMA (Arnaud Legoux Moving Average) configuration.
1839#[derive(Debug, Clone, Copy)]
1840pub struct AlmaConfig {
1841    pub period: usize,
1842    pub offset: f64,
1843    pub sigma: f64,
1844}
1845
1846/// Create an ALMA configuration.
1847#[inline]
1848pub fn alma(period: usize, offset: f64, sigma: f64) -> AlmaRef {
1849    AlmaRef {
1850        period,
1851        offset,
1852        sigma,
1853    }
1854}
1855
1856/// ALMA reference.
1857#[derive(Debug, Clone, Copy)]
1858pub struct AlmaRef {
1859    pub period: usize,
1860    pub offset: f64,
1861    pub sigma: f64,
1862}
1863
1864impl IndicatorRef for AlmaRef {
1865    fn key(&self) -> String {
1866        format!("alma_{}_{}_{}", self.period, self.offset, self.sigma)
1867    }
1868
1869    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1870        vec![(
1871            self.key(),
1872            Indicator::Alma {
1873                period: self.period,
1874                offset: self.offset,
1875                sigma: self.sigma,
1876            },
1877        )]
1878    }
1879
1880    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1881        ctx.indicator(&self.key())
1882    }
1883
1884    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1885        ctx.indicator_prev(&self.key())
1886    }
1887}
1888
1889// --- Stochastic RSI ---
1890
1891/// Stochastic RSI configuration — entry point for building K or D line refs.
1892///
1893/// Use [`.k()`](StochasticRsiConfig::k) to reference the smoothed %K line and
1894/// [`.d()`](StochasticRsiConfig::d) for the %D signal line.  Both resolve
1895/// against the same underlying `StochasticRsi` indicator computation, so only
1896/// one indicator fetch is registered regardless of which lines you use.
1897///
1898/// # Example
1899/// ```ignore
1900/// let srsi = stochastic_rsi(14, 14, 3, 3);
1901///
1902/// // K crosses above D — a common bullish signal
1903/// StrategyBuilder::new("StochRSI K/D Cross")
1904///     .entry(srsi.k().crosses_above_ref(srsi.d()))
1905///     .exit(srsi.k().crosses_below_ref(srsi.d()))
1906///     .build()
1907/// ```
1908#[derive(Debug, Clone, Copy)]
1909pub struct StochasticRsiConfig {
1910    pub rsi_period: usize,
1911    pub stoch_period: usize,
1912    pub k_period: usize,
1913    pub d_period: usize,
1914}
1915
1916impl StochasticRsiConfig {
1917    /// Reference to the smoothed %K line.
1918    pub fn k(&self) -> StochasticRsiRef {
1919        StochasticRsiRef {
1920            rsi_period: self.rsi_period,
1921            stoch_period: self.stoch_period,
1922            k_period: self.k_period,
1923            d_period: self.d_period,
1924        }
1925    }
1926
1927    /// Reference to the %D signal line (SMA of %K).
1928    pub fn d(&self) -> StochasticRsiDRef {
1929        StochasticRsiDRef {
1930            rsi_period: self.rsi_period,
1931            stoch_period: self.stoch_period,
1932            k_period: self.k_period,
1933            d_period: self.d_period,
1934        }
1935    }
1936}
1937
1938/// Create a Stochastic RSI configuration.
1939///
1940/// Returns a [`StochasticRsiConfig`] from which you can obtain
1941/// [`StochasticRsiConfig::k()`] or [`StochasticRsiConfig::d()`] refs.
1942/// Calling `stochastic_rsi(...).k()` is equivalent to the previous API that
1943/// returned `StochasticRsiRef` directly.
1944#[inline]
1945pub fn stochastic_rsi(
1946    rsi_period: usize,
1947    stoch_period: usize,
1948    k_period: usize,
1949    d_period: usize,
1950) -> StochasticRsiConfig {
1951    StochasticRsiConfig {
1952        rsi_period,
1953        stoch_period,
1954        k_period,
1955        d_period,
1956    }
1957}
1958
1959/// Stochastic RSI %K line reference.
1960#[derive(Debug, Clone, Copy)]
1961pub struct StochasticRsiRef {
1962    pub rsi_period: usize,
1963    pub stoch_period: usize,
1964    pub k_period: usize,
1965    pub d_period: usize,
1966}
1967
1968impl IndicatorRef for StochasticRsiRef {
1969    fn key(&self) -> String {
1970        format!(
1971            "stoch_rsi_k_{}_{}_{}_{}",
1972            self.rsi_period, self.stoch_period, self.k_period, self.d_period
1973        )
1974    }
1975
1976    fn required_indicators(&self) -> Vec<(String, Indicator)> {
1977        vec![(
1978            self.key(),
1979            Indicator::StochasticRsi {
1980                rsi_period: self.rsi_period,
1981                stoch_period: self.stoch_period,
1982                k_period: self.k_period,
1983                d_period: self.d_period,
1984            },
1985        )]
1986    }
1987
1988    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
1989        ctx.indicator(&self.key())
1990    }
1991
1992    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
1993        ctx.indicator_prev(&self.key())
1994    }
1995}
1996
1997/// Stochastic RSI %D line reference (SMA of %K).
1998#[derive(Debug, Clone, Copy)]
1999pub struct StochasticRsiDRef {
2000    pub rsi_period: usize,
2001    pub stoch_period: usize,
2002    pub k_period: usize,
2003    pub d_period: usize,
2004}
2005
2006impl IndicatorRef for StochasticRsiDRef {
2007    fn key(&self) -> String {
2008        format!(
2009            "stoch_rsi_d_{}_{}_{}_{}",
2010            self.rsi_period, self.stoch_period, self.k_period, self.d_period
2011        )
2012    }
2013
2014    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2015        // Registering the K key is sufficient — the engine computes both K and D
2016        // from the same StochasticRsi indicator pass and stores both keys.
2017        let k_key = format!(
2018            "stoch_rsi_k_{}_{}_{}_{}",
2019            self.rsi_period, self.stoch_period, self.k_period, self.d_period
2020        );
2021        vec![(
2022            k_key,
2023            Indicator::StochasticRsi {
2024                rsi_period: self.rsi_period,
2025                stoch_period: self.stoch_period,
2026                k_period: self.k_period,
2027                d_period: self.d_period,
2028            },
2029        )]
2030    }
2031
2032    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2033        ctx.indicator(&self.key())
2034    }
2035
2036    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2037        ctx.indicator_prev(&self.key())
2038    }
2039}
2040
2041// --- Awesome Oscillator ---
2042
2043/// Awesome Oscillator reference (uses default 5/34 periods).
2044#[derive(Debug, Clone, Copy)]
2045pub struct AwesomeOscillatorRef {
2046    pub fast: usize,
2047    pub slow: usize,
2048}
2049
2050/// Create an Awesome Oscillator reference.
2051#[inline]
2052pub fn awesome_oscillator(fast: usize, slow: usize) -> AwesomeOscillatorRef {
2053    AwesomeOscillatorRef { fast, slow }
2054}
2055
2056impl IndicatorRef for AwesomeOscillatorRef {
2057    fn key(&self) -> String {
2058        format!("ao_{}_{}", self.fast, self.slow)
2059    }
2060
2061    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2062        vec![(
2063            self.key(),
2064            Indicator::AwesomeOscillator {
2065                fast: self.fast,
2066                slow: self.slow,
2067            },
2068        )]
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// --- Coppock Curve ---
2081
2082/// Coppock Curve reference.
2083#[derive(Debug, Clone, Copy)]
2084pub struct CoppockCurveRef {
2085    pub wma_period: usize,
2086    pub long_roc: usize,
2087    pub short_roc: usize,
2088}
2089
2090/// Create a Coppock Curve reference (uses default 10/14/11 periods).
2091#[inline]
2092pub fn coppock_curve(wma_period: usize, long_roc: usize, short_roc: usize) -> CoppockCurveRef {
2093    CoppockCurveRef {
2094        wma_period,
2095        long_roc,
2096        short_roc,
2097    }
2098}
2099
2100impl IndicatorRef for CoppockCurveRef {
2101    fn key(&self) -> String {
2102        format!(
2103            "coppock_{}_{}_{}",
2104            self.wma_period, self.long_roc, self.short_roc
2105        )
2106    }
2107
2108    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2109        vec![(
2110            self.key(),
2111            Indicator::CoppockCurve {
2112                wma_period: self.wma_period,
2113                long_roc: self.long_roc,
2114                short_roc: self.short_roc,
2115            },
2116        )]
2117    }
2118
2119    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2120        ctx.indicator(&self.key())
2121    }
2122
2123    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2124        ctx.indicator_prev(&self.key())
2125    }
2126}
2127
2128// --- Choppiness Index ---
2129
2130/// Choppiness Index reference.
2131#[derive(Debug, Clone, Copy)]
2132pub struct ChoppinessIndexRef(pub usize);
2133
2134/// Create a Choppiness Index reference.
2135#[inline]
2136pub fn choppiness_index(period: usize) -> ChoppinessIndexRef {
2137    ChoppinessIndexRef(period)
2138}
2139
2140impl IndicatorRef for ChoppinessIndexRef {
2141    fn key(&self) -> String {
2142        format!("chop_{}", self.0)
2143    }
2144
2145    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2146        vec![(self.key(), Indicator::ChoppinessIndex(self.0))]
2147    }
2148
2149    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2150        ctx.indicator(&self.key())
2151    }
2152
2153    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2154        ctx.indicator_prev(&self.key())
2155    }
2156}
2157
2158// --- True Range ---
2159
2160/// True Range reference.
2161#[derive(Debug, Clone, Copy)]
2162pub struct TrueRangeRef;
2163
2164/// Create a True Range reference.
2165#[inline]
2166pub fn true_range() -> TrueRangeRef {
2167    TrueRangeRef
2168}
2169
2170impl IndicatorRef for TrueRangeRef {
2171    fn key(&self) -> String {
2172        "true_range".to_string()
2173    }
2174
2175    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2176        vec![(self.key(), Indicator::TrueRange)]
2177    }
2178
2179    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2180        ctx.indicator(&self.key())
2181    }
2182
2183    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2184        ctx.indicator_prev(&self.key())
2185    }
2186}
2187
2188// --- Chaikin Oscillator ---
2189
2190/// Chaikin Oscillator reference.
2191#[derive(Debug, Clone, Copy)]
2192pub struct ChaikinOscillatorRef;
2193
2194/// Create a Chaikin Oscillator reference.
2195#[inline]
2196pub fn chaikin_oscillator() -> ChaikinOscillatorRef {
2197    ChaikinOscillatorRef
2198}
2199
2200impl IndicatorRef for ChaikinOscillatorRef {
2201    fn key(&self) -> String {
2202        "chaikin_osc".to_string()
2203    }
2204
2205    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2206        vec![(self.key(), Indicator::ChaikinOscillator)]
2207    }
2208
2209    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2210        ctx.indicator(&self.key())
2211    }
2212
2213    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2214        ctx.indicator_prev(&self.key())
2215    }
2216}
2217
2218// --- Accumulation/Distribution ---
2219
2220/// Accumulation/Distribution reference.
2221#[derive(Debug, Clone, Copy)]
2222pub struct AccumulationDistributionRef;
2223
2224/// Create an Accumulation/Distribution reference.
2225#[inline]
2226pub fn accumulation_distribution() -> AccumulationDistributionRef {
2227    AccumulationDistributionRef
2228}
2229
2230impl IndicatorRef for AccumulationDistributionRef {
2231    fn key(&self) -> String {
2232        "ad".to_string()
2233    }
2234
2235    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2236        vec![(self.key(), Indicator::AccumulationDistribution)]
2237    }
2238
2239    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2240        ctx.indicator(&self.key())
2241    }
2242
2243    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2244        ctx.indicator_prev(&self.key())
2245    }
2246}
2247
2248// --- Balance of Power ---
2249
2250/// Balance of Power reference.
2251#[derive(Debug, Clone, Copy)]
2252pub struct BalanceOfPowerRef(pub Option<usize>);
2253
2254/// Create a Balance of Power reference.
2255#[inline]
2256pub fn balance_of_power(period: Option<usize>) -> BalanceOfPowerRef {
2257    BalanceOfPowerRef(period)
2258}
2259
2260impl IndicatorRef for BalanceOfPowerRef {
2261    fn key(&self) -> String {
2262        match self.0 {
2263            Some(p) => format!("bop_{}", p),
2264            None => "bop".to_string(),
2265        }
2266    }
2267
2268    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2269        vec![(self.key(), Indicator::BalanceOfPower(self.0))]
2270    }
2271
2272    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2273        ctx.indicator(&self.key())
2274    }
2275
2276    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2277        ctx.indicator_prev(&self.key())
2278    }
2279}
2280
2281// --- Bull/Bear Power ---
2282
2283/// Bull Power reference.
2284#[derive(Debug, Clone, Copy)]
2285pub struct BullPowerRef(pub usize);
2286
2287/// Create a Bull Power reference.
2288#[inline]
2289pub fn bull_power(period: usize) -> BullPowerRef {
2290    BullPowerRef(period)
2291}
2292
2293impl IndicatorRef for BullPowerRef {
2294    fn key(&self) -> String {
2295        format!("bull_power_{}", self.0)
2296    }
2297
2298    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2299        vec![(self.key(), Indicator::BullBearPower(self.0))]
2300    }
2301
2302    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2303        ctx.indicator(&self.key())
2304    }
2305
2306    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2307        ctx.indicator_prev(&self.key())
2308    }
2309}
2310
2311/// Bear Power reference.
2312#[derive(Debug, Clone, Copy)]
2313pub struct BearPowerRef(pub usize);
2314
2315/// Create a Bear Power reference.
2316#[inline]
2317pub fn bear_power(period: usize) -> BearPowerRef {
2318    BearPowerRef(period)
2319}
2320
2321impl IndicatorRef for BearPowerRef {
2322    fn key(&self) -> String {
2323        format!("bear_power_{}", self.0)
2324    }
2325
2326    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2327        vec![(self.key(), Indicator::BullBearPower(self.0))]
2328    }
2329
2330    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2331        ctx.indicator(&self.key())
2332    }
2333
2334    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2335        ctx.indicator_prev(&self.key())
2336    }
2337}
2338
2339// --- Elder Ray ---
2340
2341/// Elder Ray Bull Power reference.
2342#[derive(Debug, Clone, Copy)]
2343pub struct ElderBullPowerRef(pub usize);
2344
2345/// Create an Elder Ray Bull Power reference.
2346#[inline]
2347pub fn elder_bull_power(period: usize) -> ElderBullPowerRef {
2348    ElderBullPowerRef(period)
2349}
2350
2351impl IndicatorRef for ElderBullPowerRef {
2352    fn key(&self) -> String {
2353        format!("elder_bull_{}", self.0)
2354    }
2355
2356    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2357        vec![(self.key(), Indicator::ElderRay(self.0))]
2358    }
2359
2360    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2361        ctx.indicator(&self.key())
2362    }
2363
2364    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2365        ctx.indicator_prev(&self.key())
2366    }
2367}
2368
2369/// Elder Ray Bear Power reference.
2370#[derive(Debug, Clone, Copy)]
2371pub struct ElderBearPowerRef(pub usize);
2372
2373/// Create an Elder Ray Bear Power reference.
2374#[inline]
2375pub fn elder_bear_power(period: usize) -> ElderBearPowerRef {
2376    ElderBearPowerRef(period)
2377}
2378
2379impl IndicatorRef for ElderBearPowerRef {
2380    fn key(&self) -> String {
2381        format!("elder_bear_{}", self.0)
2382    }
2383
2384    fn required_indicators(&self) -> Vec<(String, Indicator)> {
2385        vec![(self.key(), Indicator::ElderRay(self.0))]
2386    }
2387
2388    fn value(&self, ctx: &StrategyContext) -> Option<f64> {
2389        ctx.indicator(&self.key())
2390    }
2391
2392    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
2393        ctx.indicator_prev(&self.key())
2394    }
2395}
2396
2397#[cfg(test)]
2398mod tests {
2399    use super::*;
2400
2401    #[test]
2402    fn test_moving_average_keys() {
2403        assert_eq!(sma(20).key(), "sma_20");
2404        assert_eq!(ema(12).key(), "ema_12");
2405        assert_eq!(wma(14).key(), "wma_14");
2406        assert_eq!(dema(21).key(), "dema_21");
2407        assert_eq!(tema(21).key(), "tema_21");
2408        assert_eq!(hma(9).key(), "hma_9");
2409        assert_eq!(vwma(20).key(), "vwma_20");
2410        assert_eq!(mcginley(14).key(), "mcginley_14");
2411        assert_eq!(alma(9, 0.85, 6.0).key(), "alma_9_0.85_6");
2412    }
2413
2414    #[test]
2415    fn test_oscillator_keys() {
2416        assert_eq!(rsi(14).key(), "rsi_14");
2417        assert_eq!(cci(20).key(), "cci_20");
2418        assert_eq!(williams_r(14).key(), "williams_r_14");
2419        assert_eq!(cmo(14).key(), "cmo_14");
2420        // stochastic_rsi() returns StochasticRsiConfig; .k()/.d() give line refs
2421        assert_eq!(
2422            stochastic_rsi(14, 14, 3, 3).k().key(),
2423            "stoch_rsi_k_14_14_3_3"
2424        );
2425        assert_eq!(
2426            stochastic_rsi(14, 14, 3, 3).d().key(),
2427            "stoch_rsi_d_14_14_3_3"
2428        );
2429        assert_eq!(awesome_oscillator(5, 34).key(), "ao_5_34");
2430        assert_eq!(choppiness_index(14).key(), "chop_14");
2431    }
2432
2433    #[test]
2434    fn test_macd_keys() {
2435        let m = macd(12, 26, 9);
2436        assert_eq!(m.line().key(), "macd_line_12_26_9");
2437        assert_eq!(m.signal_line().key(), "macd_signal_12_26_9");
2438        assert_eq!(m.histogram().key(), "macd_histogram_12_26_9");
2439    }
2440
2441    #[test]
2442    fn test_bollinger_keys() {
2443        let bb = bollinger(20, 2.0);
2444        assert_eq!(bb.upper().key(), "bollinger_upper_20_2");
2445        assert_eq!(bb.middle().key(), "bollinger_middle_20_2");
2446        assert_eq!(bb.lower().key(), "bollinger_lower_20_2");
2447    }
2448
2449    #[test]
2450    fn test_donchian_keys() {
2451        let dc = donchian(20);
2452        assert_eq!(dc.upper().key(), "donchian_upper_20");
2453        assert_eq!(dc.middle().key(), "donchian_middle_20");
2454        assert_eq!(dc.lower().key(), "donchian_lower_20");
2455    }
2456
2457    #[test]
2458    fn test_supertrend_keys() {
2459        let st = supertrend(10, 3.0);
2460        assert_eq!(st.value().key(), "supertrend_value_10_3");
2461        assert_eq!(st.uptrend().key(), "supertrend_uptrend_10_3");
2462    }
2463
2464    #[test]
2465    fn test_stochastic_keys() {
2466        let stoch = stochastic(14, 3, 3);
2467        assert_eq!(stoch.k().key(), "stochastic_k_14_3_3");
2468        assert_eq!(stoch.d().key(), "stochastic_d_14_3_3");
2469    }
2470
2471    #[test]
2472    fn test_aroon_keys() {
2473        let ar = aroon(25);
2474        assert_eq!(ar.up().key(), "aroon_up_25");
2475        assert_eq!(ar.down().key(), "aroon_down_25");
2476    }
2477
2478    #[test]
2479    fn test_ichimoku_keys() {
2480        let ich = ichimoku();
2481        assert_eq!(
2482            ich.conversion_line().key(),
2483            "ichimoku_conversion_9_26_52_26"
2484        );
2485        assert_eq!(ich.base_line().key(), "ichimoku_base_9_26_52_26");
2486        assert_eq!(ich.leading_span_a().key(), "ichimoku_leading_a_9_26_52_26");
2487        assert_eq!(ich.leading_span_b().key(), "ichimoku_leading_b_9_26_52_26");
2488        assert_eq!(ich.lagging_span().key(), "ichimoku_lagging_9_26_52_26");
2489    }
2490
2491    #[test]
2492    fn test_keltner_keys() {
2493        let kc = keltner(20, 2.0, 10);
2494        assert_eq!(kc.upper().key(), "keltner_upper_20_2_10");
2495        assert_eq!(kc.middle().key(), "keltner_middle_20_2_10");
2496        assert_eq!(kc.lower().key(), "keltner_lower_20_2_10");
2497    }
2498
2499    #[test]
2500    fn test_volume_keys() {
2501        assert_eq!(obv().key(), "obv");
2502        assert_eq!(vwap().key(), "vwap");
2503        assert_eq!(mfi(14).key(), "mfi_14");
2504        assert_eq!(cmf(20).key(), "cmf_20");
2505        assert_eq!(chaikin_oscillator().key(), "chaikin_osc");
2506        assert_eq!(accumulation_distribution().key(), "ad");
2507        assert_eq!(balance_of_power(Some(14)).key(), "bop_14");
2508        assert_eq!(balance_of_power(None).key(), "bop");
2509    }
2510
2511    #[test]
2512    fn test_power_keys() {
2513        assert_eq!(bull_power(13).key(), "bull_power_13");
2514        assert_eq!(bear_power(13).key(), "bear_power_13");
2515        assert_eq!(elder_bull_power(13).key(), "elder_bull_13");
2516        assert_eq!(elder_bear_power(13).key(), "elder_bear_13");
2517    }
2518
2519    #[test]
2520    fn test_other_keys() {
2521        assert_eq!(parabolic_sar(0.02, 0.2).key(), "psar_0.02_0.2");
2522        assert_eq!(true_range().key(), "true_range");
2523        assert_eq!(coppock_curve(10, 14, 11).key(), "coppock_10_14_11");
2524    }
2525
2526    #[test]
2527    fn test_required_indicators() {
2528        let sma_ref = sma(20);
2529        let indicators = sma_ref.required_indicators();
2530        assert_eq!(indicators.len(), 1);
2531        assert_eq!(indicators[0].0, "sma_20");
2532        assert!(matches!(indicators[0].1, Indicator::Sma(20)));
2533    }
2534}