From 1b499eb16deff82eab248478e9dca68b2fffd04a Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Mon, 2 Aug 2021 12:09:15 -0700 Subject: [PATCH] ENH smma indicator TST smma --- README.md | 14 ++-- pandas_ta/__init__.py | 4 +- pandas_ta/core.py | 5 ++ pandas_ta/overlap/__init__.py | 1 + pandas_ta/overlap/smma.py | 85 +++++++++++++++++++++++++ setup.py | 2 +- tests/test_ext_indicator_overlap_ext.py | 5 ++ tests/test_indicator_overlap.py | 5 ++ 8 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 pandas_ta/overlap/smma.py diff --git a/README.md b/README.md index 7f1ce2f..c62495f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever * [Candles](#candles-64) * [Cycles](#cycles-1) * [Momentum](#momentum-41) - * [Overlap](#overlap-33) + * [Overlap](#overlap-34) * [Performance](#performance-3) * [Statistics](#statistics-11) * [Trend](#trend-18) @@ -113,7 +113,7 @@ $ pip install pandas_ta Latest Version -------------- -Best choice! Version: *0.3.15b* +Best choice! Version: *0.3.16b* * Includes all fixes and updates between **pypi** and what is covered in this README. ```sh $ pip install -U git+https://github.com/twopirllc/pandas-ta @@ -723,7 +723,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
-### **Overlap** (33) +### **Overlap** (34) * _Arnaud Legoux Moving Average_: **alma** * _Double Exponential Moving Average_: **dema** @@ -749,6 +749,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"]) * _WildeR's Moving Average_: **rma** * _Sine Weighted Moving Average_: **sinwma** * _Simple Moving Average_: **sma** +* _Smoothed Moving Average_: **smma** * _Ehler's Super Smoother Filter_: **ssf** * _Supertrend_: **supertrend** * _Symmetric Weighted Moving Average_: **swma** @@ -969,15 +970,16 @@ trading account, or fund. See ```help(ta.drawdown)``` * _Cross Signals_ (**xsignals**) was created by Kevin Johnson. It is a wrapper of Trade Signals that returns Trends, Trades, Entries and Exits. Cross Signals are commonly used for **bbands**, **rsi**, **zscore** crossing some value either above or below two values at different times. See ```help(ta.xsignals)``` * _Directional Movement_ (**dm**) developed by J. Welles Wilder in 1978 attempts to determine which direction the price of an asset is moving. See ```help(ta.dm)``` * _Even Better Sinewave_ (**ebsw**) measures market cycles and uses a low pass filter to remove noise. See: ```help(ta.ebsw)``` -* _Jurik Moving Average_ (**jma**) attempts to eliminate noise to see the "true" underlying activity.. See: ```help(ta.jma)``` -* _Klinger Volume Oscillator_ (**kvo**) was developed by Stephen J. Klinger. It is designed to predict price reversals in a market by comparing volume to price.. See ```help(ta.kvo)``` +* _Jurik Moving Average_ (**jma**) attempts to eliminate noise to see the "true" underlying activity. See: ```help(ta.jma)``` +* _Klinger Volume Oscillator_ (**kvo**) was developed by Stephen J. Klinger. It is designed to predict price reversals in a market by comparing volume to price. See ```help(ta.kvo)``` +* _Smoothed Moving Average_ (**smma**) can be used to confirm trends and define areas of support and resistance. See: ```help(ta.smma)``` * _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two cascaded stochastic calculations with additional smoothing. See ```help(ta.stc)``` * _Squeeze Pro_ (**squeeze_pro**) is an extended version of "TTM Squeeze" from John Carter. See ```help(ta.squeeze_pro)``` * _Tom DeMark's Sequential_ (**td_seq**) attempts to identify a price point where an uptrend or a downtrend exhausts itself and reverses. Currently exlcuded from ```df.ta.strategy()``` for performance reasons. See ```help(ta.td_seq)``` * _Think or Swim Standard Deviation All_ (**tos_stdevall**) indicator which returns the standard deviation of data for the entire plot or for the interval of the last bars defined by the length parameter. See ```help(ta.tos_stdevall)``` -* _Vertical Horizontal Filter_ (**vhf**) was created by Adam White to identify trending and ranging markets.. See ```help(ta.vhf)``` +* _Vertical Horizontal Filter_ (**vhf**) was created by Adam White to identify trending and ranging markets. See ```help(ta.vhf)```
diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index 0e740b8..5da436b 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -56,8 +56,8 @@ Category = { "overlap": [ "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", "jma", "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4", - "pwma", "rma", "sinwma", "sma", "ssf", "supertrend", "swma", "t3", - "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma" + "pwma", "rma", "sinwma", "sma", "smma", "ssf", "supertrend", "swma", + "t3", "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma" ], # Performance "performance": ["log_return", "percent_return"], diff --git a/pandas_ta/core.py b/pandas_ta/core.py index fdee64a..608acd7 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1264,6 +1264,11 @@ class AnalysisIndicators(BasePandasObject): result = sma(close=close, length=length, offset=offset, **kwargs) return self._post_process(result, **kwargs) + def smma(self, length=None, offset=None, **kwargs): + close = self._get_column(kwargs.pop("close", "close")) + result = smma(close=close, length=length, offset=offset, **kwargs) + return self._post_process(result, **kwargs) + def ssf(self, length=None, poles=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) result = ssf(close=close, length=length, poles=poles, offset=offset, **kwargs) diff --git a/pandas_ta/overlap/__init__.py b/pandas_ta/overlap/__init__.py index 7a0f5ec..611c684 100644 --- a/pandas_ta/overlap/__init__.py +++ b/pandas_ta/overlap/__init__.py @@ -21,6 +21,7 @@ from .pwma import pwma from .rma import rma from .sinwma import sinwma from .sma import sma +from .smma import smma from .ssf import ssf from .supertrend import supertrend from .swma import swma diff --git a/pandas_ta/overlap/smma.py b/pandas_ta/overlap/smma.py new file mode 100644 index 0000000..5fbcc54 --- /dev/null +++ b/pandas_ta/overlap/smma.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +from numpy import nan as npNaN +from pandas_ta.overlap import ma +from pandas_ta.utils import get_offset, verify_series + + +def smma(close, length=None, mamode=None, talib=None, offset=None, **kwargs): + """Indicator: SMoothed Moving Average (SMMA)""" + # Validate Arguments + length = int(length) if length and length > 0 else 7 + min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length + close = verify_series(close, max(length, min_periods)) + offset = get_offset(offset) + mamode = mamode.lower() if isinstance(mamode, str) else "sma" + mode_tal = bool(talib) if isinstance(talib, bool) else True + + if close is None: return + + # Calculate Result + m = close.size + smma = close.copy() + smma[:length - 1] = npNaN + smma.iloc[length - 1] = ma(mamode, close[0:length], length=length, talib=mode_tal)[-1] + + for i in range(length, m): + smma.iloc[i] = ((length - 1) * smma.iloc[i - 1] + smma.iloc[i]) / length + + # Offset + if offset != 0: + smma = smma.shift(offset) + + # Handle fills + if "fillna" in kwargs: + smma.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + smma.fillna(method=kwargs["fill_method"], inplace=True) + + # Name & Category + smma.name = f"SMMA_{length}" + smma.category = "overlap" + + return smma + + +smma.__doc__ = \ +"""SMoothed Moving Average (SMMA) + +The SMoothed Moving Average (SMMA) is bootstrapped by default with a Simple +Moving Average (SMA). It tries to reduce noise rather than reduce lag. The +SMMA takes all prices into account and uses a long lookback period. Old prices +are never removed from the calculation, but they have only a minimal impact on +the Moving Average due to a low assigned weight. By reducing the noise it +removes fluctuations and plots the prevailing trend. The SMMA can be used to +confirm trends and define areas of support and resistance. A core component of +Bill William's Alligator indicator. + +Sources: + https://www.tradingview.com/scripts/smma/ + https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=173&Name=Moving_Average_-_Smoothed + +Calculation: + Default Inputs: + length=10, mamode="sma" + MA = Moving Average + + SMMA[0:length] = MA(mamode, close, length) + SMMA[:length] = ((length - 1) * SMMA[i] + close[i]) / length + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + mamode (str): See ```help(ta.ma)```. Default: 'sma' + talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib + version. Default: True + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + adjust (bool): Default: True + presma (bool, optional): If True, uses SMA for initial value. + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + +Returns: + pd.Series: New feature generated. +""" diff --git a/setup.py b/setup.py index b5f7c6f..220f3cb 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( "pandas_ta.volatility", "pandas_ta.volume" ], - version=".".join(("0", "3", "15b")), + version=".".join(("0", "3", "16b")), description=long_description, long_description=long_description, author="Kevin Johnson", diff --git a/tests/test_ext_indicator_overlap_ext.py b/tests/test_ext_indicator_overlap_ext.py index e6bdebc..db8a6f9 100644 --- a/tests/test_ext_indicator_overlap_ext.py +++ b/tests/test_ext_indicator_overlap_ext.py @@ -123,6 +123,11 @@ class TestOverlapExtension(TestCase): self.assertIsInstance(self.data, DataFrame) self.assertEqual(self.data.columns[-1], "SMA_10") + def test_smma_ext(self): + self.data.ta.smma(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(self.data.columns[-1], "SMMA_7") + def test_ssf_ext(self): self.data.ta.ssf(append=True, poles=2) self.assertIsInstance(self.data, DataFrame) diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index 5848969..4a435aa 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -327,6 +327,11 @@ class TestOverlap(TestCase): self.assertIsInstance(result, Series) self.assertEqual(result.name, "SMA_10") + def test_smma(self): + result = pandas_ta.smma(self.close) + self.assertIsInstance(result, Series) + self.assertEqual(result.name, "SMMA_7") + def test_ssf(self): result = pandas_ta.ssf(self.close, poles=2) self.assertIsInstance(result, Series)