Indicators¶
Access 52+ technical indicators calculated from historical price data.
Enable Feature¶
Add the indicators feature to your Cargo.toml:
Or enable it alongside other features:
Getting Started¶
Fetch indicators for a symbol:
use finance_query::{Ticker, Interval, TimeRange};
let ticker = Ticker::new("AAPL").await?;
let indicators = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
println!("RSI(14): {:?}", indicators.rsi_14);
println!("SMA(200): {:?}", indicators.sma_200);
println!("MACD: {:?}", indicators.macd);
Three Ways to Calculate Indicators¶
Finance Query provides three approaches for calculating indicators, each suited for different use cases:
Decision Matrix
| Approach | Use Case | Custom Periods | Data Source | Caching |
|---|---|---|---|---|
| Summary API | Multiple indicators, dashboards | ✗ Fixed only | Automatic | ✓ Yes |
| Chart Methods | Few indicators, custom periods | ✓ Yes | Chart data | ✗ No |
| Direct Functions | Advanced, backtesting, custom data | ✓ Yes | Any Vec |
✗ No |
1. Summary API¶
Get all 52+ indicators pre-calculated with standard periods. Best for dashboards and analysis requiring many indicators.
use finance_query::{Ticker, Interval, TimeRange};
let ticker = Ticker::new("AAPL").await?;
let indicators = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
// All indicators calculated at once with standard periods
println!("RSI(14): {:?}", indicators.rsi_14);
println!("SMA(200): {:?}", indicators.sma_200);
println!("MACD: {:?}", indicators.macd);
2. Chart Extension Methods¶
Call indicators directly on chart data with custom periods. Best when you need specific periods or a few indicators.
use finance_query::{Ticker, Interval, TimeRange};
let ticker = Ticker::new("AAPL").await?;
let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Calculate indicators with custom periods
let sma_15 = chart.sma(15); // Custom period: 15
let rsi_21 = chart.rsi(21)?; // Custom period: 21
let macd = chart.macd(12, 26, 9)?; // Custom MACD parameters
// Access the last value
if let Some(&last_sma) = sma_15.last().and_then(|v| v.as_ref()) {
println!("Latest SMA(15): {:.2}", last_sma);
}
3. Direct Indicator Functions¶
Call raw indicator functions on price arrays. Best for custom data sources, backtesting, or advanced use cases.
use finance_query::{Ticker, Interval, TimeRange};
use finance_query::indicators::{sma, rsi, macd};
let ticker = Ticker::new("AAPL").await?;
let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Extract price data
let closes: Vec<f64> = chart.candles.iter().map(|c| c.close).collect();
let highs: Vec<f64> = chart.candles.iter().map(|c| c.high).collect();
let lows: Vec<f64> = chart.candles.iter().map(|c| c.low).collect();
// Calculate indicators directly
let sma_25 = sma(&closes, 25); // Returns Vec<Option<f64>>
let rsi_10 = rsi(&closes, 10)?; // Returns Result<Vec<Option<f64>>>
let macd_result = macd(&closes, 12, 26, 9)?; // Returns Result<MacdResult>
// Access results
if let Some(&last_rsi) = rsi_10.last().and_then(|v| v.as_ref()) {
println!("RSI(10): {:.2}", last_rsi);
}
// MACD returns a struct with three series
if let Some(&last_macd) = macd_result.macd_line.last().and_then(|v| v.as_ref()) {
println!("MACD Line: {:.4}", last_macd);
}
Working with Compound Indicators¶
Some indicators return multiple series in a result struct. Here's how to use them with direct functions:
use finance_query::indicators::{bollinger_bands, stochastic, macd};
let ticker = Ticker::new("AAPL").await?;
let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
let closes: Vec<f64> = chart.candles.iter().map(|c| c.close).collect();
let highs: Vec<f64> = chart.candles.iter().map(|c| c.high).collect();
let lows: Vec<f64> = chart.candles.iter().map(|c| c.low).collect();
// Bollinger Bands - returns BollingerBands struct
let bb = bollinger_bands(&closes, 20, 2.0)?;
if let Some(&upper) = bb.upper.last().and_then(|v| v.as_ref()) {
if let Some(&middle) = bb.middle.last().and_then(|v| v.as_ref()) {
if let Some(&lower) = bb.lower.last().and_then(|v| v.as_ref()) {
println!("BB: Upper={:.2}, Middle={:.2}, Lower={:.2}", upper, middle, lower);
}
}
}
// Stochastic Oscillator - returns StochasticResult struct
let stoch = stochastic(&highs, &lows, &closes, 14, 3)?;
if let Some(&k) = stoch.k.last().and_then(|v| v.as_ref()) {
if let Some(&d) = stoch.d.last().and_then(|v| v.as_ref()) {
println!("Stochastic: %K={:.2}, %D={:.2}", k, d);
}
}
// MACD - returns MacdResult struct
let macd_data = macd(&closes, 12, 26, 9)?;
if let Some(&line) = macd_data.macd_line.last().and_then(|v| v.as_ref()) {
if let Some(&signal) = macd_data.signal_line.last().and_then(|v| v.as_ref()) {
if let Some(&hist) = macd_data.histogram.last().and_then(|v| v.as_ref()) {
println!("MACD: Line={:.4}, Signal={:.4}, Histogram={:.4}", line, signal, hist);
}
}
}
Available Result Structs¶
Direct indicator functions return these result types:
- Simple indicators (SMA, EMA, RSI, ATR):
Vec<Option<f64>> - MACD:
MacdResult { macd_line, signal_line, histogram } - Bollinger Bands:
BollingerBands { upper, middle, lower } - Stochastic:
StochasticResult { k, d } - Aroon:
AroonResult { aroon_up, aroon_down } - SuperTrend:
SuperTrendResult { value, is_uptrend } - Ichimoku:
IchimokuResult { conversion_line, base_line, leading_span_a, leading_span_b, lagging_span } - Keltner Channels:
KeltnerChannelsResult { upper, middle, lower } - Donchian Channels:
DonchianChannelsResult { upper, middle, lower } - Bull/Bear Power:
BullBearPowerResult { bull_power, bear_power } - Elder Ray:
ElderRayResult { bull_power, bear_power }
Available Indicators¶
All indicators return Option<T> - they're None if there's insufficient data to calculate.
Moving Averages¶
Simple, exponential, and specialized moving averages for trend identification.
Simple Moving Averages (SMA):
sma_10, sma_20, sma_50, sma_100, sma_200
Exponential Moving Averages (EMA):
ema_10, ema_20, ema_50, ema_100, ema_200
Weighted Moving Averages (WMA):
wma_10, wma_20, wma_50, wma_100, wma_200
Advanced Moving Averages:
dema_20- Double Exponential Moving Averagetema_20- Triple Exponential Moving Averagehma_20- Hull Moving Averagevwma_20- Volume Weighted Moving Averagealma_9- Arnaud Legoux Moving Averagemcginley_dynamic_20- McGinley Dynamic
Momentum Oscillators¶
Measure rate of change and momentum for entry/exit signals.
rsi_14- Relative Strength Indexstochastic- Stochastic Oscillator (K and D lines)stochastic_rsi- Stochastic RSIcci_20- Commodity Channel Indexwilliams_r_14- Williams %Rroc_12- Rate of Changemomentum_10- Momentumcmo_14- Chande Momentum Oscillatorawesome_oscillator- Bill Williams Awesome Oscillatorcoppock_curve- Coppock Curve
Trend Indicators¶
Identify trend direction and strength.
macd- MACD (line, signal, histogram)adx_14- Average Directional Indexaroon- Aroon Up/Downsupertrend- Supertrend (uptrend, downtrend, trend direction)ichimoku- Ichimoku Cloud (multiple components)parabolic_sar- Parabolic SARbull_bear_power- Bull and Bear Powerelder_ray_index- Elder Ray Index (bull power, bear power)
Volatility Indicators¶
Measure price volatility and support/resistance levels.
bollinger_bands- Bollinger Bands (upper, middle, lower)keltner_channels- Keltner Channels (upper, middle, lower)donchian_channels- Donchian Channels (high, low)atr_14- Average True Rangetrue_range- True Range (raw)choppiness_index_14- Choppiness Index
Volume Indicators¶
Analyze volume patterns and accumulation/distribution.
obv- On-Balance Volumemfi_14- Money Flow Indexcmf_20- Chaikin Money Flowchaikin_oscillator- Chaikin Oscillatoraccumulation_distribution- Accumulation/Distribution Linevwap- Volume Weighted Average Pricebalance_of_power- Balance of Power
Working with Indicator Results¶
Different indicators return different types. Simple indicators return Option<f64>, while compound indicators return special struct types:
// Simple indicators (Option<f64>)
if let Some(rsi) = indicators.rsi_14 {
println!("RSI(14): {:.2}", rsi);
if rsi < 30.0 {
println!(" Oversold");
} else if rsi > 70.0 {
println!(" Overbought");
}
}
// Moving averages
if let Some(sma200) = indicators.sma_200 {
println!("SMA(200): {:.2}", sma200);
}
// MACD (compound - MacdData struct)
if let Some(macd) = indicators.macd {
if let Some(line) = macd.macd {
println!("MACD Line: {:.4}", line);
}
if let Some(signal) = macd.signal {
println!("Signal: {:.4}", signal);
}
if let Some(histogram) = macd.histogram {
println!("Histogram: {:.4}", histogram);
}
}
// Stochastic (StochasticData struct)
if let Some(stoch) = indicators.stochastic {
if let Some(k) = stoch.k {
println!("%K: {:.2}", k);
}
if let Some(d) = stoch.d {
println!("%D: {:.2}", d);
}
}
// Bollinger Bands (BollingerBandsData struct)
if let Some(bb) = indicators.bollinger_bands {
if let Some(upper) = bb.upper {
println!("Upper: {:.2}", upper);
}
if let Some(middle) = bb.middle {
println!("Middle: {:.2}", middle);
}
if let Some(lower) = bb.lower {
println!("Lower: {:.2}", lower);
}
}
// Aroon (AroonData struct)
if let Some(aroon) = indicators.aroon {
if let Some(up) = aroon.aroon_up {
println!("Aroon Up: {:.2}", up);
}
if let Some(down) = aroon.aroon_down {
println!("Aroon Down: {:.2}", down);
}
}
// Ichimoku (IchimokuData struct)
if let Some(ichimoku) = indicators.ichimoku {
if let Some(conversion) = ichimoku.conversion_line {
println!("Conversion Line: {:.2}", conversion);
}
if let Some(base) = ichimoku.base_line {
println!("Base Line: {:.2}", base);
}
}
Converting to DataFrame¶
Convert all indicators to a Polars DataFrame for analysis:
use finance_query::{Ticker, Interval, TimeRange};
let ticker = Ticker::new("AAPL").await?;
let indicators = ticker.indicators(
Interval::OneDay,
TimeRange::ThreeMonths
).await?;
let df = indicators.to_dataframe()?;
println!("{}", df);
Caching Behavior¶
Indicators are cached by (interval, range) combination:
// First call fetches and caches
let ind1 = ticker.indicators(Interval::OneDay, TimeRange::OneMonth).await?;
// Second call returns cached result
let ind2 = ticker.indicators(Interval::OneDay, TimeRange::OneMonth).await?;
// Different range: fetches new data
let ind3 = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
Common Patterns¶
Trend Confirmation with Multiple MAs¶
let indicators = ticker.indicators(Interval::OneDay, TimeRange::OneYear).await?;
let sma_200 = indicators.sma_200.unwrap_or(0.0);
let ema_50 = indicators.ema_50.unwrap_or(0.0);
let ema_20 = indicators.ema_20.unwrap_or(0.0);
if ema_20 > ema_50 && ema_50 > sma_200 {
println!("Uptrend confirmed");
}
RSI Extremes¶
if let Some(rsi) = indicators.rsi_14 {
if rsi < 30.0 {
println!("Oversold");
} else if rsi > 70.0 {
println!("Overbought");
}
}
MACD Crossover¶
if let Some(macd) = indicators.macd {
if let (Some(line), Some(signal)) = (macd.macd, macd.signal) {
if line > signal {
println!("Bullish MACD crossover");
} else {
println!("Bearish MACD crossover");
}
}
}
Best Practices¶
Optimize Performance and Data Usage
- Store indicator results - Indicators are calculated fresh each time, so store the result if accessing multiple values
- Underlying chart data is cached - Same
(interval, range)avoids network requests but still recalculates indicators - Fetch appropriate ranges - Use the minimum time range needed for your indicators to calculate
- Check for None - Always pattern match on
Option<T>before using indicator values - Ensure sufficient data - Indicators require minimum data points to calculate:
- Most 14-period indicators need 14+ candles
- MACD needs ~26+ candles (slow EMA period)
- Ichimoku needs ~26+ candles
- Short-period indicators (SMA/EMA 10) need at least 10 candles
- If insufficient data, the indicator returns
None
// Good: Store result once, access multiple indicators
let indicators = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
if let Some(rsi) = indicators.rsi_14 {
if rsi < 30.0 {
// Oversold - check other indicators from same result
if let Some(macd) = &indicators.macd {
if let (Some(line), Some(signal)) = (macd.macd, macd.signal) {
if line > signal {
println!("Potential buy: RSI oversold + MACD bullish");
}
}
}
}
}
// Less efficient: Multiple calls recalculate all indicators
// (Chart data is cached, but indicators are recalculated)
let rsi_result = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
if let Some(rsi) = rsi_result.rsi_14 {
// ...
}
let macd_result = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Still wastes CPU recalculating all 52+ indicators
Next Steps¶
- Backtesting - Use indicators in custom trading strategies
- Ticker API - Complete reference for fetching indicators and other data
- DataFrame Support - Convert indicator results to Polars DataFrames for analysis
- Models Reference - Understanding indicator data structures