From 741dfec44347b05d7dbf2545edaf42be69bfa747 Mon Sep 17 00:00:00 2001 From: johnliang Date: Tue, 8 Mar 2022 20:27:13 +0800 Subject: [PATCH 1/3] add ATR Trailing Stops (ATRTS) identifies --- pandas_ta/core.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pandas_ta/core.py b/pandas_ta/core.py index fdee64a..5a394a6 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1575,6 +1575,13 @@ class AnalysisIndicators(BasePandasObject): result = atr(high=high, low=low, close=close, length=length, mamode=mamode, offset=offset, **kwargs) return self._post_process(result, **kwargs) + def atrts(self, length=None, factor=None, mamode=None, offset=None, **kwargs): + high = self._get_column(kwargs.pop("high", "high")) + low = self._get_column(kwargs.pop("low", "low")) + close = self._get_column(kwargs.pop("close", "close")) + result = atrts(high=high, low=low, close=close, length=length, factor=factor, mamode=mamode, offset=offset, **kwargs) + return self._post_process(result, **kwargs) + def bbands(self, length=None, std=None, mamode=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) result = bbands(close=close, length=length, std=std, mamode=mamode, offset=offset, **kwargs) From cd438e2fec0e1105049e87f92ca760790ba3a49b Mon Sep 17 00:00:00 2001 From: johnliang Date: Tue, 8 Mar 2022 20:29:09 +0800 Subject: [PATCH 2/3] add ATR Trailing Stops (ATRTS) identifies --- pandas_ta/volatility/__init__.py | 1 + pandas_ta/volatility/atrts.py | 152 +++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 pandas_ta/volatility/atrts.py diff --git a/pandas_ta/volatility/__init__.py b/pandas_ta/volatility/__init__.py index a326697..1617352 100644 --- a/pandas_ta/volatility/__init__.py +++ b/pandas_ta/volatility/__init__.py @@ -13,3 +13,4 @@ from .rvi import rvi from .thermo import thermo from .true_range import true_range from .ui import ui +from .atrts import atrts diff --git a/pandas_ta/volatility/atrts.py b/pandas_ta/volatility/atrts.py new file mode 100644 index 0000000..51d0e96 --- /dev/null +++ b/pandas_ta/volatility/atrts.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +from .true_range import true_range +from pandas_ta import Imports +from pandas_ta.overlap import ma +from pandas_ta.volatility import atr +from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import DataFrame, Series +from functools import partial + +def atrts(high, low, close, length=None, factor=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): + """Indicator: Average True Range (ATR)""" + # Validate arguments + length = int(length) if length and length > 0 else 21 + factor = int(factor) if factor and factor > 0 else 3 + mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma" + high = verify_series(high, length) + low = verify_series(low, length) + close = verify_series(close, length) + drift = get_drift(drift) + offset = get_offset(offset) + mode_tal = bool(talib) if isinstance(talib, bool) else True + + if high is None or low is None or close is None: return + + # Calculate Result - start + atr_ = atr(high=high, low=low, close=close, length=length) + ma_ = ma(mamode, close, length=length*3) + upTrend = close > ma_ + dnTrend = close <= ma_ + prevP = close.shift(1) + + func_p = partial(calculateFunc, factor=factor) + atrts_ = [func_p(a,b,c,d) for a,b,c,d in zip(upTrend, dnTrend, prevP, atr_)] + atrts_ = Series(atrts_, index=close.index) + + #prevA = atrts_.shift(1) + #atrts = [tailingStopFunc(a,b,c,d) for a,b,c,d in zip(upTrend, dnTrend, prevA, atrts_)] + + #atrts = Series(atrts, index=close.index) + atrts = atrts_.shift(-1) + # Calculate Result - end + + percentage = kwargs.pop("percent", False) + if percentage: + atrts *= 100 / close + + # Offset + if offset != 0: + atrts = atrts.shift(offset) + + # Handle fills + if "fillna" in kwargs: + atrts.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + atrts.fillna(method=kwargs["fill_method"], inplace=True) + + # Name and Categorize it + atrts.name = f"ATRTS{mamode[0]}_{length}{'p' if percentage else ''}" + atrts.category = "volatility" + + return atrts + +def calculateFunc(upTrend, dnTrend, prevP, atr, factor): + if upTrend: + return prevP - atr * factor + elif dnTrend: + return prevP + atr * factor + +def tailingStopFunc(upTrend, dnTrend, prevA, atrts): + if upTrend: + if atrts < prevA: return prevA + elif dnTrend: + if atrts > prevA: return prevA + + +atrts.__doc__ = \ +"""ATR Trailing Stops (ATRTS) +identifies exit points for long and short positions. +First, an exponential moving average (EMA) of the input is taken to determine the current trend. +Then, the Average True Range (ATR) is calculated and multiplied by a user defined factor. +If the EMA is increasing (uptrend), the ATR product is subtracted from the price or, +if the EMA is decreasing (down trend), it is added to the price, and along with a few details the ATRTS is formed. +The user may change the position (long), input (close), method (EMA), period lengths, +percent factor and show entry option(see trading signals below). +This indicator’s definition is further expressed in the condensed code given in the calculation below. + +Sources: + https://www.motivewave.com/studies/atr_trailing_stops.htm + +Calculation: + //position = pos, user defined, default is long + //input = price, user defined, default is close + //method = moving average (ma), user defined, default is EMA + //period1 = maP, user defined, default is 63 + //period2 = artP, user defined, default is 21 + //factor = fac, user defined, default is 3 + //show entrys = showE, user defined, default is false + //index = current bar number, prev = previous + //LOE = less or equal, MOE = more or equal + //shortP = short position, longP = long position + //index = current bar number + + longP = pos == "Long"; + shortP = pos == "Short"; + atrts = 0, atr = 0; + ma = ma(method, maP, input); + prevP = price[index-1]; + prevA = ifNull(price, atrts[index]); //current atrts is plotted at index+1 + upTrend = price moreThan ma; + dnTrend = price LOE ma; + atr = atr(index, atrP); + if (upTrend) + atrts = price - fac * atr; + if (atrts lessThan prevA) atrts = prevA; + endIf + if (dnTrend) + atrts = price + fac * atr; + if (atrts moreThan prevA) atrts = prevA; + endIf + Plot: atrts[index+1]; + //Signals + sell = false, buy = false; + if (atrts != 0) + if (longP AND upTrend) + sell = price lessThan atrts; //sell to exit + buy = prevP lessThan atrts AND price moreThan atrts AND showE; //buy (enter) + endIf + if (shortP AND dnTrend) + sell = prevP moreThan atrts AND price lessThan atrts AND showE; //sell short (enter) + buy = price moreThan atrts; //buy to cover + endIf + endIf + +Args: (TODO) + high (pd.Series): Series of 'high's + low (pd.Series): Series of 'low's + close (pd.Series): Series of 'close's + length (int): It's period. Default: 14 + 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 + drift (int): The difference period. Default: 1 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: (TODO) + percent (bool, optional): Return as percentage. Default: False + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + +Returns: + pd.Series: New feature generated. +""" From e97d1a85b851d5e9987975c347a06035f277f4ed Mon Sep 17 00:00:00 2001 From: johnliang Date: Wed, 9 Mar 2022 20:12:48 +0800 Subject: [PATCH 3/3] to use new format --- pandas_ta/volatility/atrts.py | 195 +++++++++++++++++----------------- 1 file changed, 99 insertions(+), 96 deletions(-) diff --git a/pandas_ta/volatility/atrts.py b/pandas_ta/volatility/atrts.py index 51d0e96..e21f6a5 100644 --- a/pandas_ta/volatility/atrts.py +++ b/pandas_ta/volatility/atrts.py @@ -7,9 +7,103 @@ from pandas_ta.utils import get_drift, get_offset, verify_series from pandas import DataFrame, Series from functools import partial +try: + from numba import njit +except ImportError: + def njit(_): return _ + +@njit +def calculateFunc(upTrend, dnTrend, prevP, atr, factor): + if upTrend: + return prevP - atr * factor + elif dnTrend: + return prevP + atr * factor + +@njit +def tailingStopFunc(upTrend, dnTrend, prevA, atrts): + if upTrend: + if atrts < prevA: return prevA + elif dnTrend: + if atrts > prevA: return prevA + def atrts(high, low, close, length=None, factor=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): - """Indicator: Average True Range (ATR)""" - # Validate arguments + """ATR Trailing Stops (ATRTS) + identifies exit points for long and short positions. + First, an exponential moving average (EMA) of the input is taken to determine the current trend. + Then, the Average True Range (ATR) is calculated and multiplied by a user defined factor. + If the EMA is increasing (uptrend), the ATR product is subtracted from the price or, + if the EMA is decreasing (down trend), it is added to the price, and along with a few details the ATRTS is formed. + The user may change the position (long), input (close), method (EMA), period lengths, + percent factor and show entry option(see trading signals below). + This indicator’s definition is further expressed in the condensed code given in the calculation below. + + Sources: + https://www.motivewave.com/studies/atr_trailing_stops.htm + + Calculation: + //position = pos, user defined, default is long + //input = price, user defined, default is close + //method = moving average (ma), user defined, default is EMA + //period1 = maP, user defined, default is 63 + //period2 = artP, user defined, default is 21 + //factor = fac, user defined, default is 3 + //show entrys = showE, user defined, default is false + //index = current bar number, prev = previous + //LOE = less or equal, MOE = more or equal + //shortP = short position, longP = long position + //index = current bar number + + longP = pos == "Long"; + shortP = pos == "Short"; + atrts = 0, atr = 0; + ma = ma(method, maP, input); + prevP = price[index-1]; + prevA = ifNull(price, atrts[index]); //current atrts is plotted at index+1 + upTrend = price moreThan ma; + dnTrend = price LOE ma; + atr = atr(index, atrP); + if (upTrend) + atrts = price - fac * atr; + if (atrts lessThan prevA) atrts = prevA; + endIf + if (dnTrend) + atrts = price + fac * atr; + if (atrts moreThan prevA) atrts = prevA; + endIf + Plot: atrts[index+1]; + //Signals + sell = false, buy = false; + if (atrts != 0) + if (longP AND upTrend) + sell = price lessThan atrts; //sell to exit + buy = prevP lessThan atrts AND price moreThan atrts AND showE; //buy (enter) + endIf + if (shortP AND dnTrend) + sell = prevP moreThan atrts AND price lessThan atrts AND showE; //sell short (enter) + buy = price moreThan atrts; //buy to cover + endIf + endIf + + Args: + high (pd.Series): Series of 'high's + low (pd.Series): Series of 'low's + close (pd.Series): Series of 'close's + length (int): It's period. Default: 14 + factor (int): the multiplyer. Default: 3 + mamode (str): See ```help(ta.ma)```. Default: 'rma' + talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib + drift (int): The difference period. Default: 1 + offset (int): How many periods to offset the result. Default: 0 + + Kwargs: + percent (bool, optional): Return as percentage. Default: False + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + + Returns: + pd.Series: New feature generated. + """ + # Validate length = int(length) if length and length > 0 else 21 factor = int(factor) if factor and factor > 0 else 3 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma" @@ -22,7 +116,7 @@ def atrts(high, low, close, length=None, factor=None, mamode=None, talib=None, d if high is None or low is None or close is None: return - # Calculate Result - start + # Calculate - start atr_ = atr(high=high, low=low, close=close, length=length) ma_ = ma(mamode, close, length=length*3) upTrend = close > ma_ @@ -38,7 +132,7 @@ def atrts(high, low, close, length=None, factor=None, mamode=None, talib=None, d #atrts = Series(atrts, index=close.index) atrts = atrts_.shift(-1) - # Calculate Result - end + # Calculate - end percentage = kwargs.pop("percent", False) if percentage: @@ -58,95 +152,4 @@ def atrts(high, low, close, length=None, factor=None, mamode=None, talib=None, d atrts.name = f"ATRTS{mamode[0]}_{length}{'p' if percentage else ''}" atrts.category = "volatility" - return atrts - -def calculateFunc(upTrend, dnTrend, prevP, atr, factor): - if upTrend: - return prevP - atr * factor - elif dnTrend: - return prevP + atr * factor - -def tailingStopFunc(upTrend, dnTrend, prevA, atrts): - if upTrend: - if atrts < prevA: return prevA - elif dnTrend: - if atrts > prevA: return prevA - - -atrts.__doc__ = \ -"""ATR Trailing Stops (ATRTS) -identifies exit points for long and short positions. -First, an exponential moving average (EMA) of the input is taken to determine the current trend. -Then, the Average True Range (ATR) is calculated and multiplied by a user defined factor. -If the EMA is increasing (uptrend), the ATR product is subtracted from the price or, -if the EMA is decreasing (down trend), it is added to the price, and along with a few details the ATRTS is formed. -The user may change the position (long), input (close), method (EMA), period lengths, -percent factor and show entry option(see trading signals below). -This indicator’s definition is further expressed in the condensed code given in the calculation below. - -Sources: - https://www.motivewave.com/studies/atr_trailing_stops.htm - -Calculation: - //position = pos, user defined, default is long - //input = price, user defined, default is close - //method = moving average (ma), user defined, default is EMA - //period1 = maP, user defined, default is 63 - //period2 = artP, user defined, default is 21 - //factor = fac, user defined, default is 3 - //show entrys = showE, user defined, default is false - //index = current bar number, prev = previous - //LOE = less or equal, MOE = more or equal - //shortP = short position, longP = long position - //index = current bar number - - longP = pos == "Long"; - shortP = pos == "Short"; - atrts = 0, atr = 0; - ma = ma(method, maP, input); - prevP = price[index-1]; - prevA = ifNull(price, atrts[index]); //current atrts is plotted at index+1 - upTrend = price moreThan ma; - dnTrend = price LOE ma; - atr = atr(index, atrP); - if (upTrend) - atrts = price - fac * atr; - if (atrts lessThan prevA) atrts = prevA; - endIf - if (dnTrend) - atrts = price + fac * atr; - if (atrts moreThan prevA) atrts = prevA; - endIf - Plot: atrts[index+1]; - //Signals - sell = false, buy = false; - if (atrts != 0) - if (longP AND upTrend) - sell = price lessThan atrts; //sell to exit - buy = prevP lessThan atrts AND price moreThan atrts AND showE; //buy (enter) - endIf - if (shortP AND dnTrend) - sell = prevP moreThan atrts AND price lessThan atrts AND showE; //sell short (enter) - buy = price moreThan atrts; //buy to cover - endIf - endIf - -Args: (TODO) - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - close (pd.Series): Series of 'close's - length (int): It's period. Default: 14 - 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 - drift (int): The difference period. Default: 1 - offset (int): How many periods to offset the result. Default: 0 - -Kwargs: (TODO) - percent (bool, optional): Return as percentage. Default: False - fillna (value, optional): pd.DataFrame.fillna(value) - fill_method (value, optional): Type of fill method - -Returns: - pd.Series: New feature generated. -""" + return atrts \ No newline at end of file