cleaned up liquidation price methods
This commit is contained in:
parent
ede9012fcc
commit
143c37d36f
@ -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,135 +2018,62 @@ 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)
|
||||
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]:
|
||||
"""
|
||||
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
|
||||
|
||||
: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 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 mm_ratio:
|
||||
: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)
|
||||
:param wallet_balance: Amount of collateral in the wallet being used to trade
|
||||
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
|
||||
# * Not required by Gateio or OKX
|
||||
:param maintenance_amt:
|
||||
: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 collateral:
|
||||
if (not taker_fee_rate):
|
||||
raise OperationalException(
|
||||
"Parameter collateral is required by liquidation_price when trading_mode is "
|
||||
f"{trading_mode}"
|
||||
f"Parameter taker_fee_rate is required by {self.name}.liquidation_price"
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
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")
|
||||
|
||||
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
|
||||
upnl_ex_1: Optional[float] = 0.0, # (Binance) Cross only
|
||||
) -> Optional[float]:
|
||||
"""
|
||||
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
|
||||
value = wallet_balance / position
|
||||
|
||||
: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.
|
||||
:param collateral: Either ISOLATED or CROSS
|
||||
:param wallet_balance: Amount of collateral in the wallet being used to trade
|
||||
Cross-Margin Mode: crossWalletBalance
|
||||
Isolated-Margin Mode: isolatedWalletBalance
|
||||
:param taker_fee_rate:
|
||||
|
||||
# * Not required by Gateio or OKX
|
||||
:param maintenance_amt:
|
||||
:param mm_ex_1:
|
||||
:param upnl_ex_1:
|
||||
"""
|
||||
if trading_mode == TradingMode.SPOT:
|
||||
return None
|
||||
|
||||
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 is_inverse:
|
||||
# raise OperationalException(
|
||||
# "Freqtrade does not support inverse contracts at the moment")
|
||||
|
||||
value = wallet_balance / position
|
||||
|
||||
mm_ratio_taker = (mm_ratio + taker_fee_rate)
|
||||
if is_short:
|
||||
return (open_rate + value) / (1 + mm_ratio_taker)
|
||||
else:
|
||||
return (open_rate - value) / (1 - mm_ratio_taker)
|
||||
mm_ratio_taker = (mm_ratio + taker_fee_rate)
|
||||
if is_short:
|
||||
return (open_rate + value) / (1 + mm_ratio_taker)
|
||||
else:
|
||||
raise OperationalException(
|
||||
f"{self.name} does not support {collateral.value} {trading_mode.value}")
|
||||
return (open_rate - value) / (1 - mm_ratio_taker)
|
||||
else:
|
||||
raise OperationalException(
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user