Skip to content

Commit 49c6b81

Browse files
committed
add cci and roc
1 parent e8633fb commit 49c6b81

File tree

5 files changed

+107
-6
lines changed

5 files changed

+107
-6
lines changed

pyindicators/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .indicators import sma, rsi, ema, wilders_rsi, adx, \
1+
from .indicators import sma, rsi, ema, wilders_rsi, adx, roc, cci, \
22
crossover, is_crossover, wma, macd, willr, is_crossunder, crossunder, \
33
is_lower_low_detected, is_divergence, bollinger_width, \
44
is_below, is_above, get_slope, has_any_higher_then_threshold, \
@@ -47,5 +47,7 @@
4747
'bullish_divergence_multi_dataframe',
4848
'bollinger_bands',
4949
'bollinger_width',
50-
'atr'
50+
'atr',
51+
'cci',
52+
'roc'
5153
]

pyindicators/indicators/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from .stochastic_oscillator import stochastic_oscillator
2222
from .average_true_range import atr
2323
from .bollinger_bands import bollinger_bands, bollinger_width
24+
from .commodity_channel_index import cci
25+
from .rate_of_change import roc
2426

2527
__all__ = [
2628
'sma',
@@ -57,5 +59,7 @@
5759
'bullish_divergence_multi_dataframe',
5860
'atr',
5961
'bollinger_bands',
60-
'bollinger_width'
62+
'bollinger_width',
63+
'cci',
64+
'roc'
6165
]

pyindicators/indicators/bollinger_bands.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ def bollinger_width(
6464
source_column=source_column,
6565
period=period,
6666
std_dev=std_dev,
67-
middle_band_column='BB_middle_temp',
68-
upper_band_column='BB_upper_temp',
69-
lower_band_column='BB_lower_temp'
67+
middle_band_column_result_column='BB_middle_temp',
68+
upper_band_column_result_column='BB_upper_temp',
69+
lower_band_column_result_column='BB_lower_temp'
7070
)
7171

7272
if isinstance(data, PdDataFrame):
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import Union
2+
from pandas import DataFrame as PdDataFrame
3+
from polars import DataFrame as PlDataFrame
4+
import polars as pl
5+
from pyindicators.exceptions import PyIndicatorException
6+
7+
8+
def cci(
9+
data: Union[PdDataFrame, PlDataFrame],
10+
high_column='High',
11+
low_column='Low',
12+
close_column='Close',
13+
period=20,
14+
result_column='CCI'
15+
) -> Union[PdDataFrame, PlDataFrame]:
16+
"""
17+
Calculate the Commodity Channel Index (CCI) for a price series.
18+
19+
Args:
20+
data: Input DataFrame (pandas or polars).
21+
high_column: Name of the column with high prices.
22+
low_column: Name of the column with low prices.
23+
close_column: Name of the column with close prices.
24+
period: Lookback period for CCI calculation.
25+
result_column: Name of the result column to store CCI values.
26+
27+
Returns the original DataFrame with a new column for CCI.
28+
"""
29+
if isinstance(data, PdDataFrame):
30+
# Calculate CCI for pandas DataFrame
31+
typical_price = (data[high_column] +
32+
data[low_column] + data[close_column]) / 3
33+
sma = typical_price.rolling(window=period).mean()
34+
mad = (typical_price - sma).abs().rolling(window=period).mean()
35+
data[result_column] = (typical_price - sma) / (0.015 * mad)
36+
return data
37+
38+
elif isinstance(data, PlDataFrame):
39+
# Calculate CCI for polars DataFrame
40+
typical_price = (pl.col(high_column)
41+
+ pl.col(low_column)
42+
+ pl.col(close_column)) / 3
43+
sma = typical_price.rolling_mean(window_size=period)
44+
mad = (typical_price - sma).abs().rolling_mean(window_size=period)
45+
return data.with_columns(
46+
(typical_price - sma) / (0.015 * mad).alias(result_column)
47+
)
48+
49+
else:
50+
raise PyIndicatorException(
51+
"Input data must be a pandas or polars DataFrame."
52+
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from typing import Union
2+
from pandas import DataFrame as PdDataFrame
3+
from polars import DataFrame as PlDataFrame
4+
import polars as pl
5+
from pyindicators.exceptions import PyIndicatorException
6+
7+
8+
def roc(
9+
data: Union[PdDataFrame, PlDataFrame],
10+
source_column='Close',
11+
period=14,
12+
result_column='ROC'
13+
) -> Union[PdDataFrame, PlDataFrame]:
14+
"""
15+
Calculate the Rate of Change (ROC) for a price series.
16+
17+
Args:
18+
data: Input DataFrame (pandas or polars).
19+
source_column: Name of the column with price data.
20+
period: Lookback period for ROC calculation.
21+
result_column: Name of the result column to store ROC values.
22+
23+
Returns:
24+
DataFrame with a new column for ROC.
25+
"""
26+
if isinstance(data, PdDataFrame):
27+
# Calculate ROC for pandas DataFrame
28+
data[result_column] = data[source_column]\
29+
.pct_change(periods=period) * 100
30+
return data
31+
32+
elif isinstance(data, PlDataFrame):
33+
# Calculate ROC for polars DataFrame
34+
return data.with_columns(
35+
(
36+
pl.col(source_column).pct_change(periods=period) * 100
37+
).alias(result_column)
38+
)
39+
40+
else:
41+
raise PyIndicatorException(
42+
"Input data must be a pandas or polars DataFrame."
43+
)

0 commit comments

Comments
 (0)