diff --git a/README.md b/README.md index a88cdf9..91f2d8a 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. @@ -500,8 +500,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** @@ -687,6 +688,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/__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 18cbc6e..a055083 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -976,6 +976,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..bdd52d3 --- /dev/null +++ b/pandas_ta/overlap/alma.py @@ -0,0 +1,90 @@ +# -*- 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 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] + 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}_{sigma}_{distribution_offset}" + 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. +""" diff --git a/setup.py b/setup.py index 5b54bd0..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", "44b")), + 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)