diff --git a/README.md b/README.md index 13016d7..00d5595 100644 --- a/README.md +++ b/README.md @@ -601,6 +601,7 @@ help(ta.yf) * Default is John Carter's. Enable Lazybear's with ```lazybear=True``` * _Stochastic Oscillator_: **stoch** * _Stochastic RSI_: **stochrsi** +* _TD Sequential_: **td** * _Trix_: **trix** * _True strength index_: **tsi** * _Ultimate Oscillator_: **uo** diff --git a/pandas_ta/core.py b/pandas_ta/core.py index d49e4e6..5de6004 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1043,6 +1043,12 @@ class AnalysisIndicators(BasePandasObject): result = stochrsi(high=high, low=low, close=close, length=length, rsi_length=rsi_length, k=k, d=d, offset=offset, **kwargs) return self._post_process(result, **kwargs) + def td(self, offset=None, show_all=True, **kwargs): + close = self._get_column(kwargs.pop("close", "close")) + + result = td(close=close, offset=offset, show_all=show_all, **kwargs) + return self._post_process(result, **kwargs) + def trix(self, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) result = trix(close=close, length=length, signal=signal, scalar=scalar, drift=drift, offset=offset, **kwargs) diff --git a/pandas_ta/momentum/__init__.py b/pandas_ta/momentum/__init__.py index 5384434..9eaa0fb 100644 --- a/pandas_ta/momentum/__init__.py +++ b/pandas_ta/momentum/__init__.py @@ -31,6 +31,7 @@ from .smi import smi from .squeeze import squeeze from .stoch import stoch from .stochrsi import stochrsi +from .td import td from .trix import trix from .tsi import tsi from .uo import uo diff --git a/pandas_ta/momentum/td.py b/pandas_ta/momentum/td.py new file mode 100644 index 0000000..d60a415 --- /dev/null +++ b/pandas_ta/momentum/td.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +import numpy as np +from pandas import DataFrame, Series +from pandas_ta.utils import get_offset, verify_series + +def true_sequence_count(s): + index = s.where(s == False).last_valid_index() + + if index is None: + return s.count() + else: + s = s[s.index > index] + return s.count() + +def calc_td(close, direction, show_all): + td_bool = close.diff(4) > 0 if direction=='up' else close.diff(4) < 0 + td_num = np.where(td_bool, td_bool.rolling(13, min_periods=0).apply(true_sequence_count), 0) + td_num = Series(td_num) + + if show_all: + td_num = td_num.mask(td_num == 0) + else: + td_num = td_num.mask(~td_num.between(6,9)) + + return td_num + +def td(close, offset=None, show_all=True, **kwargs): + up = calc_td(close, 'up', show_all) + down = calc_td(close, 'down', show_all) + df = DataFrame({'TD_up': up, 'TD_down': down}) + + # Offset + if offset and offset != 0: + df = df.shift(offset) + + if "fillna" in kwargs: + df.fillna(kwargs["fillna"], inplace=True) + + # Name & Category + df.name = "TD" + df.category = "momentum" + + return df + +td.__doc__ = \ +"""TD Sequential (TD) + +TD Sequential indicator. + +Sources: + https://tradetrekker.wordpress.com/tdsequential/ + +Calculation: + compare current close price with 4 days ago price, up to 13 days. + for the consecutive ascending or descending price sequence, display 6th to 9th day value. + +Args: + close (pd.Series): Series of 'close's + offset (int): How many periods to offset the result. Default: 0 + show_all (bool): default True, show 1 - 13. If set to false, only show 6 - 9 + +Kwargs: + fillna (value, optional): pd.DataFrame.fillna(value) + +Returns: + pd.DataFrame: New feature generated. +""" \ No newline at end of file diff --git a/tests/test_ext_indicator_momentum.py b/tests/test_ext_indicator_momentum.py index c4d6900..8beae3f 100644 --- a/tests/test_ext_indicator_momentum.py +++ b/tests/test_ext_indicator_momentum.py @@ -207,6 +207,11 @@ class TestMomentumExtension(TestCase): self.assertIsInstance(self.data, DataFrame) self.assertEqual(list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"]) + def test_td_ext(self): + self.data.ta.td(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(list(self.data.columns[-2:]), ["TD_up", "TD_down"]) + def test_trix_ext(self): self.data.ta.trix(append=True) self.assertIsInstance(self.data, DataFrame) diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 5aa9a84..51ec9ad 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -362,6 +362,12 @@ class TestMomentum(TestCase): self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "STOCHRSI_14_14_3_3") + def test_td(self): + # TD Sequential + result = pandas_ta.td(self.close) + self.assertIsInstance(result, DataFrame) + self.assertEqual(result.name, "TD") + def test_trix(self): result = pandas_ta.trix(self.close) self.assertIsInstance(result, DataFrame)