Skip to content

Commit 2873a73

Browse files
committed
add boillinger bands and fib retracements
1 parent 9346301 commit 2873a73

5 files changed

Lines changed: 561 additions & 12 deletions

File tree

pyindicators/__init__.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
from .indicators import sma, rsi, ema, wilders_rsi, adx, roc, cci, \
2-
crossover, is_crossover, wma, macd, willr, is_crossunder, crossunder, \
3-
is_lower_low_detected, is_divergence, bollinger_width, \
4-
is_below, is_above, get_slope, has_any_higher_then_threshold, \
5-
has_slope_above_threshold, has_any_lower_then_threshold, \
6-
has_values_above_threshold, has_values_below_threshold, is_down_trend, \
7-
is_up_trend, up_and_downtrends, detect_peaks, atr, bollinger_bands, \
8-
bearish_divergence, bullish_divergence, stochastic_oscillator, \
9-
bearish_divergence_multi_dataframe, bullish_divergence_multi_dataframe
1+
from .indicators import (
2+
sma, rsi, ema, wilders_rsi, adx, roc, cci,
3+
crossover, is_crossover, wma, macd, willr, is_crossunder, crossunder,
4+
is_lower_low_detected, is_divergence, bollinger_width,
5+
bollinger_overshoot, is_below, is_above, get_slope,
6+
has_any_higher_then_threshold, has_slope_above_threshold,
7+
has_any_lower_then_threshold, has_values_above_threshold,
8+
has_values_below_threshold, is_down_trend,
9+
is_up_trend, up_and_downtrends, detect_peaks, atr, bollinger_bands,
10+
bearish_divergence, bullish_divergence, stochastic_oscillator,
11+
bearish_divergence_multi_dataframe, bullish_divergence_multi_dataframe,
12+
fibonacci_retracement, fibonacci_retracement_levels, fibonacci_extension,
13+
moving_average_envelope, sma_envelope, ema_envelope
14+
)
1015
from .exceptions import PyIndicatorException
1116
from .date_range import DateRange
1217

@@ -60,7 +65,14 @@ def get_version():
6065
'bullish_divergence_multi_dataframe',
6166
'bollinger_bands',
6267
'bollinger_width',
68+
'bollinger_overshoot',
6369
'atr',
6470
'cci',
65-
'roc'
71+
'roc',
72+
'fibonacci_retracement',
73+
'fibonacci_retracement_levels',
74+
'fibonacci_extension',
75+
'moving_average_envelope',
76+
'sma_envelope',
77+
'ema_envelope'
6678
]

pyindicators/indicators/__init__.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@
2020
bullish_divergence_multi_dataframe
2121
from .stochastic_oscillator import stochastic_oscillator
2222
from .average_true_range import atr
23-
from .bollinger_bands import bollinger_bands, bollinger_width
23+
from .bollinger_bands import bollinger_bands, bollinger_width, \
24+
bollinger_overshoot
2425
from .commodity_channel_index import cci
2526
from .rate_of_change import roc
27+
from .fibonacci_retracement import fibonacci_retracement, \
28+
fibonacci_retracement_levels, fibonacci_extension
29+
from .moving_average_envelope import moving_average_envelope, \
30+
sma_envelope, ema_envelope
2631

2732
__all__ = [
2833
'sma',
@@ -60,6 +65,13 @@
6065
'atr',
6166
'bollinger_bands',
6267
'bollinger_width',
68+
'bollinger_overshoot',
6369
'cci',
64-
'roc'
70+
'roc',
71+
'fibonacci_retracement',
72+
'fibonacci_retracement_levels',
73+
'fibonacci_extension',
74+
'moving_average_envelope',
75+
'sma_envelope',
76+
'ema_envelope'
6577
]

pyindicators/indicators/bollinger_bands.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,111 @@ def bollinger_bands(
4646
)
4747

4848

49+
def bollinger_overshoot(
50+
data: Union[PdDataFrame, PlDataFrame],
51+
source_column='Close',
52+
period=20,
53+
std_dev=2,
54+
result_column='bollinger_overshoot'
55+
) -> Union[PdDataFrame, PlDataFrame]:
56+
"""
57+
Calculate Bollinger Band overshoot percentage for a price series.
58+
59+
Measures how far the price has exceeded the upper or lower band,
60+
expressed as a percentage of the half-band width (distance from
61+
middle to upper/lower band).
62+
63+
Calculation:
64+
- When price > upper band (bullish overshoot):
65+
Overshoot % = ((Price - Upper Band) / (Upper Band - Middle Band)) × 100
66+
- When price < lower band (bearish overshoot):
67+
Overshoot % = ((Price - Lower Band) / (Middle Band - Lower Band)) × 100
68+
- When price is within bands: 0%
69+
70+
Example interpretation:
71+
- A 40% overshoot means the price is 40% of the band width beyond the band
72+
- Positive values indicate overbought conditions (above upper band)
73+
- Negative values indicate oversold conditions (below lower band)
74+
- High overshoots (e.g., 40% for silver) indicate risk of mean reversion
75+
76+
Args:
77+
data: pandas or polars DataFrame with price data
78+
source_column: Column name containing the price data (default: 'Close')
79+
period: Rolling window period for calculation (default: 20)
80+
std_dev: Number of standard deviations for bands (default: 2)
81+
result_column: Name for the result column
82+
(default: 'bollinger_overshoot')
83+
84+
Returns:
85+
DataFrame with added overshoot percentage column
86+
"""
87+
# First calculate the bands
88+
data = bollinger_bands(
89+
data,
90+
source_column=source_column,
91+
period=period,
92+
std_dev=std_dev,
93+
middle_band_column_result_column='BB_middle_temp',
94+
upper_band_column_result_column='BB_upper_temp',
95+
lower_band_column_result_column='BB_lower_temp'
96+
)
97+
98+
if isinstance(data, PdDataFrame):
99+
import numpy as np
100+
101+
price = data[source_column]
102+
upper = data['BB_upper_temp']
103+
middle = data['BB_middle_temp']
104+
lower = data['BB_lower_temp']
105+
106+
# Calculate half-band width (same for upper and
107+
# lower with symmetric bands)
108+
half_band_width = upper - middle
109+
110+
# Calculate overshoot
111+
# Above upper band: positive overshoot
112+
# Below lower band: negative overshoot
113+
# Within bands: 0
114+
overshoot = np.where(
115+
price > upper,
116+
((price - upper) / half_band_width) * 100,
117+
np.where(
118+
price < lower,
119+
((price - lower) / half_band_width) * 100,
120+
0.0
121+
)
122+
)
123+
124+
data[result_column] = overshoot
125+
126+
# Drop temporary columns
127+
data = data.drop(
128+
columns=['BB_middle_temp', 'BB_upper_temp', 'BB_lower_temp']
129+
)
130+
return data
131+
132+
elif isinstance(data, PlDataFrame):
133+
half_band_width = pl.col('BB_upper_temp') - pl.col('BB_middle_temp')
134+
135+
overshoot = pl.when(pl.col(source_column) >
136+
pl.col('BB_upper_temp')).then(
137+
((pl.col(source_column) -
138+
pl.col('BB_upper_temp')) / half_band_width) * 100
139+
).when(pl.col(source_column) < pl.col('BB_lower_temp')).then(
140+
((pl.col(source_column) -
141+
pl.col('BB_lower_temp')) / half_band_width) * 100
142+
).otherwise(0.0)
143+
144+
return data.with_columns(
145+
overshoot.alias(result_column)
146+
).drop(['BB_middle_temp', 'BB_upper_temp', 'BB_lower_temp'])
147+
148+
else:
149+
raise PyIndicatorException(
150+
"Input data must be a pandas or polars DataFrame."
151+
)
152+
153+
49154
def bollinger_width(
50155
data: Union[PdDataFrame, PlDataFrame],
51156
source_column='Close',

0 commit comments

Comments
 (0)