From a0c6abf561416be3f60ca3128e6b129100518274 Mon Sep 17 00:00:00 2001 From: Miha Date: Wed, 13 Apr 2022 12:13:03 -0700 Subject: [PATCH 1/2] RMA: use SMA for seeding the series --- pandas_ta/overlap/rma.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pandas_ta/overlap/rma.py b/pandas_ta/overlap/rma.py index 11ec03e..f99057e 100644 --- a/pandas_ta/overlap/rma.py +++ b/pandas_ta/overlap/rma.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from pandas import Series +from numpy import nan from pandas_ta._typing import DictLike, Int from pandas_ta.utils import v_offset, v_pos_default, v_series @@ -39,8 +40,14 @@ def rma( alpha = (1.0 / length) if length > 0 else 0.5 offset = v_offset(offset) - # Calculate - rma = close.ewm(alpha=alpha, min_periods=length).mean() + # Prefill initial bars with NaN and SMA + close = close.copy() + sma_nth = close[0:length].mean() + close[:length - 1] = nan + close.iloc[length - 1] = sma_nth + + # Calculate Exponential part + rma = close.ewm(span=length, alpha=alpha, min_periods=length).mean() # Offset if offset != 0: From 3f59c1b2ab12329905548195bf54b781485274f3 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Thu, 21 Apr 2022 15:54:16 -0700 Subject: [PATCH 2/2] BUG #515 #402 atr, rma, true_range --- pandas_ta/overlap/rma.py | 10 +--------- pandas_ta/volatility/atr.py | 24 ++++++++++++++++++++---- pandas_ta/volatility/true_range.py | 12 ++++++++---- setup.py | 5 +++-- tests/config.py | 8 ++++++++ tests/test_indicator_overlap.py | 7 ++++++- 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/pandas_ta/overlap/rma.py b/pandas_ta/overlap/rma.py index f99057e..72584de 100644 --- a/pandas_ta/overlap/rma.py +++ b/pandas_ta/overlap/rma.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from pandas import Series -from numpy import nan from pandas_ta._typing import DictLike, Int from pandas_ta.utils import v_offset, v_pos_default, v_series @@ -40,14 +39,7 @@ def rma( alpha = (1.0 / length) if length > 0 else 0.5 offset = v_offset(offset) - # Prefill initial bars with NaN and SMA - close = close.copy() - sma_nth = close[0:length].mean() - close[:length - 1] = nan - close.iloc[length - 1] = sma_nth - - # Calculate Exponential part - rma = close.ewm(span=length, alpha=alpha, min_periods=length).mean() + rma = close.ewm(alpha=alpha, adjust=False).mean() # Offset if offset != 0: diff --git a/pandas_ta/volatility/atr.py b/pandas_ta/volatility/atr.py index ca7e7a2..3f0c011 100644 --- a/pandas_ta/volatility/atr.py +++ b/pandas_ta/volatility/atr.py @@ -1,16 +1,25 @@ # -*- coding: utf-8 -*- +from numpy import nan from pandas import Series from pandas_ta._typing import DictLike, Int from pandas_ta.ma import ma from pandas_ta.maps import Imports -from pandas_ta.utils import v_drift, v_mamode, v_offset -from pandas_ta.utils import v_pos_default, v_series, v_talib +from pandas_ta.utils import ( + v_bool, + v_drift, + v_mamode, + v_offset, + v_pos_default, + v_series, + v_talib +) from .true_range import true_range def atr( high: Series, low: Series, close: Series, length: Int = None, - mamode: str = None, talib: bool = None, drift: Int = None, + mamode: str = None, talib: bool = None, + prenan: bool = None, drift: Int = None, offset: Int = None, **kwargs: DictLike ) -> Series: """Average True Range (ATR) @@ -29,6 +38,8 @@ def atr( mamode (str): See ``help(ta.ma)``. Default: 'rma' talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib version. Default: True + prenan (bool): If True, behave like TA Lib with some initial nan + based on drift (typically 1). Default: False drift (int): The difference period. Default: 1 offset (int): How many periods to offset the result. Default: 0 @@ -51,6 +62,7 @@ def atr( mamode = v_mamode(mamode, "rma") mode_tal = v_talib(talib) + prenan = v_bool(prenan, False) drift = v_drift(drift) offset = v_offset(offset) @@ -60,8 +72,12 @@ def atr( atr = ATR(high, low, close, length) else: tr = true_range( - high=high, low=low, close=close, drift=drift, talib=mode_tal + high=high, low=low, close=close, + talib=mode_tal, prenan=prenan, drift=drift ) + sma_nth = tr[0:length].mean() + tr[:length - 1] = nan + tr.iloc[length - 1] = sma_nth atr = ma(mamode, tr, length=length, talib=mode_tal) percent = kwargs.pop("percent", False) diff --git a/pandas_ta/volatility/true_range.py b/pandas_ta/volatility/true_range.py index 372662c..2d69870 100644 --- a/pandas_ta/volatility/true_range.py +++ b/pandas_ta/volatility/true_range.py @@ -3,13 +3,13 @@ from numpy import nan from pandas import concat, Series from pandas_ta._typing import DictLike, Int from pandas_ta.maps import Imports -from pandas_ta.utils import non_zero_range, v_drift, v_offset -from pandas_ta.utils import v_series, v_talib +from pandas_ta.utils import non_zero_range, v_bool, v_drift +from pandas_ta.utils import v_offset, v_series, v_talib def true_range( high: Series, low: Series, close: Series, - talib: bool = None, drift: Int = None, + talib: bool = None, prenan: bool = None, drift: Int = None, offset: Int = None, **kwargs: DictLike ) -> Series: """True Range @@ -26,6 +26,8 @@ def true_range( close (pd.Series): Series of 'close's talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib version. Default: True + prenan (bool): If True, behave like TA Lib with some initial nan + based on drift (typically 1). Default: False drift (int): The shift period. Default: 1 offset (int): How many periods to offset the result. Default: 0 @@ -41,6 +43,7 @@ def true_range( low = v_series(low) close = v_series(close) mode_tal = v_talib(talib) + prenan = v_bool(prenan, False) drift = v_drift(drift) offset = v_offset(offset) @@ -54,7 +57,8 @@ def true_range( ranges = [hl_range, high - pc, pc - low] true_range = concat(ranges, axis=1) true_range = true_range.abs().max(axis=1) - true_range.iloc[:drift] = nan + if prenan: + true_range.iloc[:drift] = nan # Offset if offset != 0: diff --git a/setup.py b/setup.py index 52d3d73..4bc5144 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ setup( "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Operating System :: OS Independent", "License :: OSI Approved :: MIT License", "Natural Language :: English", @@ -64,8 +65,8 @@ setup( extras_require={ "full": [ "alphaVantage-api", "matplotlib", "mplfinance", "numba", "polygon" - "scipy", "sklearn", "statsmodels", "stochastic", "ta-lib", "tqdm", - "vectorbt", "yfinance", + "python-dotenv", "scipy", "sklearn", "statsmodels", "stochastic", + "ta-lib", "tqdm", "vectorbt", "yfinance", ], "test": ["ta-lib"], }, diff --git a/tests/config.py b/tests/config.py index d08abdc..215acce 100644 --- a/tests/config.py +++ b/tests/config.py @@ -2,6 +2,7 @@ import datetime from pathlib import Path +from numpy import array from pandas import DataFrame, read_csv import pandas_datareader as pdr @@ -17,6 +18,13 @@ CORRELATION: str = "corr" # "sem" CORRELATION_THRESHOLD: IntFloat = 0.99 # Less than 0.99 is undesirable VERBOSE: bool = False +welles_wilder_df = DataFrame({ + "open": array([50, 50.7, 51.7, 52.5, 53.6, 54.4, 52.9, 52]), + "high": array([51.2, 51.8, 52.9, 53.7, 54.8, 54.4, 53.2, 52.7]), + "low": array([49.8, 50.3, 51.7, 52.3, 53.5, 52.9, 52, 52]), + "close": array([50.9, 51.5, 52.8, 53.5, 54.7, 53, 52, 52.2]) +}) + def error_analysis( df: DataFrame, kind: str, msg: str, diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index 9c0968f..f674a76 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -6,7 +6,12 @@ from pandas import DataFrame, Series import talib as tal -from .config import CORRELATION, CORRELATION_THRESHOLD, error_analysis, sample_data +from .config import ( + CORRELATION, + CORRELATION_THRESHOLD, + error_analysis, + sample_data, +) from .context import pandas_ta