Skip to content

Commit 765ec1e

Browse files
committed
Add new indicators: Buy-Side/Sell-Side Liquidity, Liquidity Sweeps, Volume-Gated Trend Ribbon; update Order Blocks and SuperTrend; add tests and images for new indicators.
1 parent 8cd2abe commit 765ec1e

13 files changed

Lines changed: 1855 additions & 28 deletions

README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pip install pyindicators
8383
* [Order Blocks](#order-blocks)
8484
* [Market Structure Break](#market-structure-break)
8585
* [Market Structure CHoCH/BOS](#market-structure-chochbos)
86+
* [Liquidity Sweeps](#liquidity-sweeps)
87+
* [Buyside & Sellside Liquidity](#buyside--sellside-liquidity)
8688
* [Pattern recognition](#pattern-recognition)
8789
* [Detect Peaks](#detect-peaks)
8890
* [Detect Bullish Divergence](#detect-bullish-divergence)
@@ -1678,6 +1680,152 @@ The function returns:
16781680

16791681
![MARKET_STRUCTURE_CHOCH_BOS](https://github.com/coding-kitties/PyIndicators/blob/main/static/images/indicators/market_structure_choch_bos.png)
16801682

1683+
#### Liquidity Sweeps
1684+
1685+
Liquidity Sweeps is a Smart Money Concept indicator that detects when price momentarily pierces a swing high or swing low—grabbing resting liquidity—before reversing. This behaviour is a hallmark of institutional order flow: stop-loss clusters sitting above swing highs (buyside liquidity) or below swing lows (sellside liquidity) get triggered, and price quickly snaps back.
1686+
1687+
Three detection modes are available:
1688+
1689+
- **Wicks** – the candle wick pierces the swing level but the close remains on the original side.
1690+
- **Outbreak / Retest** – price closes beyond the level, then a later candle retests it from the other side while closing back.
1691+
- **All** – combines both wick and outbreak/retest sweeps.
1692+
1693+
```python
1694+
def liquidity_sweeps(
1695+
data: Union[PdDataFrame, PlDataFrame],
1696+
swing_length: int = 5,
1697+
mode: str = "wicks",
1698+
high_column: str = "High",
1699+
low_column: str = "Low",
1700+
close_column: str = "Close",
1701+
bullish_sweep_column: str = "liq_sweep_bullish",
1702+
bearish_sweep_column: str = "liq_sweep_bearish",
1703+
sweep_high_column: str = "liq_sweep_high",
1704+
sweep_low_column: str = "liq_sweep_low",
1705+
sweep_type_column: str = "liq_sweep_type",
1706+
) -> Union[PdDataFrame, PlDataFrame]:
1707+
```
1708+
1709+
Example
1710+
1711+
```python
1712+
import pandas as pd
1713+
from pyindicators import (
1714+
liquidity_sweeps,
1715+
liquidity_sweep_signal,
1716+
get_liquidity_sweep_stats
1717+
)
1718+
1719+
# Create sample OHLC data
1720+
df = pd.DataFrame({
1721+
'High': [...],
1722+
'Low': [...],
1723+
'Close': [...]
1724+
})
1725+
1726+
# Detect liquidity sweeps (wick-through mode)
1727+
df = liquidity_sweeps(df, swing_length=5, mode="wicks")
1728+
print(df[['liq_sweep_bullish', 'liq_sweep_bearish', 'liq_sweep_high', 'liq_sweep_low']])
1729+
1730+
# Generate trading signals
1731+
# 1 = bullish sweep, -1 = bearish sweep, 0 = no sweep
1732+
df = liquidity_sweep_signal(df)
1733+
bullish_sweeps = df[df['liq_sweep_signal'] == 1]
1734+
1735+
# Get statistics
1736+
stats = get_liquidity_sweep_stats(df)
1737+
print(f"Total bullish sweeps: {stats['total_bullish']}")
1738+
print(f"Total bearish sweeps: {stats['total_bearish']}")
1739+
```
1740+
1741+
The function returns:
1742+
- `liq_sweep_bullish`: 1 when a bullish liquidity sweep is detected (sell-side liquidity grabbed)
1743+
- `liq_sweep_bearish`: 1 when a bearish liquidity sweep is detected (buy-side liquidity grabbed)
1744+
- `liq_sweep_high`: Price level of the swept swing high
1745+
- `liq_sweep_low`: Price level of the swept swing low
1746+
- `liq_sweep_type`: Type of sweep (`"wick"` or `"outbreak_retest"`)
1747+
1748+
**Trading Strategy:**
1749+
- Bullish sweeps below swing lows indicate potential long entries (smart money accumulation)
1750+
- Bearish sweeps above swing highs indicate potential short entries (smart money distribution)
1751+
- Use the sweep level (`liq_sweep_high` / `liq_sweep_low`) as a reference for stop-loss placement
1752+
1753+
![LIQUIDITY_SWEEPS](https://github.com/coding-kitties/PyIndicators/blob/main/static/images/indicators/liquidity_sweeps.png)
1754+
1755+
#### Buyside & Sellside Liquidity
1756+
1757+
Buyside & Sellside Liquidity is a Smart Money Concept indicator that identifies clustered swing-point liquidity pools, their breaches, and optional liquidity voids.
1758+
1759+
A *buyside liquidity level* forms when multiple swing highs (≥ `min_cluster_count`) cluster within an ATR-scaled margin band. A *sellside liquidity level* is the mirror image for swing lows. When price breaks through a level, a *breach* is recorded. Optionally, *liquidity voids* (large directional candles with minimal overlap) can be detected as areas price is likely to revisit.
1760+
1761+
```python
1762+
def buyside_sellside_liquidity(
1763+
data: Union[PdDataFrame, PlDataFrame],
1764+
detection_length: int = 7,
1765+
margin: float = 6.9,
1766+
buyside_margin: float = 2.3,
1767+
sellside_margin: float = 2.3,
1768+
detect_voids: bool = False,
1769+
atr_period: int = 10,
1770+
atr_void_period: int = 200,
1771+
min_cluster_count: int = 3,
1772+
max_swings: int = 50,
1773+
high_column: str = "High",
1774+
low_column: str = "Low",
1775+
open_column: str = "Open",
1776+
close_column: str = "Close",
1777+
) -> Union[PdDataFrame, PlDataFrame]:
1778+
```
1779+
1780+
Example
1781+
1782+
```python
1783+
import pandas as pd
1784+
from pyindicators import (
1785+
buyside_sellside_liquidity,
1786+
buyside_sellside_liquidity_signal,
1787+
get_buyside_sellside_liquidity_stats
1788+
)
1789+
1790+
# Create sample OHLC data
1791+
df = pd.DataFrame({
1792+
'Open': [...],
1793+
'High': [...],
1794+
'Low': [...],
1795+
'Close': [...]
1796+
})
1797+
1798+
# Detect buyside and sellside liquidity levels
1799+
df = buyside_sellside_liquidity(df, detection_length=7, detect_voids=True)
1800+
print(df[['buyside_liq_level', 'sellside_liq_level', 'buyside_liq_broken', 'sellside_liq_broken']])
1801+
1802+
# Generate trading signals
1803+
# 1 = sellside breached (may reverse up), -1 = buyside breached (may reverse down)
1804+
df = buyside_sellside_liquidity_signal(df)
1805+
breach_events = df[df['bsl_signal'] != 0]
1806+
1807+
# Get statistics
1808+
stats = get_buyside_sellside_liquidity_stats(df)
1809+
print(f"Buyside levels: {stats['total_buyside_levels']}")
1810+
print(f"Sellside levels: {stats['total_sellside_levels']}")
1811+
print(f"Total breaches: {stats['total_breaches']}")
1812+
```
1813+
1814+
The function returns:
1815+
- `buyside_liq_level` / `sellside_liq_level`: Price of the liquidity level
1816+
- `buyside_liq_top` / `buyside_liq_bottom`: Upper and lower bounds of the buyside zone
1817+
- `sellside_liq_top` / `sellside_liq_bottom`: Upper and lower bounds of the sellside zone
1818+
- `buyside_liq_broken` / `sellside_liq_broken`: 1 when the level is breached
1819+
- `liq_void_bullish` / `liq_void_bearish`: 1 when a liquidity void is detected (if `detect_voids=True`)
1820+
- `liq_void_top` / `liq_void_bottom`: Bounds of the void zone
1821+
1822+
**Trading Strategy:**
1823+
- Buyside levels act as resistance; a breach signals institutional selling (potential reversal down)
1824+
- Sellside levels act as support; a breach signals institutional buying (potential reversal up)
1825+
- Liquidity voids are imbalance zones that price often revisits—use as take-profit targets
1826+
1827+
![BUYSIDE_SELLSIDE_LIQUIDITY](https://github.com/coding-kitties/PyIndicators/blob/main/static/images/indicators/buy_side_sell_side_liquidity.png)
1828+
16811829
### Pattern Recognition
16821830

16831831
#### Detect Peaks

pyindicators/__init__.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
get_supertrend_stats,
2424
nadaraya_watson_envelope,
2525
zero_lag_ema_envelope,
26-
ema_trend_ribbon
26+
ema_trend_ribbon,
27+
volume_gated_trend_ribbon,
28+
liquidity_sweeps, liquidity_sweep_signal, get_liquidity_sweep_stats,
29+
buyside_sellside_liquidity, buyside_sellside_liquidity_signal,
30+
get_buyside_sellside_liquidity_stats
2731
)
2832
from .exceptions import PyIndicatorException
2933
from .date_range import DateRange
@@ -113,5 +117,12 @@ def get_version():
113117
'get_supertrend_stats',
114118
'nadaraya_watson_envelope',
115119
'zero_lag_ema_envelope',
116-
'ema_trend_ribbon'
120+
'ema_trend_ribbon',
121+
'volume_gated_trend_ribbon',
122+
'liquidity_sweeps',
123+
'liquidity_sweep_signal',
124+
'get_liquidity_sweep_stats',
125+
'buyside_sellside_liquidity',
126+
'buyside_sellside_liquidity_signal',
127+
'get_buyside_sellside_liquidity_stats'
117128
]

pyindicators/indicators/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
from .nadaraya_watson_envelope import nadaraya_watson_envelope
4848
from .zero_lag_ema_envelope import zero_lag_ema_envelope
4949
from .ema_trend_ribbon import ema_trend_ribbon
50+
from .volume_gated_trend_ribbon import volume_gated_trend_ribbon
51+
from .liquidity_sweeps import (
52+
liquidity_sweeps, liquidity_sweep_signal, get_liquidity_sweep_stats
53+
)
54+
from .buyside_sellside_liquidity import (
55+
buyside_sellside_liquidity, buyside_sellside_liquidity_signal,
56+
get_buyside_sellside_liquidity_stats
57+
)
5058

5159
__all__ = [
5260
'sma',
@@ -118,5 +126,12 @@
118126
'get_supertrend_stats',
119127
'nadaraya_watson_envelope',
120128
'zero_lag_ema_envelope',
121-
'ema_trend_ribbon'
129+
'ema_trend_ribbon',
130+
'volume_gated_trend_ribbon',
131+
'liquidity_sweeps',
132+
'liquidity_sweep_signal',
133+
'get_liquidity_sweep_stats',
134+
'buyside_sellside_liquidity',
135+
'buyside_sellside_liquidity_signal',
136+
'get_buyside_sellside_liquidity_stats'
122137
]

0 commit comments

Comments
 (0)