cleaned up liquidation price methods
This commit is contained in:
		| @@ -277,18 +277,15 @@ class Binance(Exchange): | ||||
|         # The lowest notional_floor for any pair in loadLeverageBrackets is always 0 because it | ||||
|         # describes the min amount for a bracket, and the lowest bracket will always go down to 0 | ||||
|  | ||||
|     def liquidation_price_helper( | ||||
|     def liquidation_price( | ||||
|         self, | ||||
|         open_rate: float,   # Entry price of position | ||||
|         is_short: bool, | ||||
|         leverage: float, | ||||
|         mm_ratio: float, | ||||
|         position: float,  # Absolute value of position size | ||||
|         trading_mode: TradingMode, | ||||
|         collateral: Collateral, | ||||
|         maintenance_amt: Optional[float] = None,  # (Binance) | ||||
|         wallet_balance: Optional[float] = None,  # (Binance and Gateio) | ||||
|         wallet_balance: float,  # Or margin balance | ||||
|         taker_fee_rate: Optional[float] = None,  # (Gateio & Okex) | ||||
|         maintenance_amt: Optional[float] = None,  # (Binance) | ||||
|         mm_ex_1: Optional[float] = 0.0,  # (Binance) Cross only | ||||
|         upnl_ex_1: Optional[float] = 0.0,  # (Binance) Cross only | ||||
|     ) -> Optional[float]: | ||||
| @@ -299,19 +296,15 @@ class Binance(Exchange): | ||||
|         :param exchange_name: | ||||
|         :param open_rate: (EP1) Entry price of position | ||||
|         :param is_short: True if the trade is a short, false otherwise | ||||
|         :param leverage: The amount of leverage on the trade | ||||
|         :param mm_ratio: (MMR) | ||||
|             # Binance's formula specifies maintenance margin rate which is mm_ratio * 100% | ||||
|         :param position: Absolute value of position size (in base currency) | ||||
|         :param trading_mode: SPOT, MARGIN, FUTURES, etc. | ||||
|         :param collateral: Either ISOLATED or CROSS | ||||
|         :param maintenance_amt: (CUM) Maintenance Amount of position | ||||
|         :param wallet_balance: (WB) | ||||
|             Cross-Margin Mode: crossWalletBalance | ||||
|             Isolated-Margin Mode: isolatedWalletBalance | ||||
|  | ||||
|         # * Not required by Binance | ||||
|         :param taker_fee_rate: | ||||
|         :param taker_fee_rate:  # * Not required by Binance | ||||
|         :param maintenance_amt: | ||||
|  | ||||
|         # * Only required for Cross | ||||
|         :param mm_ex_1: (TMM) | ||||
| @@ -321,31 +314,32 @@ class Binance(Exchange): | ||||
|             Cross-Margin Mode: Unrealized PNL of all other contracts, excluding Contract 1. | ||||
|             Isolated-Margin Mode: 0 | ||||
|         """ | ||||
|         if trading_mode == TradingMode.SPOT: | ||||
|         if self.trading_mode == TradingMode.SPOT: | ||||
|             return None | ||||
|         elif (self.collateral is None): | ||||
|             raise OperationalException('Binance.collateral must be set for liquidation_price') | ||||
|  | ||||
|         if not collateral: | ||||
|         if (maintenance_amt is None): | ||||
|             raise OperationalException( | ||||
|                 "Parameter collateral is required by liquidation_price when trading_mode is " | ||||
|                 f"{trading_mode}" | ||||
|                 f"Parameter maintenance_amt is required by Binance.liquidation_price" | ||||
|                 f"for {self.collateral.value} {self.trading_mode.value}" | ||||
|             ) | ||||
|         if ( | ||||
|             (wallet_balance is None or maintenance_amt is None or position is None) or | ||||
|             (collateral == Collateral.CROSS and (mm_ex_1 is None or upnl_ex_1 is None)) | ||||
|         ): | ||||
|             required_params = "wallet_balance, maintenance_amt, position" | ||||
|             if collateral == Collateral.CROSS: | ||||
|                 required_params += ", mm_ex_1, upnl_ex_1" | ||||
|  | ||||
|         if (self.collateral == Collateral.CROSS and (mm_ex_1 is None or upnl_ex_1 is None)): | ||||
|             raise OperationalException( | ||||
|                 f"Parameters {required_params} are required by Binance.liquidation_price" | ||||
|                 f"for {collateral.name} {trading_mode.name}" | ||||
|                 f"Parameters mm_ex_1 and upnl_ex_1 are required by Binance.liquidation_price" | ||||
|                 f"for {self.collateral.value} {self.trading_mode.value}" | ||||
|             ) | ||||
|  | ||||
|         side_1 = -1 if is_short else 1 | ||||
|         position = abs(position) | ||||
|         cross_vars = upnl_ex_1 - mm_ex_1 if collateral == Collateral.CROSS else 0.0  # type: ignore | ||||
|         cross_vars = ( | ||||
|             upnl_ex_1 - mm_ex_1  # type: ignore | ||||
|             if self.collateral == Collateral.CROSS else | ||||
|             0.0 | ||||
|         ) | ||||
|  | ||||
|         if trading_mode == TradingMode.FUTURES: | ||||
|         if self.trading_mode == TradingMode.FUTURES: | ||||
|             return ( | ||||
|                 ( | ||||
|                     (wallet_balance + cross_vars + maintenance_amt) - | ||||
| @@ -356,4 +350,4 @@ class Binance(Exchange): | ||||
|             ) | ||||
|  | ||||
|         raise OperationalException( | ||||
|             f"Binance does not support {collateral.value} Mode {trading_mode.value} trading ") | ||||
|             f"Binance does not support {self.collateral.value} {self.trading_mode.value} trading") | ||||
|   | ||||
| @@ -2018,83 +2018,9 @@ class Exchange: | ||||
|         self, | ||||
|         open_rate: float,   # Entry price of position | ||||
|         is_short: bool, | ||||
|         leverage: float, | ||||
|         mm_ratio: float, | ||||
|         position: float,  # Absolute value of position size | ||||
|         trading_mode: TradingMode, | ||||
|         collateral: Optional[Collateral] = Collateral.ISOLATED, | ||||
|         maintenance_amt: Optional[float] = None,  # (Binance) | ||||
|         wallet_balance: Optional[float] = None,  # (Binance and Gateio) | ||||
|         taker_fee_rate: Optional[float] = None,  # (Gateio & Okex) | ||||
|         mm_ex_1: Optional[float] = 0.0,  # (Binance) Cross only | ||||
|         upnl_ex_1: Optional[float] = 0.0,  # (Binance) Cross only | ||||
|     ) -> Optional[float]: | ||||
|         """ | ||||
|         :param exchange_name: | ||||
|         :param open_rate: (EP1) Entry price of position | ||||
|         :param is_short: True if the trade is a short, false otherwise | ||||
|         :param leverage: The amount of leverage on the trade | ||||
|         :param mm_ratio: (MMR) | ||||
|             Okex: [assets in the position - (liability +interest) * mark price] / | ||||
|                 (maintenance margin + liquidation fee) | ||||
|             # * Note: Binance's formula specifies maintenance margin rate which is mm_ratio * 100% | ||||
|         :param position: Absolute value of position size (in base currency) | ||||
|         :param trading_mode: SPOT, MARGIN, FUTURES, etc. | ||||
|         :param collateral: Either ISOLATED or CROSS | ||||
|  | ||||
|         # * Binance | ||||
|         :param maintenance_amt: (CUM) Maintenance Amount of position | ||||
|  | ||||
|         # * Binance and Gateio | ||||
|         :param wallet_balance: (WB) | ||||
|             Cross-Margin Mode: crossWalletBalance | ||||
|             Isolated-Margin Mode: isolatedWalletBalance | ||||
|  | ||||
|         # * Gateio & Okex | ||||
|         :param taker_fee_rate: | ||||
|  | ||||
|         # * Cross only (Binance) | ||||
|         :param mm_ex_1: (TMM) | ||||
|             Cross-Margin Mode: Maintenance Margin of all other contracts, excluding Contract 1 | ||||
|             Isolated-Margin Mode: 0 | ||||
|         :param upnl_ex_1: (UPNL) | ||||
|             Cross-Margin Mode: Unrealized PNL of all other contracts, excluding Contract 1. | ||||
|             Isolated-Margin Mode: 0 | ||||
|         """ | ||||
|         if trading_mode == TradingMode.SPOT: | ||||
|             return None | ||||
|  | ||||
|         if not collateral: | ||||
|             raise OperationalException( | ||||
|                 "Parameter collateral is required by liquidation_price when trading_mode is " | ||||
|                 f"{trading_mode}" | ||||
|             ) | ||||
|  | ||||
|         return self.liquidation_price_helper( | ||||
|             open_rate=open_rate, | ||||
|             is_short=is_short, | ||||
|             leverage=leverage, | ||||
|             mm_ratio=mm_ratio, | ||||
|             position=position, | ||||
|             trading_mode=trading_mode, | ||||
|             collateral=collateral, | ||||
|             maintenance_amt=maintenance_amt, | ||||
|             wallet_balance=wallet_balance, | ||||
|             taker_fee_rate=taker_fee_rate, | ||||
|             mm_ex_1=mm_ex_1, | ||||
|             upnl_ex_1=upnl_ex_1, | ||||
|         ) | ||||
|  | ||||
|         def liquidation_price_helper( | ||||
|             self, | ||||
|             open_rate: float,   # Entry price of position | ||||
|             is_short: bool, | ||||
|             leverage: float, | ||||
|         mm_ratio: float, | ||||
|         position: float,  # Absolute value of position size | ||||
|         wallet_balance: float,  # Or margin balance | ||||
|             trading_mode: TradingMode, | ||||
|             collateral: Collateral, | ||||
|         taker_fee_rate: Optional[float] = None,  # (Gateio & Okex) | ||||
|         maintenance_amt: Optional[float] = None,  # (Binance) | ||||
|         mm_ex_1: Optional[float] = 0.0,  # (Binance) Cross only | ||||
| @@ -2109,7 +2035,6 @@ class Exchange: | ||||
|         :param exchange_name: | ||||
|         :param open_rate: Entry price of position | ||||
|         :param is_short: True if the trade is a short, false otherwise | ||||
|             :param leverage: The amount of leverage on the trade | ||||
|         :param position: Absolute value of position size (in base currency) | ||||
|         :param mm_ratio: | ||||
|         :param trading_mode: SPOT, MARGIN, FUTURES, etc. | ||||
| @@ -2124,15 +2049,17 @@ class Exchange: | ||||
|         :param mm_ex_1: | ||||
|         :param upnl_ex_1: | ||||
|         """ | ||||
|             if trading_mode == TradingMode.SPOT: | ||||
|         if self.trading_mode == TradingMode.SPOT: | ||||
|             return None | ||||
|         elif (self.collateral is None): | ||||
|             raise OperationalException('Binance.collateral must be set for liquidation_price') | ||||
|  | ||||
|         if (not taker_fee_rate): | ||||
|             raise OperationalException( | ||||
|                 f"Parameter taker_fee_rate is required by {self.name}.liquidation_price" | ||||
|             ) | ||||
|  | ||||
|             if trading_mode == TradingMode.FUTURES and collateral == Collateral.ISOLATED: | ||||
|         if self.trading_mode == TradingMode.FUTURES and self.collateral == Collateral.ISOLATED: | ||||
|             # if is_inverse: | ||||
|             #     raise OperationalException( | ||||
|             #         "Freqtrade does not support inverse contracts at the moment") | ||||
| @@ -2146,7 +2073,7 @@ class Exchange: | ||||
|                 return (open_rate - value) / (1 - mm_ratio_taker) | ||||
|         else: | ||||
|             raise OperationalException( | ||||
|                     f"{self.name} does not support {collateral.value} {trading_mode.value}") | ||||
|                 f"{self.name} does not support {self.collateral.value} {self.trading_mode.value}") | ||||
|  | ||||
|  | ||||
| def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| import logging | ||||
| from typing import Dict, List, Optional, Tuple | ||||
|  | ||||
| from typing import Dict, List, Tuple | ||||
| from freqtrade.enums import Collateral, TradingMode | ||||
| from freqtrade.exceptions import OperationalException | ||||
| from freqtrade.exchange import Exchange | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -626,16 +626,13 @@ class FreqtradeBot(LoggingMixin): | ||||
|                 isolated_liq = self.exchange.liquidation_price( | ||||
|                     open_rate=open_rate, | ||||
|                     is_short=is_short, | ||||
|                     leverage=leverage, | ||||
|                     trading_mode=self.trading_mode, | ||||
|                     collateral=Collateral.ISOLATED, | ||||
|                     mm_ex_1=0.0, | ||||
|                     upnl_ex_1=0.0, | ||||
|                     mm_ratio=mm_ratio, | ||||
|                     position=amount, | ||||
|                     wallet_balance=(amount * open_rate)/leverage,  # TODO: Update for cross | ||||
|                     taker_fee_rate=taker_fee_rate, | ||||
|                     maintenance_amt=maintenance_amt, | ||||
|                     mm_ratio=mm_ratio, | ||||
|                     taker_fee_rate=taker_fee_rate | ||||
|                     mm_ex_1=0.0, | ||||
|                     upnl_ex_1=0.0, | ||||
|                 ) | ||||
|             else: | ||||
|                 isolated_liq = self.exchange.get_liquidation_price(pair) | ||||
|   | ||||
| @@ -3626,6 +3626,8 @@ def test_get_liquidation_price(mocker, default_conf): | ||||
|         exchange_has=MagicMock(return_value=True), | ||||
|     ) | ||||
|     default_conf['dry_run'] = False | ||||
|     default_conf['trading_mode'] = 'futures' | ||||
|     default_conf['collateral'] = 'isolated' | ||||
|  | ||||
|     exchange = get_patched_exchange(mocker, default_conf, api_mock) | ||||
|     liq_price = exchange.get_liquidation_price('NEAR/USDT:USDT') | ||||
| @@ -3973,13 +3975,13 @@ def test__amount_to_contracts( | ||||
|  | ||||
| @pytest.mark.parametrize('exchange_name,open_rate,is_short,leverage,trading_mode,collateral', [ | ||||
|     # Bittrex | ||||
|     ('bittrex', "2.0", False, "3.0", spot, None), | ||||
|     ('bittrex', "2.0", False, "1.0", spot, cross), | ||||
|     ('bittrex', "2.0", True, "3.0", spot, isolated), | ||||
|     ('bittrex', 2.0, False, 3.0, spot, None), | ||||
|     ('bittrex', 2.0, False, 1.0, spot, cross), | ||||
|     ('bittrex', 2.0, True, 3.0, spot, isolated), | ||||
|     # Binance | ||||
|     ('binance', "2.0", False, "3.0", spot, None), | ||||
|     ('binance', "2.0", False, "1.0", spot, cross), | ||||
|     ('binance', "2.0", True, "3.0", spot, isolated), | ||||
|     ('binance', 2.0, False, 3.0, spot, None), | ||||
|     ('binance', 2.0, False, 1.0, spot, cross), | ||||
|     ('binance', 2.0, True, 3.0, spot, isolated), | ||||
| ]) | ||||
| def test_liquidation_price_is_none( | ||||
|     mocker, | ||||
| @@ -3991,51 +3993,22 @@ def test_liquidation_price_is_none( | ||||
|     trading_mode, | ||||
|     collateral | ||||
| ): | ||||
|     default_conf['trading_mode'] = trading_mode | ||||
|     default_conf['collateral'] = collateral | ||||
|     exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) | ||||
|     assert exchange.liquidation_price( | ||||
|         open_rate, | ||||
|         is_short, | ||||
|         leverage, | ||||
|         trading_mode, | ||||
|         collateral, | ||||
|         1535443.01, | ||||
|         71200.81144, | ||||
|         -56354.57, | ||||
|         135365.00, | ||||
|         3683.979, | ||||
|         0.10, | ||||
|         open_rate=open_rate, | ||||
|         is_short=is_short, | ||||
|         mm_ratio=1535443.01, | ||||
|         position=71200.81144, | ||||
|         wallet_balance=-56354.57, | ||||
|         taker_fee_rate=0.01, | ||||
|         maintenance_amt=3683.979, | ||||
|         mm_ex_1=0.10, | ||||
|         upnl_ex_1=0.0 | ||||
|     ) is None | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('exchange_name,open_rate,is_short,leverage,trading_mode,collateral', [ | ||||
|     # Bittrex | ||||
|     ('bittrex', "2.0", False, "3.0", margin, cross), | ||||
|     ('bittrex', "2.0", False, "3.0", margin, isolated), | ||||
|     ('bittrex', "2.0", False, "3.0", futures, cross), | ||||
|     ('bittrex', "2.0", False, "3.0", futures, isolated), | ||||
|     # Binance | ||||
|     # Binance supports isolated margin, but freqtrade likely won't for a while on Binance | ||||
|     ('binance', "2.0", True, "3.0", margin, isolated), | ||||
|     # Kraken | ||||
|     ('kraken', "2.0", True, "1.0", margin, isolated), | ||||
|     ('kraken', "2.0", True, "1.0", futures, isolated), | ||||
|     # FTX | ||||
|     ('ftx', "2.0", True, "3.0", margin, isolated), | ||||
|     ('ftx', "2.0", True, "3.0", futures, isolated), | ||||
| ]) | ||||
| def test_liquidation_price_exception_thrown( | ||||
|     exchange_name, | ||||
|     open_rate, | ||||
|     is_short, | ||||
|     leverage, | ||||
|     trading_mode, | ||||
|     collateral, | ||||
|     result | ||||
| ): | ||||
|     # TODO-lev assert exception is thrown | ||||
|     return  # Here to avoid indent error, remove when implemented | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize( | ||||
|     'exchange_name, is_short, leverage, trading_mode, collateral, wallet_balance, ' | ||||
|     'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, ' | ||||
| @@ -4054,13 +4027,12 @@ def test_liquidation_price( | ||||
|     mocker, default_conf, exchange_name, open_rate, is_short, leverage, trading_mode, | ||||
|     collateral, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_ratio, expected | ||||
| ): | ||||
|     default_conf['trading_mode'] = trading_mode | ||||
|     default_conf['collateral'] = collateral | ||||
|     exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) | ||||
|     assert isclose(round(exchange.liquidation_price( | ||||
|         open_rate=open_rate, | ||||
|         is_short=is_short, | ||||
|         leverage=leverage, | ||||
|         trading_mode=trading_mode, | ||||
|         collateral=collateral, | ||||
|         wallet_balance=wallet_balance, | ||||
|         mm_ex_1=mm_ex_1, | ||||
|         upnl_ex_1=upnl_ex_1, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user