From e5368f5a149bf0e330dfac53fdd0d0e44679b469 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 16 Sep 2022 13:24:20 +0000 Subject: [PATCH 01/12] backtesting confirm_trade_entry should pass correct amount, not stake-amount closes #7423 --- freqtrade/optimize/backtesting.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 97418b72c..105851e60 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -812,14 +812,6 @@ class Backtesting: return trade time_in_force = self.strategy.order_time_in_force['entry'] - if not pos_adjust: - # Confirm trade entry: - if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( - pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate, - time_in_force=time_in_force, current_time=current_time, - entry_tag=entry_tag, side=direction): - return trade - if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): self.order_id_counter += 1 base_currency = self.exchange.get_pair_base_currency(pair) @@ -834,6 +826,15 @@ class Backtesting: # Backcalculate actual stake amount. stake_amount = amount * propose_rate / leverage + if not pos_adjust: + # Confirm trade entry: + if not strategy_safe_wrapper( + self.strategy.confirm_trade_entry, default_retval=True)( + pair=pair, order_type=order_type, amount=amount, rate=propose_rate, + time_in_force=time_in_force, current_time=current_time, + entry_tag=entry_tag, side=direction): + return trade + is_short = (direction == 'short') # Necessary for Margin trading. Disabled until support is enabled. # interest_rate = self.exchange.get_interest_rate() From 0aada271cafa282494b29318ca32f7e7b4a7309a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:11:04 +0200 Subject: [PATCH 02/12] Move informative_pairs for freqAI to backend --- freqtrade/strategy/interface.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 9401ebebe..93988ac48 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -613,6 +613,22 @@ class IStrategy(ABC, HyperStrategyMixin): # END - Intended to be overridden by strategy ### + def __informative_pairs_freqai(self) -> ListPairsWithTimeframes: + """ + Create informative-pairs needed for FreqAI + """ + if self.config.get('freqai', {}).get('enabled', False): + whitelist_pairs = self.dp.current_whitelist() + candle_type = self.config.get('candle_type_def', CandleType.SPOT) + corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] + informative_pairs = [] + for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: + for pair in set(whitelist_pairs + corr_pairs): + informative_pairs.append((pair, tf, candle_type)) + return informative_pairs + + return [] + def gather_informative_pairs(self) -> ListPairsWithTimeframes: """ Internal method which gathers all informative pairs (user or automatically defined). @@ -637,6 +653,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: for pair in self.dp.current_whitelist(): informative_pairs.append((pair, inf_data.timeframe, candle_type)) + informative_pairs.extend(self.__informative_pairs_freqai()) return list(set(informative_pairs)) def get_strategy_name(self) -> str: From d62cef01beba253f4410f3ecf1b8e3a5fb22ea3f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:18:08 +0200 Subject: [PATCH 03/12] Add test for __informative_pairs_freqai --- tests/freqai/test_freqai_interface.py | 25 ++++++++++++++++++++++ tests/strategy/strats/freqai_test_strat.py | 13 ----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index a66594d7f..6aa4f40c3 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -8,6 +8,7 @@ import pytest from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.plugins.pairlistmanager import PairListManager from tests.conftest import get_patched_exchange, log_has_re from tests.freqai.conftest import get_patched_freqai_strategy @@ -315,3 +316,27 @@ def test_principal_component_analysis(mocker, freqai_conf): assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_pca_object.pkl") shutil.rmtree(Path(freqai.dk.full_path)) + + +@pytest.mark.parametrize('timeframes,corr_pairs', [ + (['5m'], ['ADA/BTC', 'DASH/BTC']), + (['5m'], ['ADA/BTC', 'DASH/BTC']), + (['5m', '15m'], ['ADA/BTC', 'DASH/BTC', 'ETH/USDT']), +]) +def test_freqai_informative_pairs(mocker, freqai_conf, timeframes, corr_pairs): + freqai_conf['freqai']['feature_parameters'].update({ + 'include_timeframes': timeframes, + 'include_corr_pairlist': corr_pairs, + + }) + strategy = get_patched_freqai_strategy(mocker, freqai_conf) + exchange = get_patched_exchange(mocker, freqai_conf) + pairlists = PairListManager(exchange, freqai_conf) + strategy.dp = DataProvider(freqai_conf, exchange, pairlists) + pairlist = strategy.dp.current_whitelist() + + pairs_a = strategy.informative_pairs() + assert len(pairs_a) == 0 + pairs_b = strategy.gather_informative_pairs() + # we expect unique pairs * timeframes + assert len(pairs_b) == len(set(pairlist + corr_pairs)) * len(timeframes) diff --git a/tests/strategy/strats/freqai_test_strat.py b/tests/strategy/strats/freqai_test_strat.py index 792a3952f..cdfb7f4d0 100644 --- a/tests/strategy/strats/freqai_test_strat.py +++ b/tests/strategy/strats/freqai_test_strat.py @@ -43,19 +43,6 @@ class freqai_test_strat(IStrategy): ) max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True) - def informative_pairs(self): - whitelist_pairs = self.dp.current_whitelist() - corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] - informative_pairs = [] - for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: - for pair in whitelist_pairs: - informative_pairs.append((pair, tf)) - for pair in corr_pairs: - if pair in whitelist_pairs: - continue # avoid duplication - informative_pairs.append((pair, tf)) - return informative_pairs - def populate_any_indicators( self, pair, df, tf, informative=None, set_generalized_indicators=False ): From 10ec681b30ad4826c5ec8a15d94a23820d1176fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:19:46 +0200 Subject: [PATCH 04/12] Clean up no longer needed informative sample code --- docs/freqai.md | 13 ------------- freqtrade/templates/FreqaiExampleStrategy.py | 13 ------------- freqtrade/templates/FreqaiHybridExampleStrategy.py | 14 -------------- .../strats/freqai_test_multimodel_strat.py | 13 ------------- 4 files changed, 53 deletions(-) diff --git a/docs/freqai.md b/docs/freqai.md index 5f523f58a..33fac198c 100644 --- a/docs/freqai.md +++ b/docs/freqai.md @@ -190,19 +190,6 @@ The FreqAI strategy requires the user to include the following lines of code in # passed to any single indicator) startup_candle_count: int = 20 - def informative_pairs(self): - whitelist_pairs = self.dp.current_whitelist() - corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] - informative_pairs = [] - for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: - for pair in whitelist_pairs: - informative_pairs.append((pair, tf)) - for pair in corr_pairs: - if pair in whitelist_pairs: - continue # avoid duplication - informative_pairs.append((pair, tf)) - return informative_pairs - def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # the model will return all labels created by user in `populate_any_indicators` diff --git a/freqtrade/templates/FreqaiExampleStrategy.py b/freqtrade/templates/FreqaiExampleStrategy.py index 15b2c6c83..907106453 100644 --- a/freqtrade/templates/FreqaiExampleStrategy.py +++ b/freqtrade/templates/FreqaiExampleStrategy.py @@ -47,19 +47,6 @@ class FreqaiExampleStrategy(IStrategy): std_dev_multiplier_sell = CategoricalParameter( [0.1, 0.25, 0.4], space="sell", default=0.2, optimize=True) - def informative_pairs(self): - whitelist_pairs = self.dp.current_whitelist() - corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] - informative_pairs = [] - for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: - for pair in whitelist_pairs: - informative_pairs.append((pair, tf)) - for pair in corr_pairs: - if pair in whitelist_pairs: - continue # avoid duplication - informative_pairs.append((pair, tf)) - return informative_pairs - def populate_any_indicators( self, pair, df, tf, informative=None, set_generalized_indicators=False ): diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index 286ff012f..593a6062b 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -95,20 +95,6 @@ class FreqaiExampleHybridStrategy(IStrategy): short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True) exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True) - # FreqAI required function, leave as is or add additional informatives to existing structure. - def informative_pairs(self): - whitelist_pairs = self.dp.current_whitelist() - corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] - informative_pairs = [] - for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: - for pair in whitelist_pairs: - informative_pairs.append((pair, tf)) - for pair in corr_pairs: - if pair in whitelist_pairs: - continue # avoid duplication - informative_pairs.append((pair, tf)) - return informative_pairs - # FreqAI required function, user can add or remove indicators, but general structure # must stay the same. def populate_any_indicators( diff --git a/tests/strategy/strats/freqai_test_multimodel_strat.py b/tests/strategy/strats/freqai_test_multimodel_strat.py index cd3327da9..ada4b25f0 100644 --- a/tests/strategy/strats/freqai_test_multimodel_strat.py +++ b/tests/strategy/strats/freqai_test_multimodel_strat.py @@ -43,19 +43,6 @@ class freqai_test_multimodel_strat(IStrategy): ) max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True) - def informative_pairs(self): - whitelist_pairs = self.dp.current_whitelist() - corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] - informative_pairs = [] - for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: - for pair in whitelist_pairs: - informative_pairs.append((pair, tf)) - for pair in corr_pairs: - if pair in whitelist_pairs: - continue # avoid duplication - informative_pairs.append((pair, tf)) - return informative_pairs - def populate_any_indicators( self, pair, df, tf, informative=None, set_generalized_indicators=False ): From 6682ae35b332539d7b0fdb72af9e5fccc04bb3e9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:37:47 +0200 Subject: [PATCH 05/12] Update cached binance_leverage_tiers --- .../exchange/binance_leverage_tiers.json | 686 +++++++++++++++++- 1 file changed, 670 insertions(+), 16 deletions(-) diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index eace16c05..2fa326bb1 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -81,6 +81,104 @@ } } ], + "1000LUNC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], "1000SHIB/BUSD": [ { "tier": 1.0, @@ -1109,6 +1207,88 @@ } } ], + "AMB/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], "ANC/BUSD": [ { "tier": 1.0, @@ -3300,13 +3480,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" @@ -4880,13 +5060,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" @@ -8333,6 +8513,104 @@ } } ], + "FOOTBALL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], "FTM/BUSD": [ { "tier": 1.0, @@ -12123,6 +12401,104 @@ } } ], + "LUNA2/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "675.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5675.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11925.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386925.0" + } + } + ], "MANA/USDT": [ { "tier": 1.0, @@ -13028,10 +13404,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -13805,6 +14181,88 @@ } } ], + "PHB/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], "QTUM/USDT": [ { "tier": 1.0, @@ -14008,10 +14466,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -14478,13 +14936,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" @@ -14576,13 +15034,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" @@ -15487,6 +15945,104 @@ } } ], + "SPELL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], "SRM/USDT": [ { "tier": 1.0, @@ -15585,6 +16141,104 @@ } } ], + "STG/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], "STMX/USDT": [ { "tier": 1.0, @@ -16176,13 +16830,13 @@ "tier": 5.0, "currency": "BUSD", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "5", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386875.0" @@ -16470,13 +17124,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 30000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", + "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" From 4182a7891a261e0dd7facd605a2667f9d2169412 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:41:48 +0200 Subject: [PATCH 06/12] Allow leverage tier cache to be 4 weeks old. we've seen from binance that it's not changing this often. --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_okx.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 33a56c530..a2ddc16e8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2304,7 +2304,7 @@ class Exchange: updated = tiers.get('updated') if updated: updated_dt = parser.parse(updated) - if updated_dt < datetime.now(timezone.utc) - timedelta(days=1): + if updated_dt < datetime.now(timezone.utc) - timedelta(weeks=4): logger.info("Cached leverage tiers are outdated. Will update.") return None return tiers['data'] diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 12322acae..ac5c81ebb 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -472,7 +472,7 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmpdir, caplog, api_mock.fetch_market_leverage_tiers.call_count == 0 # 2 day passes ... - time_machine.move_to(datetime.now() + timedelta(days=2)) + time_machine.move_to(datetime.now() + timedelta(weeks=5)) exchange.load_leverage_tiers() assert log_has(logmsg, caplog) From 38b28fc4da4caf437d8ccf2bcfd9bc95b79ebe08 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 14:19:20 +0200 Subject: [PATCH 07/12] Update duplicated test --- tests/freqai/test_freqai_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index 6aa4f40c3..998bce903 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -320,7 +320,7 @@ def test_principal_component_analysis(mocker, freqai_conf): @pytest.mark.parametrize('timeframes,corr_pairs', [ (['5m'], ['ADA/BTC', 'DASH/BTC']), - (['5m'], ['ADA/BTC', 'DASH/BTC']), + (['5m'], ['ADA/BTC', 'DASH/BTC', 'ETH/USDT']), (['5m', '15m'], ['ADA/BTC', 'DASH/BTC', 'ETH/USDT']), ]) def test_freqai_informative_pairs(mocker, freqai_conf, timeframes, corr_pairs): From 8639c1f23d89121d97775daa5157939395f10e07 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 10:56:27 +0200 Subject: [PATCH 08/12] Reduce complexity in binance stoploss handling --- freqtrade/exchange/binance.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 026ba1c65..a012aa3a3 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -48,13 +48,12 @@ class Binance(Exchange): Returns True if adjustment is necessary. :param side: "buy" or "sell" """ - - ordertype = 'stop' if self.trading_mode == TradingMode.FUTURES else 'stop_loss_limit' + order_types = ('stop_loss_limit', 'stop', 'stop_market') return ( order.get('stopPrice', None) is None or ( - order['type'] == ordertype + order['type'] in order_types and ( (side == "sell" and stop_loss > float(order['stopPrice'])) or (side == "buy" and stop_loss < float(order['stopPrice'])) From ca6dec3d4c41783c5a91046825301698c308bdb9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 14:55:00 +0200 Subject: [PATCH 09/12] Binance spot also allows market orders closes #7426 --- freqtrade/exchange/binance.py | 2 +- tests/exchange/test_binance.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index a012aa3a3..faa780529 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -31,7 +31,7 @@ class Binance(Exchange): "ccxt_futures_name": "future" } _ft_has_futures: Dict = { - "stoploss_order_types": {"limit": "stop"}, + "stoploss_order_types": {"limit": "limit", "market": "market"}, "tickers_have_price": False, } diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 4d1c40647..e9f4dfa8a 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -23,7 +23,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'stop' + order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'limit' api_mock.create_order = MagicMock(return_value={ 'id': order_id, @@ -45,12 +45,15 @@ def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side amount=1, stop_price=190, side=side, - order_types={'stoploss_on_exchange_limit_ratio': 1.05}, + order_types={'stoploss': 'limit', 'stoploss_on_exchange_limit_ratio': 1.05}, leverage=1.0 ) api_mock.create_order.reset_mock() - order_types = {} if limitratio is None else {'stoploss_on_exchange_limit_ratio': limitratio} + order_types = {'stoploss': 'limit'} + if limitratio is not None: + order_types.update({'stoploss_on_exchange_limit_ratio': limitratio}) + order = exchange.stoploss( pair='ETH/BTC', amount=1, From 9f266cbcb2232e7d3fc9219caaef4ea21aa85c48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 16:44:08 +0200 Subject: [PATCH 10/12] Allow safe_price for market stop orders --- freqtrade/persistence/trade_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index ea60796a4..2e479066c 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -83,7 +83,7 @@ class Order(_DECL_BASE): @property def safe_price(self) -> float: - return self.average or self.price + return self.average or self.price or self.stop_price @property def safe_filled(self) -> float: From 063511826c3c4ed87e1247552e7a676cc0db4afe Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 17:07:47 +0200 Subject: [PATCH 11/12] Update stoploss on exchange logic closes #7424 --- freqtrade/freqtradebot.py | 7 ++++--- freqtrade/persistence/trade_model.py | 7 ++++++- tests/test_freqtradebot.py | 11 +++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6c001a8d6..3eaec5c98 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1072,6 +1072,7 @@ class FreqtradeBot(LoggingMixin): order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss') trade.orders.append(order_obj) trade.stoploss_order_id = str(stoploss_order['id']) + trade.stoploss_last_update = datetime.now(timezone.utc) return True except InsufficientFundsError as e: logger.warning(f"Unable to place stoploss order {e}.") @@ -1145,10 +1146,9 @@ class FreqtradeBot(LoggingMixin): if self.create_stoploss_order(trade=trade, stop_price=stop_price): # The above will return False if the placement failed and the trade was force-sold. # in which case the trade will be closed - which we must check below. - trade.stoploss_last_update = datetime.utcnow() return False - # If stoploss order is canceled for some reason we add it + # If stoploss order is canceled for some reason we add it again if (trade.is_open and stoploss_order and stoploss_order['status'] in ('canceled', 'cancelled')): @@ -1186,7 +1186,8 @@ class FreqtradeBot(LoggingMixin): if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): # we check if the update is necessary update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60) - if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() >= update_beat: + upd_req = datetime.now(timezone.utc) - timedelta(seconds=update_beat) + if trade.stoploss_last_update_utc and upd_req >= trade.stoploss_last_update_utc: # cancelling the current stoploss on exchange first logger.info(f"Cancelling current stoploss on exchange for pair {trade.pair} " f"(orderid:{order['id']}) in order to add another one ...") diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 2e479066c..6e421f33e 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -376,6 +376,12 @@ class LocalTrade(): def open_date_utc(self): return self.open_date.replace(tzinfo=timezone.utc) + @property + def stoploss_last_update_utc(self): + if self.stoploss_last_update: + return self.stoploss_last_update.replace(tzinfo=timezone.utc) + return None + @property def close_date_utc(self): return self.close_date.replace(tzinfo=timezone.utc) @@ -560,7 +566,6 @@ class LocalTrade(): self.stop_loss = stop_loss_norm self.stop_loss_pct = -1 * abs(percent) - self.stoploss_last_update = datetime.utcnow() def adjust_stop_loss(self, current_price: float, stoploss: float, initial: bool = False, refresh: bool = False) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 565797d81..c1152ac09 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1427,6 +1427,7 @@ def test_handle_stoploss_on_exchange_trailing( trade.is_open = True trade.open_order_id = None trade.stoploss_order_id = 100 + trade.stoploss_last_update = arrow.utcnow().shift(minutes=-20).datetime stoploss_order_hanging = MagicMock(return_value={ 'id': 100, @@ -1456,7 +1457,7 @@ def test_handle_stoploss_on_exchange_trailing( ) cancel_order_mock = MagicMock() - stoploss_order_mock = MagicMock(return_value={'id': 13434334}) + stoploss_order_mock = MagicMock(return_value={'id': 'so1'}) mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order', cancel_order_mock) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock) @@ -1569,6 +1570,7 @@ def test_handle_stoploss_on_exchange_trailing_error( assert stoploss.call_count == 1 # Fail creating stoploss order + trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime caplog.clear() cancel_mock = mocker.patch("freqtrade.exchange.Binance.cancel_stoploss_order", MagicMock()) mocker.patch("freqtrade.exchange.Binance.stoploss", side_effect=ExchangeError()) @@ -1657,6 +1659,7 @@ def test_handle_stoploss_on_exchange_custom_stop( trade.is_open = True trade.open_order_id = None trade.stoploss_order_id = 100 + trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime stoploss_order_hanging = MagicMock(return_value={ 'id': 100, @@ -1685,7 +1688,7 @@ def test_handle_stoploss_on_exchange_custom_stop( ) cancel_order_mock = MagicMock() - stoploss_order_mock = MagicMock(return_value={'id': 13434334}) + stoploss_order_mock = MagicMock(return_value={'id': 'so1'}) mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order', cancel_order_mock) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock) @@ -1727,8 +1730,7 @@ def test_handle_stoploss_on_exchange_custom_stop( assert freqtrade.handle_trade(trade) is True -def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, - limit_order) -> None: +def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, limit_order) -> None: enter_order = limit_order['buy'] exit_order = limit_order['sell'] @@ -1784,6 +1786,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, trade.is_open = True trade.open_order_id = None trade.stoploss_order_id = 100 + trade.stoploss_last_update = arrow.utcnow() stoploss_order_hanging = MagicMock(return_value={ 'id': 100, From 92a32ab31b65f77bd63f9feab6b33b7b8b9028cc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 17 Sep 2022 17:14:45 +0200 Subject: [PATCH 12/12] Add documentation for stop-market on binance futures part of #7426 --- docs/exchanges.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index b5470f65a..dc2003f9c 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -57,7 +57,8 @@ This configuration enables kraken, as well as rate-limiting to avoid bans from t Binance supports [time_in_force](configuration.md#understand-order_time_in_force). !!! Tip "Stoploss on Exchange" - Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.. + Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange. + On futures, Binance supports both `stop-limit` as well as `stop-market` orders. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type to use. ### Binance Blacklist