From b292f28b35bfd39af87a9a9ccc7b773f4bcccf59 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Mar 2022 15:27:06 +0100 Subject: [PATCH] Call leverage before custom_stake_amount to properly determine min-stake-amount --- docs/bot-basics.md | 4 +-- freqtrade/freqtradebot.py | 43 +++++++++++++++++-------------- freqtrade/optimize/backtesting.py | 34 +++++++++++++----------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 9f4ef8277..170ba8a07 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -42,8 +42,8 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Check if trade-slots are still available (if `max_open_trades` is reached). * Verifies buy signal trying to enter new positions. * Determine buy-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback. - * Determine stake size by calling the `custom_stake_amount()` callback. * In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage. + * Determine stake size by calling the `custom_stake_amount()` callback. * Before a buy order is placed, `confirm_trade_entry()` strategy callback is called. This loop will be repeated again and again until the bot is stopped. @@ -59,8 +59,8 @@ This loop will be repeated again and again until the bot is stopped. * Loops per candle simulating entry and exit points. * Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy). * Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle). - * Determine stake size by calling the `custom_stake_amount()` callback. * In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage. + * Determine stake size by calling the `custom_stake_amount()` callback. * Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested. * Call `custom_stoploss()` and `custom_sell()` to find custom exit points. * For sells based on sell-signal and custom-sell: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle). diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c0af6e0e7..2d6b46745 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -600,26 +600,12 @@ class FreqtradeBot(LoggingMixin): trade_side = 'short' if is_short else 'long' pos_adjust = trade is not None - enter_limit_requested, stake_amount = self.get_valid_enter_price_and_stake( + enter_limit_requested, stake_amount, leverage = self.get_valid_enter_price_and_stake( pair, price, stake_amount, side, trade_side, enter_tag, trade) if not stake_amount: return False - if not pos_adjust: - max_leverage = self.exchange.get_max_leverage(pair, stake_amount) - leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( - pair=pair, - current_time=datetime.now(timezone.utc), - current_rate=enter_limit_requested, - proposed_leverage=1.0, - max_leverage=max_leverage, - side=trade_side, - ) if self.trading_mode != TradingMode.SPOT else 1.0 - # Cap leverage between 1.0 and max_leverage. - leverage = min(max(leverage, 1.0), max_leverage) - else: - # Changing leverage currently not possible - leverage = trade.leverage if trade else 1.0 + if pos_adjust: logger.info(f"Position adjust: about to create a new order for {pair} with stake: " f"{stake_amount} for {trade}") @@ -775,7 +761,7 @@ class FreqtradeBot(LoggingMixin): side: str, trade_side: str, entry_tag: Optional[str], trade: Optional[Trade] - ) -> Tuple[float, float]: + ) -> Tuple[float, float, float]: if price: enter_limit_requested = price @@ -792,13 +778,30 @@ class FreqtradeBot(LoggingMixin): if not enter_limit_requested: raise PricingError(f'Could not determine {side} price.') + if trade is None: + max_leverage = self.exchange.get_max_leverage(pair, stake_amount) + leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( + pair=pair, + current_time=datetime.now(timezone.utc), + current_rate=enter_limit_requested, + proposed_leverage=1.0, + max_leverage=max_leverage, + side=trade_side, + ) if self.trading_mode != TradingMode.SPOT else 1.0 + # Cap leverage between 1.0 and max_leverage. + leverage = min(max(leverage, 1.0), max_leverage) + else: + # Changing leverage currently not possible + leverage = trade.leverage if trade else 1.0 + # Min-stake-amount should actually include Leverage - this way our "minimal" # stake- amount might be higher than necessary. # We do however also need min-stake to determine leverage, therefore this is ignored as # edge-case for now. min_stake_amount = self.exchange.get_min_pair_stake_amount( - pair, enter_limit_requested, self.strategy.stoploss) - max_stake_amount = self.exchange.get_max_pair_stake_amount(pair, enter_limit_requested) + pair, enter_limit_requested, self.strategy.stoploss, leverage) + max_stake_amount = self.exchange.get_max_pair_stake_amount( + pair, enter_limit_requested, leverage) if not self.edge and trade is None: stake_available = self.wallets.get_available_stake_amount() @@ -817,7 +820,7 @@ class FreqtradeBot(LoggingMixin): max_stake_amount=max_stake_amount, ) - return enter_limit_requested, stake_amount + return enter_limit_requested, stake_amount, leverage def _notify_enter(self, trade: Trade, order: Dict, order_type: Optional[str] = None, fill: bool = False) -> None: diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6c5d098dc..2dcc5748e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -656,11 +656,27 @@ class Backtesting: else: propose_rate = min(propose_rate, row[HIGH_IDX]) - min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0 - max_stake_amount = self.exchange.get_max_pair_stake_amount(pair, propose_rate) + pos_adjust = trade is not None + + if not pos_adjust: + max_leverage = self.exchange.get_max_leverage(pair, stake_amount) + leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( + pair=pair, + current_time=current_time, + current_rate=row[OPEN_IDX], + proposed_leverage=1.0, + max_leverage=max_leverage, + side=direction, + ) if self._can_short else 1.0 + # Cap leverage between 1.0 and max_leverage. + leverage = min(max(leverage, 1.0), max_leverage) + + min_stake_amount = self.exchange.get_min_pair_stake_amount( + pair, propose_rate, -0.05, leverage=leverage) or 0 + max_stake_amount = self.exchange.get_max_pair_stake_amount( + pair, propose_rate, leverage=leverage) stake_available = self.wallets.get_available_stake_amount() - pos_adjust = trade is not None if not pos_adjust: try: stake_amount = self.wallets.get_trade_stake_amount(pair, None, update=False) @@ -689,18 +705,6 @@ class Backtesting: time_in_force = self.strategy.order_time_in_force['entry'] if not pos_adjust: - max_leverage = self.exchange.get_max_leverage(pair, stake_amount) - leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( - pair=pair, - current_time=current_time, - current_rate=row[OPEN_IDX], - proposed_leverage=1.0, - max_leverage=max_leverage, - side=direction, - ) if self._can_short else 1.0 - # Cap leverage between 1.0 and max_leverage. - leverage = min(max(leverage, 1.0), max_leverage) - # 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,