From 16ee6f85bce0fe58a49d54faac1f68b9b3362e38 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Tue, 2 Jun 2020 20:43:54 -0700 Subject: [PATCH] ENH inertia added --- README.md | 4 +- pandas_ta/core.py | 12 ++++- pandas_ta/momentum/__init__.py | 1 + pandas_ta/momentum/inertia.py | 72 ++++++++++++++++++++++++++++ pandas_ta/momentum/rvi.py | 4 +- tests/test_indicator_momentum.py | 5 ++ tests/test_indicator_momentum_ext.py | 5 ++ 7 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 pandas_ta/momentum/inertia.py diff --git a/README.md b/README.md index e46a577..f29f597 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ All the indicators return a named Series or a DataFrame in uppercase underscore Chande Kroll Stop (cksp) Entropy (entropy) Heikin-Ashi Candles (ha) + Inertia (inertia) KDJ (kdj) Parabolic Stop and Reverse (psar) Price Distance (pdist) @@ -189,7 +190,7 @@ df.ta.adjusted = None * _Heikin-Ashi_: **ha** -## _Momentum_ (26) +## _Momentum_ (27) * _Awesome Oscillator_: **ao** * _Absolute Price Oscillator_: **apo** @@ -201,6 +202,7 @@ df.ta.adjusted = None * _Chande Momentum Oscillator_: **cmo** * _Coppock Curve_: **coppock** * _Fisher Transform_: **fisher** +* _Inertia_: **inertia** * _KDJ_: **kdj** * _KST Oscillator_: **kst** * _Moving Average Convergence Divergence_: **macd** diff --git a/pandas_ta/core.py b/pandas_ta/core.py index b81ac0e..ef9b1ad 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -15,7 +15,7 @@ from pandas_ta.volatility import * from pandas_ta.volume import * from pandas_ta.utils import * -version = ".".join(("0", "1", "67b")) +version = ".".join(("0", "1", "68b")) def finalize(method): @wraps(method) @@ -460,6 +460,16 @@ class AnalysisIndicators(BasePandasObject): result = fisher(high=high, low=low, length=length, offset=offset, **kwargs) return result + @finalize + def inertia(self, open_=None, high=None, low=None, close=None, length=None, swma_length=None, offset=None, **kwargs): + open_ = self._get_column(open_, 'open') + high = self._get_column(high, 'high') + low = self._get_column(low, 'low') + close = self._get_column(close, 'close') + + result = inertia(open_=open_, high=high, low=low, close=close, length=length, swma_length=swma_length, offset=offset, **kwargs) + return result + @finalize def kdj(self, high=None, low=None, close=None, length=None, signal=None, offset=None, **kwargs): high = self._get_column(high, 'high') diff --git a/pandas_ta/momentum/__init__.py b/pandas_ta/momentum/__init__.py index b104c9a..4357344 100644 --- a/pandas_ta/momentum/__init__.py +++ b/pandas_ta/momentum/__init__.py @@ -9,6 +9,7 @@ from .cg import cg from .cmo import cmo from .coppock import coppock from .fisher import fisher +from .inertia import inertia from .kdj import kdj from .kst import kst from .macd import macd diff --git a/pandas_ta/momentum/inertia.py b/pandas_ta/momentum/inertia.py new file mode 100644 index 0000000..dcf88b5 --- /dev/null +++ b/pandas_ta/momentum/inertia.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +from .rvi import rvi +from pandas_ta.overlap import linreg +from pandas_ta.utils import get_offset, non_zero_range, verify_series + +def inertia(open_, high, low, close, length=None, swma_length=None, offset=None, **kwargs): + """Indicator: Inertia (INERTIA)""" + # Validate Arguments + open_ = verify_series(open_) + high = verify_series(high) + low = verify_series(low) + close = verify_series(close) + length = int(length) if length and length > 0 else 14 + swma_length = int(swma_length) if swma_length and swma_length > 0 else 4 + offset = get_offset(offset) + + # Calculate Result + rvidf = rvi(open_, high, low, close, length=length, swma_length=swma_length) + inertia = linreg(rvidf[rvidf.columns[0]], length=length) + + # Offset + if offset != 0: + inertia = inertia.shift(offset) + + # Handle fills + if 'fillna' in kwargs: + inertia.fillna(kwargs['fillna'], inplace=True) + if 'fill_method' in kwargs: + inertia.fillna(method=kwargs['fill_method'], inplace=True) + + # Name & Category + inertia.name = f"INERTIA_{length}_{swma_length}" + inertia.category = "momentum" + + return inertia + + + +inertia.__doc__ = \ +"""Inertia (INERTIA) + +Inertia was developed by Donald Dorsey and was introduced his article +in September, 1995. It is the Relative Vigor Index smoothed by the Least +Squares Moving Average. Postive Inertia when values are greater than 50, +Negative Inertia otherwise. + +Sources: + https://www.investopedia.com/terms/r/relative_vigor_index.asp + +Calculation: + Default Inputs: + length=14, swma_length=4 + LSQRMA = Least Squares Moving Average + + INERTIA = LSQRMA(RVI) + +Args: + open_ (pd.Series): Series of 'open's + 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 + swma_length (int): It's period. Default: 4 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + +Returns: + pd.Series: New feature generated. +""" \ No newline at end of file diff --git a/pandas_ta/momentum/rvi.py b/pandas_ta/momentum/rvi.py index e04ea5f..8a88ebd 100644 --- a/pandas_ta/momentum/rvi.py +++ b/pandas_ta/momentum/rvi.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from pandas import DataFrame -from ..overlap.swma import swma -from ..utils import get_offset, non_zero_range, verify_series +from pandas_ta.overlap import swma +from pandas_ta.utils import get_offset, non_zero_range, verify_series def rvi(open_, high, low, close, length=None, swma_length=None, offset=None, **kwargs): """Indicator: Relative Vigor Index (RVI)""" diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 0861343..51eaffa 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -149,6 +149,11 @@ class TestMomentum(TestCase): self.assertIsInstance(result, Series) self.assertEqual(result.name, 'FISHERT_5') + def test_inertia(self): + result = pandas_ta.inertia(self.open, self.high, self.low, self.close) + self.assertIsInstance(result, Series) + self.assertEqual(result.name, 'INERTIA_14_4') + def test_kdj(self): result = pandas_ta.kdj(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) diff --git a/tests/test_indicator_momentum_ext.py b/tests/test_indicator_momentum_ext.py index 68e729f..7ff20a4 100644 --- a/tests/test_indicator_momentum_ext.py +++ b/tests/test_indicator_momentum_ext.py @@ -73,6 +73,11 @@ class TestMomentumExtension(TestCase): self.assertIsInstance(self.data, DataFrame) self.assertEqual(self.data.columns[-1], 'FISHERT_5') + def test_inertia_ext(self): + self.data.ta.inertia(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(self.data.columns[-1], 'INERTIA_14_4') + def test_kdj_ext(self): self.data.ta.kdj(append=True) self.assertIsInstance(self.data, DataFrame)