Fix liquidation price tier calculation

closes #7294
This commit is contained in:
Matthias 2022-08-26 20:04:36 +02:00
parent 753d1b2aad
commit 01126c43f7
5 changed files with 40 additions and 26 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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
))

View File

@ -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,
))

View File

@ -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,
)