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): def get_liquidation_price(self, pair: str):
''' '''
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 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"): if self._config['dry_run'] or not self.exchange_has("fetchPositions"):
# Some exchanges only support one collateral type # Some exchanges only support one collateral type
@ -2003,6 +2003,14 @@ class Exchange:
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from 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: def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
return exchange_name in ccxt_exchanges(ccxt_module) 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, from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError) InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds 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.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.mixins import LoggingMixin from freqtrade.mixins import LoggingMixin
from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db
@ -609,24 +610,32 @@ class FreqtradeBot(LoggingMixin):
interest_rate = 0.0 interest_rate = 0.0
isolated_liq = None isolated_liq = None
# TODO-lev: Uncomment once liq and interest merged in
# 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
# ) # )
maintenance_amt, mm_rate = self.exchange.get_mm_amt_rate(pair, amount)
# if self.collateral_type == Collateral.ISOLATED: if self.collateral_type == Collateral.ISOLATED:
if self.config['dry_run']:
# isolated_liq = liquidation_price( isolated_liq = liquidation_price(
# exchange_name=self.exchange.name, exchange_name=self.exchange.name,
# trading_mode=self.trading_mode, open_rate=open_rate,
# open_rate=open_rate, is_short=is_short,
# amount=amount, leverage=leverage,
# leverage=leverage, trading_mode=self.trading_mode,
# is_short=is_short 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 return interest_rate, isolated_liq

View File

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

View File

@ -3582,6 +3582,24 @@ def test_calculate_funding_fees(
) == kraken_fee ) == 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', [ @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: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), ('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, -56354.57,
135365.00, 135365.00,
3683.979, 3683.979,
1456.84,
0.10, 0.10,
) is None ) is None
@ -89,23 +88,23 @@ def test_liquidation_price_exception_thrown(
@pytest.mark.parametrize( @pytest.mark.parametrize(
'exchange_name, open_rate, is_short, leverage, trading_mode, collateral, wallet_balance, ' 'exchange_name, is_short, leverage, trading_mode, collateral, wallet_balance, '
'mm_ex_1, upnl_ex_1, maintenance_amt, position, entry_price, ' 'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, '
'mm_rate, expected', '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), 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), 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), -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) -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, def test_liquidation_price(
wallet_balance, mm_ex_1, upnl_ex_1, exchange_name, open_rate, is_short, leverage, trading_mode, collateral, wallet_balance,
maintenance_amt, position, entry_price, mm_rate, mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_rate, expected
expected): ):
assert isclose(round(liquidation_price( assert isclose(round(liquidation_price(
exchange_name=exchange_name, exchange_name=exchange_name,
open_rate=open_rate, 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, upnl_ex_1=upnl_ex_1,
maintenance_amt=maintenance_amt, maintenance_amt=maintenance_amt,
position=position, position=position,
entry_price=entry_price,
mm_rate=mm_rate mm_rate=mm_rate
), 2), expected) ), 2), expected)