Technical Analysis (wraquant.ta)¶
263 indicators across 19 sub-modules, covering every category of technical analysis: moving averages, momentum oscillators, volume studies, trend detection, volatility measurement, candlestick patterns, cycle analysis, Fibonacci tools, support/resistance, market breadth, and exotic indicators.
Every indicator accepts pd.Series (or OHLCV components) and returns
either a pd.Series or a dict[str, pd.Series] for multi-output
indicators.
Quick Example¶
from wraquant.ta import rsi, macd, bollinger_bands, adx, atr, crossover
# Momentum: RSI and MACD
rsi_values = rsi(close, period=14)
macd_result = macd(close) # {'macd', 'signal', 'histogram'}
# Volatility bands
bb = bollinger_bands(close, period=20, std_dev=2.0)
# bb['upper'], bb['middle'], bb['lower']
# Trend strength (ADX > 25 = strong trend)
adx_values = adx(high, low, close, period=14)
# Signal generation: EMA crossover
from wraquant.ta import ema
fast = ema(close, period=10)
slow = ema(close, period=50)
buy_signal = crossover(fast, slow)
Candlestick Patterns¶
from wraquant.ta import doji, engulfing, hammer, morning_star
# Each pattern returns a boolean Series
dojis = doji(open, high, low, close)
engulfings = engulfing(open, high, low, close)
hammers = hammer(open, high, low, close)
print(f"Doji days: {dojis.sum()}")
print(f"Engulfing days: {engulfings.sum()}")
Advanced Smoothing¶
from wraquant.ta import alma, jma, supersmoother
# ALMA: Arnaud Legoux Moving Average (low lag, low noise)
alma_values = alma(close, period=21)
# JMA: Jurik Moving Average (adaptive smoothing)
jma_values = jma(close, period=14)
# Super Smoother (Ehlers 2-pole filter)
ss = supersmoother(close, period=10)
Support & Resistance Detection¶
from wraquant.ta import find_support_resistance, supply_demand_zones
levels = find_support_resistance(close, n_levels=5)
print(f"Support levels: {levels['support']}")
print(f"Resistance levels: {levels['resistance']}")
zones = supply_demand_zones(open, high, low, close)
print(f"Supply zones: {zones['supply']}")
print(f"Demand zones: {zones['demand']}")
See also
ML Alpha Research – Use TA indicators as ML features
Backtesting Strategies – Build strategies from TA signals
Machine Learning (wraquant.ml) –
technical_features()wraps TA into feature DataFrames
API Reference¶
Technical analysis indicators for wraquant.
This package provides over 200 technical analysis indicators organised
into 19 sub-modules. Every indicator accepts pd.Series (or OHLCV
components) and returns either a pd.Series or a
dict[str, pd.Series] for multi-output indicators.
Sub-modules¶
The sub-modules are designed to cover the full taxonomy of technical analysis, from classical chart overlays to exotic oscillators:
Price overlays (drawn on the price chart):
overlap – Moving averages and band studies. Start here for trend identification. SMA/EMA for trend direction, Bollinger Bands and Keltner Channel for volatility envelopes, Ichimoku for multi-timeframe support/resistance, Supertrend for trend-following signals.
trend – Trend direction and strength. ADX measures trend strength (not direction); values >25 indicate a strong trend. Aroon identifies trend starts. PSAR provides trailing stops. Heikin Ashi smooths price bars. ZigZag filters noise for swing analysis.
Oscillators (plotted in separate panels):
momentum – Speed and magnitude of price moves. RSI (14-period) is the most popular; >70 = overbought, <30 = oversold. MACD captures trend momentum via EMA crossovers. Stochastic compares close to range. Use TSI or CMO for smoother momentum signals. Squeeze Histogram detects volatility contraction breakouts.
volume – Volume-confirmed signals. OBV (On-Balance Volume) trends with the dominant side. CMF (Chaikin Money Flow) measures buying vs selling pressure. MFI is “volume-weighted RSI.” Force Index combines price change and volume.
performance – Relative performance and drawdown analytics. Alpha, tracking error, and up/down capture ratios for benchmark comparison. Rolling max drawdown and pain index for risk monitoring.
Volatility measurement (separate panel or overlay):
volatility – ATR (Average True Range) is the standard volatility measure for position sizing. Bollinger Width and Keltner Width measure squeeze/expansion. Garman-Klass, Parkinson, Rogers-Satchell, and Yang-Zhang use OHLC data for more efficient volatility estimation. Ulcer Index measures downside volatility.
Pattern recognition:
patterns – 38 candlestick patterns (Doji, Engulfing, Morning Star, Evening Star, Hammer, Three White Soldiers, etc.). Each returns a boolean Series marking pattern occurrences. Use for confirmation, not as standalone signals.
candles – Structural candlestick analytics: body size, shadow ratios, body-to-range ratio, inside/outside bars, pin bars. Useful as features for ML models.
price_action – Higher highs/lows for trend structure. Swing highs/lows for support/resistance. Gap analysis, range expansion, narrow range (NR4/NR7), key reversals.
Signal generation:
signals – Utility functions for combining indicators: crossover, crossunder, above/below, rising/falling, highest/lowest, normalize. These are building blocks for strategy logic.
Market breadth (for indices / baskets):
breadth – Advance/Decline line and ratio, McClellan Oscillator and Summation Index, Arms Index (TRIN), new highs/lows, percent above MA. Use these to confirm or diverge from index-level trends.
Statistical overlays:
statistics – Z-score, percentile rank, skewness, kurtosis, entropy, Hurst exponent, rolling beta and R-squared. These bridge technical and quantitative analysis.
Cycle analysis:
cycles – Hilbert Transform dominant period and trend mode, sine wave indicators, bandpass and roofing filters, Even Better Sinewave. Use when you suspect periodic structure (e.g., inventory or seasonal cycles).
Fibonacci analysis:
fibonacci – Retracements, extensions, fans, time zones, pivot points, and auto-Fibonacci (automatically finds swing points).
Advanced smoothing:
smoothing – ALMA (Arnaud Legoux), JMA (Jurik), Butterworth, Super Smoother, Gaussian, windowed MAs (Hann, Hamming). Use when standard MAs are too laggy or noisy.
Custom / advanced indicators:
custom – Squeeze Momentum, Anchored VWAP, Ehlers Fisher Transform, Adaptive RSI, Linear Regression Channel, Market Structure, Volume-Weighted MACD. Power tools for experienced analysts.
Exotic / lesser-known indicators:
exotic – Choppiness Index (ranging vs trending), Random Walk Index, Polarized Fractal Efficiency, Ergodic Oscillator, Elder Thermometer, KAIRI, Connors TPS. Niche but valuable for specific strategies.
Support and resistance:
support_resistance – Algorithmic detection of support/resistance levels, fractal levels, price clustering, round numbers, supply/demand zones, and trendline detection.
How to choose indicators¶
Identify the market condition first: trending or ranging? Use ADX > 25 to confirm trend, Choppiness Index > 61.8 for range.
For trending markets: use trend-following indicators (EMA crossovers, MACD, Supertrend, PSAR). Avoid mean-reversion oscillators (RSI, Stochastic) as standalone signals.
For ranging markets: use oscillators (RSI, Stochastic, CCI) at overbought/oversold levels. Bollinger Bands for mean-reversion entries.
Always confirm with volume (OBV, CMF) or breadth (A/D line).
For ML feature engineering: prefer continuous indicators (candle ratios, Z-scores, ATR-normalized values) over boolean pattern detectors. See
wraquant.ml.features.technical_features.
- sma(data, period=20)[source]¶
Simple Moving Average.
The most fundamental overlay indicator. Smooths price by averaging the last period values equally.
- Interpretation:
Price above SMA: Bullish – price is above its average.
Price below SMA: Bearish – price is below its average.
Golden cross: Short-term SMA (e.g. 50) crosses above long-term SMA (e.g. 200) = bullish trend signal.
Death cross: Short-term SMA crosses below long-term SMA = bearish trend signal.
Slope: Rising SMA confirms uptrend; falling confirms downtrend.
Common periods: 20 (short-term), 50 (medium-term), 200 (long-term institutional benchmark).
- Trading rules:
Buy when price crosses above SMA (or when shorter SMA crosses above longer SMA).
Sell when price crosses below SMA.
Use 200 SMA as a trend filter: only take longs above it.
- ema(data, period=20)[source]¶
Exponential Moving Average.
Uses the standard span-based smoothing factor
2 / (period + 1). More responsive to recent price changes than SMA because it weights recent data more heavily.- Interpretation:
Same as SMA: price above = bullish, price below = bearish.
More responsive than SMA: catches trend changes faster but may produce more whipsaws.
EMA crossovers (e.g. 12/26 EMA) form the basis of MACD.
Common periods: 9 (very short-term), 21 (swing trading), 50 and 200 (institutional benchmarks).
- Trading rules:
Same crossover rules as SMA but with faster signals.
In fast markets, prefer EMA over SMA for tighter stops and quicker entries.
- wma(data, period=20)[source]¶
Weighted Moving Average.
Weights increase linearly so that the most recent observation receives the highest weight. A compromise between SMA and EMA.
- Interpretation:
Same directional signals as SMA/EMA: price above = bullish, below = bearish, crossovers for entry/exit.
More responsive than SMA but less than EMA.
Useful when you want more weight on recent data but a smoother result than EMA.
- dema(data, period=20)[source]¶
Double Exponential Moving Average.
DEMA = 2 * EMA(data) - EMA(EMA(data))Reduces the lag inherent in a standard EMA by subtracting a double-smoothed version. More responsive to recent price changes.
- Interpretation:
Same as EMA/SMA but with reduced lag. Use for faster crossover signals and tighter trailing stops.
Price above DEMA = bullish; below = bearish.
- tema(data, period=20)[source]¶
Triple Exponential Moving Average.
TEMA = 3 * EMA - 3 * EMA(EMA) + EMA(EMA(EMA))Even less lag than DEMA. Hugs price very tightly and reacts quickly to changes. Can overshoot in choppy markets.
- Interpretation:
Same as EMA but with minimal lag. Excellent for short-term trend following but may whipsaw in consolidation.
Price above TEMA = bullish; below = bearish.
- kama(data, period=10, fast=2, slow=30)[source]¶
Kaufman Adaptive Moving Average (KAMA).
KAMA adapts its smoothing constant based on the efficiency ratio of the price movement. In trending markets it acts like a fast EMA; in choppy markets it slows down to avoid whipsaws.
- Interpretation:
Price above KAMA: Bullish.
Price below KAMA: Bearish.
Flat KAMA: Market is choppy/range-bound (KAMA stops following noise). This is the key advantage over SMA/EMA.
KAMA direction change: Potential trend reversal signal.
- Trading rules:
Buy when price crosses above KAMA.
Sell when price crosses below KAMA.
KAMA’s adaptive nature makes it better for trend following than fixed-period moving averages in volatile markets.
- vwap(high, low, close, volume)[source]¶
Volume Weighted Average Price (VWAP).
Computed as the cumulative sum of
typical_price * volumedivided by cumulative volume. This is the intraday running VWAP; for session-reset VWAP, pre-group your data by session.- Interpretation:
Price above VWAP: Buyers are in control; longs entered at good prices relative to the average fill.
Price below VWAP: Sellers are in control.
VWAP as support/resistance: Institutional traders use VWAP as a benchmark. Price tends to gravitate toward VWAP.
Mean reversion: Extreme deviations from VWAP tend to revert, especially intraday.
- Trading rules:
Buy pullbacks to VWAP in an uptrend (institutional support).
Sell rallies to VWAP in a downtrend (institutional resistance).
Institutions aim to buy below VWAP and sell above it; track whether your fills are better than VWAP.
- supertrend(high, low, close, period=10, multiplier=3.0)[source]¶
Supertrend indicator.
A trend-following overlay that flips between support and resistance levels based on ATR bands. One of the cleanest trend indicators.
- Interpretation:
Direction = 1 (uptrend): Supertrend line acts as dynamic support below price. Trend is bullish.
Direction = -1 (downtrend): Supertrend line acts as dynamic resistance above price. Trend is bearish.
Flip from -1 to 1: Buy signal (trend turns bullish).
Flip from 1 to -1: Sell signal (trend turns bearish).
- Trading rules:
Buy when direction flips to 1 (close above supertrend).
Sell when direction flips to -1 (close below supertrend).
Use the supertrend line as a trailing stop.
Higher multiplier = fewer flips but wider stops.
- Parameters:
- Returns:
supertrend— the indicator line, anddirection(1 for uptrend / bullish, -1 for downtrend / bearish).- Return type:
- ichimoku(high, low, close, tenkan=9, kijun=26, senkou_b=52)[source]¶
Ichimoku Kinko Hyo (Ichimoku Cloud).
A comprehensive trend system that provides support/resistance, trend direction, and momentum in a single view.
- Interpretation:
Price above cloud: Bullish trend. The cloud acts as support.
Price below cloud: Bearish trend. The cloud acts as resistance.
Price inside cloud: Trend is transitioning/uncertain.
Tenkan-Kijun cross: Tenkan crossing above Kijun = bullish signal (TK cross). Below = bearish.
Senkou Span A above B: Cloud is green = bullish bias.
Senkou Span A below B: Cloud is red = bearish bias.
Chikou Span above price: Confirms bullish momentum.
Cloud thickness: Thicker cloud = stronger support/ resistance.
- Trading rules:
Buy when price breaks above cloud AND Tenkan > Kijun AND Chikou is above price from 26 periods ago.
Sell when price breaks below cloud AND Tenkan < Kijun.
Use cloud edges (Senkou A/B) as stop-loss levels.
- Parameters:
- Returns:
Keys:
tenkan_sen,kijun_sen,senkou_span_a,senkou_span_b,chikou_span.- Return type:
Notes
Senkou Span A and B are shifted forward by
kijunperiods, and the Chikou Span is shifted backward bykijunperiods, matching traditional charting convention.
- bollinger_bands(data, period=20, std_dev=2.0)[source]¶
Bollinger Bands.
A volatility-based envelope around a moving average. The bands widen during high volatility and contract during low volatility.
- Interpretation:
Price touching upper band: Price is at the high end of its recent range. Not necessarily a sell signal in strong uptrends (walking the band).
Price touching lower band: Price is at the low end. Not necessarily a buy signal in downtrends.
Squeeze (narrow bandwidth): Low volatility – a breakout in either direction is imminent.
Expansion (wide bandwidth): High volatility – move may be overextended and due for consolidation.
%B > 1: Price is above the upper band.
%B < 0: Price is below the lower band.
%B near 0.5: Price is at the middle band (SMA).
- Trading rules:
Mean reversion: Buy at lower band, sell at upper band (works best in ranging markets).
Breakout: Buy on a close above upper band with expanding bandwidth (works in trending markets).
Squeeze play: Wait for narrow bands, then trade the breakout direction.
- keltner_channel(high, low, close, period=20, multiplier=1.5)[source]¶
Keltner Channel.
The middle line is an EMA of the close; upper and lower bands are offset by a multiple of the Average True Range.
- Interpretation:
Price above upper band: Strong uptrend or overbought.
Price below lower band: Strong downtrend or oversold.
Price bouncing off middle line: EMA acting as support (uptrend) or resistance (downtrend).
Keltner + Bollinger: When BB is inside KC, a “squeeze” is active. See
squeeze_momentum().
- Trading rules:
Buy pullbacks to the middle line (EMA) in an uptrend.
Sell rallies to the middle line in a downtrend.
Breakout above upper band = trend continuation (go long).
Breakout below lower band = trend continuation (go short).
- donchian_channel(high, low, period=20)[source]¶
Donchian Channel.
The simplest channel indicator: the highest high and lowest low over the look-back period. The basis of the Turtle Trading system.
- Interpretation:
Price at upper band: Price is at the highest point in period bars = potential breakout to the upside.
Price at lower band: Price is at the lowest point = potential breakout to the downside.
Channel width: Wider channel = more volatile market.
Middle line: Average of upper and lower = equilibrium.
- Trading rules:
Buy when price breaks above the upper band (20-day high).
Sell when price breaks below the lower band (20-day low).
Use 10-day Donchian for exits, 20-day for entries (Turtle Trading system).
- advance_decline_line(advancing, declining)[source]¶
Advance/Decline Line – cumulative sum of (advancing - declining).
The A/D line is a breadth indicator that tracks the running total of the difference between the number of advancing and declining issues.
- Interpretation:
Rising A/D line with rising market: Healthy uptrend – broad participation confirms the rally.
Falling A/D line with rising market: Bearish divergence – fewer stocks participating in the rally. Distribution.
Rising A/D line with falling market: Bullish divergence – accumulation occurring beneath the surface.
The A/D line often leads the market at major turning points.
- Parameters:
- Returns:
Cumulative A/D line values.
- Return type:
Example
>>> adv = pd.Series([200, 250, 180, 300, 220]) >>> dec = pd.Series([100, 150, 220, 100, 180]) >>> advance_decline_line(adv, dec)
- advance_decline_ratio(advancing, declining)[source]¶
Advance/Decline Ratio — advancing / declining.
Values above 1.0 indicate more advancers than decliners; below 1.0 indicates more decliners.
- Parameters:
- Returns:
A/D ratio values (NaN where declining is zero).
- Return type:
Example
>>> adv = pd.Series([200, 250, 180]) >>> dec = pd.Series([100, 150, 220]) >>> advance_decline_ratio(adv, dec)
- mcclellan_oscillator(advancing, declining, fast=19, slow=39)[source]¶
McClellan Oscillator – difference between fast and slow EMA of AD diff.
McClellan = EMA(advancing - declining, fast) - EMA(advancing - declining, slow)- Interpretation:
Above zero: Short-term breadth momentum is positive (more stocks advancing than declining, accelerating).
Below zero: Short-term breadth momentum is negative.
Above +100: Very overbought breadth-wise.
Below -100: Very oversold breadth-wise.
Zero-line crossover: Breadth momentum shift.
Best used for timing entries: buy when the oscillator turns up from below -100 (oversold breadth bounce).
- Parameters:
- Returns:
McClellan Oscillator values.
- Return type:
Example
>>> result = mcclellan_oscillator(advancing, declining)
- mcclellan_summation(advancing, declining, fast=19, slow=39)[source]¶
McClellan Summation Index – cumulative sum of the McClellan Oscillator.
This is the running total of the McClellan Oscillator, providing a longer-term view of market breadth.
- Interpretation:
Rising: Long-term breadth is improving (more and more stocks participating in the advance).
Falling: Long-term breadth is deteriorating.
Above +1000: Strongly bullish long-term breadth.
Below -1000: Strongly bearish long-term breadth.
Acts as a long-term trend indicator for market internals.
- Parameters:
- Returns:
McClellan Summation Index values.
- Return type:
Example
>>> result = mcclellan_summation(advancing, declining)
- arms_index(advancing_issues, declining_issues, advancing_volume, declining_volume)[source]¶
Arms Index (TRIN) — Short-Term Trading Index.
- ``TRIN = (Advancing Issues / Declining Issues) /
(Advancing Volume / Declining Volume)``
Values below 1.0 are bullish (more volume flowing into advancers); values above 1.0 are bearish.
- Parameters:
- Returns:
TRIN values (NaN where denominators are zero).
- Return type:
Example
>>> result = arms_index(adv_issues, dec_issues, adv_vol, dec_vol)
- new_highs_lows(new_highs, new_lows)[source]¶
New Highs minus New Lows.
A simple breadth measure: positive values indicate more new highs than new lows, suggesting bullish breadth.
- Parameters:
- Returns:
New highs minus new lows.
- Return type:
Example
>>> nh = pd.Series([50, 60, 30]) >>> nl = pd.Series([20, 40, 50]) >>> new_highs_lows(nh, nl)
- percent_above_ma(prices_df, period=50)[source]¶
Percentage of components above their N-period moving average.
For each row, computes how many columns have a value above their respective rolling SMA, expressed as a percentage.
- Parameters:
- Returns:
Percentage (0-100) of components above their SMA.
- Return type:
Example
>>> df = pd.DataFrame({"A": [10, 11, 12], "B": [20, 19, 18]}) >>> percent_above_ma(df, period=2)
- high_low_index(new_highs, new_lows)[source]¶
High-Low Index — new highs as a percentage of new highs + new lows.
HLI = new_highs / (new_highs + new_lows) * 100Values above 50 indicate more new highs; values below 50 indicate more new lows.
- Parameters:
- Returns:
High-Low Index values in [0, 100] (NaN where both are zero).
- Return type:
Example
>>> nh = pd.Series([50, 60, 30]) >>> nl = pd.Series([20, 40, 50]) >>> high_low_index(nh, nl)
- bullish_percent(prices_df, period=50)[source]¶
Bullish Percent Index (simplified).
Approximates the Bullish Percent Index by computing the percentage of components trading above their period-day simple moving average. The traditional BPI uses point-and-figure buy signals, but the SMA crossover is a widely accepted simplification.
- Parameters:
- Returns:
Bullish Percent values in [0, 100].
- Return type:
Example
>>> df = pd.DataFrame({"A": [10, 11, 12], "B": [20, 19, 18]}) >>> bullish_percent(df, period=2)
- cumulative_volume_index(close, volume)[source]¶
Cumulative Volume Index (CVI).
Adds volume on up days and subtracts volume on down days.
CVI = cumsum(sign(close.diff()) * volume)- Parameters:
- Returns:
CVI values.
- Return type:
Example
>>> close = pd.Series([100, 102, 101, 103, 104.0]) >>> volume = pd.Series([1000, 1500, 1200, 1800, 1600.0]) >>> cumulative_volume_index(close, volume)
- rsi(data, period=14)[source]¶
Relative Strength Index (RSI).
Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions. Uses the Wilder smoothing method (equivalent to
ewm(alpha=1/period)).RSI = 100 - (100 / (1 + RS)) where RS = avg_gain / avg_loss over
periodbars.- Interpretation:
> 70: Overbought – price may be due for a pullback. In strong uptrends, RSI can stay above 70 for extended periods.
30-70: Neutral zone.
< 30: Oversold – price may be due for a bounce. In strong downtrends, RSI can stay below 30 for extended periods.
Divergence: If price makes a new high but RSI does not, bearish divergence signals weakening momentum. Conversely, bullish divergence when price makes a new low but RSI does not.
Centerline crossover: RSI crossing above 50 = bullish shift, below 50 = bearish shift.
- Trading rules:
Buy when RSI crosses above 30 (oversold bounce).
Sell when RSI crosses below 70 (overbought reversal).
Use divergences for higher-probability signals.
Adjust thresholds in trending markets (80/20 instead of 70/30).
- stochastic(high, low, close, k_period=14, d_period=3)[source]¶
Stochastic Oscillator (%K / %D).
Measures where the close sits within the recent high-low range. When %K is near 100, price closed near the top of the range (bullish); near 0, it closed near the bottom (bearish).
- Interpretation:
> 80: Overbought zone. In strong uptrends, the indicator can stay above 80 for extended periods without signaling a top.
< 20: Oversold zone. In strong downtrends, can persist.
%K crosses above %D below 20: Bullish crossover buy signal.
%K crosses below %D above 80: Bearish crossover sell signal.
Divergence: Price makes new high but %K does not = bearish.
- Trading rules:
Buy when %K crosses above %D in the oversold zone (< 20).
Sell when %K crosses below %D in the overbought zone (> 80).
Avoid trading crossovers in the neutral zone (20-80) unless confirmed by other indicators.
- stochastic_rsi(data, period=14, k_period=3, d_period=3)[source]¶
Stochastic RSI.
Applies the Stochastic formula to the RSI output, producing an even more sensitive oscillator. Useful for detecting short-term overbought/oversold conditions within the RSI itself.
- Interpretation:
> 80: RSI is near its recent high – overbought.
< 20: RSI is near its recent low – oversold.
%K/%D crossovers: Same logic as standard Stochastic.
More volatile than standard Stochastic; best combined with a trend filter to avoid false signals in ranging markets.
- Trading rules:
Buy when StochRSI crosses above 20 (oversold bounce).
Sell when StochRSI crosses below 80 (overbought reversal).
Combine with a trend indicator (e.g. 200 EMA) to filter signals in the direction of the prevailing trend.
- macd(data, fast=12, slow=26, signal=9)[source]¶
Moving Average Convergence Divergence (MACD).
Tracks the relationship between two EMAs. When the fast EMA pulls away from the slow EMA, momentum is strong. The signal line acts as a trigger for entries and exits.
- Interpretation:
MACD above zero: Fast EMA > slow EMA = bullish momentum.
MACD below zero: Fast EMA < slow EMA = bearish momentum.
Signal line crossover: MACD crossing above its signal line is a bullish signal; crossing below is bearish.
Histogram: Represents the distance between MACD and signal. Growing bars = strengthening momentum. Shrinking bars = momentum fading (potential reversal ahead).
Divergence: Price makes a new high but MACD does not = bearish divergence. Price makes a new low but MACD does not = bullish divergence.
Zero-line crossover: MACD crossing above zero confirms an uptrend; crossing below confirms a downtrend.
- Trading rules:
Buy when MACD crosses above signal line (bullish crossover).
Sell when MACD crosses below signal line (bearish crossover).
Histogram peak/trough reversals can provide early warnings.
Combine with price action for confirmation.
- williams_r(high, low, close, period=14)[source]¶
Williams %R.
Measures where the close is relative to the high-low range over the look-back period. Mathematically the inverse of the Stochastic %K, but on a [-100, 0] scale.
- Interpretation:
-20 to 0: Overbought – close is near the period high.
-80 to -100: Oversold – close is near the period low.
-50 crossover: Crossing above -50 = bullish, below = bearish.
Note: Overbought does not mean sell immediately; in strong uptrends, Williams %R stays near 0 for extended periods.
- Trading rules:
Buy when %R crosses above -80 (leaving oversold zone).
Sell when %R crosses below -20 (leaving overbought zone).
Use divergence between price and %R for reversal signals.
- cci(high, low, close, period=20)[source]¶
Commodity Channel Index (CCI).
Measures the deviation of the typical price from its moving average, normalized by mean deviation. Uses Lambert’s constant of 0.015 so that roughly 75% of values fall within [-100, +100].
- Interpretation:
> +100: Price is unusually high relative to average – strong uptrend or overbought condition.
< -100: Price is unusually low – strong downtrend or oversold condition.
Zero-line crossover: CCI crossing above 0 indicates price is above its average (bullish); below 0 is bearish.
Divergence: Price makes new high but CCI does not = weakening momentum.
- Trading rules:
Buy when CCI crosses above +100 (trend entry) or above 0 (conservative entry).
Sell when CCI crosses below -100 (trend entry) or below 0.
Exit longs when CCI crosses back below +100 from above.
Use +200/-200 for extreme overbought/oversold levels.
- roc(data, period=10)[source]¶
Rate of Change (ROC) – percentage change over period bars.
Measures the percentage difference between the current price and the price period bars ago. A pure momentum measure.
- Interpretation:
Positive: Price is higher than it was period bars ago.
Negative: Price is lower.
Zero-line crossover: Crossing above zero = bullish momentum shift; crossing below = bearish.
Extreme readings: Unusually high ROC may indicate an overextended move ripe for mean reversion.
- Trading rules:
Buy when ROC crosses above zero from below.
Sell when ROC crosses below zero from above.
Use divergence with price for reversal signals.
- momentum(data, period=10)[source]¶
Price Momentum (difference over period bars).
The simplest momentum indicator: the absolute price change over the look-back window. Unlike ROC, this is not percentage-based, so it is scale-dependent.
- Interpretation:
Positive: Price is rising relative to period bars ago.
Negative: Price is falling.
Zero-line crossover: Same as ROC – bullish above, bearish below.
Magnitude: Larger values = stronger momentum.
- tsi(data, long=25, short=13, signal=13)[source]¶
True Strength Index (TSI).
A double-smoothed momentum oscillator that measures the ratio of smoothed price change to smoothed absolute price change. Oscillates between -100 and +100.
- Interpretation:
Above zero: Bullish momentum (price changes are predominantly positive).
Below zero: Bearish momentum.
Signal line crossover: TSI crossing above its signal line = bullish; crossing below = bearish.
Zero-line crossover: Confirms trend direction change.
Divergence: Price makes new high but TSI does not = bearish divergence (and vice versa).
- Trading rules:
Buy when TSI crosses above zero or above its signal line.
Sell when TSI crosses below zero or below its signal line.
Use both zero-line and signal-line crossovers together for higher-confidence signals.
- awesome_oscillator(high, low, fast=5, slow=34)[source]¶
Awesome Oscillator (AO).
AO = SMA(median_price, fast) - SMA(median_price, slow)Developed by Bill Williams. Measures market momentum using the difference between a 5-period and 34-period SMA of the midpoint price.
- Interpretation:
Above zero: Bullish momentum (short-term average > long-term average).
Below zero: Bearish momentum.
Zero-line crossover: AO crossing above zero = buy signal; crossing below = sell signal.
Twin peaks (bullish): Two lows below zero where the second is higher than the first, followed by a green bar.
Twin peaks (bearish): Two highs above zero where the second is lower than the first, followed by a red bar.
Saucer: Three consecutive bars above zero where the middle bar is lowest = continuation buy signal.
- ppo(data, fast=12, slow=26, signal=9)[source]¶
Percentage Price Oscillator (PPO).
Like MACD but expressed as a percentage of the slow EMA, making it comparable across different price levels and assets.
- Interpretation:
Same signals as MACD: signal-line crossovers, zero-line crossovers, histogram analysis, and divergences.
Advantage over MACD: Because it is percentage-based, you can compare PPO values across stocks of different prices.
> 0: Fast EMA is above slow EMA = bullish.
< 0: Fast EMA is below slow EMA = bearish.
Histogram: Grows when momentum accelerates, shrinks when momentum decelerates.
- Trading rules:
Same as MACD: buy on bullish signal crossover, sell on bearish signal crossover.
Use PPO instead of MACD when comparing momentum across multiple securities.
- ultimate_oscillator(high, low, close, period1=7, period2=14, period3=28)[source]¶
Ultimate Oscillator.
Combines buying pressure across three timeframes (7, 14, 28 by default) into a single oscillator. Reduces false signals by incorporating multiple periods.
- Interpretation:
> 70: Overbought.
< 30: Oversold.
Divergence: The primary signal. A bullish divergence occurs when price makes a new low but the UO does not, AND the UO is below 30. A bearish divergence occurs when price makes a new high but UO does not, AND UO is above 70.
- Trading rules (Larry Williams’ method):
Buy on bullish divergence: price makes lower low, UO makes higher low, UO dips below 30, then UO breaks above the divergence high.
Sell when UO rises above 70, or when UO crosses below 50 after a buy signal.
- Parameters:
- Returns:
Ultimate Oscillator values in [0, 100].
- Return type:
- cmo(data, period=14)[source]¶
Chande Momentum Oscillator (CMO).
Similar to RSI but uses the difference between gains and losses divided by their sum, producing an oscillator in [-100, +100] instead of [0, 100].
- Interpretation:
> +50: Strong bullish momentum, potentially overbought.
< -50: Strong bearish momentum, potentially oversold.
Zero crossover: CMO crossing above 0 = bullish; below = bearish.
Unlike RSI, CMO is symmetric around zero, making it easier to read directional bias.
- Trading rules:
Buy when CMO crosses above -50 (leaving oversold).
Sell when CMO crosses below +50 (leaving overbought).
Use zero-line crossover as a trend filter.
- dpo(data, period=20)[source]¶
Detrended Price Oscillator (DPO).
DPO = close - SMA(close, period).shift(period // 2 + 1)Removes the trend component from price to isolate cycles. Unlike most oscillators, DPO is not a momentum indicator – it helps identify cycle highs and lows.
- Interpretation:
Positive: Price is above the displaced moving average (cycle high territory).
Negative: Price is below the displaced moving average (cycle low territory).
Peaks and troughs: Mark cycle turning points. Measure the time between peaks to estimate the dominant cycle length.
Not useful for trend trading; best for timing entries within a known cycle.
- schaff_momentum(data, period=10, fast=23, slow=50)[source]¶
Schaff Trend Cycle applied to momentum (Schaff Momentum).
Applies two rounds of Stochastic smoothing to the difference between fast and slow EMAs, producing a bounded oscillator in [0, 100].
- Interpretation:
> 75: Bullish – trend cycle is in the upper zone.
< 25: Bearish – trend cycle is in the lower zone.
25-75: Transition zone.
Crosses above 25: Buy signal (turning bullish).
Crosses below 75: Sell signal (turning bearish).
Faster than MACD because of the double stochastic smoothing; provides earlier signals but may also have more false signals.
- Parameters:
- Returns:
Schaff Momentum values in [0, 100].
- Return type:
Example
>>> result = schaff_momentum(close, period=10)
- price_momentum_oscillator(data, short=35, long=20, signal=10)[source]¶
Price Momentum Oscillator (PMO) – double-smoothed ROC.
PMO = EMA(EMA(ROC(1), short), long)Developed by Carl Swenlin, the PMO is a double-smoothed one-bar rate-of-change, scaled by a factor of 10 for visibility.
- Interpretation:
Above zero: Bullish momentum (price trend is up).
Below zero: Bearish momentum.
Signal line crossover: PMO crossing above signal = buy; crossing below = sell.
Zero-line crossover: Confirms a trend change.
Extreme readings: PMO values at historical extremes (relative to asset) indicate overextended moves.
Divergence: Price makes new high but PMO does not = bearish divergence.
- Trading rules:
Buy on bullish signal-line crossover, especially near zero or in negative territory.
Sell on bearish signal-line crossover.
- Parameters:
- Returns:
pmoandsignal.- Return type:
Example
>>> result = price_momentum_oscillator(close) >>> result["pmo"]
- klinger_oscillator(high, low, close, volume, fast=34, slow=55, signal=13)[source]¶
Klinger Volume Oscillator – momentum-oriented view.
Uses the relationship between price trend direction and volume to predict price reversals. Combines volume with trend to identify accumulation and distribution.
- Interpretation:
KVO above zero: Volume flow is bullish (accumulation).
KVO below zero: Volume flow is bearish (distribution).
Signal line crossover: KVO crossing above signal = buy; crossing below = sell.
Divergence: Price makes new high but KVO does not = distribution occurring despite rising prices.
- Parameters:
- Returns:
kvoandsignal.- Return type:
Example
>>> result = klinger_oscillator(high, low, close, volume) >>> result["kvo"]
- stochastic_momentum_index(high, low, close, period=14, smooth_k=3, smooth_d=3)[source]¶
Stochastic Momentum Index (SMI).
The SMI is a refinement of the Stochastic Oscillator that measures the distance of the close relative to the midpoint of the high-low range, double-smoothed with EMAs.
- Interpretation:
> +40: Overbought zone.
< -40: Oversold zone.
Zero-line crossover: SMI above 0 = bullish; below = bearish.
Signal line crossover: SMI crossing above signal = buy; crossing below = sell.
More refined than Stochastic because it measures distance from the midpoint rather than from the low, reducing noise.
- Parameters:
- Returns:
smiandsignal.- Return type:
Example
>>> result = stochastic_momentum_index(high, low, close) >>> result["smi"]
- inertia(close, high, low, rvi_period=10, linreg_period=20)[source]¶
Ehlers Inertia Indicator – RVI smoothed by linear regression.
Applies a linear regression (moving regression value) to the Relative Volatility Index (RVI) to produce a momentum-like indicator.
- Interpretation:
Above 50: Bullish – volatility conditions favor upside.
Below 50: Bearish – volatility conditions favor downside.
50 crossover: Trend direction change signal.
Smoother than raw RVI due to the regression smoothing, so it gives clearer trend signals with fewer whipsaws.
- Parameters:
- Returns:
Inertia values. Values above 50 are bullish; below 50 are bearish.
- Return type:
Example
>>> result = inertia(close, high, low)
- squeeze_histogram(high, low, close, period=20, linreg_period=20)[source]¶
Squeeze Histogram – momentum component of the TTM Squeeze.
Computes the linear regression value of
close - midlinewhere midline is the average of the highest high and lowest low over the period, combined with the SMA.- Interpretation:
Positive and growing: Bullish momentum accelerating.
Positive and shrinking: Bullish momentum decelerating (potential top forming).
Negative and growing (more negative): Bearish momentum accelerating.
Negative and shrinking: Bearish momentum decelerating (potential bottom forming).
Color changes (when plotted): Dark green = accelerating bullish, light green = decelerating bullish, dark red = accelerating bearish, light red = decelerating bearish.
- Trading rules:
Use with squeeze detection (BB inside KC). When squeeze fires (BB exits KC), trade in the direction of the histogram.
Buy when histogram turns positive after a squeeze release.
Sell when histogram turns negative after a squeeze release.
- Parameters:
- Returns:
Squeeze momentum histogram values.
- Return type:
Example
>>> result = squeeze_histogram(high, low, close)
- center_of_gravity(data, period=10)[source]¶
Ehlers Center of Gravity Oscillator.
A weighted sum of recent prices where more recent bars receive higher weight, normalized by a simple sum. This produces a leading oscillator.
- Interpretation:
Oscillates around a negative value: The center of gravity shifts based on where price weight is concentrated.
Peaks and troughs: Mark potential turning points with virtually zero lag.
Leading indicator: Turns before price does, making it useful for timing entries.
Best used for cycle-based trading on short timeframes.
CoG = -sum(price[i] * (i+1), i=0..period-1) / sum(price[i], i=0..period-1)- Parameters:
- Returns:
Center of Gravity values (oscillates around zero).
- Return type:
Example
>>> result = center_of_gravity(close, period=10)
- psychological_line(data, period=12)[source]¶
Psychological Line — percentage of up days over N periods.
PSY = (number of up bars in period) / period * 100Values above 50 suggest bullish sentiment; below 50 suggest bearish.
- Parameters:
- Returns:
Psychological Line values in [0, 100].
- Return type:
Example
>>> result = psychological_line(close, period=12)
- relative_performance(asset, benchmark)[source]¶
Relative Performance – ratio of asset to benchmark, normalized to 100.
RP = (asset / benchmark) / (asset.iloc[0] / benchmark.iloc[0]) * 100- Interpretation:
Rising line: Asset is outperforming the benchmark. Buy the asset, sell the benchmark (or use as a selection filter).
Falling line: Asset is underperforming the benchmark.
Above 100: Asset has outperformed since the start of the measurement period.
Below 100: Asset has underperformed.
Use to identify sector rotation and relative strength leaders.
- Parameters:
- Returns:
Relative performance, starting at 100.
- Return type:
Example
>>> asset = pd.Series([100, 105, 110]) >>> bench = pd.Series([100, 102, 104]) >>> relative_performance(asset, bench)
- mansfield_rsi(asset, benchmark, period=52)[source]¶
Mansfield Relative Strength (not Wilder RSI).
Compares the asset/benchmark ratio to its own simple moving average, expressing the result as a percentage deviation.
MRS = ((asset / benchmark) / SMA(asset / benchmark, period) - 1) * 100- Parameters:
- Returns:
Mansfield RS values (percentage above/below zero line).
- Return type:
Example
>>> result = mansfield_rsi(asset, benchmark, period=52)
- alpha(asset, benchmark, window=60, risk_free=0.0)[source]¶
Rolling Jensen’s Alpha vs. benchmark.
Computes alpha as the intercept of the rolling OLS regression of excess asset returns on excess benchmark returns.
- Parameters:
- Returns:
Rolling alpha values (annualization depends on input frequency).
- Return type:
Example
>>> result = alpha(asset, benchmark, window=60)
- tracking_error(asset, benchmark, window=60)[source]¶
Rolling Tracking Error vs. benchmark.
Tracking error is the standard deviation of the difference in returns between the asset and the benchmark over a rolling window.
- Parameters:
- Returns:
Rolling tracking error values.
- Return type:
Example
>>> result = tracking_error(asset, benchmark, window=60)
- up_down_capture(asset, benchmark)[source]¶
Up/Down Market Capture Ratio.
Measures how much of the benchmark’s up- and down-market returns the asset captures.
- Parameters:
- Returns:
up_capture,down_capture, andcapture_ratio(up/down).- Return type:
Example
>>> result = up_down_capture(asset, benchmark) >>> result["up_capture"]
- drawdown(data)[source]¶
Drawdown from peak – current decline from running maximum.
DD = (data - running_max) / running_maxReturns non-positive values (0 at peaks, negative during drawdowns).
- Interpretation:
0: At or near all-time high (no drawdown).
-0.05 to -0.10: Mild pullback (5-10%). Normal in healthy trends.
-0.10 to -0.20: Correction territory. May indicate trend weakness.
< -0.20: Bear market territory. Significant damage to portfolio.
Drawdown duration (how long it stays negative) is often more painful psychologically than drawdown depth.
- Parameters:
data (
Series) – Price or equity curve.- Returns:
Drawdown values (non-positive fractions).
- Return type:
Example
>>> prices = pd.Series([100, 105, 102, 108, 103]) >>> drawdown(prices)
- max_drawdown_rolling(data, window=252)[source]¶
Rolling maximum drawdown over a look-back window.
For each point, computes the worst drawdown experienced within the trailing window periods.
- Interpretation:
Tracks the worst loss experienced in the recent past.
Deepening max drawdown: Risk is increasing.
Stable, shallow max drawdown: Low-risk environment.
Use as a risk management metric: if rolling max drawdown exceeds a threshold, reduce position size or exit.
- Parameters:
- Returns:
Rolling max drawdown values (non-positive fractions).
- Return type:
Example
>>> result = max_drawdown_rolling(prices, window=60)
- pain_index(data, window=252)[source]¶
Pain Index — mean of absolute drawdowns over a rolling window.
The Pain Index averages the magnitude of drawdowns; a higher value indicates more sustained or deeper drawdowns.
- Parameters:
- Returns:
Pain Index values (non-negative).
- Return type:
Example
>>> result = pain_index(prices, window=60)
- gain_loss_ratio(data, window=20)[source]¶
Gain/Loss Ratio — average gain / average loss over a rolling window.
Uses the per-period returns (percentage change) of the input series. Values above 1.0 indicate larger average gains than average losses.
- Parameters:
- Returns:
Gain/loss ratio values (NaN when no losses or no gains in window).
- Return type:
Example
>>> result = gain_loss_ratio(prices, window=20)
- profit_factor(data, window=20)[source]¶
Profit Factor — sum of gains / sum of losses over a rolling window.
Uses the per-period returns (percentage change) of the input series. Values above 1.0 indicate total gains exceed total losses.
- Parameters:
- Returns:
Profit factor values (NaN when no losses in window).
- Return type:
Example
>>> result = profit_factor(prices, window=20)
- atr(high, low, close, period=14)[source]¶
Average True Range (ATR).
Uses the Wilder smoothing method (
ewm(alpha=1/period)). The standard measure of market volatility.- Interpretation:
Higher ATR: More volatile market – wider price swings.
Lower ATR: Less volatile – tight price action.
Rising ATR: Volatility is increasing (often at trend beginnings or during strong moves).
Falling ATR: Volatility is decreasing (often during consolidation, before a breakout).
ATR does not indicate direction, only the magnitude of price movement.
- Trading rules:
Stop placement: Set stop-loss at 2x or 3x ATR from entry to account for normal market noise.
Position sizing: Risk a fixed dollar amount per trade; divide by ATR to determine share count.
Breakout confirmation: A breakout with rising ATR is more likely to sustain than one with falling ATR.
Trailing stop: Trail by 2-3x ATR below the highest high (for longs).
- true_range(high, low, close)[source]¶
True Range.
TR = max(H - L, |H - C_prev|, |L - C_prev|)The single-bar volatility measure that accounts for gaps.
- Interpretation:
High TR: Large price movement – high volatility bar.
Low TR: Small price movement – low volatility bar.
Spikes in TR often occur at trend changes or breakouts.
TR forms the basis of ATR and many other volatility indicators.
- natr(high, low, close, period=14)[source]¶
Normalized Average True Range (NATR).
NATR = (ATR / close) * 100- Interpretation:
Same as ATR but expressed as a percentage of price, making it comparable across different assets and price levels.
Higher NATR: More volatile (in percentage terms).
Lower NATR: Less volatile.
Useful for ranking assets by volatility or for building volatility-weighted portfolios.
- bbwidth(data, period=20, std_dev=2.0)[source]¶
Bollinger Band Width.
BBWidth = (upper - lower) / middle- Interpretation:
Low BBWidth (squeeze): Bollinger Bands are narrow – volatility is low. A breakout is imminent. This is the key signal: low volatility precedes high volatility.
High BBWidth: Bollinger Bands are wide – volatility is high. The move may be overextended.
BBWidth at 6-month low: Strong squeeze – prepare for a significant breakout.
BBWidth expanding: Breakout in progress.
- Trading rules:
Look for BBWidth at historical lows (squeeze).
When the squeeze releases (BBWidth starts expanding), trade the breakout direction.
Combine with momentum indicators to determine direction.
- kc_width(high, low, close, period=20, multiplier=1.5)[source]¶
Keltner Channel Width.
KC_Width = (upper - lower) / middle- Interpretation:
Same concept as BBWidth but based on ATR instead of standard deviation.
Narrow KC Width: Low ATR volatility, potential squeeze.
Wide KC Width: High ATR volatility, extended move.
Used with BBWidth for the TTM Squeeze: when BB is inside KC, a squeeze is active.
- chaikin_volatility(high, low, period=10, smoothing=10)[source]¶
Chaikin Volatility.
Measures the rate of change of the EMA of the high-low spread.
- Interpretation:
Rising: Volatility is increasing (range is expanding).
Falling: Volatility is decreasing (range is contracting).
Spike up: Can indicate a market top (panic/climax).
Spike down: Can indicate a market bottom (capitulation followed by quiet).
- historical_volatility(data, period=21, annualize=True)[source]¶
Historical Volatility (close-to-close).
Computes the rolling standard deviation of log returns. When annualize is
Truethe result is scaled bysqrt(252).- Interpretation:
High HV (e.g. > 30% annualized for equities): Asset is highly volatile. Wider stops and smaller positions needed.
Low HV (e.g. < 15% annualized): Asset is calm. Tighter stops and larger positions possible.
HV vs Implied Volatility: If HV < IV, options are relatively expensive (sell premium). If HV > IV, options are cheap (buy premium).
Rising HV: Uncertainty increasing. Often accompanies selloffs.
Falling HV: Market calming down. Often accompanies gradual rallies.
- mass_index(high, low, period=9, trigger=25)[source]¶
Mass Index.
The Mass Index uses the high-low range to identify trend reversals based on range expansions. It accumulates the ratio of two EMAs of the range over the trigger period.
- Interpretation:
Reversal bulge: The key signal. When Mass Index rises above 27 and then falls back below 26.5, a trend reversal is likely (regardless of direction).
The indicator does not tell you the direction of the reversal, only that one is coming.
Combine with a trend indicator to determine which direction the reversal will take.
- Trading rules:
When Mass Index crosses above 27 then back below 26.5 (reversal bulge), prepare for a trend change.
Use a 9-period EMA crossover or similar to determine the new trend direction.
- Parameters:
- Returns:
Mass Index values. A reversal bulge occurs when the index rises above 27 and then falls below 26.5.
- Return type:
- garman_klass(high, low, close, open_, period=21, annualize=True)[source]¶
Garman-Klass volatility estimator.
An efficient OHLC volatility estimator that uses open, high, low, and close prices. More efficient than close-to-close because it uses intraday range information.
- Interpretation:
Values are directly comparable to historical volatility.
Higher efficiency: Uses the same data as close-to-close but extracts more information, producing tighter estimates.
Compare with Parkinson and Yang-Zhang to assess which estimator best suits your data.
Does not handle overnight gaps well; use Yang-Zhang for assets with significant overnight risk.
GK = sqrt((1/n) * sum(0.5*(ln(H/L))^2 - (2*ln(2)-1)*(ln(C/O))^2))- Parameters:
- Returns:
Garman-Klass volatility estimate.
- Return type:
Example
>>> gk = garman_klass(high, low, close, open_, period=21)
- parkinson(high, low, period=21, annualize=True)[source]¶
Parkinson volatility estimator.
Uses the high-low range to estimate volatility, which is more efficient than close-to-close since it captures intraday extremes.
- Interpretation:
Approximately 5x more efficient than close-to-close.
Tends to underestimate true volatility when there are overnight gaps (since it ignores open/close).
Best suited for assets that trade continuously (e.g. forex).
Parkinson = sqrt((1 / (4 * n * ln(2))) * sum((ln(H/L))^2))- Parameters:
- Returns:
Parkinson volatility estimate.
- Return type:
Example
>>> pk = parkinson(high, low, period=21)
- rogers_satchell(high, low, close, open_, period=21, annualize=True)[source]¶
Rogers-Satchell volatility estimator.
Accounts for non-zero drift (trending markets), making it more robust than Parkinson or Garman-Klass for trending assets.
- Interpretation:
Better than Garman-Klass for assets with strong trends, because it does not assume zero drift.
Still does not handle overnight gaps; for that, use Yang-Zhang.
RS = sqrt((1/n) * sum(ln(H/C)*ln(H/O) + ln(L/C)*ln(L/O)))- Parameters:
- Returns:
Rogers-Satchell volatility estimate.
- Return type:
Example
>>> rs = rogers_satchell(high, low, close, open_, period=21)
- yang_zhang(high, low, close, open_, period=21, annualize=True)[source]¶
Yang-Zhang volatility estimator.
The most efficient OHLC volatility estimator. Combines overnight (close-to-open) volatility, open-to-close volatility, and the Rogers-Satchell estimator.
- Interpretation:
The gold standard for OHLC volatility estimation.
Handles both overnight gaps and intraday drift.
Use this as the default volatility estimator when you have full OHLC data.
Compare with close-to-close: if Yang-Zhang is significantly higher, overnight/gap risk is material.
- Parameters:
- Returns:
Yang-Zhang volatility estimate.
- Return type:
Example
>>> yz = yang_zhang(high, low, close, open_, period=21)
- close_to_close(data, period=21, annualize=True)[source]¶
Close-to-close volatility (standard deviation of log returns).
The simplest volatility estimator based on daily log returns. This is equivalent to
historical_volatility()but named explicitly to distinguish it from range-based estimators.- Interpretation:
The baseline volatility measure. All other estimators (Parkinson, Garman-Klass, Yang-Zhang) should be compared against this.
See
historical_volatility()for full interpretation.
- Parameters:
- Returns:
Close-to-close volatility.
- Return type:
Example
>>> cc = close_to_close(close, period=21)
- ulcer_index(data, period=14)[source]¶
Ulcer Index.
Measures downside volatility by computing the quadratic mean of percentage drawdowns from the rolling maximum over the given period. Higher values indicate greater drawdown depth and duration.
- Interpretation:
Low values (< 5): Stable asset with shallow drawdowns.
High values (> 10): Asset is experiencing significant drawdowns.
Unlike standard deviation, only measures downside risk.
Used in the Martin Ratio (return / Ulcer Index) as a risk-adjusted performance metric.
Rising Ulcer Index = drawdowns are deepening = trouble.
UI = sqrt(mean(R^2))whereR = 100 * (C - max(C, n)) / max(C, n)- Parameters:
- Returns:
Ulcer Index values (always >= 0).
- Return type:
Example
>>> ui = ulcer_index(close, period=14)
- relative_volatility_index(data, period=10, smoothing=14)[source]¶
Relative Volatility Index (RVI).
Applies the RSI formula to the rolling standard deviation of closes rather than to price changes.
- Interpretation:
> 50: Volatility is increasing (standard deviation is rising) – directional moves are more likely.
< 50: Volatility is decreasing (standard deviation is falling) – consolidation / range-bound.
Not a standalone indicator; best used as a filter.
- Trading rules:
Confirm RSI signals: only take RSI buy signals when RVI > 50.
Avoid breakout trades when RVI < 50 (low volatility = false breakout risk).
- Parameters:
- Returns:
RVI values oscillating between 0 and 100.
- Return type:
Example
>>> rvi = relative_volatility_index(close, period=10, smoothing=14)
- acceleration_bands(high, low, close, period=20, factor=0.001)[source]¶
Acceleration Bands.
Bands that widen with high-low range acceleration, narrowing during consolidation. Uses
factor * (high - low) / (high + low)as the width multiplier.- Interpretation:
Price above upper band: Strong uptrend / breakout.
Price below lower band: Strong downtrend / breakdown.
Bands narrowing: Consolidation – breakout imminent.
Bands widening: Trend accelerating.
Similar concept to Bollinger Bands but based on range acceleration rather than standard deviation.
- Parameters:
- Returns:
Dictionary with keys
"upper","middle","lower".- Return type:
Example
>>> bands = acceleration_bands(high, low, close, period=20) >>> upper = bands["upper"]
- standard_deviation(data, period=20)[source]¶
Rolling standard deviation.
Computes the rolling sample standard deviation over the given period.
- Interpretation:
Rising: Volatility increasing – larger price swings.
Falling: Volatility decreasing – tighter price action.
Low standard deviation often precedes a breakout.
Used to compute Bollinger Bands (middle +/- N * std_dev).
- Parameters:
- Returns:
Rolling standard deviation values.
- Return type:
Example
>>> sd = standard_deviation(close, period=20)
- variance(data, period=20)[source]¶
Rolling variance.
Computes the rolling sample variance over the given period.
- Interpretation:
The square of standard deviation. Same directional interpretation as standard deviation.
Useful in mathematical contexts where variance is preferred (e.g. portfolio optimization, risk decomposition).
- Parameters:
- Returns:
Rolling variance values.
- Return type:
Example
>>> v = variance(close, period=20)
- obv(close, volume)[source]¶
On Balance Volume (OBV).
OBV is a cumulative running total of volume. Volume is added on up-close days and subtracted on down-close days.
- Interpretation:
Rising OBV: Volume is flowing in (accumulation) – bullish.
Falling OBV: Volume is flowing out (distribution) – bearish.
Divergence: The most important signal. Price makes a new high but OBV does not = smart money is not confirming the move (bearish divergence). Price makes a new low but OBV does not = accumulation is occurring (bullish divergence).
Breakout confirmation: OBV breaking to a new high alongside price confirms the breakout is genuine.
- Trading rules:
Buy when OBV diverges bullishly from price (price falls, OBV holds or rises).
Sell when OBV diverges bearishly from price (price rises, OBV flattens or falls).
Use OBV trend (rising/falling) to confirm price trends.
- ad_line(high, low, close, volume)[source]¶
Accumulation/Distribution Line (AD Line).
Uses the Close Location Value (CLV) money flow multiplier.
- Interpretation:
Rising AD line: Accumulation – buying pressure dominates. Volume is flowing into the asset.
Falling AD line: Distribution – selling pressure dominates.
Divergence: Price makes new high but AD does not = distribution despite rising prices (bearish). Price makes new low but AD does not = accumulation (bullish).
Unlike OBV, the AD line considers where the close is within the high-low range, not just the direction of the close.
- cmf(high, low, close, volume, period=20)[source]¶
Chaikin Money Flow (CMF).
Measures money flow volume over a rolling period, showing whether buying or selling pressure is dominant.
- Interpretation:
Positive (> 0): Buying pressure (accumulation). The higher the value, the stronger the buying.
Negative (< 0): Selling pressure (distribution).
> +0.25: Strong buying pressure.
< -0.25: Strong selling pressure.
Zero-line crossover: Shift from accumulation to distribution or vice versa.
Divergence: Price makes new high but CMF is falling = distribution.
- Trading rules:
Buy when CMF crosses above zero (accumulation starting).
Sell when CMF crosses below zero (distribution starting).
Confirm breakouts: price breaking resistance with positive CMF is more reliable.
- mfi(high, low, close, volume, period=14)[source]¶
Money Flow Index (MFI).
Often called the volume-weighted RSI. Incorporates both price and volume to identify overbought/oversold conditions.
- Interpretation:
> 80: Overbought – high buying pressure, potential reversal.
< 20: Oversold – high selling pressure, potential bounce.
Divergence: Price makes new high but MFI does not = weakening volume-confirmed momentum (bearish).
More reliable than RSI alone because it includes volume: a price move on heavy volume is more meaningful.
- Trading rules:
Buy when MFI crosses above 20 (leaving oversold).
Sell when MFI crosses below 80 (leaving overbought).
MFI divergence with price is a strong reversal signal.
- eom(high, low, volume, period=14)[source]¶
Ease of Movement (EMV / EOM).
Relates price change to volume, showing how easily price is moving.
- Interpretation:
Positive: Price is advancing on relatively low volume (easy movement up) = bullish.
Negative: Price is declining on relatively low volume (easy movement down) = bearish.
Near zero: Price movement requires substantial volume = indecision or strong resistance/support.
Zero-line crossover: Shift from easy upward to easy downward movement or vice versa.
- force_index(close, volume, period=13)[source]¶
Force Index.
Force = close.diff() * volume, then smoothed with EMA.Combines price change and volume to measure the strength behind a move.
- Interpretation:
Positive: Buying force – price is rising with volume.
Negative: Selling force – price is falling with volume.
Magnitude: Larger values = stronger force behind the move.
Zero-line crossover: Shift from buying to selling force.
Divergence: Price makes new high but Force Index is declining = momentum weakening.
- Trading rules:
Buy when Force Index crosses above zero in an uptrend (pullback entry).
Sell when Force Index crosses below zero in a downtrend.
Use short period (2) for entries, longer period (13) for trend confirmation.
- nvi(close, volume)[source]¶
Negative Volume Index (NVI).
NVI focuses on days when volume decreases; the assumption is that smart money is active on low-volume days.
- Interpretation:
Rising NVI: Smart money is buying on quiet days.
Falling NVI: Smart money is selling on quiet days.
NVI above its 255-day MA: Bull market (historically correct ~96% of the time according to Norman Fosback).
NVI below its 255-day MA: Bear market.
Compare with PVI to distinguish smart vs. crowd behavior.
- pvi(close, volume)[source]¶
Positive Volume Index (PVI).
PVI focuses on days when volume increases; the assumption is that the crowd follows price on high-volume days.
- Interpretation:
Rising PVI: The crowd is buying on high-volume days.
Falling PVI: The crowd is selling on high-volume days.
PVI below its 255-day MA: Bearish sign (the crowd is pushing prices down on active days).
PVI tends to track what the public/retail traders are doing; NVI tracks institutional/smart money behavior.
- vpt(close, volume)[source]¶
Volume Price Trend (VPT).
VPT = cumsum(volume * pct_change(close))- Interpretation:
Rising VPT: Volume is confirming the price trend (bullish).
Falling VPT: Volume is working against the price trend.
Divergence: Price rises but VPT falls = distribution.
- adosc(high, low, close, volume, fast=3, slow=10)[source]¶
Accumulation/Distribution Oscillator (Chaikin Oscillator).
ADOSC = EMA(AD, fast) - EMA(AD, slow)- Interpretation:
Positive: Short-term accumulation exceeds long-term = money is flowing in.
Negative: Short-term distribution exceeds long-term = money is flowing out.
Zero-line crossover: Buy when crossing above zero, sell when crossing below.
Divergence: Price makes new high but oscillator falls = distribution.
- adx(high, low, close, period=14)[source]¶
Average Directional Index (ADX) with +DI and -DI.
Measures the strength of a trend regardless of its direction. +DI and -DI show the direction.
- Interpretation:
ADX > 25: Trend is strong enough to trade.
ADX > 50: Very strong trend.
ADX < 20: No trend (range-bound / choppy).
ADX rising: Trend is strengthening.
ADX falling: Trend is weakening (not necessarily reversing).
+DI > -DI: Uptrend (buyers dominate).
-DI > +DI: Downtrend (sellers dominate).
+DI/-DI crossover: Trend direction change.
- Trading rules:
Buy when +DI crosses above -DI AND ADX > 25 (trending up).
Sell when -DI crosses above +DI AND ADX > 25 (trending down).
Avoid trading when ADX < 20 (no trend).
Use ADX as a filter: only apply trend-following strategies when ADX > 25; use mean-reversion when ADX < 20.
- aroon(high, low, period=25)[source]¶
Aroon Indicator.
Measures the time since the last high/low to identify trend direction and strength.
- Interpretation:
Aroon Up > 70: Strong uptrend (recent new highs).
Aroon Down > 70: Strong downtrend (recent new lows).
Aroon Up < 30: Weak uptrend (no recent new highs).
Aroon Down < 30: Weak downtrend (no recent new lows).
Aroon Up crosses above Aroon Down: New uptrend starting.
Aroon Down crosses above Aroon Up: New downtrend starting.
Both below 50: Consolidation / no trend.
Oscillator: Positive = uptrend, negative = downtrend.
- Trading rules:
Buy when Aroon Up crosses above Aroon Down.
Sell when Aroon Down crosses above Aroon Up.
Strongest signal when one Aroon is above 70 and the other is below 30.
- psar(high, low, close, af_start=0.02, af_step=0.02, af_max=0.2)[source]¶
Parabolic SAR (Stop and Reverse).
A trend-following indicator that provides entry/exit points and trailing stop levels.
- Interpretation:
SAR dots below price: Uptrend. The SAR value is a trailing stop / support level.
SAR dots above price: Downtrend. The SAR value is a trailing stop / resistance level.
SAR flip (below to above): Sell signal – trend has reversed from bullish to bearish.
SAR flip (above to below): Buy signal – trend has reversed from bearish to bullish.
- Trading rules:
Buy when SAR flips below price (dots move under candles).
Sell when SAR flips above price (dots move above candles).
Use SAR values as trailing stop-loss levels.
Works best in trending markets; generates many false signals in ranging markets. Combine with ADX to filter.
- Parameters:
- Returns:
Parabolic SAR values. Values above price indicate downtrend; values below price indicate uptrend.
- Return type:
- vortex(high, low, close, period=14)[source]¶
Vortex Indicator.
Captures positive and negative trend movement by comparing the distance between current high/low and previous low/high.
- Interpretation:
+VI > -VI: Uptrend – positive vortex movement dominates.
-VI > +VI: Downtrend – negative vortex movement dominates.
+VI crosses above -VI: Bullish trend change signal.
-VI crosses above +VI: Bearish trend change signal.
Both near 1.0: Trend is neutral or transitioning.
- Trading rules:
Buy when +VI crosses above -VI.
Sell when -VI crosses above +VI.
Use a threshold (e.g. 1.1) to filter weak crossovers.
- trix(data, period=15)[source]¶
TRIX – Triple-smoothed EMA rate of change.
TRIX = 100 * ROC(EMA(EMA(EMA(data))))Filters out insignificant price movements by triple-smoothing before computing the rate of change.
- Interpretation:
Above zero: Bullish momentum (triple-smoothed trend up).
Below zero: Bearish momentum.
Zero-line crossover: Buy signal when crossing above zero; sell when crossing below.
Signal line: Often paired with a signal EMA for crossover signals.
Extremely smooth; good for identifying major trend changes but lags significantly.
- Trading rules:
Buy when TRIX crosses above zero.
Sell when TRIX crosses below zero.
Use for long-term trend identification, not short-term timing.
- linear_regression(data, period=14)[source]¶
Rolling Linear Regression.
Fits an OLS regression line to the last period values at each bar.
- Interpretation:
Positive slope: Price trend is up over the window.
Negative slope: Price trend is down.
R-squared near 1: Price is moving in a clean line (strong trend with low noise).
R-squared near 0: No linear trend (choppy/range-bound).
Value: The regression endpoint acts as a smoothed trend line with less lag than a moving average.
- Trading rules:
Trade in the direction of the slope when R-squared > 0.5.
Avoid trend-following trades when R-squared < 0.2.
- linear_regression_slope(data, period=14)[source]¶
Rolling Linear Regression Slope.
A convenience wrapper around
linear_regression()that returns only the slope component.- Interpretation:
Positive: Uptrend over the look-back period.
Negative: Downtrend.
Magnitude: Steeper slope = stronger trend.
Zero crossover: Trend direction change.
- zigzag(close, pct_change=5.0)[source]¶
ZigZag indicator – connects swing highs and lows.
Identifies pivots where price reverses by at least pct_change percent, then linearly interpolates between them.
- Interpretation:
Filters out noise to reveal the true swing structure of price.
Not a trading signal: The last segment repaints as new data arrives. Use only for historical analysis.
Swing counting: Measure the distance between pivots for wave analysis (Elliott Wave, harmonic patterns).
Support/resistance: Pivot levels often act as future S/R.
Useful for backtesting swing-trading strategies.
- Parameters:
- Returns:
ZigZag line (interpolated between pivots). Non-pivot bars are filled via linear interpolation; leading/trailing NaNs remain where no pivot has been established.
- Return type:
Example
>>> import pandas as pd >>> zz = zigzag(pd.Series([100, 110, 105, 95, 100, 90]), pct_change=5.0)
- heikin_ashi(open_, high, low, close)[source]¶
Heikin-Ashi modified OHLC candles.
Modified candlesticks that smooth price action and make trends easier to identify.
- Interpretation:
Green HA candles with no lower shadow: Strong uptrend.
Red HA candles with no upper shadow: Strong downtrend.
Small body with long shadows: Indecision / potential reversal.
Color change: Possible trend reversal (green to red or vice versa).
HA candles smooth noise, making it easier to stay in trends and avoid premature exits.
- Trading rules:
Stay long as long as HA candles remain green.
Stay short as long as HA candles remain red.
Watch for doji-like HA candles as reversal warnings.
Note: HA prices are synthetic – do not use them for actual order placement. Use regular prices for entries/exits.
- Parameters:
- Returns:
ha_open,ha_high,ha_low,ha_close.- Return type:
Example
>>> import pandas as pd >>> ha = heikin_ashi( ... pd.Series([100, 102]), ... pd.Series([105, 106]), ... pd.Series([99, 101]), ... pd.Series([104, 103]), ... )
- mcginley_dynamic(data, period=14)[source]¶
McGinley Dynamic – adaptive moving average.
Adjusts its speed based on market velocity, reducing whipsaws compared to a standard EMA.
- Interpretation:
Price above McGinley: Bullish trend.
Price below McGinley: Bearish trend.
Automatically speeds up in fast markets and slows down in slow markets, reducing false crossovers.
Acts as a more reliable dynamic support/resistance than traditional moving averages.
MD_t = MD_{t-1} + (price - MD_{t-1}) / (N * (price / MD_{t-1})^4)- Parameters:
- Returns:
McGinley Dynamic values.
- Return type:
Example
>>> import pandas as pd >>> md = mcginley_dynamic(pd.Series([100, 102, 101, 103, 105]))
- schaff_trend_cycle(close, period=10, fast=23, slow=50)[source]¶
Schaff Trend Cycle – MACD passed through a double stochastic.
Combines the MACD histogram with stochastic smoothing for a faster, smoother oscillator bounded between 0 and 100.
- Interpretation:
> 75: Bullish trend established.
< 25: Bearish trend established.
Crosses above 25: Buy signal (bearish-to-bullish shift).
Crosses below 75: Sell signal (bullish-to-bearish shift).
Faster than MACD because of the double stochastic processing.
Spends most of its time at extremes (near 0 or 100), with quick transitions between them.
- Parameters:
- Returns:
STC values in [0, 100].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> stc = schaff_trend_cycle(pd.Series(np.random.randn(100).cumsum() + 100))
- guppy_mma(data)[source]¶
Guppy Multiple Moving Average (GMMA).
Developed by Daryl Guppy. Uses two groups of EMAs to visualize the behavior of both traders (short-term) and investors (long-term).
- Interpretation:
Short-term group fanning out above long-term group: Strong uptrend with trader and investor agreement.
Short-term group fanning out below long-term group: Strong downtrend.
Groups compressing: Trend weakening, consolidation.
Short-term crossing long-term: Trend change signal.
Wide separation between groups: Strong conviction.
Groups intertwined/overlapping: Indecision, no trend.
Returns two groups of EMAs:
Short-term group: 3, 5, 8, 10, 12, 15
Long-term group: 30, 35, 40, 45, 50, 60
- Parameters:
data (
Series) – Price series (typically close).- Returns:
Keys
short_3,short_5, …,short_15,long_30,long_35, …,long_60.- Return type:
Example
>>> import pandas as pd, numpy as np >>> g = guppy_mma(pd.Series(np.random.randn(100).cumsum() + 100))
- rainbow_ma(data, period=10, levels=10)[source]¶
Rainbow Moving Average – recursive SMAs.
Each level is an SMA of the previous level. Level 1 is SMA of data, level 2 is SMA of level 1, and so on.
- Interpretation:
Price above all bands: Strong uptrend.
Price below all bands: Strong downtrend.
Bands fanning out: Trend strengthening.
Bands compressing: Trend weakening or consolidation.
Price touching inner bands then bouncing: Pullback buy/sell opportunity in the direction of the trend.
- Parameters:
- Returns:
Keys
sma_1throughsma_{levels}.- Return type:
Example
>>> import pandas as pd, numpy as np >>> rb = rainbow_ma(pd.Series(np.random.randn(100).cumsum() + 100))
- hull_ma(data, period=16)[source]¶
Hull Moving Average (HMA).
HMA = WMA(2 * WMA(n/2) - WMA(n), sqrt(n))Provides a fast, smooth moving average with reduced lag.
- Interpretation:
Price above HMA: Bullish.
Price below HMA: Bearish.
HMA slope change: Early trend change signal. HMA turning up = bullish; turning down = bearish.
Extremely responsive due to the sqrt(n) final smoothing; excellent for short-term trend following.
- Parameters:
- Returns:
Hull Moving Average values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> hma = hull_ma(pd.Series(np.random.randn(100).cumsum() + 100), period=16)
- zero_lag_ema(data, period=21)[source]¶
Zero-Lag Exponential Moving Average (ZLEMA).
Compensates for inherent EMA lag by applying the EMA to a de-lagged series:
zlema_input = data + (data - data.shift(lag))wherelag = (period - 1) / 2.- Interpretation:
Same signals as EMA but with near-zero lag.
More responsive to recent price changes, giving earlier crossover signals.
Can overshoot in choppy markets due to the de-lagging adjustment.
- Parameters:
- Returns:
ZLEMA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> zl = zero_lag_ema(pd.Series(np.random.randn(100).cumsum() + 100))
- vidya(data, period=14, smooth=5)[source]¶
Variable Index Dynamic Average (VIDYA).
Uses the Chande Momentum Oscillator (CMO) as a volatility index to dynamically adjust the smoothing constant of an EMA.
- Interpretation:
Behaves like a fast EMA in trending markets (high CMO) and a slow EMA in ranging markets (low CMO).
Price above VIDYA: Bullish trend.
Price below VIDYA: Bearish trend.
Less prone to whipsaws than standard EMA in choppy markets.
VIDYA_t = alpha * |CMO_t| * price_t + (1 - alpha * |CMO_t|) * VIDYA_{t-1}- Parameters:
- Returns:
VIDYA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> v = vidya(pd.Series(np.random.randn(100).cumsum() + 100))
- tilson_t3(data, period=5, volume_factor=0.7)[source]¶
Tilson T3 – triple-smoothed exponential moving average.
Applies six sequential EMAs with Tilson coefficients derived from the volume_factor to produce an ultra-smooth, low-lag average.
- Interpretation:
Extremely smooth trend line with less lag than TEMA.
Price above T3: Bullish.
Price below T3: Bearish.
T3 slope change: Very reliable trend change signal due to the heavy smoothing.
Best for medium to long-term trend identification.
- Parameters:
- Returns:
Tilson T3 values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> t3 = tilson_t3(pd.Series(np.random.randn(100).cumsum() + 100))
- fractal_adaptive_ma(data, period=16)[source]¶
Fractal Adaptive Moving Average (FRAMA).
Uses the fractal dimension of the price series to dynamically adjust the EMA smoothing factor. More responsive in trending markets and slower during consolidation.
- Interpretation:
Price above FRAMA: Bullish trend.
Price below FRAMA: Bearish trend.
Adapts automatically: becomes responsive (like short EMA) in trending markets and sluggish (like long EMA) during consolidation.
Excellent for trend following with automatic noise filtering.
- Parameters:
- Returns:
FRAMA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> f = fractal_adaptive_ma(pd.Series(np.random.randn(200).cumsum() + 100))
- candle_body_size(open_, close)[source]¶
Absolute candle body size.
Computed as
abs(close - open).- Interpretation:
Large body: Strong conviction – one side dominated.
Small body: Indecision or low activity.
Compare to average body size to identify unusual bars.
- Parameters:
- Return type:
- Returns:
Absolute body size for each bar.
Example
>>> body = candle_body_size(open_, close)
- candle_range(high, low)[source]¶
Full candle range (high minus low).
- Parameters:
- Return type:
- Returns:
Range for each bar.
Example
>>> rng = candle_range(high, low)
- upper_shadow_ratio(open_, high, low, close)[source]¶
Upper shadow as a fraction of the total candle range.
upper_shadow / (high - low)- Parameters:
- Return type:
- Returns:
Upper shadow ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = upper_shadow_ratio(open_, high, low, close)
- lower_shadow_ratio(open_, high, low, close)[source]¶
Lower shadow as a fraction of the total candle range.
lower_shadow / (high - low)- Parameters:
- Return type:
- Returns:
Lower shadow ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = lower_shadow_ratio(open_, high, low, close)
- body_to_range_ratio(open_, high, low, close)[source]¶
Body size as a fraction of the total candle range.
abs(close - open) / (high - low)- Parameters:
- Return type:
- Returns:
Body-to-range ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = body_to_range_ratio(open_, high, low, close)
- candle_direction(open_, close, doji_threshold=0.0)[source]¶
Candle direction: 1 (bullish), -1 (bearish), 0 (doji).
- Parameters:
- Return type:
- Returns:
Direction for each bar.
Example
>>> direction = candle_direction(open_, close)
- average_candle_body(open_, close, period=14)[source]¶
Simple moving average of absolute body sizes.
- Parameters:
- Return type:
- Returns:
Smoothed average body size.
Example
>>> avg_body = average_candle_body(open_, close, period=14)
- candle_momentum(open_, close, period=5)[source]¶
Sum of (close - open) over the last period bars.
A positive value indicates net bullish momentum; negative indicates bearish momentum.
- Parameters:
- Return type:
- Returns:
Cumulative close-minus-open over the window.
Example
>>> mom = candle_momentum(open_, close, period=5)
- body_gap(open_, close)[source]¶
Gap between consecutive candle bodies.
Computed as
open[i] - close[i-1], i.e. the distance between the current open and the previous close.- Parameters:
- Return type:
- Returns:
Body gap for each bar (positive = gap up, negative = gap down).
Example
>>> gap = body_gap(open_, close)
- inside_bar(high, low)[source]¶
Detect inside bars.
An inside bar has a high below the previous high and a low above the previous low (the bar is fully contained within the prior bar’s range).
- Interpretation:
Consolidation / contraction – the market is building energy.
Breakout of the inside bar’s range often leads to a significant directional move.
Multiple consecutive inside bars = stronger breakout.
- Trading rules:
Place a buy stop above the inside bar’s high and a sell stop below its low. Trade whichever triggers first.
- Parameters:
- Return type:
- Returns:
Boolean series (True where an inside bar is detected).
Example
>>> ib = inside_bar(high, low)
- outside_bar(high, low)[source]¶
Detect outside bars (engulfing range).
An outside bar has a high above the previous high and a low below the previous low (the bar’s range completely engulfs the prior bar’s range).
- Interpretation:
High volatility bar showing a battle between buyers and sellers.
The close direction determines who won: close near the high = bullish; close near the low = bearish.
Often marks a reversal or a volatility breakout.
- Parameters:
- Return type:
- Returns:
Boolean series (True where an outside bar is detected).
Example
>>> ob = outside_bar(high, low)
- pin_bar(open_, high, low, close, shadow_ratio=2.0)[source]¶
Detect pin bars.
A pin bar has a long shadow that is at least shadow_ratio times the body size. A bullish pin bar (1) has a long lower shadow; a bearish pin bar (-1) has a long upper shadow.
- Interpretation:
Bullish pin bar (1): Long lower shadow shows strong rejection of lower prices. Buy signal at support.
Bearish pin bar (-1): Long upper shadow shows strong rejection of higher prices. Sell signal at resistance.
One of the most widely used price action signals.
Most reliable at key support/resistance levels or after a sustained trend.
- Parameters:
- Return type:
- Returns:
1 (bullish pin bar), -1 (bearish pin bar), or 0.
Example
>>> pb = pin_bar(open_, high, low, close)
- doji(open_, high, low, close, threshold=0.05)[source]¶
Doji pattern.
A Doji occurs when the body is very small relative to the total range.
- Interpretation:
Indecision: Open and close are nearly equal – buyers and sellers are in balance.
At resistance: Bearish signal (potential reversal down).
At support: Bullish signal (potential reversal up).
In a trend: Warning that momentum may be fading.
Requires confirmation from the next candle – a doji alone is not a signal.
Reliability: Moderate. More significant after a strong trend.
- Parameters:
- Returns:
1 where a Doji is detected, 0 otherwise.
- Return type:
- hammer(open_, high, low, close)[source]¶
Hammer and Hanging Man pattern.
A hammer has a small body near the top and a long lower shadow (at least 2x the body). Returns 1 for bullish hammer (after a downtrend proxy: prior close < close), -1 for hanging man (after an uptrend proxy: prior close > close), 0 otherwise.
- Interpretation:
Hammer (1): Bullish reversal after a downtrend. Sellers pushed price down during the session but buyers fought back, closing near the open. The long lower shadow shows rejected selling pressure.
Hanging Man (-1): Bearish warning after an uptrend. Same shape, but context differs – suggests sellers are emerging.
Confirmation needed: Wait for the next candle to close in the reversal direction.
Reliability: High for hammers at major support levels.
- engulfing(open_, high, low, close)[source]¶
Bullish/Bearish Engulfing pattern.
- Interpretation:
Bullish engulfing (1): A small bearish candle completely engulfed by a larger bullish candle. Strong reversal signal at the bottom of a downtrend.
Bearish engulfing (-1): A small bullish candle completely engulfed by a larger bearish candle. Strong reversal signal at the top of an uptrend.
Volume confirmation strengthens the signal.
Reliability: High – one of the most reliable reversal patterns, especially at key support/resistance levels.
- morning_star(open_, high, low, close)[source]¶
Morning Star (3-candle bullish reversal).
- Interpretation:
A strong bullish reversal signal at the bottom of a downtrend.
Day 1 (large bearish): Bears are in control.
Day 2 (small body / doji): Indecision – selling pressure is exhausting.
Day 3 (large bullish): Bulls take over, closing above the midpoint of Day 1’s body.
Reliability: High – three-candle confirmation reduces false signals. Best at established support levels.
- evening_star(open_, high, low, close)[source]¶
Evening Star (3-candle bearish reversal).
- Interpretation:
The bearish counterpart of the Morning Star.
Day 1 (large bullish): Bulls are in control.
Day 2 (small body / doji): Indecision at the top.
Day 3 (large bearish): Bears take over.
Reliability: High – the mirror of Morning Star. Best at established resistance levels.
- three_white_soldiers(open_, high, low, close)[source]¶
Three White Soldiers (strong bullish continuation).
Three consecutive bullish candles, each opening within the prior body and closing at a new high.
- Interpretation:
Strong bullish signal indicating sustained buying pressure.
Each candle should have a full body (not spinning tops).
Best after a downtrend or consolidation as a reversal signal.
Reliability: Very high – three consecutive strong closes show committed buying. Weakened if upper shadows are long (advance block pattern).
- three_black_crows(open_, high, low, close)[source]¶
Three Black Crows (strong bearish continuation).
Three consecutive bearish candles, each opening within the prior body and closing at a new low.
- Interpretation:
Strong bearish signal indicating sustained selling pressure.
The bearish counterpart of Three White Soldiers.
Reliability: Very high – three consecutive strong closes downward show committed selling.
- harami(open_, high, low, close)[source]¶
Harami pattern (bullish and bearish).
The second candle’s body is entirely contained within the first candle’s body.
- Interpretation:
Bullish harami (1): A large bearish candle followed by a small bullish candle within its body. Suggests selling pressure is weakening. Reversal may follow.
Bearish harami (-1): A large bullish candle followed by a small bearish candle within its body. Suggests buying pressure is weakening.
Reliability: Moderate – requires confirmation from the following candle. Less reliable than engulfing patterns.
- spinning_top(open_, high, low, close, body_threshold=0.3)[source]¶
Spinning Top (indecision candle).
A candle with a small body relative to its range and roughly equal upper and lower shadows.
- Interpretation:
Indecision: Neither buyers nor sellers gained control.
In an uptrend: Warns that bulls are losing momentum.
In a downtrend: Warns that bears are losing momentum.
Not a reversal signal on its own – needs confirmation.
Reliability: Low as a standalone signal; moderate when appearing at key levels after a sustained trend.
- marubozu(open_, high, low, close, threshold=0.01)[source]¶
Marubozu (full-body candle with no/tiny wicks).
- Interpretation:
Bullish marubozu (1): Opens at the low and closes at the high – buyers dominated the entire session with no pushback. Very strong bullish conviction.
Bearish marubozu (-1): Opens at the high and closes at the low – sellers dominated completely.
Reliability: High – the absence of shadows shows one side had complete control. Often marks the beginning of a new trend leg.
- Parameters:
- Returns:
1 (bullish marubozu), -1 (bearish marubozu), or 0.
- Return type:
- piercing_pattern(open_, high, low, close)[source]¶
Piercing Pattern (bullish reversal).
A two-candle pattern: Day 1 is bearish, Day 2 opens below Day 1’s low and closes above the midpoint of Day 1’s body.
- Interpretation:
Bullish reversal signal at the bottom of a downtrend.
The gap down open followed by a strong close above the midpoint shows buyers stepping in aggressively.
Reliability: Moderate-high. Stronger when the close is deeper into Day 1’s body (closer to engulfing).
- Parameters:
- Returns:
1 where a Piercing Pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = piercing_pattern(open_, high, low, close)
- dark_cloud_cover(open_, high, low, close)[source]¶
Dark Cloud Cover (bearish reversal).
The bearish counterpart of the Piercing Pattern. Day 1 is bullish, Day 2 opens above Day 1’s high and closes below the midpoint of Day 1’s body.
- Interpretation:
Bearish reversal signal at the top of an uptrend.
The gap up open followed by selling down into Day 1’s body shows sellers overpowering buyers.
Reliability: Moderate-high. More significant with high volume.
- Parameters:
- Returns:
-1 where a Dark Cloud Cover is detected, 0 otherwise.
- Return type:
Example
>>> signal = dark_cloud_cover(open_, high, low, close)
- hanging_man(open_, high, low, close, trend_period=5)[source]¶
Hanging Man (bearish reversal at top).
Same hammer shape (small body near top, long lower shadow) but appears after an uptrend, signalling potential reversal.
- Interpretation:
Bearish warning signal after an uptrend. The long lower shadow shows sellers tested lower prices during the session.
Needs confirmation: a bearish close the next day confirms the reversal.
Reliability: Moderate – requires confirmation and context.
- Parameters:
- Returns:
-1 where a Hanging Man is detected, 0 otherwise.
- Return type:
Example
>>> signal = hanging_man(open_, high, low, close)
- inverted_hammer(open_, high, low, close, trend_period=5)[source]¶
Inverted Hammer (bullish reversal at bottom).
A candle with a long upper shadow (at least 2x the body) and a small lower shadow, appearing after a downtrend.
- Interpretation:
Bullish reversal signal at the bottom of a downtrend.
The long upper shadow shows buyers tested higher prices but could not hold them yet. If confirmed by next bar, buyers are gaining strength.
Reliability: Moderate – requires a bullish confirmation candle the next day.
- Parameters:
- Returns:
1 where an Inverted Hammer is detected, 0 otherwise.
- Return type:
Example
>>> signal = inverted_hammer(open_, high, low, close)
- shooting_star(open_, high, low, close, trend_period=5)[source]¶
Shooting Star (bearish reversal at top).
Same shape as inverted hammer (long upper shadow, small lower shadow) but appears after an uptrend.
- Interpretation:
Bearish reversal signal at the top of an uptrend. Buyers pushed price higher but sellers overwhelmed them, closing near the open.
The long upper shadow represents rejected higher prices.
Reliability: Moderate-high – stronger when it appears at resistance with high volume.
- Parameters:
- Returns:
-1 where a Shooting Star is detected, 0 otherwise.
- Return type:
Example
>>> signal = shooting_star(open_, high, low, close)
- tweezer_top(open_, high, low, close, tolerance=0.001)[source]¶
Tweezer Top (bearish reversal).
Two consecutive candles with nearly the same highs. The first candle is bullish and the second is bearish.
- Interpretation:
Bearish reversal at a resistance level. Both candles tested the same high and were rejected, showing strong resistance.
Reliability: Moderate – stronger at established resistance.
- Parameters:
- Returns:
-1 where a Tweezer Top is detected, 0 otherwise.
- Return type:
Example
>>> signal = tweezer_top(open_, high, low, close)
- tweezer_bottom(open_, high, low, close, tolerance=0.001)[source]¶
Tweezer Bottom (bullish reversal).
Two consecutive candles with nearly the same lows. The first candle is bearish and the second is bullish.
- Interpretation:
Bullish reversal at a support level. Both candles tested the same low and were rejected, showing strong support.
Reliability: Moderate – stronger at established support.
- Parameters:
- Returns:
1 where a Tweezer Bottom is detected, 0 otherwise.
- Return type:
Example
>>> signal = tweezer_bottom(open_, high, low, close)
- three_inside_up(open_, high, low, close)[source]¶
Three Inside Up (bullish reversal).
A three-candle pattern: bullish harami (Days 1-2) confirmed by a third bullish candle closing above Day 1’s open.
- Interpretation:
A confirmed bullish harami. The third candle provides the confirmation that a simple harami lacks.
Reliability: High – three-candle confirmation is stronger than the two-candle harami alone.
- Parameters:
- Returns:
1 where the pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = three_inside_up(open_, high, low, close)
- three_inside_down(open_, high, low, close)[source]¶
Three Inside Down (bearish reversal).
A three-candle pattern: bearish harami (Days 1-2) confirmed by a third bearish candle closing below Day 1’s open.
- Interpretation:
A confirmed bearish harami. The bearish counterpart of Three Inside Up.
Reliability: High – three-candle confirmation pattern.
- Parameters:
- Returns:
-1 where the pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = three_inside_down(open_, high, low, close)
- abandoned_baby(open_, high, low, close)[source]¶
Abandoned Baby (reversal pattern).
A rare three-candle pattern with gaps. A Doji star gaps away from the first candle and the third candle gaps in the opposite direction.
- Interpretation:
Bullish abandoned baby (1): Bearish candle, gap-down doji, gap-up bullish candle. Very strong reversal signal.
Bearish abandoned baby (-1): Bullish candle, gap-up doji, gap-down bearish candle. Very strong reversal signal.
Reliability: Very high – but extremely rare. The gap isolation of the doji shows a dramatic sentiment shift.
- Parameters:
- Returns:
1 (bullish abandoned baby), -1 (bearish abandoned baby), or 0.
- Return type:
Example
>>> signal = abandoned_baby(open_, high, low, close)
- kicking(open_, high, low, close, threshold=0.01)[source]¶
Kicking pattern.
Two consecutive marubozu candles in opposite directions with a gap between them. One of the strongest reversal signals.
- Interpretation:
Bullish kicking (1): Bearish marubozu followed by a gap-up bullish marubozu. Extremely strong reversal.
Bearish kicking (-1): Bullish marubozu followed by a gap-down bearish marubozu.
Reliability: Very high – one of the most powerful candlestick patterns. The opposing full-body candles with a gap show a complete and sudden sentiment reversal.
- Parameters:
- Returns:
1 (bullish kicking), -1 (bearish kicking), or 0.
- Return type:
Example
>>> signal = kicking(open_, high, low, close)
- belt_hold(open_, high, low, close, threshold=0.01)[source]¶
Belt Hold pattern.
A long marubozu candle that opens with a gap in the direction of the prior trend. A bullish belt hold gaps down and opens at the low; a bearish belt hold gaps up and opens at the high.
- Interpretation:
Bullish belt hold (1): Gaps down then rallies all day closing near the high – strong rejection of lower prices.
Bearish belt hold (-1): Gaps up then sells off all day.
Reliability: Moderate – the gap adds significance.
- Parameters:
- Returns:
1 (bullish belt hold), -1 (bearish belt hold), or 0.
- Return type:
Example
>>> signal = belt_hold(open_, high, low, close)
- rising_three_methods(open_, high, low, close)[source]¶
Rising Three Methods (bullish continuation).
A five-candle pattern: a long bullish candle, followed by three small bearish candles that stay within the first candle’s range, then a final long bullish candle that closes above the first candle’s close.
- Interpretation:
Bullish continuation pattern – the three small bearish candles are a rest/consolidation within the uptrend.
The final bullish candle confirms the trend resumes.
Reliability: High – five-candle confirmation shows clear trend continuation with a healthy pullback.
- Parameters:
- Return type:
- Returns:
1 where the pattern is detected, 0 otherwise.
Example
>>> signal = rising_three_methods(open_, high, low, close)
- falling_three_methods(open_, high, low, close)[source]¶
Falling Three Methods (bearish continuation).
The bearish counterpart of Rising Three Methods. A long bearish candle, three small bullish candles inside its range, then a final long bearish candle that closes below the first candle’s close.
- Interpretation:
Bearish continuation pattern – the small bullish candles are a brief consolidation within the downtrend.
Reliability: High – mirror of Rising Three Methods.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = falling_three_methods(open_, high, low, close)
- tasuki_gap(open_, high, low, close)[source]¶
Upside/Downside Tasuki Gap.
Upside (1): two bullish candles with a gap up, followed by a bearish candle that opens within the second body and closes within the gap but does not fill it.
Downside (-1): two bearish candles with a gap down, followed by a bullish candle that opens within the second body and closes within the gap but does not fill it.
- Parameters:
- Return type:
- Returns:
1 (upside Tasuki gap), -1 (downside Tasuki gap), or 0.
Example
>>> signal = tasuki_gap(open_, high, low, close)
- on_neck(open_, high, low, close, tolerance=0.001)[source]¶
On Neck pattern (bearish continuation).
A bearish candle followed by a small bullish candle that opens below the previous low and closes at or near the previous low.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = on_neck(open_, high, low, close)
- in_neck(open_, high, low, close, tolerance=0.003)[source]¶
In Neck pattern (slight bullish variant of On Neck).
A bearish candle followed by a small bullish candle that opens below the previous low and closes slightly above (but near) the previous close.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = in_neck(open_, high, low, close)
- thrusting(open_, high, low, close)[source]¶
Thrusting pattern (moderate bearish continuation).
A bearish candle followed by a bullish candle that opens below the previous low and closes above the previous close but below the midpoint of the previous body.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = thrusting(open_, high, low, close)
- separating_lines(open_, high, low, close, tolerance=0.001)[source]¶
Bullish/Bearish Separating Lines.
Bullish (1): a bearish candle followed by a bullish candle that opens at the same level as the previous open.
Bearish (-1): a bullish candle followed by a bearish candle that opens at the same level as the previous open.
- Parameters:
- Return type:
- Returns:
1 (bullish), -1 (bearish), or 0.
Example
>>> signal = separating_lines(open_, high, low, close)
- closing_marubozu(open_, high, low, close, threshold=0.01)[source]¶
Closing Marubozu — no shadow on the closing side only.
A bullish closing marubozu (1) has no upper shadow (close == high) but may have a lower shadow. A bearish closing marubozu (-1) has no lower shadow (close == low) but may have an upper shadow.
- Parameters:
- Return type:
- Returns:
1 (bullish), -1 (bearish), or 0.
Example
>>> signal = closing_marubozu(open_, high, low, close)
- rickshaw_man(open_, high, low, close, body_threshold=0.05, shadow_threshold=0.3)[source]¶
Rickshaw Man – a Doji with very long shadows and tiny body near center.
- Interpretation:
Extreme indecision: price moved significantly in both directions but closed near the open at the midpoint.
Suggests the market is at a critical juncture.
Reliability: Moderate – needs context and confirmation.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = rickshaw_man(open_, high, low, close)
- long_legged_doji(open_, high, low, close, body_threshold=0.05, shadow_threshold=0.3)[source]¶
Long Legged Doji – Doji with unusually long upper and lower shadows.
- Interpretation:
Strong indecision with high volatility. Both buyers and sellers were active but neither won.
More significant than a standard doji due to the wide range.
Reliability: Moderate – similar to Rickshaw Man.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = long_legged_doji(open_, high, low, close)
- dragonfly_doji(open_, high, low, close, body_threshold=0.05, upper_threshold=0.05, lower_min=0.3)[source]¶
Dragonfly Doji – Doji with a long lower shadow and no upper shadow.
- Interpretation:
Bullish signal, especially after a downtrend. Sellers pushed price down but buyers brought it all the way back to the open.
The long lower shadow shows strong rejection of lower prices.
Reliability: Moderate-high at support levels.
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_threshold (
float, default:0.05) – Maximum body-to-range ratio.upper_threshold (
float, default:0.05) – Maximum upper-shadow-to-range ratio.lower_min (
float, default:0.3) – Minimum lower-shadow-to-range ratio.open_ (Series)
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = dragonfly_doji(open_, high, low, close)
- gravestone_doji(open_, high, low, close, body_threshold=0.05, lower_threshold=0.05, upper_min=0.3)[source]¶
Gravestone Doji – Doji with a long upper shadow and no lower shadow.
- Interpretation:
Bearish signal, especially after an uptrend. Buyers pushed price up but sellers brought it all the way back to the open.
The long upper shadow shows strong rejection of higher prices.
Reliability: Moderate-high at resistance levels.
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_threshold (
float, default:0.05) – Maximum body-to-range ratio.lower_threshold (
float, default:0.05) – Maximum lower-shadow-to-range ratio.upper_min (
float, default:0.3) – Minimum upper-shadow-to-range ratio.open_ (Series)
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = gravestone_doji(open_, high, low, close)
- tri_star(open_, high, low, close, doji_threshold=0.05)[source]¶
Tri-Star pattern – three consecutive dojis with gaps.
- Interpretation:
Bullish tri-star (1): Three dojis where the middle gaps below. Very rare, strong reversal at bottoms.
Bearish tri-star (-1): Three dojis where the middle gaps above. Very rare, strong reversal at tops.
Reliability: Very high when it occurs, but extremely rare.
Bullish (1): three dojis where the middle gaps below the other two. Bearish (-1): three dojis where the middle gaps above the other two.
- Parameters:
- Return type:
- Returns:
1 (bullish tri-star), -1 (bearish tri-star), or 0.
Example
>>> signal = tri_star(open_, high, low, close)
- unique_three_river(open_, high, low, close)[source]¶
Unique Three River Bottom (rare bullish reversal).
Day 1: long bearish candle. Day 2: harami-like bearish candle with a lower low. Day 3: small bullish candle that closes below Day 2’s close.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = unique_three_river(open_, high, low, close)
- concealing_baby_swallow(open_, high, low, close, marubozu_threshold=0.02)[source]¶
Concealing Baby Swallow (four-candle bearish pattern).
Four bearish candles: Days 1-2 are bearish marubozus. Day 3 gaps down, has a long upper shadow into Day 2’s body. Day 4 engulfs Day 3 including the shadow.
- Parameters:
- Return type:
- Returns:
-1 where detected, 0 otherwise.
Example
>>> signal = concealing_baby_swallow(open_, high, low, close)
- higher_highs_lows(high, low, period=5)[source]¶
Detect sequences of HH/HL (uptrend) or LH/LL (downtrend).
Compares rolling highest-high and lowest-low over period bars to determine whether the market is making higher highs & higher lows (uptrend = 1), lower highs & lower lows (downtrend = -1), or neither (0).
- Interpretation:
1 (HH+HL): Classic uptrend structure. Price is making higher highs and higher lows – the textbook definition of an uptrend.
-1 (LH+LL): Classic downtrend structure.
0: No clear trend – consolidation, range, or transition.
When the sequence breaks (e.g. first lower low in an uptrend), it is an early warning of potential trend reversal.
- Parameters:
- Return type:
- Returns:
1 (uptrend / HH+HL), -1 (downtrend / LH+LL), or 0.
Example
>>> trend = higher_highs_lows(high, low, period=5)
- swing_high(high, lookback=2, lookahead=2)[source]¶
Detect swing highs.
A swing high is a bar whose high is greater than the highs of the lookback bars before it and the lookahead bars after it.
- Parameters:
- Return type:
- Returns:
Boolean series (True at swing highs).
Example
>>> sh = swing_high(high, lookback=2, lookahead=2)
- swing_low(low, lookback=2, lookahead=2)[source]¶
Detect swing lows.
A swing low is a bar whose low is less than the lows of the lookback bars before it and the lookahead bars after it.
- Parameters:
- Return type:
- Returns:
Boolean series (True at swing lows).
Example
>>> sl = swing_low(low, lookback=2, lookahead=2)
- trend_bars(close)[source]¶
Count consecutive up/down bars.
Returns a running count: positive values for consecutive up bars (close > previous close), negative values for consecutive down bars (close < previous close). The count resets to 0 on a flat bar.
- Parameters:
close (
Series) – Close prices.- Return type:
- Returns:
Consecutive bar count (positive = up streak, negative = down streak).
Example
>>> streaks = trend_bars(close)
- gap_analysis(open_, high, low, close, avg_range_period=20, breakaway_threshold=1.5)[source]¶
Detect and classify gaps.
Gaps are classified as:
common — gap size is below the average range
breakaway — gap size exceeds breakaway_threshold times the average range
exhaustion — gap that occurs after a sustained move (approximated by comparing the current close to a look-back SMA)
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.avg_range_period (
int, default:20) – Period for computing the average range.breakaway_threshold (
float, default:1.5) – Multiplier of average range for breakaway classification.open_ (Series)
- Return type:
- Returns:
Dictionary with keys
gap_size,gap_direction, andgap_type.gap_size— absolute gap sizegap_direction— 1 (gap up), -1 (gap down), 0 (no gap)gap_type— categorical string ("common","breakaway","exhaustion", or""for no gap)
Example
>>> result = gap_analysis(open_, high, low, close) >>> result["gap_type"]
- range_expansion(high, low, period=14, threshold=1.5)[source]¶
Detect range expansion (current range significantly above average).
Returns True when
(high - low) > threshold * avg_range.- Parameters:
- Return type:
- Returns:
Boolean series (True where range is expanded).
Example
>>> expanded = range_expansion(high, low)
- narrow_range(high, low, period=4)[source]¶
NR4/NR7 detection — narrowest range in period bars.
Returns True when the current bar’s range is the smallest in the last period bars (including itself).
period=4gives NR4;period=7gives NR7.- Parameters:
- Return type:
- Returns:
Boolean series (True at narrow-range bars).
Example
>>> nr4 = narrow_range(high, low, period=4) >>> nr7 = narrow_range(high, low, period=7)
- wide_range_bar(high, low, period=14, threshold=1.5)[source]¶
Wide Range Bar (WRB): range > threshold * average range.
- Parameters:
- Return type:
- Returns:
Boolean series (True at wide-range bars).
Example
>>> wrb = wide_range_bar(high, low)
- key_reversal(open_, high, low, close)[source]¶
Detect key reversal bars.
A bullish key reversal (1) makes a new low (below the previous low) but closes above the previous close.
A bearish key reversal (-1) makes a new high (above the previous high) but closes below the previous close.
- Parameters:
- Return type:
- Returns:
1 (bullish key reversal), -1 (bearish key reversal), or 0.
Example
>>> kr = key_reversal(open_, high, low, close)
- pivot_reversal(open_, high, low, close)[source]¶
Detect two-bar pivot reversal patterns at swing points.
A bullish pivot reversal (1): the previous bar makes a lower low than its predecessor, and the current bar closes above the previous bar’s high.
A bearish pivot reversal (-1): the previous bar makes a higher high than its predecessor, and the current bar closes below the previous bar’s low.
- Parameters:
- Return type:
- Returns:
1 (bullish pivot reversal), -1 (bearish pivot reversal), or 0.
Example
>>> pr = pivot_reversal(open_, high, low, close)
- crossover(series1, series2)[source]¶
Detect when series1 crosses above series2.
- Interpretation:
Returns True on the exact bar where series1 moves from below-or-equal to above series2.
Common uses: MA crossovers, RSI crossing above 30, MACD crossing above signal line.
Only fires on the transition bar, not on subsequent bars where series1 remains above series2.
- crossunder(series1, series2)[source]¶
Detect when series1 crosses below series2.
- Interpretation:
Returns True on the exact bar where series1 moves from above-or-equal to below series2.
Common uses: MA death crosses, RSI crossing below 70, MACD crossing below signal line.
- rising(data, period=1)[source]¶
Detect whether data is rising (each bar higher than period bars ago).
- falling(data, period=1)[source]¶
Detect whether data is falling (each bar lower than period bars ago).
- normalize(data, period=None)[source]¶
Z-score normalization.
When period is
None, the full-series mean and standard deviation are used. When period is given, a rolling z-score is computed.
- zscore(data, period=20)[source]¶
Rolling z-score of price.
Measures how many standard deviations the current value is from the rolling mean.
- Interpretation:
> +2: Price is 2+ standard deviations above mean – statistically unusual (overbought).
< -2: Price is 2+ standard deviations below mean – statistically unusual (oversold).
Near 0: Price is near its rolling average (fair value).
Mean-reversion traders buy at z < -2 and sell at z > +2.
In trending markets, z-score can stay extreme for extended periods – use with a trend filter.
- Parameters:
- Returns:
Z-score values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> zscore(close, period=5)
- percentile_rank(data, period=20)[source]¶
Rolling percentile rank.
Computes the percentage of values within the rolling window that are less than or equal to the current value.
- Parameters:
- Returns:
Percentile rank in [0, 100].
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> percentile_rank(close, period=5)
- mean_deviation(data, period=20)[source]¶
Rolling mean absolute deviation.
Computes the average of absolute deviations from the rolling mean.
- Parameters:
- Returns:
Mean absolute deviation values (>= 0).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> mean_deviation(close, period=5)
- median(data, period=20)[source]¶
Rolling median.
- Parameters:
- Returns:
Rolling median values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> median(close, period=5)
- skewness(data, period=20)[source]¶
Rolling skewness.
Measures the asymmetry of the distribution of values within the rolling window. Uses Fisher’s definition (bias-corrected).
- Interpretation:
Positive skew: Distribution has a long right tail – occasional large positive moves (typical of call option payoffs).
Negative skew: Distribution has a long left tail – occasional large negative moves (crash risk).
Near 0: Symmetric distribution (normal-like).
Negative skew in returns is common for equities – tail risk is to the downside.
- Parameters:
- Returns:
Skewness values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> skewness(close, period=20)
- kurtosis(data, period=20)[source]¶
Rolling kurtosis (excess kurtosis, Fisher’s definition).
Measures the tailedness of the distribution of values within the rolling window. Normal distribution has excess kurtosis of 0.
- Interpretation:
> 0 (leptokurtic): Fatter tails than normal – more extreme moves than a Gaussian would predict. Common in financial returns.
< 0 (platykurtic): Thinner tails than normal – fewer extreme moves.
Near 0 (mesokurtic): Normal-like tail behavior.
High kurtosis warns of tail risk – standard VaR and Gaussian-based risk models will underestimate risk.
- Parameters:
- Returns:
Excess kurtosis values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> kurtosis(close, period=20)
- entropy(data, period=20, bins=10)[source]¶
Rolling Shannon entropy of binned price changes.
Discretises the price changes within the rolling window into bins equal-width bins and computes Shannon entropy in nats (natural log).
- Parameters:
- Returns:
Shannon entropy values (>= 0).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> entropy(close, period=20, bins=5)
- hurst_exponent(data, period=100)[source]¶
Rolling Hurst exponent via the rescaled range (R/S) method.
H < 0.5: mean-reverting
H = 0.5: random walk
H > 0.5: trending
- Parameters:
- Returns:
Hurst exponent estimates in roughly [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(200))) >>> hurst_exponent(close, period=100)
- correlation(data, other, period=20)[source]¶
Rolling Pearson correlation between two series.
- Parameters:
- Returns:
Correlation values in [-1, 1].
- Return type:
Example
>>> import pandas as pd >>> x = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=float) >>> y = pd.Series([2, 4, 6, 8, 10, 12, 14, 16, 18, 20], dtype=float) >>> correlation(x, y, period=5)
- beta(data, benchmark, period=60)[source]¶
Rolling beta (OLS slope of data returns vs benchmark returns).
- Interpretation:
Beta = 1: Asset moves in line with the benchmark.
Beta > 1: Asset is more volatile than the benchmark (amplifies market moves). E.g. beta = 1.5 means the stock moves 1.5% for every 1% benchmark move.
Beta < 1: Asset is less volatile than the benchmark.
Beta < 0: Asset moves inversely to the benchmark (rare).
Rising beta: Asset becoming more correlated/volatile relative to benchmark.
Beta is the cornerstone of CAPM and factor models.
- Parameters:
- Returns:
Beta values (unbounded).
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> stock = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> market = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> beta(stock, market, period=30)
- r_squared(data, benchmark, period=60)[source]¶
Rolling R-squared (coefficient of determination).
Computed as the square of the rolling Pearson correlation of returns.
- Parameters:
- Returns:
R-squared values in [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> stock = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> market = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> r_squared(stock, market, period=30)
- information_coefficient(data, other, period=20)[source]¶
Rolling information coefficient (Spearman rank correlation).
Measures the rolling rank correlation between two series, commonly used to evaluate forecast skill.
- Parameters:
- Returns:
IC values in [-1, 1].
- Return type:
Example
>>> import pandas as pd >>> forecast = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=float) >>> actual = pd.Series([2, 1, 4, 3, 6, 5, 8, 7, 10, 9], dtype=float) >>> information_coefficient(forecast, actual, period=5)
- hilbert_transform_dominant_period(data, min_period=6, max_period=50)[source]¶
Dominant cycle period via Hilbert Transform.
Uses Ehlers’ Hilbert Transform Discriminator to estimate the dominant cycle period of the price series.
- Interpretation:
Output is the estimated cycle length in bars (e.g. 20 means the dominant cycle repeats every 20 bars).
Use this to adaptively set indicator periods: instead of a fixed 14-period RSI, use the dominant period.
Short period (< 10): Fast cycling market.
Long period (> 30): Slow cycling or trending market.
Stable readings = well-defined cycle. Erratic readings = no clear cycle (trending or random).
- Parameters:
- Returns:
Estimated dominant cycle period in bars.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_transform_dominant_period(close)
- hilbert_transform_trend_mode(data)[source]¶
Trend vs cycle mode indicator via Hilbert Transform.
Returns +1 when the market is in trend mode and 0 when in cycle mode, based on the relationship between the dominant cycle period and a simple moving average smoothing window.
- Parameters:
data (
Series) – Price series.- Returns:
Binary series: 1 = trending, 0 = cycling.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_transform_trend_mode(close)
- hilbert_instantaneous_phase(data)[source]¶
Instantaneous trendline via Hilbert Transform.
Computes a smooth trendline by applying the dominant cycle period as an adaptive moving average length.
- Parameters:
data (
Series) – Price series.- Returns:
Instantaneous trendline values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_instantaneous_phase(close)
- sine_wave(data)[source]¶
Ehlers Sine Wave indicator.
Uses the dominant cycle period to compute the sine and lead-sine values, generating buy/sell signals on crossovers.
- Interpretation:
Sine crosses above lead_sine: Buy signal (cycle turning up).
Sine crosses below lead_sine: Sell signal (cycle turning down).
When both values are near +/-1, the market is in cycle mode.
When values are erratic or near zero, the market may be trending rather than cycling.
- Parameters:
data (
Series) – Price series.- Returns:
sineandlead_sineseries.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> result = sine_wave(close)
- even_better_sinewave(data, hp_period=40, ss_period=10)[source]¶
Ehlers Even Better Sinewave (EBSW).
Combines a high-pass filter, super-smoother, and autocorrelation to produce an oscillator that identifies the dominant cycle.
- Interpretation:
Near +1: Cycle is at or near a peak.
Near -1: Cycle is at or near a trough.
Zero crossover up: Cycle turning bullish.
Zero crossover down: Cycle turning bearish.
More reliable than the original Sine Wave indicator because it better separates cycle from trend components.
- Parameters:
- Returns:
EBSW oscillator values in approximately [-1, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> even_better_sinewave(close)
- roofing_filter(data, hp_period=48, lp_period=10)[source]¶
Ehlers Roofing Filter.
Applies a high-pass filter followed by a super-smoother low-pass filter to isolate the dominant cycle from both trend and noise.
- Interpretation:
Output oscillates around zero, showing the pure cycle component of price.
Positive: Cycle is in the up phase.
Negative: Cycle is in the down phase.
Use to identify cycle turning points without trend or noise contamination.
- Parameters:
- Returns:
Filtered cycle component.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> roofing_filter(close)
- decycler(data, hp_period=125)[source]¶
Ehlers Decycler.
Removes the cycle component from the price series, keeping only the trend. Computed as
price - highpass(price).- Interpretation:
Shows the pure trend component of price with cycles removed.
Price above decycler: Bullish trend.
Price below decycler: Bearish trend.
Extremely smooth with virtually no lag – one of the best trend-following overlays available.
- Parameters:
- Returns:
Trend-only (decycled) series.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> decycler(close)
- bandpass_filter(data, period=20, bandwidth=0.3)[source]¶
Ehlers Bandpass Filter.
Isolates the cycle component at the specified period. Returns both the bandpass filter output and a trigger signal (one-bar lag).
- Interpretation:
BP crosses above trigger: Buy signal (cycle turning up).
BP crosses below trigger: Sell signal (cycle turning down).
BP at peak: Cycle high – potential sell zone.
BP at trough: Cycle low – potential buy zone.
Only isolates the cycle at the specified period; other frequencies are filtered out.
- Parameters:
- Returns:
bp(bandpass) andtrigger(one-bar lag of bp).- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> result = bandpass_filter(close, period=20)
- squeeze_momentum(high, low, close, bb_period=20, bb_std=2.0, kc_period=20, kc_mult=1.5, mom_period=12)[source]¶
TTM Squeeze Momentum indicator.
Detects when Bollinger Bands are inside Keltner Channels (the “squeeze”) and measures momentum via a linear regression of price.
- Interpretation:
squeeze_on = 1: Bollinger Bands are inside Keltner Channels. Volatility is compressed. A breakout is imminent.
squeeze_on = 0: No squeeze. Normal volatility.
Momentum positive: Bullish momentum – breakout likely up.
Momentum negative: Bearish momentum – breakout likely down.
Momentum growing: Accelerating.
Momentum shrinking: Decelerating.
- Trading rules:
When squeeze fires (transitions from on to off), enter in the direction of momentum.
Exit when momentum starts to decelerate (histogram shrinks).
- Parameters:
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.bb_period (
int, default:20) – Bollinger Bands SMA period.bb_std (
float, default:2.0) – Bollinger Bands standard deviation multiplier.kc_period (
int, default:20) – Keltner Channel EMA period.kc_mult (
float, default:1.5) – Keltner Channel ATR multiplier.mom_period (
int, default:12) – Momentum linear regression period.
- Returns:
squeeze_on(bool: 1 = squeeze active),momentum(momentum histogram values).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> n = 100 >>> c = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> h = c + abs(np.random.randn(n) * 0.3) >>> lo = c - abs(np.random.randn(n) * 0.3) >>> result = squeeze_momentum(h, lo, c)
- anchored_vwap(close, volume, anchor_index=0)[source]¶
VWAP anchored from a specific bar index.
Computes the Volume Weighted Average Price starting from anchor_index onwards. Values before the anchor are
NaN.- Parameters:
- Returns:
Anchored VWAP values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13], dtype=float) >>> volume = pd.Series([100, 200, 150, 300, 250, 100, 200, 150, 300, 250], dtype=float) >>> anchored_vwap(close, volume, anchor_index=3)
- linear_regression_channel(data, period=100, std_dev=2.0)[source]¶
Linear regression channel with standard deviation bands.
Fits a rolling linear regression and constructs upper/lower channel lines based on the standard error.
- Parameters:
- Returns:
middle(regression value),upper,lower.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(120, dtype=float) * 0.5) >>> result = linear_regression_channel(close, period=50)
- pivot_points(high, low, close, method='standard')[source]¶
Pivot points with support and resistance levels.
Computes pivot point and two levels of support/resistance using the prior bar’s high, low, and close.
- Parameters:
- Returns:
pivot,s1,s2,r1,r2.- Return type:
Example
>>> import pandas as pd >>> h = pd.Series([12, 13, 14, 13, 12], dtype=float) >>> lo = pd.Series([10, 11, 12, 11, 10], dtype=float) >>> c = pd.Series([11, 12, 13, 12, 11], dtype=float) >>> result = pivot_points(h, lo, c)
- market_structure(high, low, lookback=5)[source]¶
Higher highs / lower lows market structure detection.
Identifies swing highs and lows using a lookback window, then labels each swing as higher-high (HH), lower-high (LH), higher-low (HL), or lower-low (LL).
- Parameters:
- Returns:
swing_high(high values at swing highs, else NaN),swing_low(low values at swing lows, else NaN),structure(1 = bullish / HH+HL, -1 = bearish / LH+LL, 0 = neutral).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = market_structure(h, lo, lookback=3)
- swing_points(high, low, lookback=5)[source]¶
Swing high and low detection.
A swing high occurs when the high is the maximum of
2 * lookback + 1bars centred on the pivot bar. Symmetrically for swing lows.- Parameters:
- Returns:
swing_high(high values at swing highs, else NaN),swing_low(low values at swing lows, else NaN).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = swing_points(h, lo, lookback=3)
- volume_weighted_macd(close, volume, fast=12, slow=26, signal=9)[source]¶
MACD weighted by volume.
Uses volume-weighted moving averages instead of standard EMAs for the fast and slow lines.
- Parameters:
- Returns:
macd,signal,histogram.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> c = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5)) >>> v = pd.Series(np.random.randint(1000, 10000, 100), dtype=float) >>> result = volume_weighted_macd(c, v)
- ehlers_fisher(high, low, period=10)[source]¶
Ehlers Fisher Transform.
Converts prices into a Gaussian normal distribution to create sharp turning points, making it easier to identify reversals.
- Parameters:
- Returns:
fisherandtrigger(one-bar lag of fisher).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5) + 1) >>> lo = h - 2 >>> result = ehlers_fisher(h, lo)
- adaptive_rsi(data, base_period=14, vol_period=10, min_period=5, max_period=50)[source]¶
RSI with an adaptive period based on volatility.
The look-back period expands in low-volatility regimes and contracts in high-volatility regimes, improving responsiveness.
- Parameters:
data (
Series) – Price series (typically close).base_period (
int, default:14) – Base RSI period.vol_period (
int, default:10) – Period for the volatility (standard deviation) calculation.min_period (
int, default:5) – Minimum allowed RSI period.max_period (
int, default:50) – Maximum allowed RSI period.
- Returns:
Adaptive RSI values in [0, 100].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(200) * 0.5)) >>> adaptive_rsi(close)
- relative_strength(data, benchmark)[source]¶
Relative strength ratio of one series to another.
Commonly used for pair analysis or sector rotation. A rising ratio indicates data is outperforming benchmark.
- Parameters:
- Returns:
Ratio of data / benchmark.
- Return type:
Example
>>> import pandas as pd >>> stock = pd.Series([100, 105, 110, 108, 112], dtype=float) >>> index = pd.Series([1000, 1010, 1005, 1015, 1020], dtype=float) >>> relative_strength(stock, index)
- linear_regression_forecast(data, period=20, forecast_bars=1)[source]¶
Rolling linear regression forecast N bars ahead.
Fits a linear regression over each rolling window and projects the value forecast_bars steps beyond the last observation in the window.
- Parameters:
- Returns:
Forecasted values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> linear_regression_forecast(close, period=20, forecast_bars=1)
- standard_error_bands(data, period=20, num_bands=3)[source]¶
Linear regression line with standard error bands.
Fits a rolling linear regression and constructs bands at +-1, +-2, and +-3 standard errors (configurable via num_bands).
- Parameters:
- Returns:
middleplusupper_Nandlower_Nfor each band level N from 1 to num_bands.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> result = standard_error_bands(close, period=20)
- r_squared_indicator(data, period=14)[source]¶
Rolling R-squared of linear regression as a trend strength measure.
An R-squared near 1.0 indicates prices are moving in a strong linear trend; near 0.0 indicates choppy, non-trending movement.
- Parameters:
- Returns:
R-squared values in [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> r_squared_indicator(close, period=14)
- polynomial_regression(data, period=20, degree=2)[source]¶
Rolling polynomial regression fitted values.
Fits a polynomial of the given degree over each rolling window and returns the fitted value at the end of the window.
- Parameters:
- Returns:
Fitted polynomial values at the end of each window.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) ** 1.5 * 0.01) >>> polynomial_regression(close, period=20, degree=2)
- raff_regression_channel(data, period=50)[source]¶
Raff regression channel using maximum deviation.
Fits a rolling linear regression and constructs channel lines using the maximum absolute deviation of any point in the window from the regression line.
- Parameters:
- Returns:
center(regression line),upper,lower.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(80, dtype=float) * 0.5) >>> result = raff_regression_channel(close, period=50)
- detrended_regression(data, period=20)[source]¶
Residuals from rolling linear regression (for mean reversion).
Fits a rolling linear regression and returns the residual (actual minus predicted) at the end of each window. Positive values indicate price above trend; negative below trend.
- Parameters:
- Returns:
Detrended (residual) values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> detrended_regression(close, period=20)
- fibonacci_retracements(swing_high, swing_low, direction='up')[source]¶
Compute Fibonacci retracement levels from a swing high/low pair.
Given a swing high and swing low, computes the standard Fibonacci retracement levels at 23.6%, 38.2%, 50%, 61.8%, and 78.6%.
- Interpretation:
23.6%: Shallow retracement – strong trend continuation likely. Common in fast-moving markets.
38.2%: Moderate retracement – healthy pullback in a strong trend.
50%: Not a Fibonacci ratio but widely watched. A 50% retracement is considered normal.
61.8%: The “golden ratio” – the most important level. If price holds here, the trend is likely to resume.
78.6%: Deep retracement – the trend is under pressure. If this level breaks, the trend may be over.
- Trading rules:
Look for buy signals (candlestick patterns, divergence) at 38.2%-61.8% retracement levels in an uptrend.
Place stops below the 78.6% level.
The stronger the trend, the shallower the retracement (23.6%-38.2%).
- Parameters:
- Returns:
Level names (e.g.
"23.6%") as keys and price values.- Return type:
Example
>>> result = fibonacci_retracements(swing_high=110.0, swing_low=100.0) >>> result["50.0%"] 105.0
- fibonacci_extensions(swing_low, swing_high, pullback_low)[source]¶
Compute Fibonacci extension levels from three price points.
Uses a swing low, swing high, and pullback low to project extension levels at 100%, 127.2%, 161.8%, 200%, and 261.8%.
- Interpretation:
Extension levels project where price might go AFTER a retracement completes.
100%: The most common initial target (move equals the first swing).
127.2%: Common target for corrective waves.
161.8%: The golden extension – a key profit target.
200% and 261.8%: Extended targets for strong trends.
Use for setting profit targets and identifying potential resistance levels in a trend.
- Parameters:
- Returns:
Extension level names as keys and projected price values.
- Return type:
Example
>>> result = fibonacci_extensions(100.0, 110.0, 105.0) >>> result["100.0%"] 115.0
- fibonacci_fans(pivot_x, pivot_y, target_x, target_y)[source]¶
Compute Fibonacci fan line slopes from two pivot points.
Draws fan lines from
(pivot_x, pivot_y)through Fibonacci retracement levels of the vertical distance to(target_x, target_y).- Parameters:
- Returns:
Fan line labels as keys and slope values.
- Return type:
Example
>>> result = fibonacci_fans(0, 100.0, 10, 110.0) >>> abs(result["50.0%"] - 0.5) < 1e-10 True
- fibonacci_time_zones(start_index, max_index)[source]¶
Compute Fibonacci time zone indices from a start bar.
Generates a sequence of bar indices at Fibonacci intervals (1, 1, 2, 3, 5, 8, 13, 21, …) from the given start index, up to
max_index.- Parameters:
- Returns:
List of bar indices at Fibonacci time intervals.
- Return type:
Example
>>> fibonacci_time_zones(0, 50) [1, 2, 3, 5, 8, 13, 21, 34]
- fibonacci_pivot_points(high, low, close)[source]¶
Pivot points using Fibonacci ratios.
Computes the standard pivot
P = (H + L + C) / 3and derives support/resistance using Fibonacci ratios applied to the prior bar’s range.- Parameters:
- Returns:
pivot,s1,s2,s3,r1,r2,r3.- Return type:
Example
>>> import pandas as pd >>> h = pd.Series([12, 13, 14, 13, 12], dtype=float) >>> lo = pd.Series([10, 11, 12, 11, 10], dtype=float) >>> c = pd.Series([11, 12, 13, 12, 11], dtype=float) >>> result = fibonacci_pivot_points(h, lo, c)
- auto_fibonacci(data, lookback=50, direction='up')[source]¶
Automatically detect swing high/low and compute Fibonacci retracements.
Scans the most recent lookback bars to find the highest high and lowest low, then computes Fibonacci retracement levels.
- Parameters:
- Returns:
swing_high(float),swing_high_idx(index label),swing_low(float),swing_low_idx(index label),levels(dict of retracement levels).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5)) >>> result = auto_fibonacci(close, lookback=30)
- alma(data, period=9, offset=0.85, sigma=6.0)[source]¶
Arnaud Legoux Moving Average (ALMA).
A Gaussian-weighted moving average that allows the user to control the position of the bell curve along the window via offset and the width via sigma.
- Interpretation:
Price above ALMA: Bullish.
Price below ALMA: Bearish.
Combines the responsiveness of EMA with the smoothness of Gaussian weighting. The offset parameter lets you place more weight on recent prices (offset near 1) or older prices (offset near 0).
Excellent alternative to EMA for crossover systems.
- Parameters:
data (
Series) – Price series (typically close).period (
int, default:9) – Window length.offset (
float, default:0.85) – Controls the position of the Gaussian peak within the window. 0 = far left (oldest), 1 = far right (newest).sigma (
float, default:6.0) – Controls the width of the Gaussian bell curve. Higher values produce a broader, smoother curve.
- Returns:
ALMA values. The first
period - 1entries areNaN.- Return type:
Example
>>> result = alma(close, period=9, offset=0.85, sigma=6.0)
- lsma(data, period=25)[source]¶
Least Squares Moving Average (LSMA).
Also known as the Linear Regression Value or End Point Moving Average. At each bar, a least-squares line is fit over the window and the endpoint of the line is returned.
- Parameters:
- Returns:
LSMA values.
- Return type:
Example
>>> result = lsma(close, period=25)
- swma(data)[source]¶
Symmetrically Weighted Moving Average (SWMA).
A 4-bar weighted average using weights
[1, 2, 2, 1] / 6.- Parameters:
data (
Series) – Price series.- Returns:
SWMA values. The first 3 entries are
NaN.- Return type:
Example
>>> result = swma(close)
- sinema(data, period=14)[source]¶
Sine-Weighted Moving Average.
Each element in the window is weighted by the sine of its proportional position within a half-period (pi), giving the most weight to the center of the window.
- Parameters:
- Returns:
Sine-weighted moving average values.
- Return type:
Example
>>> result = sinema(close, period=14)
- trima(data, period=20)[source]¶
Triangular Moving Average (TRIMA).
Equivalent to a double SMA:
SMA(SMA(data, ceil((period+1)/2)), floor((period+1)/2)). This produces a smoother curve than a single SMA by effectively giving the most weight to the center of the window.- Parameters:
- Returns:
TRIMA values.
- Return type:
Example
>>> result = trima(close, period=20)
- jma(data, period=7, phase=50.0, power=2)[source]¶
Jurik Moving Average approximation (JMA).
An adaptive moving average that attempts to minimize lag and overshoot. This is an approximation of the proprietary Jurik algorithm using an adaptive EMA with phase and power controls.
- Parameters:
data (
Series) – Price series.period (
int, default:7) – Smoothing period.phase (
float, default:50.0) – Phase parameter in the range [-100, 100]. Controls the tradeoff between lag and overshoot. 0 is balanced, positive reduces lag.power (
int, default:2) – Power parameter controlling the smoothing curve shape.
- Returns:
JMA values.
- Return type:
Example
>>> result = jma(close, period=7, phase=50, power=2)
- gaussian_filter(data, period=14, poles=2)[source]¶
Gaussian-weighted rolling filter.
Applies a discrete Gaussian kernel over the rolling window. The standard deviation of the kernel is set to
period / 4so that the window captures approximately two standard deviations.- Parameters:
- Returns:
Gaussian-filtered values.
- Return type:
Example
>>> result = gaussian_filter(close, period=14)
- butterworth_filter(data, period=14)[source]¶
2nd-order Butterworth low-pass filter (IIR).
This implements the classic Ehlers two-pole Butterworth filter, which provides smooth output with minimal lag relative to its degree of smoothing.
- Parameters:
- Returns:
Butterworth-filtered values.
- Return type:
Example
>>> result = butterworth_filter(close, period=14)
- supersmoother(data, period=14)[source]¶
Ehlers Super Smoother (2-pole Butterworth variant).
A modified Butterworth filter by John Ehlers that removes aliasing noise while retaining a smooth, low-lag response.
- Parameters:
- Returns:
Super-smoothed values.
- Return type:
Example
>>> result = supersmoother(close, period=14)
- hann_window_ma(data, period=14)[source]¶
Hann (raised cosine) windowed moving average.
Each element in the window is weighted by the Hann function:
w(i) = 0.5 * (1 - cos(2 * pi * i / (N - 1)))- Parameters:
- Returns:
Hann-windowed moving average values.
- Return type:
Example
>>> result = hann_window_ma(close, period=14)
- hamming_window_ma(data, period=14)[source]¶
Hamming windowed moving average.
Each element in the window is weighted by the Hamming function:
w(i) = 0.54 - 0.46 * cos(2 * pi * i / (N - 1))- Parameters:
- Returns:
Hamming-windowed moving average values.
- Return type:
Example
>>> result = hamming_window_ma(close, period=14)
- kaufman_efficiency_ratio(data, period=10)[source]¶
Kaufman Efficiency Ratio (ER).
Measures the efficiency of price movement as the ratio of directional change to total path length. This is the core component of the Kaufman Adaptive Moving Average (KAMA).
ER = |close - close[period]| / sum(|close - close[1]|, period)Values near 1.0 indicate strong trending; values near 0.0 indicate choppy / mean-reverting markets.
- Parameters:
- Returns:
Efficiency ratio values in [0, 1].
- Return type:
Example
>>> result = kaufman_efficiency_ratio(close, period=10)
- choppiness_index(high, low, close, period=14)[source]¶
Choppiness Index.
Measures whether the market is trending or range-bound.
- Interpretation:
> 61.8: Choppy / range-bound market. Avoid trend-following strategies; use mean-reversion instead.
< 38.2: Strong trending market. Use trend-following strategies; avoid mean-reversion.
38.2-61.8: Transitional zone.
Does NOT indicate trend direction, only whether a trend exists.
Low choppiness often precedes a breakout.
- Trading rules:
Apply trend strategies when CI < 38.2.
Apply range strategies when CI > 61.8.
Wait for CI to drop before entering breakout trades.
CI = 100 * log10(sum(ATR(1), n) / (highest_high - lowest_low)) / log10(n)- Parameters:
- Returns:
Choppiness Index values, typically in [0, 100].
- Return type:
Example
>>> result = choppiness_index(high, low, close, period=14)
- random_walk_index(high, low, close, period=14)[source]¶
Random Walk Index (RWI).
Compares the range of directional price moves to the expected range of a random walk. Values above 1.0 suggest trending behavior.
- Interpretation:
RWI High > 1: Upward price movement exceeds what a random walk would produce = genuine uptrend.
RWI Low > 1: Downward price movement exceeds random walk = genuine downtrend.
Both < 1: Price movement is consistent with random noise = no trend.
RWI High > RWI Low: Bullish bias.
RWI Low > RWI High: Bearish bias.
- Parameters:
- Returns:
rwi_highandrwi_low.- Return type:
Example
>>> result = random_walk_index(high, low, close, period=14) >>> result["rwi_high"]
- polarized_fractal_efficiency(close, period=10, smoothing=5)[source]¶
Polarized Fractal Efficiency (PFE).
Measures the efficiency of the price path using fractal geometry. A straight-line move yields +/- 100; a random walk yields ~0.
- Interpretation:
> 0: Price is moving efficiently upward (trending up).
< 0: Price is moving efficiently downward (trending down).
Near 0: Choppy, inefficient movement (no trend).
> +50: Strong bullish trend.
< -50: Strong bearish trend.
Think of it as “how direct is the price path” – values near +/-100 mean a straight line, near 0 means a random walk.
PFE = sign(direction) * sqrt(sum_sq_diff + direction^2) / sum_single_diff * 100The raw PFE is then smoothed with an EMA.
- Parameters:
- Returns:
PFE values, bounded roughly in [-100, 100].
- Return type:
Example
>>> result = polarized_fractal_efficiency(close, period=10)
- price_zone_oscillator(close, period=14)[source]¶
Price Zone Oscillator (PZO).
An EMA-based oscillator that classifies price action into zones. A positive close change gets +close, negative gets -close.
PZO = 100 * EMA(signed_close, period) / EMA(close, period)- Parameters:
- Returns:
PZO values, typically in [-100, 100].
- Return type:
Example
>>> result = price_zone_oscillator(close, period=14)
- ergodic_oscillator(close, fast=5, slow=20, signal=5)[source]¶
Ergodic Oscillator.
A True Strength Index variant that produces a histogram (oscillator minus signal line) for identifying momentum shifts.
- Parameters:
- Returns:
ergodic(the TSI line),signal, andhistogram.- Return type:
Example
>>> result = ergodic_oscillator(close) >>> result["ergodic"]
- elder_thermometer(high, low, period=22)[source]¶
Elder Thermometer.
Measures the distance of the current bar from the previous bar’s range, indicating how far price has moved beyond the prior bar.
Thermo = max(high - prev_high, prev_low - low, 0)The result is smoothed with an EMA.
- Parameters:
- Returns:
Elder Thermometer values (non-negative).
- Return type:
Example
>>> result = elder_thermometer(high, low, period=22)
- market_facilitation_index(high, low, volume)[source]¶
Market Facilitation Index (MFI / BW MFI).
Also known as the Bill Williams Market Facilitation Index. Measures the efficiency of price movement per unit of volume.
MFI = (high - low) / volume- Parameters:
- Returns:
Market Facilitation Index values.
- Return type:
Example
>>> result = market_facilitation_index(high, low, volume)
- efficiency_ratio(data, period=10)[source]¶
Efficiency Ratio (ER).
Measures the efficiency of price movement as the ratio of net directional change to the total path traveled.
ER = |close - close[n]| / sum(|close - close[1]|, n)Values near 1.0 indicate a strong trend; values near 0.0 indicate choppy, range-bound price action.
- Parameters:
- Returns:
Efficiency ratio values in [0, 1].
- Return type:
Example
>>> result = efficiency_ratio(close, period=10)
- trend_intensity_index(data, period=30)[source]¶
Trend Intensity Index (TII).
Measures the percentage of closes above or below the SMA over the look-back period.
TII = 100 * (count_above - count_below) / period- Parameters:
- Returns:
TII values in [-100, 100]. Positive indicates bullish bias; negative indicates bearish bias.
- Return type:
Example
>>> result = trend_intensity_index(close, period=30)
- directional_movement_index(high, low, close, period=14)[source]¶
Simplified Directional Movement Index (DMI).
Computes +DI and -DI without the full ADX smoothing, providing raw directional movement readings.
- Parameters:
- Returns:
plus_di(+DI) andminus_di(-DI), both in [0, 100].- Return type:
Example
>>> result = directional_movement_index(high, low, close, period=14) >>> result["plus_di"]
- kairi(data, period=14)[source]¶
KAIRI — percentage deviation from the SMA.
KAIRI = ((close - SMA(close, period)) / SMA(close, period)) * 100- Parameters:
- Returns:
KAIRI values (percentage, unbounded).
- Return type:
Example
>>> result = kairi(close, period=14)
- gopalakrishnan_range(high, low, period=5)[source]¶
Gopalakrishnan Range Index (GAPO).
GAPO = log(max_high - min_low) / log(period)Measures the log of the high-low range relative to the log of the look-back period. Higher values indicate larger ranges.
- Parameters:
- Returns:
GAPO values (non-negative).
- Return type:
Example
>>> result = gopalakrishnan_range(high, low, period=5)
- pretty_good_oscillator(high, low, close, period=14)[source]¶
Pretty Good Oscillator (PGO).
PGO = (close - SMA(close, period)) / ATR(period)Normalizes the deviation from the SMA by the ATR, producing a volatility-adjusted momentum reading.
- Parameters:
- Returns:
PGO values (unbounded).
- Return type:
Example
>>> result = pretty_good_oscillator(high, low, close, period=14)
- connors_tps(data, period=2)[source]¶
ConnorsRSI TPS component — cumulative streak RSI.
Computes an up/down streak series, then applies RSI to the streak values. This isolates the streak component of ConnorsRSI.
- Parameters:
- Returns:
Streak RSI values in [0, 100].
- Return type:
Example
>>> result = connors_tps(close, period=2)
- relative_momentum_index(data, period=14, momentum_period=4)[source]¶
Relative Momentum Index (RMI).
RSI applied to momentum (
close - close[momentum_period]) instead of the simple one-bar change. This produces a smoother oscillator that reacts to the direction and magnitude of price swings over momentum_period bars.- Parameters:
- Returns:
RMI values in [0, 100].
- Return type:
Example
>>> result = relative_momentum_index(close, period=14, momentum_period=4)
- find_support_resistance(high, low, lookback=5, num_levels=5, tolerance=0.02)[source]¶
Detect horizontal support and resistance levels via clustering.
Identifies local swing highs and swing lows, then clusters nearby levels within tolerance (as a fraction of price) to produce consolidated S/R levels.
- Interpretation:
Support levels: Prices where buying pressure historically prevented further decline. Price tends to bounce at these levels.
Resistance levels: Prices where selling pressure historically prevented further advance.
Multiple touches: A level tested many times is stronger.
Breakout through resistance: Resistance becomes support (role reversal) and vice versa.
- Trading rules:
Buy at support levels with confirmation (candlestick pattern, volume spike).
Sell at resistance levels with confirmation.
Place stops on the other side of the S/R level.
- Parameters:
high (
Series) – High prices.low (
Series) – Low prices.lookback (
int, default:5) – Number of bars on each side to confirm a swing point.num_levels (
int, default:5) – Maximum number of S/R levels to return per side.tolerance (
float, default:0.02) – Fraction of price within which nearby levels are merged.
- Returns:
supportandresistancelists of price levels, sorted ascending.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(200) * 0.5) + 1) >>> lo = h - 2 >>> result = find_support_resistance(h, lo)
- price_clustering(data, num_levels=5, bins=100)[source]¶
Find price levels where price has spent the most time.
Builds a histogram of price values and returns the bin centres with the highest counts, analogous to a simplified volume profile.
- Parameters:
- Returns:
Array of key price levels sorted ascending.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series([100, 101, 100, 99, 100, 101, 102, 100], dtype=float) >>> price_clustering(close, num_levels=3)
- fractal_levels(high, low, period=2)[source]¶
Williams fractal swing high/low identification.
An up-fractal occurs when the high is the highest of
2 * period + 1bars. A down-fractal occurs when the low is the lowest.- Parameters:
- Returns:
up_fractals(boolean Series, True at up-fractal bars),down_fractals(boolean Series, True at down-fractal bars).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = fractal_levels(h, lo, period=2)
- round_number_levels(current_price, num_levels=5, step=None)[source]¶
Generate psychological round number levels near the current price.
Computes evenly spaced round numbers above and below the given price. Useful for identifying potential support/resistance at psychologically significant prices.
- Parameters:
- Returns:
Sorted list of round number price levels.
- Return type:
Example
>>> round_number_levels(105.3, num_levels=3, step=10.0) [80.0, 90.0, 100.0, 110.0, 120.0, 130.0]
- supply_demand_zones(open_, high, low, close, body_pct=0.6, consolidation_bars=3)[source]¶
Detect supply and demand zones from price action.
A demand zone forms when a large bullish candle follows a period of basing (small bodies). A supply zone forms similarly for bearish moves. Zones are defined by the basing candles’ range.
- Parameters:
open (
pd.Series) – Open prices.high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_pct (
float, default:0.6) – Minimum body-to-range ratio to qualify as a “large” candle.consolidation_bars (
int, default:3) – Number of preceding small-body bars required for basing.open_ (Series)
- Returns:
demandandsupplylists, each containing dicts withzone_low,zone_high, andindexkeys.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> n = 100 >>> c = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> o = c.shift(1).fillna(c.iloc[0]) >>> h = pd.concat([o, c], axis=1).max(axis=1) + 0.5 >>> lo = pd.concat([o, c], axis=1).min(axis=1) - 0.5 >>> result = supply_demand_zones(o, h, lo, c)
- trendline_detection(high, low, lookback=5, min_touches=2)[source]¶
Fit linear trendlines to swing high and swing low points.
Detects swing points, then fits lines through the most recent swing highs (resistance trendline) and swing lows (support trendline) using least-squares regression.
- Parameters:
- Returns:
resistance_linesandsupport_lines, each containing dicts withslope,intercept, andnum_toucheskeys.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5) + 1) >>> lo = h - 2 >>> result = trendline_detection(h, lo, lookback=5)
Overlap Studies¶
Moving averages, bands, and channel studies drawn on the price chart.
Overlap / moving average technical analysis studies.
This module provides a comprehensive set of moving average and overlay
indicators used in technical analysis. All functions accept pd.Series
inputs and return pd.Series (or dict[str, pd.Series] for
multi-output indicators).
- sma(data, period=20)[source]¶
Simple Moving Average.
The most fundamental overlay indicator. Smooths price by averaging the last period values equally.
- Interpretation:
Price above SMA: Bullish – price is above its average.
Price below SMA: Bearish – price is below its average.
Golden cross: Short-term SMA (e.g. 50) crosses above long-term SMA (e.g. 200) = bullish trend signal.
Death cross: Short-term SMA crosses below long-term SMA = bearish trend signal.
Slope: Rising SMA confirms uptrend; falling confirms downtrend.
Common periods: 20 (short-term), 50 (medium-term), 200 (long-term institutional benchmark).
- Trading rules:
Buy when price crosses above SMA (or when shorter SMA crosses above longer SMA).
Sell when price crosses below SMA.
Use 200 SMA as a trend filter: only take longs above it.
- ema(data, period=20)[source]¶
Exponential Moving Average.
Uses the standard span-based smoothing factor
2 / (period + 1). More responsive to recent price changes than SMA because it weights recent data more heavily.- Interpretation:
Same as SMA: price above = bullish, price below = bearish.
More responsive than SMA: catches trend changes faster but may produce more whipsaws.
EMA crossovers (e.g. 12/26 EMA) form the basis of MACD.
Common periods: 9 (very short-term), 21 (swing trading), 50 and 200 (institutional benchmarks).
- Trading rules:
Same crossover rules as SMA but with faster signals.
In fast markets, prefer EMA over SMA for tighter stops and quicker entries.
- wma(data, period=20)[source]¶
Weighted Moving Average.
Weights increase linearly so that the most recent observation receives the highest weight. A compromise between SMA and EMA.
- Interpretation:
Same directional signals as SMA/EMA: price above = bullish, below = bearish, crossovers for entry/exit.
More responsive than SMA but less than EMA.
Useful when you want more weight on recent data but a smoother result than EMA.
- dema(data, period=20)[source]¶
Double Exponential Moving Average.
DEMA = 2 * EMA(data) - EMA(EMA(data))Reduces the lag inherent in a standard EMA by subtracting a double-smoothed version. More responsive to recent price changes.
- Interpretation:
Same as EMA/SMA but with reduced lag. Use for faster crossover signals and tighter trailing stops.
Price above DEMA = bullish; below = bearish.
- tema(data, period=20)[source]¶
Triple Exponential Moving Average.
TEMA = 3 * EMA - 3 * EMA(EMA) + EMA(EMA(EMA))Even less lag than DEMA. Hugs price very tightly and reacts quickly to changes. Can overshoot in choppy markets.
- Interpretation:
Same as EMA but with minimal lag. Excellent for short-term trend following but may whipsaw in consolidation.
Price above TEMA = bullish; below = bearish.
- kama(data, period=10, fast=2, slow=30)[source]¶
Kaufman Adaptive Moving Average (KAMA).
KAMA adapts its smoothing constant based on the efficiency ratio of the price movement. In trending markets it acts like a fast EMA; in choppy markets it slows down to avoid whipsaws.
- Interpretation:
Price above KAMA: Bullish.
Price below KAMA: Bearish.
Flat KAMA: Market is choppy/range-bound (KAMA stops following noise). This is the key advantage over SMA/EMA.
KAMA direction change: Potential trend reversal signal.
- Trading rules:
Buy when price crosses above KAMA.
Sell when price crosses below KAMA.
KAMA’s adaptive nature makes it better for trend following than fixed-period moving averages in volatile markets.
- vwap(high, low, close, volume)[source]¶
Volume Weighted Average Price (VWAP).
Computed as the cumulative sum of
typical_price * volumedivided by cumulative volume. This is the intraday running VWAP; for session-reset VWAP, pre-group your data by session.- Interpretation:
Price above VWAP: Buyers are in control; longs entered at good prices relative to the average fill.
Price below VWAP: Sellers are in control.
VWAP as support/resistance: Institutional traders use VWAP as a benchmark. Price tends to gravitate toward VWAP.
Mean reversion: Extreme deviations from VWAP tend to revert, especially intraday.
- Trading rules:
Buy pullbacks to VWAP in an uptrend (institutional support).
Sell rallies to VWAP in a downtrend (institutional resistance).
Institutions aim to buy below VWAP and sell above it; track whether your fills are better than VWAP.
- supertrend(high, low, close, period=10, multiplier=3.0)[source]¶
Supertrend indicator.
A trend-following overlay that flips between support and resistance levels based on ATR bands. One of the cleanest trend indicators.
- Interpretation:
Direction = 1 (uptrend): Supertrend line acts as dynamic support below price. Trend is bullish.
Direction = -1 (downtrend): Supertrend line acts as dynamic resistance above price. Trend is bearish.
Flip from -1 to 1: Buy signal (trend turns bullish).
Flip from 1 to -1: Sell signal (trend turns bearish).
- Trading rules:
Buy when direction flips to 1 (close above supertrend).
Sell when direction flips to -1 (close below supertrend).
Use the supertrend line as a trailing stop.
Higher multiplier = fewer flips but wider stops.
- Parameters:
- Returns:
supertrend— the indicator line, anddirection(1 for uptrend / bullish, -1 for downtrend / bearish).- Return type:
- ichimoku(high, low, close, tenkan=9, kijun=26, senkou_b=52)[source]¶
Ichimoku Kinko Hyo (Ichimoku Cloud).
A comprehensive trend system that provides support/resistance, trend direction, and momentum in a single view.
- Interpretation:
Price above cloud: Bullish trend. The cloud acts as support.
Price below cloud: Bearish trend. The cloud acts as resistance.
Price inside cloud: Trend is transitioning/uncertain.
Tenkan-Kijun cross: Tenkan crossing above Kijun = bullish signal (TK cross). Below = bearish.
Senkou Span A above B: Cloud is green = bullish bias.
Senkou Span A below B: Cloud is red = bearish bias.
Chikou Span above price: Confirms bullish momentum.
Cloud thickness: Thicker cloud = stronger support/ resistance.
- Trading rules:
Buy when price breaks above cloud AND Tenkan > Kijun AND Chikou is above price from 26 periods ago.
Sell when price breaks below cloud AND Tenkan < Kijun.
Use cloud edges (Senkou A/B) as stop-loss levels.
- Parameters:
- Returns:
Keys:
tenkan_sen,kijun_sen,senkou_span_a,senkou_span_b,chikou_span.- Return type:
Notes
Senkou Span A and B are shifted forward by
kijunperiods, and the Chikou Span is shifted backward bykijunperiods, matching traditional charting convention.
- bollinger_bands(data, period=20, std_dev=2.0)[source]¶
Bollinger Bands.
A volatility-based envelope around a moving average. The bands widen during high volatility and contract during low volatility.
- Interpretation:
Price touching upper band: Price is at the high end of its recent range. Not necessarily a sell signal in strong uptrends (walking the band).
Price touching lower band: Price is at the low end. Not necessarily a buy signal in downtrends.
Squeeze (narrow bandwidth): Low volatility – a breakout in either direction is imminent.
Expansion (wide bandwidth): High volatility – move may be overextended and due for consolidation.
%B > 1: Price is above the upper band.
%B < 0: Price is below the lower band.
%B near 0.5: Price is at the middle band (SMA).
- Trading rules:
Mean reversion: Buy at lower band, sell at upper band (works best in ranging markets).
Breakout: Buy on a close above upper band with expanding bandwidth (works in trending markets).
Squeeze play: Wait for narrow bands, then trade the breakout direction.
- keltner_channel(high, low, close, period=20, multiplier=1.5)[source]¶
Keltner Channel.
The middle line is an EMA of the close; upper and lower bands are offset by a multiple of the Average True Range.
- Interpretation:
Price above upper band: Strong uptrend or overbought.
Price below lower band: Strong downtrend or oversold.
Price bouncing off middle line: EMA acting as support (uptrend) or resistance (downtrend).
Keltner + Bollinger: When BB is inside KC, a “squeeze” is active. See
squeeze_momentum().
- Trading rules:
Buy pullbacks to the middle line (EMA) in an uptrend.
Sell rallies to the middle line in a downtrend.
Breakout above upper band = trend continuation (go long).
Breakout below lower band = trend continuation (go short).
- donchian_channel(high, low, period=20)[source]¶
Donchian Channel.
The simplest channel indicator: the highest high and lowest low over the look-back period. The basis of the Turtle Trading system.
- Interpretation:
Price at upper band: Price is at the highest point in period bars = potential breakout to the upside.
Price at lower band: Price is at the lowest point = potential breakout to the downside.
Channel width: Wider channel = more volatile market.
Middle line: Average of upper and lower = equilibrium.
- Trading rules:
Buy when price breaks above the upper band (20-day high).
Sell when price breaks below the lower band (20-day low).
Use 10-day Donchian for exits, 20-day for entries (Turtle Trading system).
Momentum Indicators¶
Oscillators measuring speed and magnitude of price changes.
Momentum oscillator indicators.
This module contains oscillators that measure the speed and magnitude of
price movements. All functions accept pd.Series inputs and return
pd.Series (or dict[str, pd.Series] for multi-output indicators).
- rsi(data, period=14)[source]¶
Relative Strength Index (RSI).
Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions. Uses the Wilder smoothing method (equivalent to
ewm(alpha=1/period)).RSI = 100 - (100 / (1 + RS)) where RS = avg_gain / avg_loss over
periodbars.- Interpretation:
> 70: Overbought – price may be due for a pullback. In strong uptrends, RSI can stay above 70 for extended periods.
30-70: Neutral zone.
< 30: Oversold – price may be due for a bounce. In strong downtrends, RSI can stay below 30 for extended periods.
Divergence: If price makes a new high but RSI does not, bearish divergence signals weakening momentum. Conversely, bullish divergence when price makes a new low but RSI does not.
Centerline crossover: RSI crossing above 50 = bullish shift, below 50 = bearish shift.
- Trading rules:
Buy when RSI crosses above 30 (oversold bounce).
Sell when RSI crosses below 70 (overbought reversal).
Use divergences for higher-probability signals.
Adjust thresholds in trending markets (80/20 instead of 70/30).
- stochastic(high, low, close, k_period=14, d_period=3)[source]¶
Stochastic Oscillator (%K / %D).
Measures where the close sits within the recent high-low range. When %K is near 100, price closed near the top of the range (bullish); near 0, it closed near the bottom (bearish).
- Interpretation:
> 80: Overbought zone. In strong uptrends, the indicator can stay above 80 for extended periods without signaling a top.
< 20: Oversold zone. In strong downtrends, can persist.
%K crosses above %D below 20: Bullish crossover buy signal.
%K crosses below %D above 80: Bearish crossover sell signal.
Divergence: Price makes new high but %K does not = bearish.
- Trading rules:
Buy when %K crosses above %D in the oversold zone (< 20).
Sell when %K crosses below %D in the overbought zone (> 80).
Avoid trading crossovers in the neutral zone (20-80) unless confirmed by other indicators.
- stochastic_rsi(data, period=14, k_period=3, d_period=3)[source]¶
Stochastic RSI.
Applies the Stochastic formula to the RSI output, producing an even more sensitive oscillator. Useful for detecting short-term overbought/oversold conditions within the RSI itself.
- Interpretation:
> 80: RSI is near its recent high – overbought.
< 20: RSI is near its recent low – oversold.
%K/%D crossovers: Same logic as standard Stochastic.
More volatile than standard Stochastic; best combined with a trend filter to avoid false signals in ranging markets.
- Trading rules:
Buy when StochRSI crosses above 20 (oversold bounce).
Sell when StochRSI crosses below 80 (overbought reversal).
Combine with a trend indicator (e.g. 200 EMA) to filter signals in the direction of the prevailing trend.
- macd(data, fast=12, slow=26, signal=9)[source]¶
Moving Average Convergence Divergence (MACD).
Tracks the relationship between two EMAs. When the fast EMA pulls away from the slow EMA, momentum is strong. The signal line acts as a trigger for entries and exits.
- Interpretation:
MACD above zero: Fast EMA > slow EMA = bullish momentum.
MACD below zero: Fast EMA < slow EMA = bearish momentum.
Signal line crossover: MACD crossing above its signal line is a bullish signal; crossing below is bearish.
Histogram: Represents the distance between MACD and signal. Growing bars = strengthening momentum. Shrinking bars = momentum fading (potential reversal ahead).
Divergence: Price makes a new high but MACD does not = bearish divergence. Price makes a new low but MACD does not = bullish divergence.
Zero-line crossover: MACD crossing above zero confirms an uptrend; crossing below confirms a downtrend.
- Trading rules:
Buy when MACD crosses above signal line (bullish crossover).
Sell when MACD crosses below signal line (bearish crossover).
Histogram peak/trough reversals can provide early warnings.
Combine with price action for confirmation.
- williams_r(high, low, close, period=14)[source]¶
Williams %R.
Measures where the close is relative to the high-low range over the look-back period. Mathematically the inverse of the Stochastic %K, but on a [-100, 0] scale.
- Interpretation:
-20 to 0: Overbought – close is near the period high.
-80 to -100: Oversold – close is near the period low.
-50 crossover: Crossing above -50 = bullish, below = bearish.
Note: Overbought does not mean sell immediately; in strong uptrends, Williams %R stays near 0 for extended periods.
- Trading rules:
Buy when %R crosses above -80 (leaving oversold zone).
Sell when %R crosses below -20 (leaving overbought zone).
Use divergence between price and %R for reversal signals.
- cci(high, low, close, period=20)[source]¶
Commodity Channel Index (CCI).
Measures the deviation of the typical price from its moving average, normalized by mean deviation. Uses Lambert’s constant of 0.015 so that roughly 75% of values fall within [-100, +100].
- Interpretation:
> +100: Price is unusually high relative to average – strong uptrend or overbought condition.
< -100: Price is unusually low – strong downtrend or oversold condition.
Zero-line crossover: CCI crossing above 0 indicates price is above its average (bullish); below 0 is bearish.
Divergence: Price makes new high but CCI does not = weakening momentum.
- Trading rules:
Buy when CCI crosses above +100 (trend entry) or above 0 (conservative entry).
Sell when CCI crosses below -100 (trend entry) or below 0.
Exit longs when CCI crosses back below +100 from above.
Use +200/-200 for extreme overbought/oversold levels.
- roc(data, period=10)[source]¶
Rate of Change (ROC) – percentage change over period bars.
Measures the percentage difference between the current price and the price period bars ago. A pure momentum measure.
- Interpretation:
Positive: Price is higher than it was period bars ago.
Negative: Price is lower.
Zero-line crossover: Crossing above zero = bullish momentum shift; crossing below = bearish.
Extreme readings: Unusually high ROC may indicate an overextended move ripe for mean reversion.
- Trading rules:
Buy when ROC crosses above zero from below.
Sell when ROC crosses below zero from above.
Use divergence with price for reversal signals.
- momentum(data, period=10)[source]¶
Price Momentum (difference over period bars).
The simplest momentum indicator: the absolute price change over the look-back window. Unlike ROC, this is not percentage-based, so it is scale-dependent.
- Interpretation:
Positive: Price is rising relative to period bars ago.
Negative: Price is falling.
Zero-line crossover: Same as ROC – bullish above, bearish below.
Magnitude: Larger values = stronger momentum.
- tsi(data, long=25, short=13, signal=13)[source]¶
True Strength Index (TSI).
A double-smoothed momentum oscillator that measures the ratio of smoothed price change to smoothed absolute price change. Oscillates between -100 and +100.
- Interpretation:
Above zero: Bullish momentum (price changes are predominantly positive).
Below zero: Bearish momentum.
Signal line crossover: TSI crossing above its signal line = bullish; crossing below = bearish.
Zero-line crossover: Confirms trend direction change.
Divergence: Price makes new high but TSI does not = bearish divergence (and vice versa).
- Trading rules:
Buy when TSI crosses above zero or above its signal line.
Sell when TSI crosses below zero or below its signal line.
Use both zero-line and signal-line crossovers together for higher-confidence signals.
- awesome_oscillator(high, low, fast=5, slow=34)[source]¶
Awesome Oscillator (AO).
AO = SMA(median_price, fast) - SMA(median_price, slow)Developed by Bill Williams. Measures market momentum using the difference between a 5-period and 34-period SMA of the midpoint price.
- Interpretation:
Above zero: Bullish momentum (short-term average > long-term average).
Below zero: Bearish momentum.
Zero-line crossover: AO crossing above zero = buy signal; crossing below = sell signal.
Twin peaks (bullish): Two lows below zero where the second is higher than the first, followed by a green bar.
Twin peaks (bearish): Two highs above zero where the second is lower than the first, followed by a red bar.
Saucer: Three consecutive bars above zero where the middle bar is lowest = continuation buy signal.
- ppo(data, fast=12, slow=26, signal=9)[source]¶
Percentage Price Oscillator (PPO).
Like MACD but expressed as a percentage of the slow EMA, making it comparable across different price levels and assets.
- Interpretation:
Same signals as MACD: signal-line crossovers, zero-line crossovers, histogram analysis, and divergences.
Advantage over MACD: Because it is percentage-based, you can compare PPO values across stocks of different prices.
> 0: Fast EMA is above slow EMA = bullish.
< 0: Fast EMA is below slow EMA = bearish.
Histogram: Grows when momentum accelerates, shrinks when momentum decelerates.
- Trading rules:
Same as MACD: buy on bullish signal crossover, sell on bearish signal crossover.
Use PPO instead of MACD when comparing momentum across multiple securities.
- ultimate_oscillator(high, low, close, period1=7, period2=14, period3=28)[source]¶
Ultimate Oscillator.
Combines buying pressure across three timeframes (7, 14, 28 by default) into a single oscillator. Reduces false signals by incorporating multiple periods.
- Interpretation:
> 70: Overbought.
< 30: Oversold.
Divergence: The primary signal. A bullish divergence occurs when price makes a new low but the UO does not, AND the UO is below 30. A bearish divergence occurs when price makes a new high but UO does not, AND UO is above 70.
- Trading rules (Larry Williams’ method):
Buy on bullish divergence: price makes lower low, UO makes higher low, UO dips below 30, then UO breaks above the divergence high.
Sell when UO rises above 70, or when UO crosses below 50 after a buy signal.
- Parameters:
- Returns:
Ultimate Oscillator values in [0, 100].
- Return type:
- cmo(data, period=14)[source]¶
Chande Momentum Oscillator (CMO).
Similar to RSI but uses the difference between gains and losses divided by their sum, producing an oscillator in [-100, +100] instead of [0, 100].
- Interpretation:
> +50: Strong bullish momentum, potentially overbought.
< -50: Strong bearish momentum, potentially oversold.
Zero crossover: CMO crossing above 0 = bullish; below = bearish.
Unlike RSI, CMO is symmetric around zero, making it easier to read directional bias.
- Trading rules:
Buy when CMO crosses above -50 (leaving oversold).
Sell when CMO crosses below +50 (leaving overbought).
Use zero-line crossover as a trend filter.
- dpo(data, period=20)[source]¶
Detrended Price Oscillator (DPO).
DPO = close - SMA(close, period).shift(period // 2 + 1)Removes the trend component from price to isolate cycles. Unlike most oscillators, DPO is not a momentum indicator – it helps identify cycle highs and lows.
- Interpretation:
Positive: Price is above the displaced moving average (cycle high territory).
Negative: Price is below the displaced moving average (cycle low territory).
Peaks and troughs: Mark cycle turning points. Measure the time between peaks to estimate the dominant cycle length.
Not useful for trend trading; best for timing entries within a known cycle.
- kst(data, roc1=10, roc2=15, roc3=20, roc4=30, sma1=10, sma2=10, sma3=10, sma4=15, signal_period=9)[source]¶
Know Sure Thing (KST) oscillator.
Weighted sum of four smoothed rate-of-change values with weights 1, 2, 3, 4. A comprehensive momentum indicator that combines multiple timeframes.
- Interpretation:
KST above zero: Overall momentum is bullish across timeframes.
KST below zero: Overall momentum is bearish.
Signal line crossover: KST crossing above signal = buy; crossing below = sell.
Zero-line crossover: Confirms broader trend shifts.
Divergence: Price makes new high but KST does not = bearish divergence.
- Trading rules:
Buy when KST crosses above its signal line, preferably when both are below zero (early trend entry).
Sell when KST crosses below its signal line.
- Parameters:
data (
Series) – Price series (typically close).roc1 (
int, default:10) – First ROC period.roc2 (
int, default:15) – Second ROC period.roc3 (
int, default:20) – Third ROC period.roc4 (
int, default:30) – Fourth ROC period.sma1 (
int, default:10) – SMA smoothing for first ROC.sma2 (
int, default:10) – SMA smoothing for second ROC.sma3 (
int, default:10) – SMA smoothing for third ROC.sma4 (
int, default:15) – SMA smoothing for fourth ROC.signal_period (
int, default:9) – SMA period for the signal line.
- Returns:
kstandsignal.- Return type:
Example
>>> result = kst(close) >>> result["kst"]
- connors_rsi(data, rsi_period=3, streak_period=2, rank_period=100)[source]¶
Connors RSI – composite of RSI, up/down streak RSI, and percentile rank.
ConnorsRSI = (RSI(close, rsi_period) + RSI(streak, streak_period) + PercentRank(pct_change, rank_period)) / 3Designed specifically for mean-reversion trading. Combines three components to identify short-term overbought/oversold extremes.
- Interpretation:
> 90: Extremely overbought – high probability of short-term pullback.
< 10: Extremely oversold – high probability of short-term bounce.
70-90: Moderately overbought.
10-30: Moderately oversold.
Best on liquid, large-cap stocks and ETFs; not reliable on low-volume or highly trending instruments.
- Trading rules:
Buy when ConnorsRSI drops below 10 (mean reversion entry).
Sell when ConnorsRSI rises above 70 (exit for profit).
Use with a trend filter (e.g., above 200 SMA = only buy).
- Parameters:
- Returns:
Connors RSI values in [0, 100].
- Return type:
Example
>>> result = connors_rsi(close)
- fisher_transform(high, low, period=9)[source]¶
Fisher Transform – normalizes prices to a Gaussian distribution.
Uses the midpoint
(high + low) / 2, normalizes to [-1, 1] over the look-back window, then applies the inverse hyperbolic tangent (Fisher Transform). Produces sharp, clear turning points.- Interpretation:
> +1.5: Extreme bullish – price may be overextended.
< -1.5: Extreme bearish – price may be oversold.
Signal line crossover: Fisher crossing above signal = bullish; crossing below = bearish.
Fisher Transform values have no upper/lower bound, but values beyond +/-2.5 are rare and signal extremes.
- Trading rules:
Buy when Fisher crosses above its signal line in negative territory (reversal from oversold).
Sell when Fisher crosses below its signal line in positive territory (reversal from overbought).
The indicator is leading – it often turns before price.
- Parameters:
- Returns:
fisher(current value) andsignal(one-bar lag).- Return type:
Example
>>> result = fisher_transform(high, low) >>> result["fisher"]
- elder_ray(high, low, close, period=13)[source]¶
Elder Ray Index – bull power and bear power.
bull_power = high - EMA(close, period)bear_power = low - EMA(close, period)Measures the distance between the high/low and the EMA, showing how much power buyers (bulls) and sellers (bears) have.
- Interpretation:
Bull power > 0: Bulls pushed price above EMA = buyers are in control.
Bear power < 0: Bears pushed price below EMA = sellers are in control (this is the normal state).
Bear power > 0: Extremely bullish – even the lows are above the EMA.
Bull power < 0: Extremely bearish – even the highs cannot reach the EMA.
Divergence: New price high with lower bull power = weakening.
- Trading rules (Elder’s Triple Screen):
Buy when EMA is rising (trend filter) AND bear power is negative but rising (dip-buying).
Sell when EMA is falling AND bull power is positive but falling.
- Parameters:
- Returns:
bull_powerandbear_power.- Return type:
Example
>>> result = elder_ray(high, low, close) >>> result["bull_power"]
- aroon_oscillator(high, low, period=25)[source]¶
Aroon Oscillator – difference between Aroon Up and Aroon Down.
Aroon Up = 100 * (period - bars_since_high) / periodAroon Down = 100 * (period - bars_since_low) / periodAroon Oscillator = Aroon Up - Aroon DownMeasures the strength and direction of a trend by comparing how recently the highest high and lowest low occurred.
- Interpretation:
Near +100: Strong uptrend (new highs are recent, new lows are distant).
Near -100: Strong downtrend (new lows are recent, new highs are distant).
Near 0: No clear trend (consolidation / range-bound).
Zero-line crossover: Oscillator crossing above 0 = new uptrend starting; crossing below = new downtrend.
- Trading rules:
Buy when oscillator crosses above 0.
Sell when oscillator crosses below 0.
Strong signals when oscillator exceeds +50 or drops below -50.
- Parameters:
- Returns:
Aroon Oscillator values in [-100, 100].
- Return type:
Example
>>> result = aroon_oscillator(high, low, period=25)
- chande_forecast_oscillator(data, period=14)[source]¶
Chande Forecast Oscillator (CFO).
Percentage difference between the close and the period-bar linear regression forecast value.
CFO = ((close - linreg_forecast) / close) * 100- Interpretation:
Positive: Price is above the regression forecast – bullish deviation from trend.
Negative: Price is below the regression forecast – bearish deviation.
Zero-line crossover: Shift from bearish to bullish (or vice versa) relative to the regression trend.
Persistent positive values indicate an uptrend; persistent negative values indicate a downtrend.
- Parameters:
- Returns:
CFO values (percentage, unbounded).
- Return type:
Example
>>> result = chande_forecast_oscillator(close, period=14)
- balance_of_power(open_, high, low, close, period=14)[source]¶
Balance of Power (BOP).
BOP = SMA((close - open) / (high - low), period)Measures the strength of buyers vs sellers by comparing the close-open range to the high-low range.
- Interpretation:
Positive: Buyers dominated (close > open relative to range).
Negative: Sellers dominated (close < open relative to range).
Near +1: Extreme buying pressure.
Near -1: Extreme selling pressure.
Zero-line crossover: Shift in control from buyers to sellers or vice versa.
Divergence: Price makes new high but BOP is declining = distribution (smart money selling).
- Trading rules:
Buy when BOP crosses above zero.
Sell when BOP crosses below zero.
Divergence with price is a strong reversal signal.
- Parameters:
- Returns:
BOP values in [-1, 1] (when smoothed, may slightly exceed bounds).
- Return type:
Example
>>> result = balance_of_power(open_, high, low, close)
- qstick(open_, close, period=14)[source]¶
QStick indicator – moving average of
(close - open).Quantifies the dominance of bullish vs bearish candlesticks over the look-back period.
- Interpretation:
Positive: On average, candles are closing above their open (more bullish bars) = buying pressure.
Negative: On average, candles are closing below their open (more bearish bars) = selling pressure.
Zero-line crossover: Shift from bearish to bullish bars or vice versa.
Magnitude: Larger values = stronger candlestick conviction.
- Parameters:
- Returns:
QStick values.
- Return type:
Example
>>> result = qstick(open_, close, period=14)
- coppock_curve(data, wma_period=10, long_roc=14, short_roc=11)[source]¶
Coppock Curve – weighted moving average of the sum of two ROCs.
Coppock = WMA(ROC(long_roc) + ROC(short_roc), wma_period)Originally designed for monthly charts to identify long-term buying opportunities in the S&P 500. One of the few indicators specifically designed to detect major market bottoms.
- Interpretation:
Zero-line crossover from below: The primary buy signal. Indicates the market is turning up from a major bottom.
Above zero and rising: Bullish momentum confirmed.
Below zero: Market is in or recovering from a downturn.
Traditionally, only buy signals are used (zero crossover from below). Sell signals are less reliable.
- Trading rules:
Buy when the Coppock Curve crosses above zero.
This is a long-term indicator; best on monthly data for identifying multi-year buying opportunities.
- Parameters:
- Returns:
Coppock Curve values (unbounded).
- Return type:
Example
>>> result = coppock_curve(close)
- relative_vigor_index(open_, high, low, close, period=10)[source]¶
Relative Vigor Index (RVI).
Measures the conviction of a recent price move by comparing the close-open range to the high-low range, smoothed with a symmetric-weighted moving average and then a simple moving average.
The idea is that in bull markets, prices tend to close near their highs (high vigor), and in bear markets near their lows.
- Interpretation:
Above zero: Closing prices tend to be above opening prices = bullish energy.
Below zero: Closing prices tend to be below opening prices = bearish energy.
Signal line crossover: RVI crossing above signal = buy; crossing below = sell.
Divergence: Price makes new high but RVI does not = conviction is weakening.
- Trading rules:
Buy when RVI crosses above its signal line.
Sell when RVI crosses below its signal line.
Best combined with a trend indicator for confirmation.
- Parameters:
- Returns:
rviandsignal.- Return type:
Example
>>> result = relative_vigor_index(open_, high, low, close) >>> result["rvi"]
- schaff_momentum(data, period=10, fast=23, slow=50)[source]¶
Schaff Trend Cycle applied to momentum (Schaff Momentum).
Applies two rounds of Stochastic smoothing to the difference between fast and slow EMAs, producing a bounded oscillator in [0, 100].
- Interpretation:
> 75: Bullish – trend cycle is in the upper zone.
< 25: Bearish – trend cycle is in the lower zone.
25-75: Transition zone.
Crosses above 25: Buy signal (turning bullish).
Crosses below 75: Sell signal (turning bearish).
Faster than MACD because of the double stochastic smoothing; provides earlier signals but may also have more false signals.
- Parameters:
- Returns:
Schaff Momentum values in [0, 100].
- Return type:
Example
>>> result = schaff_momentum(close, period=10)
- price_momentum_oscillator(data, short=35, long=20, signal=10)[source]¶
Price Momentum Oscillator (PMO) – double-smoothed ROC.
PMO = EMA(EMA(ROC(1), short), long)Developed by Carl Swenlin, the PMO is a double-smoothed one-bar rate-of-change, scaled by a factor of 10 for visibility.
- Interpretation:
Above zero: Bullish momentum (price trend is up).
Below zero: Bearish momentum.
Signal line crossover: PMO crossing above signal = buy; crossing below = sell.
Zero-line crossover: Confirms a trend change.
Extreme readings: PMO values at historical extremes (relative to asset) indicate overextended moves.
Divergence: Price makes new high but PMO does not = bearish divergence.
- Trading rules:
Buy on bullish signal-line crossover, especially near zero or in negative territory.
Sell on bearish signal-line crossover.
- Parameters:
- Returns:
pmoandsignal.- Return type:
Example
>>> result = price_momentum_oscillator(close) >>> result["pmo"]
- klinger_oscillator(high, low, close, volume, fast=34, slow=55, signal=13)[source]¶
Klinger Volume Oscillator – momentum-oriented view.
Uses the relationship between price trend direction and volume to predict price reversals. Combines volume with trend to identify accumulation and distribution.
- Interpretation:
KVO above zero: Volume flow is bullish (accumulation).
KVO below zero: Volume flow is bearish (distribution).
Signal line crossover: KVO crossing above signal = buy; crossing below = sell.
Divergence: Price makes new high but KVO does not = distribution occurring despite rising prices.
- Parameters:
- Returns:
kvoandsignal.- Return type:
Example
>>> result = klinger_oscillator(high, low, close, volume) >>> result["kvo"]
- stochastic_momentum_index(high, low, close, period=14, smooth_k=3, smooth_d=3)[source]¶
Stochastic Momentum Index (SMI).
The SMI is a refinement of the Stochastic Oscillator that measures the distance of the close relative to the midpoint of the high-low range, double-smoothed with EMAs.
- Interpretation:
> +40: Overbought zone.
< -40: Oversold zone.
Zero-line crossover: SMI above 0 = bullish; below = bearish.
Signal line crossover: SMI crossing above signal = buy; crossing below = sell.
More refined than Stochastic because it measures distance from the midpoint rather than from the low, reducing noise.
- Parameters:
- Returns:
smiandsignal.- Return type:
Example
>>> result = stochastic_momentum_index(high, low, close) >>> result["smi"]
- inertia(close, high, low, rvi_period=10, linreg_period=20)[source]¶
Ehlers Inertia Indicator – RVI smoothed by linear regression.
Applies a linear regression (moving regression value) to the Relative Volatility Index (RVI) to produce a momentum-like indicator.
- Interpretation:
Above 50: Bullish – volatility conditions favor upside.
Below 50: Bearish – volatility conditions favor downside.
50 crossover: Trend direction change signal.
Smoother than raw RVI due to the regression smoothing, so it gives clearer trend signals with fewer whipsaws.
- Parameters:
- Returns:
Inertia values. Values above 50 are bullish; below 50 are bearish.
- Return type:
Example
>>> result = inertia(close, high, low)
- squeeze_histogram(high, low, close, period=20, linreg_period=20)[source]¶
Squeeze Histogram – momentum component of the TTM Squeeze.
Computes the linear regression value of
close - midlinewhere midline is the average of the highest high and lowest low over the period, combined with the SMA.- Interpretation:
Positive and growing: Bullish momentum accelerating.
Positive and shrinking: Bullish momentum decelerating (potential top forming).
Negative and growing (more negative): Bearish momentum accelerating.
Negative and shrinking: Bearish momentum decelerating (potential bottom forming).
Color changes (when plotted): Dark green = accelerating bullish, light green = decelerating bullish, dark red = accelerating bearish, light red = decelerating bearish.
- Trading rules:
Use with squeeze detection (BB inside KC). When squeeze fires (BB exits KC), trade in the direction of the histogram.
Buy when histogram turns positive after a squeeze release.
Sell when histogram turns negative after a squeeze release.
- Parameters:
- Returns:
Squeeze momentum histogram values.
- Return type:
Example
>>> result = squeeze_histogram(high, low, close)
- center_of_gravity(data, period=10)[source]¶
Ehlers Center of Gravity Oscillator.
A weighted sum of recent prices where more recent bars receive higher weight, normalized by a simple sum. This produces a leading oscillator.
- Interpretation:
Oscillates around a negative value: The center of gravity shifts based on where price weight is concentrated.
Peaks and troughs: Mark potential turning points with virtually zero lag.
Leading indicator: Turns before price does, making it useful for timing entries.
Best used for cycle-based trading on short timeframes.
CoG = -sum(price[i] * (i+1), i=0..period-1) / sum(price[i], i=0..period-1)- Parameters:
- Returns:
Center of Gravity values (oscillates around zero).
- Return type:
Example
>>> result = center_of_gravity(close, period=10)
- psychological_line(data, period=12)[source]¶
Psychological Line — percentage of up days over N periods.
PSY = (number of up bars in period) / period * 100Values above 50 suggest bullish sentiment; below 50 suggest bearish.
- Parameters:
- Returns:
Psychological Line values in [0, 100].
- Return type:
Example
>>> result = psychological_line(close, period=12)
Volume Indicators¶
Volume-confirmed signals and accumulation/distribution studies.
Volume-based indicators.
This module provides indicators that incorporate volume data to measure
buying/selling pressure and trend confirmation. All functions accept
pd.Series inputs and return pd.Series.
- obv(close, volume)[source]¶
On Balance Volume (OBV).
OBV is a cumulative running total of volume. Volume is added on up-close days and subtracted on down-close days.
- Interpretation:
Rising OBV: Volume is flowing in (accumulation) – bullish.
Falling OBV: Volume is flowing out (distribution) – bearish.
Divergence: The most important signal. Price makes a new high but OBV does not = smart money is not confirming the move (bearish divergence). Price makes a new low but OBV does not = accumulation is occurring (bullish divergence).
Breakout confirmation: OBV breaking to a new high alongside price confirms the breakout is genuine.
- Trading rules:
Buy when OBV diverges bullishly from price (price falls, OBV holds or rises).
Sell when OBV diverges bearishly from price (price rises, OBV flattens or falls).
Use OBV trend (rising/falling) to confirm price trends.
- vwap(high, low, close, volume)[source]¶
Volume Weighted Average Price (VWAP).
Computed as the cumulative sum of
typical_price * volumedivided by cumulative volume. This is the intraday running VWAP; for session-reset VWAP, pre-group your data by session.- Interpretation:
Price above VWAP: Buyers are in control; longs entered at good prices relative to the average fill.
Price below VWAP: Sellers are in control.
VWAP as support/resistance: Institutional traders use VWAP as a benchmark. Price tends to gravitate toward VWAP.
Mean reversion: Extreme deviations from VWAP tend to revert, especially intraday.
- Trading rules:
Buy pullbacks to VWAP in an uptrend (institutional support).
Sell rallies to VWAP in a downtrend (institutional resistance).
Institutions aim to buy below VWAP and sell above it; track whether your fills are better than VWAP.
- ad_line(high, low, close, volume)[source]¶
Accumulation/Distribution Line (AD Line).
Uses the Close Location Value (CLV) money flow multiplier.
- Interpretation:
Rising AD line: Accumulation – buying pressure dominates. Volume is flowing into the asset.
Falling AD line: Distribution – selling pressure dominates.
Divergence: Price makes new high but AD does not = distribution despite rising prices (bearish). Price makes new low but AD does not = accumulation (bullish).
Unlike OBV, the AD line considers where the close is within the high-low range, not just the direction of the close.
- cmf(high, low, close, volume, period=20)[source]¶
Chaikin Money Flow (CMF).
Measures money flow volume over a rolling period, showing whether buying or selling pressure is dominant.
- Interpretation:
Positive (> 0): Buying pressure (accumulation). The higher the value, the stronger the buying.
Negative (< 0): Selling pressure (distribution).
> +0.25: Strong buying pressure.
< -0.25: Strong selling pressure.
Zero-line crossover: Shift from accumulation to distribution or vice versa.
Divergence: Price makes new high but CMF is falling = distribution.
- Trading rules:
Buy when CMF crosses above zero (accumulation starting).
Sell when CMF crosses below zero (distribution starting).
Confirm breakouts: price breaking resistance with positive CMF is more reliable.
- mfi(high, low, close, volume, period=14)[source]¶
Money Flow Index (MFI).
Often called the volume-weighted RSI. Incorporates both price and volume to identify overbought/oversold conditions.
- Interpretation:
> 80: Overbought – high buying pressure, potential reversal.
< 20: Oversold – high selling pressure, potential bounce.
Divergence: Price makes new high but MFI does not = weakening volume-confirmed momentum (bearish).
More reliable than RSI alone because it includes volume: a price move on heavy volume is more meaningful.
- Trading rules:
Buy when MFI crosses above 20 (leaving oversold).
Sell when MFI crosses below 80 (leaving overbought).
MFI divergence with price is a strong reversal signal.
- eom(high, low, volume, period=14)[source]¶
Ease of Movement (EMV / EOM).
Relates price change to volume, showing how easily price is moving.
- Interpretation:
Positive: Price is advancing on relatively low volume (easy movement up) = bullish.
Negative: Price is declining on relatively low volume (easy movement down) = bearish.
Near zero: Price movement requires substantial volume = indecision or strong resistance/support.
Zero-line crossover: Shift from easy upward to easy downward movement or vice versa.
- force_index(close, volume, period=13)[source]¶
Force Index.
Force = close.diff() * volume, then smoothed with EMA.Combines price change and volume to measure the strength behind a move.
- Interpretation:
Positive: Buying force – price is rising with volume.
Negative: Selling force – price is falling with volume.
Magnitude: Larger values = stronger force behind the move.
Zero-line crossover: Shift from buying to selling force.
Divergence: Price makes new high but Force Index is declining = momentum weakening.
- Trading rules:
Buy when Force Index crosses above zero in an uptrend (pullback entry).
Sell when Force Index crosses below zero in a downtrend.
Use short period (2) for entries, longer period (13) for trend confirmation.
- nvi(close, volume)[source]¶
Negative Volume Index (NVI).
NVI focuses on days when volume decreases; the assumption is that smart money is active on low-volume days.
- Interpretation:
Rising NVI: Smart money is buying on quiet days.
Falling NVI: Smart money is selling on quiet days.
NVI above its 255-day MA: Bull market (historically correct ~96% of the time according to Norman Fosback).
NVI below its 255-day MA: Bear market.
Compare with PVI to distinguish smart vs. crowd behavior.
- pvi(close, volume)[source]¶
Positive Volume Index (PVI).
PVI focuses on days when volume increases; the assumption is that the crowd follows price on high-volume days.
- Interpretation:
Rising PVI: The crowd is buying on high-volume days.
Falling PVI: The crowd is selling on high-volume days.
PVI below its 255-day MA: Bearish sign (the crowd is pushing prices down on active days).
PVI tends to track what the public/retail traders are doing; NVI tracks institutional/smart money behavior.
- vpt(close, volume)[source]¶
Volume Price Trend (VPT).
VPT = cumsum(volume * pct_change(close))- Interpretation:
Rising VPT: Volume is confirming the price trend (bullish).
Falling VPT: Volume is working against the price trend.
Divergence: Price rises but VPT falls = distribution.
- adosc(high, low, close, volume, fast=3, slow=10)[source]¶
Accumulation/Distribution Oscillator (Chaikin Oscillator).
ADOSC = EMA(AD, fast) - EMA(AD, slow)- Interpretation:
Positive: Short-term accumulation exceeds long-term = money is flowing in.
Negative: Short-term distribution exceeds long-term = money is flowing out.
Zero-line crossover: Buy when crossing above zero, sell when crossing below.
Divergence: Price makes new high but oscillator falls = distribution.
- vwma(close, volume, period=20)[source]¶
Volume Weighted Moving Average (VWMA).
A simple moving average weighted by volume. When volume is uniform this reduces to the standard SMA.
VWMA = SUM(close * volume, period) / SUM(volume, period)- Interpretation:
Price above VWMA: Bullish – volume-confirmed uptrend.
Price below VWMA: Bearish – volume-confirmed downtrend.
VWMA vs SMA: When VWMA > SMA, heavy volume is occurring at higher prices (accumulation). When VWMA < SMA, heavy volume is at lower prices (distribution).
More responsive to volume spikes than a standard SMA.
- Parameters:
- Returns:
VWMA values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 13, 14.0]) >>> volume = pd.Series([100, 100, 100, 100, 100.0]) >>> vwma(close, volume, period=3) # equals SMA when volume is uniform
- pvt(close, volume)[source]¶
Price Volume Trend (PVT).
A cumulative indicator that adds a portion of volume proportional to the percentage change in close price.
PVT = cumsum(pct_change(close) * volume)This is functionally equivalent to
vpt()but is provided as the commonly-used PVT alias.- Parameters:
- Returns:
PVT values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12.0]) >>> volume = pd.Series([100, 200, 300.0]) >>> pvt(close, volume)
- vpt_smoothed(close, volume, period=14)[source]¶
Volume Price Trend with EMA smoothing.
Applies an EMA to the raw VPT line to reduce noise.
VPT_SMOOTHED = EMA(cumsum(pct_change(close) * volume), period)- Parameters:
- Returns:
Smoothed VPT values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 13, 14.0]) >>> volume = pd.Series([100, 200, 300, 400, 500.0]) >>> vpt_smoothed(close, volume, period=3)
- klinger(high, low, close, volume, fast=34, slow=55, signal=13)[source]¶
Klinger Volume Oscillator (KVO).
Uses the relationship between price trend direction and volume to predict price reversals.
- Interpretation:
KVO above zero: Net volume flow is bullish (accumulation).
KVO below zero: Net volume flow is bearish (distribution).
Signal line crossover: KVO crossing above signal = buy; crossing below = sell.
Divergence: Price at new high but KVO declining = money flowing out despite rising prices (distribution).
- Trading rules:
Buy when KVO crosses above its signal line.
Sell when KVO crosses below its signal line.
Best for confirming price breakouts with volume support.
trend = sign(hlc3 - hlc3.shift(1))dm = high - lowcm = cumulative dm when trend unchanged, else dmvf = volume * abs(2 * dm / cm - 1) * trend * 100KVO = EMA(vf, fast) - EMA(vf, slow)signal_line = EMA(KVO, signal)- Parameters:
- Returns:
{"kvo": <pd.Series>, "signal": <pd.Series>}- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(0) >>> n = 100 >>> close = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> high = close + 0.5 >>> low = close - 0.5 >>> volume = pd.Series(np.random.randint(1000, 5000, n).astype(float)) >>> result = klinger(high, low, close, volume) >>> result["kvo"].name 'kvo'
- taker_buy_ratio(buy_volume, total_volume)[source]¶
Taker Buy Ratio.
A helper for exchange-level data that computes the fraction of volume coming from taker buys.
ratio = buy_volume / total_volumeValues above 0.5 indicate net buying pressure; below 0.5 indicate net selling pressure.
- Parameters:
- Returns:
Ratio in [0, 1] (NaN where total_volume is 0).
- Return type:
Example
>>> import pandas as pd >>> buy = pd.Series([50, 60, 40.0]) >>> total = pd.Series([100, 100, 100.0]) >>> taker_buy_ratio(buy, total)
- elder_force(close, volume, period=2)[source]¶
Elder’s Force Index (EMA-smoothed variant).
This is Elder’s original formulation using a short EMA (default 2). For a longer-term variant, increase period.
- Interpretation:
Positive: Buying force (price rising with volume).
Negative: Selling force (price falling with volume).
2-period: Use for short-term entry timing within a trend. Buy dips to zero or slightly negative in an uptrend.
13-period: Use for trend direction confirmation.
Divergence: Price new high but Force declining = weakening.
raw = close.diff() * volumeelder_force = EMA(raw, period)- Parameters:
- Returns:
Smoothed Elder Force Index.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 13.0]) >>> volume = pd.Series([100, 200, 150, 300, 250.0]) >>> elder_force(close, volume, period=2)
- volume_profile(close, volume, bins=10)[source]¶
Volume Profile (volume at price).
Distributes volume into bins equally-spaced price buckets. This is useful for identifying high-volume nodes (support/resistance) and low-volume nodes (price gaps).
- Interpretation:
High-volume nodes (HVN): Price levels where significant trading occurred = strong support/resistance. Price tends to consolidate at these levels.
Low-volume nodes (LVN): Price levels with little trading = price tends to move quickly through these zones.
Point of control (POC): The price level with the highest volume = strongest S/R level.
Value area: The range containing ~70% of volume = fair value zone.
- Parameters:
- Returns:
{"price_bins": <pd.Series of bin labels>, "volume": <pd.Series of aggregated volume>}- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10.0]) >>> volume = pd.Series([100, 200, 300, 200, 100.0]) >>> vp = volume_profile(close, volume, bins=3) >>> len(vp["volume"]) 3
- accumulation_distribution_oscillator(high, low, close, volume, fast=3, slow=10)[source]¶
Accumulation/Distribution Oscillator.
Computes the difference between a fast and slow EMA of the Accumulation/Distribution line. This is equivalent to the Chaikin Oscillator (
adosc()) but provided under its full descriptive name.AD_OSC = EMA(AD, fast) - EMA(AD, slow)- Parameters:
- Returns:
Oscillator values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(0) >>> n = 50 >>> close = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> high = close + 0.5 >>> low = close - 0.5 >>> volume = pd.Series(np.random.randint(1000, 5000, n).astype(float)) >>> accumulation_distribution_oscillator(high, low, close, volume)
- volume_roc(volume, period=14)[source]¶
Volume Rate of Change (Volume ROC).
Measures the percentage change in volume over a given period.
- Interpretation:
Large positive: Volume is surging compared to period bars ago = heightened interest, possible breakout.
Large negative: Volume is drying up = decreasing interest.
Volume spike with price breakout: Confirms the breakout.
Volume spike without price movement: Potential distribution or accumulation before a move.
volume_roc = (volume - volume.shift(period)) / volume.shift(period) * 100- Parameters:
- Returns:
Volume ROC as a percentage.
- Return type:
Example
>>> import pandas as pd >>> volume = pd.Series([100, 120, 110, 130, 140.0]) >>> volume_roc(volume, period=2)
- positive_volume_index(close, volume)[source]¶
Positive Volume Index (PVI).
A descriptive-name alias for
pvi(). PVI focuses on days when volume increases relative to the prior day.- Parameters:
- Returns:
PVI values (starts at 1000).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 10, 12, 11.0]) >>> volume = pd.Series([100, 200, 150, 300, 100.0]) >>> positive_volume_index(close, volume).iloc[0] 1000.0
Trend Indicators¶
Trend direction, strength, and adaptive moving averages.
Trend indicators.
This module provides indicators that identify and measure the direction
and strength of price trends. All functions accept pd.Series inputs
and return pd.Series (or dict[str, pd.Series] for multi-output
indicators).
- adx(high, low, close, period=14)[source]¶
Average Directional Index (ADX) with +DI and -DI.
Measures the strength of a trend regardless of its direction. +DI and -DI show the direction.
- Interpretation:
ADX > 25: Trend is strong enough to trade.
ADX > 50: Very strong trend.
ADX < 20: No trend (range-bound / choppy).
ADX rising: Trend is strengthening.
ADX falling: Trend is weakening (not necessarily reversing).
+DI > -DI: Uptrend (buyers dominate).
-DI > +DI: Downtrend (sellers dominate).
+DI/-DI crossover: Trend direction change.
- Trading rules:
Buy when +DI crosses above -DI AND ADX > 25 (trending up).
Sell when -DI crosses above +DI AND ADX > 25 (trending down).
Avoid trading when ADX < 20 (no trend).
Use ADX as a filter: only apply trend-following strategies when ADX > 25; use mean-reversion when ADX < 20.
- aroon(high, low, period=25)[source]¶
Aroon Indicator.
Measures the time since the last high/low to identify trend direction and strength.
- Interpretation:
Aroon Up > 70: Strong uptrend (recent new highs).
Aroon Down > 70: Strong downtrend (recent new lows).
Aroon Up < 30: Weak uptrend (no recent new highs).
Aroon Down < 30: Weak downtrend (no recent new lows).
Aroon Up crosses above Aroon Down: New uptrend starting.
Aroon Down crosses above Aroon Up: New downtrend starting.
Both below 50: Consolidation / no trend.
Oscillator: Positive = uptrend, negative = downtrend.
- Trading rules:
Buy when Aroon Up crosses above Aroon Down.
Sell when Aroon Down crosses above Aroon Up.
Strongest signal when one Aroon is above 70 and the other is below 30.
- psar(high, low, close, af_start=0.02, af_step=0.02, af_max=0.2)[source]¶
Parabolic SAR (Stop and Reverse).
A trend-following indicator that provides entry/exit points and trailing stop levels.
- Interpretation:
SAR dots below price: Uptrend. The SAR value is a trailing stop / support level.
SAR dots above price: Downtrend. The SAR value is a trailing stop / resistance level.
SAR flip (below to above): Sell signal – trend has reversed from bullish to bearish.
SAR flip (above to below): Buy signal – trend has reversed from bearish to bullish.
- Trading rules:
Buy when SAR flips below price (dots move under candles).
Sell when SAR flips above price (dots move above candles).
Use SAR values as trailing stop-loss levels.
Works best in trending markets; generates many false signals in ranging markets. Combine with ADX to filter.
- Parameters:
- Returns:
Parabolic SAR values. Values above price indicate downtrend; values below price indicate uptrend.
- Return type:
- vortex(high, low, close, period=14)[source]¶
Vortex Indicator.
Captures positive and negative trend movement by comparing the distance between current high/low and previous low/high.
- Interpretation:
+VI > -VI: Uptrend – positive vortex movement dominates.
-VI > +VI: Downtrend – negative vortex movement dominates.
+VI crosses above -VI: Bullish trend change signal.
-VI crosses above +VI: Bearish trend change signal.
Both near 1.0: Trend is neutral or transitioning.
- Trading rules:
Buy when +VI crosses above -VI.
Sell when -VI crosses above +VI.
Use a threshold (e.g. 1.1) to filter weak crossovers.
- trix(data, period=15)[source]¶
TRIX – Triple-smoothed EMA rate of change.
TRIX = 100 * ROC(EMA(EMA(EMA(data))))Filters out insignificant price movements by triple-smoothing before computing the rate of change.
- Interpretation:
Above zero: Bullish momentum (triple-smoothed trend up).
Below zero: Bearish momentum.
Zero-line crossover: Buy signal when crossing above zero; sell when crossing below.
Signal line: Often paired with a signal EMA for crossover signals.
Extremely smooth; good for identifying major trend changes but lags significantly.
- Trading rules:
Buy when TRIX crosses above zero.
Sell when TRIX crosses below zero.
Use for long-term trend identification, not short-term timing.
- linear_regression(data, period=14)[source]¶
Rolling Linear Regression.
Fits an OLS regression line to the last period values at each bar.
- Interpretation:
Positive slope: Price trend is up over the window.
Negative slope: Price trend is down.
R-squared near 1: Price is moving in a clean line (strong trend with low noise).
R-squared near 0: No linear trend (choppy/range-bound).
Value: The regression endpoint acts as a smoothed trend line with less lag than a moving average.
- Trading rules:
Trade in the direction of the slope when R-squared > 0.5.
Avoid trend-following trades when R-squared < 0.2.
- linear_regression_slope(data, period=14)[source]¶
Rolling Linear Regression Slope.
A convenience wrapper around
linear_regression()that returns only the slope component.- Interpretation:
Positive: Uptrend over the look-back period.
Negative: Downtrend.
Magnitude: Steeper slope = stronger trend.
Zero crossover: Trend direction change.
- zigzag(close, pct_change=5.0)[source]¶
ZigZag indicator – connects swing highs and lows.
Identifies pivots where price reverses by at least pct_change percent, then linearly interpolates between them.
- Interpretation:
Filters out noise to reveal the true swing structure of price.
Not a trading signal: The last segment repaints as new data arrives. Use only for historical analysis.
Swing counting: Measure the distance between pivots for wave analysis (Elliott Wave, harmonic patterns).
Support/resistance: Pivot levels often act as future S/R.
Useful for backtesting swing-trading strategies.
- Parameters:
- Returns:
ZigZag line (interpolated between pivots). Non-pivot bars are filled via linear interpolation; leading/trailing NaNs remain where no pivot has been established.
- Return type:
Example
>>> import pandas as pd >>> zz = zigzag(pd.Series([100, 110, 105, 95, 100, 90]), pct_change=5.0)
- heikin_ashi(open_, high, low, close)[source]¶
Heikin-Ashi modified OHLC candles.
Modified candlesticks that smooth price action and make trends easier to identify.
- Interpretation:
Green HA candles with no lower shadow: Strong uptrend.
Red HA candles with no upper shadow: Strong downtrend.
Small body with long shadows: Indecision / potential reversal.
Color change: Possible trend reversal (green to red or vice versa).
HA candles smooth noise, making it easier to stay in trends and avoid premature exits.
- Trading rules:
Stay long as long as HA candles remain green.
Stay short as long as HA candles remain red.
Watch for doji-like HA candles as reversal warnings.
Note: HA prices are synthetic – do not use them for actual order placement. Use regular prices for entries/exits.
- Parameters:
- Returns:
ha_open,ha_high,ha_low,ha_close.- Return type:
Example
>>> import pandas as pd >>> ha = heikin_ashi( ... pd.Series([100, 102]), ... pd.Series([105, 106]), ... pd.Series([99, 101]), ... pd.Series([104, 103]), ... )
- mcginley_dynamic(data, period=14)[source]¶
McGinley Dynamic – adaptive moving average.
Adjusts its speed based on market velocity, reducing whipsaws compared to a standard EMA.
- Interpretation:
Price above McGinley: Bullish trend.
Price below McGinley: Bearish trend.
Automatically speeds up in fast markets and slows down in slow markets, reducing false crossovers.
Acts as a more reliable dynamic support/resistance than traditional moving averages.
MD_t = MD_{t-1} + (price - MD_{t-1}) / (N * (price / MD_{t-1})^4)- Parameters:
- Returns:
McGinley Dynamic values.
- Return type:
Example
>>> import pandas as pd >>> md = mcginley_dynamic(pd.Series([100, 102, 101, 103, 105]))
- schaff_trend_cycle(close, period=10, fast=23, slow=50)[source]¶
Schaff Trend Cycle – MACD passed through a double stochastic.
Combines the MACD histogram with stochastic smoothing for a faster, smoother oscillator bounded between 0 and 100.
- Interpretation:
> 75: Bullish trend established.
< 25: Bearish trend established.
Crosses above 25: Buy signal (bearish-to-bullish shift).
Crosses below 75: Sell signal (bullish-to-bearish shift).
Faster than MACD because of the double stochastic processing.
Spends most of its time at extremes (near 0 or 100), with quick transitions between them.
- Parameters:
- Returns:
STC values in [0, 100].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> stc = schaff_trend_cycle(pd.Series(np.random.randn(100).cumsum() + 100))
- guppy_mma(data)[source]¶
Guppy Multiple Moving Average (GMMA).
Developed by Daryl Guppy. Uses two groups of EMAs to visualize the behavior of both traders (short-term) and investors (long-term).
- Interpretation:
Short-term group fanning out above long-term group: Strong uptrend with trader and investor agreement.
Short-term group fanning out below long-term group: Strong downtrend.
Groups compressing: Trend weakening, consolidation.
Short-term crossing long-term: Trend change signal.
Wide separation between groups: Strong conviction.
Groups intertwined/overlapping: Indecision, no trend.
Returns two groups of EMAs:
Short-term group: 3, 5, 8, 10, 12, 15
Long-term group: 30, 35, 40, 45, 50, 60
- Parameters:
data (
Series) – Price series (typically close).- Returns:
Keys
short_3,short_5, …,short_15,long_30,long_35, …,long_60.- Return type:
Example
>>> import pandas as pd, numpy as np >>> g = guppy_mma(pd.Series(np.random.randn(100).cumsum() + 100))
- rainbow_ma(data, period=10, levels=10)[source]¶
Rainbow Moving Average – recursive SMAs.
Each level is an SMA of the previous level. Level 1 is SMA of data, level 2 is SMA of level 1, and so on.
- Interpretation:
Price above all bands: Strong uptrend.
Price below all bands: Strong downtrend.
Bands fanning out: Trend strengthening.
Bands compressing: Trend weakening or consolidation.
Price touching inner bands then bouncing: Pullback buy/sell opportunity in the direction of the trend.
- Parameters:
- Returns:
Keys
sma_1throughsma_{levels}.- Return type:
Example
>>> import pandas as pd, numpy as np >>> rb = rainbow_ma(pd.Series(np.random.randn(100).cumsum() + 100))
- hull_ma(data, period=16)[source]¶
Hull Moving Average (HMA).
HMA = WMA(2 * WMA(n/2) - WMA(n), sqrt(n))Provides a fast, smooth moving average with reduced lag.
- Interpretation:
Price above HMA: Bullish.
Price below HMA: Bearish.
HMA slope change: Early trend change signal. HMA turning up = bullish; turning down = bearish.
Extremely responsive due to the sqrt(n) final smoothing; excellent for short-term trend following.
- Parameters:
- Returns:
Hull Moving Average values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> hma = hull_ma(pd.Series(np.random.randn(100).cumsum() + 100), period=16)
- zero_lag_ema(data, period=21)[source]¶
Zero-Lag Exponential Moving Average (ZLEMA).
Compensates for inherent EMA lag by applying the EMA to a de-lagged series:
zlema_input = data + (data - data.shift(lag))wherelag = (period - 1) / 2.- Interpretation:
Same signals as EMA but with near-zero lag.
More responsive to recent price changes, giving earlier crossover signals.
Can overshoot in choppy markets due to the de-lagging adjustment.
- Parameters:
- Returns:
ZLEMA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> zl = zero_lag_ema(pd.Series(np.random.randn(100).cumsum() + 100))
- vidya(data, period=14, smooth=5)[source]¶
Variable Index Dynamic Average (VIDYA).
Uses the Chande Momentum Oscillator (CMO) as a volatility index to dynamically adjust the smoothing constant of an EMA.
- Interpretation:
Behaves like a fast EMA in trending markets (high CMO) and a slow EMA in ranging markets (low CMO).
Price above VIDYA: Bullish trend.
Price below VIDYA: Bearish trend.
Less prone to whipsaws than standard EMA in choppy markets.
VIDYA_t = alpha * |CMO_t| * price_t + (1 - alpha * |CMO_t|) * VIDYA_{t-1}- Parameters:
- Returns:
VIDYA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> v = vidya(pd.Series(np.random.randn(100).cumsum() + 100))
- tilson_t3(data, period=5, volume_factor=0.7)[source]¶
Tilson T3 – triple-smoothed exponential moving average.
Applies six sequential EMAs with Tilson coefficients derived from the volume_factor to produce an ultra-smooth, low-lag average.
- Interpretation:
Extremely smooth trend line with less lag than TEMA.
Price above T3: Bullish.
Price below T3: Bearish.
T3 slope change: Very reliable trend change signal due to the heavy smoothing.
Best for medium to long-term trend identification.
- Parameters:
- Returns:
Tilson T3 values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> t3 = tilson_t3(pd.Series(np.random.randn(100).cumsum() + 100))
- fractal_adaptive_ma(data, period=16)[source]¶
Fractal Adaptive Moving Average (FRAMA).
Uses the fractal dimension of the price series to dynamically adjust the EMA smoothing factor. More responsive in trending markets and slower during consolidation.
- Interpretation:
Price above FRAMA: Bullish trend.
Price below FRAMA: Bearish trend.
Adapts automatically: becomes responsive (like short EMA) in trending markets and sluggish (like long EMA) during consolidation.
Excellent for trend following with automatic noise filtering.
- Parameters:
- Returns:
FRAMA values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> f = fractal_adaptive_ma(pd.Series(np.random.randn(200).cumsum() + 100))
Volatility Indicators¶
ATR, Bollinger Width, and OHLC-based volatility estimators.
Volatility indicators.
This module provides indicators that measure the degree of price
variation over time. All functions accept pd.Series inputs and
return pd.Series.
- atr(high, low, close, period=14)[source]¶
Average True Range (ATR).
Uses the Wilder smoothing method (
ewm(alpha=1/period)). The standard measure of market volatility.- Interpretation:
Higher ATR: More volatile market – wider price swings.
Lower ATR: Less volatile – tight price action.
Rising ATR: Volatility is increasing (often at trend beginnings or during strong moves).
Falling ATR: Volatility is decreasing (often during consolidation, before a breakout).
ATR does not indicate direction, only the magnitude of price movement.
- Trading rules:
Stop placement: Set stop-loss at 2x or 3x ATR from entry to account for normal market noise.
Position sizing: Risk a fixed dollar amount per trade; divide by ATR to determine share count.
Breakout confirmation: A breakout with rising ATR is more likely to sustain than one with falling ATR.
Trailing stop: Trail by 2-3x ATR below the highest high (for longs).
- true_range(high, low, close)[source]¶
True Range.
TR = max(H - L, |H - C_prev|, |L - C_prev|)The single-bar volatility measure that accounts for gaps.
- Interpretation:
High TR: Large price movement – high volatility bar.
Low TR: Small price movement – low volatility bar.
Spikes in TR often occur at trend changes or breakouts.
TR forms the basis of ATR and many other volatility indicators.
- natr(high, low, close, period=14)[source]¶
Normalized Average True Range (NATR).
NATR = (ATR / close) * 100- Interpretation:
Same as ATR but expressed as a percentage of price, making it comparable across different assets and price levels.
Higher NATR: More volatile (in percentage terms).
Lower NATR: Less volatile.
Useful for ranking assets by volatility or for building volatility-weighted portfolios.
- bbwidth(data, period=20, std_dev=2.0)[source]¶
Bollinger Band Width.
BBWidth = (upper - lower) / middle- Interpretation:
Low BBWidth (squeeze): Bollinger Bands are narrow – volatility is low. A breakout is imminent. This is the key signal: low volatility precedes high volatility.
High BBWidth: Bollinger Bands are wide – volatility is high. The move may be overextended.
BBWidth at 6-month low: Strong squeeze – prepare for a significant breakout.
BBWidth expanding: Breakout in progress.
- Trading rules:
Look for BBWidth at historical lows (squeeze).
When the squeeze releases (BBWidth starts expanding), trade the breakout direction.
Combine with momentum indicators to determine direction.
- kc_width(high, low, close, period=20, multiplier=1.5)[source]¶
Keltner Channel Width.
KC_Width = (upper - lower) / middle- Interpretation:
Same concept as BBWidth but based on ATR instead of standard deviation.
Narrow KC Width: Low ATR volatility, potential squeeze.
Wide KC Width: High ATR volatility, extended move.
Used with BBWidth for the TTM Squeeze: when BB is inside KC, a squeeze is active.
- chaikin_volatility(high, low, period=10, smoothing=10)[source]¶
Chaikin Volatility.
Measures the rate of change of the EMA of the high-low spread.
- Interpretation:
Rising: Volatility is increasing (range is expanding).
Falling: Volatility is decreasing (range is contracting).
Spike up: Can indicate a market top (panic/climax).
Spike down: Can indicate a market bottom (capitulation followed by quiet).
- historical_volatility(data, period=21, annualize=True)[source]¶
Historical Volatility (close-to-close).
Computes the rolling standard deviation of log returns. When annualize is
Truethe result is scaled bysqrt(252).- Interpretation:
High HV (e.g. > 30% annualized for equities): Asset is highly volatile. Wider stops and smaller positions needed.
Low HV (e.g. < 15% annualized): Asset is calm. Tighter stops and larger positions possible.
HV vs Implied Volatility: If HV < IV, options are relatively expensive (sell premium). If HV > IV, options are cheap (buy premium).
Rising HV: Uncertainty increasing. Often accompanies selloffs.
Falling HV: Market calming down. Often accompanies gradual rallies.
- mass_index(high, low, period=9, trigger=25)[source]¶
Mass Index.
The Mass Index uses the high-low range to identify trend reversals based on range expansions. It accumulates the ratio of two EMAs of the range over the trigger period.
- Interpretation:
Reversal bulge: The key signal. When Mass Index rises above 27 and then falls back below 26.5, a trend reversal is likely (regardless of direction).
The indicator does not tell you the direction of the reversal, only that one is coming.
Combine with a trend indicator to determine which direction the reversal will take.
- Trading rules:
When Mass Index crosses above 27 then back below 26.5 (reversal bulge), prepare for a trend change.
Use a 9-period EMA crossover or similar to determine the new trend direction.
- Parameters:
- Returns:
Mass Index values. A reversal bulge occurs when the index rises above 27 and then falls below 26.5.
- Return type:
- garman_klass(high, low, close, open_, period=21, annualize=True)[source]¶
Garman-Klass volatility estimator.
An efficient OHLC volatility estimator that uses open, high, low, and close prices. More efficient than close-to-close because it uses intraday range information.
- Interpretation:
Values are directly comparable to historical volatility.
Higher efficiency: Uses the same data as close-to-close but extracts more information, producing tighter estimates.
Compare with Parkinson and Yang-Zhang to assess which estimator best suits your data.
Does not handle overnight gaps well; use Yang-Zhang for assets with significant overnight risk.
GK = sqrt((1/n) * sum(0.5*(ln(H/L))^2 - (2*ln(2)-1)*(ln(C/O))^2))- Parameters:
- Returns:
Garman-Klass volatility estimate.
- Return type:
Example
>>> gk = garman_klass(high, low, close, open_, period=21)
- parkinson(high, low, period=21, annualize=True)[source]¶
Parkinson volatility estimator.
Uses the high-low range to estimate volatility, which is more efficient than close-to-close since it captures intraday extremes.
- Interpretation:
Approximately 5x more efficient than close-to-close.
Tends to underestimate true volatility when there are overnight gaps (since it ignores open/close).
Best suited for assets that trade continuously (e.g. forex).
Parkinson = sqrt((1 / (4 * n * ln(2))) * sum((ln(H/L))^2))- Parameters:
- Returns:
Parkinson volatility estimate.
- Return type:
Example
>>> pk = parkinson(high, low, period=21)
- rogers_satchell(high, low, close, open_, period=21, annualize=True)[source]¶
Rogers-Satchell volatility estimator.
Accounts for non-zero drift (trending markets), making it more robust than Parkinson or Garman-Klass for trending assets.
- Interpretation:
Better than Garman-Klass for assets with strong trends, because it does not assume zero drift.
Still does not handle overnight gaps; for that, use Yang-Zhang.
RS = sqrt((1/n) * sum(ln(H/C)*ln(H/O) + ln(L/C)*ln(L/O)))- Parameters:
- Returns:
Rogers-Satchell volatility estimate.
- Return type:
Example
>>> rs = rogers_satchell(high, low, close, open_, period=21)
- yang_zhang(high, low, close, open_, period=21, annualize=True)[source]¶
Yang-Zhang volatility estimator.
The most efficient OHLC volatility estimator. Combines overnight (close-to-open) volatility, open-to-close volatility, and the Rogers-Satchell estimator.
- Interpretation:
The gold standard for OHLC volatility estimation.
Handles both overnight gaps and intraday drift.
Use this as the default volatility estimator when you have full OHLC data.
Compare with close-to-close: if Yang-Zhang is significantly higher, overnight/gap risk is material.
- Parameters:
- Returns:
Yang-Zhang volatility estimate.
- Return type:
Example
>>> yz = yang_zhang(high, low, close, open_, period=21)
- close_to_close(data, period=21, annualize=True)[source]¶
Close-to-close volatility (standard deviation of log returns).
The simplest volatility estimator based on daily log returns. This is equivalent to
historical_volatility()but named explicitly to distinguish it from range-based estimators.- Interpretation:
The baseline volatility measure. All other estimators (Parkinson, Garman-Klass, Yang-Zhang) should be compared against this.
See
historical_volatility()for full interpretation.
- Parameters:
- Returns:
Close-to-close volatility.
- Return type:
Example
>>> cc = close_to_close(close, period=21)
- ulcer_index(data, period=14)[source]¶
Ulcer Index.
Measures downside volatility by computing the quadratic mean of percentage drawdowns from the rolling maximum over the given period. Higher values indicate greater drawdown depth and duration.
- Interpretation:
Low values (< 5): Stable asset with shallow drawdowns.
High values (> 10): Asset is experiencing significant drawdowns.
Unlike standard deviation, only measures downside risk.
Used in the Martin Ratio (return / Ulcer Index) as a risk-adjusted performance metric.
Rising Ulcer Index = drawdowns are deepening = trouble.
UI = sqrt(mean(R^2))whereR = 100 * (C - max(C, n)) / max(C, n)- Parameters:
- Returns:
Ulcer Index values (always >= 0).
- Return type:
Example
>>> ui = ulcer_index(close, period=14)
- relative_volatility_index(data, period=10, smoothing=14)[source]¶
Relative Volatility Index (RVI).
Applies the RSI formula to the rolling standard deviation of closes rather than to price changes.
- Interpretation:
> 50: Volatility is increasing (standard deviation is rising) – directional moves are more likely.
< 50: Volatility is decreasing (standard deviation is falling) – consolidation / range-bound.
Not a standalone indicator; best used as a filter.
- Trading rules:
Confirm RSI signals: only take RSI buy signals when RVI > 50.
Avoid breakout trades when RVI < 50 (low volatility = false breakout risk).
- Parameters:
- Returns:
RVI values oscillating between 0 and 100.
- Return type:
Example
>>> rvi = relative_volatility_index(close, period=10, smoothing=14)
- acceleration_bands(high, low, close, period=20, factor=0.001)[source]¶
Acceleration Bands.
Bands that widen with high-low range acceleration, narrowing during consolidation. Uses
factor * (high - low) / (high + low)as the width multiplier.- Interpretation:
Price above upper band: Strong uptrend / breakout.
Price below lower band: Strong downtrend / breakdown.
Bands narrowing: Consolidation – breakout imminent.
Bands widening: Trend accelerating.
Similar concept to Bollinger Bands but based on range acceleration rather than standard deviation.
- Parameters:
- Returns:
Dictionary with keys
"upper","middle","lower".- Return type:
Example
>>> bands = acceleration_bands(high, low, close, period=20) >>> upper = bands["upper"]
- standard_deviation(data, period=20)[source]¶
Rolling standard deviation.
Computes the rolling sample standard deviation over the given period.
- Interpretation:
Rising: Volatility increasing – larger price swings.
Falling: Volatility decreasing – tighter price action.
Low standard deviation often precedes a breakout.
Used to compute Bollinger Bands (middle +/- N * std_dev).
- Parameters:
- Returns:
Rolling standard deviation values.
- Return type:
Example
>>> sd = standard_deviation(close, period=20)
- variance(data, period=20)[source]¶
Rolling variance.
Computes the rolling sample variance over the given period.
- Interpretation:
The square of standard deviation. Same directional interpretation as standard deviation.
Useful in mathematical contexts where variance is preferred (e.g. portfolio optimization, risk decomposition).
- Parameters:
- Returns:
Rolling variance values.
- Return type:
Example
>>> v = variance(close, period=20)
Pattern Recognition¶
38 candlestick patterns returning boolean match Series.
Candlestick pattern recognition.
Each function returns a pd.Series of integers:
1 — bullish signal
-1 — bearish signal
0 — no pattern detected
Some patterns are inherently one-directional (e.g. Morning Star is always bullish), but where a pattern has both bullish and bearish variants the sign distinguishes them.
- doji(open_, high, low, close, threshold=0.05)[source]¶
Doji pattern.
A Doji occurs when the body is very small relative to the total range.
- Interpretation:
Indecision: Open and close are nearly equal – buyers and sellers are in balance.
At resistance: Bearish signal (potential reversal down).
At support: Bullish signal (potential reversal up).
In a trend: Warning that momentum may be fading.
Requires confirmation from the next candle – a doji alone is not a signal.
Reliability: Moderate. More significant after a strong trend.
- Parameters:
- Returns:
1 where a Doji is detected, 0 otherwise.
- Return type:
- hammer(open_, high, low, close)[source]¶
Hammer and Hanging Man pattern.
A hammer has a small body near the top and a long lower shadow (at least 2x the body). Returns 1 for bullish hammer (after a downtrend proxy: prior close < close), -1 for hanging man (after an uptrend proxy: prior close > close), 0 otherwise.
- Interpretation:
Hammer (1): Bullish reversal after a downtrend. Sellers pushed price down during the session but buyers fought back, closing near the open. The long lower shadow shows rejected selling pressure.
Hanging Man (-1): Bearish warning after an uptrend. Same shape, but context differs – suggests sellers are emerging.
Confirmation needed: Wait for the next candle to close in the reversal direction.
Reliability: High for hammers at major support levels.
- engulfing(open_, high, low, close)[source]¶
Bullish/Bearish Engulfing pattern.
- Interpretation:
Bullish engulfing (1): A small bearish candle completely engulfed by a larger bullish candle. Strong reversal signal at the bottom of a downtrend.
Bearish engulfing (-1): A small bullish candle completely engulfed by a larger bearish candle. Strong reversal signal at the top of an uptrend.
Volume confirmation strengthens the signal.
Reliability: High – one of the most reliable reversal patterns, especially at key support/resistance levels.
- morning_star(open_, high, low, close)[source]¶
Morning Star (3-candle bullish reversal).
- Interpretation:
A strong bullish reversal signal at the bottom of a downtrend.
Day 1 (large bearish): Bears are in control.
Day 2 (small body / doji): Indecision – selling pressure is exhausting.
Day 3 (large bullish): Bulls take over, closing above the midpoint of Day 1’s body.
Reliability: High – three-candle confirmation reduces false signals. Best at established support levels.
- evening_star(open_, high, low, close)[source]¶
Evening Star (3-candle bearish reversal).
- Interpretation:
The bearish counterpart of the Morning Star.
Day 1 (large bullish): Bulls are in control.
Day 2 (small body / doji): Indecision at the top.
Day 3 (large bearish): Bears take over.
Reliability: High – the mirror of Morning Star. Best at established resistance levels.
- three_white_soldiers(open_, high, low, close)[source]¶
Three White Soldiers (strong bullish continuation).
Three consecutive bullish candles, each opening within the prior body and closing at a new high.
- Interpretation:
Strong bullish signal indicating sustained buying pressure.
Each candle should have a full body (not spinning tops).
Best after a downtrend or consolidation as a reversal signal.
Reliability: Very high – three consecutive strong closes show committed buying. Weakened if upper shadows are long (advance block pattern).
- three_black_crows(open_, high, low, close)[source]¶
Three Black Crows (strong bearish continuation).
Three consecutive bearish candles, each opening within the prior body and closing at a new low.
- Interpretation:
Strong bearish signal indicating sustained selling pressure.
The bearish counterpart of Three White Soldiers.
Reliability: Very high – three consecutive strong closes downward show committed selling.
- harami(open_, high, low, close)[source]¶
Harami pattern (bullish and bearish).
The second candle’s body is entirely contained within the first candle’s body.
- Interpretation:
Bullish harami (1): A large bearish candle followed by a small bullish candle within its body. Suggests selling pressure is weakening. Reversal may follow.
Bearish harami (-1): A large bullish candle followed by a small bearish candle within its body. Suggests buying pressure is weakening.
Reliability: Moderate – requires confirmation from the following candle. Less reliable than engulfing patterns.
- spinning_top(open_, high, low, close, body_threshold=0.3)[source]¶
Spinning Top (indecision candle).
A candle with a small body relative to its range and roughly equal upper and lower shadows.
- Interpretation:
Indecision: Neither buyers nor sellers gained control.
In an uptrend: Warns that bulls are losing momentum.
In a downtrend: Warns that bears are losing momentum.
Not a reversal signal on its own – needs confirmation.
Reliability: Low as a standalone signal; moderate when appearing at key levels after a sustained trend.
- marubozu(open_, high, low, close, threshold=0.01)[source]¶
Marubozu (full-body candle with no/tiny wicks).
- Interpretation:
Bullish marubozu (1): Opens at the low and closes at the high – buyers dominated the entire session with no pushback. Very strong bullish conviction.
Bearish marubozu (-1): Opens at the high and closes at the low – sellers dominated completely.
Reliability: High – the absence of shadows shows one side had complete control. Often marks the beginning of a new trend leg.
- Parameters:
- Returns:
1 (bullish marubozu), -1 (bearish marubozu), or 0.
- Return type:
- piercing_pattern(open_, high, low, close)[source]¶
Piercing Pattern (bullish reversal).
A two-candle pattern: Day 1 is bearish, Day 2 opens below Day 1’s low and closes above the midpoint of Day 1’s body.
- Interpretation:
Bullish reversal signal at the bottom of a downtrend.
The gap down open followed by a strong close above the midpoint shows buyers stepping in aggressively.
Reliability: Moderate-high. Stronger when the close is deeper into Day 1’s body (closer to engulfing).
- Parameters:
- Returns:
1 where a Piercing Pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = piercing_pattern(open_, high, low, close)
- dark_cloud_cover(open_, high, low, close)[source]¶
Dark Cloud Cover (bearish reversal).
The bearish counterpart of the Piercing Pattern. Day 1 is bullish, Day 2 opens above Day 1’s high and closes below the midpoint of Day 1’s body.
- Interpretation:
Bearish reversal signal at the top of an uptrend.
The gap up open followed by selling down into Day 1’s body shows sellers overpowering buyers.
Reliability: Moderate-high. More significant with high volume.
- Parameters:
- Returns:
-1 where a Dark Cloud Cover is detected, 0 otherwise.
- Return type:
Example
>>> signal = dark_cloud_cover(open_, high, low, close)
- hanging_man(open_, high, low, close, trend_period=5)[source]¶
Hanging Man (bearish reversal at top).
Same hammer shape (small body near top, long lower shadow) but appears after an uptrend, signalling potential reversal.
- Interpretation:
Bearish warning signal after an uptrend. The long lower shadow shows sellers tested lower prices during the session.
Needs confirmation: a bearish close the next day confirms the reversal.
Reliability: Moderate – requires confirmation and context.
- Parameters:
- Returns:
-1 where a Hanging Man is detected, 0 otherwise.
- Return type:
Example
>>> signal = hanging_man(open_, high, low, close)
- inverted_hammer(open_, high, low, close, trend_period=5)[source]¶
Inverted Hammer (bullish reversal at bottom).
A candle with a long upper shadow (at least 2x the body) and a small lower shadow, appearing after a downtrend.
- Interpretation:
Bullish reversal signal at the bottom of a downtrend.
The long upper shadow shows buyers tested higher prices but could not hold them yet. If confirmed by next bar, buyers are gaining strength.
Reliability: Moderate – requires a bullish confirmation candle the next day.
- Parameters:
- Returns:
1 where an Inverted Hammer is detected, 0 otherwise.
- Return type:
Example
>>> signal = inverted_hammer(open_, high, low, close)
- shooting_star(open_, high, low, close, trend_period=5)[source]¶
Shooting Star (bearish reversal at top).
Same shape as inverted hammer (long upper shadow, small lower shadow) but appears after an uptrend.
- Interpretation:
Bearish reversal signal at the top of an uptrend. Buyers pushed price higher but sellers overwhelmed them, closing near the open.
The long upper shadow represents rejected higher prices.
Reliability: Moderate-high – stronger when it appears at resistance with high volume.
- Parameters:
- Returns:
-1 where a Shooting Star is detected, 0 otherwise.
- Return type:
Example
>>> signal = shooting_star(open_, high, low, close)
- tweezer_top(open_, high, low, close, tolerance=0.001)[source]¶
Tweezer Top (bearish reversal).
Two consecutive candles with nearly the same highs. The first candle is bullish and the second is bearish.
- Interpretation:
Bearish reversal at a resistance level. Both candles tested the same high and were rejected, showing strong resistance.
Reliability: Moderate – stronger at established resistance.
- Parameters:
- Returns:
-1 where a Tweezer Top is detected, 0 otherwise.
- Return type:
Example
>>> signal = tweezer_top(open_, high, low, close)
- tweezer_bottom(open_, high, low, close, tolerance=0.001)[source]¶
Tweezer Bottom (bullish reversal).
Two consecutive candles with nearly the same lows. The first candle is bearish and the second is bullish.
- Interpretation:
Bullish reversal at a support level. Both candles tested the same low and were rejected, showing strong support.
Reliability: Moderate – stronger at established support.
- Parameters:
- Returns:
1 where a Tweezer Bottom is detected, 0 otherwise.
- Return type:
Example
>>> signal = tweezer_bottom(open_, high, low, close)
- three_inside_up(open_, high, low, close)[source]¶
Three Inside Up (bullish reversal).
A three-candle pattern: bullish harami (Days 1-2) confirmed by a third bullish candle closing above Day 1’s open.
- Interpretation:
A confirmed bullish harami. The third candle provides the confirmation that a simple harami lacks.
Reliability: High – three-candle confirmation is stronger than the two-candle harami alone.
- Parameters:
- Returns:
1 where the pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = three_inside_up(open_, high, low, close)
- three_inside_down(open_, high, low, close)[source]¶
Three Inside Down (bearish reversal).
A three-candle pattern: bearish harami (Days 1-2) confirmed by a third bearish candle closing below Day 1’s open.
- Interpretation:
A confirmed bearish harami. The bearish counterpart of Three Inside Up.
Reliability: High – three-candle confirmation pattern.
- Parameters:
- Returns:
-1 where the pattern is detected, 0 otherwise.
- Return type:
Example
>>> signal = three_inside_down(open_, high, low, close)
- abandoned_baby(open_, high, low, close)[source]¶
Abandoned Baby (reversal pattern).
A rare three-candle pattern with gaps. A Doji star gaps away from the first candle and the third candle gaps in the opposite direction.
- Interpretation:
Bullish abandoned baby (1): Bearish candle, gap-down doji, gap-up bullish candle. Very strong reversal signal.
Bearish abandoned baby (-1): Bullish candle, gap-up doji, gap-down bearish candle. Very strong reversal signal.
Reliability: Very high – but extremely rare. The gap isolation of the doji shows a dramatic sentiment shift.
- Parameters:
- Returns:
1 (bullish abandoned baby), -1 (bearish abandoned baby), or 0.
- Return type:
Example
>>> signal = abandoned_baby(open_, high, low, close)
- kicking(open_, high, low, close, threshold=0.01)[source]¶
Kicking pattern.
Two consecutive marubozu candles in opposite directions with a gap between them. One of the strongest reversal signals.
- Interpretation:
Bullish kicking (1): Bearish marubozu followed by a gap-up bullish marubozu. Extremely strong reversal.
Bearish kicking (-1): Bullish marubozu followed by a gap-down bearish marubozu.
Reliability: Very high – one of the most powerful candlestick patterns. The opposing full-body candles with a gap show a complete and sudden sentiment reversal.
- Parameters:
- Returns:
1 (bullish kicking), -1 (bearish kicking), or 0.
- Return type:
Example
>>> signal = kicking(open_, high, low, close)
- belt_hold(open_, high, low, close, threshold=0.01)[source]¶
Belt Hold pattern.
A long marubozu candle that opens with a gap in the direction of the prior trend. A bullish belt hold gaps down and opens at the low; a bearish belt hold gaps up and opens at the high.
- Interpretation:
Bullish belt hold (1): Gaps down then rallies all day closing near the high – strong rejection of lower prices.
Bearish belt hold (-1): Gaps up then sells off all day.
Reliability: Moderate – the gap adds significance.
- Parameters:
- Returns:
1 (bullish belt hold), -1 (bearish belt hold), or 0.
- Return type:
Example
>>> signal = belt_hold(open_, high, low, close)
- rising_three_methods(open_, high, low, close)[source]¶
Rising Three Methods (bullish continuation).
A five-candle pattern: a long bullish candle, followed by three small bearish candles that stay within the first candle’s range, then a final long bullish candle that closes above the first candle’s close.
- Interpretation:
Bullish continuation pattern – the three small bearish candles are a rest/consolidation within the uptrend.
The final bullish candle confirms the trend resumes.
Reliability: High – five-candle confirmation shows clear trend continuation with a healthy pullback.
- Parameters:
- Return type:
- Returns:
1 where the pattern is detected, 0 otherwise.
Example
>>> signal = rising_three_methods(open_, high, low, close)
- falling_three_methods(open_, high, low, close)[source]¶
Falling Three Methods (bearish continuation).
The bearish counterpart of Rising Three Methods. A long bearish candle, three small bullish candles inside its range, then a final long bearish candle that closes below the first candle’s close.
- Interpretation:
Bearish continuation pattern – the small bullish candles are a brief consolidation within the downtrend.
Reliability: High – mirror of Rising Three Methods.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = falling_three_methods(open_, high, low, close)
- tasuki_gap(open_, high, low, close)[source]¶
Upside/Downside Tasuki Gap.
Upside (1): two bullish candles with a gap up, followed by a bearish candle that opens within the second body and closes within the gap but does not fill it.
Downside (-1): two bearish candles with a gap down, followed by a bullish candle that opens within the second body and closes within the gap but does not fill it.
- Parameters:
- Return type:
- Returns:
1 (upside Tasuki gap), -1 (downside Tasuki gap), or 0.
Example
>>> signal = tasuki_gap(open_, high, low, close)
- on_neck(open_, high, low, close, tolerance=0.001)[source]¶
On Neck pattern (bearish continuation).
A bearish candle followed by a small bullish candle that opens below the previous low and closes at or near the previous low.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = on_neck(open_, high, low, close)
- in_neck(open_, high, low, close, tolerance=0.003)[source]¶
In Neck pattern (slight bullish variant of On Neck).
A bearish candle followed by a small bullish candle that opens below the previous low and closes slightly above (but near) the previous close.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = in_neck(open_, high, low, close)
- thrusting(open_, high, low, close)[source]¶
Thrusting pattern (moderate bearish continuation).
A bearish candle followed by a bullish candle that opens below the previous low and closes above the previous close but below the midpoint of the previous body.
- Parameters:
- Return type:
- Returns:
-1 where the pattern is detected, 0 otherwise.
Example
>>> signal = thrusting(open_, high, low, close)
- separating_lines(open_, high, low, close, tolerance=0.001)[source]¶
Bullish/Bearish Separating Lines.
Bullish (1): a bearish candle followed by a bullish candle that opens at the same level as the previous open.
Bearish (-1): a bullish candle followed by a bearish candle that opens at the same level as the previous open.
- Parameters:
- Return type:
- Returns:
1 (bullish), -1 (bearish), or 0.
Example
>>> signal = separating_lines(open_, high, low, close)
- closing_marubozu(open_, high, low, close, threshold=0.01)[source]¶
Closing Marubozu — no shadow on the closing side only.
A bullish closing marubozu (1) has no upper shadow (close == high) but may have a lower shadow. A bearish closing marubozu (-1) has no lower shadow (close == low) but may have an upper shadow.
- Parameters:
- Return type:
- Returns:
1 (bullish), -1 (bearish), or 0.
Example
>>> signal = closing_marubozu(open_, high, low, close)
- rickshaw_man(open_, high, low, close, body_threshold=0.05, shadow_threshold=0.3)[source]¶
Rickshaw Man – a Doji with very long shadows and tiny body near center.
- Interpretation:
Extreme indecision: price moved significantly in both directions but closed near the open at the midpoint.
Suggests the market is at a critical juncture.
Reliability: Moderate – needs context and confirmation.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = rickshaw_man(open_, high, low, close)
- long_legged_doji(open_, high, low, close, body_threshold=0.05, shadow_threshold=0.3)[source]¶
Long Legged Doji – Doji with unusually long upper and lower shadows.
- Interpretation:
Strong indecision with high volatility. Both buyers and sellers were active but neither won.
More significant than a standard doji due to the wide range.
Reliability: Moderate – similar to Rickshaw Man.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = long_legged_doji(open_, high, low, close)
- dragonfly_doji(open_, high, low, close, body_threshold=0.05, upper_threshold=0.05, lower_min=0.3)[source]¶
Dragonfly Doji – Doji with a long lower shadow and no upper shadow.
- Interpretation:
Bullish signal, especially after a downtrend. Sellers pushed price down but buyers brought it all the way back to the open.
The long lower shadow shows strong rejection of lower prices.
Reliability: Moderate-high at support levels.
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_threshold (
float, default:0.05) – Maximum body-to-range ratio.upper_threshold (
float, default:0.05) – Maximum upper-shadow-to-range ratio.lower_min (
float, default:0.3) – Minimum lower-shadow-to-range ratio.open_ (Series)
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = dragonfly_doji(open_, high, low, close)
- gravestone_doji(open_, high, low, close, body_threshold=0.05, lower_threshold=0.05, upper_min=0.3)[source]¶
Gravestone Doji – Doji with a long upper shadow and no lower shadow.
- Interpretation:
Bearish signal, especially after an uptrend. Buyers pushed price up but sellers brought it all the way back to the open.
The long upper shadow shows strong rejection of higher prices.
Reliability: Moderate-high at resistance levels.
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_threshold (
float, default:0.05) – Maximum body-to-range ratio.lower_threshold (
float, default:0.05) – Maximum lower-shadow-to-range ratio.upper_min (
float, default:0.3) – Minimum upper-shadow-to-range ratio.open_ (Series)
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = gravestone_doji(open_, high, low, close)
- tri_star(open_, high, low, close, doji_threshold=0.05)[source]¶
Tri-Star pattern – three consecutive dojis with gaps.
- Interpretation:
Bullish tri-star (1): Three dojis where the middle gaps below. Very rare, strong reversal at bottoms.
Bearish tri-star (-1): Three dojis where the middle gaps above. Very rare, strong reversal at tops.
Reliability: Very high when it occurs, but extremely rare.
Bullish (1): three dojis where the middle gaps below the other two. Bearish (-1): three dojis where the middle gaps above the other two.
- Parameters:
- Return type:
- Returns:
1 (bullish tri-star), -1 (bearish tri-star), or 0.
Example
>>> signal = tri_star(open_, high, low, close)
- unique_three_river(open_, high, low, close)[source]¶
Unique Three River Bottom (rare bullish reversal).
Day 1: long bearish candle. Day 2: harami-like bearish candle with a lower low. Day 3: small bullish candle that closes below Day 2’s close.
- Parameters:
- Return type:
- Returns:
1 where detected, 0 otherwise.
Example
>>> signal = unique_three_river(open_, high, low, close)
- concealing_baby_swallow(open_, high, low, close, marubozu_threshold=0.02)[source]¶
Concealing Baby Swallow (four-candle bearish pattern).
Four bearish candles: Days 1-2 are bearish marubozus. Day 3 gaps down, has a long upper shadow into Day 2’s body. Day 4 engulfs Day 3 including the shadow.
- Parameters:
- Return type:
- Returns:
-1 where detected, 0 otherwise.
Example
>>> signal = concealing_baby_swallow(open_, high, low, close)
Signal Generation¶
Utility functions for combining indicators: crossover, crossunder, above, below, rising, falling.
Signal generation utilities.
Helper functions for detecting crossovers, comparing series, and computing rolling extremes. These are the building blocks used to translate indicator values into actionable trading signals.
- crossover(series1, series2)[source]¶
Detect when series1 crosses above series2.
- Interpretation:
Returns True on the exact bar where series1 moves from below-or-equal to above series2.
Common uses: MA crossovers, RSI crossing above 30, MACD crossing above signal line.
Only fires on the transition bar, not on subsequent bars where series1 remains above series2.
- crossunder(series1, series2)[source]¶
Detect when series1 crosses below series2.
- Interpretation:
Returns True on the exact bar where series1 moves from above-or-equal to below series2.
Common uses: MA death crosses, RSI crossing below 70, MACD crossing below signal line.
- rising(data, period=1)[source]¶
Detect whether data is rising (each bar higher than period bars ago).
- falling(data, period=1)[source]¶
Detect whether data is falling (each bar lower than period bars ago).
Statistical Functions¶
Z-score, percentile rank, skewness, kurtosis, Hurst exponent, rolling beta, R-squared.
Statistical technical analysis indicators.
This module provides rolling statistical measures commonly used in
quantitative analysis and systematic trading. All functions accept
pd.Series inputs and return pd.Series.
- zscore(data, period=20)[source]¶
Rolling z-score of price.
Measures how many standard deviations the current value is from the rolling mean.
- Interpretation:
> +2: Price is 2+ standard deviations above mean – statistically unusual (overbought).
< -2: Price is 2+ standard deviations below mean – statistically unusual (oversold).
Near 0: Price is near its rolling average (fair value).
Mean-reversion traders buy at z < -2 and sell at z > +2.
In trending markets, z-score can stay extreme for extended periods – use with a trend filter.
- Parameters:
- Returns:
Z-score values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> zscore(close, period=5)
- percentile_rank(data, period=20)[source]¶
Rolling percentile rank.
Computes the percentage of values within the rolling window that are less than or equal to the current value.
- Parameters:
- Returns:
Percentile rank in [0, 100].
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> percentile_rank(close, period=5)
- mean_deviation(data, period=20)[source]¶
Rolling mean absolute deviation.
Computes the average of absolute deviations from the rolling mean.
- Parameters:
- Returns:
Mean absolute deviation values (>= 0).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> mean_deviation(close, period=5)
- median(data, period=20)[source]¶
Rolling median.
- Parameters:
- Returns:
Rolling median values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13]) >>> median(close, period=5)
- skewness(data, period=20)[source]¶
Rolling skewness.
Measures the asymmetry of the distribution of values within the rolling window. Uses Fisher’s definition (bias-corrected).
- Interpretation:
Positive skew: Distribution has a long right tail – occasional large positive moves (typical of call option payoffs).
Negative skew: Distribution has a long left tail – occasional large negative moves (crash risk).
Near 0: Symmetric distribution (normal-like).
Negative skew in returns is common for equities – tail risk is to the downside.
- Parameters:
- Returns:
Skewness values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> skewness(close, period=20)
- kurtosis(data, period=20)[source]¶
Rolling kurtosis (excess kurtosis, Fisher’s definition).
Measures the tailedness of the distribution of values within the rolling window. Normal distribution has excess kurtosis of 0.
- Interpretation:
> 0 (leptokurtic): Fatter tails than normal – more extreme moves than a Gaussian would predict. Common in financial returns.
< 0 (platykurtic): Thinner tails than normal – fewer extreme moves.
Near 0 (mesokurtic): Normal-like tail behavior.
High kurtosis warns of tail risk – standard VaR and Gaussian-based risk models will underestimate risk.
- Parameters:
- Returns:
Excess kurtosis values (unbounded).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> kurtosis(close, period=20)
- entropy(data, period=20, bins=10)[source]¶
Rolling Shannon entropy of binned price changes.
Discretises the price changes within the rolling window into bins equal-width bins and computes Shannon entropy in nats (natural log).
- Parameters:
- Returns:
Shannon entropy values (>= 0).
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series(range(30), dtype=float) >>> entropy(close, period=20, bins=5)
- hurst_exponent(data, period=100)[source]¶
Rolling Hurst exponent via the rescaled range (R/S) method.
H < 0.5: mean-reverting
H = 0.5: random walk
H > 0.5: trending
- Parameters:
- Returns:
Hurst exponent estimates in roughly [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(200))) >>> hurst_exponent(close, period=100)
- correlation(data, other, period=20)[source]¶
Rolling Pearson correlation between two series.
- Parameters:
- Returns:
Correlation values in [-1, 1].
- Return type:
Example
>>> import pandas as pd >>> x = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=float) >>> y = pd.Series([2, 4, 6, 8, 10, 12, 14, 16, 18, 20], dtype=float) >>> correlation(x, y, period=5)
- beta(data, benchmark, period=60)[source]¶
Rolling beta (OLS slope of data returns vs benchmark returns).
- Interpretation:
Beta = 1: Asset moves in line with the benchmark.
Beta > 1: Asset is more volatile than the benchmark (amplifies market moves). E.g. beta = 1.5 means the stock moves 1.5% for every 1% benchmark move.
Beta < 1: Asset is less volatile than the benchmark.
Beta < 0: Asset moves inversely to the benchmark (rare).
Rising beta: Asset becoming more correlated/volatile relative to benchmark.
Beta is the cornerstone of CAPM and factor models.
- Parameters:
- Returns:
Beta values (unbounded).
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> stock = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> market = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> beta(stock, market, period=30)
- r_squared(data, benchmark, period=60)[source]¶
Rolling R-squared (coefficient of determination).
Computed as the square of the rolling Pearson correlation of returns.
- Parameters:
- Returns:
R-squared values in [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> stock = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> market = pd.Series(100 + np.cumsum(np.random.randn(100))) >>> r_squared(stock, market, period=30)
- information_coefficient(data, other, period=20)[source]¶
Rolling information coefficient (Spearman rank correlation).
Measures the rolling rank correlation between two series, commonly used to evaluate forecast skill.
- Parameters:
- Returns:
IC values in [-1, 1].
- Return type:
Example
>>> import pandas as pd >>> forecast = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=float) >>> actual = pd.Series([2, 1, 4, 3, 6, 5, 8, 7, 10, 9], dtype=float) >>> information_coefficient(forecast, actual, period=5)
Cycles¶
Hilbert Transform, sine wave indicators, bandpass and roofing filters.
Cycle analysis indicators.
This module provides indicators based on digital signal processing
techniques, primarily those developed by John Ehlers. They detect
dominant cycle periods and separate trend from cycle components.
All functions accept pd.Series inputs and return pd.Series
(or dict[str, pd.Series] for multi-output indicators).
- hilbert_transform_dominant_period(data, min_period=6, max_period=50)[source]¶
Dominant cycle period via Hilbert Transform.
Uses Ehlers’ Hilbert Transform Discriminator to estimate the dominant cycle period of the price series.
- Interpretation:
Output is the estimated cycle length in bars (e.g. 20 means the dominant cycle repeats every 20 bars).
Use this to adaptively set indicator periods: instead of a fixed 14-period RSI, use the dominant period.
Short period (< 10): Fast cycling market.
Long period (> 30): Slow cycling or trending market.
Stable readings = well-defined cycle. Erratic readings = no clear cycle (trending or random).
- Parameters:
- Returns:
Estimated dominant cycle period in bars.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_transform_dominant_period(close)
- hilbert_transform_trend_mode(data)[source]¶
Trend vs cycle mode indicator via Hilbert Transform.
Returns +1 when the market is in trend mode and 0 when in cycle mode, based on the relationship between the dominant cycle period and a simple moving average smoothing window.
- Parameters:
data (
Series) – Price series.- Returns:
Binary series: 1 = trending, 0 = cycling.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_transform_trend_mode(close)
- hilbert_instantaneous_phase(data)[source]¶
Instantaneous trendline via Hilbert Transform.
Computes a smooth trendline by applying the dominant cycle period as an adaptive moving average length.
- Parameters:
data (
Series) – Price series.- Returns:
Instantaneous trendline values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> hilbert_instantaneous_phase(close)
- sine_wave(data)[source]¶
Ehlers Sine Wave indicator.
Uses the dominant cycle period to compute the sine and lead-sine values, generating buy/sell signals on crossovers.
- Interpretation:
Sine crosses above lead_sine: Buy signal (cycle turning up).
Sine crosses below lead_sine: Sell signal (cycle turning down).
When both values are near +/-1, the market is in cycle mode.
When values are erratic or near zero, the market may be trending rather than cycling.
- Parameters:
data (
Series) – Price series.- Returns:
sineandlead_sineseries.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> result = sine_wave(close)
- even_better_sinewave(data, hp_period=40, ss_period=10)[source]¶
Ehlers Even Better Sinewave (EBSW).
Combines a high-pass filter, super-smoother, and autocorrelation to produce an oscillator that identifies the dominant cycle.
- Interpretation:
Near +1: Cycle is at or near a peak.
Near -1: Cycle is at or near a trough.
Zero crossover up: Cycle turning bullish.
Zero crossover down: Cycle turning bearish.
More reliable than the original Sine Wave indicator because it better separates cycle from trend components.
- Parameters:
- Returns:
EBSW oscillator values in approximately [-1, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> even_better_sinewave(close)
- roofing_filter(data, hp_period=48, lp_period=10)[source]¶
Ehlers Roofing Filter.
Applies a high-pass filter followed by a super-smoother low-pass filter to isolate the dominant cycle from both trend and noise.
- Interpretation:
Output oscillates around zero, showing the pure cycle component of price.
Positive: Cycle is in the up phase.
Negative: Cycle is in the down phase.
Use to identify cycle turning points without trend or noise contamination.
- Parameters:
- Returns:
Filtered cycle component.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> roofing_filter(close)
- decycler(data, hp_period=125)[source]¶
Ehlers Decycler.
Removes the cycle component from the price series, keeping only the trend. Computed as
price - highpass(price).- Interpretation:
Shows the pure trend component of price with cycles removed.
Price above decycler: Bullish trend.
Price below decycler: Bearish trend.
Extremely smooth with virtually no lag – one of the best trend-following overlays available.
- Parameters:
- Returns:
Trend-only (decycled) series.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> decycler(close)
- bandpass_filter(data, period=20, bandwidth=0.3)[source]¶
Ehlers Bandpass Filter.
Isolates the cycle component at the specified period. Returns both the bandpass filter output and a trigger signal (one-bar lag).
- Interpretation:
BP crosses above trigger: Buy signal (cycle turning up).
BP crosses below trigger: Sell signal (cycle turning down).
BP at peak: Cycle high – potential sell zone.
BP at trough: Cycle low – potential buy zone.
Only isolates the cycle at the specified period; other frequencies are filtered out.
- Parameters:
- Returns:
bp(bandpass) andtrigger(one-bar lag of bp).- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(np.sin(np.linspace(0, 8 * np.pi, 200)) * 10 + 100) >>> result = bandpass_filter(close, period=20)
Custom Indicators¶
Squeeze Momentum, Anchored VWAP, Adaptive RSI, Linear Regression Channel, Market Structure, Volume-Weighted MACD.
Modern and advanced technical analysis indicators.
This module provides contemporary indicators including squeeze detection,
anchored VWAP, market structure analysis, and adaptive oscillators.
All functions accept pd.Series inputs and return pd.Series
(or dict[str, pd.Series] for multi-output indicators).
- squeeze_momentum(high, low, close, bb_period=20, bb_std=2.0, kc_period=20, kc_mult=1.5, mom_period=12)[source]¶
TTM Squeeze Momentum indicator.
Detects when Bollinger Bands are inside Keltner Channels (the “squeeze”) and measures momentum via a linear regression of price.
- Interpretation:
squeeze_on = 1: Bollinger Bands are inside Keltner Channels. Volatility is compressed. A breakout is imminent.
squeeze_on = 0: No squeeze. Normal volatility.
Momentum positive: Bullish momentum – breakout likely up.
Momentum negative: Bearish momentum – breakout likely down.
Momentum growing: Accelerating.
Momentum shrinking: Decelerating.
- Trading rules:
When squeeze fires (transitions from on to off), enter in the direction of momentum.
Exit when momentum starts to decelerate (histogram shrinks).
- Parameters:
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.bb_period (
int, default:20) – Bollinger Bands SMA period.bb_std (
float, default:2.0) – Bollinger Bands standard deviation multiplier.kc_period (
int, default:20) – Keltner Channel EMA period.kc_mult (
float, default:1.5) – Keltner Channel ATR multiplier.mom_period (
int, default:12) – Momentum linear regression period.
- Returns:
squeeze_on(bool: 1 = squeeze active),momentum(momentum histogram values).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> n = 100 >>> c = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> h = c + abs(np.random.randn(n) * 0.3) >>> lo = c - abs(np.random.randn(n) * 0.3) >>> result = squeeze_momentum(h, lo, c)
- anchored_vwap(close, volume, anchor_index=0)[source]¶
VWAP anchored from a specific bar index.
Computes the Volume Weighted Average Price starting from anchor_index onwards. Values before the anchor are
NaN.- Parameters:
- Returns:
Anchored VWAP values.
- Return type:
Example
>>> import pandas as pd >>> close = pd.Series([10, 11, 12, 11, 10, 9, 10, 11, 12, 13], dtype=float) >>> volume = pd.Series([100, 200, 150, 300, 250, 100, 200, 150, 300, 250], dtype=float) >>> anchored_vwap(close, volume, anchor_index=3)
- linear_regression_channel(data, period=100, std_dev=2.0)[source]¶
Linear regression channel with standard deviation bands.
Fits a rolling linear regression and constructs upper/lower channel lines based on the standard error.
- Parameters:
- Returns:
middle(regression value),upper,lower.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(120, dtype=float) * 0.5) >>> result = linear_regression_channel(close, period=50)
- pivot_points(high, low, close, method='standard')[source]¶
Pivot points with support and resistance levels.
Computes pivot point and two levels of support/resistance using the prior bar’s high, low, and close.
- Parameters:
- Returns:
pivot,s1,s2,r1,r2.- Return type:
Example
>>> import pandas as pd >>> h = pd.Series([12, 13, 14, 13, 12], dtype=float) >>> lo = pd.Series([10, 11, 12, 11, 10], dtype=float) >>> c = pd.Series([11, 12, 13, 12, 11], dtype=float) >>> result = pivot_points(h, lo, c)
- market_structure(high, low, lookback=5)[source]¶
Higher highs / lower lows market structure detection.
Identifies swing highs and lows using a lookback window, then labels each swing as higher-high (HH), lower-high (LH), higher-low (HL), or lower-low (LL).
- Parameters:
- Returns:
swing_high(high values at swing highs, else NaN),swing_low(low values at swing lows, else NaN),structure(1 = bullish / HH+HL, -1 = bearish / LH+LL, 0 = neutral).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = market_structure(h, lo, lookback=3)
- swing_points(high, low, lookback=5)[source]¶
Swing high and low detection.
A swing high occurs when the high is the maximum of
2 * lookback + 1bars centred on the pivot bar. Symmetrically for swing lows.- Parameters:
- Returns:
swing_high(high values at swing highs, else NaN),swing_low(low values at swing lows, else NaN).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = swing_points(h, lo, lookback=3)
- volume_weighted_macd(close, volume, fast=12, slow=26, signal=9)[source]¶
MACD weighted by volume.
Uses volume-weighted moving averages instead of standard EMAs for the fast and slow lines.
- Parameters:
- Returns:
macd,signal,histogram.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> c = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5)) >>> v = pd.Series(np.random.randint(1000, 10000, 100), dtype=float) >>> result = volume_weighted_macd(c, v)
- ehlers_fisher(high, low, period=10)[source]¶
Ehlers Fisher Transform.
Converts prices into a Gaussian normal distribution to create sharp turning points, making it easier to identify reversals.
- Parameters:
- Returns:
fisherandtrigger(one-bar lag of fisher).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5) + 1) >>> lo = h - 2 >>> result = ehlers_fisher(h, lo)
- adaptive_rsi(data, base_period=14, vol_period=10, min_period=5, max_period=50)[source]¶
RSI with an adaptive period based on volatility.
The look-back period expands in low-volatility regimes and contracts in high-volatility regimes, improving responsiveness.
- Parameters:
data (
Series) – Price series (typically close).base_period (
int, default:14) – Base RSI period.vol_period (
int, default:10) – Period for the volatility (standard deviation) calculation.min_period (
int, default:5) – Minimum allowed RSI period.max_period (
int, default:50) – Maximum allowed RSI period.
- Returns:
Adaptive RSI values in [0, 100].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(200) * 0.5)) >>> adaptive_rsi(close)
- relative_strength(data, benchmark)[source]¶
Relative strength ratio of one series to another.
Commonly used for pair analysis or sector rotation. A rising ratio indicates data is outperforming benchmark.
- Parameters:
- Returns:
Ratio of data / benchmark.
- Return type:
Example
>>> import pandas as pd >>> stock = pd.Series([100, 105, 110, 108, 112], dtype=float) >>> index = pd.Series([1000, 1010, 1005, 1015, 1020], dtype=float) >>> relative_strength(stock, index)
- linear_regression_forecast(data, period=20, forecast_bars=1)[source]¶
Rolling linear regression forecast N bars ahead.
Fits a linear regression over each rolling window and projects the value forecast_bars steps beyond the last observation in the window.
- Parameters:
- Returns:
Forecasted values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> linear_regression_forecast(close, period=20, forecast_bars=1)
- standard_error_bands(data, period=20, num_bands=3)[source]¶
Linear regression line with standard error bands.
Fits a rolling linear regression and constructs bands at +-1, +-2, and +-3 standard errors (configurable via num_bands).
- Parameters:
- Returns:
middleplusupper_Nandlower_Nfor each band level N from 1 to num_bands.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> result = standard_error_bands(close, period=20)
- r_squared_indicator(data, period=14)[source]¶
Rolling R-squared of linear regression as a trend strength measure.
An R-squared near 1.0 indicates prices are moving in a strong linear trend; near 0.0 indicates choppy, non-trending movement.
- Parameters:
- Returns:
R-squared values in [0, 1].
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> r_squared_indicator(close, period=14)
- polynomial_regression(data, period=20, degree=2)[source]¶
Rolling polynomial regression fitted values.
Fits a polynomial of the given degree over each rolling window and returns the fitted value at the end of the window.
- Parameters:
- Returns:
Fitted polynomial values at the end of each window.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) ** 1.5 * 0.01) >>> polynomial_regression(close, period=20, degree=2)
- raff_regression_channel(data, period=50)[source]¶
Raff regression channel using maximum deviation.
Fits a rolling linear regression and constructs channel lines using the maximum absolute deviation of any point in the window from the regression line.
- Parameters:
- Returns:
center(regression line),upper,lower.- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(80, dtype=float) * 0.5) >>> result = raff_regression_channel(close, period=50)
- detrended_regression(data, period=20)[source]¶
Residuals from rolling linear regression (for mean reversion).
Fits a rolling linear regression and returns the residual (actual minus predicted) at the end of each window. Positive values indicate price above trend; negative below trend.
- Parameters:
- Returns:
Detrended (residual) values.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series(100 + np.arange(50, dtype=float) * 0.5) >>> detrended_regression(close, period=20)
Fibonacci¶
Retracements, extensions, fans, time zones, pivot points, auto-Fibonacci.
Fibonacci-based technical analysis indicators.
This module provides Fibonacci retracement, extension, fan, time zone,
pivot point, and auto-detection indicators. All functions accept
pd.Series inputs and return dict or list outputs.
- fibonacci_retracements(swing_high, swing_low, direction='up')[source]¶
Compute Fibonacci retracement levels from a swing high/low pair.
Given a swing high and swing low, computes the standard Fibonacci retracement levels at 23.6%, 38.2%, 50%, 61.8%, and 78.6%.
- Interpretation:
23.6%: Shallow retracement – strong trend continuation likely. Common in fast-moving markets.
38.2%: Moderate retracement – healthy pullback in a strong trend.
50%: Not a Fibonacci ratio but widely watched. A 50% retracement is considered normal.
61.8%: The “golden ratio” – the most important level. If price holds here, the trend is likely to resume.
78.6%: Deep retracement – the trend is under pressure. If this level breaks, the trend may be over.
- Trading rules:
Look for buy signals (candlestick patterns, divergence) at 38.2%-61.8% retracement levels in an uptrend.
Place stops below the 78.6% level.
The stronger the trend, the shallower the retracement (23.6%-38.2%).
- Parameters:
- Returns:
Level names (e.g.
"23.6%") as keys and price values.- Return type:
Example
>>> result = fibonacci_retracements(swing_high=110.0, swing_low=100.0) >>> result["50.0%"] 105.0
- fibonacci_extensions(swing_low, swing_high, pullback_low)[source]¶
Compute Fibonacci extension levels from three price points.
Uses a swing low, swing high, and pullback low to project extension levels at 100%, 127.2%, 161.8%, 200%, and 261.8%.
- Interpretation:
Extension levels project where price might go AFTER a retracement completes.
100%: The most common initial target (move equals the first swing).
127.2%: Common target for corrective waves.
161.8%: The golden extension – a key profit target.
200% and 261.8%: Extended targets for strong trends.
Use for setting profit targets and identifying potential resistance levels in a trend.
- Parameters:
- Returns:
Extension level names as keys and projected price values.
- Return type:
Example
>>> result = fibonacci_extensions(100.0, 110.0, 105.0) >>> result["100.0%"] 115.0
- fibonacci_fans(pivot_x, pivot_y, target_x, target_y)[source]¶
Compute Fibonacci fan line slopes from two pivot points.
Draws fan lines from
(pivot_x, pivot_y)through Fibonacci retracement levels of the vertical distance to(target_x, target_y).- Parameters:
- Returns:
Fan line labels as keys and slope values.
- Return type:
Example
>>> result = fibonacci_fans(0, 100.0, 10, 110.0) >>> abs(result["50.0%"] - 0.5) < 1e-10 True
- fibonacci_time_zones(start_index, max_index)[source]¶
Compute Fibonacci time zone indices from a start bar.
Generates a sequence of bar indices at Fibonacci intervals (1, 1, 2, 3, 5, 8, 13, 21, …) from the given start index, up to
max_index.- Parameters:
- Returns:
List of bar indices at Fibonacci time intervals.
- Return type:
Example
>>> fibonacci_time_zones(0, 50) [1, 2, 3, 5, 8, 13, 21, 34]
- fibonacci_pivot_points(high, low, close)[source]¶
Pivot points using Fibonacci ratios.
Computes the standard pivot
P = (H + L + C) / 3and derives support/resistance using Fibonacci ratios applied to the prior bar’s range.- Parameters:
- Returns:
pivot,s1,s2,s3,r1,r2,r3.- Return type:
Example
>>> import pandas as pd >>> h = pd.Series([12, 13, 14, 13, 12], dtype=float) >>> lo = pd.Series([10, 11, 12, 11, 10], dtype=float) >>> c = pd.Series([11, 12, 13, 12, 11], dtype=float) >>> result = fibonacci_pivot_points(h, lo, c)
- auto_fibonacci(data, lookback=50, direction='up')[source]¶
Automatically detect swing high/low and compute Fibonacci retracements.
Scans the most recent lookback bars to find the highest high and lowest low, then computes Fibonacci retracement levels.
- Parameters:
- Returns:
swing_high(float),swing_high_idx(index label),swing_low(float),swing_low_idx(index label),levels(dict of retracement levels).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> close = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5)) >>> result = auto_fibonacci(close, lookback=30)
Support & Resistance¶
Algorithmic detection of support/resistance levels, fractal levels, supply/demand zones, trendlines.
Support and resistance detection indicators.
This module provides tools for identifying horizontal support/resistance
levels, fractals, supply/demand zones, and trendlines from price data.
All functions accept pd.Series inputs and return dict, list,
or pd.Series outputs.
- find_support_resistance(high, low, lookback=5, num_levels=5, tolerance=0.02)[source]¶
Detect horizontal support and resistance levels via clustering.
Identifies local swing highs and swing lows, then clusters nearby levels within tolerance (as a fraction of price) to produce consolidated S/R levels.
- Interpretation:
Support levels: Prices where buying pressure historically prevented further decline. Price tends to bounce at these levels.
Resistance levels: Prices where selling pressure historically prevented further advance.
Multiple touches: A level tested many times is stronger.
Breakout through resistance: Resistance becomes support (role reversal) and vice versa.
- Trading rules:
Buy at support levels with confirmation (candlestick pattern, volume spike).
Sell at resistance levels with confirmation.
Place stops on the other side of the S/R level.
- Parameters:
high (
Series) – High prices.low (
Series) – Low prices.lookback (
int, default:5) – Number of bars on each side to confirm a swing point.num_levels (
int, default:5) – Maximum number of S/R levels to return per side.tolerance (
float, default:0.02) – Fraction of price within which nearby levels are merged.
- Returns:
supportandresistancelists of price levels, sorted ascending.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(200) * 0.5) + 1) >>> lo = h - 2 >>> result = find_support_resistance(h, lo)
- price_clustering(data, num_levels=5, bins=100)[source]¶
Find price levels where price has spent the most time.
Builds a histogram of price values and returns the bin centres with the highest counts, analogous to a simplified volume profile.
- Parameters:
- Returns:
Array of key price levels sorted ascending.
- Return type:
Example
>>> import pandas as pd, numpy as np >>> close = pd.Series([100, 101, 100, 99, 100, 101, 102, 100], dtype=float) >>> price_clustering(close, num_levels=3)
- fractal_levels(high, low, period=2)[source]¶
Williams fractal swing high/low identification.
An up-fractal occurs when the high is the highest of
2 * period + 1bars. A down-fractal occurs when the low is the lowest.- Parameters:
- Returns:
up_fractals(boolean Series, True at up-fractal bars),down_fractals(boolean Series, True at down-fractal bars).- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(50) * 0.5) + 1) >>> lo = h - 2 >>> result = fractal_levels(h, lo, period=2)
- round_number_levels(current_price, num_levels=5, step=None)[source]¶
Generate psychological round number levels near the current price.
Computes evenly spaced round numbers above and below the given price. Useful for identifying potential support/resistance at psychologically significant prices.
- Parameters:
- Returns:
Sorted list of round number price levels.
- Return type:
Example
>>> round_number_levels(105.3, num_levels=3, step=10.0) [80.0, 90.0, 100.0, 110.0, 120.0, 130.0]
- supply_demand_zones(open_, high, low, close, body_pct=0.6, consolidation_bars=3)[source]¶
Detect supply and demand zones from price action.
A demand zone forms when a large bullish candle follows a period of basing (small bodies). A supply zone forms similarly for bearish moves. Zones are defined by the basing candles’ range.
- Parameters:
open (
pd.Series) – Open prices.high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.body_pct (
float, default:0.6) – Minimum body-to-range ratio to qualify as a “large” candle.consolidation_bars (
int, default:3) – Number of preceding small-body bars required for basing.open_ (Series)
- Returns:
demandandsupplylists, each containing dicts withzone_low,zone_high, andindexkeys.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> n = 100 >>> c = pd.Series(100 + np.cumsum(np.random.randn(n) * 0.5)) >>> o = c.shift(1).fillna(c.iloc[0]) >>> h = pd.concat([o, c], axis=1).max(axis=1) + 0.5 >>> lo = pd.concat([o, c], axis=1).min(axis=1) - 0.5 >>> result = supply_demand_zones(o, h, lo, c)
- trendline_detection(high, low, lookback=5, min_touches=2)[source]¶
Fit linear trendlines to swing high and swing low points.
Detects swing points, then fits lines through the most recent swing highs (resistance trendline) and swing lows (support trendline) using least-squares regression.
- Parameters:
- Returns:
resistance_linesandsupport_lines, each containing dicts withslope,intercept, andnum_toucheskeys.- Return type:
Example
>>> import pandas as pd, numpy as np >>> np.random.seed(42) >>> h = pd.Series(100 + np.cumsum(np.random.randn(100) * 0.5) + 1) >>> lo = h - 2 >>> result = trendline_detection(h, lo, lookback=5)
Market Breadth¶
Advance/Decline, McClellan, Arms Index, percent above MA – for indices and baskets.
Market breadth indicators.
This module provides indicators that measure the overall health and direction
of the broader market by analyzing the number of advancing/declining issues,
new highs/lows, and the percentage of components meeting certain criteria.
All functions accept pd.Series (or pd.DataFrame where noted) inputs
and return pd.Series.
- advance_decline_line(advancing, declining)[source]¶
Advance/Decline Line – cumulative sum of (advancing - declining).
The A/D line is a breadth indicator that tracks the running total of the difference between the number of advancing and declining issues.
- Interpretation:
Rising A/D line with rising market: Healthy uptrend – broad participation confirms the rally.
Falling A/D line with rising market: Bearish divergence – fewer stocks participating in the rally. Distribution.
Rising A/D line with falling market: Bullish divergence – accumulation occurring beneath the surface.
The A/D line often leads the market at major turning points.
- Parameters:
- Returns:
Cumulative A/D line values.
- Return type:
Example
>>> adv = pd.Series([200, 250, 180, 300, 220]) >>> dec = pd.Series([100, 150, 220, 100, 180]) >>> advance_decline_line(adv, dec)
- advance_decline_ratio(advancing, declining)[source]¶
Advance/Decline Ratio — advancing / declining.
Values above 1.0 indicate more advancers than decliners; below 1.0 indicates more decliners.
- Parameters:
- Returns:
A/D ratio values (NaN where declining is zero).
- Return type:
Example
>>> adv = pd.Series([200, 250, 180]) >>> dec = pd.Series([100, 150, 220]) >>> advance_decline_ratio(adv, dec)
- mcclellan_oscillator(advancing, declining, fast=19, slow=39)[source]¶
McClellan Oscillator – difference between fast and slow EMA of AD diff.
McClellan = EMA(advancing - declining, fast) - EMA(advancing - declining, slow)- Interpretation:
Above zero: Short-term breadth momentum is positive (more stocks advancing than declining, accelerating).
Below zero: Short-term breadth momentum is negative.
Above +100: Very overbought breadth-wise.
Below -100: Very oversold breadth-wise.
Zero-line crossover: Breadth momentum shift.
Best used for timing entries: buy when the oscillator turns up from below -100 (oversold breadth bounce).
- Parameters:
- Returns:
McClellan Oscillator values.
- Return type:
Example
>>> result = mcclellan_oscillator(advancing, declining)
- mcclellan_summation(advancing, declining, fast=19, slow=39)[source]¶
McClellan Summation Index – cumulative sum of the McClellan Oscillator.
This is the running total of the McClellan Oscillator, providing a longer-term view of market breadth.
- Interpretation:
Rising: Long-term breadth is improving (more and more stocks participating in the advance).
Falling: Long-term breadth is deteriorating.
Above +1000: Strongly bullish long-term breadth.
Below -1000: Strongly bearish long-term breadth.
Acts as a long-term trend indicator for market internals.
- Parameters:
- Returns:
McClellan Summation Index values.
- Return type:
Example
>>> result = mcclellan_summation(advancing, declining)
- arms_index(advancing_issues, declining_issues, advancing_volume, declining_volume)[source]¶
Arms Index (TRIN) — Short-Term Trading Index.
- ``TRIN = (Advancing Issues / Declining Issues) /
(Advancing Volume / Declining Volume)``
Values below 1.0 are bullish (more volume flowing into advancers); values above 1.0 are bearish.
- Parameters:
- Returns:
TRIN values (NaN where denominators are zero).
- Return type:
Example
>>> result = arms_index(adv_issues, dec_issues, adv_vol, dec_vol)
- new_highs_lows(new_highs, new_lows)[source]¶
New Highs minus New Lows.
A simple breadth measure: positive values indicate more new highs than new lows, suggesting bullish breadth.
- Parameters:
- Returns:
New highs minus new lows.
- Return type:
Example
>>> nh = pd.Series([50, 60, 30]) >>> nl = pd.Series([20, 40, 50]) >>> new_highs_lows(nh, nl)
- percent_above_ma(prices_df, period=50)[source]¶
Percentage of components above their N-period moving average.
For each row, computes how many columns have a value above their respective rolling SMA, expressed as a percentage.
- Parameters:
- Returns:
Percentage (0-100) of components above their SMA.
- Return type:
Example
>>> df = pd.DataFrame({"A": [10, 11, 12], "B": [20, 19, 18]}) >>> percent_above_ma(df, period=2)
- high_low_index(new_highs, new_lows)[source]¶
High-Low Index — new highs as a percentage of new highs + new lows.
HLI = new_highs / (new_highs + new_lows) * 100Values above 50 indicate more new highs; values below 50 indicate more new lows.
- Parameters:
- Returns:
High-Low Index values in [0, 100] (NaN where both are zero).
- Return type:
Example
>>> nh = pd.Series([50, 60, 30]) >>> nl = pd.Series([20, 40, 50]) >>> high_low_index(nh, nl)
- bullish_percent(prices_df, period=50)[source]¶
Bullish Percent Index (simplified).
Approximates the Bullish Percent Index by computing the percentage of components trading above their period-day simple moving average. The traditional BPI uses point-and-figure buy signals, but the SMA crossover is a widely accepted simplification.
- Parameters:
- Returns:
Bullish Percent values in [0, 100].
- Return type:
Example
>>> df = pd.DataFrame({"A": [10, 11, 12], "B": [20, 19, 18]}) >>> bullish_percent(df, period=2)
- cumulative_volume_index(close, volume)[source]¶
Cumulative Volume Index (CVI).
Adds volume on up days and subtracts volume on down days.
CVI = cumsum(sign(close.diff()) * volume)- Parameters:
- Returns:
CVI values.
- Return type:
Example
>>> close = pd.Series([100, 102, 101, 103, 104.0]) >>> volume = pd.Series([1000, 1500, 1200, 1800, 1600.0]) >>> cumulative_volume_index(close, volume)
Performance¶
Relative performance, alpha, tracking error, up/down capture, drawdown analytics.
Performance and comparison indicators.
This module provides indicators that measure asset performance relative to
a benchmark or on an absolute basis: relative strength, alpha, tracking
error, drawdowns, and risk-adjusted return metrics. All functions accept
pd.Series inputs and return pd.Series (or dict where noted).
- relative_performance(asset, benchmark)[source]¶
Relative Performance – ratio of asset to benchmark, normalized to 100.
RP = (asset / benchmark) / (asset.iloc[0] / benchmark.iloc[0]) * 100- Interpretation:
Rising line: Asset is outperforming the benchmark. Buy the asset, sell the benchmark (or use as a selection filter).
Falling line: Asset is underperforming the benchmark.
Above 100: Asset has outperformed since the start of the measurement period.
Below 100: Asset has underperformed.
Use to identify sector rotation and relative strength leaders.
- Parameters:
- Returns:
Relative performance, starting at 100.
- Return type:
Example
>>> asset = pd.Series([100, 105, 110]) >>> bench = pd.Series([100, 102, 104]) >>> relative_performance(asset, bench)
- mansfield_rsi(asset, benchmark, period=52)[source]¶
Mansfield Relative Strength (not Wilder RSI).
Compares the asset/benchmark ratio to its own simple moving average, expressing the result as a percentage deviation.
MRS = ((asset / benchmark) / SMA(asset / benchmark, period) - 1) * 100- Parameters:
- Returns:
Mansfield RS values (percentage above/below zero line).
- Return type:
Example
>>> result = mansfield_rsi(asset, benchmark, period=52)
- alpha(asset, benchmark, window=60, risk_free=0.0)[source]¶
Rolling Jensen’s Alpha vs. benchmark.
Computes alpha as the intercept of the rolling OLS regression of excess asset returns on excess benchmark returns.
- Parameters:
- Returns:
Rolling alpha values (annualization depends on input frequency).
- Return type:
Example
>>> result = alpha(asset, benchmark, window=60)
- tracking_error(asset, benchmark, window=60)[source]¶
Rolling Tracking Error vs. benchmark.
Tracking error is the standard deviation of the difference in returns between the asset and the benchmark over a rolling window.
- Parameters:
- Returns:
Rolling tracking error values.
- Return type:
Example
>>> result = tracking_error(asset, benchmark, window=60)
- up_down_capture(asset, benchmark)[source]¶
Up/Down Market Capture Ratio.
Measures how much of the benchmark’s up- and down-market returns the asset captures.
- Parameters:
- Returns:
up_capture,down_capture, andcapture_ratio(up/down).- Return type:
Example
>>> result = up_down_capture(asset, benchmark) >>> result["up_capture"]
- drawdown(data)[source]¶
Drawdown from peak – current decline from running maximum.
DD = (data - running_max) / running_maxReturns non-positive values (0 at peaks, negative during drawdowns).
- Interpretation:
0: At or near all-time high (no drawdown).
-0.05 to -0.10: Mild pullback (5-10%). Normal in healthy trends.
-0.10 to -0.20: Correction territory. May indicate trend weakness.
< -0.20: Bear market territory. Significant damage to portfolio.
Drawdown duration (how long it stays negative) is often more painful psychologically than drawdown depth.
- Parameters:
data (
Series) – Price or equity curve.- Returns:
Drawdown values (non-positive fractions).
- Return type:
Example
>>> prices = pd.Series([100, 105, 102, 108, 103]) >>> drawdown(prices)
- max_drawdown_rolling(data, window=252)[source]¶
Rolling maximum drawdown over a look-back window.
For each point, computes the worst drawdown experienced within the trailing window periods.
- Interpretation:
Tracks the worst loss experienced in the recent past.
Deepening max drawdown: Risk is increasing.
Stable, shallow max drawdown: Low-risk environment.
Use as a risk management metric: if rolling max drawdown exceeds a threshold, reduce position size or exit.
- Parameters:
- Returns:
Rolling max drawdown values (non-positive fractions).
- Return type:
Example
>>> result = max_drawdown_rolling(prices, window=60)
- pain_index(data, window=252)[source]¶
Pain Index — mean of absolute drawdowns over a rolling window.
The Pain Index averages the magnitude of drawdowns; a higher value indicates more sustained or deeper drawdowns.
- Parameters:
- Returns:
Pain Index values (non-negative).
- Return type:
Example
>>> result = pain_index(prices, window=60)
- gain_loss_ratio(data, window=20)[source]¶
Gain/Loss Ratio — average gain / average loss over a rolling window.
Uses the per-period returns (percentage change) of the input series. Values above 1.0 indicate larger average gains than average losses.
- Parameters:
- Returns:
Gain/loss ratio values (NaN when no losses or no gains in window).
- Return type:
Example
>>> result = gain_loss_ratio(prices, window=20)
- profit_factor(data, window=20)[source]¶
Profit Factor — sum of gains / sum of losses over a rolling window.
Uses the per-period returns (percentage change) of the input series. Values above 1.0 indicate total gains exceed total losses.
- Parameters:
- Returns:
Profit factor values (NaN when no losses in window).
- Return type:
Example
>>> result = profit_factor(prices, window=20)
Smoothing¶
ALMA, JMA, Butterworth, Super Smoother, Gaussian, windowed MAs.
Advanced smoothing and filtering indicators.
This module provides sophisticated moving averages and digital filters used
in technical analysis. All functions accept pd.Series inputs and return
pd.Series.
- alma(data, period=9, offset=0.85, sigma=6.0)[source]¶
Arnaud Legoux Moving Average (ALMA).
A Gaussian-weighted moving average that allows the user to control the position of the bell curve along the window via offset and the width via sigma.
- Interpretation:
Price above ALMA: Bullish.
Price below ALMA: Bearish.
Combines the responsiveness of EMA with the smoothness of Gaussian weighting. The offset parameter lets you place more weight on recent prices (offset near 1) or older prices (offset near 0).
Excellent alternative to EMA for crossover systems.
- Parameters:
data (
Series) – Price series (typically close).period (
int, default:9) – Window length.offset (
float, default:0.85) – Controls the position of the Gaussian peak within the window. 0 = far left (oldest), 1 = far right (newest).sigma (
float, default:6.0) – Controls the width of the Gaussian bell curve. Higher values produce a broader, smoother curve.
- Returns:
ALMA values. The first
period - 1entries areNaN.- Return type:
Example
>>> result = alma(close, period=9, offset=0.85, sigma=6.0)
- lsma(data, period=25)[source]¶
Least Squares Moving Average (LSMA).
Also known as the Linear Regression Value or End Point Moving Average. At each bar, a least-squares line is fit over the window and the endpoint of the line is returned.
- Parameters:
- Returns:
LSMA values.
- Return type:
Example
>>> result = lsma(close, period=25)
- swma(data)[source]¶
Symmetrically Weighted Moving Average (SWMA).
A 4-bar weighted average using weights
[1, 2, 2, 1] / 6.- Parameters:
data (
Series) – Price series.- Returns:
SWMA values. The first 3 entries are
NaN.- Return type:
Example
>>> result = swma(close)
- sinema(data, period=14)[source]¶
Sine-Weighted Moving Average.
Each element in the window is weighted by the sine of its proportional position within a half-period (pi), giving the most weight to the center of the window.
- Parameters:
- Returns:
Sine-weighted moving average values.
- Return type:
Example
>>> result = sinema(close, period=14)
- trima(data, period=20)[source]¶
Triangular Moving Average (TRIMA).
Equivalent to a double SMA:
SMA(SMA(data, ceil((period+1)/2)), floor((period+1)/2)). This produces a smoother curve than a single SMA by effectively giving the most weight to the center of the window.- Parameters:
- Returns:
TRIMA values.
- Return type:
Example
>>> result = trima(close, period=20)
- jma(data, period=7, phase=50.0, power=2)[source]¶
Jurik Moving Average approximation (JMA).
An adaptive moving average that attempts to minimize lag and overshoot. This is an approximation of the proprietary Jurik algorithm using an adaptive EMA with phase and power controls.
- Parameters:
data (
Series) – Price series.period (
int, default:7) – Smoothing period.phase (
float, default:50.0) – Phase parameter in the range [-100, 100]. Controls the tradeoff between lag and overshoot. 0 is balanced, positive reduces lag.power (
int, default:2) – Power parameter controlling the smoothing curve shape.
- Returns:
JMA values.
- Return type:
Example
>>> result = jma(close, period=7, phase=50, power=2)
- gaussian_filter(data, period=14, poles=2)[source]¶
Gaussian-weighted rolling filter.
Applies a discrete Gaussian kernel over the rolling window. The standard deviation of the kernel is set to
period / 4so that the window captures approximately two standard deviations.- Parameters:
- Returns:
Gaussian-filtered values.
- Return type:
Example
>>> result = gaussian_filter(close, period=14)
- butterworth_filter(data, period=14)[source]¶
2nd-order Butterworth low-pass filter (IIR).
This implements the classic Ehlers two-pole Butterworth filter, which provides smooth output with minimal lag relative to its degree of smoothing.
- Parameters:
- Returns:
Butterworth-filtered values.
- Return type:
Example
>>> result = butterworth_filter(close, period=14)
- supersmoother(data, period=14)[source]¶
Ehlers Super Smoother (2-pole Butterworth variant).
A modified Butterworth filter by John Ehlers that removes aliasing noise while retaining a smooth, low-lag response.
- Parameters:
- Returns:
Super-smoothed values.
- Return type:
Example
>>> result = supersmoother(close, period=14)
- hann_window_ma(data, period=14)[source]¶
Hann (raised cosine) windowed moving average.
Each element in the window is weighted by the Hann function:
w(i) = 0.5 * (1 - cos(2 * pi * i / (N - 1)))- Parameters:
- Returns:
Hann-windowed moving average values.
- Return type:
Example
>>> result = hann_window_ma(close, period=14)
- hamming_window_ma(data, period=14)[source]¶
Hamming windowed moving average.
Each element in the window is weighted by the Hamming function:
w(i) = 0.54 - 0.46 * cos(2 * pi * i / (N - 1))- Parameters:
- Returns:
Hamming-windowed moving average values.
- Return type:
Example
>>> result = hamming_window_ma(close, period=14)
- kaufman_efficiency_ratio(data, period=10)[source]¶
Kaufman Efficiency Ratio (ER).
Measures the efficiency of price movement as the ratio of directional change to total path length. This is the core component of the Kaufman Adaptive Moving Average (KAMA).
ER = |close - close[period]| / sum(|close - close[1]|, period)Values near 1.0 indicate strong trending; values near 0.0 indicate choppy / mean-reverting markets.
- Parameters:
- Returns:
Efficiency ratio values in [0, 1].
- Return type:
Example
>>> result = kaufman_efficiency_ratio(close, period=10)
Exotic Indicators¶
Choppiness Index, Random Walk Index, Polarized Fractal Efficiency, Elder Thermometer, Connors TPS, and more.
Lesser-known and exotic technical analysis indicators.
This module provides uncommon or specialized indicators that measure
various aspects of market behaviour. All functions accept pd.Series
inputs and return pd.Series (or dict[str, pd.Series] for
multi-output indicators).
- choppiness_index(high, low, close, period=14)[source]¶
Choppiness Index.
Measures whether the market is trending or range-bound.
- Interpretation:
> 61.8: Choppy / range-bound market. Avoid trend-following strategies; use mean-reversion instead.
< 38.2: Strong trending market. Use trend-following strategies; avoid mean-reversion.
38.2-61.8: Transitional zone.
Does NOT indicate trend direction, only whether a trend exists.
Low choppiness often precedes a breakout.
- Trading rules:
Apply trend strategies when CI < 38.2.
Apply range strategies when CI > 61.8.
Wait for CI to drop before entering breakout trades.
CI = 100 * log10(sum(ATR(1), n) / (highest_high - lowest_low)) / log10(n)- Parameters:
- Returns:
Choppiness Index values, typically in [0, 100].
- Return type:
Example
>>> result = choppiness_index(high, low, close, period=14)
- random_walk_index(high, low, close, period=14)[source]¶
Random Walk Index (RWI).
Compares the range of directional price moves to the expected range of a random walk. Values above 1.0 suggest trending behavior.
- Interpretation:
RWI High > 1: Upward price movement exceeds what a random walk would produce = genuine uptrend.
RWI Low > 1: Downward price movement exceeds random walk = genuine downtrend.
Both < 1: Price movement is consistent with random noise = no trend.
RWI High > RWI Low: Bullish bias.
RWI Low > RWI High: Bearish bias.
- Parameters:
- Returns:
rwi_highandrwi_low.- Return type:
Example
>>> result = random_walk_index(high, low, close, period=14) >>> result["rwi_high"]
- polarized_fractal_efficiency(close, period=10, smoothing=5)[source]¶
Polarized Fractal Efficiency (PFE).
Measures the efficiency of the price path using fractal geometry. A straight-line move yields +/- 100; a random walk yields ~0.
- Interpretation:
> 0: Price is moving efficiently upward (trending up).
< 0: Price is moving efficiently downward (trending down).
Near 0: Choppy, inefficient movement (no trend).
> +50: Strong bullish trend.
< -50: Strong bearish trend.
Think of it as “how direct is the price path” – values near +/-100 mean a straight line, near 0 means a random walk.
PFE = sign(direction) * sqrt(sum_sq_diff + direction^2) / sum_single_diff * 100The raw PFE is then smoothed with an EMA.
- Parameters:
- Returns:
PFE values, bounded roughly in [-100, 100].
- Return type:
Example
>>> result = polarized_fractal_efficiency(close, period=10)
- price_zone_oscillator(close, period=14)[source]¶
Price Zone Oscillator (PZO).
An EMA-based oscillator that classifies price action into zones. A positive close change gets +close, negative gets -close.
PZO = 100 * EMA(signed_close, period) / EMA(close, period)- Parameters:
- Returns:
PZO values, typically in [-100, 100].
- Return type:
Example
>>> result = price_zone_oscillator(close, period=14)
- ergodic_oscillator(close, fast=5, slow=20, signal=5)[source]¶
Ergodic Oscillator.
A True Strength Index variant that produces a histogram (oscillator minus signal line) for identifying momentum shifts.
- Parameters:
- Returns:
ergodic(the TSI line),signal, andhistogram.- Return type:
Example
>>> result = ergodic_oscillator(close) >>> result["ergodic"]
- elder_thermometer(high, low, period=22)[source]¶
Elder Thermometer.
Measures the distance of the current bar from the previous bar’s range, indicating how far price has moved beyond the prior bar.
Thermo = max(high - prev_high, prev_low - low, 0)The result is smoothed with an EMA.
- Parameters:
- Returns:
Elder Thermometer values (non-negative).
- Return type:
Example
>>> result = elder_thermometer(high, low, period=22)
- market_facilitation_index(high, low, volume)[source]¶
Market Facilitation Index (MFI / BW MFI).
Also known as the Bill Williams Market Facilitation Index. Measures the efficiency of price movement per unit of volume.
MFI = (high - low) / volume- Parameters:
- Returns:
Market Facilitation Index values.
- Return type:
Example
>>> result = market_facilitation_index(high, low, volume)
- efficiency_ratio(data, period=10)[source]¶
Efficiency Ratio (ER).
Measures the efficiency of price movement as the ratio of net directional change to the total path traveled.
ER = |close - close[n]| / sum(|close - close[1]|, n)Values near 1.0 indicate a strong trend; values near 0.0 indicate choppy, range-bound price action.
- Parameters:
- Returns:
Efficiency ratio values in [0, 1].
- Return type:
Example
>>> result = efficiency_ratio(close, period=10)
- trend_intensity_index(data, period=30)[source]¶
Trend Intensity Index (TII).
Measures the percentage of closes above or below the SMA over the look-back period.
TII = 100 * (count_above - count_below) / period- Parameters:
- Returns:
TII values in [-100, 100]. Positive indicates bullish bias; negative indicates bearish bias.
- Return type:
Example
>>> result = trend_intensity_index(close, period=30)
- directional_movement_index(high, low, close, period=14)[source]¶
Simplified Directional Movement Index (DMI).
Computes +DI and -DI without the full ADX smoothing, providing raw directional movement readings.
- Parameters:
- Returns:
plus_di(+DI) andminus_di(-DI), both in [0, 100].- Return type:
Example
>>> result = directional_movement_index(high, low, close, period=14) >>> result["plus_di"]
- kairi(data, period=14)[source]¶
KAIRI — percentage deviation from the SMA.
KAIRI = ((close - SMA(close, period)) / SMA(close, period)) * 100- Parameters:
- Returns:
KAIRI values (percentage, unbounded).
- Return type:
Example
>>> result = kairi(close, period=14)
- gopalakrishnan_range(high, low, period=5)[source]¶
Gopalakrishnan Range Index (GAPO).
GAPO = log(max_high - min_low) / log(period)Measures the log of the high-low range relative to the log of the look-back period. Higher values indicate larger ranges.
- Parameters:
- Returns:
GAPO values (non-negative).
- Return type:
Example
>>> result = gopalakrishnan_range(high, low, period=5)
- pretty_good_oscillator(high, low, close, period=14)[source]¶
Pretty Good Oscillator (PGO).
PGO = (close - SMA(close, period)) / ATR(period)Normalizes the deviation from the SMA by the ATR, producing a volatility-adjusted momentum reading.
- Parameters:
- Returns:
PGO values (unbounded).
- Return type:
Example
>>> result = pretty_good_oscillator(high, low, close, period=14)
- connors_tps(data, period=2)[source]¶
ConnorsRSI TPS component — cumulative streak RSI.
Computes an up/down streak series, then applies RSI to the streak values. This isolates the streak component of ConnorsRSI.
- Parameters:
- Returns:
Streak RSI values in [0, 100].
- Return type:
Example
>>> result = connors_tps(close, period=2)
- relative_momentum_index(data, period=14, momentum_period=4)[source]¶
Relative Momentum Index (RMI).
RSI applied to momentum (
close - close[momentum_period]) instead of the simple one-bar change. This produces a smoother oscillator that reacts to the direction and magnitude of price swings over momentum_period bars.- Parameters:
- Returns:
RMI values in [0, 100].
- Return type:
Example
>>> result = relative_momentum_index(close, period=14, momentum_period=4)
Candlestick Analytics¶
Structural candlestick measures: body size, shadow ratios, inside/outside bars, pin bars.
Candlestick analytics (structural metrics, not pattern recognition).
Functions in this module quantify the shape of individual candlesticks —
body size, shadow ratios, gaps, and structural bar classifications such as
inside bars, outside bars, and pin bars. All functions accept pd.Series
inputs and return pd.Series.
- candle_body_size(open_, close)[source]¶
Absolute candle body size.
Computed as
abs(close - open).- Interpretation:
Large body: Strong conviction – one side dominated.
Small body: Indecision or low activity.
Compare to average body size to identify unusual bars.
- Parameters:
- Return type:
- Returns:
Absolute body size for each bar.
Example
>>> body = candle_body_size(open_, close)
- candle_range(high, low)[source]¶
Full candle range (high minus low).
- Parameters:
- Return type:
- Returns:
Range for each bar.
Example
>>> rng = candle_range(high, low)
- upper_shadow_ratio(open_, high, low, close)[source]¶
Upper shadow as a fraction of the total candle range.
upper_shadow / (high - low)- Parameters:
- Return type:
- Returns:
Upper shadow ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = upper_shadow_ratio(open_, high, low, close)
- lower_shadow_ratio(open_, high, low, close)[source]¶
Lower shadow as a fraction of the total candle range.
lower_shadow / (high - low)- Parameters:
- Return type:
- Returns:
Lower shadow ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = lower_shadow_ratio(open_, high, low, close)
- body_to_range_ratio(open_, high, low, close)[source]¶
Body size as a fraction of the total candle range.
abs(close - open) / (high - low)- Parameters:
- Return type:
- Returns:
Body-to-range ratio in [0, 1]. Returns 0 when the range is zero.
Example
>>> ratio = body_to_range_ratio(open_, high, low, close)
- candle_direction(open_, close, doji_threshold=0.0)[source]¶
Candle direction: 1 (bullish), -1 (bearish), 0 (doji).
- Parameters:
- Return type:
- Returns:
Direction for each bar.
Example
>>> direction = candle_direction(open_, close)
- average_candle_body(open_, close, period=14)[source]¶
Simple moving average of absolute body sizes.
- Parameters:
- Return type:
- Returns:
Smoothed average body size.
Example
>>> avg_body = average_candle_body(open_, close, period=14)
- candle_momentum(open_, close, period=5)[source]¶
Sum of (close - open) over the last period bars.
A positive value indicates net bullish momentum; negative indicates bearish momentum.
- Parameters:
- Return type:
- Returns:
Cumulative close-minus-open over the window.
Example
>>> mom = candle_momentum(open_, close, period=5)
- body_gap(open_, close)[source]¶
Gap between consecutive candle bodies.
Computed as
open[i] - close[i-1], i.e. the distance between the current open and the previous close.- Parameters:
- Return type:
- Returns:
Body gap for each bar (positive = gap up, negative = gap down).
Example
>>> gap = body_gap(open_, close)
- inside_bar(high, low)[source]¶
Detect inside bars.
An inside bar has a high below the previous high and a low above the previous low (the bar is fully contained within the prior bar’s range).
- Interpretation:
Consolidation / contraction – the market is building energy.
Breakout of the inside bar’s range often leads to a significant directional move.
Multiple consecutive inside bars = stronger breakout.
- Trading rules:
Place a buy stop above the inside bar’s high and a sell stop below its low. Trade whichever triggers first.
- Parameters:
- Return type:
- Returns:
Boolean series (True where an inside bar is detected).
Example
>>> ib = inside_bar(high, low)
- outside_bar(high, low)[source]¶
Detect outside bars (engulfing range).
An outside bar has a high above the previous high and a low below the previous low (the bar’s range completely engulfs the prior bar’s range).
- Interpretation:
High volatility bar showing a battle between buyers and sellers.
The close direction determines who won: close near the high = bullish; close near the low = bearish.
Often marks a reversal or a volatility breakout.
- Parameters:
- Return type:
- Returns:
Boolean series (True where an outside bar is detected).
Example
>>> ob = outside_bar(high, low)
- pin_bar(open_, high, low, close, shadow_ratio=2.0)[source]¶
Detect pin bars.
A pin bar has a long shadow that is at least shadow_ratio times the body size. A bullish pin bar (1) has a long lower shadow; a bearish pin bar (-1) has a long upper shadow.
- Interpretation:
Bullish pin bar (1): Long lower shadow shows strong rejection of lower prices. Buy signal at support.
Bearish pin bar (-1): Long upper shadow shows strong rejection of higher prices. Sell signal at resistance.
One of the most widely used price action signals.
Most reliable at key support/resistance levels or after a sustained trend.
- Parameters:
- Return type:
- Returns:
1 (bullish pin bar), -1 (bearish pin bar), or 0.
Example
>>> pb = pin_bar(open_, high, low, close)
Price Action¶
Higher highs/lows, swing points, gap analysis, narrow range, key reversals.
Price action analysis.
Functions for detecting structural price action features such as swing
points, trend bars, gaps, range expansion/contraction, and reversal bars.
All functions accept pd.Series inputs and return pd.Series (or
dict where noted).
- higher_highs_lows(high, low, period=5)[source]¶
Detect sequences of HH/HL (uptrend) or LH/LL (downtrend).
Compares rolling highest-high and lowest-low over period bars to determine whether the market is making higher highs & higher lows (uptrend = 1), lower highs & lower lows (downtrend = -1), or neither (0).
- Interpretation:
1 (HH+HL): Classic uptrend structure. Price is making higher highs and higher lows – the textbook definition of an uptrend.
-1 (LH+LL): Classic downtrend structure.
0: No clear trend – consolidation, range, or transition.
When the sequence breaks (e.g. first lower low in an uptrend), it is an early warning of potential trend reversal.
- Parameters:
- Return type:
- Returns:
1 (uptrend / HH+HL), -1 (downtrend / LH+LL), or 0.
Example
>>> trend = higher_highs_lows(high, low, period=5)
- swing_high(high, lookback=2, lookahead=2)[source]¶
Detect swing highs.
A swing high is a bar whose high is greater than the highs of the lookback bars before it and the lookahead bars after it.
- Parameters:
- Return type:
- Returns:
Boolean series (True at swing highs).
Example
>>> sh = swing_high(high, lookback=2, lookahead=2)
- swing_low(low, lookback=2, lookahead=2)[source]¶
Detect swing lows.
A swing low is a bar whose low is less than the lows of the lookback bars before it and the lookahead bars after it.
- Parameters:
- Return type:
- Returns:
Boolean series (True at swing lows).
Example
>>> sl = swing_low(low, lookback=2, lookahead=2)
- trend_bars(close)[source]¶
Count consecutive up/down bars.
Returns a running count: positive values for consecutive up bars (close > previous close), negative values for consecutive down bars (close < previous close). The count resets to 0 on a flat bar.
- Parameters:
close (
Series) – Close prices.- Return type:
- Returns:
Consecutive bar count (positive = up streak, negative = down streak).
Example
>>> streaks = trend_bars(close)
- gap_analysis(open_, high, low, close, avg_range_period=20, breakaway_threshold=1.5)[source]¶
Detect and classify gaps.
Gaps are classified as:
common — gap size is below the average range
breakaway — gap size exceeds breakaway_threshold times the average range
exhaustion — gap that occurs after a sustained move (approximated by comparing the current close to a look-back SMA)
- Parameters:
open – Open prices.
high (
Series) – High prices.low (
Series) – Low prices.close (
Series) – Close prices.avg_range_period (
int, default:20) – Period for computing the average range.breakaway_threshold (
float, default:1.5) – Multiplier of average range for breakaway classification.open_ (Series)
- Return type:
- Returns:
Dictionary with keys
gap_size,gap_direction, andgap_type.gap_size— absolute gap sizegap_direction— 1 (gap up), -1 (gap down), 0 (no gap)gap_type— categorical string ("common","breakaway","exhaustion", or""for no gap)
Example
>>> result = gap_analysis(open_, high, low, close) >>> result["gap_type"]
- range_expansion(high, low, period=14, threshold=1.5)[source]¶
Detect range expansion (current range significantly above average).
Returns True when
(high - low) > threshold * avg_range.- Parameters:
- Return type:
- Returns:
Boolean series (True where range is expanded).
Example
>>> expanded = range_expansion(high, low)
- narrow_range(high, low, period=4)[source]¶
NR4/NR7 detection — narrowest range in period bars.
Returns True when the current bar’s range is the smallest in the last period bars (including itself).
period=4gives NR4;period=7gives NR7.- Parameters:
- Return type:
- Returns:
Boolean series (True at narrow-range bars).
Example
>>> nr4 = narrow_range(high, low, period=4) >>> nr7 = narrow_range(high, low, period=7)
- wide_range_bar(high, low, period=14, threshold=1.5)[source]¶
Wide Range Bar (WRB): range > threshold * average range.
- Parameters:
- Return type:
- Returns:
Boolean series (True at wide-range bars).
Example
>>> wrb = wide_range_bar(high, low)
- key_reversal(open_, high, low, close)[source]¶
Detect key reversal bars.
A bullish key reversal (1) makes a new low (below the previous low) but closes above the previous close.
A bearish key reversal (-1) makes a new high (above the previous high) but closes below the previous close.
- Parameters:
- Return type:
- Returns:
1 (bullish key reversal), -1 (bearish key reversal), or 0.
Example
>>> kr = key_reversal(open_, high, low, close)
- pivot_reversal(open_, high, low, close)[source]¶
Detect two-bar pivot reversal patterns at swing points.
A bullish pivot reversal (1): the previous bar makes a lower low than its predecessor, and the current bar closes above the previous bar’s high.
A bearish pivot reversal (-1): the previous bar makes a higher high than its predecessor, and the current bar closes below the previous bar’s low.
- Parameters:
- Return type:
- Returns:
1 (bullish pivot reversal), -1 (bearish pivot reversal), or 0.
Example
>>> pr = pivot_reversal(open_, high, low, close)