1mod accumulation_distribution;
44mod adx;
45mod alma;
46mod aroon;
47mod atr;
48mod awesome_oscillator;
49mod balance_of_power;
50mod bollinger;
51mod bull_bear_power;
52mod cci;
53mod chaikin_oscillator;
54mod choppiness_index;
55mod cmf;
56mod cmo;
57mod coppock_curve;
58mod dema;
59mod donchian_channels;
60mod elder_ray;
61mod ema;
62mod fibonacci_retracement;
63mod heikin_ashi;
64mod hma;
65mod ichimoku;
66mod keltner_channels;
67mod macd;
68mod mcginley_dynamic;
69mod mfi;
70mod momentum;
71mod obv;
72mod parabolic_sar;
73mod patterns;
74mod pivot_points;
75mod roc;
76mod rsi;
77mod sma;
78mod stochastic;
79mod stochastic_rsi;
80mod supertrend;
81mod tema;
82mod true_range;
83mod vwap;
84mod vwma;
85mod williams_r;
86mod wma;
87mod zigzag;
88
89pub mod summary;
91
92pub use accumulation_distribution::accumulation_distribution;
94pub use adx::adx;
95pub use alma::alma;
96pub use aroon::{AroonResult, aroon};
97pub use atr::atr;
98pub use awesome_oscillator::awesome_oscillator;
99pub use balance_of_power::balance_of_power;
100pub use bollinger::{BollingerBands, bollinger_bands};
101pub use bull_bear_power::{BullBearPowerResult, bull_bear_power};
102pub use cci::cci;
103pub use chaikin_oscillator::chaikin_oscillator;
104pub use choppiness_index::choppiness_index;
105pub use cmf::cmf;
106pub use cmo::cmo;
107pub use coppock_curve::coppock_curve;
108pub use dema::dema;
109pub use donchian_channels::{DonchianChannelsResult, donchian_channels};
110pub use elder_ray::{ElderRayResult, elder_ray};
111pub use ema::ema;
112pub use fibonacci_retracement::{FibonacciLevels, fibonacci_retracement};
113pub use heikin_ashi::heikin_ashi;
114pub(crate) use heikin_ashi::heikin_ashi_raw;
115pub use hma::hma;
116pub use ichimoku::{IchimokuResult, ichimoku};
117pub use keltner_channels::{KeltnerChannelsResult, keltner_channels};
118pub use macd::{MacdResult, macd};
119pub use mcginley_dynamic::mcginley_dynamic;
120pub use mfi::mfi;
121pub use momentum::momentum;
122pub use obv::obv;
123pub use parabolic_sar::parabolic_sar;
124pub use patterns::{CandlePattern, PatternSentiment, patterns};
125pub use pivot_points::{PivotPoints, fibonacci_pivot_points, pivot_points};
126pub use roc::roc;
127pub use rsi::rsi;
128pub use sma::sma;
129pub use stochastic::{StochasticResult, stochastic};
130pub use stochastic_rsi::stochastic_rsi;
131pub use supertrend::{SuperTrendResult, supertrend};
132pub use tema::tema;
133pub use true_range::true_range;
134pub use vwap::vwap;
135pub use vwma::vwma;
136pub use williams_r::williams_r;
137pub use wma::wma;
138pub use zigzag::{ZigZagPoint, zigzag};
139
140pub use summary::{
142 AroonData, BollingerBandsData, BullBearPowerData, DonchianChannelsData, ElderRayData,
143 IchimokuData, IndicatorsSummary, KeltnerChannelsData, MacdData, StochasticData, SuperTrendData,
144};
145
146pub use Indicator as IndicatorType;
148
149#[derive(Debug, thiserror::Error)]
151pub enum IndicatorError {
152 #[error("Insufficient data: need at least {need} data points, got {got}")]
154 InsufficientData {
155 need: usize,
157 got: usize,
159 },
160
161 #[error("Invalid period: {0}")]
163 InvalidPeriod(String),
164}
165
166pub type Result<T> = std::result::Result<T, IndicatorError>;
168
169#[derive(Debug, Clone, PartialEq)]
175#[non_exhaustive]
176pub enum IndicatorResult {
177 Series(Vec<Option<f64>>),
179 Macd(MacdResult),
181 Bollinger(BollingerBands),
183 Stochastic(StochasticResult),
185 Aroon(AroonResult),
187 SuperTrend(SuperTrendResult),
189 Ichimoku(IchimokuResult),
191 BullBearPower(BullBearPowerResult),
193 ElderRay(ElderRayResult),
195 Keltner(KeltnerChannelsResult),
197 Donchian(DonchianChannelsResult),
199 PivotPoints(Vec<Option<PivotPoints>>),
201 HeikinAshi(Vec<crate::Candle>),
203 ZigZag(Vec<ZigZagPoint>),
205 FibonacciRetracement(Vec<Option<FibonacciLevels>>),
207}
208
209#[derive(Debug, Clone, Copy, PartialEq)]
214#[non_exhaustive]
215pub enum Indicator {
216 Sma(usize),
218 Ema(usize),
220 Rsi(usize),
222 Macd {
224 fast: usize,
226 slow: usize,
228 signal: usize,
230 },
231 Bollinger {
233 period: usize,
235 std_dev: f64,
237 },
238 Atr(usize),
240 Obv,
242 Vwap,
244 Wma(usize),
246 Dema(usize),
248 Tema(usize),
250 Hma(usize),
252 Vwma(usize),
254 Alma {
256 period: usize,
258 offset: f64,
260 sigma: f64,
262 },
263 McginleyDynamic(usize),
265 Stochastic {
267 k_period: usize,
269 k_slow: usize,
271 d_period: usize,
273 },
274 StochasticRsi {
276 rsi_period: usize,
278 stoch_period: usize,
280 k_period: usize,
282 d_period: usize,
284 },
285 Cci(usize),
287 WilliamsR(usize),
289 Roc(usize),
291 Momentum(usize),
293 Cmo(usize),
295 AwesomeOscillator {
297 fast: usize,
299 slow: usize,
301 },
302 CoppockCurve {
304 wma_period: usize,
306 long_roc: usize,
308 short_roc: usize,
310 },
311 Adx(usize),
313 Aroon(usize),
315 Supertrend {
317 period: usize,
319 multiplier: f64,
321 },
322 Ichimoku {
324 conversion: usize,
326 base: usize,
328 lagging: usize,
330 displacement: usize,
332 },
333 ParabolicSar {
335 step: f64,
337 max: f64,
339 },
340 BullBearPower(usize),
342 ElderRay(usize),
344 KeltnerChannels {
346 period: usize,
348 multiplier: f64,
350 atr_period: usize,
352 },
353 DonchianChannels(usize),
355 TrueRange,
357 ChoppinessIndex(usize),
359 Mfi(usize),
361 Cmf(usize),
363 ChaikinOscillator,
365 AccumulationDistribution,
367 BalanceOfPower(Option<usize>),
369 PivotPointsStandard,
371 PivotPointsFibonacci,
373 HeikinAshi,
375 ZigZag(f64),
377 FibonacciRetracement(usize),
379}
380
381impl Indicator {
382 pub fn with_defaults(self) -> Self {
393 match self {
394 Indicator::Sma(_) => Indicator::Sma(20),
395 Indicator::Ema(_) => Indicator::Ema(12),
396 Indicator::Rsi(_) => Indicator::Rsi(14),
397 Indicator::Macd { .. } => Indicator::Macd {
398 fast: 12,
399 slow: 26,
400 signal: 9,
401 },
402 Indicator::Bollinger { .. } => Indicator::Bollinger {
403 period: 20,
404 std_dev: 2.0,
405 },
406 Indicator::Atr(_) => Indicator::Atr(14),
407 ind => ind,
408 }
409 }
410
411 pub fn name(&self) -> &'static str {
413 match self {
414 Indicator::Sma(_) => "Simple Moving Average",
415 Indicator::Ema(_) => "Exponential Moving Average",
416 Indicator::Rsi(_) => "Relative Strength Index",
417 Indicator::Macd { .. } => "MACD",
418 Indicator::Bollinger { .. } => "Bollinger Bands",
419 Indicator::Atr(_) => "Average True Range",
420 Indicator::Obv => "On-Balance Volume",
421 Indicator::Vwap => "VWAP",
422 Indicator::Wma(_) => "Weighted Moving Average",
423 Indicator::Dema(_) => "Double Exponential Moving Average",
424 Indicator::Tema(_) => "Triple Exponential Moving Average",
425 Indicator::Hma(_) => "Hull Moving Average",
426 Indicator::Vwma(_) => "Volume Weighted Moving Average",
427 Indicator::Alma { .. } => "Arnaud Legoux Moving Average",
428 Indicator::McginleyDynamic(_) => "McGinley Dynamic",
429 Indicator::Stochastic { .. } => "Stochastic Oscillator",
430 Indicator::StochasticRsi { .. } => "Stochastic RSI",
431 Indicator::Cci(_) => "Commodity Channel Index",
432 Indicator::WilliamsR(_) => "Williams %R",
433 Indicator::Roc(_) => "Rate of Change",
434 Indicator::Momentum(_) => "Momentum",
435 Indicator::Cmo(_) => "Chande Momentum Oscillator",
436 Indicator::AwesomeOscillator { .. } => "Awesome Oscillator",
437 Indicator::CoppockCurve { .. } => "Coppock Curve",
438 Indicator::Adx(_) => "Average Directional Index",
439 Indicator::Aroon(_) => "Aroon",
440 Indicator::Supertrend { .. } => "SuperTrend",
441 Indicator::Ichimoku { .. } => "Ichimoku Cloud",
442 Indicator::ParabolicSar { .. } => "Parabolic SAR",
443 Indicator::BullBearPower(_) => "Bull/Bear Power",
444 Indicator::ElderRay(_) => "Elder Ray Index",
445 Indicator::KeltnerChannels { .. } => "Keltner Channels",
446 Indicator::DonchianChannels(_) => "Donchian Channels",
447 Indicator::TrueRange => "True Range",
448 Indicator::ChoppinessIndex(_) => "Choppiness Index",
449 Indicator::Mfi(_) => "Money Flow Index",
450 Indicator::Cmf(_) => "Chaikin Money Flow",
451 Indicator::ChaikinOscillator => "Chaikin Oscillator",
452 Indicator::AccumulationDistribution => "Accumulation/Distribution",
453 Indicator::BalanceOfPower(_) => "Balance of Power",
454 Indicator::PivotPointsStandard => "Pivot Points (Standard)",
455 Indicator::PivotPointsFibonacci => "Pivot Points (Fibonacci)",
456 Indicator::HeikinAshi => "Heikin-Ashi",
457 Indicator::ZigZag(_) => "ZigZag",
458 Indicator::FibonacciRetracement(_) => "Fibonacci Retracement",
459 }
460 }
461
462 pub fn warmup_bars(&self) -> usize {
478 match self {
479 Self::Sma(p)
480 | Self::Ema(p)
481 | Self::Rsi(p)
482 | Self::Atr(p)
483 | Self::Wma(p)
484 | Self::Dema(p)
485 | Self::Tema(p)
486 | Self::Hma(p)
487 | Self::Vwma(p)
488 | Self::McginleyDynamic(p)
489 | Self::Cci(p)
490 | Self::WilliamsR(p)
491 | Self::Roc(p)
492 | Self::Momentum(p)
493 | Self::Cmo(p)
494 | Self::Adx(p)
495 | Self::Aroon(p)
496 | Self::DonchianChannels(p)
497 | Self::ChoppinessIndex(p)
498 | Self::Mfi(p)
499 | Self::Cmf(p)
500 | Self::BullBearPower(p)
501 | Self::ElderRay(p) => *p,
502 Self::Macd { fast, slow, signal } => *slow.max(fast) + signal,
503 Self::Bollinger { period, .. } => *period,
504 Self::Alma { period, .. } => *period,
505 Self::Stochastic {
506 k_period,
507 k_slow,
508 d_period,
509 } => k_period + k_slow + d_period,
510 Self::StochasticRsi {
511 rsi_period,
512 stoch_period,
513 k_period,
514 d_period,
515 } => rsi_period + stoch_period + k_period.max(d_period),
516 Self::AwesomeOscillator { slow, .. } => *slow,
517 Self::CoppockCurve {
518 long_roc,
519 wma_period,
520 ..
521 } => long_roc + wma_period,
522 Self::Ichimoku {
523 base, displacement, ..
524 } => base + displacement,
525 Self::Supertrend { period, .. } => *period,
526 Self::ParabolicSar { .. } => 2,
527 Self::KeltnerChannels {
528 period, atr_period, ..
529 } => *period.max(atr_period),
530 Self::BalanceOfPower(Some(p)) => *p,
531 Self::FibonacciRetracement(p) => *p,
532 Self::PivotPointsStandard | Self::PivotPointsFibonacci => 2,
534 Self::ZigZag(_) => 2,
535 Self::Obv
537 | Self::Vwap
538 | Self::TrueRange
539 | Self::ChaikinOscillator
540 | Self::AccumulationDistribution
541 | Self::BalanceOfPower(None)
542 | Self::HeikinAshi => 1,
543 }
544 }
545}
546
547pub fn last_value(values: &[Option<f64>]) -> Option<f64> {
560 values.iter().rev().find_map(|&v| v)
561}
562
563pub(crate) fn compute_indicator(
569 indicator: Indicator,
570 chart: &crate::models::chart::Chart,
571) -> Result<IndicatorResult> {
572 let o = chart.open_prices();
573 let h = chart.high_prices();
574 let l = chart.low_prices();
575 let c = chart.close_prices();
576 let v = chart.volumes();
577 Ok(match indicator {
578 Indicator::Sma(p) => IndicatorResult::Series(chart.sma(p)),
579 Indicator::Ema(p) => IndicatorResult::Series(chart.ema(p)),
580 Indicator::Rsi(p) => IndicatorResult::Series(chart.rsi(p)?),
581 Indicator::Macd { fast, slow, signal } => {
582 IndicatorResult::Macd(chart.macd(fast, slow, signal)?)
583 }
584 Indicator::Bollinger { period, std_dev } => {
585 IndicatorResult::Bollinger(chart.bollinger_bands(period, std_dev)?)
586 }
587 Indicator::Atr(p) => IndicatorResult::Series(chart.atr(p)?),
588 Indicator::Vwap => IndicatorResult::Series(crate::indicators::vwap(&h, &l, &c, &v)?),
589 Indicator::Wma(p) => IndicatorResult::Series(crate::indicators::wma(&c, p)?),
590 Indicator::Obv => IndicatorResult::Series(crate::indicators::obv(&c, &v)?),
591 Indicator::Dema(p) => IndicatorResult::Series(crate::indicators::dema(&c, p)?),
592 Indicator::Tema(p) => IndicatorResult::Series(crate::indicators::tema(&c, p)?),
593 Indicator::Hma(p) => IndicatorResult::Series(crate::indicators::hma(&c, p)?),
594 Indicator::Vwma(p) => IndicatorResult::Series(crate::indicators::vwma(&c, &v, p)?),
595 Indicator::Alma {
596 period,
597 offset,
598 sigma,
599 } => IndicatorResult::Series(crate::indicators::alma(&c, period, offset, sigma)?),
600 Indicator::McginleyDynamic(p) => {
601 IndicatorResult::Series(crate::indicators::mcginley_dynamic(&c, p)?)
602 }
603 Indicator::Stochastic {
604 k_period,
605 k_slow,
606 d_period,
607 } => IndicatorResult::Stochastic(crate::indicators::stochastic(
608 &h, &l, &c, k_period, k_slow, d_period,
609 )?),
610 Indicator::StochasticRsi {
611 rsi_period,
612 stoch_period,
613 k_period,
614 d_period,
615 } => IndicatorResult::Stochastic(crate::indicators::stochastic_rsi(
616 &c,
617 rsi_period,
618 stoch_period,
619 k_period,
620 d_period,
621 )?),
622 Indicator::Cci(p) => IndicatorResult::Series(crate::indicators::cci(&h, &l, &c, p)?),
623 Indicator::WilliamsR(p) => {
624 IndicatorResult::Series(crate::indicators::williams_r(&h, &l, &c, p)?)
625 }
626 Indicator::Roc(p) => IndicatorResult::Series(crate::indicators::roc(&c, p)?),
627 Indicator::Momentum(p) => IndicatorResult::Series(crate::indicators::momentum(&c, p)?),
628 Indicator::Cmo(p) => IndicatorResult::Series(crate::indicators::cmo(&c, p)?),
629 Indicator::AwesomeOscillator { fast, slow } => {
630 IndicatorResult::Series(crate::indicators::awesome_oscillator(&h, &l, fast, slow)?)
631 }
632 Indicator::CoppockCurve {
633 long_roc,
634 short_roc,
635 wma_period,
636 } => IndicatorResult::Series(crate::indicators::coppock_curve(
637 &c, long_roc, short_roc, wma_period,
638 )?),
639 Indicator::Adx(p) => IndicatorResult::Series(crate::indicators::adx(&h, &l, &c, p)?),
640 Indicator::Aroon(p) => IndicatorResult::Aroon(crate::indicators::aroon(&h, &l, p)?),
641 Indicator::Supertrend { period, multiplier } => IndicatorResult::SuperTrend(
642 crate::indicators::supertrend(&h, &l, &c, period, multiplier)?,
643 ),
644 Indicator::Ichimoku {
645 conversion,
646 base,
647 lagging,
648 displacement,
649 } => IndicatorResult::Ichimoku(crate::indicators::ichimoku(
650 &h,
651 &l,
652 &c,
653 conversion,
654 base,
655 lagging,
656 displacement,
657 )?),
658 Indicator::ParabolicSar { step, max } => {
659 IndicatorResult::Series(crate::indicators::parabolic_sar(&h, &l, &c, step, max)?)
660 }
661 Indicator::BullBearPower(p) => {
662 IndicatorResult::BullBearPower(crate::indicators::bull_bear_power(&h, &l, &c, p)?)
663 }
664 Indicator::ElderRay(p) => {
665 IndicatorResult::ElderRay(crate::indicators::elder_ray(&h, &l, &c, p)?)
666 }
667 Indicator::KeltnerChannels {
668 period,
669 multiplier,
670 atr_period,
671 } => IndicatorResult::Keltner(crate::indicators::keltner_channels(
672 &h, &l, &c, period, atr_period, multiplier,
673 )?),
674 Indicator::DonchianChannels(p) => {
675 IndicatorResult::Donchian(crate::indicators::donchian_channels(&h, &l, p)?)
676 }
677 Indicator::TrueRange => IndicatorResult::Series(crate::indicators::true_range(&h, &l, &c)?),
678 Indicator::ChoppinessIndex(p) => {
679 IndicatorResult::Series(crate::indicators::choppiness_index(&h, &l, &c, p)?)
680 }
681 Indicator::Mfi(p) => IndicatorResult::Series(crate::indicators::mfi(&h, &l, &c, &v, p)?),
682 Indicator::Cmf(p) => IndicatorResult::Series(crate::indicators::cmf(&h, &l, &c, &v, p)?),
683 Indicator::ChaikinOscillator => {
684 IndicatorResult::Series(crate::indicators::chaikin_oscillator(&h, &l, &c, &v)?)
685 }
686 Indicator::AccumulationDistribution => IndicatorResult::Series(
687 crate::indicators::accumulation_distribution(&h, &l, &c, &v)?,
688 ),
689 Indicator::BalanceOfPower(p) => {
690 IndicatorResult::Series(crate::indicators::balance_of_power(&o, &h, &l, &c, p)?)
691 }
692 Indicator::PivotPointsStandard => {
693 IndicatorResult::PivotPoints(crate::indicators::pivot_points(&h, &l, &c)?)
694 }
695 Indicator::PivotPointsFibonacci => {
696 IndicatorResult::PivotPoints(crate::indicators::fibonacci_pivot_points(&h, &l, &c)?)
697 }
698 Indicator::HeikinAshi => {
699 IndicatorResult::HeikinAshi(crate::indicators::heikin_ashi(&chart.candles)?)
700 }
701 Indicator::ZigZag(deviation_pct) => {
702 IndicatorResult::ZigZag(crate::indicators::zigzag(&h, &l, deviation_pct)?)
703 }
704 Indicator::FibonacciRetracement(period) => IndicatorResult::FibonacciRetracement(
705 crate::indicators::fibonacci_retracement(&h, &l, period)?,
706 ),
707 })
708}
709
710#[cfg(test)]
711mod tests {
712 use super::*;
713
714 #[test]
715 fn test_last_value() {
716 assert_eq!(last_value(&[None, None, Some(1.0), Some(2.0)]), Some(2.0));
717 assert_eq!(last_value(&[None, None, Some(1.0), None]), Some(1.0));
718 assert_eq!(last_value(&[None, None, None]), None);
719 assert_eq!(last_value(&[]), None);
720 }
721}