| @@ -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: | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
|                 )) | ||||
|   | ||||
| @@ -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, | ||||
|             )) | ||||
|   | ||||
| @@ -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, | ||||
|     ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user