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