Skip to main content

finance_query/indicators/
mod.rs

1//! Technical analysis indicators for financial data.
2//!
3//! This module provides common technical indicators used by traders and analysts.
4//! All indicators work with time series price data from OHLCV candles.
5//!
6//! # Available Indicators
7//!
8//! ## Moving Averages
9//! - [`sma`] - Simple Moving Average
10//! - [`ema`] - Exponential Moving Average
11//!
12//! ## Momentum Oscillators
13//! - [`rsi`] - Relative Strength Index
14//!
15//! ## Trend Indicators
16//! - [`macd`] - Moving Average Convergence Divergence
17//!
18//! ## Volatility Indicators
19//! - [`bollinger_bands`] - Bollinger Bands
20//! - [`atr`] - Average True Range
21//!
22//! # Example
23//!
24//! ```no_run
25//! use finance_query::{Ticker, Interval, TimeRange};
26//!
27//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
28//! let ticker = Ticker::new("AAPL").await?;
29//! let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
30//!
31//! // Use Chart extension methods (requires "indicators" feature)
32//! let sma_20 = chart.sma(20);
33//! let rsi_14 = chart.rsi(14)?;
34//! let atr_14 = chart.atr(14)?;
35//!
36//! // Or call indicators directly
37//! let closes: Vec<f64> = chart.candles.iter().map(|c| c.close).collect();
38//! let ema_12 = finance_query::indicators::ema(&closes, 12);
39//! # Ok(())
40//! # }
41//! ```
42
43mod 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
89// Summary module for batch indicator calculations
90pub mod summary;
91
92// Re-export all indicators and patterns
93pub 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
140// Re-export summary types
141pub use summary::{
142    AroonData, BollingerBandsData, BullBearPowerData, DonchianChannelsData, ElderRayData,
143    IchimokuData, IndicatorsSummary, KeltnerChannelsData, MacdData, StochasticData, SuperTrendData,
144};
145
146// Re-export Indicator enum for easy access
147pub use Indicator as IndicatorType;
148
149/// Error type for indicator calculations
150#[derive(Debug, thiserror::Error)]
151pub enum IndicatorError {
152    /// Not enough data points to calculate the indicator
153    #[error("Insufficient data: need at least {need} data points, got {got}")]
154    InsufficientData {
155        /// Minimum number of data points required
156        need: usize,
157        /// Actual number of data points provided
158        got: usize,
159    },
160
161    /// Invalid period parameter provided
162    #[error("Invalid period: {0}")]
163    InvalidPeriod(String),
164}
165
166/// Result type for indicator calculations
167pub type Result<T> = std::result::Result<T, IndicatorError>;
168
169/// Result of an indicator calculation
170///
171/// Different indicators return different types of data:
172/// - Simple indicators (SMA, EMA, RSI, ATR) return a time series of values
173/// - Complex indicators (MACD, Bollinger Bands) return multiple series
174#[derive(Debug, Clone, PartialEq)]
175#[non_exhaustive]
176pub enum IndicatorResult {
177    /// Single value time series (SMA, EMA, RSI, ATR, OBV, VWAP)
178    Series(Vec<Option<f64>>),
179    /// MACD result with three series
180    Macd(MacdResult),
181    /// Bollinger Bands with upper, middle, lower bands
182    Bollinger(BollingerBands),
183    /// Stochastic Oscillator result
184    Stochastic(StochasticResult),
185    /// Aroon result
186    Aroon(AroonResult),
187    /// SuperTrend result
188    SuperTrend(SuperTrendResult),
189    /// Ichimoku Cloud result
190    Ichimoku(IchimokuResult),
191    /// Bull/Bear Power result
192    BullBearPower(BullBearPowerResult),
193    /// Elder Ray Index result
194    ElderRay(ElderRayResult),
195    /// Keltner Channels result
196    Keltner(KeltnerChannelsResult),
197    /// Donchian Channels result
198    Donchian(DonchianChannelsResult),
199    /// Pivot Points result (standard or Fibonacci variant)
200    PivotPoints(Vec<Option<PivotPoints>>),
201    /// Heikin-Ashi transformed candles
202    HeikinAshi(Vec<crate::Candle>),
203    /// ZigZag swing points
204    ZigZag(Vec<ZigZagPoint>),
205    /// Fibonacci Retracement levels
206    FibonacciRetracement(Vec<Option<FibonacciLevels>>),
207}
208
209/// Enum representing all available technical indicators.
210///
211/// This enum is used with `Ticker::indicator()` to calculate specific indicators
212/// over a given interval and time range.
213#[derive(Debug, Clone, Copy, PartialEq)]
214#[non_exhaustive]
215pub enum Indicator {
216    /// Simple Moving Average with custom period
217    Sma(usize),
218    /// Exponential Moving Average with custom period
219    Ema(usize),
220    /// Relative Strength Index with custom period
221    Rsi(usize),
222    /// Moving Average Convergence Divergence (fast, slow, signal periods)
223    Macd {
224        /// Fast EMA period
225        fast: usize,
226        /// Slow EMA period
227        slow: usize,
228        /// Signal line EMA period
229        signal: usize,
230    },
231    /// Bollinger Bands (period, standard deviation multiplier)
232    Bollinger {
233        /// SMA period
234        period: usize,
235        /// Standard deviation multiplier
236        std_dev: f64,
237    },
238    /// Average True Range with custom period
239    Atr(usize),
240    /// On-Balance Volume
241    Obv,
242    /// Volume Weighted Average Price
243    Vwap,
244    /// Weighted Moving Average
245    Wma(usize),
246    /// Double Exponential Moving Average
247    Dema(usize),
248    /// Triple Exponential Moving Average
249    Tema(usize),
250    /// Hull Moving Average
251    Hma(usize),
252    /// Volume Weighted Moving Average
253    Vwma(usize),
254    /// Arnaud Legoux Moving Average
255    Alma {
256        /// Window period
257        period: usize,
258        /// Offset (typically 0.85)
259        offset: f64,
260        /// Sigma (typically 6.0)
261        sigma: f64,
262    },
263    /// McGinley Dynamic
264    McginleyDynamic(usize),
265    /// Stochastic Oscillator
266    Stochastic {
267        /// %K period
268        k_period: usize,
269        /// %K slowing period
270        k_slow: usize,
271        /// %D period (SMA of %K)
272        d_period: usize,
273    },
274    /// Stochastic RSI
275    StochasticRsi {
276        /// RSI period
277        rsi_period: usize,
278        /// Stochastic period applied to RSI
279        stoch_period: usize,
280        /// %K smoothing period
281        k_period: usize,
282        /// %D smoothing period
283        d_period: usize,
284    },
285    /// Commodity Channel Index
286    Cci(usize),
287    /// Williams %R
288    WilliamsR(usize),
289    /// Rate of Change
290    Roc(usize),
291    /// Momentum
292    Momentum(usize),
293    /// Chande Momentum Oscillator
294    Cmo(usize),
295    /// Awesome Oscillator
296    AwesomeOscillator {
297        /// Fast SMA period
298        fast: usize,
299        /// Slow SMA period
300        slow: usize,
301    },
302    /// Coppock Curve
303    CoppockCurve {
304        /// WMA smoothing period
305        wma_period: usize,
306        /// Long ROC period
307        long_roc: usize,
308        /// Short ROC period
309        short_roc: usize,
310    },
311    /// Average Directional Index
312    Adx(usize),
313    /// Aroon
314    Aroon(usize),
315    /// SuperTrend
316    Supertrend {
317        /// ATR period
318        period: usize,
319        /// ATR multiplier
320        multiplier: f64,
321    },
322    /// Ichimoku Cloud
323    Ichimoku {
324        /// Conversion line period (Tenkan-sen)
325        conversion: usize,
326        /// Base line period (Kijun-sen)
327        base: usize,
328        /// Lagging span period (Chikou Span)
329        lagging: usize,
330        /// Displacement for leading spans
331        displacement: usize,
332    },
333    /// Parabolic SAR
334    ParabolicSar {
335        /// Acceleration factor step
336        step: f64,
337        /// Maximum acceleration factor
338        max: f64,
339    },
340    /// Bull/Bear Power
341    BullBearPower(usize),
342    /// Elder Ray Index
343    ElderRay(usize),
344    /// Keltner Channels
345    KeltnerChannels {
346        /// EMA period for middle line
347        period: usize,
348        /// ATR multiplier for bands
349        multiplier: f64,
350        /// ATR period
351        atr_period: usize,
352    },
353    /// Donchian Channels
354    DonchianChannels(usize),
355    /// True Range
356    TrueRange,
357    /// Choppiness Index
358    ChoppinessIndex(usize),
359    /// Money Flow Index
360    Mfi(usize),
361    /// Chaikin Money Flow
362    Cmf(usize),
363    /// Chaikin Oscillator
364    ChaikinOscillator,
365    /// Accumulation/Distribution
366    AccumulationDistribution,
367    /// Balance of Power
368    BalanceOfPower(Option<usize>),
369    /// Standard (classic) Pivot Points, derived from the previous bar's high/low/close
370    PivotPointsStandard,
371    /// Fibonacci Pivot Points, derived from the previous bar's high/low/close
372    PivotPointsFibonacci,
373    /// Heikin-Ashi candle transform
374    HeikinAshi,
375    /// ZigZag with a percentage reversal threshold
376    ZigZag(f64),
377    /// Fibonacci Retracement over a rolling lookback window
378    FibonacciRetracement(usize),
379}
380
381impl Indicator {
382    /// Get a default instance with standard parameters
383    ///
384    /// # Examples
385    ///
386    /// ```
387    /// use finance_query::indicators::Indicator;
388    ///
389    /// let rsi = Indicator::Rsi(14);  // 14-period RSI
390    /// let macd = Indicator::Macd { fast: 12, slow: 26, signal: 9 };
391    /// ```
392    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    /// Get the human-readable name of the indicator
412    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    /// Minimum number of data bars required before this indicator produces
463    /// meaningful output.
464    ///
465    /// Used by the backtesting engine's `CustomStrategy` to automatically
466    /// compute the warmup period instead of parsing key-name suffixes.
467    ///
468    /// # Examples
469    ///
470    /// ```
471    /// use finance_query::indicators::Indicator;
472    ///
473    /// assert_eq!(Indicator::Sma(20).warmup_bars(), 20);
474    /// assert_eq!(Indicator::Macd { fast: 12, slow: 26, signal: 9 }.warmup_bars(), 35);
475    /// assert_eq!(Indicator::Bollinger { period: 20, std_dev: 2.0 }.warmup_bars(), 20);
476    /// ```
477    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            // Both pivot-point variants only need the single prior bar.
533            Self::PivotPointsStandard | Self::PivotPointsFibonacci => 2,
534            Self::ZigZag(_) => 2,
535            // Volume/price indicators with no meaningful lookback
536            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
547/// Helper function to extract the last non-None value from a vector.
548///
549/// Useful for converting historical indicator values to latest value only.
550///
551/// # Example
552///
553/// ```
554/// use finance_query::indicators::last_value;
555///
556/// let values = vec![None, None, Some(10.0), Some(20.0)];
557/// assert_eq!(last_value(&values), Some(20.0));
558/// ```
559pub fn last_value(values: &[Option<f64>]) -> Option<f64> {
560    values.iter().rev().find_map(|&v| v)
561}
562
563/// Compute a single [`Indicator`] over a [`Chart`](crate::models::chart::Chart).
564///
565/// Shared dispatch used by `Ticker::indicator` and the domain handles
566/// (`ForexPair`, `CryptoCoin`, `Index`, `FuturesContract`, `Commodity`) so the
567/// indicator-selection logic lives in exactly one place.
568pub(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}