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