mod accumulation_distribution;
mod adx;
mod alma;
mod aroon;
mod atr;
mod awesome_oscillator;
mod balance_of_power;
mod bollinger;
mod bull_bear_power;
mod cci;
mod chaikin_oscillator;
mod choppiness_index;
mod cmf;
mod cmo;
mod coppock_curve;
mod dema;
mod donchian_channels;
mod elder_ray;
mod ema;
mod hma;
mod ichimoku;
mod keltner_channels;
mod macd;
mod mcginley_dynamic;
mod mfi;
mod momentum;
mod obv;
mod parabolic_sar;
mod patterns;
mod roc;
mod rsi;
mod sma;
mod stochastic;
mod stochastic_rsi;
mod supertrend;
mod tema;
mod true_range;
mod vwap;
mod vwma;
mod williams_r;
mod wma;
pub mod summary;
pub use accumulation_distribution::accumulation_distribution;
pub use adx::adx;
pub use alma::alma;
pub use aroon::{AroonResult, aroon};
pub use atr::atr;
pub use awesome_oscillator::awesome_oscillator;
pub use balance_of_power::balance_of_power;
pub use bollinger::{BollingerBands, bollinger_bands};
pub use bull_bear_power::{BullBearPowerResult, bull_bear_power};
pub use cci::cci;
pub use chaikin_oscillator::chaikin_oscillator;
pub use choppiness_index::choppiness_index;
pub use cmf::cmf;
pub use cmo::cmo;
pub use coppock_curve::coppock_curve;
pub use dema::dema;
pub use donchian_channels::{DonchianChannelsResult, donchian_channels};
pub use elder_ray::{ElderRayResult, elder_ray};
pub use ema::ema;
pub use hma::hma;
pub use ichimoku::{IchimokuResult, ichimoku};
pub use keltner_channels::{KeltnerChannelsResult, keltner_channels};
pub use macd::{MacdResult, macd};
pub use mcginley_dynamic::mcginley_dynamic;
pub use mfi::mfi;
pub use momentum::momentum;
pub use obv::obv;
pub use parabolic_sar::parabolic_sar;
pub use patterns::{CandlePattern, PatternSentiment, patterns};
pub use roc::roc;
pub use rsi::rsi;
pub use sma::sma;
pub use stochastic::{StochasticResult, stochastic};
pub use stochastic_rsi::stochastic_rsi;
pub use supertrend::{SuperTrendResult, supertrend};
pub use tema::tema;
pub use true_range::true_range;
pub use vwap::vwap;
pub use vwma::vwma;
pub use williams_r::williams_r;
pub use wma::wma;
pub use summary::{
AroonData, BollingerBandsData, BullBearPowerData, DonchianChannelsData, ElderRayData,
IchimokuData, IndicatorsSummary, KeltnerChannelsData, MacdData, StochasticData, SuperTrendData,
};
pub use Indicator as IndicatorType;
#[derive(Debug, thiserror::Error)]
pub enum IndicatorError {
#[error("Insufficient data: need at least {need} data points, got {got}")]
InsufficientData {
need: usize,
got: usize,
},
#[error("Invalid period: {0}")]
InvalidPeriod(String),
}
pub type Result<T> = std::result::Result<T, IndicatorError>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum IndicatorResult {
Series(Vec<Option<f64>>),
Macd(MacdResult),
Bollinger(BollingerBands),
Stochastic(StochasticResult),
Aroon(AroonResult),
SuperTrend(SuperTrendResult),
Ichimoku(IchimokuResult),
BullBearPower(BullBearPowerResult),
ElderRay(ElderRayResult),
Keltner(KeltnerChannelsResult),
Donchian(DonchianChannelsResult),
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum Indicator {
Sma(usize),
Ema(usize),
Rsi(usize),
Macd {
fast: usize,
slow: usize,
signal: usize,
},
Bollinger {
period: usize,
std_dev: f64,
},
Atr(usize),
Obv,
Vwap,
Wma(usize),
Dema(usize),
Tema(usize),
Hma(usize),
Vwma(usize),
Alma {
period: usize,
offset: f64,
sigma: f64,
},
McginleyDynamic(usize),
Stochastic {
k_period: usize,
k_slow: usize,
d_period: usize,
},
StochasticRsi {
rsi_period: usize,
stoch_period: usize,
k_period: usize,
d_period: usize,
},
Cci(usize),
WilliamsR(usize),
Roc(usize),
Momentum(usize),
Cmo(usize),
AwesomeOscillator {
fast: usize,
slow: usize,
},
CoppockCurve {
wma_period: usize,
long_roc: usize,
short_roc: usize,
},
Adx(usize),
Aroon(usize),
Supertrend {
period: usize,
multiplier: f64,
},
Ichimoku {
conversion: usize,
base: usize,
lagging: usize,
displacement: usize,
},
ParabolicSar {
step: f64,
max: f64,
},
BullBearPower(usize),
ElderRay(usize),
KeltnerChannels {
period: usize,
multiplier: f64,
atr_period: usize,
},
DonchianChannels(usize),
TrueRange,
ChoppinessIndex(usize),
Mfi(usize),
Cmf(usize),
ChaikinOscillator,
AccumulationDistribution,
BalanceOfPower(Option<usize>),
}
impl Indicator {
pub fn with_defaults(self) -> Self {
match self {
Indicator::Sma(_) => Indicator::Sma(20),
Indicator::Ema(_) => Indicator::Ema(12),
Indicator::Rsi(_) => Indicator::Rsi(14),
Indicator::Macd { .. } => Indicator::Macd {
fast: 12,
slow: 26,
signal: 9,
},
Indicator::Bollinger { .. } => Indicator::Bollinger {
period: 20,
std_dev: 2.0,
},
Indicator::Atr(_) => Indicator::Atr(14),
ind => ind,
}
}
pub fn name(&self) -> &'static str {
match self {
Indicator::Sma(_) => "Simple Moving Average",
Indicator::Ema(_) => "Exponential Moving Average",
Indicator::Rsi(_) => "Relative Strength Index",
Indicator::Macd { .. } => "MACD",
Indicator::Bollinger { .. } => "Bollinger Bands",
Indicator::Atr(_) => "Average True Range",
Indicator::Obv => "On-Balance Volume",
Indicator::Vwap => "VWAP",
Indicator::Wma(_) => "Weighted Moving Average",
Indicator::Dema(_) => "Double Exponential Moving Average",
Indicator::Tema(_) => "Triple Exponential Moving Average",
Indicator::Hma(_) => "Hull Moving Average",
Indicator::Vwma(_) => "Volume Weighted Moving Average",
Indicator::Alma { .. } => "Arnaud Legoux Moving Average",
Indicator::McginleyDynamic(_) => "McGinley Dynamic",
Indicator::Stochastic { .. } => "Stochastic Oscillator",
Indicator::StochasticRsi { .. } => "Stochastic RSI",
Indicator::Cci(_) => "Commodity Channel Index",
Indicator::WilliamsR(_) => "Williams %R",
Indicator::Roc(_) => "Rate of Change",
Indicator::Momentum(_) => "Momentum",
Indicator::Cmo(_) => "Chande Momentum Oscillator",
Indicator::AwesomeOscillator { .. } => "Awesome Oscillator",
Indicator::CoppockCurve { .. } => "Coppock Curve",
Indicator::Adx(_) => "Average Directional Index",
Indicator::Aroon(_) => "Aroon",
Indicator::Supertrend { .. } => "SuperTrend",
Indicator::Ichimoku { .. } => "Ichimoku Cloud",
Indicator::ParabolicSar { .. } => "Parabolic SAR",
Indicator::BullBearPower(_) => "Bull/Bear Power",
Indicator::ElderRay(_) => "Elder Ray Index",
Indicator::KeltnerChannels { .. } => "Keltner Channels",
Indicator::DonchianChannels(_) => "Donchian Channels",
Indicator::TrueRange => "True Range",
Indicator::ChoppinessIndex(_) => "Choppiness Index",
Indicator::Mfi(_) => "Money Flow Index",
Indicator::Cmf(_) => "Chaikin Money Flow",
Indicator::ChaikinOscillator => "Chaikin Oscillator",
Indicator::AccumulationDistribution => "Accumulation/Distribution",
Indicator::BalanceOfPower(_) => "Balance of Power",
}
}
pub fn warmup_bars(&self) -> usize {
match self {
Self::Sma(p)
| Self::Ema(p)
| Self::Rsi(p)
| Self::Atr(p)
| Self::Wma(p)
| Self::Dema(p)
| Self::Tema(p)
| Self::Hma(p)
| Self::Vwma(p)
| Self::McginleyDynamic(p)
| Self::Cci(p)
| Self::WilliamsR(p)
| Self::Roc(p)
| Self::Momentum(p)
| Self::Cmo(p)
| Self::Adx(p)
| Self::Aroon(p)
| Self::DonchianChannels(p)
| Self::ChoppinessIndex(p)
| Self::Mfi(p)
| Self::Cmf(p)
| Self::BullBearPower(p)
| Self::ElderRay(p) => *p,
Self::Macd { fast, slow, signal } => *slow.max(fast) + signal,
Self::Bollinger { period, .. } => *period,
Self::Alma { period, .. } => *period,
Self::Stochastic {
k_period,
k_slow,
d_period,
} => k_period + k_slow + d_period,
Self::StochasticRsi {
rsi_period,
stoch_period,
k_period,
d_period,
} => rsi_period + stoch_period + k_period.max(d_period),
Self::AwesomeOscillator { slow, .. } => *slow,
Self::CoppockCurve {
long_roc,
wma_period,
..
} => long_roc + wma_period,
Self::Ichimoku {
base, displacement, ..
} => base + displacement,
Self::Supertrend { period, .. } => *period,
Self::ParabolicSar { .. } => 2,
Self::KeltnerChannels {
period, atr_period, ..
} => *period.max(atr_period),
Self::BalanceOfPower(Some(p)) => *p,
Self::Obv
| Self::Vwap
| Self::TrueRange
| Self::ChaikinOscillator
| Self::AccumulationDistribution
| Self::BalanceOfPower(None) => 1,
}
}
}
pub fn last_value(values: &[Option<f64>]) -> Option<f64> {
values.iter().rev().find_map(|&v| v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_last_value() {
assert_eq!(last_value(&[None, None, Some(1.0), Some(2.0)]), Some(2.0));
assert_eq!(last_value(&[None, None, Some(1.0), None]), Some(1.0));
assert_eq!(last_value(&[None, None, None]), None);
assert_eq!(last_value(&[]), None);
}
}