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