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:
parent
eee7271ab8
commit
ba02605d77
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
))
|
||||||
|
@ -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:
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user