Skip to main content

mantis_ta/indicators/
mod.rs

1//! Tier 1 indicators with streaming and batch APIs.
2//!
3//! # Examples
4//! Compute several indicators over a generated candle series:
5//!
6//! ```rust
7//! use mantis_ta::indicators::{
8//!     BollingerBands, Indicator, MACD, OBV, PivotPoints, RSI, SMA, Stochastic, VolumeSMA, ATR, EMA,
9//! };
10//! use mantis_ta::types::Candle;
11//!
12//! // Build a small candle series (60 bars) for the examples.
13//! let candles: Vec<Candle> = (0..60)
14//!     .map(|i| Candle {
15//!         timestamp: i,
16//!         open: 100.0 + i as f64 * 0.1,
17//!         high: 100.2 + i as f64 * 0.1,
18//!         low:  99.8 + i as f64 * 0.1,
19//!         close:100.1 + i as f64 * 0.1,
20//!         volume: 1_000.0 + i as f64,
21//!     })
22//!     .collect();
23//!
24//! // Batch calculations (Vec<Option<_>> aligned to input)
25//! let sma = SMA::new(20).calculate(&candles);
26//! let ema = EMA::new(20).calculate(&candles);
27//! let macd = MACD::new(12, 26, 9).calculate(&candles);
28//! let rsi = RSI::new(14).calculate(&candles);
29//! let stoch = Stochastic::new(14, 3).calculate(&candles);
30//! let bb = BollingerBands::new(20, 2.0).calculate(&candles);
31//! let atr = ATR::new(14).calculate(&candles);
32//! let vol_sma = VolumeSMA::new(20).calculate(&candles);
33//! let obv = OBV::new().calculate(&candles);
34//! let pivot = PivotPoints::new().calculate(&candles);
35//!
36//! // All indicators produce `None` until their warmup is reached.
37//! assert!(sma.iter().take(19).all(|v| v.is_none()));
38//! assert!(ema.iter().take(19).all(|v| v.is_none()));
39//! assert!(rsi.iter().take(13).all(|v| v.is_none()));
40//! // Stochastic warmup = k + d - 1 (14 + 3 - 1 = 16)
41//! assert!(stoch.iter().take(15).all(|v| v.is_none()));
42//! assert!(stoch[15].is_some());
43//! assert!(bb.iter().take(19).all(|v| v.is_none()));
44//! assert!(atr.iter().take(13).all(|v| v.is_none()));
45//! assert!(macd.iter().all(|v| v.is_none()) || macd.iter().any(|v| v.is_some()));
46//! assert!(pivot.iter().all(|v| v.is_some()));
47//! assert!(obv.iter().all(|v| v.is_some()));
48//! assert!(vol_sma.iter().take(19).all(|v| v.is_none()));
49//!
50//! // Streaming example: re-use the same indicator instance per-bar
51//! let mut rsi_stream = RSI::new(14);
52//! for c in &candles {
53//!     let _ = rsi_stream.next(c);
54//! }
55//! ```
56
57use crate::types::Candle;
58use std::fmt::Debug;
59
60pub mod momentum;
61pub mod obv;
62pub mod support_resistance;
63pub mod trend;
64pub mod volatility;
65pub mod volume;
66pub use momentum::{CCI, ROC, RSI, Stochastic, WilliamsR};
67pub use obv::OBV;
68pub use support_resistance::PivotPoints;
69pub use trend::{ADX, DEMA, EMA, MACD, SMA, TEMA, WMA};
70pub use volatility::{ATR, BollingerBands, StdDev};
71pub use volume::VolumeSMA;
72
73/// Core interface for all streaming technical indicators.
74///
75/// Implementations should be stateful and cheap to `next()`. The default
76/// `calculate` helper performs a streaming pass over a slice of candles.
77pub trait Indicator: Send + Sync {
78    /// Concrete output type for this indicator (e.g., `f64` or a struct).
79    type Output: Clone + Debug;
80
81    /// Feed one candle; returns `Some(output)` once warmup is satisfied, otherwise `None`.
82    fn next(&mut self, candle: &Candle) -> Option<Self::Output>;
83
84    /// Reset to initial state.
85    fn reset(&mut self);
86
87    /// Number of candles required before the first valid output.
88    fn warmup_period(&self) -> usize;
89
90    /// Batch compute over a candle series (default streaming loop).
91    fn calculate(&self, candles: &[Candle]) -> Vec<Option<Self::Output>> {
92        let mut instance = self.clone_boxed();
93        candles.iter().map(|c| instance.next(c)).collect()
94    }
95
96    /// Clone into a boxed trait object.
97    fn clone_boxed(&self) -> Box<dyn Indicator<Output = Self::Output>>;
98}
99
100/// Extension of [`Indicator`] that supports state snapshot/restore.
101pub trait IncrementalIndicator: Indicator {
102    /// Serializable (or cloneable) state representation.
103    type State: Clone;
104
105    /// Snapshot internal state for checkpointing.
106    fn state(&self) -> Self::State;
107
108    /// Restore from a prior snapshot.
109    fn restore(&mut self, state: Self::State);
110}
111
112// Re-export common types for convenience.
113pub use crate::types::{BollingerOutput, MacdOutput, PivotOutput, StochasticOutput};