diff --git a/README.md b/README.md index 0bfd4544..e51477a3 100644 --- a/README.md +++ b/README.md @@ -986,6 +986,7 @@ of the last bars defined by the length parameter. See ```help(ta.tos_stdevall)`` * _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**): Bug fix and adjustment to match TradingView's ```sar```. New argument ```af0``` to initialize the Acceleration Factor. See ```help(ta.psar)```. * _Percentage Price Oscillator_ (**ppo**): Included new argument ```mamode``` as an option. Default is **sma** to match TA Lib. See ```help(ta.ppo)```. +* _True Strength Index_ (**tsi**): Added ```signal``` argument. See ```help(ta.tsi)```. * _Volume Profile_ (**vp**): Calculation improvements. See [Pull Request #320](https://github.com/twopirllc/pandas-ta/pull/320) See ```help(ta.vp)```. * _Volume Weighted Moving Average_ (**vwma**): Fixed bug in DataFrame Extension call. See ```help(ta.vwma)```. * _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. See ```help(ta.vwap)```. diff --git a/pandas_ta/momentum/smi.py b/pandas_ta/momentum/smi.py index a652ccad..ba8c5257 100644 --- a/pandas_ta/momentum/smi.py +++ b/pandas_ta/momentum/smi.py @@ -20,8 +20,9 @@ def smi(close, fast=None, slow=None, signal=None, scalar=None, offset=None, **kw if close is None: return # Calculate Result - smi = tsi(close, fast=fast, slow=slow, scalar=scalar) - signalma = ema(smi, signal) + tsi_df = tsi(close, fast=fast, slow=slow, signal=signal, scalar=scalar) + smi = tsi_df.iloc[:, 0] + signalma = tsi_df.iloc[:, 1] osc = smi - signalma # Offset diff --git a/pandas_ta/momentum/tsi.py b/pandas_ta/momentum/tsi.py index 587e3ebd..9b628397 100644 --- a/pandas_ta/momentum/tsi.py +++ b/pandas_ta/momentum/tsi.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- +from pandas import DataFrame from pandas_ta.overlap import ema from pandas_ta.utils import get_drift, get_offset, verify_series -def tsi(close, fast=None, slow=None, scalar=None, drift=None, offset=None, **kwargs): +def tsi(close, fast=None, slow=None, signal=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: True Strength Index (TSI)""" # Validate Arguments fast = int(fast) if fast and fast > 0 else 13 slow = int(slow) if slow and slow > 0 else 25 + signal = int(signal) if signal and signal > 0 else 13 # if slow < fast: # fast, slow = slow, fast scalar = float(scalar) if scalar else 100 @@ -29,21 +31,32 @@ def tsi(close, fast=None, slow=None, scalar=None, drift=None, offset=None, **kwa tsi = scalar * fast_slow_ema / abs_fast_slow_ema + tsi_signal = ema(close=tsi, length=signal) + # Offset if offset != 0: tsi = tsi.shift(offset) + tsi_signal = tsi_signal.shift(offset) # Handle fills if "fillna" in kwargs: tsi.fillna(kwargs["fillna"], inplace=True) + tsi_signal.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: tsi.fillna(method=kwargs["fill_method"], inplace=True) + tsi_signal.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it - tsi.name = f"TSI_{fast}_{slow}" - tsi.category = "momentum" + tsi.name = f"TSI_{fast}_{slow}_{signal}" + tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}" + tsi.category = tsi_signal.category = "momentum" + + # Prepare DataFrame to return + df = DataFrame({tsi.name: tsi, tsi_signal.name: tsi_signal}) + df.name = f"TSI_{fast}_{slow}_{signal}" + df.category = "momentum" - return tsi + return df tsi.__doc__ = \ @@ -58,7 +71,7 @@ def tsi(close, fast=None, slow=None, scalar=None, drift=None, offset=None, **kwa Calculation: Default Inputs: - fast=13, slow=25, scalar=100, drift=1 + fast=13, slow=25, signal=13, scalar=100, drift=1 EMA = Exponential Moving Average diff = close.diff(drift) @@ -69,11 +82,13 @@ def tsi(close, fast=None, slow=None, scalar=None, drift=None, offset=None, **kwa abema = abs_diff_fast_slow_ema = EMA(abs_diff_slow_ema, fast) TSI = scalar * fast_slow_ema / abema + Signal = EMA(TSI, signal) Args: close (pd.Series): Series of 'close's fast (int): The short period. Default: 13 slow (int): The long period. Default: 25 + signal (int): The signal period. Default: 13 scalar (float): How much to magnify. Default: 100 drift (int): The difference period. Default: 1 offset (int): How many periods to offset the result. Default: 0 @@ -83,5 +98,5 @@ def tsi(close, fast=None, slow=None, scalar=None, drift=None, offset=None, **kwa fill_method (value, optional): Type of fill method Returns: - pd.Series: New feature generated. + pd.DataFrame: tsi, signal. """ diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 84e4b689..348062bd 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -453,8 +453,8 @@ def test_trix(self): def test_tsi(self): result = pandas_ta.tsi(self.close) - self.assertIsInstance(result, Series) - self.assertEqual(result.name, "TSI_13_25") + self.assertIsInstance(result, DataFrame) + self.assertEqual(result.name, "TSI_13_25_13") def test_uo(self): result = pandas_ta.uo(self.high, self.low, self.close)