exchange.liquidation_price methods combined, dry_run check on exchange for liquidation price
This commit is contained in:
parent
143c37d36f
commit
b8f4cebce7
@ -277,17 +277,15 @@ class Binance(Exchange):
|
|||||||
# The lowest notional_floor for any pair in loadLeverageBrackets is always 0 because it
|
# 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
|
# describes the min amount for a bracket, and the lowest bracket will always go down to 0
|
||||||
|
|
||||||
def liquidation_price(
|
def dry_run_liquidation_price(
|
||||||
self,
|
self,
|
||||||
|
pair: str,
|
||||||
open_rate: float, # Entry price of position
|
open_rate: float, # Entry price of position
|
||||||
is_short: bool,
|
is_short: bool,
|
||||||
mm_ratio: float,
|
|
||||||
position: float, # Absolute value of position size
|
position: float, # Absolute value of position size
|
||||||
wallet_balance: float, # Or margin balance
|
wallet_balance: float, # Or margin balance
|
||||||
taker_fee_rate: Optional[float] = None, # (Gateio & Okex)
|
mm_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
maintenance_amt: Optional[float] = None, # (Binance)
|
upnl_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
mm_ex_1: Optional[float] = 0.0, # (Binance) Cross only
|
|
||||||
upnl_ex_1: Optional[float] = 0.0, # (Binance) Cross only
|
|
||||||
) -> Optional[float]:
|
) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
MARGIN: https://www.binance.com/en/support/faq/f6b010588e55413aa58b7d63ee0125ed
|
MARGIN: https://www.binance.com/en/support/faq/f6b010588e55413aa58b7d63ee0125ed
|
||||||
@ -296,14 +294,10 @@ class Binance(Exchange):
|
|||||||
:param exchange_name:
|
:param exchange_name:
|
||||||
:param open_rate: (EP1) Entry price of position
|
:param open_rate: (EP1) 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 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 position: Absolute value of position size (in base currency)
|
||||||
:param maintenance_amt: (CUM) Maintenance Amount of position
|
|
||||||
:param wallet_balance: (WB)
|
:param wallet_balance: (WB)
|
||||||
Cross-Margin Mode: crossWalletBalance
|
Cross-Margin Mode: crossWalletBalance
|
||||||
Isolated-Margin Mode: isolatedWalletBalance
|
Isolated-Margin Mode: isolatedWalletBalance
|
||||||
:param taker_fee_rate: # * Not required by Binance
|
|
||||||
:param maintenance_amt:
|
:param maintenance_amt:
|
||||||
|
|
||||||
# * Only required for Cross
|
# * Only required for Cross
|
||||||
@ -314,30 +308,20 @@ class Binance(Exchange):
|
|||||||
Cross-Margin Mode: Unrealized PNL of all other contracts, excluding Contract 1.
|
Cross-Margin Mode: Unrealized PNL of all other contracts, excluding Contract 1.
|
||||||
Isolated-Margin Mode: 0
|
Isolated-Margin Mode: 0
|
||||||
"""
|
"""
|
||||||
if self.trading_mode == TradingMode.SPOT:
|
|
||||||
return None
|
|
||||||
elif (self.collateral is None):
|
|
||||||
raise OperationalException('Binance.collateral must be set for liquidation_price')
|
|
||||||
|
|
||||||
if (maintenance_amt is None):
|
|
||||||
raise OperationalException(
|
|
||||||
f"Parameter maintenance_amt is required by Binance.liquidation_price"
|
|
||||||
f"for {self.collateral.value} {self.trading_mode.value}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (self.collateral == Collateral.CROSS and (mm_ex_1 is None or upnl_ex_1 is None)):
|
|
||||||
raise OperationalException(
|
|
||||||
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
|
side_1 = -1 if is_short else 1
|
||||||
position = abs(position)
|
position = abs(position)
|
||||||
cross_vars = (
|
cross_vars = upnl_ex_1 - mm_ex_1 if self.collateral == Collateral.CROSS else 0.0
|
||||||
upnl_ex_1 - mm_ex_1 # type: ignore
|
|
||||||
if self.collateral == Collateral.CROSS else
|
# mm_ratio: Binance's formula specifies maintenance margin rate which is mm_ratio * 100%
|
||||||
0.0
|
# maintenance_amt: (CUM) Maintenance Amount of position
|
||||||
)
|
mm_ratio, maintenance_amt = self.get_maintenance_ratio_and_amt(pair, position)
|
||||||
|
|
||||||
|
if (maintenance_amt is None):
|
||||||
|
raise OperationalException(
|
||||||
|
"Parameter maintenance_amt is required by Binance.liquidation_price"
|
||||||
|
f"for {self.trading_mode.value}"
|
||||||
|
)
|
||||||
|
|
||||||
if self.trading_mode == TradingMode.FUTURES:
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
return (
|
return (
|
||||||
@ -348,6 +332,6 @@ class Binance(Exchange):
|
|||||||
(position * mm_ratio) - (side_1 * position)
|
(position * mm_ratio) - (side_1 * position)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f"Binance does not support {self.collateral.value} {self.trading_mode.value} trading")
|
"Freqtrade only supports isolated futures for leverage trading")
|
||||||
|
@ -1984,18 +1984,55 @@ class Exchange:
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def get_liquidation_price(self, pair: str):
|
def get_liquidation_price(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
# Dry-run
|
||||||
|
open_rate: Optional[float] = None, # Entry price of position
|
||||||
|
is_short: Optional[bool] = None,
|
||||||
|
position: Optional[float] = None, # Absolute value of position size
|
||||||
|
wallet_balance: Optional[float] = None, # Or margin balance
|
||||||
|
mm_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
|
upnl_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
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")
|
:param pair: base/quote currency pair (e.g. "ADA/USDT")
|
||||||
"""
|
"""
|
||||||
|
if self.trading_mode == TradingMode.SPOT:
|
||||||
|
return None
|
||||||
|
elif (self.collateral is None):
|
||||||
|
raise OperationalException(f'{self.name}.collateral must be set for liquidation_price')
|
||||||
|
elif (self.trading_mode != TradingMode.FUTURES and self.collateral != Collateral.ISOLATED):
|
||||||
|
raise OperationalException(
|
||||||
|
f"{self.name} does not support {self.collateral.value} {self.trading_mode.value}")
|
||||||
|
|
||||||
if self._config['dry_run'] or not self.exchange_has("fetchPositions"):
|
if self._config['dry_run'] or not self.exchange_has("fetchPositions"):
|
||||||
return
|
if (
|
||||||
|
open_rate is None or
|
||||||
|
is_short is None or
|
||||||
|
position is None or
|
||||||
|
wallet_balance is None
|
||||||
|
):
|
||||||
|
raise OperationalException(
|
||||||
|
f"Parameters open_rate, is_short, position, wallet_balance are"
|
||||||
|
f"required by {self.name}.liquidation_price for dry_run"
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.dry_run_liquidation_price(
|
||||||
|
pair=pair,
|
||||||
|
open_rate=open_rate,
|
||||||
|
is_short=is_short,
|
||||||
|
position=position,
|
||||||
|
wallet_balance=wallet_balance,
|
||||||
|
mm_ex_1=mm_ex_1,
|
||||||
|
upnl_ex_1=upnl_ex_1
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
positions = self._api.fetch_positions([pair])
|
positions = self._api.fetch_positions([pair])
|
||||||
position = positions[0]
|
pos = positions[0]
|
||||||
return position['liquidationPrice']
|
return pos['liquidationPrice']
|
||||||
except ccxt.DDoSProtection as e:
|
except ccxt.DDoSProtection as e:
|
||||||
raise DDosProtection(e) from e
|
raise DDosProtection(e) from e
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
@ -2014,17 +2051,15 @@ class Exchange:
|
|||||||
"""
|
"""
|
||||||
raise OperationalException(self.name + ' does not support leverage futures trading')
|
raise OperationalException(self.name + ' does not support leverage futures trading')
|
||||||
|
|
||||||
def liquidation_price(
|
def dry_run_liquidation_price(
|
||||||
self,
|
self,
|
||||||
|
pair: str,
|
||||||
open_rate: float, # Entry price of position
|
open_rate: float, # Entry price of position
|
||||||
is_short: bool,
|
is_short: bool,
|
||||||
mm_ratio: float,
|
|
||||||
position: float, # Absolute value of position size
|
position: float, # Absolute value of position size
|
||||||
wallet_balance: float, # Or margin balance
|
wallet_balance: float, # Or margin balance
|
||||||
taker_fee_rate: Optional[float] = None, # (Gateio & Okex)
|
mm_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
maintenance_amt: Optional[float] = None, # (Binance)
|
upnl_ex_1: float = 0.0, # (Binance) Cross only
|
||||||
mm_ex_1: Optional[float] = 0.0, # (Binance) Cross only
|
|
||||||
upnl_ex_1: Optional[float] = 0.0, # (Binance) Cross only
|
|
||||||
) -> Optional[float]:
|
) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
PERPETUAL:
|
PERPETUAL:
|
||||||
@ -2036,33 +2071,26 @@ class Exchange:
|
|||||||
: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 (in base currency)
|
:param position: Absolute value of position size (in base currency)
|
||||||
:param mm_ratio:
|
|
||||||
:param trading_mode: SPOT, MARGIN, FUTURES, etc.
|
:param trading_mode: SPOT, MARGIN, FUTURES, etc.
|
||||||
:param collateral: Either ISOLATED or CROSS
|
:param collateral: Either ISOLATED or CROSS
|
||||||
:param wallet_balance: Amount of collateral in the wallet being used to trade
|
:param wallet_balance: Amount of collateral in the wallet being used to trade
|
||||||
Cross-Margin Mode: crossWalletBalance
|
Cross-Margin Mode: crossWalletBalance
|
||||||
Isolated-Margin Mode: isolatedWalletBalance
|
Isolated-Margin Mode: isolatedWalletBalance
|
||||||
:param taker_fee_rate:
|
|
||||||
|
|
||||||
# * Not required by Gateio or OKX
|
# * Not required by Gateio or OKX
|
||||||
:param maintenance_amt:
|
|
||||||
:param mm_ex_1:
|
:param mm_ex_1:
|
||||||
:param upnl_ex_1:
|
:param upnl_ex_1:
|
||||||
"""
|
"""
|
||||||
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):
|
market = self.markets[pair]
|
||||||
raise OperationalException(
|
taker_fee_rate = market['taker']
|
||||||
f"Parameter taker_fee_rate is required by {self.name}.liquidation_price"
|
mm_ratio, _ = self.get_maintenance_ratio_and_amt(pair, position)
|
||||||
)
|
|
||||||
|
|
||||||
if self.trading_mode == TradingMode.FUTURES and self.collateral == Collateral.ISOLATED:
|
if self.trading_mode == TradingMode.FUTURES and self.collateral == Collateral.ISOLATED:
|
||||||
# if is_inverse:
|
|
||||||
# raise OperationalException(
|
if market['inverse']:
|
||||||
# "Freqtrade does not support inverse contracts at the moment")
|
raise OperationalException(
|
||||||
|
"Freqtrade does not yet support inverse contracts")
|
||||||
|
|
||||||
value = wallet_balance / position
|
value = wallet_balance / position
|
||||||
|
|
||||||
@ -2073,7 +2101,7 @@ class Exchange:
|
|||||||
return (open_rate - value) / (1 - mm_ratio_taker)
|
return (open_rate - value) / (1 - mm_ratio_taker)
|
||||||
else:
|
else:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f"{self.name} does not support {self.collateral.value} {self.trading_mode.value}")
|
"Freqtrade only supports isolated futures for leverage trading")
|
||||||
|
|
||||||
|
|
||||||
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
from freqtrade.enums import Collateral, TradingMode
|
from freqtrade.enums import Collateral, TradingMode
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ from freqtrade.edge import Edge
|
|||||||
from freqtrade.enums import (Collateral, RPCMessageType, RunMode, SellType, SignalDirection, State,
|
from freqtrade.enums import (Collateral, RPCMessageType, RunMode, SellType, SignalDirection, State,
|
||||||
TradingMode)
|
TradingMode)
|
||||||
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
|
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
|
||||||
InvalidOrderException, PricingError)
|
InvalidOrderException, OperationalException, PricingError)
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||||
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
|
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
@ -606,38 +606,31 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
is_short: bool
|
is_short: bool
|
||||||
) -> Tuple[float, Optional[float]]:
|
) -> Tuple[float, Optional[float]]:
|
||||||
|
|
||||||
interest_rate = 0.0
|
|
||||||
isolated_liq = None
|
|
||||||
|
|
||||||
# if TradingMode == TradingMode.MARGIN:
|
# if TradingMode == TradingMode.MARGIN:
|
||||||
# interest_rate = self.exchange.get_interest_rate(
|
# interest_rate = self.exchange.get_interest_rate(
|
||||||
# pair=pair,
|
# pair=pair,
|
||||||
# open_rate=open_rate,
|
# open_rate=open_rate,
|
||||||
# is_short=is_short
|
# is_short=is_short
|
||||||
# )
|
# )
|
||||||
|
if self.trading_mode == TradingMode.SPOT:
|
||||||
if self.collateral_type == Collateral.ISOLATED:
|
return (0.0, None)
|
||||||
if self.config['dry_run']:
|
elif (
|
||||||
mm_ratio, maintenance_amt = self.exchange.get_maintenance_ratio_and_amt(
|
self.collateral_type == Collateral.ISOLATED and
|
||||||
pair,
|
self.trading_mode == TradingMode.FUTURES
|
||||||
amount
|
):
|
||||||
)
|
isolated_liq = self.exchange.get_liquidation_price(
|
||||||
taker_fee_rate = self.exchange.markets[pair]['taker']
|
pair=pair,
|
||||||
isolated_liq = self.exchange.liquidation_price(
|
open_rate=open_rate,
|
||||||
open_rate=open_rate,
|
is_short=is_short,
|
||||||
is_short=is_short,
|
position=amount,
|
||||||
mm_ratio=mm_ratio,
|
wallet_balance=(amount * open_rate)/leverage, # TODO: Update for cross
|
||||||
position=amount,
|
mm_ex_1=0.0,
|
||||||
wallet_balance=(amount * open_rate)/leverage, # TODO: Update for cross
|
upnl_ex_1=0.0,
|
||||||
taker_fee_rate=taker_fee_rate,
|
)
|
||||||
maintenance_amt=maintenance_amt,
|
return (0.0, isolated_liq)
|
||||||
mm_ex_1=0.0,
|
else:
|
||||||
upnl_ex_1=0.0,
|
raise OperationalException(
|
||||||
)
|
"Freqtrade only supports isolated futures for leverage trading")
|
||||||
else:
|
|
||||||
isolated_liq = self.exchange.get_liquidation_price(pair)
|
|
||||||
|
|
||||||
return interest_rate, isolated_liq
|
|
||||||
|
|
||||||
def execute_entry(
|
def execute_entry(
|
||||||
self,
|
self,
|
||||||
|
@ -362,10 +362,7 @@ class LocalTrade():
|
|||||||
self.stop_loss_pct = -1 * abs(percent)
|
self.stop_loss_pct = -1 * abs(percent)
|
||||||
self.stoploss_last_update = datetime.utcnow()
|
self.stoploss_last_update = datetime.utcnow()
|
||||||
|
|
||||||
def set_isolated_liq(
|
def set_isolated_liq(self, isolated_liq: float):
|
||||||
self,
|
|
||||||
isolated_liq: float,
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Method you should use to set self.liquidation price.
|
Method you should use to set self.liquidation price.
|
||||||
Assures stop_loss is not passed the liquidation price
|
Assures stop_loss is not passed the liquidation price
|
||||||
|
@ -817,29 +817,42 @@ def get_markets():
|
|||||||
'symbol': 'ETH/USDT',
|
'symbol': 'ETH/USDT',
|
||||||
'base': 'ETH',
|
'base': 'ETH',
|
||||||
'quote': 'USDT',
|
'quote': 'USDT',
|
||||||
'spot': True,
|
'settle': 'USDT',
|
||||||
'future': True,
|
'baseId': 'ETH',
|
||||||
'swap': True,
|
'quoteId': 'USDT',
|
||||||
'margin': True,
|
'settleId': 'USDT',
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'spot': True,
|
||||||
|
'margin': True,
|
||||||
|
'swap': True,
|
||||||
|
'future': True,
|
||||||
|
'option': False,
|
||||||
|
'active': True,
|
||||||
|
'contract': True,
|
||||||
|
'linear': True,
|
||||||
|
'inverse': False,
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
'maker': 0.0002,
|
'maker': 0.0002,
|
||||||
|
'contractSize': 1,
|
||||||
|
'expiry': 1680220800000,
|
||||||
|
'expiryDateTime': '2023-03-31T00:00:00.000Z',
|
||||||
|
'strike': None,
|
||||||
|
'optionType': None,
|
||||||
'precision': {
|
'precision': {
|
||||||
'amount': 8,
|
'amount': 8,
|
||||||
'price': 8
|
'price': 8,
|
||||||
},
|
},
|
||||||
'limits': {
|
'limits': {
|
||||||
|
'leverage': {
|
||||||
|
'min': 1,
|
||||||
|
'max': 100,
|
||||||
|
},
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.02214286,
|
'min': 0.02214286,
|
||||||
'max': None
|
'max': None,
|
||||||
},
|
},
|
||||||
'price': {
|
'price': {
|
||||||
'min': 1e-08,
|
'min': 1e-08,
|
||||||
'max': None
|
|
||||||
},
|
|
||||||
'leverage': {
|
|
||||||
'min': None,
|
|
||||||
'max': None,
|
'max': None,
|
||||||
},
|
},
|
||||||
'cost': {
|
'cost': {
|
||||||
@ -847,8 +860,9 @@ def get_markets():
|
|||||||
'max': None,
|
'max': None,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'active': True,
|
'info': {
|
||||||
'info': {},
|
'maintenance_rate': '0.005',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'LTC/USDT': {
|
'LTC/USDT': {
|
||||||
'id': 'USDT-LTC',
|
'id': 'USDT-LTC',
|
||||||
@ -1110,7 +1124,6 @@ def get_markets():
|
|||||||
'swap': True,
|
'swap': True,
|
||||||
'futures': False,
|
'futures': False,
|
||||||
'option': False,
|
'option': False,
|
||||||
'derivative': True,
|
|
||||||
'contract': True,
|
'contract': True,
|
||||||
'linear': True,
|
'linear': True,
|
||||||
'inverse': False,
|
'inverse': False,
|
||||||
|
@ -3975,13 +3975,13 @@ def test__amount_to_contracts(
|
|||||||
|
|
||||||
@pytest.mark.parametrize('exchange_name,open_rate,is_short,leverage,trading_mode,collateral', [
|
@pytest.mark.parametrize('exchange_name,open_rate,is_short,leverage,trading_mode,collateral', [
|
||||||
# Bittrex
|
# Bittrex
|
||||||
('bittrex', 2.0, False, 3.0, spot, None),
|
('bittrex', 2.0, False, 3.0, 'spot', None),
|
||||||
('bittrex', 2.0, False, 1.0, spot, cross),
|
('bittrex', 2.0, False, 1.0, 'spot', 'cross'),
|
||||||
('bittrex', 2.0, True, 3.0, spot, isolated),
|
('bittrex', 2.0, True, 3.0, 'spot', 'isolated'),
|
||||||
# Binance
|
# Binance
|
||||||
('binance', 2.0, False, 3.0, spot, None),
|
('binance', 2.0, False, 3.0, 'spot', None),
|
||||||
('binance', 2.0, False, 1.0, spot, cross),
|
('binance', 2.0, False, 1.0, 'spot', 'cross'),
|
||||||
('binance', 2.0, True, 3.0, spot, isolated),
|
('binance', 2.0, True, 3.0, 'spot', 'isolated'),
|
||||||
])
|
])
|
||||||
def test_liquidation_price_is_none(
|
def test_liquidation_price_is_none(
|
||||||
mocker,
|
mocker,
|
||||||
@ -3996,14 +3996,12 @@ def test_liquidation_price_is_none(
|
|||||||
default_conf['trading_mode'] = trading_mode
|
default_conf['trading_mode'] = trading_mode
|
||||||
default_conf['collateral'] = collateral
|
default_conf['collateral'] = collateral
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
assert exchange.liquidation_price(
|
assert exchange.get_liquidation_price(
|
||||||
|
pair='DOGE/USDT',
|
||||||
open_rate=open_rate,
|
open_rate=open_rate,
|
||||||
is_short=is_short,
|
is_short=is_short,
|
||||||
mm_ratio=1535443.01,
|
|
||||||
position=71200.81144,
|
position=71200.81144,
|
||||||
wallet_balance=-56354.57,
|
wallet_balance=-56354.57,
|
||||||
taker_fee_rate=0.01,
|
|
||||||
maintenance_amt=3683.979,
|
|
||||||
mm_ex_1=0.10,
|
mm_ex_1=0.10,
|
||||||
upnl_ex_1=0.0
|
upnl_ex_1=0.0
|
||||||
) is None
|
) is None
|
||||||
@ -4014,13 +4012,13 @@ def test_liquidation_price_is_none(
|
|||||||
'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, '
|
'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, '
|
||||||
'mm_ratio, expected',
|
'mm_ratio, expected',
|
||||||
[
|
[
|
||||||
("binance", False, 1, futures, isolated, 1535443.01, 0.0,
|
("binance", False, 1, 'futures', 'isolated', 1535443.01, 0.0,
|
||||||
0.0, 135365.00, 3683.979, 1456.84, 0.10, 1114.78),
|
0.0, 135365.00, 3683.979, 1456.84, 0.10, 1114.78),
|
||||||
("binance", False, 1, futures, isolated, 1535443.01, 0.0,
|
("binance", False, 1, 'futures', 'isolated', 1535443.01, 0.0,
|
||||||
0.0, 16300.000, 109.488, 32481.980, 0.025, 18778.73),
|
0.0, 16300.000, 109.488, 32481.980, 0.025, 18778.73),
|
||||||
("binance", False, 1, futures, cross, 1535443.01, 71200.81144,
|
("binance", False, 1, 'futures', 'cross', 1535443.01, 71200.81144,
|
||||||
-56354.57, 135365.00, 3683.979, 1456.84, 0.10, 1153.26),
|
-56354.57, 135365.00, 3683.979, 1456.84, 0.10, 1153.26),
|
||||||
("binance", False, 1, futures, cross, 1535443.01, 356512.508,
|
("binance", False, 1, 'futures', 'cross', 1535443.01, 356512.508,
|
||||||
-448192.89, 16300.000, 109.488, 32481.980, 0.025, 26316.89)
|
-448192.89, 16300.000, 109.488, 32481.980, 0.025, 26316.89)
|
||||||
])
|
])
|
||||||
def test_liquidation_price(
|
def test_liquidation_price(
|
||||||
@ -4030,13 +4028,13 @@ def test_liquidation_price(
|
|||||||
default_conf['trading_mode'] = trading_mode
|
default_conf['trading_mode'] = trading_mode
|
||||||
default_conf['collateral'] = collateral
|
default_conf['collateral'] = collateral
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
assert isclose(round(exchange.liquidation_price(
|
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt))
|
||||||
|
assert isclose(round(exchange.get_liquidation_price(
|
||||||
|
pair='DOGE/USDT',
|
||||||
open_rate=open_rate,
|
open_rate=open_rate,
|
||||||
is_short=is_short,
|
is_short=is_short,
|
||||||
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,
|
||||||
maintenance_amt=maintenance_amt,
|
|
||||||
position=position,
|
position=position,
|
||||||
mm_ratio=mm_ratio
|
|
||||||
), 2), expected)
|
), 2), expected)
|
||||||
|
@ -735,11 +735,11 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
||||||
((2 + 0.01) - (1 * 1 * 10)) / ((1 * 0.01) - (1 * 1)) = 8.070707070707071
|
((2 + 0.01) - (1 * 1 * 10)) / ((1 * 0.01) - (1 * 1)) = 8.070707070707071
|
||||||
|
|
||||||
exchange_name = gateio, is_short = true
|
exchange_name = gateio/okex, is_short = true
|
||||||
(open_rate + (wallet_balance / position)) / (1 + (mm_ratio + taker_fee_rate))
|
(open_rate + (wallet_balance / position)) / (1 + (mm_ratio + taker_fee_rate))
|
||||||
(10 + (2 / 1)) / (1 + (0.01 + 0.0006)) = 11.87413417771621
|
(10 + (2 / 1)) / (1 + (0.01 + 0.0006)) = 11.87413417771621
|
||||||
|
|
||||||
exchange_name = gateio, is_short = false
|
exchange_name = gateio/okex, is_short = false
|
||||||
(open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
|
(open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
|
||||||
(10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
|
(10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
|
||||||
"""
|
"""
|
||||||
@ -747,10 +747,12 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
order = limit_order[enter_side(is_short)]
|
order = limit_order[enter_side(is_short)]
|
||||||
default_conf_usdt['trading_mode'] = trading_mode
|
default_conf_usdt['trading_mode'] = trading_mode
|
||||||
leverage = 1.0 if trading_mode == 'spot' else 5.0
|
leverage = 1.0 if trading_mode == 'spot' else 5.0
|
||||||
|
default_conf_usdt['exchange']['name'] = exchange_name
|
||||||
if margin_mode:
|
if margin_mode:
|
||||||
default_conf_usdt['collateral'] = margin_mode
|
default_conf_usdt['collateral'] = margin_mode
|
||||||
|
mocker.patch('freqtrade.exchange.Gateio.validate_ordertypes')
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker, id=exchange_name)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
|
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
|
||||||
freqtrade.strategy.leverage = MagicMock(return_value=leverage)
|
freqtrade.strategy.leverage = MagicMock(return_value=leverage)
|
||||||
|
Loading…
Reference in New Issue
Block a user