From db39a4eb3110adfdbd23cc0a253c870047bfd031 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Mon, 7 Sep 2020 17:33:51 -0700 Subject: [PATCH] ENH #70 stochrsi added BUG #114 #112 stoch TV correlated --- Makefile | 7 +- README.md | 16 +++-- pandas_ta/__init__.py | 2 +- pandas_ta/core.py | 15 ++-- pandas_ta/momentum/__init__.py | 1 + pandas_ta/momentum/bop.py | 20 +++--- pandas_ta/momentum/stoch.py | 100 +++++++++++++------------- pandas_ta/momentum/stochrsi.py | 102 +++++++++++++++++++++++++++ tests/config.py | 2 +- tests/test_indicator_momentum.py | 42 +++-------- tests/test_indicator_momentum_ext.py | 7 +- tests/test_strategy.py | 6 +- 12 files changed, 208 insertions(+), 112 deletions(-) create mode 100644 pandas_ta/momentum/stochrsi.py diff --git a/Makefile b/Makefile index 043a64a..acfb2c8 100644 --- a/Makefile +++ b/Makefile @@ -7,5 +7,8 @@ caches: init: pip install -r requirements.txt -test: - python -m unittest -v \ No newline at end of file +ti: + python -m unittest -v tests/test_indicator*.py + +ts: + python -m unittest -v tests/test_strategy.py \ No newline at end of file diff --git a/README.md b/README.md index 5dcaf3b..c298046 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ # Pandas Technical Analysis Library in _Python 3_ ![Example Chart](/images/TA_Chart.png) -_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that is built upon Python's Pandas library with more than 115 Indicators and Utility functions. These indicators are commonly used for financial time series datasets with columns or labels: datetime, _open_, _high_, _low_, _close_, _volume_, et al. Many commonly used indicators are included, such as: _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **many more**. +_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that is built upon Python's Pandas library with more than 120 Indicators and Utility functions. These indicators are commonly used for financial time series datasets with columns or labels: datetime, _open_, _high_, _low_, _close_, _volume_, et al. Many commonly used indicators are included, such as: _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **many more**. **Pandas TA** has three different ways of processing Technical Indicators as described below. The **primary** requirement to run indicators in [Pandas DataFrame Extension](https://pandas.pydata.org/pandas-docs/stable/extending.html) mode, is that _open, high, low, close, volume_ are **lowercase**. Depending on the indicator, they either return a named Series or a DataFrame in uppercase underscore parameter format. For example, MACD(fast=12, slow=26, signal=9) will return a DataFrame with columns: ['MACD_12_26_9', 'MACDh_12_26_9', 'MACDs_12_26_9']. @@ -29,7 +29,7 @@ Please take a moment to read **this** and the rest of this **README** before pos * Documentation could always use improvement. Can you contribute? * ### [Indicator or Feature Requests & Contributions](https://github.com/twopirllc/pandas-ta/issues) - * Please be as detailed and concise as possible. Links and screenshots and sometimes data samples are welcome. + * Please be as detailed as possible. Links, screenshots, and sometimes data samples are welcome. * You want a new indicator not currently listed. * You want an alternate version of an existing indicator. * The indicator does not match another website, library, broker platform, language, et al. @@ -38,7 +38,7 @@ Please take a moment to read **this** and the rest of this **README** before pos ## __Features__ -* Has 115+ indicators and utility functions. +* Has 120+ indicators and utility functions. * Easily add prefixes or suffixes or both to columns names. Useful for building Custom Strategies. * __Extended Pandas DataFrame__ as 'ta'. * Indicators are tightly correlated with the de facto [TA Lib](https://mrjbq7.github.io/ta-lib/) if they share common indicators. @@ -55,6 +55,9 @@ and _Weighted Moving Average_. * Updated *trend_return* utility to return a more pertinenet trade info for a _trend_. Example can be found in the [AI Example Notebook](https://github.com/twopirllc/pandas-ta/tree/master/examples/AIExample.ipynb). The notebook is still a work in progress and open to colloboration. +## __Breaking Indicators__ +* _Stochastic Oscillator_ (**stoch**): Now in line with Trading View's calculation. See: ```help(ta.stoch)``` + ## __New Indicators__ * _Squeeze_ (**squeeze**). A Momentum indicator. Both John Carter's TTM **and** Lazybear's TradingView versions are implemented. The default is John Carter's, or ```lazybear=False```. Set ```lazybear=True``` to enable Lazybear's. * _TTM Trend_ (**ttm_trend**). A trend indicator inspired from John Carter's book "Mastering the Trade". @@ -62,6 +65,7 @@ and _Weighted Moving Average_. * _Gann High-Low Activator_ (**hilo**) The Gann High Low Activator Indicator was created by Robert Krausz in a 1998 issue of Stocks & Commodities Magazine. It is a moving average based trend indicator consisting of two different simple moving averages. +* _Stochastic RSI_ (**stochrsi**) "Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll. In line with Trading View's calculation. See: ```help(ta.stochrsi)``` ## __Updated Indicators__ * _Fisher Transform_ (**fisher**): Added Fisher's default **ema** signal line. To change the length of the signal line, use the argument: ```signal=5```. Default: 5 @@ -321,7 +325,7 @@ print(bothhl2.name) # "pre_HL2_post" * _Doji_: **cdl_doji** * _Heikin-Ashi_: **ha** -## _Momentum_ (32) +## _Momentum_ (33) * _Awesome Oscillator_: **ao** * _Absolute Price Oscillator_: **apo** @@ -350,8 +354,9 @@ print(bothhl2.name) # "pre_HL2_post" * _Slope_: **slope** * _SMI Ergodic_ **smi** * _Squeeze_: **squeeze** - * Default is John Carter's. Enable Lazybear's by ```lazybear=True``` + * Default is John Carter's. Enable Lazybear's with ```lazybear=True``` * _Stochastic Oscillator_: **stoch** +* _Stochastic RSI_: **stochrsi** * _Trix_: **trix** * _True strength index_: **tsi** * _Ultimate Oscillator_: **uo** @@ -501,6 +506,7 @@ Use parameter: cumulative=**True** for cumulative results. * [allahyarzadeh](https://github.com/allahyarzadeh) * [FGU1](https://github.com/FGU1) * [lluissalord](https://github.com/lluissalord) +* [SoftDevDanial](https://github.com/SoftDevDanial) * [YuvalWein](https://github.com/YuvalWein) diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index ab5a4f6..071b785 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -25,7 +25,7 @@ Category = { "candles": ["cdl_doji", "ha"], # Momentum - "momentum": ["ao", "apo", "bias", "bop", "brar", "cci", "cg", "cmo", "coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "pvo", "roc", "rsi", "rvgi", "slope", "smi", "squeeze", "stoch", "trix", "tsi", "uo", "willr"], + "momentum": ["ao", "apo", "bias", "bop", "brar", "cci", "cg", "cmo", "coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "pvo", "roc", "rsi", "rvgi", "slope", "smi", "squeeze", "stoch", "stochrsi", "trix", "tsi", "uo", "willr"], # Overlap "overlap": ["dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", "kama", "linreg", "midpoint", "midprice", "ohlc4", "pwma", "rma", "sinwma", "sma", "supertrend", "swma", "t3", "tema", "trima", "vwap", "vwma", "wcp", "wma", "zlma"], diff --git a/pandas_ta/core.py b/pandas_ta/core.py index 1e6206d..927168f 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -23,7 +23,7 @@ from pandas_ta.volatility import * from pandas_ta.volume import * from pandas_ta.utils import * -version = ".".join(("0", "2", "00b")) +version = ".".join(("0", "2", "01b")) def mp_worker(args): @@ -146,7 +146,7 @@ class BasePandasObject(PandasObject): # Preemptively drop the rows that are all nas # Might need to be moved to AnalysisIndicators.__call__() to be # toggleable via kwargs. - df.dropna(axis=0, inplace=True) + # df.dropna(axis=0, inplace=True) # Preemptively rename columns to lowercase df.rename(columns=common_names, errors="ignore", inplace=True) @@ -643,13 +643,13 @@ class AnalysisIndicators(BasePandasObject): return result @finalize - def bop(self, open_=None, high=None, low=None, close=None, percentage=False, offset=None, **kwargs): + def bop(self, open_=None, high=None, low=None, close=None, scalar=None, offset=None, **kwargs): open_ = self._get_column(open_, "open") high = self._get_column(high, "high") low = self._get_column(low, "low") close = self._get_column(close, "close") - result = bop(open_=open_, high=high, low=low, close=close, percentage=percentage, offset=offset, **kwargs) + result = bop(open_=open_, high=high, low=low, close=close, scalar=scalar, offset=offset, **kwargs) return result @finalize @@ -848,6 +848,13 @@ class AnalysisIndicators(BasePandasObject): result = stoch(high=high, low=low, close=close, fast_k=fast_k, slow_k=slow_k, slow_d=slow_d, offset=offset, **kwargs) return result + @finalize + def stochrsi(self, close=None, length=None, rsi_length=None, k=None, d=None, offset=None, **kwargs): + close = self._get_column(close, "close") + + result = stochrsi(close=close, length=length, rsi_length=rsi_length, k=k, d=d, offset=offset, **kwargs) + return result + @finalize def trix(self, close=None, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs): close = self._get_column(close, "close") diff --git a/pandas_ta/momentum/__init__.py b/pandas_ta/momentum/__init__.py index 0321474..8de9f33 100644 --- a/pandas_ta/momentum/__init__.py +++ b/pandas_ta/momentum/__init__.py @@ -27,6 +27,7 @@ from .slope import slope from .smi import smi from .squeeze import squeeze from .stoch import stoch +from .stochrsi import stochrsi from .trix import trix from .tsi import tsi from .uo import uo diff --git a/pandas_ta/momentum/bop.py b/pandas_ta/momentum/bop.py index 4679f63..93f90c3 100644 --- a/pandas_ta/momentum/bop.py +++ b/pandas_ta/momentum/bop.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- from ..utils import get_offset, non_zero_range, verify_series -def bop(open_, high, low, close, offset=None, **kwargs): +def bop(open_, high, low, close, scalar=None, offset=None, **kwargs): """Indicator: Balance of Power (BOP)""" # Validate Arguments open_ = verify_series(open_) high = verify_series(high) low = verify_series(low) close = verify_series(close) + scalar = float(scalar) if scalar else 1 high_low_range = non_zero_range(high, low) close_open_range = non_zero_range(close, open_) offset = get_offset(offset) # Calculate Result - bop = close_open_range / high_low_range + bop = scalar * close_open_range / high_low_range # Offset if offset != 0: bop = bop.shift(offset) # Handle fills - if 'fillna' in kwargs: - bop.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - bop.fillna(method=kwargs['fill_method'], inplace=True) + if "fillna" in kwargs: + bop.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + bop.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it bop.name = f"BOP" - bop.category = 'momentum' + bop.category = "momentum" return bop @@ -42,14 +43,15 @@ Sources: http://www.worden.com/TeleChartHelp/Content/Indicators/Balance_of_Power.htm Calculation: - BOP = (close - open) / (high - low) + BOP = scalar * (close - open) / (high - low) 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 + scalar (float): How much to magnify. Default: 1 + offset (int): How many periods to offset the result. Default: 0 Kwargs: fillna (value, optional): pd.DataFrame.fillna(value) diff --git a/pandas_ta/momentum/stoch.py b/pandas_ta/momentum/stoch.py index 2a06bf8..807896e 100644 --- a/pandas_ta/momentum/stoch.py +++ b/pandas_ta/momentum/stoch.py @@ -1,95 +1,93 @@ # -*- coding: utf-8 -*- from pandas import DataFrame -from ..overlap.sma import sma -from ..utils import get_offset, non_zero_range, verify_series +from pandas_ta.overlap import sma +from pandas_ta.utils import get_offset, non_zero_range, verify_series -def stoch(high, low, close, fast_k=None, slow_k=None, slow_d=None, offset=None, **kwargs): +def stoch(high, low, close, k=None, d=None, smooth_k=None, offset=None, **kwargs): """Indicator: Stochastic Oscillator (STOCH)""" # Validate arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) - fast_k = fast_k if fast_k and fast_k > 0 else 14 - slow_k = slow_k if slow_k and slow_k > 0 else 5 - slow_d = slow_d if slow_d and slow_d > 0 else 3 + k = k if k and k > 0 else 14 + d = d if d and d > 0 else 3 + smooth_k = smooth_k if smooth_k and smooth_k > 0 else 3 offset = get_offset(offset) # Calculate Result - lowest_low = low.rolling(fast_k).min() - highest_high = high.rolling(fast_k).max() + lowest_low = low.rolling(k).min() + highest_high = high.rolling(k).max() - fastk = 100 * (close - lowest_low) / non_zero_range(highest_high, lowest_low) - fastd = sma(fastk, length=slow_d) + stoch = 100 * (close - lowest_low) + stoch /= non_zero_range(highest_high, lowest_low) - slowk = sma(fastk, length=slow_k) - slowd = sma(slowk, length=slow_d) + stoch_k = sma(stoch, length=smooth_k) + stoch_d = sma(stoch_k, length=d) # Offset if offset != 0: - fastk = fastk.shift(offset) - fastd = fastd.shift(offset) - slowk = slowk.shift(offset) - slowd = slowd.shift(offset) + stoch_k = stoch_k.shift(offset) + stoch_d = stoch_d.shift(offset) # Handle fills - if 'fillna' in kwargs: - fastk.fillna(kwargs['fillna'], inplace=True) - fastd.fillna(kwargs['fillna'], inplace=True) - slowk.fillna(kwargs['fillna'], inplace=True) - slowd.fillna(kwargs['fillna'], inplace=True) - if 'fill_method' in kwargs: - fastk.fillna(method=kwargs['fill_method'], inplace=True) - fastd.fillna(method=kwargs['fill_method'], inplace=True) - slowk.fillna(method=kwargs['fill_method'], inplace=True) - slowd.fillna(method=kwargs['fill_method'], inplace=True) + if "fillna" in kwargs: + stoch_k.fillna(kwargs["fillna"], inplace=True) + stoch_d.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + stoch_k.fillna(method=kwargs["fill_method"], inplace=True) + stoch_d.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it - fastk.name = f"STOCHFk_{fast_k}" - fastd.name = f"STOCHFd_{slow_d}" - slowk.name = f"STOCHk_{slow_k}" - slowd.name = f"STOCHd_{slow_d}" - fastk.category = fastd.category = slowk.category = slowd.category = 'momentum' + _name = "STOCH" + _props = f"_{k}_{d}_{smooth_k}" + stoch_k.name = f"{_name}k{_props}" + stoch_d.name = f"{_name}d{_props}" + stoch_k.category = stoch_d.category = "momentum" # Prepare DataFrame to return - _props = f"_{fast_k}_{slow_k}_{slow_d}" - data = {fastk.name: fastk, fastd.name: fastd, slowk.name: slowk, slowd.name: slowd} - stochdf = DataFrame(data) - stochdf.name = f"STOCH{_props}" - stochdf.category = 'momentum' + data = {stoch_k.name: stoch_k, stoch_d.name: stoch_d} + df = DataFrame(data) + df.name = f"{_name}{_props}" + df.category = stoch_k.category - return stochdf + return df stoch.__doc__ = \ """Stochastic (STOCH) -Stochastic Oscillator is a range bound momentum indicator. It displays the location -of the close relative to the high-low range over a period. +The Stochastic Oscillator (STOCH) was developed by George Lane in the 1950's. +He believed this indicator was a good way to measure momentum because changes in +momentum precede changes in price. + +It is a range-bound oscillator with two lines moving between 0 and 100. +The first line (%K) displays the current close in relation to the period's +high/low range. The second line (%D) is a Simple Moving Average of the %K line. +The most common choices are a 14 period %K and a 3 period SMA for %D. Sources: https://www.tradingview.com/wiki/Stochastic_(STOCH) + https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=332&Name=KD_-_Slow Calculation: Default Inputs: - fast_k=14, slow_k=5, slow_d=3 + k=14, d=3, smooth_k=3 SMA = Simple Moving Average - LL = low for last fast_k periods - HH = high for last fast_k periods + LL = low for last k periods + HH = high for last k periods - FASTK = 100 * (close - LL) / (HH - LL) - FASTD = SMA(FASTK, slow_d) - - SLOWK = SMA(FASTK, slow_k) - SLOWD = SMA(SLOWK, slow_d) + STOCH = 100 * (close - LL) / (HH - LL) + STOCHk = SMA(STOCH, smooth_k) + STOCHd = SMA(FASTK, d) Args: high (pd.Series): Series of 'high's low (pd.Series): Series of 'low's close (pd.Series): Series of 'close's - fast_k (int): The Fast %K period. Default: 14 - slow_k (int): The Slow %K period. Default: 5 - slow_d (int): The Slow %D period. Default: 3 + k (int): The Fast %K period. Default: 14 + d (int): The Slow %K period. Default: 3 + smooth_k (int): The Slow %D period. Default: 3 offset (int): How many periods to offset the result. Default: 0 Kwargs: @@ -97,5 +95,5 @@ Kwargs: fill_method (value, optional): Type of fill method Returns: - pd.DataFrame: fastk, fastd, slowk, slowd columns. + pd.DataFrame: %K, %D columns. """ \ No newline at end of file diff --git a/pandas_ta/momentum/stochrsi.py b/pandas_ta/momentum/stochrsi.py new file mode 100644 index 0000000..48f685f --- /dev/null +++ b/pandas_ta/momentum/stochrsi.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +from pandas import DataFrame +from .rsi import rsi +from pandas_ta.overlap import sma +from pandas_ta.utils import get_offset, non_zero_range, verify_series + +def stochrsi(close, length=None, rsi_length=None, k=None, d=None, offset=None, **kwargs): + """Indicator: Stochastic RSI Oscillator (STOCHRSI)""" + # Validate arguments + close = verify_series(close) + length = length if length and length > 0 else 14 + rsi_length = rsi_length if rsi_length and rsi_length > 0 else 14 + k = k if k and k > 0 else 3 + d = d if d and d > 0 else 3 + offset = get_offset(offset) + + # Calculate Result + rsi_ = rsi(close, length=rsi_length) + lowest_rsi = rsi_.rolling(length).min() + highest_rsi = rsi_.rolling(length).max() + + stoch = 100 * (rsi_ - lowest_rsi) + stoch /= non_zero_range(highest_rsi, lowest_rsi) + + stochrsi_k = sma(stoch, length=k) + stochrsi_d = sma(stochrsi_k, length=d) + + # Offset + if offset != 0: + stochrsi_k = stochrsi_k.shift(offset) + stochrsi_d = stochrsi_d.shift(offset) + + # Handle fills + if "fillna" in kwargs: + stochrsi_k.fillna(kwargs["fillna"], inplace=True) + stochrsi_d.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: + stochrsi_k.fillna(method=kwargs["fill_method"], inplace=True) + stochrsi_d.fillna(method=kwargs["fill_method"], inplace=True) + + # Name and Categorize it + _name = "STOCHRSI" + _props = f"_{length}_{rsi_length}_{k}_{d}" + stochrsi_k.name = f"{_name}k{_props}" + stochrsi_d.name = f"{_name}d{_props}" + stochrsi_k.category = stochrsi_d.category = "momentum" + + # Prepare DataFrame to return + data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d} + df = DataFrame(data) + df.name = f"{_name}{_props}" + df.category = stochrsi_k.category + + return df + + + +stochrsi.__doc__ = \ +"""Stochastic (STOCHRSI) + + +"Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll and published in Stock & Commodities V.11:5 (189-199) + +It is a range-bound oscillator with two lines moving between 0 and 100. +The first line (%K) displays the current RSI in relation to the period's +high/low range. The second line (%D) is a Simple Moving Average of the %K line. +The most common choices are a 14 period %K and a 3 period SMA for %D. + +Sources: + https://www.tradingview.com/wiki/Stochastic_(STOCH) + +Calculation: + Default Inputs: + length=14, rsi_length=14, k=3, d=3 + RSI = Relative Strength Index + SMA = Simple Moving Average + + RSI = RSI(high, low, close, rsi_length) + LL = lowest RSI for last rsi_length periods + HH = highest RSI for last rsi_length periods + + STOCHRSI = 100 * (RSI - LL) / (HH - LL) + STOCHRSIk = SMA(STOCHRSI, k) + STOCHRSId = SMA(STOCHRSIk, d) + +Args: + high (pd.Series): Series of 'high's + low (pd.Series): Series of 'low's + close (pd.Series): Series of 'close's + length (int): The STOCHRSI period. Default: 14 + rsi_length (int): RSI period. Default: 14 + k (int): The Fast %K period. Default: 3 + d (int): The Slow %K period. Default: 3 + 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: RSI %K, RSI %D columns. +""" \ No newline at end of file diff --git a/tests/config.py b/tests/config.py index dde7f77..7c01aa3 100644 --- a/tests/config.py +++ b/tests/config.py @@ -19,6 +19,6 @@ sample_data = read_csv( def error_analysis(df, kind, msg, icon=INFO, newline=True): if VERBOSE: - s = f" {icon} {df.name}['{kind}']: {msg}" + s = f"{icon} {df.name}['{kind}']: {msg}" if newline: s = f"\n{s}" print(s) \ No newline at end of file diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 0222866..0efc501 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -335,45 +335,19 @@ class TestMomentum(TestCase): self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZhlr_20_2.0_20_1.5_LB") + # @skip def test_stoch(self): - result = pandas_ta.stoch(self.high, self.low, self.close, fast_k=14, slow_k=14, slow_d=14) - self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, "STOCH_14_14_14") - self.assertEqual(len(result.columns), 4) - + # TV Correlation result = pandas_ta.stoch(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, "STOCH_14_5_3") + self.assertEqual(result.name, "STOCH_14_3_3") - try: - tal_stochf = tal.STOCHF(self.high, self.low, self.close) - tal_stoch = tal.STOCH(self.high, self.low, self.close) - tal_stochdf = DataFrame({"STOCHF_14": tal_stochf[0], "STOCHF_3": tal_stochf[1], "STOCH_5": tal_stoch[0], "STOCH_3": tal_stoch[1]}) - pdt.assert_frame_equal(result, tal_stochdf) - except AssertionError as ae: - try: - stochfk_corr = pandas_ta.utils.df_error_analysis(result.iloc[:,0], tal_stochdf.iloc[:,0], col=CORRELATION) - self.assertGreater(stochfk_corr, CORRELATION_THRESHOLD) - except Exception as ex: - error_analysis(result.iloc[:,0], CORRELATION, ex) - try: - stochfd_corr = pandas_ta.utils.df_error_analysis(result.iloc[:,1], tal_stochdf.iloc[:,1], col=CORRELATION) - self.assertGreater(stochfd_corr, CORRELATION_THRESHOLD) - except Exception as ex: - error_analysis(result.iloc[:,1], CORRELATION, ex, newline=False) - - try: - stochsk_corr = pandas_ta.utils.df_error_analysis(result.iloc[:,2], tal_stochdf.iloc[:,2], col=CORRELATION) - self.assertGreater(stochsk_corr, CORRELATION_THRESHOLD) - except Exception as ex: - error_analysis(result.iloc[:,2], CORRELATION, ex, newline=False) - - try: - stochsd_corr = pandas_ta.utils.df_error_analysis(result.iloc[:,3], tal_stochdf.iloc[:,3], col=CORRELATION) - self.assertGreater(stochsd_corr, CORRELATION_THRESHOLD) - except Exception as ex: - error_analysis(result.iloc[:,3], CORRELATION, ex, newline=False) + def test_stochrsi(self): + # TV Correlation + result = pandas_ta.stochrsi(self.close) + self.assertIsInstance(result, DataFrame) + self.assertEqual(result.name, "STOCHRSI_14_14_3_3") def test_trix(self): result = pandas_ta.trix(self.close) diff --git a/tests/test_indicator_momentum_ext.py b/tests/test_indicator_momentum_ext.py index 7439337..5369479 100644 --- a/tests/test_indicator_momentum_ext.py +++ b/tests/test_indicator_momentum_ext.py @@ -187,7 +187,12 @@ class TestMomentumExtension(TestCase): def test_stoch_ext(self): self.data.ta.stoch(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ["STOCHFk_14", "STOCHFd_3", "STOCHk_5", "STOCHd_3"]) + self.assertEqual(list(self.data.columns[-2:]), ["STOCHk_14_3_3", "STOCHd_14_3_3"]) + + def test_stochrsi_ext(self): + self.data.ta.stochrsi(append=True) + self.assertIsInstance(self.data, DataFrame) + self.assertEqual(list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"]) def test_trix_ext(self): self.data.ta.trix(append=True) diff --git a/tests/test_strategy.py b/tests/test_strategy.py index 493b570..a3bede2 100644 --- a/tests/test_strategy.py +++ b/tests/test_strategy.py @@ -10,8 +10,7 @@ from pandas import DataFrame class TestStrategyMethods(TestCase): @classmethod def setUpClass(cls): - cls.data = sample_data.tail(210).copy() - print(cls.data.shape) + cls.data = sample_data @classmethod def tearDownClass(cls): @@ -89,9 +88,8 @@ class TestStrategyMethods(TestCase): "MACD and RSI Momo with BBANDS and SMAs 50 & 200 and Cumulative Log Returns" # description ) - print(self.data.shape) init_cols = len(self.data.columns) - self.data.ta.strategy(custom, verbose=False, timed=True) + self.data.ta.strategy(custom, verbose=False) added_cols = len(self.data.columns) - init_cols self.assertEqual(added_cols, 11)