From d02edeac5702caaee630f9ea07f293b9af3af4a8 Mon Sep 17 00:00:00 2001 From: Yakir Date: Wed, 29 May 2024 18:43:17 +0300 Subject: [PATCH 01/17] fix(Strategy): cancel exit orders --- jesse/strategies/Strategy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jesse/strategies/Strategy.py b/jesse/strategies/Strategy.py index a78ef9785..ba6165b92 100644 --- a/jesse/strategies/Strategy.py +++ b/jesse/strategies/Strategy.py @@ -598,6 +598,7 @@ def _detect_and_handle_entry_and_exit_modifications(self) -> None: self.position.is_open and (self.stop_loss is not None and self.take_profit is not None) and np.array_equal(self.stop_loss, self.take_profit) + and self.stop_loss != [] ): raise exceptions.InvalidStrategy( 'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.') From e2fc54bc310d84c351e5eeff07550d3ec3c70e8e Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Mon, 10 Jun 2024 17:27:39 +0330 Subject: [PATCH 02/17] handle single-dimensional candles in stddev function --- jesse/indicators/stddev.py | 7 +++++-- tests/test_indicators.py | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) 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/tests/test_indicators.py b/tests/test_indicators.py index a36df2b81..52d8998eb 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) From 42aa97b7287dfe6871cb710d51bcfd652a771f85 Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Tue, 11 Jun 2024 12:31:27 +0330 Subject: [PATCH 03/17] feat: handle single-dimensional candles in MACD function --- jesse/indicators/macd.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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) From 10f38d6c22873c2eebd4afdfab2986dde7e2917b Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Tue, 11 Jun 2024 13:51:02 +0330 Subject: [PATCH 04/17] add waddah attr explosion indicator --- jesse/indicators/__init__.py | 1 + jesse/indicators/waddah_attr_explosion.py | 57 +++++++++++++++++++++++ tests/test_indicators.py | 9 ++++ 3 files changed, 67 insertions(+) create mode 100644 jesse/indicators/waddah_attr_explosion.py diff --git a/jesse/indicators/__init__.py b/jesse/indicators/__init__.py index 858ae0de0..4cc5bcd0e 100644 --- a/jesse/indicators/__init__.py +++ b/jesse/indicators/__init__.py @@ -175,3 +175,4 @@ from .wt import wt from .zlema import zlema from .zscore import zscore +from .waddah_attr_explosion import waddah_attr_explosion diff --git a/jesse/indicators/waddah_attr_explosion.py b/jesse/indicators/waddah_attr_explosion.py new file mode 100644 index 000000000..76f0aef83 --- /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://docs.google.com/document/d/15AGCufJZ8CIUvwFJ9W-IKns88gkWOKBCvByMEvm5MLo/edit + + 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/tests/test_indicators.py b/tests/test_indicators.py index 52d8998eb..8c2457c91 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -2334,3 +2334,12 @@ 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 From a13521160ee322ebb498dcb19e68e680353f2d33 Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Tue, 11 Jun 2024 15:55:41 +0330 Subject: [PATCH 05/17] feat: add stiffness indicator --- jesse/indicators/__init__.py | 1 + jesse/indicators/stiffness.py | 57 +++++++++++++++++++++++++++++++++++ tests/test_indicators.py | 9 ++++++ 3 files changed, 67 insertions(+) create mode 100644 jesse/indicators/stiffness.py diff --git a/jesse/indicators/__init__.py b/jesse/indicators/__init__.py index 4cc5bcd0e..8d94a5f10 100644 --- a/jesse/indicators/__init__.py +++ b/jesse/indicators/__init__.py @@ -176,3 +176,4 @@ 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/stiffness.py b/jesse/indicators/stiffness.py new file mode 100644 index 000000000..cb00647da --- /dev/null +++ b/jesse/indicators/stiffness.py @@ -0,0 +1,57 @@ +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', 'use_stiffness']) + + +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(explosion_line, trend_power, trend_direction) + """ + if len(candles.shape) == 1: + source = candles + else: + candles = slice_candles(candles, False) + source = get_candle_source(candles, source_type=source_type) + + boundStiffness = sma(source, ma_length, sequential=True) - 0.2 * \ + stddev(source, ma_length, sequential=True) + sumAboveStiffness = count_price_exceed_series(source, boundStiffness, stiff_length) + stiffness = ema(np.array(sumAboveStiffness) * 100 / stiff_length, period=stiff_smooth) + use_stiffness = 1 if stiffness > threshold else -1 + + return StiffnessTuple(stiffness, threshold, use_stiffness) + + +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/tests/test_indicators.py b/tests/test_indicators.py index 8c2457c91..143954c2a 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -2343,3 +2343,12 @@ def test_waddah_attr_explosion(): 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 + assert round(single.use_stiffness) == 1 From 64252257159fb7fd61980c7cfd96c825f20ba8ee Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Tue, 11 Jun 2024 16:19:23 +0330 Subject: [PATCH 06/17] refactor: improve readability and encapsulation of stiffness indicator --- jesse/indicators/stiffness.py | 11 +++++++---- jesse/indicators/waddah_attr_explosion.py | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/jesse/indicators/stiffness.py b/jesse/indicators/stiffness.py index cb00647da..f74733293 100644 --- a/jesse/indicators/stiffness.py +++ b/jesse/indicators/stiffness.py @@ -33,17 +33,18 @@ def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, candles = slice_candles(candles, False) source = get_candle_source(candles, source_type=source_type) - boundStiffness = sma(source, ma_length, sequential=True) - 0.2 * \ + bound_stiffness = sma(source, ma_length, sequential=True) - 0.2 * \ stddev(source, ma_length, sequential=True) - sumAboveStiffness = count_price_exceed_series(source, boundStiffness, stiff_length) - stiffness = ema(np.array(sumAboveStiffness) * 100 / stiff_length, period=stiff_smooth) + 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) use_stiffness = 1 if stiffness > threshold else -1 return StiffnessTuple(stiffness, threshold, use_stiffness) -def count_price_exceed_series(close_prices, art_series, length): +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) @@ -53,5 +54,7 @@ def count_price_exceed_series(close_prices, art_series, length): 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 index 76f0aef83..8b4a42ca8 100644 --- a/jesse/indicators/waddah_attr_explosion.py +++ b/jesse/indicators/waddah_attr_explosion.py @@ -15,7 +15,7 @@ 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://docs.google.com/document/d/15AGCufJZ8CIUvwFJ9W-IKns88gkWOKBCvByMEvm5MLo/edit + credits: https://www.tradingview.com/v/iu3kKWDI/ WADDAH_ATTR_EXPLOSION - Waddah ATTR Explosion @@ -40,18 +40,18 @@ def waddah_attr_explosion(candles: np.ndarray, sensitivity: int = 150, fast_leng 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) + 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): +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): +def _calc_bb_lower(source, length, mult): basis = sma(source, length) dev = mult * stddev(source, length) return basis - dev From 6895a98bb77533408b93f61a1ef0cfb31cf40989 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Tue, 11 Jun 2024 19:52:22 +0330 Subject: [PATCH 07/17] Update stiffness.py --- jesse/indicators/stiffness.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jesse/indicators/stiffness.py b/jesse/indicators/stiffness.py index f74733293..b8e21f6e6 100644 --- a/jesse/indicators/stiffness.py +++ b/jesse/indicators/stiffness.py @@ -8,7 +8,7 @@ from jesse.helpers import get_candle_source, slice_candles -StiffnessTuple = namedtuple('StiffnessTuple', ['stiffness', 'threshold', 'use_stiffness']) +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: @@ -25,7 +25,7 @@ def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, :param threshold: int - default: 90 :param source_type: str - default: "close" - :return: StiffnessTuple(explosion_line, trend_power, trend_direction) + :return: StiffnessTuple(stiffness, threshold) """ if len(candles.shape) == 1: source = candles @@ -37,9 +37,8 @@ def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, 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) - use_stiffness = 1 if stiffness > threshold else -1 - return StiffnessTuple(stiffness, threshold, use_stiffness) + return StiffnessTuple(stiffness, threshold) def _count_price_exceed_series(close_prices, art_series, length): From 80956961110b5bc5f05218b51c06333ed305e970 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Tue, 11 Jun 2024 19:53:07 +0330 Subject: [PATCH 08/17] bump version --- jesse/version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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() From fc5e226b1cc429895d40eb6700c850eca6979415 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Tue, 11 Jun 2024 19:53:45 +0330 Subject: [PATCH 09/17] fix test --- tests/test_indicators.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_indicators.py b/tests/test_indicators.py index 143954c2a..c9b47045d 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -2351,4 +2351,3 @@ def test_stiffness(): assert round(single.stiffness) == 96 assert round(single.threshold) == 90 - assert round(single.use_stiffness) == 1 From 2a20eca845e557a90953fc1a040f4d944ccc6af2 Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Wed, 1 May 2024 13:25:06 +0330 Subject: [PATCH 10/17] add apex testnet import candles --- jesse/enums/__init__.py | 1 + jesse/info.py | 24 +++++- .../drivers/Apex/ApexProMain.py | 76 +++++++++++++++++++ .../drivers/Apex/ApexProPerpetualTestnet.py | 10 +++ .../drivers/Apex/__init__.py | 0 .../drivers/Apex/apex_pro_utils.py | 65 ++++++++++++++++ .../import_candles_mode/drivers/__init__.py | 2 + 7 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py create mode 100644 jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetualTestnet.py create mode 100644 jesse/modes/import_candles_mode/drivers/Apex/__init__.py create mode 100644 jesse/modes/import_candles_mode/drivers/Apex/apex_pro_utils.py diff --git a/jesse/enums/__init__.py b/jesse/enums/__init__.py index de32becf9..0fa262f7b 100644 --- a/jesse/enums/__init__.py +++ b/jesse/enums/__init__.py @@ -76,6 +76,7 @@ class exchanges: BITGET_USDT_PERPETUAL_TESTNET = 'Bitget USDT Perpetual Testnet' DYDX_PERPETUAL = "Dydx Perpetual" DYDX_PERPETUAL_TESTNET = "Dydx Perpetual Testnet" + APEX_PRO_PERPETUAL_TESTNET = 'Apex Pro Perpetual Testnet' class migration_actions: diff --git a/jesse/info.py b/jesse/info.py index fc3057f49..173110da1 100644 --- a/jesse/info.py +++ b/jesse/info.py @@ -1,9 +1,9 @@ from jesse.enums import exchanges as exchanges_enums, timeframes -JESSE_API_URL = 'https://api1.jesse.trade/api' -# JESSE_API_URL = 'http://localhost:8040/api' -JESSE_WEBSITE_URL = 'https://jesse.trade' -# JESSE_WEBSITE_URL = 'http://localhost:8040' +# JESSE_API_URL = 'https://api1.jesse.trade/api' +JESSE_API_URL = 'http://localhost:8040/api' +# JESSE_WEBSITE_URL = 'https://jesse.trade' +JESSE_WEBSITE_URL = 'http://localhost:8040' BYBIT_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1] FTX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1] @@ -11,6 +11,7 @@ COINBASE_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.HOUR_1, timeframes.HOUR_6, timeframes.DAY_1] BITGET_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.HOUR_12, timeframes.DAY_1] DYDX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1] +APEX_PRO_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1] exchange_info = { # BYBIT_USDT_PERPETUAL @@ -297,7 +298,22 @@ 'live_trading': True, }, 'required_live_plan': 'premium' + }, + + exchanges_enums.APEX_PRO_PERPETUAL_TESTNET: { + 'name': exchanges_enums.APEX_PRO_PERPETUAL_TESTNET, + 'url': 'https://testnet.pro.apex.exchange/trade/BTCUSD', + 'fee': 0.0005, + 'type': 'futures', + 'supported_leverage_modes': ['cross'], + 'supported_timeframes': APEX_PRO_TIMEFRAMES, + 'modes': { + 'backtesting': False, + 'live_trading': True, + }, + 'required_live_plan': 'premium' } + } # list of supported exchanges for backtesting diff --git a/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py b/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py new file mode 100644 index 000000000..1958028db --- /dev/null +++ b/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py @@ -0,0 +1,76 @@ +import requests +import jesse.helpers as jh +from jesse.modes.import_candles_mode.drivers.interface import CandleExchange +from typing import Union +from jesse import exceptions +from .apex_pro_utils import timeframe_to_interval + + +class ApexProMain(CandleExchange): + def __init__(self, name: str, rest_endpoint: str) -> None: + from jesse.modes.import_candles_mode.drivers.Binance.BinanceSpot import BinanceSpot + + super().__init__(name=name, count=200, rate_limit_per_second=10, backup_exchange_class=BinanceSpot) + self.name = name + self.endpoint = rest_endpoint + + def get_starting_time(self, symbol: str) -> int: + dashless_symbol = jh.dashless_symbol(symbol) + payload = { + 'symbol': dashless_symbol, + 'interval': 'W', + 'limit': 200, + 'start': 1514811660 + } + + response = requests.get(self.endpoint + '/v2/klines', params=payload) + self.validate_response(response) + + if 'data' not in response.json(): + raise exceptions.ExchangeInMaintenance(response.json()['msg']) + elif response.json()['data'] == {}: + raise exceptions.InvalidSymbol('Exchange does not support the entered symbol. Please enter a valid symbol.') + + data = response.json()['data'][dashless_symbol] + # Reverse the data list + data = data[::-1] + + return int(data[1]['t']) + + def fetch(self, symbol: str, start_timestamp: int, timeframe: str = '1m') -> Union[list, None]: + dashless_symbol = jh.dashless_symbol(symbol) + interval = timeframe_to_interval(timeframe) + + payload = { + 'symbol': dashless_symbol, + 'interval': interval, + 'start': int(start_timestamp / 1000), + 'limit': self.count + } + + response = requests.get(self.endpoint + '/v2/klines', params=payload) + # check data exist in response.json + + if 'data' not in response.json(): + raise exceptions.ExchangeInMaintenance(response.json()['msg']) + elif response.json()['data'] == {}: + raise exceptions.InvalidSymbol('Exchange does not support the entered symbol. Please enter a valid symbol.') + + data = response.json()['data'][dashless_symbol] + # Reverse the data list + data = data[::-1] + + return [ + { + 'id': jh.generate_unique_id(), + 'exchange': self.name, + 'symbol': symbol, + 'timeframe': timeframe, + 'timestamp': int(d['t']), + 'open': float(d['o']), + 'close': float(d['c']), + 'high': float(d['h']), + 'low': float(d['l']), + 'volume': float(d['v']) + } for d in data + ] \ No newline at end of file diff --git a/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetualTestnet.py b/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetualTestnet.py new file mode 100644 index 000000000..d25c3bae7 --- /dev/null +++ b/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetualTestnet.py @@ -0,0 +1,10 @@ +from .ApexProMain import ApexProMain +from jesse.enums import exchanges + + +class ApexProPerpetualTestnet(ApexProMain): + def __init__(self) -> None: + super().__init__( + name=exchanges.APEX_PRO_PERPETUAL_TESTNET, + rest_endpoint='https://testnet.pro.apex.exchange/api' + ) diff --git a/jesse/modes/import_candles_mode/drivers/Apex/__init__.py b/jesse/modes/import_candles_mode/drivers/Apex/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/jesse/modes/import_candles_mode/drivers/Apex/apex_pro_utils.py b/jesse/modes/import_candles_mode/drivers/Apex/apex_pro_utils.py new file mode 100644 index 000000000..4d7e02425 --- /dev/null +++ b/jesse/modes/import_candles_mode/drivers/Apex/apex_pro_utils.py @@ -0,0 +1,65 @@ +from jesse.enums import timeframes + + +def timeframe_to_interval(timeframe: str) -> str: + """ + Convert a timeframe string to an interval in seconds. + """ + if timeframe == timeframes.MINUTE_1: + return '1' + elif timeframe == timeframes.MINUTE_3: + return '3' + elif timeframe == timeframes.MINUTE_5: + return '5' + elif timeframe == timeframes.MINUTE_15: + return '15' + elif timeframe == timeframes.MINUTE_30: + return '30' + elif timeframe == timeframes.HOUR_1: + return '60' + elif timeframe == timeframes.HOUR_2: + return '120' + elif timeframe == timeframes.HOUR_4: + return '240' + elif timeframe == timeframes.HOUR_6: + return '360' + elif timeframe == timeframes.HOUR_12: + return '720' + elif timeframe == timeframes.DAY_1: + return 'D' + elif timeframe == timeframes.WEEK_1: + return 'W' + else: + raise ValueError('Invalid timeframe: {}'.format(timeframe)) + + +def interval_to_timeframe(interval: str) -> str: + """ + Convert an interval in seconds to a timeframe string. + """ + if interval == '1': + return timeframes.MINUTE_1 + elif interval == '3': + return timeframes.MINUTE_3 + elif interval == '5': + return timeframes.MINUTE_5 + elif interval == '15': + return timeframes.MINUTE_15 + elif interval == '30': + return timeframes.MINUTE_30 + elif interval == '60': + return timeframes.HOUR_1 + elif interval == '120': + return timeframes.HOUR_2 + elif interval == '240': + return timeframes.HOUR_4 + elif interval == '360': + return timeframes.HOUR_6 + elif interval == '720': + return timeframes.HOUR_12 + elif interval == 'D': + return timeframes.DAY_1 + elif interval == 'W': + return timeframes.WEEK_1 + else: + raise ValueError('Invalid interval: {}'.format(interval)) diff --git a/jesse/modes/import_candles_mode/drivers/__init__.py b/jesse/modes/import_candles_mode/drivers/__init__.py index 1ca396b06..6891fb5bf 100644 --- a/jesse/modes/import_candles_mode/drivers/__init__.py +++ b/jesse/modes/import_candles_mode/drivers/__init__.py @@ -19,6 +19,7 @@ from jesse.modes.import_candles_mode.drivers.DyDx.DydxPerpetualTestnet import DydxPerpetualTestnet from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpotTestnet import BybitSpotTestnet from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpot import BybitSpot +from jesse.modes.import_candles_mode.drivers.Apex.ApexProPerpetualTestnet import ApexProPerpetualTestnet drivers = { @@ -34,6 +35,7 @@ exchanges.FTX_PERPETUAL_FUTURES: FTXPerpetualFutures, exchanges.BITGET_USDT_PERPETUAL: BitgetUSDTPerpetual, exchanges.BITGET_USDT_PERPETUAL_TESTNET: BitgetUSDTPerpetualTestnet, + exchanges.APEX_PRO_PERPETUAL_TESTNET: ApexProPerpetualTestnet, # Spot exchanges.FTX_SPOT: FTXSpot, From d5df5b5647a968d49cb85f4713ed51f74c16e0c6 Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Wed, 1 May 2024 13:32:28 +0330 Subject: [PATCH 11/17] add apex mainnet import candles --- jesse/enums/__init__.py | 2 +- jesse/info.py | 14 ++++++++++++++ .../drivers/Apex/ApexProPerpetual.py | 10 ++++++++++ .../modes/import_candles_mode/drivers/__init__.py | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetual.py diff --git a/jesse/enums/__init__.py b/jesse/enums/__init__.py index 0fa262f7b..481a5370a 100644 --- a/jesse/enums/__init__.py +++ b/jesse/enums/__init__.py @@ -77,7 +77,7 @@ class exchanges: DYDX_PERPETUAL = "Dydx Perpetual" DYDX_PERPETUAL_TESTNET = "Dydx Perpetual Testnet" APEX_PRO_PERPETUAL_TESTNET = 'Apex Pro Perpetual Testnet' - + APEX_PRO_PERPETUAL = 'Apex Pro Perpetual' class migration_actions: ADD = 'add' diff --git a/jesse/info.py b/jesse/info.py index 173110da1..8f51e39ee 100644 --- a/jesse/info.py +++ b/jesse/info.py @@ -312,6 +312,20 @@ 'live_trading': True, }, 'required_live_plan': 'premium' + }, + + exchanges_enums.APEX_PRO_PERPETUAL: { + 'name': exchanges_enums.APEX_PRO_PERPETUAL, + 'url': 'https://pro.apex.exchange/trade/BTCUSD', + 'fee': 0.0005, + 'type': 'futures', + 'supported_leverage_modes': ['cross'], + 'supported_timeframes': APEX_PRO_TIMEFRAMES, + 'modes': { + 'backtesting': False, + 'live_trading': True, + }, + 'required_live_plan': 'premium' } } diff --git a/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetual.py b/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetual.py new file mode 100644 index 000000000..01d2ea244 --- /dev/null +++ b/jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetual.py @@ -0,0 +1,10 @@ +from .ApexProMain import ApexProMain +from jesse.enums import exchanges + + +class ApexProPerpetual(ApexProMain): + def __init__(self) -> None: + super().__init__( + name=exchanges.APEX_PRO_PERPETUAL, + rest_endpoint='https://pro.apex.exchange/api' + ) diff --git a/jesse/modes/import_candles_mode/drivers/__init__.py b/jesse/modes/import_candles_mode/drivers/__init__.py index 6891fb5bf..37a0b5107 100644 --- a/jesse/modes/import_candles_mode/drivers/__init__.py +++ b/jesse/modes/import_candles_mode/drivers/__init__.py @@ -20,6 +20,7 @@ from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpotTestnet import BybitSpotTestnet from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpot import BybitSpot from jesse.modes.import_candles_mode.drivers.Apex.ApexProPerpetualTestnet import ApexProPerpetualTestnet +from jesse.modes.import_candles_mode.drivers.Apex.ApexProPerpetual import ApexProPerpetual drivers = { @@ -36,6 +37,7 @@ exchanges.BITGET_USDT_PERPETUAL: BitgetUSDTPerpetual, exchanges.BITGET_USDT_PERPETUAL_TESTNET: BitgetUSDTPerpetualTestnet, exchanges.APEX_PRO_PERPETUAL_TESTNET: ApexProPerpetualTestnet, + exchanges.APEX_PRO_PERPETUAL: ApexProPerpetual, # Spot exchanges.FTX_SPOT: FTXSpot, From e5ecb30b553420614b1944b10fda18a1bb7dc7a9 Mon Sep 17 00:00:00 2001 From: morteza-koohgard Date: Tue, 7 May 2024 13:04:31 +0330 Subject: [PATCH 12/17] Refactor ApexProMain.py to remove unnecessary code --- jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py b/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py index 1958028db..debb874dd 100644 --- a/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py +++ b/jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py @@ -57,8 +57,6 @@ def fetch(self, symbol: str, start_timestamp: int, timeframe: str = '1m') -> Uni raise exceptions.InvalidSymbol('Exchange does not support the entered symbol. Please enter a valid symbol.') data = response.json()['data'][dashless_symbol] - # Reverse the data list - data = data[::-1] return [ { From 111ee733a64c76ed1fd7937887fcca1b1131a197 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Fri, 7 Jun 2024 19:03:57 +0330 Subject: [PATCH 13/17] feat: improve waiting time for pending market exit orders The code changes in `Strategy.py` modify the waiting time for pending market exit orders. The previous waiting time was set to 10 seconds, but it has been increased to 12 seconds in order to allow more time for the exchange to respond as expected for order execution. This change aims to improve the reliability of the order execution process. Note: The recent user commits and repository commits are not relevant for generating the commit message in this case. --- jesse/strategies/Strategy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jesse/strategies/Strategy.py b/jesse/strategies/Strategy.py index ba6165b92..516325aa5 100644 --- a/jesse/strategies/Strategy.py +++ b/jesse/strategies/Strategy.py @@ -660,11 +660,12 @@ def _check(self) -> None: if jh.is_debugging(): logger.info(f'Waiting {waiting_seconds} second for pending market exit orders to be handled...') waiting_counter += 1 - sleep(1) - if waiting_counter > 10: + if waiting_counter > 12: raise exceptions.ExchangeNotResponding( 'The exchange did not respond as expected for order execution' ) + else: + sleep(1) self._simulate_market_order_execution() From 1e868c2ebed16b5a4ddac67cab2b750603cbc511 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Fri, 7 Jun 2024 19:04:08 +0330 Subject: [PATCH 14/17] improve waiting time for pending market exit orders --- jesse/info.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jesse/info.py b/jesse/info.py index 8f51e39ee..ed3777d44 100644 --- a/jesse/info.py +++ b/jesse/info.py @@ -1,9 +1,9 @@ from jesse.enums import exchanges as exchanges_enums, timeframes -# JESSE_API_URL = 'https://api1.jesse.trade/api' -JESSE_API_URL = 'http://localhost:8040/api' -# JESSE_WEBSITE_URL = 'https://jesse.trade' -JESSE_WEBSITE_URL = 'http://localhost:8040' +JESSE_API_URL = 'https://api1.jesse.trade/api' +# JESSE_API_URL = 'http://localhost:8040/api' +JESSE_WEBSITE_URL = 'https://jesse.trade' +# JESSE_WEBSITE_URL = 'http://localhost:8040' BYBIT_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1] FTX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1] From 4640acdc014f5fb72955ca0e186602c8d50c56f2 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Wed, 12 Jun 2024 23:39:34 +0330 Subject: [PATCH 15/17] refactor: rename waddah_attr_explosion to waddah_attar_explosion --- jesse/indicators/__init__.py | 2 +- jesse/indicators/stiffness.py | 8 ++++---- jesse/indicators/waddah_attr_explosion.py | 18 +++++++----------- tests/test_indicators.py | 2 +- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/jesse/indicators/__init__.py b/jesse/indicators/__init__.py index 8d94a5f10..6b211802a 100644 --- a/jesse/indicators/__init__.py +++ b/jesse/indicators/__init__.py @@ -175,5 +175,5 @@ from .wt import wt from .zlema import zlema from .zscore import zscore -from .waddah_attr_explosion import waddah_attr_explosion +from .waddah_attr_explosion import waddah_attar_explosion from .stiffness import stiffness diff --git a/jesse/indicators/stiffness.py b/jesse/indicators/stiffness.py index b8e21f6e6..ff0522920 100644 --- a/jesse/indicators/stiffness.py +++ b/jesse/indicators/stiffness.py @@ -8,10 +8,10 @@ from jesse.helpers import get_candle_source, slice_candles -StiffnessTuple = namedtuple('StiffnessTuple', ['stiffness', 'threshold']) +Stiffness = namedtuple('Stiffness', ['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: +def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, stiff_smooth: int = 3, threshold: int = 90, source_type: str = "close") -> Stiffness: """ @author daviddtech credits: https://www.tradingview.com/script/MOw6mUQl-Stiffness-Indicator-DaviddTech @@ -25,7 +25,7 @@ def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, :param threshold: int - default: 90 :param source_type: str - default: "close" - :return: StiffnessTuple(stiffness, threshold) + :return: Stiffness(stiffness, threshold) """ if len(candles.shape) == 1: source = candles @@ -38,7 +38,7 @@ def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, 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) + return Stiffness(stiffness, threshold) def _count_price_exceed_series(close_prices, art_series, length): diff --git a/jesse/indicators/waddah_attr_explosion.py b/jesse/indicators/waddah_attr_explosion.py index 8b4a42ca8..b9a8bdedc 100644 --- a/jesse/indicators/waddah_attr_explosion.py +++ b/jesse/indicators/waddah_attr_explosion.py @@ -1,23 +1,21 @@ 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']) +WaddahAttarExplosionTuple = namedtuple( + 'WaddahAttarExplosionTuple', ['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: +def waddah_attar_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") -> WaddahAttarExplosionTuple: """ @author LazyBear credits: https://www.tradingview.com/v/iu3kKWDI/ - WADDAH_ATTR_EXPLOSION - Waddah ATTR Explosion + WADDAH_ATTAR_EXPLOSION - Waddah Attar Explosion :param candles: np.ndarray :param sensitivity: int - default: 150 @@ -26,11 +24,9 @@ def waddah_attr_explosion(candles: np.ndarray, sensitivity: int = 150, fast_leng :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) + :return: WaddahAttarExplosionTuple(explosion_line, trend_power, trend_direction) """ - jh.dump(candles) if len(candles.shape) == 1: source = candles else: @@ -42,7 +38,7 @@ def waddah_attr_explosion(candles: np.ndarray, sensitivity: int = 150, fast_leng 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) + return WaddahAttarExplosionTuple(e1, t1, trend) def _calc_bb_upper(source, length, mult): diff --git a/tests/test_indicators.py b/tests/test_indicators.py index c9b47045d..050a1ce3c 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -2338,7 +2338,7 @@ def test_zscore(): def test_waddah_attr_explosion(): candles = np.array(test_candles_19) - single = ta.waddah_attr_explosion(candles) + single = ta.waddah_attar_explosion(candles) assert round(single[0]) == 135 assert round(single[1]) == -827 From a79124d973966d1ae95e93697fde3c603dbf3e1d Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Wed, 12 Jun 2024 23:40:25 +0330 Subject: [PATCH 16/17] Bump version to 0.48.3 --- jesse/version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jesse/version.py b/jesse/version.py index e52e3b172..8c70e5767 100644 --- a/jesse/version.py +++ b/jesse/version.py @@ -1 +1 @@ -__version__ = '0.48.2' +__version__ = '0.48.3' diff --git a/setup.py b/setup.py index d2e1fa682..823f17a53 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.2' +VERSION = '0.48.3' DESCRIPTION = "A trading framework for cryptocurrencies" with open("requirements.txt", "r", encoding="utf-8") as f: REQUIRED_PACKAGES = f.read().splitlines() From 191b1377ddd4431a65f8e1fb547ea3718548fa9c Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Sun, 16 Jun 2024 00:04:33 +0330 Subject: [PATCH 17/17] bump version --- jesse/version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jesse/version.py b/jesse/version.py index 8c70e5767..d70ce98da 100644 --- a/jesse/version.py +++ b/jesse/version.py @@ -1 +1 @@ -__version__ = '0.48.3' +__version__ = '0.48.4' diff --git a/setup.py b/setup.py index 823f17a53..0340fba3d 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.3' +VERSION = '0.48.4' DESCRIPTION = "A trading framework for cryptocurrencies" with open("requirements.txt", "r", encoding="utf-8") as f: REQUIRED_PACKAGES = f.read().splitlines()