exchange.fill_leverage_brackets/get_maintenance_ratio_and_amt docstring and type specification

This commit is contained in:
Sam Germain 2022-01-14 06:11:17 -06:00
parent b4a0611afc
commit bb2b2211d0
8 changed files with 57 additions and 31 deletions

View File

@ -119,10 +119,25 @@ class Binance(Exchange):
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier @retrier
def fill_leverage_brackets(self): def fill_leverage_brackets(self) -> None:
""" """
Assigns property _leverage_brackets to a dictionary of information about the leverage Assigns property _leverage_brackets to a dictionary of information about the leverage
allowed on each pair allowed on each pair
After exectution, self._leverage_brackets = {
"pair_name": [
[notional_floor, maintenenace_margin_ratio, maintenance_amt],
...
],
...
}
e.g. {
"ETH/USDT:USDT": [
[0.0, 0.01, 0.0],
[10000, 0.02, 0.01],
...
],
...
}
""" """
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
try: try:
@ -136,14 +151,14 @@ class Binance(Exchange):
leverage_brackets = self._api.load_leverage_brackets() leverage_brackets = self._api.load_leverage_brackets()
for pair, brkts in leverage_brackets.items(): for pair, brkts in leverage_brackets.items():
[amt, old_ratio] = [None, None] [amt, old_ratio] = [0.0, 0.0]
brackets = [] brackets = []
for [notional_floor, mm_ratio] in brkts: for [notional_floor, mm_ratio] in brkts:
amt = ( amt = (
( (
(float(notional_floor) * (float(mm_ratio)) - float(old_ratio)) (float(notional_floor) * (float(mm_ratio)) - float(old_ratio))
) + amt ) + amt
) if old_ratio else 0 ) if old_ratio else 0.0
old_ratio = mm_ratio old_ratio = mm_ratio
brackets.append([ brackets.append([
float(notional_floor), float(notional_floor),
@ -167,6 +182,9 @@ class Binance(Exchange):
""" """
if pair not in self._leverage_brackets: if pair not in self._leverage_brackets:
return 1.0 return 1.0
if (pair is None or nominal_value is None):
raise OperationalException(
"binance.get_max_leverage requires parameters pair and nominal_value")
pair_brackets = self._leverage_brackets[pair] pair_brackets = self._leverage_brackets[pair]
for [notional_floor, mm_ratio, _] in reversed(pair_brackets): for [notional_floor, mm_ratio, _] in reversed(pair_brackets):
if nominal_value >= notional_floor: if nominal_value >= notional_floor:
@ -236,15 +254,20 @@ class Binance(Exchange):
self, self,
pair: str, pair: str,
nominal_value: Optional[float] = 0.0, nominal_value: Optional[float] = 0.0,
): ) -> Tuple[float, Optional[float]]:
""" """
Formula: https://www.binance.com/en/support/faq/b3c689c1f50a44cabb3a84e663b81d93
Maintenance amt = Floor of Position Bracket on Level n * Maintenance amt = Floor of Position Bracket on Level n *
difference between difference between
Maintenance Margin Rate on Level n and Maintenance Margin Rate on Level n and
Maintenance Margin Rate on Level n-1) Maintenance Margin Rate on Level n-1)
+ Maintenance Amount on Level n-1 + Maintenance Amount on Level n-1
https://www.binance.com/en/support/faq/b3c689c1f50a44cabb3a84e663b81d93 :return: The maintenance margin ratio and maintenance amount
""" """
if nominal_value is None:
raise OperationalException(
"nominal value is required for binance.get_maintenance_ratio_and_amt")
if pair not in self._leverage_brackets: if pair not in self._leverage_brackets:
raise InvalidOrderException(f"Cannot calculate liquidation price for {pair}") raise InvalidOrderException(f"Cannot calculate liquidation price for {pair}")
pair_brackets = self._leverage_brackets[pair] pair_brackets = self._leverage_brackets[pair]

View File

@ -90,7 +90,7 @@ class Exchange:
self._api: ccxt.Exchange = None self._api: ccxt.Exchange = None
self._api_async: ccxt_async.Exchange = None self._api_async: ccxt_async.Exchange = None
self._markets: Dict = {} self._markets: Dict = {}
self._leverage_brackets: Dict = {} self._leverage_brackets: Dict[str, List[List[float]]] = {}
self.loop = asyncio.new_event_loop() self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop) asyncio.set_event_loop(self.loop)
@ -2006,12 +2006,12 @@ class Exchange:
self, self,
pair: str, pair: str,
nominal_value: Optional[float] = 0.0, nominal_value: Optional[float] = 0.0,
): ) -> Tuple[float, Optional[float]]:
""" """
:return: The maintenance amount, and maintenance margin rate :return: The maintenance margin ratio and maintenance amount
""" """
# TODO-lev: return the real amounts # TODO-lev: return the real amounts
return 0, 0.4 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:

View File

@ -45,9 +45,9 @@ class Gateio(Exchange):
self, self,
pair: str, pair: str,
nominal_value: Optional[float] = 0.0, nominal_value: Optional[float] = 0.0,
): ) -> Tuple[float, Optional[float]]:
"""
:return: The maintenance margin ratio and maintenance amount
"""
info = self.markets[pair]['info'] info = self.markets[pair]['info']
if 'maintenance_rate' in info: return (float(info['maintenance_rate']), None)
return [float(info['maintenance_rate']), None]
else:
return [None, None]

View File

@ -620,7 +620,9 @@ class FreqtradeBot(LoggingMixin):
if self.collateral_type == Collateral.ISOLATED: if self.collateral_type == Collateral.ISOLATED:
if self.config['dry_run']: if self.config['dry_run']:
mm_ratio, maintenance_amt = self.exchange.get_maintenance_ratio_and_amt( mm_ratio, maintenance_amt = self.exchange.get_maintenance_ratio_and_amt(
pair, amount) pair,
amount
)
taker_fee_rate = self.exchange.markets[pair]['taker'] taker_fee_rate = self.exchange.markets[pair]['taker']
isolated_liq = liquidation_price( isolated_liq = liquidation_price(
exchange_name=self.exchange.name, exchange_name=self.exchange.name,
@ -637,7 +639,7 @@ class FreqtradeBot(LoggingMixin):
mm_ratio=mm_ratio, mm_ratio=mm_ratio,
taker_fee_rate=taker_fee_rate taker_fee_rate=taker_fee_rate
# Okex # TODO-lev: Okex parameters
# liability: Optional[float]=None, # liability: Optional[float]=None,
# interest: Optional[float]=None, # interest: Optional[float]=None,
# position_assets: Optional[float]=None, # * Might be same as position # position_assets: Optional[float]=None, # * Might be same as position

View File

@ -337,7 +337,8 @@ def kraken(
): ):
""" """
# ! Not Implemented # ! Not Implemented
MARGIN: https://support.kraken.com/hc/en-us/articles/203325763-Margin-Call-Level-and-Margin-Liquidation-Level MARGIN:
https://support.kraken.com/hc/en-us/articles/203325763-Margin-Call-Level-and-Margin-Liquidation-Level
: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

View File

@ -407,7 +407,7 @@ def test_get_maintenance_ratio_and_amt_binance(
pair, pair,
nominal_value, nominal_value,
mm_ratio, mm_ratio,
amt amt,
): ):
exchange = get_patched_exchange(mocker, default_conf, id="binance") exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange._leverage_brackets = { exchange._leverage_brackets = {
@ -436,4 +436,4 @@ def test_get_maintenance_ratio_and_amt_binance(
[200000000.0, 0.25], [200000000.0, 0.25],
[300000000.0, 0.5]], [300000000.0, 0.5]],
} }
assert exchange.get_max_leverage(pair, nominal_value) == max_lev assert exchange.get_maintenance_ratio_and_amt(pair, nominal_value) == (mm_ratio, amt)

View File

@ -34,7 +34,7 @@ def test_validate_order_types_gateio(default_conf, mocker):
@pytest.mark.parametrize('pair,mm_ratio', [ @pytest.mark.parametrize('pair,mm_ratio', [
("ETH/USDT:USDT", 0.005), ("ETH/USDT:USDT", 0.005),
("ADA/USDT:USDT", 0.003), ("ADA/USDT:USDT", 0.003),
("DOGE/USDT:USDT", None), # ("DOGE/USDT:USDT", None),
]) ])
def test_get_maintenance_ratio_and_amt_gateio(default_conf, mocker, pair, mm_ratio): def test_get_maintenance_ratio_and_amt_gateio(default_conf, mocker, pair, mm_ratio):
api_mock = MagicMock() api_mock = MagicMock()
@ -61,16 +61,16 @@ def test_get_maintenance_ratio_and_amt_gateio(default_conf, mocker, pair, mm_rat
'id': 'ADA_USDT', 'id': 'ADA_USDT',
'symbol': 'ADA/USDT:USDT', 'symbol': 'ADA/USDT:USDT',
}, },
'DOGE/USDT:USDT': { # 'DOGE/USDT:USDT': {
'taker': 0.0000075, # 'taker': 0.0000075,
'maker': -0.0000025, # 'maker': -0.0000025,
'info': { # 'info': {
'nonmaintenance_rate': '0.003', # 'nonmaintenance_rate': '0.003',
}, # },
'id': 'DOGE_USDT', # 'id': 'DOGE_USDT',
'symbol': 'DOGE/USDT:USDT', # 'symbol': 'DOGE/USDT:USDT',
} # }
} }
) )
) )
assert exchange.get_maintenance_ratio_and_amt(pair) == [mm_ratio, None] assert exchange.get_maintenance_ratio_and_amt(pair) == (mm_ratio, None)

View File

@ -913,7 +913,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
name=exchange_name, name=exchange_name,
get_maintenance_ratio_and_amt=MagicMock(return_value=[0.01, 0.01]) get_maintenance_ratio_and_amt=MagicMock(return_value=(0.01, 0.01))
) )
order['status'] = 'open' order['status'] = 'open'
order['id'] = '5568' order['id'] = '5568'