diff --git a/README.md b/README.md
index 00d5595..707912b 100644
--- a/README.md
+++ b/README.md
@@ -34,10 +34,11 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Pandas TA Strategies](#pandas-ta-strategies)
* [Types of Strategies](#types-of-strategies)
* [DataFrame Properties](#dataframe-properties)
+* [DataFrame Methods](#dataframe-methods)
* [Indicators by Category](#indicators-by-category)
* [Candles](#candles-3)
* [Cycles](#cycles-1)
- * [Momentum](#momentum-36)
+ * [Momentum](#momentum-37)
* [Overlap](#overlap-31)
* [Performance](#performance-4)
* [Statistics](#statistics-9)
@@ -84,7 +85,7 @@ $ pip install pandas_ta
Latest Version
--------------
-Best choice! Version: *0.2.53b*
+Best choice! Version: *0.2.62b*
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
```
@@ -182,7 +183,7 @@ Thanks for using **Pandas TA**!
_Thank you for your contributions!_
-[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [codesutras](https://github.com/codesutras) | [daikts](https://github.com/daikts) | [DrPaprikaa](https://github.com/DrPaprikaa) | [edwardwang1](https://github.com/edwardwang1) | [ffhirata](https://github.com/ffhirata) | [FGU1](https://github.com/FGU1) | [lluissalord](https://github.com/lluissalord) | [maxdignan](https://github.com/maxdignan) | [moritzgun](https://github.com/moritzgun) | [M6stafa](https://github.com/M6stafa) | [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) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein)
+[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [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) | [lluissalord](https://github.com/lluissalord) | [M6stafa](https://github.com/M6stafa) | [maxdignan](https://github.com/maxdignan) | [moritzgun](https://github.com/moritzgun) | [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) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein)
@@ -566,7 +567,7 @@ help(ta.yf)
* _Even Better Sinewave_: **ebsw**
-### **Momentum** (36)
+### **Momentum** (37)
* _Awesome Oscillator_: **ao**
* _Absolute Price Oscillator_: **apo**
@@ -601,7 +602,8 @@ help(ta.yf)
* Default is John Carter's. Enable Lazybear's with ```lazybear=True```
* _Stochastic Oscillator_: **stoch**
* _Stochastic RSI_: **stochrsi**
-* _TD Sequential_: **td**
+* _TD Sequential_: **td_seq**
+ * Excluded from ```df.ta.strategy()```.
* _Trix_: **trix**
* _True strength index_: **tsi**
* _Ultimate Oscillator_: **uo**
@@ -799,27 +801,12 @@ result = ta.cagr(df.close)
-## **Breaking Indicators**
-* _Bollinger Bands_ (**bbands**): New column 'bandwidth' appended to the returning DataFrame. See: ```help(ta.bbands)```
-* _Volume Weighted Average Price_ (**vwap**): **Requires** the DataFrame index to be a DatetimeIndex.
-
-
## **New Indicators**
* _Arnaud Legoux Moving Average_ (**alma**) uses the curve of the Normal (Gauss) distribution to allow regulating the smoothness and high sensitivity of the indicator. See: ```help(ta.alma)```
-* _Drawdown_ (**drawdown**) shows the peak-to-trough decline during a specific period for an investment,
trading account, or fund. See: ```help(ta.drawdown)```
* _Even Better Sinewave_ (**ebsw**) measures market cycles and uses a low pass filter to remove noise. See: ```help(ta.ebsw)```
-* _Gann High-Low Activator_ (**hilo**) was created by Robert Krausz in a 1998. See: ```help(ta.hilo)```
-* _Holt-Winter Moving Average_ (**hwma**) is a three-parameter moving average by the Holt-Winter method.
-* _McGinley Dynamic_ (**mcgd**) is an overlap indicator developed by John R. McGinley, a Certified Market Technician. See: ```help(ta.mcgd)```
-* _Price Volume Rank_ (**pvr**) was created by Anthony J. Macek. See: ```help(ta.pvr)```
-* _Quantitative Qualitative Estimation_ (**qqe**) is like SuperTrend for a Smoothed RSI. See: ```help(ta.qqe)```
-article in the June, 1994 issue of Technical Analysis of Stocks & Commodities Magazine. See: ```help(ta.pvr)```
-* _Relative Strength Xtra_ (**rsx**) is based on the popular RSI indicator and inspired by the work Jurik Research. See: ```help(ta.rsx)```
-* _Ehler's Super Smoother Filter_ (**ssf**). Ehler's solution to reduce lag and remove aliasing noise compared to other common moving average indicators. See: ```help(ta.ssf)```
-* _Elder's Thermometer_ (**thermo**) measures price volatility. See: ```help(ta.thermo)```
-* _TTM Trend_ (**ttm_trend**) is a trend indicator inspired from John Carter's book "Mastering the Trade" issue of Stocks & Commodities Magazine. It is a moving average based trend indicator consisting of two different simple moving averages. See: ```help(ta.ttm_trend)```
-* _Variable Index Dynamic Average_ (**vidya**) is a popular Dynamic Moving Average created by Tushar Chande. See: ```help(ta.vidya)```
+* _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)```
+
## **Updated Indicators**
* _ADX_ (**adx**): Added ```mamode``` with default "**RMA**" and with the same ```mamode``` options as TradingView. See ```help(ta.adx)```.
diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py
index 6f7e029..7972ef3 100644
--- a/pandas_ta/__init__.py
+++ b/pandas_ta/__init__.py
@@ -47,7 +47,7 @@ Category = {
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
"coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
"mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi",
- "slope", "smi", "squeeze", "stoch", "stochrsi", "trix", "tsi", "uo",
+ "slope", "smi", "squeeze", "stoch", "stochrsi", "td_seq", "trix", "tsi", "uo",
"willr"
],
# Overlap
diff --git a/pandas_ta/core.py b/pandas_ta/core.py
index 5de6004..2ca01f5 100644
--- a/pandas_ta/core.py
+++ b/pandas_ta/core.py
@@ -607,21 +607,30 @@ class AnalysisIndicators(BasePandasObject):
"""Strategy Method
An experimental method that by default runs all applicable indicators.
- Future implementations will allow more specific indicator generation through
- a json config file.
+ Future implementations will allow more specific indicator generation
+ with possibly as json, yaml config file or an sqlite3 table.
- Args:
- name (str, optional): Default: "all"
- exclude (list, optional): Default: []. List of indicator names to exclude.
- kwargs:
- (optional) Default: {}. Any indicator argument you want to modify.
- For example, length=20 or offset=-1 or high=df["high"] ...
+ Kwargs:
+ chunksize (bool): Adjust the chunksize for the Multiprocessing Pool.
+ Default: Number of cores of the OS
+ exclude (list): List of indicator names to exclude. Some are
+ excluded by default for various reasons; they require additional
+ sources, performance (td_seq), not a ohlcv chart (vp) etc.
+ name (str): Select all indicators or indicators by
+ Category such as: "candles", "cycles", "momentum", "overlap",
+ "performance", "statistics", "trend", "volatility", "volume", or
+ "all". Default: "all"
+ ordered (bool): Whether to run "all" in order. Default: True
+ timed (bool): Show the process time of the strategy().
+ Default: False
+ verbose (bool): Provide some additional insight on the progress of
+ the strategy() execution. Default: False
"""
# cpus = cpu_count()
# Ensure indicators are appended to the DataFrame
kwargs["append"] = True
- all_ordered = kwargs.pop("ordered", False)
+ all_ordered = kwargs.pop("ordered", True)
mp_chunksize = kwargs.pop("chunksize", self.cores)
# Initialize
@@ -637,6 +646,7 @@ class AnalysisIndicators(BasePandasObject):
"long_run",
"short_run",
"trend_return",
+ "td_seq", # Performance exclusion
"vp",
]
@@ -702,7 +712,7 @@ class AnalysisIndicators(BasePandasObject):
# Some magic to optimize chunksize for speed based on total ta indicators
_chunksize = mp_chunksize - 1 if mp_chunksize > _total_ta else int(npLog10(_total_ta)) + 1
if verbose:
- print(f"[i] Multiprocessing {_total_ta} indicators with {_chunksize} chunks over {self.cores}/{cpu_count()} cpus.")
+ print(f"[i] Multiprocessing {_total_ta} indicators with {_chunksize} chunks and {self.cores}/{cpu_count()} cpus.")
results = None
if mode["custom"]:
@@ -810,7 +820,11 @@ class AnalysisIndicators(BasePandasObject):
elif df.empty:
print(f"[X] DataFrame is empty: {df.shape}")
return
- else: self._df = df
+ else:
+ if kwargs.pop("lc_input", False):
+ df.index.name = df.index.name.lower()
+ df.columns = df.columns.str.lower()
+ self._df = df
if strategy is not None: self.strategy(strategy, **kwargs)
return df
@@ -1043,10 +1057,9 @@ class AnalysisIndicators(BasePandasObject):
result = stochrsi(high=high, low=low, close=close, length=length, rsi_length=rsi_length, k=k, d=d, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
- def td(self, offset=None, show_all=True, **kwargs):
+ def td_seq(self, asint=None, offset=None, show_all=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
-
- result = td(close=close, offset=offset, show_all=show_all, **kwargs)
+ result = td_seq(close=close, asint=asint, offset=offset, show_all=show_all, **kwargs)
return self._post_process(result, **kwargs)
def trix(self, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs):
diff --git a/pandas_ta/momentum/__init__.py b/pandas_ta/momentum/__init__.py
index 9eaa0fb..ded8f90 100644
--- a/pandas_ta/momentum/__init__.py
+++ b/pandas_ta/momentum/__init__.py
@@ -31,7 +31,7 @@ from .smi import smi
from .squeeze import squeeze
from .stoch import stoch
from .stochrsi import stochrsi
-from .td import td
+from .td_seq import td_seq
from .trix import trix
from .tsi import tsi
from .uo import uo
diff --git a/pandas_ta/momentum/td.py b/pandas_ta/momentum/td.py
deleted file mode 100644
index d60a415..0000000
--- a/pandas_ta/momentum/td.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-import numpy as np
-from pandas import DataFrame, Series
-from pandas_ta.utils import get_offset, verify_series
-
-def true_sequence_count(s):
- index = s.where(s == False).last_valid_index()
-
- if index is None:
- return s.count()
- else:
- s = s[s.index > index]
- return s.count()
-
-def calc_td(close, direction, show_all):
- td_bool = close.diff(4) > 0 if direction=='up' else close.diff(4) < 0
- td_num = np.where(td_bool, td_bool.rolling(13, min_periods=0).apply(true_sequence_count), 0)
- td_num = Series(td_num)
-
- if show_all:
- td_num = td_num.mask(td_num == 0)
- else:
- td_num = td_num.mask(~td_num.between(6,9))
-
- return td_num
-
-def td(close, offset=None, show_all=True, **kwargs):
- up = calc_td(close, 'up', show_all)
- down = calc_td(close, 'down', show_all)
- df = DataFrame({'TD_up': up, 'TD_down': down})
-
- # Offset
- if offset and offset != 0:
- df = df.shift(offset)
-
- if "fillna" in kwargs:
- df.fillna(kwargs["fillna"], inplace=True)
-
- # Name & Category
- df.name = "TD"
- df.category = "momentum"
-
- return df
-
-td.__doc__ = \
-"""TD Sequential (TD)
-
-TD Sequential indicator.
-
-Sources:
- https://tradetrekker.wordpress.com/tdsequential/
-
-Calculation:
- compare current close price with 4 days ago price, up to 13 days.
- for the consecutive ascending or descending price sequence, display 6th to 9th day value.
-
-Args:
- close (pd.Series): Series of 'close's
- offset (int): How many periods to offset the result. Default: 0
- show_all (bool): default True, show 1 - 13. If set to false, only show 6 - 9
-
-Kwargs:
- fillna (value, optional): pd.DataFrame.fillna(value)
-
-Returns:
- pd.DataFrame: New feature generated.
-"""
\ No newline at end of file
diff --git a/pandas_ta/momentum/td_seq.py b/pandas_ta/momentum/td_seq.py
new file mode 100644
index 0000000..8bd12fd
--- /dev/null
+++ b/pandas_ta/momentum/td_seq.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+# import numpy as np
+from numpy import where as npWhere
+from pandas import DataFrame, Series
+from pandas_ta.utils import get_offset, verify_series
+
+
+def td_seq(close, asint=None, offset=None, **kwargs):
+ """Indicator: Tom Demark Sequential (TD_SEQ)"""
+ # Validate arguments
+ close = verify_series(close)
+ offset = get_offset(offset)
+ asint = asint if isinstance(asint, bool) else False
+ show_all = kwargs.setdefault("show_all", True)
+
+ def true_sequence_count(series: Series):
+ index = series.where(series == False).last_valid_index()
+
+ if index is None:
+ return series.count()
+ else:
+ s = series[series.index > index]
+ return s.count()
+
+ def calc_td(series: Series, direction: str, show_all: bool):
+ td_bool = series.diff(4) > 0 if direction=="up" else series.diff(4) < 0
+ td_num = npWhere(
+ td_bool, td_bool.rolling(13, min_periods=0).apply(true_sequence_count), 0
+ )
+ td_num = Series(td_num)
+
+ if show_all:
+ td_num = td_num.mask(td_num == 0)
+ else:
+ td_num = td_num.mask(~td_num.between(6,9))
+
+ return td_num
+
+ up_seq = calc_td(close, "up", show_all)
+ down_seq = calc_td(close, "down", show_all)
+
+ if asint:
+ if up_seq.hasnans and down_seq.hasnans:
+ up_seq.fillna(0, inplace=True)
+ down_seq.fillna(0, inplace=True)
+ up_seq = up_seq.astype(int)
+ down_seq = down_seq.astype(int)
+
+ # Offset
+ if offset != 0:
+ up_seq = up_seq.shift(offset)
+ down_seq = down_seq.shift(offset)
+
+ # Handle fills
+ if "fillna" in kwargs:
+ up_seq.fillna(kwargs["fillna"], inplace=True)
+ down_seq.fillna(kwargs["fillna"], inplace=True)
+
+ if "fill_method" in kwargs:
+ up_seq.fillna(method=kwargs["fill_method"], inplace=True)
+ down_seq.fillna(method=kwargs["fill_method"], inplace=True)
+
+ # Name & Category
+ up_seq.name = f"TD_SEQ_UPa" if show_all else f"TD_SEQ_UP"
+ down_seq.name = f"TD_SEQ_DNa" if show_all else f"TD_SEQ_DN"
+ up_seq.category = down_seq.category = "momentum"
+
+ # Prepare Dataframe to return
+ data = {
+ up_seq.name: up_seq,
+ down_seq.name: down_seq
+ }
+ df = DataFrame(data)
+ df.name = "TD_SEQ"
+ df.category = up_seq.category
+
+ return df
+
+
+td_seq.__doc__ = \
+"""TD Sequential (TD_SEQ)
+
+Tom DeMark's Sequential indicator attempts to identify a price point where an
+uptrend or a downtrend exhausts itself and reverses.
+
+Sources:
+ https://tradetrekker.wordpress.com/tdsequential/
+
+Calculation:
+ Compare current close price with 4 days ago price, up to 13 days. For the
+ consecutive ascending or descending price sequence, display 6th to 9th day
+ value.
+
+Args:
+ close (pd.Series): Series of 'close's
+ asint (bool): If True, fillnas with 0 and change type to int. Default: False
+ offset (int): How many periods to offset the result. Default: 0
+
+Kwargs:
+ show_all (bool): Show 1 - 13. If set to False, show 6 - 9. Default: True
+ fillna (value, optional): pd.DataFrame.fillna(value)
+
+Returns:
+ pd.DataFrame: New feature generated.
+"""
\ No newline at end of file
diff --git a/pandas_ta/trend/cksp.py b/pandas_ta/trend/cksp.py
index c9f9a55..88dde58 100644
--- a/pandas_ta/trend/cksp.py
+++ b/pandas_ta/trend/cksp.py
@@ -57,10 +57,9 @@ def cksp(high, low, close, p=None, x=None, q=None, offset=None, **kwargs):
cksp.__doc__ = \
"""Chande Kroll Stop (CKSP)
-The Tushar Chande and Stanley Kroll in their book
-“The New Technical Trader”. It is a trend-following indicator,
-identifying your stop by calculating the average true range of
-the recent market volatility.
+The Tushar Chande and Stanley Kroll in their book “The New Technical Trader”.
+It is a trend-following indicator, identifying your stop by calculating the
+average true range of the recent market volatility.
Sources:
https://www.multicharts.com/discussion/viewtopic.php?t=48914
diff --git a/pandas_ta/utils/_math.py b/pandas_ta/utils/_math.py
index 3bc8253..e532b37 100644
--- a/pandas_ta/utils/_math.py
+++ b/pandas_ta/utils/_math.py
@@ -57,7 +57,7 @@ def fibonacci(n: int = 2, **kwargs: dict) -> npNdArray:
a, b = 1, 1
result = npArray([a])
- for i in range(0, n):
+ for _ in range(0, n):
a, b = b, a + b
result = npAppend(result, a)
diff --git a/setup.py b/setup.py
index dc59882..946f8c2 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
- version=".".join(("0", "2", "61b")),
+ version=".".join(("0", "2", "62b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
diff --git a/tests/test_ext_indicator_momentum.py b/tests/test_ext_indicator_momentum.py
index 8beae3f..d5b5c95 100644
--- a/tests/test_ext_indicator_momentum.py
+++ b/tests/test_ext_indicator_momentum.py
@@ -207,10 +207,14 @@ class TestMomentumExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"])
- def test_td_ext(self):
- self.data.ta.td(append=True)
+ def test_td_seq_ext(self):
+ self.data.ta.td_seq(show_all=False, append=True)
self.assertIsInstance(self.data, DataFrame)
- self.assertEqual(list(self.data.columns[-2:]), ["TD_up", "TD_down"])
+ self.assertEqual(list(self.data.columns[-2:]), ["TD_SEQ_UP", "TD_SEQ_DN"])
+
+ self.data.ta.td_seq(show_all=True, append=True)
+ self.assertIsInstance(self.data, DataFrame)
+ self.assertEqual(list(self.data.columns[-2:]), ["TD_SEQ_UPa", "TD_SEQ_DNa"])
def test_trix_ext(self):
self.data.ta.trix(append=True)
diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py
index 51ec9ad..7589ac3 100644
--- a/tests/test_indicator_momentum.py
+++ b/tests/test_indicator_momentum.py
@@ -362,11 +362,10 @@ class TestMomentum(TestCase):
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, "STOCHRSI_14_14_3_3")
- def test_td(self):
- # TD Sequential
- result = pandas_ta.td(self.close)
+ def test_td_seq(self):
+ result = pandas_ta.td_seq(self.close)
self.assertIsInstance(result, DataFrame)
- self.assertEqual(result.name, "TD")
+ self.assertEqual(result.name, "TD_SEQ")
def test_trix(self):
result = pandas_ta.trix(self.close)
diff --git a/tests/test_strategy.py b/tests/test_strategy.py
index 7ec1939..0efb262 100644
--- a/tests/test_strategy.py
+++ b/tests/test_strategy.py
@@ -165,6 +165,25 @@ class TestStrategyMethods(TestCase):
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
+ # @skip
+ def test_custom_a(self):
+ self.category = "Custom E"
+
+ amat_logret_ta = [
+ {"kind": "amat", "fast": 20, "slow": 50 }, # 2
+ {"kind": "log_return", "cumulative": True}, # 1
+ {"kind": "ema", "close": "CUMLOGRET_1", "length": 5} # 1
+ ]
+
+ custom = pandas_ta.Strategy(
+ "AMAT Log Returns", # name
+ amat_logret_ta, # ta
+ "AMAT Log Returns", # description
+ )
+ self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed, ordered=True)
+ self.data.ta.trend_return(trend=self.data["AMATe_LR_2"], cumulative=True, append=True)
+ self.assertEqual(len(self.data.columns), 13)
+
# @skip
def test_momentum_category(self):
self.category = "Momentum"