diff --git a/README.md b/README.md index 0678f06..456c6fa 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Pandas TA - A Technical Analysis Library in Python 3 [![Downloads](https://img.shields.io/pypi/dm/pandas_ta?style=flat)](https://pypistats.org/packages/pandas_ta) [![Stars](https://img.shields.io/github/stars/twopirllc/pandas-ta?style=flat)](#stars) [![Forks](https://img.shields.io/github/forks/twopirllc/pandas-ta?style=flat)](#forks) -[![Used By](https://img.shields.io/badge/used_by-136-orange.svg?style=flat)](#usedby) +[![Used By](https://img.shields.io/badge/used_by-138-orange.svg?style=flat)](#usedby) [![Contributors](https://img.shields.io/github/contributors/twopirllc/pandas-ta?style=flat)](#contributors) [![Issues](https://img.shields.io/github/issues-raw/twopirllc/pandas-ta?style=flat)](#issues) [![Closed Issues](https://img.shields.io/github/issues-closed-raw/twopirllc/pandas-ta?style=flat)](#closed-issues) @@ -43,6 +43,9 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever * [Help](#help) * [Issues and Contributions](#issues-and-contributions) * [Programming Conventions](#programming-conventions) + * [Standard](#standard) + * [Pandas TA DataFrame Extension](#pandas-ta-dataframe-extension) + * [Pandas TA Strategy](#pandas-ta-strategy) * [Pandas TA Strategies](#pandas-ta-strategies) * [Types of Strategies](#types-of-strategies) * [DataFrame Properties](#dataframe-properties) @@ -50,7 +53,7 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever * [Indicators by Category](#indicators-by-category) * [Candles](#candles-64) * [Cycles](#cycles-1) - * [Momentum](#momentum-39) + * [Momentum](#momentum-40) * [Overlap](#overlap-32) * [Performance](#performance-3) * [Statistics](#statistics-9) @@ -107,7 +110,7 @@ $ pip install pandas_ta Latest Version -------------- -Best choice! Version: *0.2.88b* +Best choice! Version: *0.2.89b* ```sh $ pip install -U git+https://github.com/twopirllc/pandas-ta ``` @@ -195,7 +198,7 @@ Thanks for using **Pandas TA**! * The indicator does not match another website, library, broker platform, language, et al. * Do you have correlation analysis to back your claim? * Can you contribute? - * You will be asked to fill out an Issue even if you email my personal email address. + * You **will** be asked to fill out an Issue even if you email my personally.
@@ -203,9 +206,9 @@ Thanks for using **Pandas TA**! **Contributors** ================ -_Thank you for your contributions!_ +_Thank you for your contributions!_ -[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [CMobley7](https://github.com/CMobley7) | [codesutras](https://github.com/codesutras) | [DrPaprikaa](https://github.com/DrPaprikaa) | [daikts](https://github.com/daikts) | [dorren](https://github.com/dorren) | [edwardwang1](https://github.com/edwardwang1) | [ffhirata](https://github.com/ffhirata) | [FGU1](https://github.com/FGU1) | [floatinghotpot](https://github.com/floatinghotpot) | [GSlinger](https://github.com/gslinger) | [JoeSchr](https://github.com/JoeSchr) | [lluissalord](https://github.com/lluissalord) | [luisbarrancos](https://github.com/luisbarrancos) | [M6stafa](https://github.com/M6stafa) | [maxdignan](https://github.com/maxdignan) | [mchant](https://github.com/mchant) | [moritzgun](https://github.com/moritzgun) | [nicoloridulfo](https://github.com/nicoloridulfo) [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [RajeshDhalange](https://github.com/RajeshDhalange) | [rengel8](https://github.com/rengel8) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [twrobel](https://github.com/twrobel) | [WellMaybeItIs](https://github.com/WellMaybeItIs) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein) +[AbyssAlora](https://github.com/AbyssAlora) | [alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [CMobley7](https://github.com/CMobley7) | [codesutras](https://github.com/codesutras) | [DrPaprikaa](https://github.com/DrPaprikaa) | [daikts](https://github.com/daikts) | [dorren](https://github.com/dorren) | [edwardwang1](https://github.com/edwardwang1) | [ffhirata](https://github.com/ffhirata) | [FGU1](https://github.com/FGU1) | [floatinghotpot](https://github.com/floatinghotpot) | [GSlinger](https://github.com/gslinger) | [JoeSchr](https://github.com/JoeSchr) | [lluissalord](https://github.com/lluissalord) | [luisbarrancos](https://github.com/luisbarrancos) | [M6stafa](https://github.com/M6stafa) | [maxdignan](https://github.com/maxdignan) | [mchant](https://github.com/mchant) | [moritzgun](https://github.com/moritzgun) | [nicoloridulfo](https://github.com/nicoloridulfo) [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [RajeshDhalange](https://github.com/RajeshDhalange) | [rengel8](https://github.com/rengel8) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [twrobel](https://github.com/twrobel) | [WellMaybeItIs](https://github.com/WellMaybeItIs) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein)
@@ -661,7 +664,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
-### **Momentum** (39) +### **Momentum** (40) * _Awesome Oscillator_: **ao** * _Absolute Price Oscillator_: **apo** * _Bias_: **bias** @@ -674,6 +677,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"]) * _Coppock Curve_: **coppock** * _Correlation Trend Indicator_: **cti** * A wrapper for ```ta.linreg(series, r=True)``` +* _Directional Movement_: **dm** * _Efficiency Ratio_: **er** * _Elder Ray Index_: **eri** * _Fisher Transform_: **fisher** @@ -725,7 +729,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"]) * _Hull Exponential Moving Average_: **hma** * _Holt-Winter Moving Average_: **hwma** * _Ichimoku Kinkō Hyō_: **ichimoku** - * Use: help(ta.ichimoku). Returns two DataFrames. + * Returns two DataFrames. For more information: ```help(ta.ichimoku)```. * Drop the Chikou Span Column, the final column of the first resultant DataFrame, remove potential data leak. * _Kaufman's Adaptive Moving Average_: **kama** * _Linear Regression_: **linreg** @@ -942,7 +946,7 @@ print(pf.returns_stats())
-## **Breaking Indicators** +## **Breaking / Depreciated Indicators** * _Trend Return_ (**trend_return**) has been removed and replaced with **tsignals**. When given a trend Series like ```close > sma(close, 50)``` it returns the Trend, Trade Entries and Trade Exits of that trend to make it compatible with [**vectorbt**](https://github.com/polakowo/vectorbt) by setting ```asbool=True``` to get boolean Trade Entries and Exits. See: ```help(ta.tsignals)```
@@ -954,10 +958,10 @@ trading account, or fund. See: ```help(ta.drawdown)``` * _Candle Z Score_ (**cdl_z**) normalizes OHLC Candles with a rolling Z Score. See: ```help(ta.cdl_z)``` * _Correlation Trend Indicator_ (**cti**) is an oscillator created by John Ehler in 2020. See: ```help(ta.cti)``` * _Cross Signals_ (**xsignals**) was created by Kevin Johnson. It is a wrapper of Trade Signals that returns Trends, Trades, Entries and Exits. Cross Signals are commonly used for **bbands**, **rsi**, **zscore** crossing some value either above or below two values at different times. See: ```help(ta.xsignals)``` +* _Directional Movement_ (**dm**) developed by J. Welles Wilder in 1978 attempts to determine which direction the price of an asset is moving. See: ```help(ta.dm)``` * _Even Better Sinewave_ (**ebsw**) measures market cycles and uses a low pass filter to remove noise. See: ```help(ta.ebsw)``` * _Klinger Volume Oscillator_ (**kvo**) was developed by Stephen J. Klinger. It is designed to predict price reversals in a market by comparing volume to price.. See: ```help(ta.kvo)``` -* _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two -cascaded stochastic calculations with additional smoothing. See: ```help(ta.stc)``` +* _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two cascaded stochastic calculations with additional smoothing. See: ```help(ta.stc)``` * _Tom DeMark's Sequential_ (**td_seq**) attempts to identify a price point where an uptrend or a downtrend exhausts itself and reverses. Currently exlcuded from ```df.ta.strategy()``` for performance reasons. See: ```help(ta.td_seq)``` * _Vertical Horizontal Filter_ (**vhf**) was created by Adam White to identify trending and ranging markets.. See: ```help(ta.vhf)``` @@ -972,7 +976,7 @@ cascaded stochastic calculations with additional smoothing. See: ```help(ta.stc) * _Chande Kroll Stop_ (**cksp**): Added ```tvmode``` with default ```True```. When ```tvmode=False```, **cksp** implements “The New Technical Trader” with default values. See ```help(ta.cksp)```. * _Decreasing_ (**decreasing**): New argument ```strict``` checks if the series is continuously decreasing over period ```length``` with a faster calculation. Default: ```False```. The ```percent``` argument has also been added with default None. See ```help(ta.decreasing)```. * _Increasing_ (**increasing**): New argument ```strict``` checks if the series is continuously increasing over period ```length``` with a faster calculation. Default: ```False```. The ```percent``` argument has also been added with default None. See ```help(ta.increasing)```. -* _Parabolic Stop and Reverse_ (**psar**): New argument ```af0``` to initialize the Acceleration Factor. ```help(ta.psar)```. +* _Parabolic Stop and Reverse_ (**psar**): Bug fix and adjustment to match TradingView's ```sar```. New argument ```af0``` to initialize the Acceleration Factor. ```help(ta.psar)```. * _Volume Weighted Average Price_ (**vwap**): Added a new parameter called ```anchor```. Default: "D" for "Daily". See [Timeseries Offset Aliases](https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases) for additional options. **Requires** the DataFrame index to be a DatetimeIndex * _Z Score_ (**zscore**): Changed return column name from ```Z_length``` to ```ZS_length```. diff --git a/pandas_ta/cycles/ebsw.py b/pandas_ta/cycles/ebsw.py index 01bfb51..6fc008b 100644 --- a/pandas_ta/cycles/ebsw.py +++ b/pandas_ta/cycles/ebsw.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from numpy import cos as npCos from numpy import exp as npExp -from numpy import NaN as npNaN +from numpy import nan as npNaN from numpy import pi as npPi from numpy import sin as npSin from numpy import sqrt as npSqrt diff --git a/pandas_ta/momentum/dm.py b/pandas_ta/momentum/dm.py index 657152b..68ddb49 100644 --- a/pandas_ta/momentum/dm.py +++ b/pandas_ta/momentum/dm.py @@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame +from pandas_ta import Imports +from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series, get_drift, zero -def dm(high, low, drift=None, offset=None, **kwargs): +def dm(high, low, length=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: DM""" # Validate Arguments + length = int(length) if length and length > 0 else 14 + mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma" high = verify_series(high) low = verify_series(low) drift = get_drift(drift) @@ -15,27 +19,37 @@ def dm(high, low, drift=None, offset=None, **kwargs): if high is None or low is None: return - up = high - high.shift(drift) - dn = low.shift(drift) - low + if Imports["talib"]: + from talib import MINUS_DM, PLUS_DM + pos, neg = PLUS_DM(high, low), MINUS_DM(high, low) + else: + up = high - high.shift(drift) + dn = low.shift(drift) - low - pos = ((up > dn) & (up > 0)) * up - neg = ((dn > up) & (dn > 0)) * dn + pos_ = ((up > dn) & (up > 0)) * up + neg_ = ((dn > up) & (dn > 0)) * dn - pos = pos.apply(zero) - neg = neg.apply(zero) + pos_ = pos_.apply(zero) + neg_ = neg_.apply(zero) + + # Not the same values as TA Lib's -+DM (Good First Issue) + pos = ma(mamode, pos_, length=length) + neg = ma(mamode, neg_, length=length) # Offset if offset != 0: pos = pos.shift(offset) neg = neg.shift(offset) - _params = f"_{drift}" + _params = f"_{length}" data = { - f"+DM{_params}": pos, - f"-DM{_params}": neg, + f"DMP{_params}": pos, + f"DMN{_params}": neg, } dmdf = DataFrame(data) + # print(dmdf.head(20)) + # print() dmdf.name = f"DM{_params}" dmdf.category = "trend" @@ -43,9 +57,31 @@ def dm(high, low, drift=None, offset=None, **kwargs): dm.__doc__ = \ - """Directional Movement (DM) +"""Directional Movement (DM) -Directional Movement +The Directional Movement was developed by J. Welles Wilder in 1978 attempts to +determine which direction the price of an asset is moving. It compares prior +highs and lows to yield to two series +DM and -DM. + +Sources: + https://www.tradingview.com/pine-script-reference/#fun_dmi + https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=24&Name=Directional_Movement_Index + +Calculation: + Default Inputs: + length=14, mamode="rma", drift=1 + up = high - high.shift(drift) + dn = low.shift(drift) - low + + pos_ = ((up > dn) & (up > 0)) * up + neg_ = ((dn > up) & (dn > 0)) * dn + + pos_ = pos_.apply(zero) + neg_ = neg_.apply(zero) + + # Not the same values as TA Lib's -+DM + pos = ma(mamode, pos_, length=length) + neg = ma(mamode, neg_, length=length) Args: high (pd.Series): Series of 'high's @@ -54,5 +90,5 @@ Args: offset (int): How many periods to offset the result. Default: 0 Returns: - pd.DataFrame: +DM and -DM columns. + pd.DataFrame: DMP (+DM) and DMN (-DM) columns. """ diff --git a/pandas_ta/momentum/fisher.py b/pandas_ta/momentum/fisher.py index f097637..7c0f133 100644 --- a/pandas_ta/momentum/fisher.py +++ b/pandas_ta/momentum/fisher.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from numpy import log as nplog -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame, Series from pandas_ta.overlap import ema, hl2 from pandas_ta.utils import get_offset, high_low_range, verify_series, zero diff --git a/pandas_ta/momentum/rsx.py b/pandas_ta/momentum/rsx.py index 4ae6dbd..6ee5be1 100644 --- a/pandas_ta/momentum/rsx.py +++ b/pandas_ta/momentum/rsx.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import concat, DataFrame, Series from pandas_ta.utils import get_drift, get_offset, verify_series, signals diff --git a/pandas_ta/momentum/squeeze.py b/pandas_ta/momentum/squeeze.py index 64e6197..514d149 100644 --- a/pandas_ta/momentum/squeeze.py +++ b/pandas_ta/momentum/squeeze.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame from pandas_ta.momentum import mom from pandas_ta.overlap import ema, linreg, sma diff --git a/pandas_ta/overlap/alma.py b/pandas_ta/overlap/alma.py index a98eed6..2107831 100644 --- a/pandas_ta/overlap/alma.py +++ b/pandas_ta/overlap/alma.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from numpy import exp as npExp -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import Series from pandas_ta.utils import get_offset, verify_series diff --git a/pandas_ta/overlap/ema.py b/pandas_ta/overlap/ema.py index a6e3c7b..105f856 100644 --- a/pandas_ta/overlap/ema.py +++ b/pandas_ta/overlap/ema.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series diff --git a/pandas_ta/overlap/hilo.py b/pandas_ta/overlap/hilo.py index bfd7471..5c7250a 100644 --- a/pandas_ta/overlap/hilo.py +++ b/pandas_ta/overlap/hilo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame, Series from .ma import ma from pandas_ta.utils import get_offset, verify_series diff --git a/pandas_ta/overlap/kama.py b/pandas_ta/overlap/kama.py index ab5ec8c..4248459 100644 --- a/pandas_ta/overlap/kama.py +++ b/pandas_ta/overlap/kama.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import Series from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series diff --git a/pandas_ta/overlap/linreg.py b/pandas_ta/overlap/linreg.py index e9d4eb4..00f943b 100644 --- a/pandas_ta/overlap/linreg.py +++ b/pandas_ta/overlap/linreg.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from numpy import array as npArray from numpy import arctan as npAtan -from numpy import NaN as npNaN +from numpy import nan as npNaN from numpy import pi as npPi from numpy.lib.stride_tricks import sliding_window_view from pandas import Series diff --git a/pandas_ta/overlap/supertrend.py b/pandas_ta/overlap/supertrend.py index 5fd0f47..644bfbb 100644 --- a/pandas_ta/overlap/supertrend.py +++ b/pandas_ta/overlap/supertrend.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame from pandas_ta.overlap import hl2 from pandas_ta.volatility import atr diff --git a/pandas_ta/trend/psar.py b/pandas_ta/trend/psar.py index 76ff73b..18fe263 100644 --- a/pandas_ta/trend/psar.py +++ b/pandas_ta/trend/psar.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame, Series -from pandas_ta.utils import get_offset, verify_series from pandas_ta.momentum import dm +from pandas_ta.utils import get_offset, verify_series, zero def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **kwargs): @@ -15,9 +15,16 @@ def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **k max_af = float(max_af) if max_af and max_af > 0 else 0.2 offset = get_offset(offset) - _dm = dm(high, low, close) + def _falling(high, low, drift:int=1): + """Returns the last -DM value""" + # Not to be confused with ta.falling() + up = high - high.shift(drift) + dn = low.shift(drift) - low + _dmn = (((dn > up) & (dn > 0)) * dn).apply(zero)[-1] + return _dmn > 0 - falling = _dm["-DM_1"].iloc[1] > 0 + # Falling if the first NaN -DM is positive + falling = _falling(high.iloc[:2], low.iloc[:2]) if falling: sar = high.iloc[0] ep = low.iloc[0] @@ -33,50 +40,46 @@ def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **k short = long.copy() reversal = Series(False, index=high.index) _af = long.copy() - _af.iloc[0:1] = af0 - - m = high.shape[0] + _af.iloc[0:2] = af0 # Calculate Result + m = high.shape[0] for row in range(1, m): - HIGH = high.iloc[row] - LOW = low.iloc[row] + high_ = high.iloc[row] + low_ = low.iloc[row] if falling: - new_sar = sar + af * (ep - sar) - reverse = HIGH > new_sar + _sar = sar + af * (ep - sar) + reverse = high_ > _sar - if LOW < ep: - ep = LOW + if low_ < ep: + ep = low_ af = min(af + af0, max_af) - new_sar = max(high.iloc[row - 1], high.iloc[row - 2], new_sar) + _sar = max(high.iloc[row - 1], high.iloc[row - 2], _sar) else: - new_sar = sar + af * (ep - sar) - reverse = LOW < new_sar + _sar = sar + af * (ep - sar) + reverse = low_ < _sar - if HIGH > ep: - ep = HIGH + if high_ > ep: + ep = high_ af = min(af + af0, max_af) - new_sar = min(low.iloc[row - 1], low.iloc[row - 2], new_sar) + _sar = min(low.iloc[row - 1], low.iloc[row - 2], _sar) if reverse: - new_sar = ep + _sar = ep af = af0 - falling = not falling + falling = not falling # Must come before next line + ep = low_ if falling else high_ - if falling: - ep = LOW - else: - ep = HIGH + sar = _sar # Update SAR - sar = new_sar - - if not falling: - long.iloc[row] = sar - else: + # Seperate long/short sar based on falling + if falling: short.iloc[row] = sar + else: + long.iloc[row] = sar _af.iloc[row] = af reversal.iloc[row] = reverse @@ -118,12 +121,26 @@ def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **k psar.__doc__ = \ """Parabolic Stop and Reverse (psar) -Parabolic Stop and Reverse +Parabolic Stop and Reverse (PSAR) was developed by J. Wells Wilder, that is used +to determine trend direction and it's potential reversals in price. PSAR uses a +trailing stop and reverse method called "SAR," or stop and reverse, to identify +possible entries and exits. It is also known as SAR. + +PSAR indicator typically appears on a chart as a series of dots, either above or +below an asset's price, depending on the direction the price is moving. A dot is +placed below the price when it is trending upward, and above the price when it +is trending downward. + +Sources: + https://www.tradingview.com/pine-script-reference/#fun_sar + https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=66&Name=Parabolic Calculation: Default Inputs: af0=0.02, af=0.02, max_af=0.2 + See Source links + Args: high (pd.Series): Series of 'high's low (pd.Series): Series of 'low's diff --git a/pandas_ta/trend/xsignals.py b/pandas_ta/trend/xsignals.py index 7c08f3a..cf707fb 100644 --- a/pandas_ta/trend/xsignals.py +++ b/pandas_ta/trend/xsignals.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame from .tsignals import tsignals from pandas_ta.utils._signals import cross_value diff --git a/pandas_ta/utils/_core.py b/pandas_ta/utils/_core.py index 7fca158..0324689 100644 --- a/pandas_ta/utils/_core.py +++ b/pandas_ta/utils/_core.py @@ -4,7 +4,7 @@ from pathlib import Path from sys import float_info as sflt from numpy import argmax, argmin -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import DataFrame, Series from pandas.api.types import is_datetime64_any_dtype diff --git a/pandas_ta/utils/_math.py b/pandas_ta/utils/_math.py index e48918d..5bdbdcc 100644 --- a/pandas_ta/utils/_math.py +++ b/pandas_ta/utils/_math.py @@ -15,7 +15,7 @@ from numpy import fabs as npFabs from numpy import floor as npFloor from numpy import exp as npExp from numpy import log as npLog -from numpy import NaN as npNaN +from numpy import nan as npNaN from numpy import ndarray as npNdArray from numpy import seterr from numpy import sqrt as npSqrt diff --git a/pandas_ta/utils/_metrics.py b/pandas_ta/utils/_metrics.py index 511e995..f1fbe97 100644 --- a/pandas_ta/utils/_metrics.py +++ b/pandas_ta/utils/_metrics.py @@ -2,7 +2,7 @@ from typing import Tuple from numpy import log as npLog -from numpy import NaN as npNaN +from numpy import nan as npNaN from numpy import sqrt as npSqrt from pandas import Series, Timedelta diff --git a/pandas_ta/volatility/true_range.py b/pandas_ta/volatility/true_range.py index 4521517..687b630 100644 --- a/pandas_ta/volatility/true_range.py +++ b/pandas_ta/volatility/true_range.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from numpy import NaN as npNaN +from numpy import nan as npNaN from pandas import concat from pandas_ta import Imports from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series diff --git a/setup.py b/setup.py index e181535..f9feba5 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ setup( "pandas_ta.volatility", "pandas_ta.volume" ], - version=".".join(("0", "2", "88b")), + version=".".join(("0", "2", "89b")), description=long_description, long_description=long_description, author="Kevin Johnson", diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 7fbfad3..885eba1 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -159,6 +159,29 @@ class TestMomentum(TestCase): self.assertIsInstance(result, Series) self.assertEqual(result.name, "ER_10") + def test_dm(self): + result = pandas_ta.dm(self.high, self.low) + self.assertIsInstance(result, DataFrame) + self.assertEqual(result.name, "DM_14") + + try: + expected_pos = tal.PLUS_DM(self.high, self.low) + expected_neg = tal.MINUS_DM(self.high, self.low) + expecteddf = DataFrame({"DMP_14": expected_pos, "DMN_14": expected_neg}) + pdt.assert_frame_equal(result, expecteddf) + except AssertionError as ae: + try: + dmp = pandas_ta.utils.df_error_analysis(result.iloc[:,0], expecteddf.iloc[:,0], col=CORRELATION) + self.assertGreater(dmp, CORRELATION_THRESHOLD) + except Exception as ex: + error_analysis(result, CORRELATION, ex) + + try: + dmn = pandas_ta.utils.df_error_analysis(result.iloc[:,1], expecteddf.iloc[:,1], col=CORRELATION) + self.assertGreater(dmn, CORRELATION_THRESHOLD) + except Exception as ex: + error_analysis(result, CORRELATION, ex) + def test_eri(self): result = pandas_ta.eri(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame)