From f438189cdadb952581e37d79e51228a47bc89546 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Mon, 20 May 2019 13:25:33 -0700 Subject: [PATCH] overlap refactoring --- .gitignore | 1 + pandas_ta/__init__.py | 24 + pandas_ta/core.py | 23 +- pandas_ta/momentum.py | 5 +- pandas_ta/overlap.py | 1484 ------------------------------- pandas_ta/overlap/__init__.py | 1 + pandas_ta/overlap/dema.py | 59 ++ pandas_ta/overlap/ema.py | 86 ++ pandas_ta/overlap/fwma.py | 61 ++ pandas_ta/overlap/hl2.py | 22 + pandas_ta/overlap/hlc3.py | 23 + pandas_ta/overlap/hma.py | 69 ++ pandas_ta/overlap/ichimoku.py | 126 +++ pandas_ta/overlap/linreg.py | 122 +++ pandas_ta/overlap/midpoint.py | 31 + pandas_ta/overlap/midprice.py | 32 + pandas_ta/overlap/ohlc4.py | 24 + pandas_ta/overlap/pwma.py | 61 ++ pandas_ta/overlap/rma.py | 55 ++ pandas_ta/overlap/sma.py | 54 ++ pandas_ta/overlap/swma.py | 63 ++ pandas_ta/overlap/t3.py | 79 ++ pandas_ta/overlap/tema.py | 61 ++ pandas_ta/overlap/trima.py | 60 ++ pandas_ta/overlap/vwap.py | 59 ++ pandas_ta/overlap/vwma.py | 56 ++ pandas_ta/overlap/wma.py | 75 ++ pandas_ta/overlap/zlma.py | 74 ++ pandas_ta/statistics/zscore.py | 2 +- pandas_ta/trend/adx.py | 2 +- pandas_ta/trend/amat.py | 7 +- pandas_ta/trend/qstick.py | 6 +- pandas_ta/volatility/bbands.py | 3 +- pandas_ta/volatility/kc.py | 2 +- pandas_ta/volatility/massi.py | 2 +- pandas_ta/volume/adosc.py | 2 +- pandas_ta/volume/aobv.py | 9 +- pandas_ta/volume/eom.py | 2 +- pandas_ta/volume/mfi.py | 2 +- tests/test_indicator_overlap.py | 59 +- 40 files changed, 1459 insertions(+), 1529 deletions(-) delete mode 100644 pandas_ta/overlap.py create mode 100644 pandas_ta/overlap/__init__.py create mode 100644 pandas_ta/overlap/dema.py create mode 100644 pandas_ta/overlap/ema.py create mode 100644 pandas_ta/overlap/fwma.py create mode 100644 pandas_ta/overlap/hl2.py create mode 100644 pandas_ta/overlap/hlc3.py create mode 100644 pandas_ta/overlap/hma.py create mode 100644 pandas_ta/overlap/ichimoku.py create mode 100644 pandas_ta/overlap/linreg.py create mode 100644 pandas_ta/overlap/midpoint.py create mode 100644 pandas_ta/overlap/midprice.py create mode 100644 pandas_ta/overlap/ohlc4.py create mode 100644 pandas_ta/overlap/pwma.py create mode 100644 pandas_ta/overlap/rma.py create mode 100644 pandas_ta/overlap/sma.py create mode 100644 pandas_ta/overlap/swma.py create mode 100644 pandas_ta/overlap/t3.py create mode 100644 pandas_ta/overlap/tema.py create mode 100644 pandas_ta/overlap/trima.py create mode 100644 pandas_ta/overlap/vwap.py create mode 100644 pandas_ta/overlap/vwma.py create mode 100644 pandas_ta/overlap/wma.py create mode 100644 pandas_ta/overlap/zlma.py diff --git a/.gitignore b/.gitignore index d935376..d74f7ea 100644 --- a/.gitignore +++ b/.gitignore @@ -134,6 +134,7 @@ pandas_pips reqs.txt requirements.txt qd.py +_overlap.py _trend.py _statistics.py simple.ipynb \ No newline at end of file diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index dab2501..95a7afa 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -18,6 +18,30 @@ except DistributionNotFound: else: __version__ = _dist.version +# Overlap +from .overlap.dema import dema +from .overlap.ema import ema +from .overlap.fwma import fwma +from .overlap.hl2 import hl2 +from .overlap.hlc3 import hlc3 +from .overlap.hma import hma +from .overlap.ichimoku import ichimoku +from .overlap.linreg import linreg +from .overlap.midpoint import midpoint +from .overlap.midprice import midprice +from .overlap.ohlc4 import ohlc4 +from .overlap.pwma import pwma +from .overlap.rma import rma +from .overlap.sma import sma +from .overlap.swma import swma +from .overlap.t3 import t3 +from .overlap.tema import tema +from .overlap.trima import trima +from .overlap.vwap import vwap +from .overlap.vwma import vwma +from .overlap.wma import wma +from .overlap.zlma import zlma + # Performance from .performance.log_return import log_return from .performance.percent_return import percent_return diff --git a/pandas_ta/core.py b/pandas_ta/core.py index a2118ab..f123614 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -4,7 +4,6 @@ import pandas as pd from pandas.core.base import PandasObject from .momentum import * -from .overlap import * from .utils import * class BasePandasObject(PandasObject): @@ -351,18 +350,21 @@ class AnalysisIndicators(BasePandasObject): # Overlap Indicators def dema(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.dema import dema result = dema(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def ema(self, close=None, length=None, offset=None, adjust=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.ema import ema result = ema(close=close, length=length, offset=offset, adjust=adjust, **kwargs) self._append(result, **kwargs) return result def fwma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.fwma import fwma result = fwma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -370,6 +372,7 @@ class AnalysisIndicators(BasePandasObject): def hl2(self, high=None, low=None, offset=None, **kwargs): high = self._get_column(high, 'high') low = self._get_column(low, 'low') + from .overlap.hl2 import hl2 result = hl2(high=high, low=low, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -378,12 +381,14 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(high, 'high') low = self._get_column(low, 'low') close = self._get_column(close, 'close') + from .overlap.hlc3 import hlc3 result = hlc3(high=high, low=low, close=close, offset=offset, **kwargs) self._append(result, **kwargs) return result def hma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.hma import hma result = hma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -392,18 +397,21 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(high, 'high') low = self._get_column(low, 'low') close = self._get_column(close, 'close') + from .overlap.ichimoku import ichimoku result, span = ichimoku(high=high, low=low, close=close, tenkan=tenkan, kijun=kijun, senkou=senkou, offset=offset, **kwargs) self._append(result, **kwargs) return result, span def linreg(self, close=None, length=None, offset=None, adjust=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.linreg import linreg result = linreg(close=close, length=length, offset=offset, adjust=adjust, **kwargs) self._append(result, **kwargs) return result def midpoint(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.midpoint import midpoint result = midpoint(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -411,6 +419,7 @@ class AnalysisIndicators(BasePandasObject): def midprice(self, high=None, low=None, length=None, offset=None, **kwargs): high = self._get_column(high, 'high') low = self._get_column(low, 'low') + from .overlap.midprice import midprice result = midprice(high=high, low=low, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -420,48 +429,56 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(high, 'high') low = self._get_column(low, 'low') close = self._get_column(close, 'close') + from .overlap.ohlc4 import ohlc4 result = ohlc4(open_=open_, high=high, low=low, close=close, offset=offset, **kwargs) self._append(result, **kwargs) return result def pwma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.pwma import pwma result = pwma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def rma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.rma import rma result = rma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def sma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.sma import sma result = sma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def swma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.swma import swma 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') + from .overlap.t3 import t3 result = t3(close=close, length=length, a=a, offset=offset, **kwargs) self._append(result, **kwargs) return result def tema(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.tema import tema result = tema(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def trima(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.trima import trima result = trima(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -471,6 +488,7 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(low, 'low') close = self._get_column(close, 'close') volume = self._get_column(volume, 'volume') + from .overlap.vwap import vwap result = vwap(high=high, low=low, close=close, volume=volume, offset=offset, **kwargs) self._append(result, **kwargs) return result @@ -478,18 +496,21 @@ class AnalysisIndicators(BasePandasObject): def vwma(self, close=None, volume=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') volume = self._get_column(volume, 'volume') + from .overlap.vwma import vwma result = vwma(close=close, volume=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def wma(self, close=None, length=None, offset=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.wma import wma result = wma(close=close, length=length, offset=offset, **kwargs) self._append(result, **kwargs) return result def zlma(self, close=None, length=None, offset=None, mamode=None, **kwargs): close = self._get_column(close, 'close') + from .overlap.zlma import zlma result = zlma(close=close, length=length, offset=offset, mamode=mamode, **kwargs) self._append(result, **kwargs) return result diff --git a/pandas_ta/momentum.py b/pandas_ta/momentum.py index 35a33ca..3d6014a 100644 --- a/pandas_ta/momentum.py +++ b/pandas_ta/momentum.py @@ -2,7 +2,10 @@ import numpy as np import pandas as pd -from .overlap import hlc3, ema, sma, wma +from .overlap.hlc3 import hlc3 +from .overlap.ema import ema +from .overlap.sma import sma +from .overlap.wma import wma from .statistics.mad import mad from .utils import get_drift, get_offset, verify_series diff --git a/pandas_ta/overlap.py b/pandas_ta/overlap.py deleted file mode 100644 index 130d197..0000000 --- a/pandas_ta/overlap.py +++ /dev/null @@ -1,1484 +0,0 @@ -# -*- coding: utf-8 -*- -import math -import numpy as np -import pandas as pd - -from .utils import fibonacci, get_drift, get_offset, pascals_triangle, symmetric_triangle, verify_series, weights - - - -def dema(close, length=None, offset=None, **kwargs): - """Indicator: Double Exponential Moving Average (DEMA)""" - # 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 - offset = get_offset(offset) - - # Calculate Result - ema1 = ema(close=close, length=length, **kwargs) - ema2 = ema(close=ema1, length=length, **kwargs) - dema = 2 * ema1 - ema2 - - # Offset - if offset != 0: - dema = dema.shift(offset) - - # Name & Category - dema.name = f"DEMA_{length}" - dema.category = 'overlap' - - return dema - - -def ema(close, length=None, offset=None, **kwargs): - """Indicator: Exponential Moving Average (EMA)""" - # Validate Arguments - close = verify_series(close) - length = int(length) if length and length > 0 else 10 - min_periods = kwargs.pop('min_periods', length) - adjust = kwargs.pop('adjust', True) - offset = get_offset(offset) - sma = kwargs.pop('sma', True) - ewm = kwargs.pop('ewm', False) - - # Calculate Result - if ewm: - # Mathematical Implementation of an Exponential Weighted Moving Average - ema = close.ewm(span=length, min_periods=min_periods, adjust=adjust).mean() - else: - alpha = 2 / (length + 1) - close = close.copy() - - def ema_(series): - # Technical Anaylsis Definition of an Exponential Moving Average - # Slow for large series - series.iloc[1] = alpha * (series.iloc[1] - series.iloc[0]) + series.iloc[0] - return series.iloc[1] - - seed = close[0:length].mean() if sma else close.iloc[0] - - close[:length - 1] = np.NaN - close.iloc[length - 1] = seed - ma = close[length - 1:].rolling(2, min_periods=2).apply(ema_, raw=False) - ema = close[:length].append(ma[1:]) - - # Offset - if offset != 0: - ema = ema.shift(offset) - - # Name & Category - ema.name = f"EMA_{length}" - ema.category = 'overlap' - - return ema - - -def fwma(close, length=None, asc=None, offset=None, **kwargs): - """Indicator: Fibonacci's Weighted Moving Average (FWMA)""" - # 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 - fibs = fibonacci(n=length, weighted=True) - fwma = close.rolling(length, min_periods=length).apply(weights(fibs), raw=True) - - # Offset - if offset != 0: - fwma = fwma.shift(offset) - - # Name & Category - fwma.name = f"FWMA_{length}" - fwma.category = 'overlap' - - return fwma - - -def hl2(high, low, offset=None, **kwargs): - """Indicator: HL2 """ - # Validate Arguments - high = verify_series(high) - low = verify_series(low) - offset = get_offset(offset) - - # Calculate Result - hl2 = 0.5 * (high + low) - - # Offset - if offset != 0: - hl2 = hl2.shift(offset) - - # Name & Category - hl2.name = "HL2" - hl2.category = 'overlap' - - return hl2 - - -def hlc3(high, low, close, offset=None, **kwargs): - """Indicator: HLC3""" - # Validate Arguments - high = verify_series(high) - low = verify_series(low) - close = verify_series(close) - offset = get_offset(offset) - - # Calculate Result - hlc3 = (high + low + close) / 3 - - # Offset - if offset != 0: - hlc3 = hlc3.shift(offset) - - # Name & Category - hlc3.name = "HLC3" - hlc3.category = 'overlap' - - return hlc3 - - -def hma(close, length=None, offset=None, **kwargs): - """Indicator: Hull Moving Average (HMA) - - Use help(df.ta.hma) for specific documentation where 'df' represents - the DataFrame you are using. - """ - # 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 - offset = get_offset(offset) - - # Calculate Result - half_length = int(length / 2) - sqrt_length = int(math.sqrt(length)) - - wmaf = wma(close=close, length=half_length) - wmas = wma(close=close, length=length) - hma = wma(close=2 * wmaf - wmas, length=sqrt_length) - - # Offset - if offset != 0: - hma = hma.shift(offset) - - # Name & Category - hma.name = f"HMA_{length}" - hma.category = 'overlap' - - return hma - - -def ichimoku(high, low, close, tenkan=None, kijun=None, senkou=None, offset=None, **kwargs): - """Indicator: Ichimoku Kinkō Hyō (Ichimoku)""" - high = verify_series(high) - low = verify_series(low) - close = verify_series(close) - tenkan = int(tenkan) if tenkan and tenkan > 0 else 9 - kijun = int(kijun) if kijun and kijun > 0 else 26 - senkou = int(senkou) if senkou and senkou > 0 else 52 - offset = get_offset(offset) - - # Calculate Result - tenkan_sen = midprice(high=high, low=low, length=tenkan) - kijun_sen = midprice(high=high, low=low, length=kijun) - span_a = 0.5 * (tenkan_sen + kijun_sen) - span_b = midprice(high=high, low=low, length=senkou) - - # Copy Span A and B values before their shift - _span_a = span_a[-kijun:].copy() - _span_b = span_b[-kijun:].copy() - - span_a = span_a.shift(kijun) - span_b = span_b.shift(kijun) - chikou_span = close.shift(-kijun) - - # Offset - if offset != 0: - tenkan_sen = tenkan_sen.shift(offset) - kijun_sen = kijun_sen.shift(offset) - span_a = span_a.shift(offset) - span_b = span_b.shift(offset) - chikou_span = chikou_span.shift(offset) - - # Handle fills - if 'fillna' in kwargs: - span_a.fillna(kwargs['fillna'], inplace=True) - span_b.fillna(kwargs['fillna'], inplace=True) - chikou_span.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - span_a.fillna(method=kwargs['fill_method'], inplace=True) - span_b.fillna(method=kwargs['fill_method'], inplace=True) - chikou_span.fillna(method=kwargs['fill_method'], inplace=True) - - # Name and Categorize it - span_a.name = f"ISA_{tenkan}" - span_b.name = f"ISB_{kijun}" - tenkan_sen.name = f"ITS_{tenkan}" - kijun_sen.name = f"IKS_{kijun}" - chikou_span.name = f"ICS_{kijun}" - - chikou_span.category = kijun_sen.category = tenkan_sen.category = 'trend' - span_b.category = span_a.category = chikou_span - - # Prepare Ichimoku DataFrame - data = {span_a.name: span_a, span_b.name: span_b, tenkan_sen.name: tenkan_sen, kijun_sen.name: kijun_sen, chikou_span.name: chikou_span} - ichimokudf = pd.DataFrame(data) - ichimokudf.name = f"ICHIMOKU_{tenkan}_{kijun}_{senkou}" - ichimokudf.category = 'overlap' - - # Prepare Span DataFrame - last = close.index[-1] - if close.index.dtype == 'int64': - ext_index = pd.RangeIndex(start=last + 1, stop=last + kijun + 1) - spandf = pd.DataFrame(index=ext_index, columns=[span_a.name, span_b.name]) - _span_a.index = _span_b.index = ext_index - else: - df_freq = close.index.value_counts().mode()[0] - tdelta = pd.Timedelta(df_freq, unit='d') - new_dt = pd.date_range(start=last + tdelta, periods=kijun, freq='B') - spandf = pd.DataFrame(index=new_dt, columns=[span_a.name, span_b.name]) - _span_a.index = _span_b.index = new_dt - - spandf[span_a.name] = _span_a - spandf[span_b.name] = _span_b - spandf.name = f"ICHISPAN_{tenkan}_{kijun}" - spandf.category = 'overlap' - - return ichimokudf, spandf - - -def linreg(close, length=None, offset=None, **kwargs): - """Indicator: Linear Regression""" - # Validate arguments - close = verify_series(close) - length = int(length) if length and length > 0 else 14 - min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length - offset = get_offset(offset) - angle = kwargs.pop('angle', False) - intercept = kwargs.pop('intercept', False) - degrees = kwargs.pop('degrees', False) - r = kwargs.pop('r', False) - slope = kwargs.pop('slope', False) - tsf = kwargs.pop('tsf', False) - - # Calculate Result - x = range(1, length + 1) # [1, 2, ..., n] from 1 to n keeps Sum(xy) low - x_sum = 0.5 * length * (length + 1) - x2_sum = x_sum * (2 * length + 1) / 3 - divisor = length * x2_sum - x_sum * x_sum - - def linear_regression(series): - y_sum = series.sum() - xy_sum = (x * series).sum() - - m = (length * xy_sum - x_sum * y_sum) / divisor - if slope: - return m - b = (y_sum * x2_sum - x_sum * xy_sum) / divisor - if intercept: - return b - - if angle: - theta = math.atan(m) - if degrees: - theta *= 180 / math.pi - return theta - - if r: - y2_sum = (series * series).sum() - rn = length * xy_sum - x_sum * y_sum - rd = math.sqrt(divisor * (length * y2_sum - y_sum * y_sum)) - return rn / rd - - return m * length + b if tsf else m * (length - 1) + b - - linreg = close.rolling(length, min_periods=length).apply(linear_regression, raw=False) - - # Offset - if offset != 0: - linreg = linreg.shift(offset) - - # Handle fills - if 'fillna' in kwargs: - linreg.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - linreg.fillna(method=kwargs['fill_method'], inplace=True) - - # Name and Categorize it - linreg.name = f"LR" - if slope: - linreg.name += "m" - if intercept: - linreg.name += "b" - if angle: - linreg.name += "a" - if r: - linreg.name += "r" - linreg.name += f"_{length}" - linreg.category = 'overlap' - - return linreg - - -def midpoint(close, length=None, offset=None, **kwargs): - """Indicator: Midpoint""" - # Validate arguments - close = verify_series(close) - length = int(length) if length and length > 0 else 2 - min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length - offset = get_offset(offset) - - # Calculate Result - lowest = close.rolling(length, min_periods=min_periods).min() - highest = close.rolling(length, min_periods=min_periods).max() - midpoint = 0.5 * (lowest + highest) - - # Offset - if offset != 0: - midpoint = midpoint.shift(offset) - - # Handle fills - if 'fillna' in kwargs: - midpoint.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - midpoint.fillna(method=kwargs['fill_method'], inplace=True) - - # Name and Categorize it - midpoint.name = f"MIDPOINT_{length}" - midpoint.category = 'overlap' - - return midpoint - - -def midprice(high, low, length=None, offset=None, **kwargs): - """Indicator: Midprice""" - # Validate arguments - high = verify_series(high) - low = verify_series(low) - length = int(length) if length and length > 0 else 2 - min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length - offset = get_offset(offset) - - # Calculate Result - lowest_low = low.rolling(length, min_periods=min_periods).min() - highest_high = high.rolling(length, min_periods=min_periods).max() - midprice = 0.5 * (lowest_low + highest_high) - - # Offset - if offset != 0: - midprice = midprice.shift(offset) - - # Handle fills - if 'fillna' in kwargs: - midprice.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - midprice.fillna(method=kwargs['fill_method'], inplace=True) - - # Name and Categorize it - midprice.name = f"MIDPRICE_{length}" - midprice.category = 'overlap' - - return midprice - - -def ohlc4(open_, high, low, close, offset=None, **kwargs): - """Indicator: OHLC4""" - # Validate Arguments - open_ = verify_series(open_) - high = verify_series(high) - low = verify_series(low) - close = verify_series(close) - offset = get_offset(offset) - - # Calculate Result - ohlc4 = 0.25 * (open_ + high + low + close) - - # Offset - if offset != 0: - ohlc4 = ohlc4.shift(offset) - - # Name & Category - ohlc4.name = "OHLC4" - ohlc4.category = 'overlap' - - return ohlc4 - - -def pwma(close, length=None, asc=None, offset=None, **kwargs): - """Indicator: Pascals Weighted Moving Average (PWMA)""" - # 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) - pwma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True) - - # Offset - if offset != 0: - pwma = pwma.shift(offset) - - # Name & Category - pwma.name = f"PWMA_{length}" - pwma.category = 'overlap' - - return pwma - - -def rma(close, length=None, offset=None, **kwargs): - """Indicator: wildeR's Moving Average (RMA)""" - # 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 - offset = get_offset(offset) - alpha = (1.0 / length) if length > 0 else 0.5 - - # Calculate Result - rma = close.ewm(alpha=alpha, min_periods=min_periods).mean() - - # Offset - if offset != 0: - rma = rma.shift(offset) - - # Name & Category - rma.name = f"RMA_{length}" - rma.category = 'overlap' - - return rma - - -def sma(close, length=None, offset=None, **kwargs): - """Indicator: Simple Moving Average (SMA)""" - # 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 - offset = get_offset(offset) - - # Calculate Result - sma = close.rolling(length, min_periods=min_periods).mean() - - # Offset - if offset != 0: - sma = sma.shift(offset) - - # Name & Category - sma.name = f"SMA_{length}" - sma.category = 'overlap' - - 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 - 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 - a = float(a) if a and a > 0 and a < 1 else 0.7 - offset = get_offset(offset) - - # Calculate Result - c1 = -a * a ** 2 - c2 = 3 * a ** 2 + 3 * a ** 3 - c3 = -6 * a ** 2 - 3 * a - 3 * a ** 3 - c4 = a ** 3 + 3 * a ** 2 + 3 * a + 1 - - e1 = ema(close=close, length=length, **kwargs) - e2 = ema(close=e1, length=length, **kwargs) - e3 = ema(close=e2, length=length, **kwargs) - e4 = ema(close=e3, length=length, **kwargs) - e5 = ema(close=e4, length=length, **kwargs) - e6 = ema(close=e5, length=length, **kwargs) - t3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3 - - # Offset - if offset != 0: - t3 = t3.shift(offset) - - # Name & Category - t3.name = f"T3_{length}_{a}" - t3.category = 'overlap' - - return t3 - - -def tema(close, length=None, offset=None, **kwargs): - """Indicator: Triple Exponential Moving Average (TEMA)""" - # 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 - offset = get_offset(offset) - - # Calculate Result - ema1 = ema(close=close, length=length, **kwargs) - ema2 = ema(close=ema1, length=length, **kwargs) - ema3 = ema(close=ema2, length=length, **kwargs) - tema = 3 * (ema1 - ema2) + ema3 - - # Offset - if offset != 0: - tema = tema.shift(offset) - - # Name & Category - tema.name = f"TEMA_{length}" - tema.category = 'overlap' - - return tema - - -def trima(close, length=None, offset=None, **kwargs): - """Indicator: Triangular Moving Average (TRIMA)""" - # 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 - offset = get_offset(offset) - - # Calculate Result - half_length = round(0.5 * (length + 1)) - sma1 = close.rolling(half_length, min_periods=half_length).mean() - trima = sma1.rolling(half_length, min_periods=half_length).mean() - - # Offset - if offset != 0: - trima = trima.shift(offset) - - # Name & Category - trima.name = f"TRIMA_{length}" - trima.category = 'overlap' - - return trima - - -def vwap(high, low, close, volume, offset=None, **kwargs): - """Indicator: Volume Weighted Average Price (VWAP)""" - # Validate Arguments - high = verify_series(high) - low = verify_series(low) - close = verify_series(close) - volume = verify_series(volume) - offset = get_offset(offset) - - # Calculate Result - tp = hlc3(high=high, low=low, close=close) - vwap = (tp * volume).cumsum() / volume.cumsum() - - # Offset - if offset != 0: - vwap = vwap.shift(offset) - - # Name & Category - vwap.name = "VWAP" - vwap.category = 'overlap' - - return vwap - - -def vwma(close, volume, length=None, offset=None, **kwargs): - """Indicator: Volume Weighted Moving Average (VWMA)""" - # Validate Arguments - close = verify_series(close) - volume = verify_series(volume) - length = int(length) if length and length > 0 else 10 - offset = get_offset(offset) - - # Calculate Result - pv = close * volume - vwma = sma(close=pv, length=length) / sma(close=volume, length=length) - - # Offset - if offset != 0: - vwma = vwma.shift(offset) - - # Name & Category - vwma.name = f"VWMA_{length}" - vwma.category = 'overlap' - - return vwma - - -def wma(close, length=None, asc=None, offset=None, **kwargs): - """Indicator: Weighted Moving Average (WMA)""" - # 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 - total_weight = 0.5 * length * (length + 1) - weights_ = pd.Series(np.arange(1, length + 1)) - weights = weights_ if asc else weights_[::-1] - - def linear(w): - def _compute(x): - return (w * x).sum() / total_weight - return _compute - - close_ = close.rolling(length, min_periods=length) - wma = close_.apply(linear(weights), raw=True) - - # Offset - if offset != 0: - wma = wma.shift(offset) - - # Name & Category - wma.name = f"WMA_{length}" - wma.category = 'overlap' - - return wma - - -def zlma(close, length=None, offset=None, mamode=None, **kwargs): - """Indicator: Zero Lag Moving Average (ZLMA)""" - # 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 - offset = get_offset(offset) - mamode = mamode.lower() if mamode else None - - # Calculate Result - lag = int(0.5 * (length - 1)) - close = 2 * close - close.shift(lag) - if mamode is None or mamode == 'ema': - zlma = ema(close, length=length, **kwargs) - kind = "E" - if mamode == 'hma': - zlma = hma(close, length=length, **kwargs) - kind = "H" - if mamode == 'sma': - zlma = sma(close, length=length, **kwargs) - kind = "S" - if mamode == 'wma': - zlma = wma(close, length=length, **kwargs) - kind = "W" - - # Offset - if offset != 0: - zlma = zlma.shift(offset) - - # Name & Category - zlma.name = f"ZL{kind}MA_{length}" - zlma.category = 'overlap' - - return zlma - - - -# Overlap Documentation -hl2.__doc__ = \ -"""Average of High-Low (HL2) - -Equally weighted Average of two series', namely High and Low. - -Sources: - https://www.tradingview.com/study-script-reference/#var_hl2 - -Calculation: - HL2 = 0.5 * (high + low) - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - 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. -""" - - -ichimoku.__doc__ = \ -"""Ichimoku Kinkō Hyō (Ichimoku) - -It identifies the trend and look for potential signals within that trend. - -Sources: - http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:ichimoku_cloud - -Calculation: - Default Inputs: - tenkan=9, kijun=26, senkou=52 - tenkan_sen = midprice(high, low, length=tenkan) - kijun_sen = midprice(high, low, length=kijun) - span_a = 0.5 * (tenkan_sen + kijun_sen) - span_b = midprice(high=high, low=low, length=senkou) - - span_a = span_a.shift(kijun) - span_b = span_b.shift(kijun) - chikou_span = close.shift(-kijun) - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - close (pd.Series): Series of 'close's - tenkan (int): Tenkan period. Default: 9 - kijun (int): Kijun period. Default: 26 - senkou (int): Senkou period. Default: 52 - 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: - pandas.Series: New feature generated. -""" - - - -hlc3.__doc__ = \ -"""Average of High-Low-Close (HLC3) - -Equally weighted Average of three series', namely High, Low, Close. - -Sources: - https://www.tradingview.com/study-script-reference/#var_hlc3 - -Calculation: - HLC3 = (high + low + close) / 3.0 - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - close (pd.Series): Series of 'close's - 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. -""" - - -ohlc4.__doc__ = \ -"""Average of Open-High-Low-Close (OHLC4) - -Equally weighted Average of four series', namely Open, High, Low, Close. - -Sources: - https://www.tradingview.com/study-script-reference/#var_ohlc4 - -Calculation: - OHLC4 = 0.25 * (open + high + low + close) - -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 - 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. -""" - - -midpoint.__doc__ = \ -"""Midpoint (MIDPOINT) - -The Midpoint is the average of the highest and lowest closes over a period. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/midpoint-midpnt/ - -Calculation: - Default Inputs: - length=1 - lowest_close = close.rolling(length).min() - highest_close = close.rolling(length).max() - - MIDPOINT = 0.5 * (highest_close + lowest_close) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 14 - 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. -""" - - -midprice.__doc__ = \ -"""Midprice (MIDPRICE) - -William's Percent R is a momentum oscillator similar to the RSI that -attempts to identify overbought and oversold conditions. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/midprice-midpri/ - -Calculation: - Default Inputs: - length=1 - lowest_low = low.rolling(length).min() - highest_high = high.rolling(length).max() - - MIDPRICE = 0.5 * (highest_high + lowest_low) - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - length (int): It's period. Default: 1 - 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. -""" - - -dema.__doc__ = \ -"""Double Exponential Moving Average (DEMA) - -The Double Exponential Moving Average attempts to a smoother average with less -lag than the normal Exponential Moving Average (EMA). - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/ - -Calculation: - Default Inputs: - length=10 - EMA = Exponential Moving Average - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - - DEMA = 2 * ema1 - ema2 - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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. -""" - - -ema.__doc__ = \ -"""Exponential Moving Average (EMA) - -The Exponential Moving Average is more responsive moving average compared to the -Simple Moving Average (SMA). The weights are determined by alpha which is -proportional to it's length. There are several different methods of calculating -EMA. One method uses just the standard definition of EMA and another uses the -SMA to generate the initial value for the rest of the calculation. - -Sources: - https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp - -Calculation: - Default Inputs: - length=10 - SMA = Simple Moving Average - if kwargs['presma']: - initial = SMA(close, length) - rest = close[length:] - close = initial + rest - - EMA = close.ewm(span=length, adjust=adjust).mean() - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - offset (int): How many periods to offset the result. Default: 0 - -Kwargs: - adjust (bool, optional): Default: True - sma (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. -""" - - -fwma.__doc__ = \ -"""Fibonacci's Weighted Moving Average (FWMA) - -Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average -(WMA) where the weights are based on the Fibonacci Sequence. - -Source: Kevin Johnson - -Calculation: - Default Inputs: - length=10, - - def weights(w): - def _compute(x): - return np.dot(w * x) - return _compute - - fibs = utils.fibonacci(length - 1) - FWMA = close.rolling(length)_.apply(weights(fibs), 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. -""" - - -hma.__doc__ = \ -"""Hull Moving Average (HMA) - -The Hull Exponential Moving Average attempts to reduce or remove lag in moving -averages. - -Sources: - https://alanhull.com/hull-moving-average - -Calculation: - Default Inputs: - length=10 - WMA = Weighted Moving Average - half_length = int(0.5 * length) - sqrt_length = int(math.sqrt(length)) - - wmaf = WMA(close, half_length) - wmas = WMA(close, length) - HMA = WMA(2 * wmaf - wmas, sqrt_length) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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. -""" - - -ichimoku.__doc__ = \ -"""Ichimoku Kinkō Hyō (ichimoku) - -Developed Pre WWII as a forecasting model for financial markets. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/ichimoku-ich/ - -Calculation: - Default Inputs: - tenkan=9, kijun=26, senkou=52 - MIDPRICE = Midprice - TENKAN_SEN = MIDPRICE(high, low, close, length=tenkan) - KIJUN_SEN = MIDPRICE(high, low, close, length=kijun) - CHIKOU_SPAN = close.shift(-kijun) - - SPAN_A = 0.5 * (TENKAN_SEN + KIJUN_SEN) - SPAN_A = SPAN_A.shift(kijun) - - SPAN_B = MIDPRICE(high, low, close, length=senkou) - SPAN_B = SPAN_B.shift(kijun) - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - close (pd.Series): Series of 'close's - tenkan (int): Tenkan period. Default: 9 - kijun (int): Kijun period. Default: 26 - senkou (int): Senkou period. Default: 52 - 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.DataFrame: Two DataFrames. - For the visible period: spanA, spanB, tenkan_sen, kijun_sen, - and chikou_span columns - For the forward looking period: spanA and spanB columns -""" - - -linreg.__doc__ = \ -"""Linear Regression Moving Average (linreg) - -Linear Regression Moving Average - -Source: TA Lib - -Calculation: - Default Inputs: - length=14 - x = [1, 2, ..., n] - x_sum = 0.5 * length * (length + 1) - x2_sum = length * (length + 1) * (2 * length + 1) / 6 - divisor = length * x2_sum - x_sum * x_sum - - lr(series): - y_sum = series.sum() - y2_sum = (series* series).sum() - xy_sum = (x * series).sum() - - m = (length * xy_sum - x_sum * y_sum) / divisor - b = (y_sum * x2_sum - x_sum * xy_sum) / divisor - return m * (length - 1) + b - - linreg = close.rolling(length).apply(lr) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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 - angle (bool, optional): Default: False. If True, returns the angle of the slope in radians - degrees (bool, optional): Default: False. If True, returns the angle of the slope in degrees - intercept (bool, optional): Default: False. If True, returns the angle of the slope in radians - r (bool, optional): Default: False. If True, returns it's correlation 'r' - slope (bool, optional): Default: False. If True, returns the slope - tsf (bool, optional): Default: False. If True, returns the Time Series Forecast value. - -Returns: - pd.Series: New feature generated. -""" - - -pwma.__doc__ = \ -"""Pascal's Weighted Moving Average (PWMA) - -Pascal's Weighted Moving Average is similar to a symmetric triangular -window except PWMA's weights are based on Pascal's Triangle. - -Source: Kevin Johnson - -Calculation: - Default Inputs: - length=10 - - def weights(w): - def _compute(x): - return np.dot(w * x) - return _compute - - triangle = utils.pascals_triangle(length + 1) - PWMA = 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. -""" - - -rma.__doc__ = \ -"""wildeR's Moving Average (RMA) - -The WildeR's Moving Average is simply an Exponential Moving Average (EMA) -with a modified alpha = 1 / length. - -Sources: - https://alanhull.com/hull-moving-average - -Calculation: - Default Inputs: - length=10 - EMA = Exponential Moving Average - alpha = 1 / length - RMA = EMA(close, alpha=alpha) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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. -""" - - -sma.__doc__ = \ -"""Simple Moving Average (SMA) - -The Simple Moving Average is the classic moving average that is the equally -weighted average over n periods. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/ - -Calculation: - Default Inputs: - length=10 - SMA = SUM(close, length) / length - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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. -""" - - -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) - -Tim Tillson's T3 Moving Average is considered a smoother and more responsive -moving average relative to other moving averages. - -Sources: - http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/ - -Calculation: - Default Inputs: - length=10, a=0.7 - c1 = -a^3 - c2 = 3a^2 + 3a^3 = 3a^2 * (1 + a) - c3 = -6a^2 - 3a - 3a^3 - c4 = a^3 + 3a^2 + 3a + 1 - - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - ema3 = EMA(ema2, length) - ema4 = EMA(ema3, length) - ema5 = EMA(ema4, length) - ema6 = EMA(ema5, length) - T3 = c1 * ema6 + c2 * ema5 + c3 * ema4 + c4 * ema3 - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - a (float): 0 < a < 1. Default: 0.7 - 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. -""" - - -tema.__doc__ = \ -"""Triple Exponential Moving Average (TEMA) - -A less laggy Exponential Moving Average. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/ - -Calculation: - Default Inputs: - length=10 - EMA = Exponential Moving Average - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - ema3 = EMA(ema2, length) - TEMA = 3 * (ema1 - ema2) + ema3 - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - 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. -""" - - -trima.__doc__ = \ -"""Triangular Moving Average (TRIMA) - -A weighted moving average where the shape of the weights are triangular and the -greatest weight is in the middle of the period. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/ - tma = sma(sma(src, ceil(length / 2)), floor(length / 2) + 1) # Tradingview - trima = sma(sma(x, n), n) # Tradingview - -Calculation: - Default Inputs: - length=10 - SMA = Simple Moving Average - half_length = math.round(0.5 * (length + 1)) - SMA1 = SMA(close, half_length) - TRIMA = SMA(SMA1, half_length) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - offset (int): How many periods to offset the result. Default: 0 - -Kwargs: - adjust (bool): Default: True - fillna (value, optional): pd.DataFrame.fillna(value) - fill_method (value, optional): Type of fill method - -Returns: - pd.Series: New feature generated. -""" - - -vwap.__doc__ = \ -"""Volume Weighted Average Price (VWAP) - -The Volume Weighted Average Price that measures the average typical price -by volume. It is typically used with intraday charts to identify general -direction. - -Sources: - https://www.tradingview.com/wiki/Volume_Weighted_Average_Price_(VWAP) - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/volume-weighted-average-price-vwap/ - -Calculation: - tp = typical_price = hlc3(high, low, close) - tpv = tp * volume - VWAP = tpv.cumsum() / volume.cumsum() - -Args: - high (pd.Series): Series of 'high's - low (pd.Series): Series of 'low's - close (pd.Series): Series of 'close's - volume (pd.Series): Series of 'volume's - 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. -""" - - -vwma.__doc__ = \ -"""Volume Weighted Moving Average (VWMA) - -Volume Weighted Moving Average. - -Sources: - https://www.motivewave.com/studies/volume_weighted_moving_average.htm - -Calculation: - Default Inputs: - length=10 - SMA = Simple Moving Average - pv = close * volume - VWMA = SMA(pv, length) / SMA(volume, length) - -Args: - close (pd.Series): Series of 'close's - volume (pd.Series): Series of 'volume's - length (int): It's period. Default: 10 - 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. -""" - - -wma.__doc__ = \ -"""Weighted Moving Average (WMA) - -The Weighted Moving Average where the weights are linearly increasing and -the most recent data has the heaviest weight. - -Sources: - https://en.wikipedia.org/wiki/Moving_average#Weighted_moving_average - -Calculation: - Default Inputs: - length=10, asc=True - total_weight = 0.5 * length * (length + 1) - weights_ = [1, 2, ..., length + 1] # Ascending - weights = weights if asc else weights[::-1] - - def linear_weights(w): - def _compute(x): - return (w * x).sum() / total_weight - return _compute - - WMA = close.rolling(length)_.apply(linear_weights(weights), 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. -""" - - -zlma.__doc__ = \ -"""Zero Lag Moving Average (ZLMA) - -The Zero Lag Moving Average attempts to eliminate the lag associated -with moving averages. This is an adaption created by John Ehler and Ric Way. - -Sources: - https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average - -Calculation: - Default Inputs: - length=10, mamode=EMA - EMA = Exponential Moving Average - lag = int(0.5 * (length - 1)) - source = 2 * close - close.shift(lag) - ZLMA = EMA(source, length) - -Args: - close (pd.Series): Series of 'close's - length (int): It's period. Default: 10 - mamode (str): Two options: None or 'ema'. Default: 'ema' - 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/overlap/__init__.py b/pandas_ta/overlap/__init__.py new file mode 100644 index 0000000..7c68785 --- /dev/null +++ b/pandas_ta/overlap/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- \ No newline at end of file diff --git a/pandas_ta/overlap/dema.py b/pandas_ta/overlap/dema.py new file mode 100644 index 0000000..266696a --- /dev/null +++ b/pandas_ta/overlap/dema.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +from .ema import ema +from ..utils import get_offset, verify_series, weights + +def dema(close, length=None, offset=None, **kwargs): + """Indicator: Double Exponential Moving Average (DEMA)""" + # 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 + offset = get_offset(offset) + + # Calculate Result + ema1 = ema(close=close, length=length, **kwargs) + ema2 = ema(close=ema1, length=length, **kwargs) + dema = 2 * ema1 - ema2 + + # Offset + if offset != 0: + dema = dema.shift(offset) + + # Name & Category + dema.name = f"DEMA_{length}" + dema.category = 'overlap' + + return dema + + + +dema.__doc__ = \ +"""Double Exponential Moving Average (DEMA) + +The Double Exponential Moving Average attempts to a smoother average with less +lag than the normal Exponential Moving Average (EMA). + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/ + +Calculation: + Default Inputs: + length=10 + EMA = Exponential Moving Average + ema1 = EMA(close, length) + ema2 = EMA(ema1, length) + + DEMA = 2 * ema1 - ema2 + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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/overlap/ema.py b/pandas_ta/overlap/ema.py new file mode 100644 index 0000000..7fb22e2 --- /dev/null +++ b/pandas_ta/overlap/ema.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +from numpy import NaN as npNaN +from ..utils import get_offset, verify_series + +def ema(close, length=None, offset=None, **kwargs): + """Indicator: Exponential Moving Average (EMA)""" + # Validate Arguments + close = verify_series(close) + length = int(length) if length and length > 0 else 10 + min_periods = kwargs.pop('min_periods', length) + adjust = kwargs.pop('adjust', True) + offset = get_offset(offset) + sma = kwargs.pop('sma', True) + ewm = kwargs.pop('ewm', False) + + # Calculate Result + if ewm: + # Mathematical Implementation of an Exponential Weighted Moving Average + ema = close.ewm(span=length, min_periods=min_periods, adjust=adjust).mean() + else: + alpha = 2 / (length + 1) + close = close.copy() + + def ema_(series): + # Technical Anaylsis Definition of an Exponential Moving Average + # Slow for large series + series.iloc[1] = alpha * (series.iloc[1] - series.iloc[0]) + series.iloc[0] + return series.iloc[1] + + seed = close[0:length].mean() if sma else close.iloc[0] + + close[:length - 1] = npNaN + close.iloc[length - 1] = seed + ma = close[length - 1:].rolling(2, min_periods=2).apply(ema_, raw=False) + ema = close[:length].append(ma[1:]) + + # Offset + if offset != 0: + ema = ema.shift(offset) + + # Name & Category + ema.name = f"EMA_{length}" + ema.category = 'overlap' + + return ema + + + +ema.__doc__ = \ +"""Exponential Moving Average (EMA) + +The Exponential Moving Average is more responsive moving average compared to the +Simple Moving Average (SMA). The weights are determined by alpha which is +proportional to it's length. There are several different methods of calculating +EMA. One method uses just the standard definition of EMA and another uses the +SMA to generate the initial value for the rest of the calculation. + +Sources: + https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages + https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp + +Calculation: + Default Inputs: + length=10 + SMA = Simple Moving Average + if kwargs['presma']: + initial = SMA(close, length) + rest = close[length:] + close = initial + rest + + EMA = close.ewm(span=length, adjust=adjust).mean() + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + adjust (bool, optional): Default: True + sma (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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/fwma.py b/pandas_ta/overlap/fwma.py new file mode 100644 index 0000000..61164c0 --- /dev/null +++ b/pandas_ta/overlap/fwma.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ..utils import fibonacci, get_offset, verify_series, weights + +def fwma(close, length=None, asc=None, offset=None, **kwargs): + """Indicator: Fibonacci's Weighted Moving Average (FWMA)""" + # 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 + fibs = fibonacci(n=length, weighted=True) + fwma = close.rolling(length, min_periods=length).apply(weights(fibs), raw=True) + + # Offset + if offset != 0: + fwma = fwma.shift(offset) + + # Name & Category + fwma.name = f"FWMA_{length}" + fwma.category = 'overlap' + + return fwma + + + +fwma.__doc__ = \ +"""Fibonacci's Weighted Moving Average (FWMA) + +Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average +(WMA) where the weights are based on the Fibonacci Sequence. + +Source: Kevin Johnson + +Calculation: + Default Inputs: + length=10, + + def weights(w): + def _compute(x): + return np.dot(w * x) + return _compute + + fibs = utils.fibonacci(length - 1) + FWMA = close.rolling(length)_.apply(weights(fibs), 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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/hl2.py b/pandas_ta/overlap/hl2.py new file mode 100644 index 0000000..de79a73 --- /dev/null +++ b/pandas_ta/overlap/hl2.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def hl2(high, low, offset=None, **kwargs): + """Indicator: HL2 """ + # Validate Arguments + high = verify_series(high) + low = verify_series(low) + offset = get_offset(offset) + + # Calculate Result + hl2 = 0.5 * (high + low) + + # Offset + if offset != 0: + hl2 = hl2.shift(offset) + + # Name & Category + hl2.name = "HL2" + hl2.category = 'overlap' + + return hl2 \ No newline at end of file diff --git a/pandas_ta/overlap/hlc3.py b/pandas_ta/overlap/hlc3.py new file mode 100644 index 0000000..4a1abb8 --- /dev/null +++ b/pandas_ta/overlap/hlc3.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def hlc3(high, low, close, offset=None, **kwargs): + """Indicator: HLC3""" + # Validate Arguments + high = verify_series(high) + low = verify_series(low) + close = verify_series(close) + offset = get_offset(offset) + + # Calculate Result + hlc3 = (high + low + close) / 3 + + # Offset + if offset != 0: + hlc3 = hlc3.shift(offset) + + # Name & Category + hlc3.name = "HLC3" + hlc3.category = 'overlap' + + return hlc3 \ No newline at end of file diff --git a/pandas_ta/overlap/hma.py b/pandas_ta/overlap/hma.py new file mode 100644 index 0000000..375ddee --- /dev/null +++ b/pandas_ta/overlap/hma.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from math import sqrt +from .wma import wma +from ..utils import get_offset, verify_series + +def hma(close, length=None, offset=None, **kwargs): + """Indicator: Hull Moving Average (HMA) + + Use help(df.ta.hma) for specific documentation where 'df' represents + the DataFrame you are using. + """ + # 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 + offset = get_offset(offset) + + # Calculate Result + half_length = int(length / 2) + sqrt_length = int(sqrt(length)) + + wmaf = wma(close=close, length=half_length) + wmas = wma(close=close, length=length) + hma = wma(close=2 * wmaf - wmas, length=sqrt_length) + + # Offset + if offset != 0: + hma = hma.shift(offset) + + # Name & Category + hma.name = f"HMA_{length}" + hma.category = 'overlap' + + return hma + + + +hma.__doc__ = \ +"""Hull Moving Average (HMA) + +The Hull Exponential Moving Average attempts to reduce or remove lag in moving +averages. + +Sources: + https://alanhull.com/hull-moving-average + +Calculation: + Default Inputs: + length=10 + WMA = Weighted Moving Average + half_length = int(0.5 * length) + sqrt_length = int(math.sqrt(length)) + + wmaf = WMA(close, half_length) + wmas = WMA(close, length) + HMA = WMA(2 * wmaf - wmas, sqrt_length) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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/overlap/ichimoku.py b/pandas_ta/overlap/ichimoku.py new file mode 100644 index 0000000..c4fcb80 --- /dev/null +++ b/pandas_ta/overlap/ichimoku.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +from pandas import date_range, DataFrame, RangeIndex, Timedelta +from .midprice import midprice +from ..utils import get_offset, verify_series + +def ichimoku(high, low, close, tenkan=None, kijun=None, senkou=None, offset=None, **kwargs): + """Indicator: Ichimoku Kinkō Hyō (Ichimoku)""" + high = verify_series(high) + low = verify_series(low) + close = verify_series(close) + tenkan = int(tenkan) if tenkan and tenkan > 0 else 9 + kijun = int(kijun) if kijun and kijun > 0 else 26 + senkou = int(senkou) if senkou and senkou > 0 else 52 + offset = get_offset(offset) + + # Calculate Result + tenkan_sen = midprice(high=high, low=low, length=tenkan) + kijun_sen = midprice(high=high, low=low, length=kijun) + span_a = 0.5 * (tenkan_sen + kijun_sen) + span_b = midprice(high=high, low=low, length=senkou) + + # Copy Span A and B values before their shift + _span_a = span_a[-kijun:].copy() + _span_b = span_b[-kijun:].copy() + + span_a = span_a.shift(kijun) + span_b = span_b.shift(kijun) + chikou_span = close.shift(-kijun) + + # Offset + if offset != 0: + tenkan_sen = tenkan_sen.shift(offset) + kijun_sen = kijun_sen.shift(offset) + span_a = span_a.shift(offset) + span_b = span_b.shift(offset) + chikou_span = chikou_span.shift(offset) + + # Handle fills + if 'fillna' in kwargs: + span_a.fillna(kwargs['fillna'], inplace=True) + span_b.fillna(kwargs['fillna'], inplace=True) + chikou_span.fillna(kwargs['fillna'], inplace=True) + if 'fill_method' in kwargs: + span_a.fillna(method=kwargs['fill_method'], inplace=True) + span_b.fillna(method=kwargs['fill_method'], inplace=True) + chikou_span.fillna(method=kwargs['fill_method'], inplace=True) + + # Name and Categorize it + span_a.name = f"ISA_{tenkan}" + span_b.name = f"ISB_{kijun}" + tenkan_sen.name = f"ITS_{tenkan}" + kijun_sen.name = f"IKS_{kijun}" + chikou_span.name = f"ICS_{kijun}" + + chikou_span.category = kijun_sen.category = tenkan_sen.category = 'trend' + span_b.category = span_a.category = chikou_span + + # Prepare Ichimoku DataFrame + data = {span_a.name: span_a, span_b.name: span_b, tenkan_sen.name: tenkan_sen, kijun_sen.name: kijun_sen, chikou_span.name: chikou_span} + ichimokudf = DataFrame(data) + ichimokudf.name = f"ICHIMOKU_{tenkan}_{kijun}_{senkou}" + ichimokudf.category = 'overlap' + + # Prepare Span DataFrame + last = close.index[-1] + if close.index.dtype == 'int64': + ext_index = RangeIndex(start=last + 1, stop=last + kijun + 1) + spandf = DataFrame(index=ext_index, columns=[span_a.name, span_b.name]) + _span_a.index = _span_b.index = ext_index + else: + df_freq = close.index.value_counts().mode()[0] + tdelta = Timedelta(df_freq, unit='d') + new_dt = date_range(start=last + tdelta, periods=kijun, freq='B') + spandf = DataFrame(index=new_dt, columns=[span_a.name, span_b.name]) + _span_a.index = _span_b.index = new_dt + + spandf[span_a.name] = _span_a + spandf[span_b.name] = _span_b + spandf.name = f"ICHISPAN_{tenkan}_{kijun}" + spandf.category = 'overlap' + + return ichimokudf, spandf + + + +ichimoku.__doc__ = \ +"""Ichimoku Kinkō Hyō (ichimoku) + +Developed Pre WWII as a forecasting model for financial markets. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/ichimoku-ich/ + +Calculation: + Default Inputs: + tenkan=9, kijun=26, senkou=52 + MIDPRICE = Midprice + TENKAN_SEN = MIDPRICE(high, low, close, length=tenkan) + KIJUN_SEN = MIDPRICE(high, low, close, length=kijun) + CHIKOU_SPAN = close.shift(-kijun) + + SPAN_A = 0.5 * (TENKAN_SEN + KIJUN_SEN) + SPAN_A = SPAN_A.shift(kijun) + + SPAN_B = MIDPRICE(high, low, close, length=senkou) + SPAN_B = SPAN_B.shift(kijun) + +Args: + high (pd.Series): Series of 'high's + low (pd.Series): Series of 'low's + close (pd.Series): Series of 'close's + tenkan (int): Tenkan period. Default: 9 + kijun (int): Kijun period. Default: 26 + senkou (int): Senkou period. Default: 52 + 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.DataFrame: Two DataFrames. + For the visible period: spanA, spanB, tenkan_sen, kijun_sen, + and chikou_span columns + For the forward looking period: spanA and spanB columns +""" \ No newline at end of file diff --git a/pandas_ta/overlap/linreg.py b/pandas_ta/overlap/linreg.py new file mode 100644 index 0000000..ddc17b1 --- /dev/null +++ b/pandas_ta/overlap/linreg.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +import math +from ..utils import get_offset, verify_series + +def linreg(close, length=None, offset=None, **kwargs): + """Indicator: Linear Regression""" + # Validate arguments + close = verify_series(close) + length = int(length) if length and length > 0 else 14 + min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length + offset = get_offset(offset) + angle = kwargs.pop('angle', False) + intercept = kwargs.pop('intercept', False) + degrees = kwargs.pop('degrees', False) + r = kwargs.pop('r', False) + slope = kwargs.pop('slope', False) + tsf = kwargs.pop('tsf', False) + + # Calculate Result + x = range(1, length + 1) # [1, 2, ..., n] from 1 to n keeps Sum(xy) low + x_sum = 0.5 * length * (length + 1) + x2_sum = x_sum * (2 * length + 1) / 3 + divisor = length * x2_sum - x_sum * x_sum + + def linear_regression(series): + y_sum = series.sum() + xy_sum = (x * series).sum() + + m = (length * xy_sum - x_sum * y_sum) / divisor + if slope: + return m + b = (y_sum * x2_sum - x_sum * xy_sum) / divisor + if intercept: + return b + + if angle: + theta = math.atan(m) + if degrees: + theta *= 180 / math.pi + return theta + + if r: + y2_sum = (series * series).sum() + rn = length * xy_sum - x_sum * y_sum + rd = math.sqrt(divisor * (length * y2_sum - y_sum * y_sum)) + return rn / rd + + return m * length + b if tsf else m * (length - 1) + b + + linreg = close.rolling(length, min_periods=length).apply(linear_regression, raw=False) + + # Offset + if offset != 0: + linreg = linreg.shift(offset) + + # Handle fills + if 'fillna' in kwargs: + linreg.fillna(kwargs['fillna'], inplace=True) + if 'fill_method' in kwargs: + linreg.fillna(method=kwargs['fill_method'], inplace=True) + + # Name and Categorize it + linreg.name = f"LR" + if slope: + linreg.name += "m" + if intercept: + linreg.name += "b" + if angle: + linreg.name += "a" + if r: + linreg.name += "r" + linreg.name += f"_{length}" + linreg.category = 'overlap' + + return linreg + + + +linreg.__doc__ = \ +"""Linear Regression Moving Average (linreg) + +Linear Regression Moving Average + +Source: TA Lib + +Calculation: + Default Inputs: + length=14 + x = [1, 2, ..., n] + x_sum = 0.5 * length * (length + 1) + x2_sum = length * (length + 1) * (2 * length + 1) / 6 + divisor = length * x2_sum - x_sum * x_sum + + lr(series): + y_sum = series.sum() + y2_sum = (series* series).sum() + xy_sum = (x * series).sum() + + m = (length * xy_sum - x_sum * y_sum) / divisor + b = (y_sum * x2_sum - x_sum * xy_sum) / divisor + return m * (length - 1) + b + + linreg = close.rolling(length).apply(lr) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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 + angle (bool, optional): Default: False. If True, returns the angle of the slope in radians + degrees (bool, optional): Default: False. If True, returns the angle of the slope in degrees + intercept (bool, optional): Default: False. If True, returns the angle of the slope in radians + r (bool, optional): Default: False. If True, returns it's correlation 'r' + slope (bool, optional): Default: False. If True, returns the slope + tsf (bool, optional): Default: False. If True, returns the Time Series Forecast value. + +Returns: + pd.Series: New feature generated. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/midpoint.py b/pandas_ta/overlap/midpoint.py new file mode 100644 index 0000000..83d69d0 --- /dev/null +++ b/pandas_ta/overlap/midpoint.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def midpoint(close, length=None, offset=None, **kwargs): + """Indicator: Midpoint""" + # Validate arguments + close = verify_series(close) + length = int(length) if length and length > 0 else 2 + min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length + offset = get_offset(offset) + + # Calculate Result + lowest = close.rolling(length, min_periods=min_periods).min() + highest = close.rolling(length, min_periods=min_periods).max() + midpoint = 0.5 * (lowest + highest) + + # Offset + if offset != 0: + midpoint = midpoint.shift(offset) + + # Handle fills + if 'fillna' in kwargs: + midpoint.fillna(kwargs['fillna'], inplace=True) + if 'fill_method' in kwargs: + midpoint.fillna(method=kwargs['fill_method'], inplace=True) + + # Name and Categorize it + midpoint.name = f"MIDPOINT_{length}" + midpoint.category = 'overlap' + + return midpoint \ No newline at end of file diff --git a/pandas_ta/overlap/midprice.py b/pandas_ta/overlap/midprice.py new file mode 100644 index 0000000..e6cda9a --- /dev/null +++ b/pandas_ta/overlap/midprice.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def midprice(high, low, length=None, offset=None, **kwargs): + """Indicator: Midprice""" + # Validate arguments + high = verify_series(high) + low = verify_series(low) + length = int(length) if length and length > 0 else 2 + min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length + offset = get_offset(offset) + + # Calculate Result + lowest_low = low.rolling(length, min_periods=min_periods).min() + highest_high = high.rolling(length, min_periods=min_periods).max() + midprice = 0.5 * (lowest_low + highest_high) + + # Offset + if offset != 0: + midprice = midprice.shift(offset) + + # Handle fills + if 'fillna' in kwargs: + midprice.fillna(kwargs['fillna'], inplace=True) + if 'fill_method' in kwargs: + midprice.fillna(method=kwargs['fill_method'], inplace=True) + + # Name and Categorize it + midprice.name = f"MIDPRICE_{length}" + midprice.category = 'overlap' + + return midprice \ No newline at end of file diff --git a/pandas_ta/overlap/ohlc4.py b/pandas_ta/overlap/ohlc4.py new file mode 100644 index 0000000..8a18488 --- /dev/null +++ b/pandas_ta/overlap/ohlc4.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def ohlc4(open_, high, low, close, offset=None, **kwargs): + """Indicator: OHLC4""" + # Validate Arguments + open_ = verify_series(open_) + high = verify_series(high) + low = verify_series(low) + close = verify_series(close) + offset = get_offset(offset) + + # Calculate Result + ohlc4 = 0.25 * (open_ + high + low + close) + + # Offset + if offset != 0: + ohlc4 = ohlc4.shift(offset) + + # Name & Category + ohlc4.name = "OHLC4" + ohlc4.category = 'overlap' + + return ohlc4 \ No newline at end of file diff --git a/pandas_ta/overlap/pwma.py b/pandas_ta/overlap/pwma.py new file mode 100644 index 0000000..2ae589f --- /dev/null +++ b/pandas_ta/overlap/pwma.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, pascals_triangle, verify_series, weights + +def pwma(close, length=None, asc=None, offset=None, **kwargs): + """Indicator: Pascals Weighted Moving Average (PWMA)""" + # 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) + pwma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True) + + # Offset + if offset != 0: + pwma = pwma.shift(offset) + + # Name & Category + pwma.name = f"PWMA_{length}" + pwma.category = 'overlap' + + return pwma + + + +pwma.__doc__ = \ +"""Pascal's Weighted Moving Average (PWMA) + +Pascal's Weighted Moving Average is similar to a symmetric triangular +window except PWMA's weights are based on Pascal's Triangle. + +Source: Kevin Johnson + +Calculation: + Default Inputs: + length=10 + + def weights(w): + def _compute(x): + return np.dot(w * x) + return _compute + + triangle = utils.pascals_triangle(length + 1) + PWMA = 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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/rma.py b/pandas_ta/overlap/rma.py new file mode 100644 index 0000000..ddbc917 --- /dev/null +++ b/pandas_ta/overlap/rma.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def rma(close, length=None, offset=None, **kwargs): + """Indicator: wildeR's Moving Average (RMA)""" + # 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 + offset = get_offset(offset) + alpha = (1.0 / length) if length > 0 else 0.5 + + # Calculate Result + rma = close.ewm(alpha=alpha, min_periods=min_periods).mean() + + # Offset + if offset != 0: + rma = rma.shift(offset) + + # Name & Category + rma.name = f"RMA_{length}" + rma.category = 'overlap' + + return rma + + + +rma.__doc__ = \ +"""wildeR's Moving Average (RMA) + +The WildeR's Moving Average is simply an Exponential Moving Average (EMA) +with a modified alpha = 1 / length. + +Sources: + https://alanhull.com/hull-moving-average + +Calculation: + Default Inputs: + length=10 + EMA = Exponential Moving Average + alpha = 1 / length + RMA = EMA(close, alpha=alpha) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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/overlap/sma.py b/pandas_ta/overlap/sma.py new file mode 100644 index 0000000..2f137db --- /dev/null +++ b/pandas_ta/overlap/sma.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def sma(close, length=None, offset=None, **kwargs): + """Indicator: Simple Moving Average (SMA)""" + # 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 + offset = get_offset(offset) + + # Calculate Result + sma = close.rolling(length, min_periods=min_periods).mean() + + # Offset + if offset != 0: + sma = sma.shift(offset) + + # Name & Category + sma.name = f"SMA_{length}" + sma.category = 'overlap' + + return sma + + + +sma.__doc__ = \ +"""Simple Moving Average (SMA) + +The Simple Moving Average is the classic moving average that is the equally +weighted average over n periods. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/ + +Calculation: + Default Inputs: + length=10 + SMA = SUM(close, length) / length + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/swma.py b/pandas_ta/overlap/swma.py new file mode 100644 index 0000000..8060475 --- /dev/null +++ b/pandas_ta/overlap/swma.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, pascals_triangle, verify_series, weights + +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 + + + +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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/t3.py b/pandas_ta/overlap/t3.py new file mode 100644 index 0000000..ed47c19 --- /dev/null +++ b/pandas_ta/overlap/t3.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from .ema import ema +from ..utils import get_offset, verify_series + +def t3(close, length=None, a=None, offset=None, **kwargs): + """Indicator: T3""" + # 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 + a = float(a) if a and a > 0 and a < 1 else 0.7 + offset = get_offset(offset) + + # Calculate Result + c1 = -a * a ** 2 + c2 = 3 * a ** 2 + 3 * a ** 3 + c3 = -6 * a ** 2 - 3 * a - 3 * a ** 3 + c4 = a ** 3 + 3 * a ** 2 + 3 * a + 1 + + e1 = ema(close=close, length=length, **kwargs) + e2 = ema(close=e1, length=length, **kwargs) + e3 = ema(close=e2, length=length, **kwargs) + e4 = ema(close=e3, length=length, **kwargs) + e5 = ema(close=e4, length=length, **kwargs) + e6 = ema(close=e5, length=length, **kwargs) + t3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3 + + # Offset + if offset != 0: + t3 = t3.shift(offset) + + # Name & Category + t3.name = f"T3_{length}_{a}" + t3.category = 'overlap' + + return t3 + + + +t3.__doc__ = \ +"""Tim Tillson's T3 Moving Average (T3) + +Tim Tillson's T3 Moving Average is considered a smoother and more responsive +moving average relative to other moving averages. + +Sources: + http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/ + +Calculation: + Default Inputs: + length=10, a=0.7 + c1 = -a^3 + c2 = 3a^2 + 3a^3 = 3a^2 * (1 + a) + c3 = -6a^2 - 3a - 3a^3 + c4 = a^3 + 3a^2 + 3a + 1 + + ema1 = EMA(close, length) + ema2 = EMA(ema1, length) + ema3 = EMA(ema2, length) + ema4 = EMA(ema3, length) + ema5 = EMA(ema4, length) + ema6 = EMA(ema5, length) + T3 = c1 * ema6 + c2 * ema5 + c3 * ema4 + c4 * ema3 + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + a (float): 0 < a < 1. Default: 0.7 + 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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/tema.py b/pandas_ta/overlap/tema.py new file mode 100644 index 0000000..1581b8e --- /dev/null +++ b/pandas_ta/overlap/tema.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from .ema import ema +from ..utils import get_offset, verify_series + +def tema(close, length=None, offset=None, **kwargs): + """Indicator: Triple Exponential Moving Average (TEMA)""" + # 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 + offset = get_offset(offset) + + # Calculate Result + ema1 = ema(close=close, length=length, **kwargs) + ema2 = ema(close=ema1, length=length, **kwargs) + ema3 = ema(close=ema2, length=length, **kwargs) + tema = 3 * (ema1 - ema2) + ema3 + + # Offset + if offset != 0: + tema = tema.shift(offset) + + # Name & Category + tema.name = f"TEMA_{length}" + tema.category = 'overlap' + + return tema + + + +tema.__doc__ = \ +"""Triple Exponential Moving Average (TEMA) + +A less laggy Exponential Moving Average. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/ + +Calculation: + Default Inputs: + length=10 + EMA = Exponential Moving Average + ema1 = EMA(close, length) + ema2 = EMA(ema1, length) + ema3 = EMA(ema2, length) + TEMA = 3 * (ema1 - ema2) + ema3 + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + 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. +""" \ No newline at end of file diff --git a/pandas_ta/overlap/trima.py b/pandas_ta/overlap/trima.py new file mode 100644 index 0000000..369c252 --- /dev/null +++ b/pandas_ta/overlap/trima.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from ..utils import get_offset, verify_series + +def trima(close, length=None, offset=None, **kwargs): + """Indicator: Triangular Moving Average (TRIMA)""" + # 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 + offset = get_offset(offset) + + # Calculate Result + half_length = round(0.5 * (length + 1)) + sma1 = close.rolling(half_length, min_periods=half_length).mean() + trima = sma1.rolling(half_length, min_periods=half_length).mean() + + # Offset + if offset != 0: + trima = trima.shift(offset) + + # Name & Category + trima.name = f"TRIMA_{length}" + trima.category = 'overlap' + + return trima + + + +trima.__doc__ = \ +"""Triangular Moving Average (TRIMA) + +A weighted moving average where the shape of the weights are triangular and the +greatest weight is in the middle of the period. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/ + tma = sma(sma(src, ceil(length / 2)), floor(length / 2) + 1) # Tradingview + trima = sma(sma(x, n), n) # Tradingview + +Calculation: + Default Inputs: + length=10 + SMA = Simple Moving Average + half_length = math.round(0.5 * (length + 1)) + SMA1 = SMA(close, half_length) + TRIMA = SMA(SMA1, half_length) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + adjust (bool): Default: True + 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/overlap/vwap.py b/pandas_ta/overlap/vwap.py new file mode 100644 index 0000000..219db3f --- /dev/null +++ b/pandas_ta/overlap/vwap.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +from .hlc3 import hlc3 +from ..utils import get_offset, verify_series + +def vwap(high, low, close, volume, offset=None, **kwargs): + """Indicator: Volume Weighted Average Price (VWAP)""" + # Validate Arguments + high = verify_series(high) + low = verify_series(low) + close = verify_series(close) + volume = verify_series(volume) + offset = get_offset(offset) + + # Calculate Result + tp = hlc3(high=high, low=low, close=close) + vwap = (tp * volume).cumsum() / volume.cumsum() + + # Offset + if offset != 0: + vwap = vwap.shift(offset) + + # Name & Category + vwap.name = "VWAP" + vwap.category = 'overlap' + + return vwap + + + +vwap.__doc__ = \ +"""Volume Weighted Average Price (VWAP) + +The Volume Weighted Average Price that measures the average typical price +by volume. It is typically used with intraday charts to identify general +direction. + +Sources: + https://www.tradingview.com/wiki/Volume_Weighted_Average_Price_(VWAP) + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/volume-weighted-average-price-vwap/ + +Calculation: + tp = typical_price = hlc3(high, low, close) + tpv = tp * volume + VWAP = tpv.cumsum() / volume.cumsum() + +Args: + high (pd.Series): Series of 'high's + low (pd.Series): Series of 'low's + close (pd.Series): Series of 'close's + volume (pd.Series): Series of 'volume's + 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/pandas_ta/overlap/vwma.py b/pandas_ta/overlap/vwma.py new file mode 100644 index 0000000..b2b06d3 --- /dev/null +++ b/pandas_ta/overlap/vwma.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +from .sma import sma +from ..utils import get_offset, verify_series + +def vwma(close, volume, length=None, offset=None, **kwargs): + """Indicator: Volume Weighted Moving Average (VWMA)""" + # Validate Arguments + close = verify_series(close) + volume = verify_series(volume) + length = int(length) if length and length > 0 else 10 + offset = get_offset(offset) + + # Calculate Result + pv = close * volume + vwma = sma(close=pv, length=length) / sma(close=volume, length=length) + + # Offset + if offset != 0: + vwma = vwma.shift(offset) + + # Name & Category + vwma.name = f"VWMA_{length}" + vwma.category = 'overlap' + + return vwma + + + +vwma.__doc__ = \ +"""Volume Weighted Moving Average (VWMA) + +Volume Weighted Moving Average. + +Sources: + https://www.motivewave.com/studies/volume_weighted_moving_average.htm + +Calculation: + Default Inputs: + length=10 + SMA = Simple Moving Average + pv = close * volume + VWMA = SMA(pv, length) / SMA(volume, length) + +Args: + close (pd.Series): Series of 'close's + volume (pd.Series): Series of 'volume's + length (int): It's period. Default: 10 + 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/overlap/wma.py b/pandas_ta/overlap/wma.py new file mode 100644 index 0000000..99afeed --- /dev/null +++ b/pandas_ta/overlap/wma.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +from numpy import arange as nparange +from pandas import Series +from ..utils import get_offset, verify_series + +def wma(close, length=None, asc=None, offset=None, **kwargs): + """Indicator: Weighted Moving Average (WMA)""" + # 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 + total_weight = 0.5 * length * (length + 1) + weights_ = Series(nparange(1, length + 1)) + weights = weights_ if asc else weights_[::-1] + + def linear(w): + def _compute(x): + return (w * x).sum() / total_weight + return _compute + + close_ = close.rolling(length, min_periods=length) + wma = close_.apply(linear(weights), raw=True) + + # Offset + if offset != 0: + wma = wma.shift(offset) + + # Name & Category + wma.name = f"WMA_{length}" + wma.category = 'overlap' + + return wma + + + +wma.__doc__ = \ +"""Weighted Moving Average (WMA) + +The Weighted Moving Average where the weights are linearly increasing and +the most recent data has the heaviest weight. + +Sources: + https://en.wikipedia.org/wiki/Moving_average#Weighted_moving_average + +Calculation: + Default Inputs: + length=10, asc=True + total_weight = 0.5 * length * (length + 1) + weights_ = [1, 2, ..., length + 1] # Ascending + weights = weights if asc else weights[::-1] + + def linear_weights(w): + def _compute(x): + return (w * x).sum() / total_weight + return _compute + + WMA = close.rolling(length)_.apply(linear_weights(weights), 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. +""" diff --git a/pandas_ta/overlap/zlma.py b/pandas_ta/overlap/zlma.py new file mode 100644 index 0000000..cdec9b2 --- /dev/null +++ b/pandas_ta/overlap/zlma.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from .ema import ema +from .hma import hma +from .sma import sma +from .wma import wma +from ..utils import get_offset, verify_series + +def zlma(close, length=None, offset=None, mamode=None, **kwargs): + """Indicator: Zero Lag Moving Average (ZLMA)""" + # 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 + offset = get_offset(offset) + mamode = mamode.lower() if mamode else None + + # Calculate Result + lag = int(0.5 * (length - 1)) + close = 2 * close - close.shift(lag) + if mamode is None or mamode == 'ema': + zlma = ema(close, length=length, **kwargs) + kind = "E" + if mamode == 'hma': + zlma = hma(close, length=length, **kwargs) + kind = "H" + if mamode == 'sma': + zlma = sma(close, length=length, **kwargs) + kind = "S" + if mamode == 'wma': + zlma = wma(close, length=length, **kwargs) + kind = "W" + + # Offset + if offset != 0: + zlma = zlma.shift(offset) + + # Name & Category + zlma.name = f"ZL{kind}MA_{length}" + zlma.category = 'overlap' + + return zlma + + + +zlma.__doc__ = \ +"""Zero Lag Moving Average (ZLMA) + +The Zero Lag Moving Average attempts to eliminate the lag associated +with moving averages. This is an adaption created by John Ehler and Ric Way. + +Sources: + https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average + +Calculation: + Default Inputs: + length=10, mamode=EMA + EMA = Exponential Moving Average + lag = int(0.5 * (length - 1)) + source = 2 * close - close.shift(lag) + ZLMA = EMA(source, length) + +Args: + close (pd.Series): Series of 'close's + length (int): It's period. Default: 10 + mamode (str): Two options: None or 'ema'. Default: 'ema' + 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/statistics/zscore.py b/pandas_ta/statistics/zscore.py index 7a97a1a..848e49b 100644 --- a/pandas_ta/statistics/zscore.py +++ b/pandas_ta/statistics/zscore.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from ..overlap import sma +from ..overlap.sma import sma from .stdev import stdev from ..utils import get_offset, verify_series diff --git a/pandas_ta/trend/adx.py b/pandas_ta/trend/adx.py index 56e92a5..096c86c 100644 --- a/pandas_ta/trend/adx.py +++ b/pandas_ta/trend/adx.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from pandas import DataFrame -from ..overlap import rma +from ..overlap.rma import rma from ..volatility.atr import atr from ..utils import get_drift, get_offset, verify_series, zero diff --git a/pandas_ta/trend/amat.py b/pandas_ta/trend/amat.py index 99a3000..74f3007 100644 --- a/pandas_ta/trend/amat.py +++ b/pandas_ta/trend/amat.py @@ -1,7 +1,12 @@ # -*- coding: utf-8 -*- from pandas import DataFrame from .long_run import long_run -from ..overlap import ema, hma, linreg, rma, sma, wma +from ..overlap.ema import ema +from ..overlap.hma import hma +from ..overlap.linreg import linreg +from ..overlap.rma import rma +from ..overlap.sma import sma +from ..overlap.wma import wma from .short_run import short_run from ..utils import get_offset, verify_series diff --git a/pandas_ta/trend/qstick.py b/pandas_ta/trend/qstick.py index d5ceeb0..8dbb4b4 100644 --- a/pandas_ta/trend/qstick.py +++ b/pandas_ta/trend/qstick.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- -from ..overlap import dema, ema, hma, rma, sma +from ..overlap.dema import dema +from ..overlap.ema import ema +from ..overlap.hma import hma +from ..overlap.rma import rma +from ..overlap.sma import sma from ..utils import get_offset, verify_series def qstick(open_, close, length=None, offset=None, **kwargs): diff --git a/pandas_ta/volatility/bbands.py b/pandas_ta/volatility/bbands.py index ff73bb6..3137e3a 100644 --- a/pandas_ta/volatility/bbands.py +++ b/pandas_ta/volatility/bbands.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from pandas import DataFrame -from ..overlap import ema, sma +from ..overlap.ema import ema +from ..overlap.sma import sma from ..statistics.stdev import stdev from ..utils import get_offset, verify_series diff --git a/pandas_ta/volatility/kc.py b/pandas_ta/volatility/kc.py index 49cbb8a..deccae8 100644 --- a/pandas_ta/volatility/kc.py +++ b/pandas_ta/volatility/kc.py @@ -2,7 +2,7 @@ from numpy import sqrt as npsqrt from pandas import DataFrame from .atr import atr -from ..overlap import hlc3 +from ..overlap.hlc3 import hlc3 from ..statistics.variance import variance from ..utils import get_offset, verify_series diff --git a/pandas_ta/volatility/massi.py b/pandas_ta/volatility/massi.py index c63b8a0..b9c2b3e 100644 --- a/pandas_ta/volatility/massi.py +++ b/pandas_ta/volatility/massi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from ..overlap import ema +from ..overlap.ema import ema from ..utils import get_offset, verify_series def massi(high, low, fast=None, slow=None, offset=None, **kwargs): diff --git a/pandas_ta/volume/adosc.py b/pandas_ta/volume/adosc.py index fced4d6..40ecc1a 100644 --- a/pandas_ta/volume/adosc.py +++ b/pandas_ta/volume/adosc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from .ad import ad -from ..overlap import ema +from ..overlap.ema import ema from ..utils import get_offset, verify_series def adosc(high, low, close, volume, open_=None, fast=None, slow=None, offset=None, **kwargs): diff --git a/pandas_ta/volume/aobv.py b/pandas_ta/volume/aobv.py index a6f33ce..7f72582 100644 --- a/pandas_ta/volume/aobv.py +++ b/pandas_ta/volume/aobv.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- +from pandas import DataFrame from .obv import obv -from ..overlap import * +from ..overlap.ema import ema +from ..overlap.hma import hma +from ..overlap.linreg import linreg +from ..overlap.sma import sma +from ..overlap.wma import wma from ..trend.long_run import long_run from ..trend.short_run import short_run from ..utils import get_offset, verify_series @@ -75,7 +80,7 @@ def aobv(close, volume, fast=None, slow=None, mamode=None, max_lookback=None, mi f"AOBV_LR_{run_length}": obv_long, f"AOBV_SR_{run_length}": obv_short } - aobvdf = pd.DataFrame(data) + aobvdf = DataFrame(data) # Name and Categorize it aobvdf.name = f"AOBV_{mamode}_{fast}_{slow}_{min_lookback}_{max_lookback}_{run_length}" diff --git a/pandas_ta/volume/eom.py b/pandas_ta/volume/eom.py index fd84adb..c269493 100644 --- a/pandas_ta/volume/eom.py +++ b/pandas_ta/volume/eom.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from ..overlap import hl2 +from ..overlap.hl2 import hl2 from ..utils import get_drift, get_offset, verify_series def eom(high, low, close, volume, length=None, divisor=None, drift=None, offset=None, **kwargs): diff --git a/pandas_ta/volume/mfi.py b/pandas_ta/volume/mfi.py index 43e859f..6e2ff5a 100644 --- a/pandas_ta/volume/mfi.py +++ b/pandas_ta/volume/mfi.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from pandas import DataFrame -from ..overlap import hlc3 +from ..overlap.hlc3 import hlc3 from ..utils import get_drift, get_offset, verify_series def mfi(high, low, close, volume, length=None, drift=None, offset=None, **kwargs): diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index 35bae40..f90830f 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -32,15 +32,12 @@ class TestOverlap(TestCase): - def setUp(self): - self.overlap = pandas_ta.overlap - - def tearDown(self): - del self.overlap + def setUp(self): pass + def tearDown(self): pass def test_dema(self): - result = self.overlap.dema(self.close) + result = pandas_ta.dema(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'DEMA_10') @@ -55,7 +52,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_ema(self): - result = self.overlap.ema(self.close, presma=False) + result = pandas_ta.ema(self.close, presma=False) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'EMA_10') @@ -70,17 +67,17 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_fwma(self): - result = self.overlap.fwma(self.close) + result = pandas_ta.fwma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'FWMA_10') def test_hl2(self): - result = self.overlap.hl2(self.high, self.low) + result = pandas_ta.hl2(self.high, self.low) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'HL2') def test_hlc3(self): - result = self.overlap.hlc3(self.high, self.low, self.close) + result = pandas_ta.hlc3(self.high, self.low, self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'HLC3') @@ -95,19 +92,19 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_hma(self): - result = self.overlap.hma(self.close) + result = pandas_ta.hma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'HMA_10') def test_ichimoku(self): - ichimoku, span = self.overlap.ichimoku(self.high, self.low, self.close) + ichimoku, span = pandas_ta.ichimoku(self.high, self.low, self.close) self.assertIsInstance(ichimoku, DataFrame) self.assertIsInstance(span, DataFrame) self.assertEqual(ichimoku.name, 'ICHIMOKU_9_26_52') self.assertEqual(span.name, 'ICHISPAN_9_26') def test_linreg(self): - result = self.overlap.linreg(self.close) + result = pandas_ta.linreg(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'LR_14') @@ -122,7 +119,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_linreg_angle(self): - result = self.overlap.linreg(self.close, angle=True) + result = pandas_ta.linreg(self.close, angle=True) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'LRa_14') @@ -137,7 +134,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_linreg_intercept(self): - result = self.overlap.linreg(self.close, intercept=True) + result = pandas_ta.linreg(self.close, intercept=True) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'LRb_14') @@ -152,12 +149,12 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_linreg_r(self): - result = self.overlap.linreg(self.close, r=True) + result = pandas_ta.linreg(self.close, r=True) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'LRr_14') def test_linreg_slope(self): - result = self.overlap.linreg(self.close, slope=True) + result = pandas_ta.linreg(self.close, slope=True) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'LRm_14') @@ -172,7 +169,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_midpoint(self): - result = self.overlap.midpoint(self.close) + result = pandas_ta.midpoint(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'MIDPOINT_2') @@ -187,7 +184,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_midprice(self): - result = self.overlap.midprice(self.high, self.low) + result = pandas_ta.midprice(self.high, self.low) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'MIDPRICE_2') @@ -202,22 +199,22 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_ohlc4(self): - result = self.overlap.ohlc4(self.open, self.high, self.low, self.close) + result = pandas_ta.ohlc4(self.open, self.high, self.low, self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'OHLC4') def test_pwma(self): - result = self.overlap.pwma(self.close) + result = pandas_ta.pwma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'PWMA_10') def test_rma(self): - result = self.overlap.rma(self.close) + result = pandas_ta.rma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'RMA_10') def test_sma(self): - result = self.overlap.sma(self.close) + result = pandas_ta.sma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'SMA_10') @@ -232,12 +229,12 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_swma(self): - result = self.overlap.swma(self.close) + result = pandas_ta.swma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'SWMA_10') def test_t3(self): - result = self.overlap.t3(self.close) + result = pandas_ta.t3(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'T3_10_0.7') @@ -252,7 +249,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_tema(self): - result = self.overlap.tema(self.close) + result = pandas_ta.tema(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'TEMA_10') @@ -267,7 +264,7 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_trima(self): - result = self.overlap.trima(self.close) + result = pandas_ta.trima(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'TRIMA_10') @@ -282,17 +279,17 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_vwap(self): - result = self.overlap.vwap(self.high, self.low, self.close, self.volume) + result = pandas_ta.vwap(self.high, self.low, self.close, self.volume) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'VWAP') def test_vwma(self): - result = self.overlap.vwma(self.close, self.volume) + result = pandas_ta.vwma(self.close, self.volume) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'VWMA_10') def test_wma(self): - result = self.overlap.wma(self.close) + result = pandas_ta.wma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'WMA_10') @@ -307,6 +304,6 @@ class TestOverlap(TestCase): error_analysis(result, CORRELATION, ex) def test_zlma(self): - result = self.overlap.zlma(self.close) + result = pandas_ta.zlma(self.close) self.assertIsInstance(result, Series) self.assertEqual(result.name, 'ZLEMA_10')