diff --git a/README.md b/README.md index cba400b..327b4c9 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ help(pd.DataFrame().ta.log_return) * _Pascal's Weighted Moving Average_: **pwma** * _William's Moving Average_: **rma** * _Simple Moving Average_: **sma** +* _Symmetric Weighted Moving Average_: **swma** * _T3 Moving Average_: **t3** * _Triple Exponential Moving Average_: **tema** * _Triangular Moving Average_: **trima** diff --git a/pandas_ta/core.py b/pandas_ta/core.py index f081804..1f9302b 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -449,6 +449,12 @@ class AnalysisIndicators(BasePandasObject): self._append(result, **kwargs) return result + def swma(self, close=None, length=None, offset=None, **kwargs): + close = self._get_column(close, 'close') + result = swma(close=close, length=length, offset=offset, **kwargs) + self._append(result, **kwargs) + return result + def t3(self, close=None, length=None, a=None, offset=None, **kwargs): close = self._get_column(close, 'close') result = t3(close=close, length=length, a=a, offset=offset, **kwargs) diff --git a/pandas_ta/overlap.py b/pandas_ta/overlap.py index 2909df3..130d197 100644 --- a/pandas_ta/overlap.py +++ b/pandas_ta/overlap.py @@ -3,7 +3,7 @@ import math import numpy as np import pandas as pd -from .utils import fibonacci, get_drift, get_offset, pascals_triangle, verify_series, weights +from .utils import fibonacci, get_drift, get_offset, pascals_triangle, symmetric_triangle, verify_series, weights @@ -477,6 +477,30 @@ def sma(close, length=None, offset=None, **kwargs): return sma +def swma(close, length=None, asc=None, offset=None, **kwargs): + """Indicator: Symmetric Weighted Moving Average (SWMA)""" + # Validate Arguments + close = verify_series(close) + length = int(length) if length and length > 0 else 10 + min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length + asc = asc if asc else True + offset = get_offset(offset) + + # Calculate Result + triangle = pascals_triangle(n=length - 1, weighted=True) + swma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True) + + # Offset + if offset != 0: + swma = swma.shift(offset) + + # Name & Category + swma.name = f"SWMA_{length}" + swma.category = 'overlap' + + return swma + + def t3(close, length=None, a=None, offset=None, **kwargs): """Indicator: T3""" # Validate Arguments @@ -1097,7 +1121,7 @@ Source: Kevin Johnson Calculation: Default Inputs: - length=10, + length=10 def weights(w): def _compute(x): @@ -1182,6 +1206,43 @@ Returns: """ +swma.__doc__ = \ +"""Symmetric Weighted Moving Average (SWMA) + +Symmetric Weighted Moving Average where weights are based on a symmetric +triangle. For example: n=3 -> [1, 2, 1], n=4 -> [1, 2, 2, 1], etc... This moving +average has variable length in contrast to TradingView's fixed length of 4. + +Source: + https://www.tradingview.com/study-script-reference/#fun_swma + +Calculation: + Default Inputs: + length=10 + + def weights(w): + def _compute(x): + return np.dot(w * x) + return _compute + + triangle = utils.symmetric_triangle(length - 1) + SWMA = close.rolling(length)_.apply(weights(triangle), raw=True) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + asc (bool): Recent values weigh more. Default: True + 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. +""" + + t3.__doc__ = \ """Tim Tillson's T3 Moving Average (T3) diff --git a/setup.py b/setup.py index cf41061..2f74baf 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ long_description = "An easy to use Python 3 Pandas Extension of Technical Analys setup( name = "pandas_ta", packages = ["pandas_ta"], - version = "0.1.10a", + version = "0.1.11a", description=long_description, long_description=long_description, author = "Kevin Johnson", diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index b77c550..35bae40 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -231,6 +231,11 @@ class TestOverlap(TestCase): except Exception as ex: error_analysis(result, CORRELATION, ex) + def test_swma(self): + result = self.overlap.swma(self.close) + self.assertIsInstance(result, Series) + self.assertEqual(result.name, 'SWMA_10') + def test_t3(self): result = self.overlap.t3(self.close) self.assertIsInstance(result, Series) diff --git a/tests/test_indicator_overlap_ext.py b/tests/test_indicator_overlap_ext.py index 98e81cb..5947fce 100644 --- a/tests/test_indicator_overlap_ext.py +++ b/tests/test_indicator_overlap_ext.py @@ -93,6 +93,11 @@ class TestOverlapExtension(TestCase): self.assertIsInstance(self.data, DataFrame) self.assertEqual(self.data.columns[-1], 'SMA_10') + def test_swma_ext(self): + self.data.ta.swma(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(self.data.columns[-1], 'SWMA_10') + def test_t3_ext(self): self.data.ta.t3(append=True) self.assertIsInstance(self.data, DataFrame)