Isolated liq branch passes all tests and has the general structure that it is supposed to, but is patchy, and doesnt get the correct maintenance amt and maintenance margin rate yet

This commit is contained in:
Sam Germain 2021-11-05 19:27:06 -06:00
parent eee7271ab8
commit ba02605d77
6 changed files with 103 additions and 44 deletions

View File

@ -1987,7 +1987,7 @@ class Exchange:
def get_liquidation_price(self, pair: str):
'''
Set's the margin mode on the exchange to cross or isolated for a specific pair
:param symbol: base/quote currency pair (e.g. "ADA/USDT")
:param pair: base/quote currency pair (e.g. "ADA/USDT")
'''
if self._config['dry_run'] or not self.exchange_has("fetchPositions"):
# Some exchanges only support one collateral type
@ -2003,6 +2003,14 @@ class Exchange:
except ccxt.BaseError as e:
raise OperationalException(e) from e
@retrier
def get_mm_amt_rate(self, pair: str, amount: float):
'''
:return: The maintenance amount, and maintenance margin rate
'''
# TODO-lev: return the real amounts
return 0, 0.4
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
return exchange_name in ccxt_exchanges(ccxt_module)

View File

@ -21,6 +21,7 @@ from freqtrade.enums import (Collateral, RPCMessageType, RunMode, SellType, Sign
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.leverage import liquidation_price
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.mixins import LoggingMixin
from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db
@ -609,24 +610,32 @@ class FreqtradeBot(LoggingMixin):
interest_rate = 0.0
isolated_liq = None
# TODO-lev: Uncomment once liq and interest merged in
# if TradingMode == TradingMode.MARGIN:
# interest_rate = self.exchange.get_interest_rate(
# pair=pair,
# open_rate=open_rate,
# is_short=is_short
# )
maintenance_amt, mm_rate = self.exchange.get_mm_amt_rate(pair, amount)
# if self.collateral_type == Collateral.ISOLATED:
# isolated_liq = liquidation_price(
# exchange_name=self.exchange.name,
# trading_mode=self.trading_mode,
# open_rate=open_rate,
# amount=amount,
# leverage=leverage,
# is_short=is_short
# )
if self.collateral_type == Collateral.ISOLATED:
if self.config['dry_run']:
isolated_liq = liquidation_price(
exchange_name=self.exchange.name,
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,
position=amount * open_rate,
wallet_balance=amount/leverage, # TODO-lev: Is this correct?
maintenance_amt=maintenance_amt,
mm_rate=mm_rate,
)
else:
isolated_liq = self.exchange.get_liquidation_price(pair)
return interest_rate, isolated_liq

View File

@ -16,7 +16,6 @@ def liquidation_price(
upnl_ex_1: Optional[float],
maintenance_amt: Optional[float],
position: Optional[float],
entry_price: Optional[float],
mm_rate: Optional[float]
) -> Optional[float]:
if trading_mode == TradingMode.SPOT:
@ -29,17 +28,33 @@ def liquidation_price(
)
if exchange_name.lower() == "binance":
if None in [wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, position, entry_price,
mm_rate]:
if (
wallet_balance is None or
mm_ex_1 is None or
upnl_ex_1 is None or
maintenance_amt is None or
position is None or
mm_rate is None
):
raise OperationalException(
f"Parameters wallet_balance, mm_ex_1, upnl_ex_1, "
f"maintenance_amt, position, entry_price, mm_rate "
f"maintenance_amt, position, mm_rate "
f"is required by liquidation_price when exchange is {exchange_name.lower()}")
# Suppress incompatible type "Optional[float]"; expected "float" as the check exists above.
return binance(open_rate, is_short, leverage, trading_mode, collateral, # type: ignore
wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, # type: ignore
position, entry_price, mm_rate) # type: ignore
return binance(
open_rate=open_rate,
is_short=is_short,
leverage=leverage,
trading_mode=trading_mode,
collateral=collateral, # type: ignore
wallet_balance=wallet_balance,
mm_ex_1=mm_ex_1,
upnl_ex_1=upnl_ex_1,
maintenance_amt=maintenance_amt, # type: ignore
position=position,
mm_rate=mm_rate,
) # type: ignore
elif exchange_name.lower() == "kraken":
return kraken(open_rate, is_short, leverage, trading_mode, collateral)
elif exchange_name.lower() == "ftx":
@ -76,12 +91,10 @@ def binance(
upnl_ex_1: float,
maintenance_amt: float,
position: float,
entry_price: float,
mm_rate: float,
):
"""
Calculates the liquidation price on Binance
:param open_rate: open_rate
:param is_short: true or false
:param leverage: leverage in float
:param trading_mode: spot, margin, futures
@ -100,7 +113,7 @@ def binance(
:param position: Absolute value of position size (one-way mode)
:param entry_price: Entry Price of position (one-way mode)
:param open_rate: Entry Price of position (one-way mode)
:param mm_rate: Maintenance margin rate of position (one-way mode)
@ -112,7 +125,7 @@ def binance(
cum_b = maintenance_amt
side_1 = -1 if is_short else 1
position = abs(position)
ep1 = entry_price
ep1 = open_rate
mmr_b = mm_rate
if trading_mode == TradingMode.MARGIN and collateral == Collateral.CROSS:
@ -189,6 +202,17 @@ def ftx(
if __name__ == '__main__':
print(liquidation_price("binance", 0.0, False, 1, TradingMode.FUTURES, Collateral.ISOLATED,
1535443.01, 356512.508,
0.0, 16300.000, 109.488, 32481.980, 0.025))
print(liquidation_price(
"binance",
32481.980,
False,
1,
TradingMode.FUTURES,
Collateral.ISOLATED,
1535443.01,
356512.508,
0.0,
16300.000,
109.488,
0.025
))

View File

@ -365,9 +365,11 @@ class LocalTrade():
def set_isolated_liq(
self,
isolated_liq: Optional[float],
wallet_balance: Optional[float],
current_price: Optional[float]
isolated_liq: Optional[float] = None,
wallet_balance: Optional[float] = None,
current_price: Optional[float] = None,
maintenance_amt: Optional[float] = None,
mm_rate: Optional[float] = None,
):
"""
Method you should use to set self.liquidation price.
@ -389,9 +391,9 @@ class LocalTrade():
mm_ex_1=0.0,
upnl_ex_1=0.0,
position=self.amount * current_price,
wallet_balance=wallet_balance,
# TODO-lev: maintenance_amt,
# TODO-lev: mm_rate,
wallet_balance=self.amount / self.leverage, # TODO-lev: Is this correct?
maintenance_amt=maintenance_amt,
mm_rate=mm_rate,
)
if isolated_liq is None:

View File

@ -3582,6 +3582,24 @@ def test_calculate_funding_fees(
) == kraken_fee
def test_get_liquidation_price(mocker, default_conf):
api_mock = MagicMock()
api_mock.fetch_positions = MagicMock()
type(api_mock).has = PropertyMock(return_value={'fetchPositions': True})
default_conf['dry_run'] = False
ccxt_exceptionhandlers(
mocker,
default_conf,
api_mock,
"binance",
"get_liquidation_price",
"fetch_positions",
pair="XRP/USDT"
)
@pytest.mark.parametrize('exchange,rate_start,rate_end,d1,d2,amount,expected_fees', [
('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.0009140999999999999),
('binance', 0, 2, "2021-09-01 00:00:15", "2021-09-01 08:00:00", 30.0, -0.0009140999999999999),

View File

@ -54,7 +54,6 @@ def test_liquidation_price_is_none(
-56354.57,
135365.00,
3683.979,
1456.84,
0.10,
) is None
@ -89,23 +88,23 @@ def test_liquidation_price_exception_thrown(
@pytest.mark.parametrize(
'exchange_name, open_rate, is_short, leverage, trading_mode, collateral, wallet_balance, '
'mm_ex_1, upnl_ex_1, maintenance_amt, position, entry_price, '
'exchange_name, is_short, leverage, trading_mode, collateral, wallet_balance, '
'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, '
'mm_rate, expected',
[
("binance", 0.0, False, 1, TradingMode.FUTURES, Collateral.ISOLATED, 1535443.01, 0.0,
("binance", False, 1, TradingMode.FUTURES, Collateral.ISOLATED, 1535443.01, 0.0,
0.0, 135365.00, 3683.979, 1456.84, 0.10, 1114.78),
("binance", 0.0, False, 1, TradingMode.FUTURES, Collateral.ISOLATED, 1535443.01, 0.0,
("binance", False, 1, TradingMode.FUTURES, Collateral.ISOLATED, 1535443.01, 0.0,
0.0, 16300.000, 109.488, 32481.980, 0.025, 18778.73),
("binance", 0.0, False, 1, TradingMode.FUTURES, Collateral.CROSS, 1535443.01, 71200.81144,
("binance", False, 1, TradingMode.FUTURES, Collateral.CROSS, 1535443.01, 71200.81144,
-56354.57, 135365.00, 3683.979, 1456.84, 0.10, 1153.26),
("binance", 0.0, False, 1, TradingMode.FUTURES, Collateral.CROSS, 1535443.01, 356512.508,
("binance", False, 1, TradingMode.FUTURES, Collateral.CROSS, 1535443.01, 356512.508,
-448192.89, 16300.000, 109.488, 32481.980, 0.025, 26316.89)
])
def test_liquidation_price(exchange_name, open_rate, is_short, leverage, trading_mode, collateral,
wallet_balance, mm_ex_1, upnl_ex_1,
maintenance_amt, position, entry_price, mm_rate,
expected):
def test_liquidation_price(
exchange_name, open_rate, is_short, leverage, trading_mode, collateral, wallet_balance,
mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_rate, expected
):
assert isclose(round(liquidation_price(
exchange_name=exchange_name,
open_rate=open_rate,
@ -118,6 +117,5 @@ def test_liquidation_price(exchange_name, open_rate, is_short, leverage, trading
upnl_ex_1=upnl_ex_1,
maintenance_amt=maintenance_amt,
position=position,
entry_price=entry_price,
mm_rate=mm_rate
), 2), expected)