Skip to main content

finance_query/backtesting/refs/
mod.rs

1//! Indicator reference system for building strategy conditions.
2//!
3//! This module provides a type-safe way to reference indicator values
4//! that can be used to build trading conditions.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use finance_query::backtesting::refs::*;
10//!
11//! // Reference RSI indicator
12//! let rsi_ref = rsi(14);
13//!
14//! // Build conditions
15//! let oversold = rsi_ref.below(30.0);
16//! let overbought = rsi_ref.above(70.0);
17//!
18//! // Compose conditions
19//! let entry = rsi(14).crosses_below(30.0).and(price().above_ref(sma(200)));
20//! ```
21
22// The fluent constructors (sma(), rsi(), ...) are the public surface; the ref
23// structs they return carry undocumented fields on purpose.
24#[allow(missing_docs)]
25mod ichimoku;
26#[allow(missing_docs)]
27mod moving_averages;
28#[allow(missing_docs)]
29mod oscillators;
30#[allow(missing_docs)]
31mod power;
32#[allow(missing_docs)]
33mod trend;
34#[allow(missing_docs)]
35mod volatility;
36#[allow(missing_docs)]
37mod volume;
38
39mod htf;
40mod price;
41
42pub use htf::*;
43pub use ichimoku::*;
44pub use moving_averages::*;
45pub use oscillators::*;
46pub use power::*;
47pub use price::*;
48pub use trend::*;
49pub use volatility::*;
50pub use volume::*;
51
52use crate::indicators::Indicator;
53
54use super::strategy::StrategyContext;
55
56/// A reference to a value that can be compared in conditions.
57///
58/// This is the building block for creating conditions. Each indicator
59/// reference knows:
60/// - Its unique key for storing computed values
61/// - What indicators it requires
62/// - How to retrieve its value from the strategy context
63///
64/// # Implementing Custom References
65///
66/// ```ignore
67/// use finance_query::backtesting::refs::IndicatorRef;
68///
69/// #[derive(Clone)]
70/// struct MyCustomRef {
71///     period: usize,
72/// }
73///
74/// impl IndicatorRef for MyCustomRef {
75///     fn key(&self) -> &str {
76///         "my_custom_14" // pre-computed at construction time
77///     }
78///
79///     fn required_indicators(&self) -> Vec<(String, Indicator)> {
80///         vec![(self.key().to_string(), Indicator::Sma(self.period))]
81///     }
82///
83///     fn value(&self, ctx: &StrategyContext) -> Option<f64> {
84///         ctx.indicator(self.key())
85///     }
86///
87///     fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
88///         ctx.indicator_prev(self.key())
89///     }
90/// }
91/// ```
92pub trait IndicatorRef: Clone + Send + Sync + 'static {
93    /// Unique key for storing computed values in the context.
94    ///
95    /// This key is used to look up pre-computed indicator values
96    /// in the `StrategyContext::indicators` map.
97    fn key(&self) -> &str;
98
99    /// Required indicators to compute this reference.
100    ///
101    /// Returns a list of (key, Indicator) pairs that must be
102    /// pre-computed by the backtest engine before the strategy runs.
103    fn required_indicators(&self) -> Vec<(String, Indicator)>;
104
105    /// Get the value at current candle index from context.
106    fn value(&self, ctx: &StrategyContext) -> Option<f64>;
107
108    /// Get the value at the previous candle index.
109    fn prev_value(&self, ctx: &StrategyContext) -> Option<f64>;
110}
111
112/// Extension trait that adds condition-building methods to all indicator references.
113///
114/// This trait provides a fluent API for building conditions from indicator values.
115/// It is automatically implemented for all types that implement `IndicatorRef`.
116///
117/// # Example
118///
119/// ```ignore
120/// use finance_query::backtesting::refs::*;
121///
122/// // All these methods are available on any IndicatorRef
123/// let cond1 = rsi(14).above(70.0);
124/// let cond2 = rsi(14).below(30.0);
125/// let cond3 = rsi(14).crosses_above(30.0);
126/// let cond4 = rsi(14).crosses_below(70.0);
127/// let cond5 = rsi(14).between(30.0, 70.0);
128/// let cond6 = sma(10).above_ref(sma(20));
129/// let cond7 = sma(10).crosses_above_ref(sma(20));
130/// ```
131pub trait IndicatorRefExt: IndicatorRef + Sized {
132    /// Create a condition that checks if this indicator is above a threshold.
133    ///
134    /// # Example
135    ///
136    /// ```ignore
137    /// let overbought = rsi(14).above(70.0);
138    /// ```
139    fn above(self, threshold: f64) -> super::condition::Above<Self> {
140        super::condition::Above::new(self, threshold)
141    }
142
143    /// Create a condition that checks if this indicator is above another indicator.
144    ///
145    /// # Example
146    ///
147    /// ```ignore
148    /// let uptrend = price().above_ref(sma(200));
149    /// ```
150    fn above_ref<R: IndicatorRef>(self, other: R) -> super::condition::AboveRef<Self, R> {
151        super::condition::AboveRef::new(self, other)
152    }
153
154    /// Create a condition that checks if this indicator is below a threshold.
155    ///
156    /// # Example
157    ///
158    /// ```ignore
159    /// let oversold = rsi(14).below(30.0);
160    /// ```
161    fn below(self, threshold: f64) -> super::condition::Below<Self> {
162        super::condition::Below::new(self, threshold)
163    }
164
165    /// Create a condition that checks if this indicator is below another indicator.
166    ///
167    /// # Example
168    ///
169    /// ```ignore
170    /// let downtrend = price().below_ref(sma(200));
171    /// ```
172    fn below_ref<R: IndicatorRef>(self, other: R) -> super::condition::BelowRef<Self, R> {
173        super::condition::BelowRef::new(self, other)
174    }
175
176    /// Create a condition that checks if this indicator crosses above a threshold.
177    ///
178    /// A crossover occurs when the previous value was at or below the threshold
179    /// and the current value is above it.
180    ///
181    /// # Example
182    ///
183    /// ```ignore
184    /// let rsi_exit_oversold = rsi(14).crosses_above(30.0);
185    /// ```
186    fn crosses_above(self, threshold: f64) -> super::condition::CrossesAbove<Self> {
187        super::condition::CrossesAbove::new(self, threshold)
188    }
189
190    /// Create a condition that checks if this indicator crosses above another indicator.
191    ///
192    /// # Example
193    ///
194    /// ```ignore
195    /// let golden_cross = sma(50).crosses_above_ref(sma(200));
196    /// ```
197    fn crosses_above_ref<R: IndicatorRef>(
198        self,
199        other: R,
200    ) -> super::condition::CrossesAboveRef<Self, R> {
201        super::condition::CrossesAboveRef::new(self, other)
202    }
203
204    /// Create a condition that checks if this indicator crosses below a threshold.
205    ///
206    /// A crossover occurs when the previous value was at or above the threshold
207    /// and the current value is below it.
208    ///
209    /// # Example
210    ///
211    /// ```ignore
212    /// let rsi_enter_overbought = rsi(14).crosses_below(70.0);
213    /// ```
214    fn crosses_below(self, threshold: f64) -> super::condition::CrossesBelow<Self> {
215        super::condition::CrossesBelow::new(self, threshold)
216    }
217
218    /// Create a condition that checks if this indicator crosses below another indicator.
219    ///
220    /// # Example
221    ///
222    /// ```ignore
223    /// let death_cross = sma(50).crosses_below_ref(sma(200));
224    /// ```
225    fn crosses_below_ref<R: IndicatorRef>(
226        self,
227        other: R,
228    ) -> super::condition::CrossesBelowRef<Self, R> {
229        super::condition::CrossesBelowRef::new(self, other)
230    }
231
232    /// Create a condition that checks if this indicator is between two thresholds.
233    ///
234    /// Returns true when `low < value < high`.
235    ///
236    /// # Example
237    ///
238    /// ```ignore
239    /// let neutral_rsi = rsi(14).between(40.0, 60.0);
240    /// ```
241    fn between(self, low: f64, high: f64) -> super::condition::Between<Self> {
242        super::condition::Between::new(self, low, high)
243    }
244
245    /// Create a condition that checks if this indicator equals a value (within tolerance).
246    ///
247    /// # Example
248    ///
249    /// ```ignore
250    /// let at_zero = macd(12, 26, 9).histogram().equals(0.0, 0.001);
251    /// ```
252    fn equals(self, value: f64, tolerance: f64) -> super::condition::Equals<Self> {
253        super::condition::Equals::new(self, value, tolerance)
254    }
255}
256
257// Auto-implement IndicatorRefExt for all types that implement IndicatorRef
258impl<T: IndicatorRef + Sized> IndicatorRefExt for T {}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_indicator_ref_ext_methods_exist() {
266        // This test just verifies the trait methods compile
267        let _sma = sma(20);
268        let _ema = ema(12);
269        let _rsi = rsi(14);
270
271        // Verify key() works
272        assert_eq!(_sma.key(), "sma_20");
273        assert_eq!(_ema.key(), "ema_12");
274        assert_eq!(_rsi.key(), "rsi_14");
275    }
276}