diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 37a3c419d..a5e9fd37c 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -137,23 +137,27 @@ class Binance(Exchange): pair: str, open_rate: float, # Entry price of position is_short: bool, - position: float, # Absolute value of position size + amount: float, + stake_amount: float, wallet_balance: float, # Or margin balance mm_ex_1: float = 0.0, # (Binance) Cross only upnl_ex_1: float = 0.0, # (Binance) Cross only ) -> Optional[float]: """ + Important: Must be fetching data from cached values as this is used by backtesting! MARGIN: https://www.binance.com/en/support/faq/f6b010588e55413aa58b7d63ee0125ed PERPETUAL: https://www.binance.com/en/support/faq/b3c689c1f50a44cabb3a84e663b81d93 :param exchange_name: - :param open_rate: (EP1) Entry price of position + :param open_rate: Entry price of position :param is_short: True if the trade is a short, false otherwise - :param position: Absolute value of position size (in base currency) - :param wallet_balance: (WB) + :param amount: Absolute value of position size incl. leverage (in base currency) + :param stake_amount: Stake amount - Collateral in settle currency. + :param trading_mode: SPOT, MARGIN, FUTURES, etc. + :param margin_mode: Either ISOLATED or CROSS + :param wallet_balance: Amount of margin_mode in the wallet being used to trade Cross-Margin Mode: crossWalletBalance Isolated-Margin Mode: isolatedWalletBalance - :param maintenance_amt: # * Only required for Cross :param mm_ex_1: (TMM) @@ -165,12 +169,11 @@ class Binance(Exchange): """ side_1 = -1 if is_short else 1 - position = abs(position) cross_vars = upnl_ex_1 - mm_ex_1 if self.margin_mode == MarginMode.CROSS else 0.0 # mm_ratio: Binance's formula specifies maintenance margin rate which is mm_ratio * 100% # maintenance_amt: (CUM) Maintenance Amount of position - mm_ratio, maintenance_amt = self.get_maintenance_ratio_and_amt(pair, position) + mm_ratio, maintenance_amt = self.get_maintenance_ratio_and_amt(pair, stake_amount) if (maintenance_amt is None): raise OperationalException( @@ -182,9 +185,9 @@ class Binance(Exchange): return ( ( (wallet_balance + cross_vars + maintenance_amt) - - (side_1 * position * open_rate) + (side_1 * amount * open_rate) ) / ( - (position * mm_ratio) - (side_1 * position) + (amount * mm_ratio) - (side_1 * amount) ) ) else: diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index afda824f2..4386f47f6 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2437,6 +2437,7 @@ class Exchange: pair: str, open_rate: float, amount: float, # quote currency, includes leverage + stake_amount: float, leverage: float, is_short: bool ) -> Optional[float]: @@ -2446,13 +2447,13 @@ class Exchange: elif ( self.trading_mode == TradingMode.FUTURES ): - wallet_balance = (amount * open_rate) / leverage isolated_liq = self.get_or_calculate_liquidation_price( pair=pair, open_rate=open_rate, is_short=is_short, - position=amount, - wallet_balance=wallet_balance, + amount=amount, + stake_amount=stake_amount, + wallet_balance=stake_amount, # In isolated mode, stake-amount = wallet size mm_ex_1=0.0, upnl_ex_1=0.0, ) @@ -2627,14 +2628,14 @@ class Exchange: # Dry-run open_rate: float, # Entry price of position is_short: bool, - position: float, # Absolute value of position size + amount: float, # Absolute value of position size + stake_amount: float, wallet_balance: float, # Or margin balance mm_ex_1: float = 0.0, # (Binance) Cross only upnl_ex_1: float = 0.0, # (Binance) Cross only ) -> Optional[float]: """ Set's the margin mode on the exchange to cross or isolated for a specific pair - :param pair: base/quote currency pair (e.g. "ADA/USDT") """ if self.trading_mode == TradingMode.SPOT: return None @@ -2648,7 +2649,8 @@ class Exchange: pair=pair, open_rate=open_rate, is_short=is_short, - position=position, + amount=amount, + stake_amount=stake_amount, wallet_balance=wallet_balance, mm_ex_1=mm_ex_1, upnl_ex_1=upnl_ex_1 @@ -2677,22 +2679,24 @@ class Exchange: pair: str, open_rate: float, # Entry price of position is_short: bool, - position: float, # Absolute value of position size + amount: float, + stake_amount: float, wallet_balance: float, # Or margin balance mm_ex_1: float = 0.0, # (Binance) Cross only upnl_ex_1: float = 0.0, # (Binance) Cross only ) -> Optional[float]: """ + Important: Must be fetching data from cached values as this is used by backtesting! PERPETUAL: gateio: https://www.gate.io/help/futures/perpetual/22160/calculation-of-liquidation-price okex: https://www.okex.com/support/hc/en-us/articles/ 360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin - Important: Must be fetching data from cached values as this is used by backtesting! :param exchange_name: :param open_rate: Entry price of position :param is_short: True if the trade is a short, false otherwise - :param position: Absolute value of position size incl. leverage (in base currency) + :param amount: Absolute value of position size incl. leverage (in base currency) + :param stake_amount: Stake amount - Collateral in settle currency. :param trading_mode: SPOT, MARGIN, FUTURES, etc. :param margin_mode: Either ISOLATED or CROSS :param wallet_balance: Amount of margin_mode in the wallet being used to trade @@ -2706,7 +2710,7 @@ class Exchange: market = self.markets[pair] taker_fee_rate = market['taker'] - mm_ratio, _ = self.get_maintenance_ratio_and_amt(pair, position) + mm_ratio, _ = self.get_maintenance_ratio_and_amt(pair, stake_amount) if self.trading_mode == TradingMode.FUTURES and self.margin_mode == MarginMode.ISOLATED: @@ -2714,7 +2718,7 @@ class Exchange: raise OperationalException( "Freqtrade does not yet support inverse contracts") - value = wallet_balance / position + value = wallet_balance / amount mm_ratio_taker = (mm_ratio + taker_fee_rate) if is_short: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1967de1fe..f4731220c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1734,6 +1734,7 @@ class FreqtradeBot(LoggingMixin): leverage=trade.leverage, pair=trade.pair, amount=trade.amount, + stake_amount=trade.stake_amount, open_rate=trade.open_rate, is_short=trade.is_short )) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 00cd8fa4a..ff30dbc2a 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -876,6 +876,7 @@ class Backtesting: pair=pair, open_rate=propose_rate, amount=amount, + stake_amount=trade.stake_amount, leverage=leverage, is_short=is_short, )) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 650a20197..093284668 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -4132,7 +4132,8 @@ def test_get_or_calculate_liquidation_price(mocker, default_conf): pair='NEAR/USDT:USDT', open_rate=18.884, is_short=False, - position=0.8, + amount=0.8, + stake_amount=18.884 * 0.8, wallet_balance=0.8, ) assert liq_price == 17.47 @@ -4143,7 +4144,8 @@ def test_get_or_calculate_liquidation_price(mocker, default_conf): pair='NEAR/USDT:USDT', open_rate=18.884, is_short=False, - position=0.8, + amount=0.8, + stake_amount=18.884 * 0.8, wallet_balance=0.8, ) assert liq_price == 17.540699999999998 @@ -4543,7 +4545,8 @@ def test_liquidation_price_is_none( pair='DOGE/USDT', open_rate=open_rate, is_short=is_short, - position=71200.81144, + amount=71200.81144, + stake_amount=open_rate * 71200.81144, wallet_balance=-56354.57, mm_ex_1=0.10, upnl_ex_1=0.0 @@ -4552,7 +4555,7 @@ def test_liquidation_price_is_none( @pytest.mark.parametrize( 'exchange_name, is_short, trading_mode, margin_mode, wallet_balance, ' - 'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, ' + 'mm_ex_1, upnl_ex_1, maintenance_amt, amount, open_rate, ' 'mm_ratio, expected', [ ("binance", False, 'futures', 'isolated', 1535443.01, 0.0, @@ -4566,7 +4569,7 @@ def test_liquidation_price_is_none( ]) def test_liquidation_price( mocker, default_conf, exchange_name, open_rate, is_short, trading_mode, - margin_mode, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_ratio, expected + margin_mode, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, amount, mm_ratio, expected ): default_conf['trading_mode'] = trading_mode default_conf['margin_mode'] = margin_mode @@ -4580,7 +4583,8 @@ def test_liquidation_price( wallet_balance=wallet_balance, mm_ex_1=mm_ex_1, upnl_ex_1=upnl_ex_1, - position=position, + amount=amount, + stake_amount=open_rate * amount, ), 2), expected) @@ -5111,6 +5115,7 @@ def test_get_liquidation_price( pair='ETH/USDT:USDT', open_rate=open_rate, amount=amount, + stake_amount=amount * open_rate / leverage, leverage=leverage, is_short=is_short, )