moved get_maintenance_ratio_and_amt to base.exchange. Wrote get_leverage_tiers. Added mmr_key to exchange._ft_has
This commit is contained in:
@@ -169,11 +169,11 @@ class Binance(Exchange):
|
||||
+ amt
|
||||
) if old_ratio else 0.0
|
||||
old_ratio = mm_ratio
|
||||
brackets.append([
|
||||
brackets.append((
|
||||
float(notional_floor),
|
||||
float(mm_ratio),
|
||||
amt,
|
||||
])
|
||||
))
|
||||
self._leverage_brackets[pair] = brackets
|
||||
except ccxt.DDoSProtection as e:
|
||||
raise DDosProtection(e) from e
|
||||
@@ -272,34 +272,6 @@ class Binance(Exchange):
|
||||
"""
|
||||
return open_date.minute > 0 or (open_date.minute == 0 and open_date.second > 15)
|
||||
|
||||
def get_maintenance_ratio_and_amt(
|
||||
self,
|
||||
pair: str,
|
||||
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 *
|
||||
difference between
|
||||
Maintenance Margin Rate on Level n and
|
||||
Maintenance Margin Rate on Level n-1)
|
||||
+ Maintenance Amount on Level n-1
|
||||
: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:
|
||||
raise InvalidOrderException(f"Cannot calculate liquidation price for {pair}")
|
||||
pair_brackets = self._leverage_brackets[pair]
|
||||
for [notional_floor, mm_ratio, amt] in reversed(pair_brackets):
|
||||
if nominal_value >= notional_floor:
|
||||
return (mm_ratio, amt)
|
||||
raise OperationalException("nominal value can not be lower than 0")
|
||||
# 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
|
||||
|
||||
def dry_run_liquidation_price(
|
||||
self,
|
||||
pair: str,
|
||||
|
@@ -73,7 +73,8 @@ class Exchange:
|
||||
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||
"mark_ohlcv_price": "mark",
|
||||
"mark_ohlcv_timeframe": "8h",
|
||||
"ccxt_futures_name": "swap"
|
||||
"ccxt_futures_name": "swap",
|
||||
"mmr_key": None,
|
||||
}
|
||||
_ft_has: Dict = {}
|
||||
|
||||
@@ -90,7 +91,7 @@ class Exchange:
|
||||
self._api: ccxt.Exchange = None
|
||||
self._api_async: ccxt_async.Exchange = None
|
||||
self._markets: Dict = {}
|
||||
self._leverage_brackets: Dict[str, List[List[float]]] = {}
|
||||
self._leverage_brackets: Dict[str, List[Tuple[float, float, Optional(float)]]] = {}
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
|
||||
@@ -2099,16 +2100,6 @@ class Exchange:
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_maintenance_ratio_and_amt(
|
||||
self,
|
||||
pair: str,
|
||||
nominal_value: Optional[float] = 0.0,
|
||||
) -> Tuple[float, Optional[float]]:
|
||||
"""
|
||||
:return: The maintenance margin ratio and maintenance amount
|
||||
"""
|
||||
raise OperationalException(self.name + ' does not support leverage futures trading')
|
||||
|
||||
def dry_run_liquidation_price(
|
||||
self,
|
||||
pair: str,
|
||||
@@ -2161,6 +2152,59 @@ class Exchange:
|
||||
raise OperationalException(
|
||||
"Freqtrade only supports isolated futures for leverage trading")
|
||||
|
||||
def get_leverage_tiers(self, pair: str):
|
||||
# When exchanges can load all their leverage brackets at once in the constructor
|
||||
# then this method does nothing, it should only be implemented when the leverage
|
||||
# brackets requires per symbol fetching to avoid excess api calls
|
||||
return None
|
||||
|
||||
def get_maintenance_ratio_and_amt(
|
||||
self,
|
||||
pair: str,
|
||||
nominal_value: Optional[float] = 0.0,
|
||||
) -> Tuple[float, Optional[float]]:
|
||||
"""
|
||||
:param pair: Market symbol
|
||||
:param nominal_value: The total trade amount in quote currency including leverage
|
||||
maintenance amount only on Binance
|
||||
:return: (maintenance margin ratio, maintenance amount)
|
||||
"""
|
||||
if nominal_value is None:
|
||||
raise OperationalException(
|
||||
f"nominal value is required for {self.name}.get_maintenance_ratio_and_amt"
|
||||
)
|
||||
if self._api.has['fetchLeverageTiers']:
|
||||
if pair not in self._leverage_brackets:
|
||||
# Used when fetchLeverageTiers cannot fetch all symbols at once
|
||||
tiers = self.get_leverage_tiers(pair)
|
||||
if not bool(tiers):
|
||||
raise InvalidOrderException(f"Cannot calculate liquidation price for {pair}")
|
||||
else:
|
||||
self._leverage_brackets[pair] = []
|
||||
for tier in tiers[pair]:
|
||||
self._leverage_brackets[pair].append((
|
||||
tier['notionalFloor'],
|
||||
tier['maintenanceMarginRatio'],
|
||||
None,
|
||||
))
|
||||
pair_brackets = self._leverage_brackets[pair]
|
||||
for (notional_floor, mm_ratio, amt) in reversed(pair_brackets):
|
||||
if nominal_value >= notional_floor:
|
||||
return (mm_ratio, amt)
|
||||
raise OperationalException("nominal value can not be lower than 0")
|
||||
# The lowest notional_floor for any pair in loadLeverageBrackets is always 0 because it
|
||||
# describes the min amt for a bracket, and the lowest bracket will always go down to 0
|
||||
else:
|
||||
info = self.markets[pair]['info']
|
||||
mmr_key = self._ft_has['mmr_key']
|
||||
if mmr_key and mmr_key in info:
|
||||
return (float(info[mmr_key]), None)
|
||||
else:
|
||||
raise OperationalException(
|
||||
f"Cannot fetch maintenance margin. Dry-run for freqtrade {self.trading_mode}"
|
||||
f"is not available for {self.name}"
|
||||
)
|
||||
|
||||
|
||||
def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool:
|
||||
return exchange_name in ccxt_exchanges(ccxt_module)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
""" Gate.io exchange subclass """
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from freqtrade.enums import MarginMode, TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
@@ -23,6 +23,7 @@ class Gateio(Exchange):
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 1000,
|
||||
"ohlcv_volume_currency": "quote",
|
||||
"mmr_key": "maintenance_rate",
|
||||
}
|
||||
|
||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
||||
@@ -40,14 +41,3 @@ class Gateio(Exchange):
|
||||
if any(v == 'market' for k, v in order_types.items()):
|
||||
raise OperationalException(
|
||||
f'Exchange {self.name} does not support market orders.')
|
||||
|
||||
def get_maintenance_ratio_and_amt(
|
||||
self,
|
||||
pair: str,
|
||||
nominal_value: Optional[float] = 0.0,
|
||||
) -> Tuple[float, Optional[float]]:
|
||||
"""
|
||||
:return: The maintenance margin ratio and maintenance amount
|
||||
"""
|
||||
info = self.markets[pair]['info']
|
||||
return (float(info['maintenance_rate']), None)
|
||||
|
@@ -25,7 +25,7 @@ class Okx(Exchange):
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||
# (TradingMode.FUTURES, MarginMode.CROSS),
|
||||
# (TradingMode.FUTURES, MarginMode.ISOLATED)
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
]
|
||||
|
||||
def _lev_prep(
|
||||
@@ -46,3 +46,6 @@ class Okx(Exchange):
|
||||
"mgnMode": self.margin_mode.value,
|
||||
"posSide": "long" if side == "buy" else "short",
|
||||
})
|
||||
|
||||
def get_leverage_tiers(self, pair: str):
|
||||
return self._api.fetch_leverage_tiers(pair)
|
||||
|
Reference in New Issue
Block a user