A tool to detect RSI divergences in price data.
Project description
rsi-divergence-detector
Lightweight, tested Python library & CLI for detecting RSI divergences (regular & hidden) on time‑series price data.
It uses Wilder‑style RSI, robust peak finding via SciPy, and deterministic, monotonic pivot pairing with adaptive defaults so you can get useful results with minimal tuning. Clean function contracts, no hidden globals, and a tidy tabular output.
Python: 3.9–3.13 · Dependencies: NumPy (>=1.26), pandas (>=2.2.2), SciPy (>=1.13), Typer (for the CLI)
Table of Contents
Features
- ✅ Regular & hidden RSI divergences:
regular_bullish,regular_bearish,hidden_bullish,hidden_bearish. - ✅ Deterministic pairing: nearest‑in‑time RSI pivots for each consecutive price pivot pair; monotonic in time (no re‑use/backtracking).
- ✅ Adaptive defaults for peak gates (prominence/width/distance) to stay scale‑aware without hand‑tuning.
- ✅ Clear contracts: explicit, stateless functions returning a tidy DataFrame.
- ✅ CLI + Python API (Typer‑powered CLI with
--help). - ✅ Tests: unit tests (RSI consistency & divergence scenarios) + property tests (Hypothesis) + CLI smoke tests.
Install
From PyPI:
pip install rsi-divergence-detector
Uses prebuilt wheels for NumPy/SciPy on common platforms; keep Python/pip recent for smooth installs.
Quickstart (Python API)
import pandas as pd
from rsi_divergence import calculate_rsi, find_divergences
# Price series with DatetimeIndex
close = pd.read_csv("ohlc.csv", index_col=0, parse_dates=True)["close"]
# Wilder-style RSI (period=14)
rsi = calculate_rsi(close, period=14)
# Detect divergences using adaptive defaults
divs = find_divergences(
prices=close,
rsi=rsi,
rsi_period=14, # used for adaptive defaults
max_lag=3, # bars allowed between paired price/RSI pivots
include_hidden=True, # include hidden (continuation) divergences
)
print(divs.head())
Quickstart (CLI)
Read a CSV with a close column (index = timestamps):
rsi_divergence --file ohlc.csv --rsi-period 14
Optional tuning (omit to use adaptive defaults):
rsi_divergence \
--file ohlc.csv \
--rsi-period 14 \
--price-prominence 0.25 \
--rsi-prominence 5 \
--price-width 2 \
--rsi-width 2 \
--distance 7 \
--max-lag 3 \
--include-hidden
The CLI is built with Typer;
--helpshows all options and help text.
Returned Schema
The detector returns a pd.DataFrame with one row per divergence:
['kind','p1_idx','p1_price','p2_idx','p2_price','r1_idx','r1_val','r2_idx','r2_val']
kind: one ofregular_bullish,regular_bearish,hidden_bullish,hidden_bearishp1_idx,p2_idx: timestamps of the price pivots (first → second)p1_price,p2_price: price at those pivotsr1_idx,r2_idx: timestamps of the paired RSI pivotsr1_val,r2_val: RSI values at those pivots
Concepts & Algorithm
Divergences we detect
- Regular bullish: price makes lower low (LL), RSI makes higher low (HL) → potential reversal up.
- Regular bearish: price makes higher high (HH), RSI makes lower high (LH) → potential reversal down.
- Hidden bullish: price HL, RSI LL → continuation of uptrend.
- Hidden bearish: price LH, RSI HH → continuation of downtrend.
Hidden divergences are commonly framed as trend continuation; regular divergences as reversal‑leaning.
RSI (Wilder‑style)
We compute RSI exactly as described by Wilder: average gains/losses smoothed with Wilder’s smoothing (a form of exponential smoothing with $\alpha = 1/\text{period}$), then $\text{RSI} = 100 - \frac{100}{1+RS} \quad \text{where} \quad RS = \frac{\text{avg gain}}{\text{avg loss}}.$ The output is aligned to the input index and lives in [0, 100] (initial warm‑up may contain NaNs).
Peak detection (SciPy)
We locate local extrema on price and RSI using scipy.signal.find_peaks. Minima are obtained by applying find_peaks to the negated series. The following gates control selection:
- prominence — how much a peak stands out relative to its surroundings
- width — peak “thickness” in samples
- distance — minimum separation between neighboring peaks
Adaptive defaults (used when the parameter is None):
price_prominence≈0.5 * rolling_std(pct_change, window=rsi_period).iloc[-1] * last_price(scale‑aware)rsi_prominence=5.0(RSI points)price_width=2,rsi_width=2distance=max(1, rsi_period // 2)
Monotonic pivot pairing
For each consecutive price pivot pair (minima for bullish paths, maxima for bearish), we pick the nearest‑in‑time RSI pivots within ±max_lag bars. Pairing is monotonic: the second RSI pivot must occur after the first RSI pivot used in that loop, and we don’t re‑use RSI pivots out of order. When comparisons below hold, we append a row:
regular_bullish:price2 < price1andrsi2 > rsi1regular_bearish:price2 > price1andrsi2 < rsi1hidden_bullish:price2 > price1andrsi2 < rsi1hidden_bearish:price2 < price1andrsi2 > rsi1
allow_equal=True (default) makes comparisons inclusive (tolerant to ties on intrabar data).
API Reference
calculate_rsi
calculate_rsi(prices: pd.Series, period: int = 14) -> pd.Series
Contract
prices: non‑emptypd.Serieswith a monotonic increasingDatetimeIndex.- Returns an RSI
pd.Seriesaligned toprices.indexwith values in [0,100] (warm‑up may contain NaNs). - Implements Wilder‑style smoothing (equivalent to an EMA with
alpha = 1/period,adjust=False).
find_divergences
find_divergences(
prices: pd.Series,
rsi: pd.Series,
*,
rsi_period: int = 14,
price_prominence: float | None = None,
rsi_prominence: float | None = None,
price_width: int | None = None,
rsi_width: int | None = None,
distance: int | None = None,
max_lag: int = 3,
include_hidden: bool = True,
allow_equal: bool = True,
) -> pd.DataFrame
Inputs
prices,rsi: numericpd.Serieswith the sameDatetimeIndex(RSI will be reindexed toprices).rsican have initial NaNs (warm‑up).- Adaptive defaults apply when gates are
None(see above).
Pairing & rules
- For each consecutive price pivot pair, find nearest RSI pivots within ±
max_lagbars, enforcing monotonic time. - Append a row when the rule for the target divergence holds (see Monotonic pivot pairing).
Returns
- A tidy
pd.DataFramewith the columns described here.
Tuning Tips
- Too few pivots? Lower
price_prominence/rsi_prominenceand/ordistance, or increasemax_lag. - Too many/weak pivots? Increase prominence and width.
- Intraday noise: increase
widthanddistanceto suppress micro‑wiggles. - Interested in classical RSI zones (≈30/70)? Filter results post‑hoc by
r1_val/r2_valranges. (Heuristics, not hard rules.)
Examples
Minimal flow
import pandas as pd
from rsi_divergence import calculate_rsi, find_divergences
close = pd.read_csv("ohlc.csv", index_col=0, parse_dates=True)["close"]
rsi = calculate_rsi(close, period=14)
divs = find_divergences(close, rsi, rsi_period=14, include_hidden=True)
print(divs.tail())
Filtering for “strong” bearish signals
strong = divs[(divs["kind"].str.contains("bearish")) & (divs["r2_val"] > 65.0)]
Plotting (optional)
Layer p1/p2 and r1/r2 pivots over candles (e.g., with mplfinance) to visualize each divergence. Plotting is intentionally out‑of‑scope here to keep the core lightweight.
Testing
We ship unit tests and property tests:
- Unit tests: RSI contract, range, Wilder consistency; deterministic divergence scenarios; CLI smoke test.
- Property tests (Hypothesis): generator‑driven random but valid series; no crashes, stable schema.
Run the suite:
pip install -r requirements-dev.txt # or: pip install -e ".[dev]"
pytest -q
Versioning / Compatibility
- Python: 3.9–3.13
- NumPy: ≥ 1.26 (works with NumPy 2.x)
- pandas: ≥ 2.2.2
- SciPy: ≥ 1.13
We avoid strict upper pins to reduce resolver conflicts across environments.
Limitations & Notes
- Divergences can persist without immediate reversal/continuation; confirm with broader structure/flow.
- Peak‑based methods are parameter‑sensitive; adaptive defaults help, but context matters (market, timeframe).
- This library does not predict or place orders; it annotates structure to support your own analysis.
Roadmap
A forward‑looking sketch (subject to change):
- Optional zone‑aware scoring (down‑weight signals far from RSI 30/70 bands).
- Multi‑timeframe aggregation (e.g., 1h pivots confirming 5m signals).
- Volume/OI/CVD hooks for richer filters.
- Numba/JIT fast‑path for large universes.
- Streaming examples (WebSocket → rolling detection).
PRs welcome!
References
-
RSI (Wilder) and smoothing background — StockCharts “RSI” notes; TC2000 help; Wikipedia overview.
-
Divergence (regular vs hidden) — Babypips primers; Investopedia overviews.
- Babypips Hidden Divergence: https://www.babypips.com/learn/forex/hidden-divergence
- Babypips Divergence Cheatsheet: https://www.babypips.com/learn/forex/divergence-cheat-sheet
- Investopedia Divergence: https://www.investopedia.com/terms/d/divergence.asp
-
SciPy peak selection —
find_peaks,peak_prominences,peak_widths. -
Typer (CLI) — official docs.
- Typer: https://typer.tiangolo.com/
SEO Keywords
RSI divergence detector, RSI hidden divergence, Wilder RSI Python, RSI Wilder smoothing, RSI divergence Python library, SciPy find_peaks RSI, RSI divergence CLI, regular vs hidden divergence, price oscillator divergence, RSI 30/70 heuristic, pandas RSI, quantitative trading divergence, Python technical analysis.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rsi_divergence_detector-0.1.0.tar.gz.
File metadata
- Download URL: rsi_divergence_detector-0.1.0.tar.gz
- Upload date:
- Size: 14.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a64d020d297f02bb078635865c4723dfd583f6047869b8f444ddf970335520a7
|
|
| MD5 |
75a0b186693f7ca7365b54e3cfd94c89
|
|
| BLAKE2b-256 |
b1c5a8f65a79464b213adfc32d134d549a03b9b8b624e0bc34c48fe6473f5878
|
File details
Details for the file rsi_divergence_detector-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rsi_divergence_detector-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5129f9d58f30a4a15640cf5e493775876e20bf82b81accee69fa11c54d6fb9f
|
|
| MD5 |
594a8972ee59127e67b8026cdc8fbd95
|
|
| BLAKE2b-256 |
36f540d322b507455b2dd0c0e8852ebcbfd4e0b9440c57160fa1a29df21ad031
|