diff --git a/jesse/indicators/__init__.py b/jesse/indicators/__init__.py index 858ae0de0..8d94a5f10 100644 --- a/jesse/indicators/__init__.py +++ b/jesse/indicators/__init__.py @@ -175,3 +175,5 @@ from .wt import wt from .zlema import zlema from .zscore import zscore +from .waddah_attr_explosion import waddah_attr_explosion +from .stiffness import stiffness diff --git a/jesse/indicators/macd.py b/jesse/indicators/macd.py index 5207b5f5b..0132b0ee0 100644 --- a/jesse/indicators/macd.py +++ b/jesse/indicators/macd.py @@ -23,9 +23,13 @@ def macd(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, sign :return: MACD(macd, signal, hist) """ - candles = slice_candles(candles, sequential) - source = get_candle_source(candles, source_type=source_type) + if len(candles.shape) == 1: + source = candles + else: + candles = slice_candles(candles, sequential) + source = get_candle_source(candles, source_type=source_type) + macd_val, macdsignal, macdhist = talib.MACD(source, fastperiod=fast_period, slowperiod=slow_period, signalperiod=signal_period) diff --git a/jesse/indicators/stddev.py b/jesse/indicators/stddev.py index 2b1fa0138..54116afec 100644 --- a/jesse/indicators/stddev.py +++ b/jesse/indicators/stddev.py @@ -19,9 +19,12 @@ def stddev(candles: np.ndarray, period: int = 5, nbdev: float = 1, source_type: :return: float | np.ndarray """ - candles = slice_candles(candles, sequential) + if len(candles.shape) == 1: + source = candles + else: + candles = slice_candles(candles, sequential) + source = get_candle_source(candles, source_type=source_type) - source = get_candle_source(candles, source_type=source_type) res = talib.STDDEV(source, timeperiod=period, nbdev=nbdev) return res if sequential else res[-1] diff --git a/jesse/indicators/stiffness.py b/jesse/indicators/stiffness.py new file mode 100644 index 000000000..b8e21f6e6 --- /dev/null +++ b/jesse/indicators/stiffness.py @@ -0,0 +1,59 @@ +from collections import namedtuple + +from .sma import sma +from .ema import ema +from .stddev import stddev + +import numpy as np + +from jesse.helpers import get_candle_source, slice_candles + +StiffnessTuple = namedtuple('StiffnessTuple', ['stiffness', 'threshold']) + + +def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, stiff_smooth: int = 3, threshold: int = 90, source_type: str = "close") -> StiffnessTuple: + """ + @author daviddtech + credits: https://www.tradingview.com/script/MOw6mUQl-Stiffness-Indicator-DaviddTech + + STIFNESS - Stifness + + :param candles: np.ndarray + :param ma_length: int - default: 100 + :param stiff_length: int - default: 60 + :param stiff_smooth: int - default: 3 + :param threshold: int - default: 90 + :param source_type: str - default: "close" + + :return: StiffnessTuple(stiffness, threshold) + """ + if len(candles.shape) == 1: + source = candles + else: + candles = slice_candles(candles, False) + source = get_candle_source(candles, source_type=source_type) + + bound_stiffness = sma(source, ma_length, sequential=True) - 0.2 * \ + stddev(source, ma_length, sequential=True) + sum_above_stiffness = _count_price_exceed_series(source, bound_stiffness, stiff_length) + stiffness = ema(np.array(sum_above_stiffness) * 100 / stiff_length, period=stiff_smooth) + + return StiffnessTuple(stiffness, threshold) + + +def _count_price_exceed_series(close_prices, art_series, length): + ex_counts = [] + + for i in range(len(close_prices)): + if i < length: + ex_counts.append(0) + continue + count = 0 + + for j in range(i - length + 1, i + 1): + if close_prices[j] > art_series[j]: + count += 1 + + ex_counts.append(count) + + return ex_counts diff --git a/jesse/indicators/waddah_attr_explosion.py b/jesse/indicators/waddah_attr_explosion.py new file mode 100644 index 000000000..8b4a42ca8 --- /dev/null +++ b/jesse/indicators/waddah_attr_explosion.py @@ -0,0 +1,57 @@ +from collections import namedtuple + +from .macd import macd +from .sma import sma +from .stddev import stddev + +import numpy as np +import jesse.helpers as jh +from jesse.helpers import get_candle_source, slice_candles + +WaddahATTRExplosionTuple = namedtuple('WaddahATTRExplosionTuple', [ + 'explosion_line', 'trend_power', 'trend_direction']) + + +def waddah_attr_explosion(candles: np.ndarray, sensitivity: int = 150, fast_length: int = 20, slow_length: int = 40, channel_length: int = 20, mult: float = 2.0, source_type: str = "close") -> WaddahATTRExplosionTuple: + """ + @author LazyBear + credits: https://www.tradingview.com/v/iu3kKWDI/ + + WADDAH_ATTR_EXPLOSION - Waddah ATTR Explosion + + :param candles: np.ndarray + :param sensitivity: int - default: 150 + :param fast_length: int - default: 20 + :param slow_length: int - default: 40 + :param channel_length: int - default: 20 + :param mult: float - default: 2.0 + :param source_type: str - default: "close" + :param sequential: bool - default: False + + :return: WaddahATTRExplosionTuple(explosion_line, trend_power, trend_direction) + """ + jh.dump(candles) + if len(candles.shape) == 1: + source = candles + else: + candles = slice_candles(candles, False) + source = get_candle_source(candles, source_type=source_type) + + t1 = (macd(source, fast_period=fast_length, slow_period=slow_length)[0] - + macd(source[:-1], fast_period=fast_length, slow_period=slow_length)[0])*sensitivity + trend = 1 if t1 >= 0 else -1 + e1 = _calc_bb_upper(source, channel_length, mult) - _calc_bb_lower(source, channel_length, mult) + + return WaddahATTRExplosionTuple(e1, t1, trend) + + +def _calc_bb_upper(source, length, mult): + basis = sma(source, length) + dev = mult * stddev(source, length) + return basis + dev + + +def _calc_bb_lower(source, length, mult): + basis = sma(source, length) + dev = mult * stddev(source, length) + return basis - dev diff --git a/jesse/version.py b/jesse/version.py index c053e5cfa..e52e3b172 100644 --- a/jesse/version.py +++ b/jesse/version.py @@ -1 +1 @@ -__version__ = '0.48.1' +__version__ = '0.48.2' diff --git a/setup.py b/setup.py index 15b5f362a..d2e1fa682 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages # also change in version.py -VERSION = '0.48.1' +VERSION = '0.48.2' DESCRIPTION = "A trading framework for cryptocurrencies" with open("requirements.txt", "r", encoding="utf-8") as f: REQUIRED_PACKAGES = f.read().splitlines() diff --git a/tests/test_indicators.py b/tests/test_indicators.py index a36df2b81..c9b47045d 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -6,6 +6,7 @@ matypes = 39 + def test_acosc(): candles = np.array(test_candles_19) single = ta.acosc(candles) @@ -693,10 +694,11 @@ def test_gauss(): assert len(seq) == len(candles) assert seq[-1] == single + def test_heikin_ashi_candles(): candles = np.array(test_candles_19) - open_single,close_single,high_single,low_single = ta.heikin_ashi_candles(candles) - open_seq,close_seq,high_seq,low_seq = ta.heikin_ashi_candles(candles, sequential=True) + open_single, close_single, high_single, low_single = ta.heikin_ashi_candles(candles) + open_seq, close_seq, high_seq, low_seq = ta.heikin_ashi_candles(candles, sequential=True) # SINGLES assert round(open_single, 2) == 197.68 assert round(close_single, 2) == 149.8 @@ -709,6 +711,7 @@ def test_heikin_ashi_candles(): assert high_seq[-1] == high_single assert low_seq[-1] == low_single + def test_high_pass(): candles = np.array(test_candles_19) single = ta.high_pass(candles) @@ -846,7 +849,8 @@ def test_ichimoku_cloud(): assert type(ic).__name__ == 'IchimokuCloud' - assert (current_conversion_line, current_base_line, span_a, span_b) == (8861.59, 8861.59, 8466.385, 8217.45) + assert (current_conversion_line, current_base_line, span_a, + span_b) == (8861.59, 8861.59, 8466.385, 8217.45) def test_ichimoku_cloud_seq(): @@ -1158,7 +1162,8 @@ def test_macdext(): for matype in range(matypes): if matype != 29: - single = ta.macdext(candles, fast_period=12, fast_matype=matype, slow_period=26, slow_matype=matype, signal_period=9, signal_matype=matype) + single = ta.macdext(candles, fast_period=12, fast_matype=matype, slow_period=26, + slow_matype=matype, signal_period=9, signal_matype=matype) assert type(single).__name__ == 'MACDEXT' assert type(single.macd) == np.float64 assert type(single.signal) == np.float64 @@ -1855,6 +1860,13 @@ def test_stddev(): assert len(seq) == len(candles) assert seq[-1] == single + closes = candles[:, 2] + single = ta.stddev(closes) + seq = ta.stddev(closes, sequential=True) + + assert round(single, 0) == 37 + assert seq[-1] == single + def test_stoch(): candles = np.array(test_candles_3) @@ -2322,3 +2334,20 @@ def test_zscore(): assert round(single, 1) == -3.2 assert len(seq) == len(candles) assert seq[-1] == single + + +def test_waddah_attr_explosion(): + candles = np.array(test_candles_19) + single = ta.waddah_attr_explosion(candles) + + assert round(single[0]) == 135 + assert round(single[1]) == -827 + assert round(single[2]) == -1 + + +def test_stiffness(): + candles = np.array(test_candles_19) + single = ta.stiffness(candles) + + assert round(single.stiffness) == 96 + assert round(single.threshold) == 90