From fb916b884a02ecc8a09650d85561d915f4e034f2 Mon Sep 17 00:00:00 2001 From: rengel8 <34138513+rengel8@users.noreply.github.com> Date: Fri, 5 Feb 2021 22:22:44 +0100 Subject: [PATCH 1/2] ALMA ALMA (new indicator) Arnaud Legoux Moving Average --- pandas_ta/__init__.py | 2 +- pandas_ta/core.py | 5 ++ pandas_ta/overlap/__init__.py | 1 + pandas_ta/overlap/alma.py | 87 +++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 pandas_ta/overlap/alma.py diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index bfb07df..4275b1d 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -49,7 +49,7 @@ Category = { ], # Overlap "overlap": [ - "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", + "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4", "pwma", "rma", "sinwma", "sma", "ssf", "supertrend", "swma", "t3", "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma" diff --git a/pandas_ta/core.py b/pandas_ta/core.py index fe82a07..6f73c03 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -955,6 +955,11 @@ class AnalysisIndicators(BasePandasObject): return self._post_process(result, **kwargs) # Overlap + def alma(self, length=None, sigma=None, distribution_offset=None, offset=None, **kwargs): + close = self._get_column(kwargs.pop("close", "close")) + result = alma(close=close, length=length, sigma=sigma, distribution_offset=distribution_offset, offset=offset, **kwargs) + return self._post_process(result, **kwargs) + def dema(self, length=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) result = dema(close=close, length=length, offset=offset, **kwargs) diff --git a/pandas_ta/overlap/__init__.py b/pandas_ta/overlap/__init__.py index 71725ae..c212759 100644 --- a/pandas_ta/overlap/__init__.py +++ b/pandas_ta/overlap/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from .alma import alma from .dema import dema from .ema import ema from .fwma import fwma diff --git a/pandas_ta/overlap/alma.py b/pandas_ta/overlap/alma.py new file mode 100644 index 0000000..2d8af98 --- /dev/null +++ b/pandas_ta/overlap/alma.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +from numpy import NaN as npNaN +from pandas import Series +from pandas_ta.utils import get_offset, verify_series +import math + + +def alma(close, length=None, sigma=None, distribution_offset=None, offset=None, **kwargs): + """Indicator: Arnaud Legoux Moving Average (ALMA)""" + # Validate Arguments + close = verify_series(close) + length = int(length) if length and length > 0 else 10 + sigma = float(sigma) if sigma and sigma > 0 else 6.0 + distribution_offset = float(distribution_offset) if distribution_offset and distribution_offset > 0 else 0.85 + offset = get_offset(offset) + + # Pre-Calculations + m = (distribution_offset * (length - 1)) + s = length / sigma + wtd = list(range(length)) + for j in range(0, length): + wtd[j] = math.exp(-1 * ((j - m) * (j - m)) / (2 * s * s)) + + # Calculate Result + result = [npNaN for _ in range(0, length - 1)] + [0] + for i in range(length, close.size): + window_sum = 0 + cum_sum = 0 + for j in range(0, length): + # wtd = math.exp(-1 * ((j - m) * (j - m)) / (2 * s * s)) # moved to pre-calc for efficiency + window_sum = window_sum + wtd[j] * close[i - j] + cum_sum = cum_sum + wtd[j] + almean = window_sum / cum_sum + if i == length: + result.append(npNaN) # additional one bar NaN as pre-roll + else: + result.append(almean) + + alma = Series(result, index=close.index) + + # Offset + if offset != 0: + alma = alma.shift(offset) + + # Handle fills + if "fillna" in kwargs: + alma.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + alma.fillna(method=kwargs["fill_method"], inplace=True) + + # Name & Category + alma.name = f"ALMA_{length}" + alma.category = "overlap" + + return alma + + +alma.__doc__ = \ +"""Arnaud Legoux Moving Average (ALMA) + +The ALMA moving average uses the curve of the Normal (Gauss) distribution, which can be shifted +from 0 to 1. This allows regulating the smoothness and high sensitivity of the indicator. +Sigma is another parameter that is responsible for the shape of the curve coefficients. This moving average +reduces lag of the data in conjunction with smoothing to reduce noise. + +Implemented for Pandas TA by rengel8 based on the source provided below. + +Sources: + https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/ + +Calculation: + refer to provided source + +Args: + close (pd.Series): Series of 'close's + length (int): It's period, window size. Default: 10 + sigma (float): Smoothing value. Default 6.0 + distribution_offset (float): Value to offset the distribution min 0 (smoother), max 1 (more responsive). Default 0.85 + 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. +""" From 82f12be4e68ec28e8686205cac329398487c4a1b Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Fri, 19 Feb 2021 11:06:03 -0800 Subject: [PATCH 2/2] ENH #216 alma indicator TST added DOC readme --- README.md | 8 +++++--- pandas_ta/overlap/alma.py | 19 +++++++++++-------- setup.py | 4 ++-- tests/test_ext_indicator_overlap_ext.py | 5 +++++ tests/test_indicator_overlap.py | 5 +++++ 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index cd33b0d..dac9be5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Pandas TA - A Technical Analysis Library in Python 3 ![Example Chart](/images/TA_Chart.png) -_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that leverages the Pandas library with more than 120 Indicators and Utility functions. Many commonly used indicators are included, such as: _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **_many more_**. +_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that leverages the Pandas library with more than 130 Indicators and Utility functions. Many commonly used indicators are included, such as: _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **_many more_**.
@@ -59,7 +59,7 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever # **Features** -* Has 120+ indicators and utility functions. +* Has 130+ indicators and utility functions. * Indicators are tightly correlated with the de facto [TA Lib](https://mrjbq7.github.io/ta-lib/) if they share common indicators. * Have the need for speed? By using the DataFrame _strategy_ method, you get **multiprocessing** for free! * Easily add _prefixes_ or _suffixes_ or both to columns names. Useful for Custom Chained Strategies. @@ -496,8 +496,9 @@ print(bothhl2.name) # "pre_HL2_post" |:--------:| | ![Example MACD](/images/SPY_MACD.png) | -### **Overlap** (30) +### **Overlap** (31) +* _Arnaud Legoux Moving Average_: **alma** * _Double Exponential Moving Average_: **dema** * _Exponential Moving Average_: **ema** * _Fibonacci's Weighted Moving Average_: **fwma** @@ -683,6 +684,7 @@ result = ta.cagr(df.close) ## **New Indicators** +* _Arnaud Legoux Moving Average_ (**alma**) uses the curve of the Normal (Gauss) distribution to allow regulating the smoothness and high sensitivity of the indicator. See: ```help(ta.alma)``` * _Drawdown_ (**drawdown**) shows the peak-to-trough decline during a specific period for an investment, trading account, or fund. See: ```help(ta.drawdown)``` * _Gann High-Low Activator_ (**hilo**) was created by Robert Krausz in a 1998. See: ```help(ta.hilo)``` diff --git a/pandas_ta/overlap/alma.py b/pandas_ta/overlap/alma.py index 2d8af98..bdd52d3 100644 --- a/pandas_ta/overlap/alma.py +++ b/pandas_ta/overlap/alma.py @@ -18,8 +18,8 @@ def alma(close, length=None, sigma=None, distribution_offset=None, offset=None, m = (distribution_offset * (length - 1)) s = length / sigma wtd = list(range(length)) - for j in range(0, length): - wtd[j] = math.exp(-1 * ((j - m) * (j - m)) / (2 * s * s)) + for i in range(0, length): + wtd[i] = math.exp(-1 * ((i - m) * (i - m)) / (2 * s * s)) # Calculate Result result = [npNaN for _ in range(0, length - 1)] + [0] @@ -49,7 +49,7 @@ def alma(close, length=None, sigma=None, distribution_offset=None, offset=None, alma.fillna(method=kwargs["fill_method"], inplace=True) # Name & Category - alma.name = f"ALMA_{length}" + alma.name = f"ALMA_{length}_{sigma}_{distribution_offset}" alma.category = "overlap" return alma @@ -58,10 +58,11 @@ def alma(close, length=None, sigma=None, distribution_offset=None, offset=None, alma.__doc__ = \ """Arnaud Legoux Moving Average (ALMA) -The ALMA moving average uses the curve of the Normal (Gauss) distribution, which can be shifted -from 0 to 1. This allows regulating the smoothness and high sensitivity of the indicator. -Sigma is another parameter that is responsible for the shape of the curve coefficients. This moving average -reduces lag of the data in conjunction with smoothing to reduce noise. +The ALMA moving average uses the curve of the Normal (Gauss) distribution, which +can be shifted from 0 to 1. This allows regulating the smoothness and high +sensitivity of the indicator. Sigma is another parameter that is responsible for +the shape of the curve coefficients. This moving average reduces lag of the data +in conjunction with smoothing to reduce noise. Implemented for Pandas TA by rengel8 based on the source provided below. @@ -75,7 +76,9 @@ Args: close (pd.Series): Series of 'close's length (int): It's period, window size. Default: 10 sigma (float): Smoothing value. Default 6.0 - distribution_offset (float): Value to offset the distribution min 0 (smoother), max 1 (more responsive). Default 0.85 + distribution_offset (float): + Value to offset the distribution min 0 (smoother), + max 1 (more responsive). Default 0.85 offset (int): How many periods to offset the result. Default: 0 Kwargs: diff --git a/setup.py b/setup.py index ac8c755..6809c3d 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from distutils.core import setup -long_description = "An easy to use Python 3 Pandas Extension with 115+ Technical Analysis Indicators. Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib." +long_description = "An easy to use Python 3 Pandas Extension with 130+ Technical Analysis Indicators. Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib." setup( name="pandas_ta", @@ -17,7 +17,7 @@ setup( "pandas_ta.volatility", "pandas_ta.volume" ], - version=".".join(("0", "2", "42b")), + version=".".join(("0", "2", "43b")), 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 9744621..d19d35b 100644 --- a/tests/test_ext_indicator_overlap_ext.py +++ b/tests/test_ext_indicator_overlap_ext.py @@ -18,6 +18,11 @@ class TestOverlapExtension(TestCase): def tearDown(self): pass + def test_alma_ext(self): + self.data.ta.alma(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(self.data.columns[-1], "ALMA_10_6.0_0.85") + def test_dema_ext(self): self.data.ta.dema(append=True) self.assertIsInstance(self.data, DataFrame) diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index 971aea6..dfd86e6 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -34,6 +34,11 @@ class TestOverlap(TestCase): def tearDown(self): pass + def test_alma(self): + result = pandas_ta.alma(self.close)# , length=None, sigma=None, distribution_offset=) + self.assertIsInstance(result, Series) + self.assertEqual(result.name, "ALMA_10_6.0_0.85") + def test_dema(self): result = pandas_ta.dema(self.close) self.assertIsInstance(result, Series)