commit
f0cbc47bb1
@ -128,91 +128,6 @@ class Binance(Exchange):
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
@retrier
|
|
||||||
def fill_leverage_brackets(self) -> None:
|
|
||||||
"""
|
|
||||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
|
||||||
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:
|
|
||||||
try:
|
|
||||||
if self._config['dry_run']:
|
|
||||||
leverage_brackets_path = (
|
|
||||||
Path(__file__).parent / 'binance_leverage_brackets.json'
|
|
||||||
)
|
|
||||||
with open(leverage_brackets_path) as json_file:
|
|
||||||
leverage_brackets = json.load(json_file)
|
|
||||||
else:
|
|
||||||
leverage_brackets = self._api.load_leverage_brackets()
|
|
||||||
|
|
||||||
for pair, brkts in leverage_brackets.items():
|
|
||||||
[amt, old_ratio] = [0.0, 0.0]
|
|
||||||
brackets = []
|
|
||||||
for [notional_floor, mm_ratio] in brkts:
|
|
||||||
amt = (
|
|
||||||
(float(notional_floor) * (float(mm_ratio) - float(old_ratio)))
|
|
||||||
+ amt
|
|
||||||
) if old_ratio else 0.0
|
|
||||||
old_ratio = mm_ratio
|
|
||||||
brackets.append([
|
|
||||||
float(notional_floor),
|
|
||||||
float(mm_ratio),
|
|
||||||
amt,
|
|
||||||
])
|
|
||||||
self._leverage_brackets[pair] = brackets
|
|
||||||
except ccxt.DDoSProtection as e:
|
|
||||||
raise DDosProtection(e) from e
|
|
||||||
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
|
||||||
raise TemporaryError(f'Could not fetch leverage amounts due to'
|
|
||||||
f'{e.__class__.__name__}. Message: {e}') from e
|
|
||||||
except ccxt.BaseError as e:
|
|
||||||
raise OperationalException(e) from e
|
|
||||||
|
|
||||||
def get_max_leverage(self, pair: str, stake_amount: Optional[float]) -> float:
|
|
||||||
"""
|
|
||||||
Returns the maximum leverage that a pair can be traded at
|
|
||||||
:param pair: The base/quote currency pair being traded
|
|
||||||
:stake_amount: The total value of the traders margin_mode in quote currency
|
|
||||||
"""
|
|
||||||
if stake_amount is None:
|
|
||||||
raise OperationalException('binance.get_max_leverage requires argument stake_amount')
|
|
||||||
if pair not in self._leverage_brackets:
|
|
||||||
return 1.0
|
|
||||||
pair_brackets = self._leverage_brackets[pair]
|
|
||||||
num_brackets = len(pair_brackets)
|
|
||||||
min_amount = 0.0
|
|
||||||
for bracket_num in range(num_brackets):
|
|
||||||
[notional_floor, mm_ratio, _] = pair_brackets[bracket_num]
|
|
||||||
lev = 1.0
|
|
||||||
if mm_ratio != 0:
|
|
||||||
lev = 1.0/mm_ratio
|
|
||||||
else:
|
|
||||||
logger.warning(f"mm_ratio for {pair} with notional floor {notional_floor} is 0")
|
|
||||||
if bracket_num+1 != num_brackets: # If not on last bracket
|
|
||||||
[min_amount, _, __] = pair_brackets[bracket_num+1] # Get min_amount of next bracket
|
|
||||||
else:
|
|
||||||
return lev
|
|
||||||
nominal_value = stake_amount * lev
|
|
||||||
# Bracket is good if the leveraged trade value doesnt exceed min_amount of next bracket
|
|
||||||
if nominal_value < min_amount:
|
|
||||||
return lev
|
|
||||||
return 1.0 # default leverage
|
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def _set_leverage(
|
def _set_leverage(
|
||||||
self,
|
self,
|
||||||
@ -272,34 +187,6 @@ class Binance(Exchange):
|
|||||||
"""
|
"""
|
||||||
return open_date.minute > 0 or (open_date.minute == 0 and open_date.second > 15)
|
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(
|
def dry_run_liquidation_price(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
@ -358,3 +245,25 @@ class Binance(Exchange):
|
|||||||
else:
|
else:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
"Freqtrade only supports isolated futures for leverage trading")
|
"Freqtrade only supports isolated futures for leverage trading")
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def load_leverage_tiers(self) -> Dict[str, List[Dict]]:
|
||||||
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
|
if self._config['dry_run']:
|
||||||
|
leverage_tiers_path = (
|
||||||
|
Path(__file__).parent / 'binance_leverage_tiers.json'
|
||||||
|
)
|
||||||
|
with open(leverage_tiers_path) as json_file:
|
||||||
|
return json.load(json_file)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return self._api.fetch_leverage_tiers()
|
||||||
|
except ccxt.DDoSProtection as e:
|
||||||
|
raise DDosProtection(e) from e
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(f'Could not fetch leverage amounts due to'
|
||||||
|
f'{e.__class__.__name__}. Message: {e}') from e
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e) from e
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
File diff suppressed because it is too large
Load Diff
16481
freqtrade/exchange/binance_leverage_tiers.json
Normal file
16481
freqtrade/exchange/binance_leverage_tiers.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,8 @@ class Exchange:
|
|||||||
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||||
"mark_ohlcv_price": "mark",
|
"mark_ohlcv_price": "mark",
|
||||||
"mark_ohlcv_timeframe": "8h",
|
"mark_ohlcv_timeframe": "8h",
|
||||||
"ccxt_futures_name": "swap"
|
"ccxt_futures_name": "swap",
|
||||||
|
"can_fetch_multiple_tiers": True,
|
||||||
}
|
}
|
||||||
_ft_has: Dict = {}
|
_ft_has: Dict = {}
|
||||||
|
|
||||||
@ -90,7 +91,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[str, List[List[float]]] = {}
|
self._leverage_tiers: Dict[str, List[Dict]] = {}
|
||||||
self.loop = asyncio.new_event_loop()
|
self.loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(self.loop)
|
asyncio.set_event_loop(self.loop)
|
||||||
|
|
||||||
@ -183,7 +184,7 @@ class Exchange:
|
|||||||
"markets_refresh_interval", 60) * 60
|
"markets_refresh_interval", 60) * 60
|
||||||
|
|
||||||
if self.trading_mode != TradingMode.SPOT:
|
if self.trading_mode != TradingMode.SPOT:
|
||||||
self.fill_leverage_brackets()
|
self.fill_leverage_tiers()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"""
|
"""
|
||||||
@ -346,7 +347,10 @@ class Exchange:
|
|||||||
return self.markets.get(pair, {}).get('base', '')
|
return self.markets.get(pair, {}).get('base', '')
|
||||||
|
|
||||||
def market_is_future(self, market: Dict[str, Any]) -> bool:
|
def market_is_future(self, market: Dict[str, Any]) -> bool:
|
||||||
return market.get(self._ft_has["ccxt_futures_name"], False) is True
|
return (
|
||||||
|
market.get(self._ft_has["ccxt_futures_name"], False) is True and
|
||||||
|
market.get('linear', False) is True
|
||||||
|
)
|
||||||
|
|
||||||
def market_is_spot(self, market: Dict[str, Any]) -> bool:
|
def market_is_spot(self, market: Dict[str, Any]) -> bool:
|
||||||
return market.get('spot', False) is True
|
return market.get('spot', False) is True
|
||||||
@ -459,7 +463,7 @@ class Exchange:
|
|||||||
# Also reload async markets to avoid issues with newly listed pairs
|
# Also reload async markets to avoid issues with newly listed pairs
|
||||||
self._load_async_markets(reload=True)
|
self._load_async_markets(reload=True)
|
||||||
self._last_markets_refresh = arrow.utcnow().int_timestamp
|
self._last_markets_refresh = arrow.utcnow().int_timestamp
|
||||||
self.fill_leverage_brackets()
|
self.fill_leverage_tiers()
|
||||||
except ccxt.BaseError:
|
except ccxt.BaseError:
|
||||||
logger.exception("Could not reload markets.")
|
logger.exception("Could not reload markets.")
|
||||||
|
|
||||||
@ -691,13 +695,14 @@ class Exchange:
|
|||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
price: float,
|
price: float,
|
||||||
|
leverage: float = 1.0
|
||||||
) -> float:
|
) -> float:
|
||||||
max_stake_amount = self._get_stake_amount_limit(pair, price, 0.0, 'max')
|
max_stake_amount = self._get_stake_amount_limit(pair, price, 0.0, 'max')
|
||||||
if max_stake_amount is None:
|
if max_stake_amount is None:
|
||||||
# * Should never be executed
|
# * Should never be executed
|
||||||
raise OperationalException(f'{self.name}.get_max_pair_stake_amount should'
|
raise OperationalException(f'{self.name}.get_max_pair_stake_amount should'
|
||||||
'never set max_stake_amount to None')
|
'never set max_stake_amount to None')
|
||||||
return max_stake_amount
|
return max_stake_amount / leverage
|
||||||
|
|
||||||
def _get_stake_amount_limit(
|
def _get_stake_amount_limit(
|
||||||
self,
|
self,
|
||||||
@ -1852,23 +1857,117 @@ class Exchange:
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
def fill_leverage_brackets(self):
|
@retrier
|
||||||
|
def load_leverage_tiers(self) -> Dict[str, List[Dict]]:
|
||||||
|
if self.trading_mode == TradingMode.FUTURES and self.exchange_has('fetchLeverageTiers'):
|
||||||
|
try:
|
||||||
|
return self._api.fetch_leverage_tiers()
|
||||||
|
except ccxt.DDoSProtection as e:
|
||||||
|
raise DDosProtection(e) from e
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not load leverage tiers due to {e.__class__.__name__}.'
|
||||||
|
f'Message: {e}'
|
||||||
|
) from e
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e) from e
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def fill_leverage_tiers(self) -> None:
|
||||||
"""
|
"""
|
||||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
Assigns property _leverage_tiers to a dictionary of information about the leverage
|
||||||
allowed on each pair
|
allowed on each pair
|
||||||
Not used if the exchange has a static max leverage value for the account or each pair
|
|
||||||
"""
|
"""
|
||||||
return
|
leverage_tiers = self.load_leverage_tiers()
|
||||||
|
for pair, tiers in leverage_tiers.items():
|
||||||
|
pair_tiers = []
|
||||||
|
for tier in tiers:
|
||||||
|
pair_tiers.append(self.parse_leverage_tier(tier))
|
||||||
|
self._leverage_tiers[pair] = pair_tiers
|
||||||
|
|
||||||
|
def parse_leverage_tier(self, tier) -> Dict:
|
||||||
|
info = tier.get('info', {})
|
||||||
|
return {
|
||||||
|
'min': tier['notionalFloor'],
|
||||||
|
'max': tier['notionalCap'],
|
||||||
|
'mmr': tier['maintenanceMarginRate'],
|
||||||
|
'lev': tier['maxLeverage'],
|
||||||
|
'maintAmt': float(info['cum']) if 'cum' in info else None,
|
||||||
|
}
|
||||||
|
|
||||||
def get_max_leverage(self, pair: str, stake_amount: Optional[float]) -> float:
|
def get_max_leverage(self, pair: str, stake_amount: Optional[float]) -> float:
|
||||||
"""
|
"""
|
||||||
Returns the maximum leverage that a pair can be traded at
|
Returns the maximum leverage that a pair can be traded at
|
||||||
:param pair: The base/quote currency pair being traded
|
:param pair: The base/quote currency pair being traded
|
||||||
:param nominal_value: The total value of the trade in quote currency (margin_mode + debt)
|
:stake_amount: The total value of the traders margin_mode in quote currency
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if self.trading_mode == TradingMode.SPOT:
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
|
|
||||||
|
# Checks and edge cases
|
||||||
|
if stake_amount is None:
|
||||||
|
raise OperationalException(
|
||||||
|
f'{self.name}.get_max_leverage requires argument stake_amount'
|
||||||
|
)
|
||||||
|
|
||||||
|
if pair not in self._leverage_tiers:
|
||||||
|
# Maybe raise exception because it can't be traded on futures?
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
pair_tiers = self._leverage_tiers[pair]
|
||||||
|
|
||||||
|
if stake_amount == 0:
|
||||||
|
return self._leverage_tiers[pair][0]['lev'] # Max lev for lowest amount
|
||||||
|
|
||||||
|
for tier_index in range(len(pair_tiers)):
|
||||||
|
|
||||||
|
tier = pair_tiers[tier_index]
|
||||||
|
lev = tier['lev']
|
||||||
|
|
||||||
|
if tier_index < len(pair_tiers) - 1:
|
||||||
|
next_tier = pair_tiers[tier_index+1]
|
||||||
|
next_floor = next_tier['min'] / next_tier['lev']
|
||||||
|
if next_floor > stake_amount: # Next tier min too high for stake amount
|
||||||
|
return min((tier['max'] / stake_amount), lev)
|
||||||
|
#
|
||||||
|
# With the two leverage tiers below,
|
||||||
|
# - a stake amount of 150 would mean a max leverage of (10000 / 150) = 66.66
|
||||||
|
# - stakes below 133.33 = max_lev of 75
|
||||||
|
# - stakes between 133.33-200 = max_lev of 10000/stake = 50.01-74.99
|
||||||
|
# - stakes from 200 + 1000 = max_lev of 50
|
||||||
|
#
|
||||||
|
# {
|
||||||
|
# "min": 0, # stake = 0.0
|
||||||
|
# "max": 10000, # max_stake@75 = 10000/75 = 133.33333333333334
|
||||||
|
# "lev": 75,
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "min": 10000, # stake = 200.0
|
||||||
|
# "max": 50000, # max_stake@50 = 50000/50 = 1000.0
|
||||||
|
# "lev": 50,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
|
||||||
|
else: # if on the last tier
|
||||||
|
if stake_amount > tier['max']: # If stake is > than max tradeable amount
|
||||||
|
raise InvalidOrderException(f'Amount {stake_amount} too high for {pair}')
|
||||||
|
else:
|
||||||
|
return tier['lev']
|
||||||
|
|
||||||
|
raise OperationalException(
|
||||||
|
'Looped through all tiers without finding a max leverage. Should never be reached'
|
||||||
|
)
|
||||||
|
|
||||||
|
elif self.trading_mode == TradingMode.MARGIN: # Search markets.limits for max lev
|
||||||
market = self.markets[pair]
|
market = self.markets[pair]
|
||||||
if market['limits']['leverage']['max'] is not None:
|
if market['limits']['leverage']['max'] is not None:
|
||||||
return market['limits']['leverage']['max']
|
return market['limits']['leverage']['max']
|
||||||
|
else:
|
||||||
|
return 1.0 # Default if max leverage cannot be found
|
||||||
else:
|
else:
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
@ -2098,16 +2197,6 @@ class Exchange:
|
|||||||
else:
|
else:
|
||||||
return None
|
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(
|
def dry_run_liquidation_price(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
@ -2160,6 +2249,37 @@ class Exchange:
|
|||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
"Freqtrade only supports isolated futures for leverage trading")
|
"Freqtrade only supports isolated futures for leverage trading")
|
||||||
|
|
||||||
|
def get_maintenance_ratio_and_amt(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
nominal_value: 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 self.exchange_has('fetchLeverageTiers'):
|
||||||
|
|
||||||
|
if pair not in self._leverage_tiers:
|
||||||
|
raise InvalidOrderException(
|
||||||
|
f"Maintenance margin rate for {pair} is unavailable for {self.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
pair_tiers = self._leverage_tiers[pair]
|
||||||
|
|
||||||
|
for tier in reversed(pair_tiers):
|
||||||
|
if nominal_value >= tier['min']:
|
||||||
|
return (tier['mmr'], tier['maintAmt'])
|
||||||
|
|
||||||
|
raise OperationalException("nominal value can not be lower than 0")
|
||||||
|
# The lowest notional_floor for any pair in fetch_leverage_tiers is always 0 because it
|
||||||
|
# describes the min amt for a tier, and the lowest tier will always go down to 0
|
||||||
|
else:
|
||||||
|
raise OperationalException(f"Cannot get maintenance ratio using {self.name}")
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -51,3 +51,15 @@ class Gateio(Exchange):
|
|||||||
"""
|
"""
|
||||||
info = self.markets[pair]['info']
|
info = self.markets[pair]['info']
|
||||||
return (float(info['maintenance_rate']), None)
|
return (float(info['maintenance_rate']), None)
|
||||||
|
|
||||||
|
def get_max_leverage(self, pair: str, stake_amount: Optional[float]) -> float:
|
||||||
|
"""
|
||||||
|
Returns the maximum leverage that a pair can be traded at
|
||||||
|
:param pair: The base/quote currency pair being traded
|
||||||
|
:param nominal_value: The total value of the trade in quote currency (margin_mode + debt)
|
||||||
|
"""
|
||||||
|
market = self.markets[pair]
|
||||||
|
if market['limits']['leverage']['max'] is not None:
|
||||||
|
return market['limits']['leverage']['max']
|
||||||
|
else:
|
||||||
|
return 1.0
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
|
import ccxt
|
||||||
|
|
||||||
from freqtrade.enums import MarginMode, TradingMode
|
from freqtrade.enums import MarginMode, TradingMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
from freqtrade.exchange.common import retrier
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -19,15 +22,34 @@ class Okx(Exchange):
|
|||||||
"ohlcv_candle_limit": 300,
|
"ohlcv_candle_limit": 300,
|
||||||
"mark_ohlcv_timeframe": "4h",
|
"mark_ohlcv_timeframe": "4h",
|
||||||
"funding_fee_timeframe": "8h",
|
"funding_fee_timeframe": "8h",
|
||||||
|
"can_fetch_multiple_tiers": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
|
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
|
||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||||
# (TradingMode.FUTURES, MarginMode.CROSS),
|
# (TradingMode.FUTURES, MarginMode.CROSS),
|
||||||
# (TradingMode.FUTURES, MarginMode.ISOLATED)
|
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def _get_params(
|
||||||
|
self,
|
||||||
|
ordertype: str,
|
||||||
|
leverage: float,
|
||||||
|
reduceOnly: bool,
|
||||||
|
time_in_force: str = 'gtc',
|
||||||
|
) -> Dict:
|
||||||
|
params = super()._get_params(
|
||||||
|
ordertype=ordertype,
|
||||||
|
leverage=leverage,
|
||||||
|
reduceOnly=reduceOnly,
|
||||||
|
time_in_force=time_in_force,
|
||||||
|
)
|
||||||
|
if self.trading_mode == TradingMode.FUTURES and self.margin_mode:
|
||||||
|
params['tdMode'] = self.margin_mode.value
|
||||||
|
return params
|
||||||
|
|
||||||
|
@retrier
|
||||||
def _lev_prep(
|
def _lev_prep(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
@ -39,10 +61,63 @@ class Okx(Exchange):
|
|||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f"{self.name}.margin_mode must be set for {self.trading_mode.value}"
|
f"{self.name}.margin_mode must be set for {self.trading_mode.value}"
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
# TODO-lev: Test me properly (check mgnMode passed)
|
||||||
self._api.set_leverage(
|
self._api.set_leverage(
|
||||||
leverage,
|
leverage=leverage,
|
||||||
pair,
|
symbol=pair,
|
||||||
params={
|
params={
|
||||||
"mgnMode": self.margin_mode.value,
|
"mgnMode": self.margin_mode.value,
|
||||||
"posSide": "long" if side == "buy" else "short",
|
# "posSide": "net"",
|
||||||
})
|
})
|
||||||
|
except ccxt.DDoSProtection as e:
|
||||||
|
raise DDosProtection(e) from e
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not set leverage due to {e.__class__.__name__}. Message: {e}') from e
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
def get_max_pair_stake_amount(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
price: float,
|
||||||
|
leverage: float = 1.0
|
||||||
|
) -> float:
|
||||||
|
|
||||||
|
if self.trading_mode == TradingMode.SPOT:
|
||||||
|
return float('inf') # Not actually inf, but this probably won't matter for SPOT
|
||||||
|
|
||||||
|
if pair not in self._leverage_tiers:
|
||||||
|
return float('inf')
|
||||||
|
|
||||||
|
pair_tiers = self._leverage_tiers[pair]
|
||||||
|
return pair_tiers[-1]['max'] / leverage
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def load_leverage_tiers(self) -> Dict[str, List[Dict]]:
|
||||||
|
# * This is slow(~45s) on Okex, must make 90-some api calls to load all linear swap markets
|
||||||
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
|
markets = self.markets
|
||||||
|
symbols = []
|
||||||
|
|
||||||
|
for symbol, market in markets.items():
|
||||||
|
if (self.market_is_future(market)
|
||||||
|
and market['quote'] == self._config['stake_currency']):
|
||||||
|
symbols.append(symbol)
|
||||||
|
|
||||||
|
tiers: Dict[str, List[Dict]] = {}
|
||||||
|
|
||||||
|
# Be verbose here, as this delays startup by ~1 minute.
|
||||||
|
logger.info(
|
||||||
|
f"Initializing leverage_tiers for {len(symbols)} markets. "
|
||||||
|
"This will take about a minute.")
|
||||||
|
|
||||||
|
for symbol in sorted(symbols):
|
||||||
|
res = self._api.fetch_leverage_tiers(symbol)
|
||||||
|
tiers[symbol] = res[symbol]
|
||||||
|
logger.info(f"Done initializing {len(symbols)} markets.")
|
||||||
|
|
||||||
|
return tiers
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
@ -2,7 +2,7 @@ numpy==1.22.2
|
|||||||
pandas==1.4.0
|
pandas==1.4.0
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.72.36
|
ccxt==1.73.17
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==36.0.1
|
cryptography==36.0.1
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
|
2
setup.py
2
setup.py
@ -42,7 +42,7 @@ setup(
|
|||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
# from requirements.txt
|
# from requirements.txt
|
||||||
'ccxt>=1.72.29',
|
'ccxt>=1.73.1',
|
||||||
'SQLAlchemy',
|
'SQLAlchemy',
|
||||||
'python-telegram-bot>=13.4',
|
'python-telegram-bot>=13.4',
|
||||||
'arrow>=0.17.0',
|
'arrow>=0.17.0',
|
||||||
|
@ -231,9 +231,9 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 10 active markets: "
|
assert ("Exchange Bittrex has 12 active markets: "
|
||||||
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, NEO/BTC, "
|
"ADA/USDT:USDT, BLK/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, "
|
||||||
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
"LTC/ETH, LTC/USD, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
|
patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
|
||||||
@ -246,7 +246,7 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
pargs['config'] = None
|
pargs['config'] = None
|
||||||
start_list_markets(pargs, False)
|
start_list_markets(pargs, False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert re.match("\nExchange Binance has 10 active markets:\n",
|
assert re.match("\nExchange Binance has 12 active markets:\n",
|
||||||
captured.out)
|
captured.out)
|
||||||
|
|
||||||
patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static)
|
patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static)
|
||||||
@ -258,9 +258,9 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 12 markets: "
|
assert ("Exchange Bittrex has 14 markets: "
|
||||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, "
|
"ADA/USDT:USDT, BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, "
|
||||||
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
"LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# Test list-pairs subcommand: active pairs
|
# Test list-pairs subcommand: active pairs
|
||||||
@ -297,8 +297,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 6 active markets with ETH, LTC as base currencies: "
|
assert ("Exchange Bittrex has 7 active markets with ETH, LTC as base currencies: "
|
||||||
"ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
|
"ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# active markets, base=LTC
|
# active markets, base=LTC
|
||||||
@ -323,8 +323,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 3 active markets with USDT, USD as quote currencies: "
|
assert ("Exchange Bittrex has 5 active markets with USDT, USD as quote currencies: "
|
||||||
"ETH/USDT, LTC/USD, XLTCUSDT.\n"
|
"ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, LTC/USD, XLTCUSDT.\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# active markets, quote=USDT
|
# active markets, quote=USDT
|
||||||
@ -336,8 +336,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 2 active markets with USDT as quote currency: "
|
assert ("Exchange Bittrex has 4 active markets with USDT as quote currency: "
|
||||||
"ETH/USDT, XLTCUSDT.\n"
|
"ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, XLTCUSDT.\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# active markets, base=LTC, quote=USDT
|
# active markets, base=LTC, quote=USDT
|
||||||
@ -399,7 +399,7 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ("Exchange Bittrex has 10 active markets:\n"
|
assert ("Exchange Bittrex has 12 active markets:\n"
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# Test tabular output, no markets found
|
# Test tabular output, no markets found
|
||||||
@ -422,8 +422,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
|||||||
]
|
]
|
||||||
start_list_markets(get_args(args), False)
|
start_list_markets(get_args(args), False)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert ('["BLK/BTC","ETH/BTC","ETH/USDT","LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC",'
|
assert ('["ADA/USDT:USDT","BLK/BTC","ETH/BTC","ETH/USDT","ETH/USDT:USDT",'
|
||||||
'"TKN/BTC","XLTCUSDT","XRP/BTC"]'
|
'"LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC","TKN/BTC","XLTCUSDT","XRP/BTC"]'
|
||||||
in captured.out)
|
in captured.out)
|
||||||
|
|
||||||
# Test --print-csv
|
# Test --print-csv
|
||||||
|
@ -579,6 +579,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -614,6 +616,8 @@ def get_markets():
|
|||||||
# According to ccxt, markets without active item set are also active
|
# According to ccxt, markets without active item set are also active
|
||||||
# 'active': True,
|
# 'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -648,6 +652,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -682,6 +688,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -717,6 +725,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -752,6 +762,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
@ -787,6 +799,8 @@ def get_markets():
|
|||||||
'quote': 'BTC',
|
'quote': 'BTC',
|
||||||
'active': False,
|
'active': False,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'contractSize': None,
|
||||||
'precision': {
|
'precision': {
|
||||||
@ -877,6 +891,8 @@ def get_markets():
|
|||||||
'future': True,
|
'future': True,
|
||||||
'swap': True,
|
'swap': True,
|
||||||
'margin': True,
|
'margin': True,
|
||||||
|
'linear': None,
|
||||||
|
'inverse': False,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'contractSize': None,
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
@ -912,6 +928,8 @@ def get_markets():
|
|||||||
'quote': 'USDT',
|
'quote': 'USDT',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
'maker': 0.0002,
|
'maker': 0.0002,
|
||||||
@ -943,19 +961,40 @@ def get_markets():
|
|||||||
'symbol': 'NEO/USDT',
|
'symbol': 'NEO/USDT',
|
||||||
'base': 'NEO',
|
'base': 'NEO',
|
||||||
'quote': 'USDT',
|
'quote': 'USDT',
|
||||||
'active': True,
|
'settle': '',
|
||||||
'spot': True,
|
'baseId': 'NEO',
|
||||||
|
'quoteId': 'USDT',
|
||||||
|
'settleId': '',
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
|
'spot': True,
|
||||||
|
'margin': True,
|
||||||
|
'swap': False,
|
||||||
|
'futures': False,
|
||||||
|
'option': False,
|
||||||
|
'active': True,
|
||||||
|
'contract': False,
|
||||||
|
'linear': None,
|
||||||
|
'inverse': None,
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
'maker': 0.0002,
|
'maker': 0.0002,
|
||||||
|
'contractSize': None,
|
||||||
|
'expiry': None,
|
||||||
|
'expiryDatetime': None,
|
||||||
|
'strike': None,
|
||||||
|
'optionType': None,
|
||||||
|
'tierBased': None,
|
||||||
|
'percentage': None,
|
||||||
|
'lot': 0.00000001,
|
||||||
'precision': {
|
'precision': {
|
||||||
'price': 8,
|
'price': 8,
|
||||||
'amount': 8,
|
'amount': 8,
|
||||||
'cost': 8,
|
'cost': 8,
|
||||||
},
|
},
|
||||||
'lot': 0.00000001,
|
|
||||||
'contractSize': None,
|
|
||||||
'limits': {
|
'limits': {
|
||||||
|
"leverage": {
|
||||||
|
'min': 1,
|
||||||
|
'max': 10
|
||||||
|
},
|
||||||
'amount': {
|
'amount': {
|
||||||
'min': 0.01,
|
'min': 0.01,
|
||||||
'max': 1000,
|
'max': 1000,
|
||||||
@ -978,6 +1017,8 @@ def get_markets():
|
|||||||
'quote': 'USDT',
|
'quote': 'USDT',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'contractSize': None,
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
@ -1015,6 +1056,8 @@ def get_markets():
|
|||||||
'quote': 'USD',
|
'quote': 'USD',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'contractSize': None,
|
||||||
'precision': {
|
'precision': {
|
||||||
@ -1050,6 +1093,8 @@ def get_markets():
|
|||||||
'spot': False,
|
'spot': False,
|
||||||
'type': 'swap',
|
'type': 'swap',
|
||||||
'contractSize': 0.01,
|
'contractSize': 0.01,
|
||||||
|
'swap': False,
|
||||||
|
'linear': False,
|
||||||
'taker': 0.0006,
|
'taker': 0.0006,
|
||||||
'maker': 0.0002,
|
'maker': 0.0002,
|
||||||
'precision': {
|
'precision': {
|
||||||
@ -1083,6 +1128,8 @@ def get_markets():
|
|||||||
'quote': 'ETH',
|
'quote': 'ETH',
|
||||||
'active': True,
|
'active': True,
|
||||||
'spot': True,
|
'spot': True,
|
||||||
|
'swap': False,
|
||||||
|
'linear': None,
|
||||||
'type': 'spot',
|
'type': 'spot',
|
||||||
'contractSize': None,
|
'contractSize': None,
|
||||||
'precision': {
|
'precision': {
|
||||||
@ -1163,7 +1210,185 @@ def get_markets():
|
|||||||
'amount': 1
|
'amount': 1
|
||||||
},
|
},
|
||||||
'info': {}
|
'info': {}
|
||||||
|
},
|
||||||
|
'ADA/USDT:USDT': {
|
||||||
|
'limits': {
|
||||||
|
'leverage': {
|
||||||
|
'min': 1,
|
||||||
|
'max': 20,
|
||||||
|
},
|
||||||
|
'amount': {
|
||||||
|
'min': 1,
|
||||||
|
'max': 1000000,
|
||||||
|
},
|
||||||
|
'price': {
|
||||||
|
'min': 0.52981,
|
||||||
|
'max': 1.58943,
|
||||||
|
},
|
||||||
|
'cost': {
|
||||||
|
'min': None,
|
||||||
|
'max': None,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'precision': {
|
||||||
|
'amount': 1,
|
||||||
|
'price': 0.00001
|
||||||
|
},
|
||||||
|
'tierBased': True,
|
||||||
|
'percentage': True,
|
||||||
|
'taker': 0.0000075,
|
||||||
|
'maker': -0.0000025,
|
||||||
|
'feeSide': 'get',
|
||||||
|
'tiers': {
|
||||||
|
'maker': [
|
||||||
|
[0, 0.002], [1.5, 0.00185],
|
||||||
|
[3, 0.00175], [6, 0.00165],
|
||||||
|
[12.5, 0.00155], [25, 0.00145],
|
||||||
|
[75, 0.00135], [200, 0.00125],
|
||||||
|
[500, 0.00115], [1250, 0.00105],
|
||||||
|
[2500, 0.00095], [3000, 0.00085],
|
||||||
|
[6000, 0.00075], [11000, 0.00065],
|
||||||
|
[20000, 0.00055], [40000, 0.00055],
|
||||||
|
[75000, 0.00055]
|
||||||
|
],
|
||||||
|
'taker': [
|
||||||
|
[0, 0.002], [1.5, 0.00195],
|
||||||
|
[3, 0.00185], [6, 0.00175],
|
||||||
|
[12.5, 0.00165], [25, 0.00155],
|
||||||
|
[75, 0.00145], [200, 0.00135],
|
||||||
|
[500, 0.00125], [1250, 0.00115],
|
||||||
|
[2500, 0.00105], [3000, 0.00095],
|
||||||
|
[6000, 0.00085], [11000, 0.00075],
|
||||||
|
[20000, 0.00065], [40000, 0.00065],
|
||||||
|
[75000, 0.00065]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'id': 'ADA_USDT',
|
||||||
|
'symbol': 'ADA/USDT:USDT',
|
||||||
|
'base': 'ADA',
|
||||||
|
'quote': 'USDT',
|
||||||
|
'settle': 'USDT',
|
||||||
|
'baseId': 'ADA',
|
||||||
|
'quoteId': 'USDT',
|
||||||
|
'settleId': 'usdt',
|
||||||
|
'type': 'swap',
|
||||||
|
'spot': False,
|
||||||
|
'margin': False,
|
||||||
|
'swap': True,
|
||||||
|
'future': False,
|
||||||
|
'option': False,
|
||||||
|
'active': True,
|
||||||
|
'contract': True,
|
||||||
|
'linear': True,
|
||||||
|
'inverse': False,
|
||||||
|
'contractSize': 0.01,
|
||||||
|
'expiry': None,
|
||||||
|
'expiryDatetime': None,
|
||||||
|
'strike': None,
|
||||||
|
'optionType': None,
|
||||||
|
'info': {}
|
||||||
|
},
|
||||||
|
'SOL/BUSD:BUSD': {
|
||||||
|
'limits': {
|
||||||
|
'leverage': {'min': None, 'max': None},
|
||||||
|
'amount': {'min': 1, 'max': 1000000},
|
||||||
|
'price': {'min': 0.04, 'max': 100000},
|
||||||
|
'cost': {'min': 5, 'max': None},
|
||||||
|
'market': {'min': 1, 'max': 1500}
|
||||||
|
},
|
||||||
|
'precision': {'amount': 0, 'price': 2, 'base': 8, 'quote': 8},
|
||||||
|
'tierBased': False,
|
||||||
|
'percentage': True,
|
||||||
|
'taker': 0.0004,
|
||||||
|
'maker': 0.0002,
|
||||||
|
'feeSide': 'get',
|
||||||
|
'id': 'SOLBUSD',
|
||||||
|
'lowercaseId': 'solbusd',
|
||||||
|
'symbol': 'SOL/BUSD',
|
||||||
|
'base': 'SOL',
|
||||||
|
'quote': 'BUSD',
|
||||||
|
'settle': 'BUSD',
|
||||||
|
'baseId': 'SOL',
|
||||||
|
'quoteId': 'BUSD',
|
||||||
|
'settleId': 'BUSD',
|
||||||
|
'type': 'future',
|
||||||
|
'spot': False,
|
||||||
|
'margin': False,
|
||||||
|
'future': True,
|
||||||
|
'delivery': False,
|
||||||
|
'option': False,
|
||||||
|
'active': True,
|
||||||
|
'contract': True,
|
||||||
|
'linear': True,
|
||||||
|
'inverse': False,
|
||||||
|
'contractSize': 1,
|
||||||
|
'expiry': None,
|
||||||
|
'expiryDatetime': None,
|
||||||
|
'strike': None,
|
||||||
|
'optionType': None,
|
||||||
|
'info': {
|
||||||
|
'symbol': 'SOLBUSD',
|
||||||
|
'pair': 'SOLBUSD',
|
||||||
|
'contractType': 'PERPETUAL',
|
||||||
|
'deliveryDate': '4133404800000',
|
||||||
|
'onboardDate': '1630566000000',
|
||||||
|
'status': 'TRADING',
|
||||||
|
'maintMarginPercent': '2.5000',
|
||||||
|
'requiredMarginPercent': '5.0000',
|
||||||
|
'baseAsset': 'SOL',
|
||||||
|
'quoteAsset': 'BUSD',
|
||||||
|
'marginAsset': 'BUSD',
|
||||||
|
'pricePrecision': '4',
|
||||||
|
'quantityPrecision': '0',
|
||||||
|
'baseAssetPrecision': '8',
|
||||||
|
'quotePrecision': '8',
|
||||||
|
'underlyingType': 'COIN',
|
||||||
|
'underlyingSubType': [],
|
||||||
|
'settlePlan': '0',
|
||||||
|
'triggerProtect': '0.0500',
|
||||||
|
'liquidationFee': '0.005000',
|
||||||
|
'marketTakeBound': '0.05',
|
||||||
|
'filters': [
|
||||||
|
{
|
||||||
|
'minPrice': '0.0400',
|
||||||
|
'maxPrice': '100000',
|
||||||
|
'filterType': 'PRICE_FILTER',
|
||||||
|
'tickSize': '0.0100'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'stepSize': '1',
|
||||||
|
'filterType': 'LOT_SIZE',
|
||||||
|
'maxQty': '1000000',
|
||||||
|
'minQty': '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'stepSize': '1',
|
||||||
|
'filterType': 'MARKET_LOT_SIZE',
|
||||||
|
'maxQty': '1500',
|
||||||
|
'minQty': '1'
|
||||||
|
},
|
||||||
|
{'limit': '200', 'filterType': 'MAX_NUM_ORDERS'},
|
||||||
|
{'limit': '10', 'filterType': 'MAX_NUM_ALGO_ORDERS'},
|
||||||
|
{'notional': '5', 'filterType': 'MIN_NOTIONAL'},
|
||||||
|
{
|
||||||
|
'multiplierDown': '0.9500',
|
||||||
|
'multiplierUp': '1.0500',
|
||||||
|
'multiplierDecimal': '4',
|
||||||
|
'filterType': 'PERCENT_PRICE'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'orderTypes': [
|
||||||
|
'LIMIT',
|
||||||
|
'MARKET',
|
||||||
|
'STOP',
|
||||||
|
'STOP_MARKET',
|
||||||
|
'TAKE_PROFIT',
|
||||||
|
'TAKE_PROFIT_MARKET',
|
||||||
|
'TRAILING_STOP_MARKET'
|
||||||
|
],
|
||||||
|
'timeInForce': ['GTC', 'IOC', 'FOK', 'GTX']
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1173,7 +1398,9 @@ def markets_static():
|
|||||||
# market list. Do not modify this list without a good reason! Do not modify market parameters
|
# market list. Do not modify this list without a good reason! Do not modify market parameters
|
||||||
# of listed pairs in get_markets() without a good reason either!
|
# of listed pairs in get_markets() without a good reason either!
|
||||||
static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']
|
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC',
|
||||||
|
'ADA/USDT:USDT', 'ETH/USDT:USDT',
|
||||||
|
]
|
||||||
all_markets = get_markets()
|
all_markets = get_markets()
|
||||||
return {m: all_markets[m] for m in static_markets}
|
return {m: all_markets[m] for m in static_markets}
|
||||||
|
|
||||||
@ -2841,3 +3068,438 @@ def funding_rate_history_octohourly():
|
|||||||
"datetime": "2021-09-01T08:00:00.000Z"
|
"datetime": "2021-09-01T08:00:00.000Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def leverage_tiers():
|
||||||
|
return {
|
||||||
|
"1000SHIB/USDT": [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 50000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 50000,
|
||||||
|
'max': 150000,
|
||||||
|
'mmr': 0.025,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': 750.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 150000,
|
||||||
|
'max': 250000,
|
||||||
|
'mmr': 0.05,
|
||||||
|
'lev': 10,
|
||||||
|
'maintAmt': 4500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 250000,
|
||||||
|
'max': 500000,
|
||||||
|
'mmr': 0.1,
|
||||||
|
'lev': 5,
|
||||||
|
'maintAmt': 17000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 500000,
|
||||||
|
'max': 1000000,
|
||||||
|
'mmr': 0.125,
|
||||||
|
'lev': 4,
|
||||||
|
'maintAmt': 29500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1000000,
|
||||||
|
'max': 2000000,
|
||||||
|
'mmr': 0.25,
|
||||||
|
'lev': 2,
|
||||||
|
'maintAmt': 154500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 2000000,
|
||||||
|
'max': 30000000,
|
||||||
|
'mmr': 0.5,
|
||||||
|
'lev': 1,
|
||||||
|
'maintAmt': 654500.0
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"1INCH/USDT": [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 5000,
|
||||||
|
'mmr': 0.012,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 5000,
|
||||||
|
'max': 25000,
|
||||||
|
'mmr': 0.025,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': 65.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 25000,
|
||||||
|
'max': 100000,
|
||||||
|
'mmr': 0.05,
|
||||||
|
'lev': 10,
|
||||||
|
'maintAmt': 690.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 100000,
|
||||||
|
'max': 250000,
|
||||||
|
'mmr': 0.1,
|
||||||
|
'lev': 5,
|
||||||
|
'maintAmt': 5690.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 250000,
|
||||||
|
'max': 1000000,
|
||||||
|
'mmr': 0.125,
|
||||||
|
'lev': 2,
|
||||||
|
'maintAmt': 11940.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1000000,
|
||||||
|
'max': 100000000,
|
||||||
|
'mmr': 0.5,
|
||||||
|
'lev': 1,
|
||||||
|
'maintAmt': 386940.0
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"AAVE/USDT": [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 50000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 50000,
|
||||||
|
'max': 250000,
|
||||||
|
'mmr': 0.02,
|
||||||
|
'lev': 25,
|
||||||
|
'maintAmt': 500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 250000,
|
||||||
|
'max': 1000000,
|
||||||
|
'mmr': 0.05,
|
||||||
|
'lev': 10,
|
||||||
|
'maintAmt': 8000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1000000,
|
||||||
|
'max': 2000000,
|
||||||
|
'mmr': 0.1,
|
||||||
|
'lev': 5,
|
||||||
|
'maintAmt': 58000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 2000000,
|
||||||
|
'max': 5000000,
|
||||||
|
'mmr': 0.125,
|
||||||
|
'lev': 4,
|
||||||
|
'maintAmt': 108000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 5000000,
|
||||||
|
'max': 10000000,
|
||||||
|
'mmr': 0.1665,
|
||||||
|
'lev': 3,
|
||||||
|
'maintAmt': 315500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 10000000,
|
||||||
|
'max': 20000000,
|
||||||
|
'mmr': 0.25,
|
||||||
|
'lev': 2,
|
||||||
|
'maintAmt': 1150500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 20000000,
|
||||||
|
"max": 50000000,
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 6150500.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ADA/BUSD": [
|
||||||
|
{
|
||||||
|
"min": 0,
|
||||||
|
"max": 100000,
|
||||||
|
"mmr": 0.025,
|
||||||
|
"lev": 20,
|
||||||
|
"maintAmt": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 100000,
|
||||||
|
"max": 500000,
|
||||||
|
"mmr": 0.05,
|
||||||
|
"lev": 10,
|
||||||
|
"maintAmt": 2500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 500000,
|
||||||
|
"max": 1000000,
|
||||||
|
"mmr": 0.1,
|
||||||
|
"lev": 5,
|
||||||
|
"maintAmt": 27500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 1000000,
|
||||||
|
"max": 2000000,
|
||||||
|
"mmr": 0.15,
|
||||||
|
"lev": 3,
|
||||||
|
"maintAmt": 77500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 2000000,
|
||||||
|
"max": 5000000,
|
||||||
|
"mmr": 0.25,
|
||||||
|
"lev": 2,
|
||||||
|
"maintAmt": 277500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 5000000,
|
||||||
|
"max": 30000000,
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 1527500.0
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'BNB/BUSD': [
|
||||||
|
{
|
||||||
|
"min": 0, # stake(before leverage) = 0
|
||||||
|
"max": 100000, # max stake(before leverage) = 5000
|
||||||
|
"mmr": 0.025,
|
||||||
|
"lev": 20,
|
||||||
|
"maintAmt": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 100000, # stake = 10000.0
|
||||||
|
"max": 500000, # max_stake = 50000.0
|
||||||
|
"mmr": 0.05,
|
||||||
|
"lev": 10,
|
||||||
|
"maintAmt": 2500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 500000, # stake = 100000.0
|
||||||
|
"max": 1000000, # max_stake = 200000.0
|
||||||
|
"mmr": 0.1,
|
||||||
|
"lev": 5,
|
||||||
|
"maintAmt": 27500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 1000000, # stake = 333333.3333333333
|
||||||
|
"max": 2000000, # max_stake = 666666.6666666666
|
||||||
|
"mmr": 0.15,
|
||||||
|
"lev": 3,
|
||||||
|
"maintAmt": 77500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 2000000, # stake = 1000000.0
|
||||||
|
"max": 5000000, # max_stake = 2500000.0
|
||||||
|
"mmr": 0.25,
|
||||||
|
"lev": 2,
|
||||||
|
"maintAmt": 277500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 5000000, # stake = 5000000.0
|
||||||
|
"max": 30000000, # max_stake = 30000000.0
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 1527500.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'BNB/USDT': [
|
||||||
|
{
|
||||||
|
"min": 0, # stake = 0.0
|
||||||
|
"max": 10000, # max_stake = 133.33333333333334
|
||||||
|
"mmr": 0.0065,
|
||||||
|
"lev": 75,
|
||||||
|
"maintAmt": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 10000, # stake = 200.0
|
||||||
|
"max": 50000, # max_stake = 1000.0
|
||||||
|
"mmr": 0.01,
|
||||||
|
"lev": 50,
|
||||||
|
"maintAmt": 35.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 50000, # stake = 2000.0
|
||||||
|
"max": 250000, # max_stake = 10000.0
|
||||||
|
"mmr": 0.02,
|
||||||
|
"lev": 25,
|
||||||
|
"maintAmt": 535.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 250000, # stake = 25000.0
|
||||||
|
"max": 1000000, # max_stake = 100000.0
|
||||||
|
"mmr": 0.05,
|
||||||
|
"lev": 10,
|
||||||
|
"maintAmt": 8035.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 1000000, # stake = 200000.0
|
||||||
|
"max": 2000000, # max_stake = 400000.0
|
||||||
|
"mmr": 0.1,
|
||||||
|
"lev": 5,
|
||||||
|
"maintAmt": 58035.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 2000000, # stake = 500000.0
|
||||||
|
"max": 5000000, # max_stake = 1250000.0
|
||||||
|
"mmr": 0.125,
|
||||||
|
"lev": 4,
|
||||||
|
"maintAmt": 108035.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 5000000, # stake = 1666666.6666666667
|
||||||
|
"max": 10000000, # max_stake = 3333333.3333333335
|
||||||
|
"mmr": 0.15,
|
||||||
|
"lev": 3,
|
||||||
|
"maintAmt": 233035.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 10000000, # stake = 5000000.0
|
||||||
|
"max": 20000000, # max_stake = 10000000.0
|
||||||
|
"mmr": 0.25,
|
||||||
|
"lev": 2,
|
||||||
|
"maintAmt": 1233035.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 20000000, # stake = 20000000.0
|
||||||
|
"max": 50000000, # max_stake = 50000000.0
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 6233035.0
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'BTC/USDT': [
|
||||||
|
{
|
||||||
|
"min": 0, # stake = 0.0
|
||||||
|
"max": 50000, # max_stake = 400.0
|
||||||
|
"mmr": 0.004,
|
||||||
|
"lev": 125,
|
||||||
|
"maintAmt": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 50000, # stake = 500.0
|
||||||
|
"max": 250000, # max_stake = 2500.0
|
||||||
|
"mmr": 0.005,
|
||||||
|
"lev": 100,
|
||||||
|
"maintAmt": 50.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 250000, # stake = 5000.0
|
||||||
|
"max": 1000000, # max_stake = 20000.0
|
||||||
|
"mmr": 0.01,
|
||||||
|
"lev": 50,
|
||||||
|
"maintAmt": 1300.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 1000000, # stake = 50000.0
|
||||||
|
"max": 7500000, # max_stake = 375000.0
|
||||||
|
"mmr": 0.025,
|
||||||
|
"lev": 20,
|
||||||
|
"maintAmt": 16300.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 7500000, # stake = 750000.0
|
||||||
|
"max": 40000000, # max_stake = 4000000.0
|
||||||
|
"mmr": 0.05,
|
||||||
|
"lev": 10,
|
||||||
|
"maintAmt": 203800.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 40000000, # stake = 8000000.0
|
||||||
|
"max": 100000000, # max_stake = 20000000.0
|
||||||
|
"mmr": 0.1,
|
||||||
|
"lev": 5,
|
||||||
|
"maintAmt": 2203800.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 100000000, # stake = 25000000.0
|
||||||
|
"max": 200000000, # max_stake = 50000000.0
|
||||||
|
"mmr": 0.125,
|
||||||
|
"lev": 4,
|
||||||
|
"maintAmt": 4703800.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 200000000, # stake = 66666666.666666664
|
||||||
|
"max": 400000000, # max_stake = 133333333.33333333
|
||||||
|
"mmr": 0.15,
|
||||||
|
"lev": 3,
|
||||||
|
"maintAmt": 9703800.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 400000000, # stake = 200000000.0
|
||||||
|
"max": 600000000, # max_stake = 300000000.0
|
||||||
|
"mmr": 0.25,
|
||||||
|
"lev": 2,
|
||||||
|
"maintAmt": 4.97038E7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 600000000, # stake = 600000000.0
|
||||||
|
"max": 1000000000, # max_stake = 1000000000.0
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 1.997038E8
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"ZEC/USDT": [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 50000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 50000,
|
||||||
|
'max': 150000,
|
||||||
|
'mmr': 0.025,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': 750.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 150000,
|
||||||
|
'max': 250000,
|
||||||
|
'mmr': 0.05,
|
||||||
|
'lev': 10,
|
||||||
|
'maintAmt': 4500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 250000,
|
||||||
|
'max': 500000,
|
||||||
|
'mmr': 0.1,
|
||||||
|
'lev': 5,
|
||||||
|
'maintAmt': 17000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 500000,
|
||||||
|
'max': 1000000,
|
||||||
|
'mmr': 0.125,
|
||||||
|
'lev': 4,
|
||||||
|
'maintAmt': 29500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1000000,
|
||||||
|
'max': 2000000,
|
||||||
|
'mmr': 0.25,
|
||||||
|
'lev': 2,
|
||||||
|
'maintAmt': 154500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 2000000,
|
||||||
|
'max': 30000000,
|
||||||
|
'mmr': 0.5,
|
||||||
|
'lev': 1,
|
||||||
|
'maintAmt': 654500.0
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -162,168 +162,338 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
|||||||
assert not exchange.stoploss_adjust(sl3, order, side=side)
|
assert not exchange.stoploss_adjust(sl3, order, side=side)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('pair,stake_amount,max_lev', [
|
def test_fill_leverage_tiers_binance(default_conf, mocker):
|
||||||
("BNB/BUSD", 0.0, 40.0),
|
|
||||||
("BNB/USDT", 100.0, 100.0),
|
|
||||||
("BTC/USDT", 170.30, 250.0),
|
|
||||||
("BNB/BUSD", 99999.9, 10.0),
|
|
||||||
("BNB/USDT", 750000, 6.666666666666667),
|
|
||||||
("BTC/USDT", 150000000.1, 2.0),
|
|
||||||
])
|
|
||||||
def test_get_max_leverage_binance(default_conf, mocker, pair, stake_amount, max_lev):
|
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
|
||||||
exchange._leverage_brackets = {
|
|
||||||
'BNB/BUSD': [
|
|
||||||
[0.0, 0.025, 0.0], # lev = 40.0
|
|
||||||
[100000.0, 0.05, 2500.0], # lev = 20.0
|
|
||||||
[500000.0, 0.1, 27500.0], # lev = 10.0
|
|
||||||
[1000000.0, 0.15, 77500.0], # lev = 6.666666666666667
|
|
||||||
[2000000.0, 0.25, 277500.0], # lev = 4.0
|
|
||||||
[5000000.0, 0.5, 1527500.0], # lev = 2.0
|
|
||||||
],
|
|
||||||
'BNB/USDT': [
|
|
||||||
[0.0, 0.0065, 0.0], # lev = 153.84615384615384
|
|
||||||
[10000.0, 0.01, 35.0], # lev = 100.0
|
|
||||||
[50000.0, 0.02, 535.0], # lev = 50.0
|
|
||||||
[250000.0, 0.05, 8035.0], # lev = 20.0
|
|
||||||
[1000000.0, 0.1, 58035.0], # lev = 10.0
|
|
||||||
[2000000.0, 0.125, 108035.0], # lev = 8.0
|
|
||||||
[5000000.0, 0.15, 233035.0], # lev = 6.666666666666667
|
|
||||||
[10000000.0, 0.25, 1233035.0], # lev = 4.0
|
|
||||||
],
|
|
||||||
'BTC/USDT': [
|
|
||||||
[0.0, 0.004, 0.0], # lev = 250.0
|
|
||||||
[50000.0, 0.005, 50.0], # lev = 200.0
|
|
||||||
[250000.0, 0.01, 1300.0], # lev = 100.0
|
|
||||||
[1000000.0, 0.025, 16300.0], # lev = 40.0
|
|
||||||
[5000000.0, 0.05, 141300.0], # lev = 20.0
|
|
||||||
[20000000.0, 0.1, 1141300.0], # lev = 10.0
|
|
||||||
[50000000.0, 0.125, 2391300.0], # lev = 8.0
|
|
||||||
[100000000.0, 0.15, 4891300.0], # lev = 6.666666666666667
|
|
||||||
[200000000.0, 0.25, 24891300.0], # lev = 4.0
|
|
||||||
[300000000.0, 0.5, 99891300.0], # lev = 2.0
|
|
||||||
]
|
|
||||||
}
|
|
||||||
assert exchange.get_max_leverage(pair, stake_amount) == max_lev
|
|
||||||
|
|
||||||
|
|
||||||
def test_fill_leverage_brackets_binance(default_conf, mocker):
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.load_leverage_brackets = MagicMock(return_value={
|
api_mock.fetch_leverage_tiers = MagicMock(return_value={
|
||||||
'ADA/BUSD': [[0.0, 0.025],
|
'ADA/BUSD': [
|
||||||
[100000.0, 0.05],
|
{
|
||||||
[500000.0, 0.1],
|
"tier": 1,
|
||||||
[1000000.0, 0.15],
|
"notionalFloor": 0,
|
||||||
[2000000.0, 0.25],
|
"notionalCap": 100000,
|
||||||
[5000000.0, 0.5]],
|
"maintenanceMarginRate": 0.025,
|
||||||
'BTC/USDT': [[0.0, 0.004],
|
"maxLeverage": 20,
|
||||||
[50000.0, 0.005],
|
"info": {
|
||||||
[250000.0, 0.01],
|
"bracket": "1",
|
||||||
[1000000.0, 0.025],
|
"initialLeverage": "20",
|
||||||
[5000000.0, 0.05],
|
"notionalCap": "100000",
|
||||||
[20000000.0, 0.1],
|
"notionalFloor": "0",
|
||||||
[50000000.0, 0.125],
|
"maintMarginRatio": "0.025",
|
||||||
[100000000.0, 0.15],
|
"cum": "0.0"
|
||||||
[200000000.0, 0.25],
|
}
|
||||||
[300000000.0, 0.5]],
|
},
|
||||||
"ZEC/USDT": [[0.0, 0.01],
|
{
|
||||||
[5000.0, 0.025],
|
"tier": 2,
|
||||||
[25000.0, 0.05],
|
"notionalFloor": 100000,
|
||||||
[100000.0, 0.1],
|
"notionalCap": 500000,
|
||||||
[250000.0, 0.125],
|
"maintenanceMarginRate": 0.05,
|
||||||
[1000000.0, 0.5]],
|
"maxLeverage": 10,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "500000",
|
||||||
|
"notionalFloor": "100000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "2500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3,
|
||||||
|
"notionalFloor": 500000,
|
||||||
|
"notionalCap": 1000000,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "500000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "27500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4,
|
||||||
|
"notionalFloor": 1000000,
|
||||||
|
"notionalCap": 2000000,
|
||||||
|
"maintenanceMarginRate": 0.15,
|
||||||
|
"maxLeverage": 3,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "3",
|
||||||
|
"notionalCap": "2000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.15",
|
||||||
|
"cum": "77500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5,
|
||||||
|
"notionalFloor": 2000000,
|
||||||
|
"notionalCap": 5000000,
|
||||||
|
"maintenanceMarginRate": 0.25,
|
||||||
|
"maxLeverage": 2,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "5000000",
|
||||||
|
"notionalFloor": "2000000",
|
||||||
|
"maintMarginRatio": "0.25",
|
||||||
|
"cum": "277500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6,
|
||||||
|
"notionalFloor": 5000000,
|
||||||
|
"notionalCap": 30000000,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "30000000",
|
||||||
|
"notionalFloor": "5000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "1527500.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ZEC/USDT": [
|
||||||
|
{
|
||||||
|
"tier": 1,
|
||||||
|
"notionalFloor": 0,
|
||||||
|
"notionalCap": 50000,
|
||||||
|
"maintenanceMarginRate": 0.01,
|
||||||
|
"maxLeverage": 50,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "50",
|
||||||
|
"notionalCap": "50000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.01",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 2,
|
||||||
|
"notionalFloor": 50000,
|
||||||
|
"notionalCap": 150000,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20,
|
||||||
|
"info": {
|
||||||
|
"bracket": "2",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "150000",
|
||||||
|
"notionalFloor": "50000",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "750.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 3,
|
||||||
|
"notionalFloor": 150000,
|
||||||
|
"notionalCap": 250000,
|
||||||
|
"maintenanceMarginRate": 0.05,
|
||||||
|
"maxLeverage": 10,
|
||||||
|
"info": {
|
||||||
|
"bracket": "3",
|
||||||
|
"initialLeverage": "10",
|
||||||
|
"notionalCap": "250000",
|
||||||
|
"notionalFloor": "150000",
|
||||||
|
"maintMarginRatio": "0.05",
|
||||||
|
"cum": "4500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 4,
|
||||||
|
"notionalFloor": 250000,
|
||||||
|
"notionalCap": 500000,
|
||||||
|
"maintenanceMarginRate": 0.1,
|
||||||
|
"maxLeverage": 5,
|
||||||
|
"info": {
|
||||||
|
"bracket": "4",
|
||||||
|
"initialLeverage": "5",
|
||||||
|
"notionalCap": "500000",
|
||||||
|
"notionalFloor": "250000",
|
||||||
|
"maintMarginRatio": "0.1",
|
||||||
|
"cum": "17000.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 5,
|
||||||
|
"notionalFloor": 500000,
|
||||||
|
"notionalCap": 1000000,
|
||||||
|
"maintenanceMarginRate": 0.125,
|
||||||
|
"maxLeverage": 4,
|
||||||
|
"info": {
|
||||||
|
"bracket": "5",
|
||||||
|
"initialLeverage": "4",
|
||||||
|
"notionalCap": "1000000",
|
||||||
|
"notionalFloor": "500000",
|
||||||
|
"maintMarginRatio": "0.125",
|
||||||
|
"cum": "29500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 6,
|
||||||
|
"notionalFloor": 1000000,
|
||||||
|
"notionalCap": 2000000,
|
||||||
|
"maintenanceMarginRate": 0.25,
|
||||||
|
"maxLeverage": 2,
|
||||||
|
"info": {
|
||||||
|
"bracket": "6",
|
||||||
|
"initialLeverage": "2",
|
||||||
|
"notionalCap": "2000000",
|
||||||
|
"notionalFloor": "1000000",
|
||||||
|
"maintMarginRatio": "0.25",
|
||||||
|
"cum": "154500.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tier": 7,
|
||||||
|
"notionalFloor": 2000000,
|
||||||
|
"notionalCap": 30000000,
|
||||||
|
"maintenanceMarginRate": 0.5,
|
||||||
|
"maxLeverage": 1,
|
||||||
|
"info": {
|
||||||
|
"bracket": "7",
|
||||||
|
"initialLeverage": "1",
|
||||||
|
"notionalCap": "30000000",
|
||||||
|
"notionalFloor": "2000000",
|
||||||
|
"maintMarginRatio": "0.5",
|
||||||
|
"cum": "654500.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
})
|
})
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
default_conf['trading_mode'] = TradingMode.FUTURES
|
default_conf['trading_mode'] = TradingMode.FUTURES
|
||||||
default_conf['margin_mode'] = MarginMode.ISOLATED
|
default_conf['margin_mode'] = MarginMode.ISOLATED
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
|
||||||
exchange.fill_leverage_brackets()
|
exchange.fill_leverage_tiers()
|
||||||
|
|
||||||
assert exchange._leverage_brackets == {
|
assert exchange._leverage_tiers == {
|
||||||
'ADA/BUSD': [[0.0, 0.025, 0.0],
|
'ADA/BUSD': [
|
||||||
[100000.0, 0.05, 2500.0],
|
{
|
||||||
[500000.0, 0.1, 27500.0],
|
"min": 0,
|
||||||
[1000000.0, 0.15, 77499.99999999999],
|
"max": 100000,
|
||||||
[2000000.0, 0.25, 277500.0],
|
"mmr": 0.025,
|
||||||
[5000000.0, 0.5, 1527500.0]],
|
"lev": 20,
|
||||||
'BTC/USDT': [[0.0, 0.004, 0.0],
|
"maintAmt": 0.0
|
||||||
[50000.0, 0.005, 50.0],
|
},
|
||||||
[250000.0, 0.01, 1300.0],
|
{
|
||||||
[1000000.0, 0.025, 16300.000000000002],
|
"min": 100000,
|
||||||
[5000000.0, 0.05, 141300.0],
|
"max": 500000,
|
||||||
[20000000.0, 0.1, 1141300.0],
|
"mmr": 0.05,
|
||||||
[50000000.0, 0.125, 2391300.0],
|
"lev": 10,
|
||||||
[100000000.0, 0.15, 4891300.0],
|
"maintAmt": 2500.0
|
||||||
[200000000.0, 0.25, 24891300.0],
|
},
|
||||||
[300000000.0, 0.5, 99891300.0]],
|
{
|
||||||
"ZEC/USDT": [[0.0, 0.01, 0.0],
|
"min": 500000,
|
||||||
[5000.0, 0.025, 75.0],
|
"max": 1000000,
|
||||||
[25000.0, 0.05, 700.0],
|
"mmr": 0.1,
|
||||||
[100000.0, 0.1, 5700.0],
|
"lev": 5,
|
||||||
[250000.0, 0.125, 11949.999999999998],
|
"maintAmt": 27500.0
|
||||||
[1000000.0, 0.5, 386950.0]]
|
},
|
||||||
|
{
|
||||||
|
"min": 1000000,
|
||||||
|
"max": 2000000,
|
||||||
|
"mmr": 0.15,
|
||||||
|
"lev": 3,
|
||||||
|
"maintAmt": 77500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 2000000,
|
||||||
|
"max": 5000000,
|
||||||
|
"mmr": 0.25,
|
||||||
|
"lev": 2,
|
||||||
|
"maintAmt": 277500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"min": 5000000,
|
||||||
|
"max": 30000000,
|
||||||
|
"mmr": 0.5,
|
||||||
|
"lev": 1,
|
||||||
|
"maintAmt": 1527500.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ZEC/USDT": [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 50000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 50000,
|
||||||
|
'max': 150000,
|
||||||
|
'mmr': 0.025,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': 750.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 150000,
|
||||||
|
'max': 250000,
|
||||||
|
'mmr': 0.05,
|
||||||
|
'lev': 10,
|
||||||
|
'maintAmt': 4500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 250000,
|
||||||
|
'max': 500000,
|
||||||
|
'mmr': 0.1,
|
||||||
|
'lev': 5,
|
||||||
|
'maintAmt': 17000.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 500000,
|
||||||
|
'max': 1000000,
|
||||||
|
'mmr': 0.125,
|
||||||
|
'lev': 4,
|
||||||
|
'maintAmt': 29500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1000000,
|
||||||
|
'max': 2000000,
|
||||||
|
'mmr': 0.25,
|
||||||
|
'lev': 2,
|
||||||
|
'maintAmt': 154500.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 2000000,
|
||||||
|
'max': 30000000,
|
||||||
|
'mmr': 0.5,
|
||||||
|
'lev': 1,
|
||||||
|
'maintAmt': 654500.0
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.load_leverage_brackets = MagicMock()
|
api_mock.load_leverage_tiers = MagicMock()
|
||||||
type(api_mock).has = PropertyMock(return_value={'loadLeverageBrackets': True})
|
type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True})
|
||||||
|
|
||||||
ccxt_exceptionhandlers(
|
ccxt_exceptionhandlers(
|
||||||
mocker,
|
mocker,
|
||||||
default_conf,
|
default_conf,
|
||||||
api_mock,
|
api_mock,
|
||||||
"binance",
|
"binance",
|
||||||
"fill_leverage_brackets",
|
"fill_leverage_tiers",
|
||||||
"load_leverage_brackets"
|
"fetch_leverage_tiers",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_fill_leverage_brackets_binance_dryrun(default_conf, mocker):
|
def test_fill_leverage_tiers_binance_dryrun(default_conf, mocker, leverage_tiers):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
default_conf['trading_mode'] = TradingMode.FUTURES
|
default_conf['trading_mode'] = TradingMode.FUTURES
|
||||||
default_conf['margin_mode'] = MarginMode.ISOLATED
|
default_conf['margin_mode'] = MarginMode.ISOLATED
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
|
||||||
exchange.fill_leverage_brackets()
|
exchange.fill_leverage_tiers()
|
||||||
|
|
||||||
leverage_brackets = {
|
leverage_tiers = leverage_tiers
|
||||||
"1000SHIB/USDT": [
|
|
||||||
[0.0, 0.01, 0.0],
|
|
||||||
[5000.0, 0.025, 75.0],
|
|
||||||
[25000.0, 0.05, 700.0],
|
|
||||||
[100000.0, 0.1, 5700.0],
|
|
||||||
[250000.0, 0.125, 11949.999999999998],
|
|
||||||
[1000000.0, 0.5, 386950.0],
|
|
||||||
],
|
|
||||||
"1INCH/USDT": [
|
|
||||||
[0.0, 0.012, 0.0],
|
|
||||||
[5000.0, 0.025, 65.0],
|
|
||||||
[25000.0, 0.05, 690.0],
|
|
||||||
[100000.0, 0.1, 5690.0],
|
|
||||||
[250000.0, 0.125, 11939.999999999998],
|
|
||||||
[1000000.0, 0.5, 386940.0],
|
|
||||||
],
|
|
||||||
"AAVE/USDT": [
|
|
||||||
[0.0, 0.01, 0.0],
|
|
||||||
[50000.0, 0.02, 500.0],
|
|
||||||
[250000.0, 0.05, 8000.000000000001],
|
|
||||||
[1000000.0, 0.1, 58000.0],
|
|
||||||
[2000000.0, 0.125, 107999.99999999999],
|
|
||||||
[5000000.0, 0.1665, 315500.00000000006],
|
|
||||||
[10000000.0, 0.25, 1150500.0],
|
|
||||||
],
|
|
||||||
"ADA/BUSD": [
|
|
||||||
[0.0, 0.025, 0.0],
|
|
||||||
[100000.0, 0.05, 2500.0],
|
|
||||||
[500000.0, 0.1, 27500.0],
|
|
||||||
[1000000.0, 0.15, 77499.99999999999],
|
|
||||||
[2000000.0, 0.25, 277500.0],
|
|
||||||
[5000000.0, 0.5, 1527500.0],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value in leverage_brackets.items():
|
for key, value in leverage_tiers.items():
|
||||||
assert exchange._leverage_brackets[key] == value
|
assert exchange._leverage_tiers[key] == value
|
||||||
|
|
||||||
|
|
||||||
def test__set_leverage_binance(mocker, default_conf):
|
def test__set_leverage_binance(mocker, default_conf):
|
||||||
@ -403,43 +573,19 @@ def test__ccxt_config(default_conf, mocker, trading_mode, margin_mode, config):
|
|||||||
("BTC/USDT", 170.30, 0.004, 0),
|
("BTC/USDT", 170.30, 0.004, 0),
|
||||||
("BNB/BUSD", 999999.9, 0.1, 27500.0),
|
("BNB/BUSD", 999999.9, 0.1, 27500.0),
|
||||||
("BNB/USDT", 5000000.0, 0.15, 233035.0),
|
("BNB/USDT", 5000000.0, 0.15, 233035.0),
|
||||||
("BTC/USDT", 300000000.1, 0.5, 99891300.0),
|
("BTC/USDT", 600000000, 0.5, 1.997038E8),
|
||||||
])
|
])
|
||||||
def test_get_maintenance_ratio_and_amt_binance(
|
def test_get_maintenance_ratio_and_amt_binance(
|
||||||
default_conf,
|
default_conf,
|
||||||
mocker,
|
mocker,
|
||||||
|
leverage_tiers,
|
||||||
pair,
|
pair,
|
||||||
nominal_value,
|
nominal_value,
|
||||||
mm_ratio,
|
mm_ratio,
|
||||||
amt,
|
amt,
|
||||||
):
|
):
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
exchange._leverage_brackets = {
|
exchange._leverage_tiers = leverage_tiers
|
||||||
'BNB/BUSD': [[0.0, 0.025, 0.0],
|
|
||||||
[100000.0, 0.05, 2500.0],
|
|
||||||
[500000.0, 0.1, 27500.0],
|
|
||||||
[1000000.0, 0.15, 77500.0],
|
|
||||||
[2000000.0, 0.25, 277500.0],
|
|
||||||
[5000000.0, 0.5, 1527500.0]],
|
|
||||||
'BNB/USDT': [[0.0, 0.0065, 0.0],
|
|
||||||
[10000.0, 0.01, 35.0],
|
|
||||||
[50000.0, 0.02, 535.0],
|
|
||||||
[250000.0, 0.05, 8035.0],
|
|
||||||
[1000000.0, 0.1, 58035.0],
|
|
||||||
[2000000.0, 0.125, 108035.0],
|
|
||||||
[5000000.0, 0.15, 233035.0],
|
|
||||||
[10000000.0, 0.25, 1233035.0]],
|
|
||||||
'BTC/USDT': [[0.0, 0.004, 0.0],
|
|
||||||
[50000.0, 0.005, 50.0],
|
|
||||||
[250000.0, 0.01, 1300.0],
|
|
||||||
[1000000.0, 0.025, 16300.0],
|
|
||||||
[5000000.0, 0.05, 141300.0],
|
|
||||||
[20000000.0, 0.1, 1141300.0],
|
|
||||||
[50000000.0, 0.125, 2391300.0],
|
|
||||||
[100000000.0, 0.15, 4891300.0],
|
|
||||||
[200000000.0, 0.25, 24891300.0],
|
|
||||||
[300000000.0, 0.5, 99891300.0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
(result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, nominal_value)
|
(result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, nominal_value)
|
||||||
assert (round(result_ratio, 8), round(result_amt, 8)) == (mm_ratio, amt)
|
assert (round(result_ratio, 8), round(result_amt, 8)) == (mm_ratio, amt)
|
||||||
|
@ -24,10 +24,8 @@ EXCHANGES = {
|
|||||||
'stake_currency': 'USDT',
|
'stake_currency': 'USDT',
|
||||||
'hasQuoteVolume': False,
|
'hasQuoteVolume': False,
|
||||||
'timeframe': '1h',
|
'timeframe': '1h',
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False,
|
||||||
'spot': False,
|
'leverage_in_spot_market': False,
|
||||||
'futures': False,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'binance': {
|
'binance': {
|
||||||
'pair': 'BTC/USDT',
|
'pair': 'BTC/USDT',
|
||||||
@ -35,20 +33,16 @@ EXCHANGES = {
|
|||||||
'hasQuoteVolume': True,
|
'hasQuoteVolume': True,
|
||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'futures': True,
|
'futures': True,
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False,
|
||||||
'spot': False,
|
'leverage_in_spot_market': False,
|
||||||
'futures': False,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'kraken': {
|
'kraken': {
|
||||||
'pair': 'BTC/USDT',
|
'pair': 'BTC/USDT',
|
||||||
'stake_currency': 'USDT',
|
'stake_currency': 'USDT',
|
||||||
'hasQuoteVolume': True,
|
'hasQuoteVolume': True,
|
||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False,
|
||||||
'spot': True,
|
'leverage_in_spot_market': True,
|
||||||
'futures': True,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'ftx': {
|
'ftx': {
|
||||||
'pair': 'BTC/USD',
|
'pair': 'BTC/USD',
|
||||||
@ -57,20 +51,16 @@ EXCHANGES = {
|
|||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'futures_pair': 'BTC/USD:USD',
|
'futures_pair': 'BTC/USD:USD',
|
||||||
'futures': True,
|
'futures': True,
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False, # TODO: Set to True once implemented on CCXT
|
||||||
'spot': True,
|
'leverage_in_spot_market': True,
|
||||||
'futures': True,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'kucoin': {
|
'kucoin': {
|
||||||
'pair': 'BTC/USDT',
|
'pair': 'BTC/USDT',
|
||||||
'stake_currency': 'USDT',
|
'stake_currency': 'USDT',
|
||||||
'hasQuoteVolume': True,
|
'hasQuoteVolume': True,
|
||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False,
|
||||||
'spot': False,
|
'leverage_in_spot_market': True,
|
||||||
'futures': False,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'gateio': {
|
'gateio': {
|
||||||
'pair': 'BTC/USDT',
|
'pair': 'BTC/USDT',
|
||||||
@ -79,10 +69,8 @@ EXCHANGES = {
|
|||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'futures': True,
|
'futures': True,
|
||||||
'futures_pair': 'BTC/USDT:USDT',
|
'futures_pair': 'BTC/USDT:USDT',
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False, # TODO-lev: Set to True once implemented on CCXT
|
||||||
'spot': True,
|
'leverage_in_spot_market': True,
|
||||||
'futures': True,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'okx': {
|
'okx': {
|
||||||
'pair': 'BTC/USDT',
|
'pair': 'BTC/USDT',
|
||||||
@ -91,20 +79,16 @@ EXCHANGES = {
|
|||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'futures_pair': 'BTC/USDT:USDT',
|
'futures_pair': 'BTC/USDT:USDT',
|
||||||
'futures': True,
|
'futures': True,
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': True,
|
||||||
'spot': True,
|
'leverage_in_spot_market': True,
|
||||||
'futures': True,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'bitvavo': {
|
'bitvavo': {
|
||||||
'pair': 'BTC/EUR',
|
'pair': 'BTC/EUR',
|
||||||
'stake_currency': 'EUR',
|
'stake_currency': 'EUR',
|
||||||
'hasQuoteVolume': True,
|
'hasQuoteVolume': True,
|
||||||
'timeframe': '5m',
|
'timeframe': '5m',
|
||||||
'leverage_in_market': {
|
'leverage_tiers_public': False,
|
||||||
'spot': False,
|
'leverage_in_spot_market': False,
|
||||||
'futures': False,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,14 +120,14 @@ def exchange_futures(request, exchange_conf, class_mocker):
|
|||||||
exchange_conf = deepcopy(exchange_conf)
|
exchange_conf = deepcopy(exchange_conf)
|
||||||
exchange_conf['exchange']['name'] = request.param
|
exchange_conf['exchange']['name'] = request.param
|
||||||
exchange_conf['trading_mode'] = 'futures'
|
exchange_conf['trading_mode'] = 'futures'
|
||||||
exchange_conf['margin_mode'] = 'cross'
|
exchange_conf['margin_mode'] = 'isolated'
|
||||||
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
|
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
|
||||||
|
|
||||||
# TODO-lev: This mock should no longer be necessary once futures are enabled.
|
# TODO-lev: This mock should no longer be necessary once futures are enabled.
|
||||||
class_mocker.patch(
|
class_mocker.patch(
|
||||||
'freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode')
|
'freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode')
|
||||||
class_mocker.patch(
|
class_mocker.patch(
|
||||||
'freqtrade.exchange.binance.Binance.fill_leverage_brackets')
|
'freqtrade.exchange.binance.Binance.fill_leverage_tiers')
|
||||||
|
|
||||||
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
||||||
|
|
||||||
@ -329,21 +313,21 @@ class TestCCXTExchange():
|
|||||||
assert 0 < exchange.get_fee(pair, 'market', 'buy') < threshold
|
assert 0 < exchange.get_fee(pair, 'market', 'buy') < threshold
|
||||||
assert 0 < exchange.get_fee(pair, 'market', 'sell') < threshold
|
assert 0 < exchange.get_fee(pair, 'market', 'sell') < threshold
|
||||||
|
|
||||||
def test_get_max_leverage_spot(self, exchange):
|
def test_ccxt_get_max_leverage_spot(self, exchange):
|
||||||
spot, spot_name = exchange
|
spot, spot_name = exchange
|
||||||
if spot:
|
if spot:
|
||||||
leverage_in_market_spot = EXCHANGES[spot_name]['leverage_in_market']['spot']
|
leverage_in_market_spot = EXCHANGES[spot_name]['leverage_in_spot_market']
|
||||||
if leverage_in_market_spot:
|
if leverage_in_market_spot:
|
||||||
spot_pair = EXCHANGES[spot_name].get('pair', EXCHANGES[spot_name]['pair'])
|
spot_pair = EXCHANGES[spot_name].get('pair', EXCHANGES[spot_name]['pair'])
|
||||||
spot_leverage = spot.get_max_leverage(spot_pair, 20)
|
spot_leverage = spot.get_max_leverage(spot_pair, 20)
|
||||||
assert (isinstance(spot_leverage, float) or isinstance(spot_leverage, int))
|
assert (isinstance(spot_leverage, float) or isinstance(spot_leverage, int))
|
||||||
assert spot_leverage >= 1.0
|
assert spot_leverage >= 1.0
|
||||||
|
|
||||||
def test_get_max_leverage_futures(self, exchange_futures):
|
def test_ccxt_get_max_leverage_futures(self, exchange_futures):
|
||||||
futures, futures_name = exchange_futures
|
futures, futures_name = exchange_futures
|
||||||
if futures:
|
if futures:
|
||||||
leverage_in_market_futures = EXCHANGES[futures_name]['leverage_in_market']['futures']
|
leverage_tiers_public = EXCHANGES[futures_name]['leverage_tiers_public']
|
||||||
if leverage_in_market_futures:
|
if leverage_tiers_public:
|
||||||
futures_pair = EXCHANGES[futures_name].get(
|
futures_pair = EXCHANGES[futures_name].get(
|
||||||
'futures_pair',
|
'futures_pair',
|
||||||
EXCHANGES[futures_name]['pair']
|
EXCHANGES[futures_name]['pair']
|
||||||
@ -362,3 +346,76 @@ class TestCCXTExchange():
|
|||||||
contract_size = futures._get_contract_size(futures_pair)
|
contract_size = futures._get_contract_size(futures_pair)
|
||||||
assert (isinstance(contract_size, float) or isinstance(contract_size, int))
|
assert (isinstance(contract_size, float) or isinstance(contract_size, int))
|
||||||
assert contract_size >= 0.0
|
assert contract_size >= 0.0
|
||||||
|
|
||||||
|
def test_ccxt_load_leverage_tiers(self, exchange_futures):
|
||||||
|
futures, futures_name = exchange_futures
|
||||||
|
if futures and EXCHANGES[futures_name]['leverage_tiers_public']:
|
||||||
|
leverage_tiers = futures.load_leverage_tiers()
|
||||||
|
futures_pair = EXCHANGES[futures_name].get(
|
||||||
|
'futures_pair',
|
||||||
|
EXCHANGES[futures_name]['pair']
|
||||||
|
)
|
||||||
|
assert (isinstance(leverage_tiers, dict))
|
||||||
|
assert futures_pair in leverage_tiers
|
||||||
|
pair_tiers = leverage_tiers[futures_pair]
|
||||||
|
assert len(pair_tiers) > 0
|
||||||
|
oldLeverage = float('inf')
|
||||||
|
oldMaintenanceMarginRate = oldNotionalFloor = oldNotionalCap = -1
|
||||||
|
for tier in pair_tiers:
|
||||||
|
for key in [
|
||||||
|
'maintenanceMarginRate',
|
||||||
|
'notionalFloor',
|
||||||
|
'notionalCap',
|
||||||
|
'maxLeverage'
|
||||||
|
]:
|
||||||
|
assert key in tier
|
||||||
|
assert tier[key] >= 0.0
|
||||||
|
assert tier['notionalCap'] > tier['notionalFloor']
|
||||||
|
assert tier['maxLeverage'] <= oldLeverage
|
||||||
|
assert tier['maintenanceMarginRate'] >= oldMaintenanceMarginRate
|
||||||
|
assert tier['notionalFloor'] > oldNotionalFloor
|
||||||
|
assert tier['notionalCap'] > oldNotionalCap
|
||||||
|
oldLeverage = tier['maxLeverage']
|
||||||
|
oldMaintenanceMarginRate = tier['maintenanceMarginRate']
|
||||||
|
oldNotionalFloor = tier['notionalFloor']
|
||||||
|
oldNotionalCap = tier['notionalCap']
|
||||||
|
|
||||||
|
def test_ccxt_dry_run_liquidation_price(self, exchange_futures):
|
||||||
|
futures, futures_name = exchange_futures
|
||||||
|
if futures and EXCHANGES[futures_name]['leverage_tiers_public']:
|
||||||
|
|
||||||
|
futures_pair = EXCHANGES[futures_name].get(
|
||||||
|
'futures_pair',
|
||||||
|
EXCHANGES[futures_name]['pair']
|
||||||
|
)
|
||||||
|
|
||||||
|
liquidation_price = futures.dry_run_liquidation_price(
|
||||||
|
futures_pair,
|
||||||
|
40000,
|
||||||
|
False,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
)
|
||||||
|
assert (isinstance(liquidation_price, float))
|
||||||
|
assert liquidation_price >= 0.0
|
||||||
|
|
||||||
|
liquidation_price = futures.dry_run_liquidation_price(
|
||||||
|
futures_pair,
|
||||||
|
40000,
|
||||||
|
False,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
)
|
||||||
|
assert (isinstance(liquidation_price, float))
|
||||||
|
assert liquidation_price >= 0.0
|
||||||
|
|
||||||
|
def test_ccxt_get_max_pair_stake_amount(self, exchange_futures):
|
||||||
|
futures, futures_name = exchange_futures
|
||||||
|
if futures:
|
||||||
|
futures_pair = EXCHANGES[futures_name].get(
|
||||||
|
'futures_pair',
|
||||||
|
EXCHANGES[futures_name]['pair']
|
||||||
|
)
|
||||||
|
max_stake_amount = futures.get_max_pair_stake_amount(futures_pair, 40000)
|
||||||
|
assert (isinstance(max_stake_amount, float))
|
||||||
|
assert max_stake_amount >= 0.0
|
||||||
|
@ -689,7 +689,7 @@ def test_validate_stakecurrency_error(default_conf, mocker, caplog):
|
|||||||
def test_get_quote_currencies(default_conf, mocker):
|
def test_get_quote_currencies(default_conf, mocker):
|
||||||
ex = get_patched_exchange(mocker, default_conf)
|
ex = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
assert set(ex.get_quote_currencies()) == set(['USD', 'ETH', 'BTC', 'USDT'])
|
assert set(ex.get_quote_currencies()) == set(['USD', 'ETH', 'BTC', 'USDT', 'BUSD'])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('pair,expected', [
|
@pytest.mark.parametrize('pair,expected', [
|
||||||
@ -1207,9 +1207,20 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
assert exchange._set_leverage.call_count == 0
|
assert exchange._set_leverage.call_count == 0
|
||||||
assert exchange.set_margin_mode.call_count == 0
|
assert exchange.set_margin_mode.call_count == 0
|
||||||
|
|
||||||
|
api_mock.create_order = MagicMock(return_value={
|
||||||
|
'id': order_id,
|
||||||
|
'info': {
|
||||||
|
'foo': 'bar'
|
||||||
|
},
|
||||||
|
'symbol': 'ADA/USDT:USDT',
|
||||||
|
'amount': 1
|
||||||
|
})
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
exchange.trading_mode = TradingMode.FUTURES
|
exchange.trading_mode = TradingMode.FUTURES
|
||||||
|
exchange._set_leverage = MagicMock()
|
||||||
|
exchange.set_margin_mode = MagicMock()
|
||||||
order = exchange.create_order(
|
order = exchange.create_order(
|
||||||
pair='XLTCUSDT',
|
pair='ADA/USDT:USDT',
|
||||||
ordertype=ordertype,
|
ordertype=ordertype,
|
||||||
side=side,
|
side=side,
|
||||||
amount=1,
|
amount=1,
|
||||||
@ -2977,7 +2988,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"base_currencies,quote_currencies,tradable_only,active_only,spot_only,"
|
"base_currencies,quote_currencies,tradable_only,active_only,spot_only,"
|
||||||
"futures_only,expected_keys", [
|
"futures_only,expected_keys,test_comment", [
|
||||||
# Testing markets (in conftest.py):
|
# Testing markets (in conftest.py):
|
||||||
# 'BLK/BTC': 'active': True
|
# 'BLK/BTC': 'active': True
|
||||||
# 'BTT/BTC': 'active': True
|
# 'BTT/BTC': 'active': True
|
||||||
@ -2991,64 +3002,67 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
|||||||
# 'TKN/BTC': 'active' not set
|
# 'TKN/BTC': 'active' not set
|
||||||
# 'XLTCUSDT': 'active': True, not a pair
|
# 'XLTCUSDT': 'active': True, not a pair
|
||||||
# 'XRP/BTC': 'active': False
|
# 'XRP/BTC': 'active': False
|
||||||
# all markets
|
|
||||||
([], [], False, False, False, False,
|
([], [], False, False, False, False,
|
||||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT',
|
||||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']),
|
'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', 'ADA/USDT:USDT',
|
||||||
# all markets, only spot pairs
|
'ETH/USDT:USDT'],
|
||||||
|
'all markets'),
|
||||||
([], [], False, False, True, False,
|
([], [], False, False, True, False,
|
||||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']),
|
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC'],
|
||||||
# active markets
|
'all markets, only spot pairs'),
|
||||||
([], [], False, True, False, False,
|
([], [], False, True, False, False,
|
||||||
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
|
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
|
||||||
'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']),
|
'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', 'ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||||
# all pairs
|
'active markets'),
|
||||||
([], [], True, False, False, False,
|
([], [], True, False, False, False,
|
||||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']),
|
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC'],
|
||||||
# active pairs
|
'all pairs'),
|
||||||
([], [], True, True, False, False,
|
([], [], True, True, False, False,
|
||||||
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
|
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
|
||||||
'TKN/BTC', 'XRP/BTC']),
|
'TKN/BTC', 'XRP/BTC'],
|
||||||
# all markets, base=ETH, LTC
|
'active pairs'),
|
||||||
(['ETH', 'LTC'], [], False, False, False, False,
|
(['ETH', 'LTC'], [], False, False, False, False,
|
||||||
['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT',
|
||||||
# all markets, base=LTC
|
'ETH/USDT:USDT'],
|
||||||
|
'all markets, base=ETH, LTC'),
|
||||||
(['LTC'], [], False, False, False, False,
|
(['LTC'], [], False, False, False, False,
|
||||||
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT'],
|
||||||
# spot markets, base=LTC
|
'all markets, base=LTC'),
|
||||||
(['LTC'], [], False, False, True, False,
|
(['LTC'], [], False, False, True, False,
|
||||||
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT']),
|
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT'],
|
||||||
# all markets, quote=USDT
|
'spot markets, base=LTC'),
|
||||||
([], ['USDT'], False, False, False, False,
|
([], ['USDT'], False, False, False, False,
|
||||||
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT']),
|
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT', 'ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||||
# Futures markets, quote=USDT
|
'all markets, quote=USDT'),
|
||||||
([], ['USDT'], False, False, False, True,
|
([], ['USDT'], False, False, False, True,
|
||||||
['ETH/USDT', 'LTC/USDT']),
|
['ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||||
# all markets, quote=USDT, USD
|
'Futures markets, quote=USDT'),
|
||||||
([], ['USDT', 'USD'], False, False, False, False,
|
([], ['USDT', 'USD'], False, False, False, False,
|
||||||
['ETH/USDT', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
|
['ETH/USDT', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT', 'ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||||
# spot markets, quote=USDT, USD
|
'all markets, quote=USDT, USD'),
|
||||||
([], ['USDT', 'USD'], False, False, True, False,
|
([], ['USDT', 'USD'], False, False, True, False,
|
||||||
['ETH/USDT', 'LTC/USD', 'LTC/USDT']),
|
['ETH/USDT', 'LTC/USD', 'LTC/USDT'],
|
||||||
# all markets, base=LTC, quote=USDT
|
'spot markets, quote=USDT, USD'),
|
||||||
(['LTC'], ['USDT'], False, False, False, False,
|
(['LTC'], ['USDT'], False, False, False, False,
|
||||||
['LTC/USDT', 'XLTCUSDT']),
|
['LTC/USDT', 'XLTCUSDT'],
|
||||||
# all pairs, base=LTC, quote=USDT
|
'all markets, base=LTC, quote=USDT'),
|
||||||
(['LTC'], ['USDT'], True, False, False, False,
|
(['LTC'], ['USDT'], True, False, False, False,
|
||||||
['LTC/USDT']),
|
['LTC/USDT'],
|
||||||
# all markets, base=LTC, quote=USDT, NONEXISTENT
|
'all pairs, base=LTC, quote=USDT'),
|
||||||
(['LTC'], ['USDT', 'NONEXISTENT'], False, False, False, False,
|
(['LTC'], ['USDT', 'NONEXISTENT'], False, False, False, False,
|
||||||
['LTC/USDT', 'XLTCUSDT']),
|
['LTC/USDT', 'XLTCUSDT'],
|
||||||
# all markets, base=LTC, quote=NONEXISTENT
|
'all markets, base=LTC, quote=USDT, NONEXISTENT'),
|
||||||
(['LTC'], ['NONEXISTENT'], False, False, False, False,
|
(['LTC'], ['NONEXISTENT'], False, False, False, False,
|
||||||
[]),
|
[],
|
||||||
|
'all markets, base=LTC, quote=NONEXISTENT'),
|
||||||
])
|
])
|
||||||
def test_get_markets(default_conf, mocker, markets_static,
|
def test_get_markets(default_conf, mocker, markets_static,
|
||||||
base_currencies, quote_currencies, tradable_only, active_only,
|
base_currencies, quote_currencies, tradable_only, active_only,
|
||||||
spot_only, futures_only,
|
spot_only, futures_only, expected_keys,
|
||||||
expected_keys):
|
test_comment # Here for debugging purposes (Not used within method)
|
||||||
|
):
|
||||||
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
||||||
_init_ccxt=MagicMock(return_value=MagicMock()),
|
_init_ccxt=MagicMock(return_value=MagicMock()),
|
||||||
_load_async_markets=MagicMock(),
|
_load_async_markets=MagicMock(),
|
||||||
@ -3221,6 +3235,7 @@ def test_market_is_tradable(
|
|||||||
'future': futures,
|
'future': futures,
|
||||||
'swap': futures,
|
'swap': futures,
|
||||||
'margin': margin,
|
'margin': margin,
|
||||||
|
'linear': True,
|
||||||
**(add_dict),
|
**(add_dict),
|
||||||
}
|
}
|
||||||
assert ex.market_is_tradable(market) == expected_result
|
assert ex.market_is_tradable(market) == expected_result
|
||||||
@ -3486,9 +3501,9 @@ def test_set_margin_mode(mocker, default_conf, margin_mode):
|
|||||||
|
|
||||||
("binance", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
("binance", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
||||||
("gateio", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
("gateio", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
||||||
|
("okx", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
||||||
|
|
||||||
# * Remove once implemented
|
# * Remove once implemented
|
||||||
("okx", TradingMode.FUTURES, MarginMode.ISOLATED, True),
|
|
||||||
("binance", TradingMode.MARGIN, MarginMode.CROSS, True),
|
("binance", TradingMode.MARGIN, MarginMode.CROSS, True),
|
||||||
("binance", TradingMode.FUTURES, MarginMode.CROSS, True),
|
("binance", TradingMode.FUTURES, MarginMode.CROSS, True),
|
||||||
("kraken", TradingMode.MARGIN, MarginMode.CROSS, True),
|
("kraken", TradingMode.MARGIN, MarginMode.CROSS, True),
|
||||||
@ -3499,7 +3514,6 @@ def test_set_margin_mode(mocker, default_conf, margin_mode):
|
|||||||
("gateio", TradingMode.FUTURES, MarginMode.CROSS, True),
|
("gateio", TradingMode.FUTURES, MarginMode.CROSS, True),
|
||||||
|
|
||||||
# * Uncomment once implemented
|
# * Uncomment once implemented
|
||||||
# ("okx", TradingMode.FUTURES, MarginMode.ISOLATED, False),
|
|
||||||
# ("binance", TradingMode.MARGIN, MarginMode.CROSS, False),
|
# ("binance", TradingMode.MARGIN, MarginMode.CROSS, False),
|
||||||
# ("binance", TradingMode.FUTURES, MarginMode.CROSS, False),
|
# ("binance", TradingMode.FUTURES, MarginMode.CROSS, False),
|
||||||
# ("kraken", TradingMode.MARGIN, MarginMode.CROSS, False),
|
# ("kraken", TradingMode.MARGIN, MarginMode.CROSS, False),
|
||||||
@ -3561,9 +3575,12 @@ def test__ccxt_config(
|
|||||||
("LTC/BTC", 0.0, 1.0),
|
("LTC/BTC", 0.0, 1.0),
|
||||||
("TKN/USDT", 210.30, 1.0),
|
("TKN/USDT", 210.30, 1.0),
|
||||||
])
|
])
|
||||||
def test_get_max_leverage(default_conf, mocker, pair, nominal_value, max_lev):
|
def test_get_max_leverage_from_margin(default_conf, mocker, pair, nominal_value, max_lev):
|
||||||
# Binance has a different method of getting the max leverage
|
default_conf['trading_mode'] = 'margin'
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id="kraken")
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
api_mock = MagicMock()
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': False})
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="gateio")
|
||||||
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
||||||
|
|
||||||
|
|
||||||
@ -3834,13 +3851,13 @@ def test__fetch_and_calculate_funding_fees_datetime_called(
|
|||||||
('XLTCUSDT', 1, 'spot'),
|
('XLTCUSDT', 1, 'spot'),
|
||||||
('LTC/USD', 1, 'futures'),
|
('LTC/USD', 1, 'futures'),
|
||||||
('XLTCUSDT', 0.01, 'futures'),
|
('XLTCUSDT', 0.01, 'futures'),
|
||||||
('LTC/ETH', 1, 'futures'),
|
|
||||||
('ETH/USDT:USDT', 10, 'futures')
|
('ETH/USDT:USDT', 10, 'futures')
|
||||||
])
|
])
|
||||||
def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_mode):
|
def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_mode):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
default_conf['trading_mode'] = trading_mode
|
default_conf['trading_mode'] = trading_mode
|
||||||
default_conf['margin_mode'] = 'isolated'
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.markets', {
|
mocker.patch('freqtrade.exchange.Exchange.markets', {
|
||||||
'LTC/USD': {
|
'LTC/USD': {
|
||||||
'symbol': 'LTC/USD',
|
'symbol': 'LTC/USD',
|
||||||
@ -3850,15 +3867,11 @@ def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_m
|
|||||||
'symbol': 'XLTCUSDT',
|
'symbol': 'XLTCUSDT',
|
||||||
'contractSize': '0.01',
|
'contractSize': '0.01',
|
||||||
},
|
},
|
||||||
'LTC/ETH': {
|
|
||||||
'symbol': 'LTC/ETH',
|
|
||||||
},
|
|
||||||
'ETH/USDT:USDT': {
|
'ETH/USDT:USDT': {
|
||||||
'symbol': 'ETH/USDT:USDT',
|
'symbol': 'ETH/USDT:USDT',
|
||||||
'contractSize': '10',
|
'contractSize': '10',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
|
||||||
size = exchange._get_contract_size(pair)
|
size = exchange._get_contract_size(pair)
|
||||||
assert expected_size == size
|
assert expected_size == size
|
||||||
|
|
||||||
@ -3866,7 +3879,7 @@ def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_m
|
|||||||
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
||||||
('XLTCUSDT', 1, 'spot'),
|
('XLTCUSDT', 1, 'spot'),
|
||||||
('LTC/USD', 1, 'futures'),
|
('LTC/USD', 1, 'futures'),
|
||||||
('XLTCUSDT', 0.01, 'futures'),
|
('ADA/USDT:USDT', 0.01, 'futures'),
|
||||||
('LTC/ETH', 1, 'futures'),
|
('LTC/ETH', 1, 'futures'),
|
||||||
('ETH/USDT:USDT', 10, 'futures'),
|
('ETH/USDT:USDT', 10, 'futures'),
|
||||||
])
|
])
|
||||||
@ -3950,7 +3963,7 @@ def test__order_contracts_to_amount(
|
|||||||
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
@pytest.mark.parametrize('pair,contract_size,trading_mode', [
|
||||||
('XLTCUSDT', 1, 'spot'),
|
('XLTCUSDT', 1, 'spot'),
|
||||||
('LTC/USD', 1, 'futures'),
|
('LTC/USD', 1, 'futures'),
|
||||||
('XLTCUSDT', 0.01, 'futures'),
|
('ADA/USDT:USDT', 0.01, 'futures'),
|
||||||
('LTC/ETH', 1, 'futures'),
|
('LTC/ETH', 1, 'futures'),
|
||||||
('ETH/USDT:USDT', 10, 'futures'),
|
('ETH/USDT:USDT', 10, 'futures'),
|
||||||
])
|
])
|
||||||
@ -3985,7 +3998,7 @@ def test__trades_contracts_to_amount(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('pair,param_amount,param_size', [
|
@pytest.mark.parametrize('pair,param_amount,param_size', [
|
||||||
('XLTCUSDT', 40, 4000),
|
('ADA/USDT:USDT', 40, 4000),
|
||||||
('LTC/ETH', 30, 30),
|
('LTC/ETH', 30, 30),
|
||||||
('LTC/USD', 30, 30),
|
('LTC/USD', 30, 30),
|
||||||
('ETH/USDT:USDT', 10, 1),
|
('ETH/USDT:USDT', 10, 1),
|
||||||
@ -4001,6 +4014,7 @@ def test__amount_to_contracts(
|
|||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
default_conf['trading_mode'] = 'spot'
|
default_conf['trading_mode'] = 'spot'
|
||||||
default_conf['margin_mode'] = 'isolated'
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.markets', {
|
mocker.patch('freqtrade.exchange.Exchange.markets', {
|
||||||
'LTC/USD': {
|
'LTC/USD': {
|
||||||
'symbol': 'LTC/USD',
|
'symbol': 'LTC/USD',
|
||||||
@ -4018,7 +4032,6 @@ def test__amount_to_contracts(
|
|||||||
'contractSize': '10',
|
'contractSize': '10',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
|
||||||
result_size = exchange._amount_to_contracts(pair, param_amount)
|
result_size = exchange._amount_to_contracts(pair, param_amount)
|
||||||
assert result_size == param_amount
|
assert result_size == param_amount
|
||||||
result_amount = exchange._contracts_to_amount(pair, param_size)
|
result_amount = exchange._contracts_to_amount(pair, param_size)
|
||||||
@ -4210,6 +4223,7 @@ def test_get_max_pair_stake_amount(
|
|||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||||
assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0) == 20000
|
assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0) == 20000
|
||||||
|
assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0, 5) == 4000
|
||||||
assert exchange.get_max_pair_stake_amount('LTC/USDT:USDT', 2.0) == float('inf')
|
assert exchange.get_max_pair_stake_amount('LTC/USDT:USDT', 2.0) == float('inf')
|
||||||
assert exchange.get_max_pair_stake_amount('ETH/USDT:USDT', 2.0) == 200
|
assert exchange.get_max_pair_stake_amount('ETH/USDT:USDT', 2.0) == 200
|
||||||
assert exchange.get_max_pair_stake_amount('DOGE/USDT:USDT', 2.0) == 500
|
assert exchange.get_max_pair_stake_amount('DOGE/USDT:USDT', 2.0) == 500
|
||||||
@ -4220,3 +4234,275 @@ def test_get_max_pair_stake_amount(
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
mocker.patch('freqtrade.exchange.Exchange.markets', markets)
|
||||||
assert exchange.get_max_pair_stake_amount('BTC/USDT', 2.0) == 20000
|
assert exchange.get_max_pair_stake_amount('BTC/USDT', 2.0) == 20000
|
||||||
assert exchange.get_max_pair_stake_amount('ADA/USDT', 2.0) == 500
|
assert exchange.get_max_pair_stake_amount('ADA/USDT', 2.0) == 500
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('exchange_name', EXCHANGES)
|
||||||
|
def test_load_leverage_tiers(mocker, default_conf, leverage_tiers, exchange_name):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_leverage_tiers = MagicMock()
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True})
|
||||||
|
default_conf['dry_run'] = False
|
||||||
|
mocker.patch('freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode')
|
||||||
|
|
||||||
|
api_mock.fetch_leverage_tiers = MagicMock(return_value={
|
||||||
|
'ADA/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 500,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '500',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# SPOT
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
assert exchange.load_leverage_tiers() == {}
|
||||||
|
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
|
||||||
|
if exchange_name != 'binance':
|
||||||
|
# FUTURES has.fetchLeverageTiers == False
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': False})
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
assert exchange.load_leverage_tiers() == {}
|
||||||
|
|
||||||
|
# FUTURES regular
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True})
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
assert exchange.load_leverage_tiers() == {
|
||||||
|
'ADA/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 500,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '500',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
ccxt_exceptionhandlers(
|
||||||
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
api_mock,
|
||||||
|
exchange_name,
|
||||||
|
"load_leverage_tiers",
|
||||||
|
"fetch_leverage_tiers",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_leverage_tier(mocker, default_conf):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
|
tier = {
|
||||||
|
"tier": 1,
|
||||||
|
"notionalFloor": 0,
|
||||||
|
"notionalCap": 100000,
|
||||||
|
"maintenanceMarginRate": 0.025,
|
||||||
|
"maxLeverage": 20,
|
||||||
|
"info": {
|
||||||
|
"bracket": "1",
|
||||||
|
"initialLeverage": "20",
|
||||||
|
"notionalCap": "100000",
|
||||||
|
"notionalFloor": "0",
|
||||||
|
"maintMarginRatio": "0.025",
|
||||||
|
"cum": "0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert exchange.parse_leverage_tier(tier) == {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100000,
|
||||||
|
"mmr": 0.025,
|
||||||
|
"lev": 20,
|
||||||
|
"maintAmt": 0.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
tier2 = {
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 2000,
|
||||||
|
'maintenanceMarginRate': 0.01,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '2000',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'SHIB-USDT'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert exchange.parse_leverage_tier(tier2) == {
|
||||||
|
'min': 0,
|
||||||
|
'max': 2000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 75,
|
||||||
|
"maintAmt": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_maintenance_ratio_and_amt_exceptions(mocker, default_conf, leverage_tiers):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
|
||||||
|
exchange._leverage_tiers = leverage_tiers
|
||||||
|
with pytest.raises(
|
||||||
|
OperationalException,
|
||||||
|
match='nominal value can not be lower than 0',
|
||||||
|
):
|
||||||
|
exchange.get_maintenance_ratio_and_amt('1000SHIB/USDT', -1)
|
||||||
|
|
||||||
|
exchange._leverage_tiers = {}
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
InvalidOrderException,
|
||||||
|
match="Maintenance margin rate for 1000SHIB/USDT is unavailable for",
|
||||||
|
):
|
||||||
|
exchange.get_maintenance_ratio_and_amt('1000SHIB/USDT', 10000)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('pair,value,mmr,maintAmt', [
|
||||||
|
('ADA/BUSD', 500, 0.025, 0.0),
|
||||||
|
('ADA/BUSD', 20000000, 0.5, 1527500.0),
|
||||||
|
('ZEC/USDT', 500, 0.01, 0.0),
|
||||||
|
('ZEC/USDT', 20000000, 0.5, 654500.0),
|
||||||
|
])
|
||||||
|
def test_get_maintenance_ratio_and_amt(
|
||||||
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
leverage_tiers,
|
||||||
|
pair,
|
||||||
|
value,
|
||||||
|
mmr,
|
||||||
|
maintAmt
|
||||||
|
):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||||
|
exchange._leverage_tiers = leverage_tiers
|
||||||
|
exchange.get_maintenance_ratio_and_amt(pair, value) == (mmr, maintAmt)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
|
||||||
|
|
||||||
|
# Test Spot
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
|
assert exchange.get_max_leverage("BNB/USDT", 100.0) == 1.0
|
||||||
|
|
||||||
|
# Test Futures
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
|
|
||||||
|
exchange._leverage_tiers = leverage_tiers
|
||||||
|
|
||||||
|
assert exchange.get_max_leverage("BNB/BUSD", 1.0) == 20.0
|
||||||
|
assert exchange.get_max_leverage("BNB/USDT", 100.0) == 75.0
|
||||||
|
assert exchange.get_max_leverage("BTC/USDT", 170.30) == 125.0
|
||||||
|
assert isclose(exchange.get_max_leverage("BNB/BUSD", 99999.9), 5.000005)
|
||||||
|
assert isclose(exchange.get_max_leverage("BNB/USDT", 1500), 33.333333333333333)
|
||||||
|
assert exchange.get_max_leverage("BTC/USDT", 300000000) == 2.0
|
||||||
|
assert exchange.get_max_leverage("BTC/USDT", 600000000) == 1.0 # Last tier
|
||||||
|
|
||||||
|
assert exchange.get_max_leverage("SPONGE/USDT", 200) == 1.0 # Pair not in leverage_tiers
|
||||||
|
assert exchange.get_max_leverage("BTC/USDT", 0.0) == 125.0 # No stake amount
|
||||||
|
with pytest.raises(
|
||||||
|
InvalidOrderException,
|
||||||
|
match=r'Amount 1000000000.01 too high for BTC/USDT'
|
||||||
|
):
|
||||||
|
exchange.get_max_leverage("BTC/USDT", 1000000000.01)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'ftx', 'gateio', 'okx'])
|
||||||
|
def test__get_params(mocker, default_conf, exchange_name):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
exchange._params = {'test': True}
|
||||||
|
|
||||||
|
params1 = {'test': True}
|
||||||
|
params2 = {
|
||||||
|
'test': True,
|
||||||
|
'timeInForce': 'ioc',
|
||||||
|
'reduceOnly': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
if exchange_name == 'kraken':
|
||||||
|
params2['leverage'] = 3.0
|
||||||
|
|
||||||
|
if exchange_name == 'okx':
|
||||||
|
params2['tdMode'] = 'isolated'
|
||||||
|
|
||||||
|
assert exchange._get_params(
|
||||||
|
ordertype='market',
|
||||||
|
reduceOnly=False,
|
||||||
|
time_in_force='gtc',
|
||||||
|
leverage=1.0,
|
||||||
|
) == params1
|
||||||
|
|
||||||
|
assert exchange._get_params(
|
||||||
|
ordertype='market',
|
||||||
|
reduceOnly=False,
|
||||||
|
time_in_force='ioc',
|
||||||
|
leverage=1.0,
|
||||||
|
) == params1
|
||||||
|
|
||||||
|
assert exchange._get_params(
|
||||||
|
ordertype='limit',
|
||||||
|
reduceOnly=False,
|
||||||
|
time_in_force='gtc',
|
||||||
|
leverage=1.0,
|
||||||
|
) == params1
|
||||||
|
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
exchange._params = {'test': True}
|
||||||
|
|
||||||
|
assert exchange._get_params(
|
||||||
|
ordertype='limit',
|
||||||
|
reduceOnly=True,
|
||||||
|
time_in_force='ioc',
|
||||||
|
leverage=3.0,
|
||||||
|
) == params2
|
||||||
|
@ -64,3 +64,16 @@ def test_get_maintenance_ratio_and_amt_gateio(default_conf, mocker, pair, mm_rat
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert exchange.get_maintenance_ratio_and_amt(pair) == (mm_ratio, None)
|
assert exchange.get_maintenance_ratio_and_amt(pair) == (mm_ratio, None)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('pair,nominal_value,max_lev', [
|
||||||
|
("ETH/BTC", 0.0, 2.0),
|
||||||
|
("TKN/BTC", 100.0, 5.0),
|
||||||
|
("BLK/BTC", 173.31, 3.0),
|
||||||
|
("LTC/BTC", 0.0, 1.0),
|
||||||
|
("TKN/USDT", 210.30, 1.0),
|
||||||
|
])
|
||||||
|
def test_get_max_leverage_gateio(default_conf, mocker, pair, nominal_value, max_lev):
|
||||||
|
# Binance has a different method of getting the max leverage
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="gateio")
|
||||||
|
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
||||||
|
360
tests/exchange/test_okx.py
Normal file
360
tests/exchange/test_okx.py
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
from unittest.mock import MagicMock # , PropertyMock
|
||||||
|
|
||||||
|
from freqtrade.enums import MarginMode, TradingMode
|
||||||
|
from tests.conftest import get_patched_exchange
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_maintenance_ratio_and_amt_okx(
|
||||||
|
default_conf,
|
||||||
|
mocker,
|
||||||
|
):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
default_conf['dry_run'] = False
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Okx',
|
||||||
|
exchange_has=MagicMock(return_value=True),
|
||||||
|
load_leverage_tiers=MagicMock(return_value={
|
||||||
|
'ETH/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 2000,
|
||||||
|
'maintenanceMarginRate': 0.01,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '2000',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 2,
|
||||||
|
'notionalFloor': 2001,
|
||||||
|
'notionalCap': 4000,
|
||||||
|
'maintenanceMarginRate': 0.015,
|
||||||
|
'maxLeverage': 50,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.02',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '50',
|
||||||
|
'maxSz': '4000',
|
||||||
|
'minSz': '2001',
|
||||||
|
'mmr': '0.015',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '2',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 3,
|
||||||
|
'notionalFloor': 4001,
|
||||||
|
'notionalCap': 8000,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 20,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.05',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '20',
|
||||||
|
'maxSz': '8000',
|
||||||
|
'minSz': '4001',
|
||||||
|
'mmr': '0.02',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '3',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'ADA/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 500,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '500',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 2,
|
||||||
|
'notionalFloor': 501,
|
||||||
|
'notionalCap': 1000,
|
||||||
|
'maintenanceMarginRate': 0.025,
|
||||||
|
'maxLeverage': 50,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.02',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '50',
|
||||||
|
'maxSz': '1000',
|
||||||
|
'minSz': '501',
|
||||||
|
'mmr': '0.015',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '2',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 3,
|
||||||
|
'notionalFloor': 1001,
|
||||||
|
'notionalCap': 2000,
|
||||||
|
'maintenanceMarginRate': 0.03,
|
||||||
|
'maxLeverage': 20,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.05',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '20',
|
||||||
|
'maxSz': '2000',
|
||||||
|
'minSz': '1001',
|
||||||
|
'mmr': '0.02',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '3',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ETH/USDT:USDT', 2000) == (0.01, None)
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ETH/USDT:USDT', 2001) == (0.015, None)
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ETH/USDT:USDT', 4001) == (0.02, None)
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ETH/USDT:USDT', 8000) == (0.02, None)
|
||||||
|
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ADA/USDT:USDT', 1) == (0.02, None)
|
||||||
|
assert exchange.get_maintenance_ratio_and_amt('ADA/USDT:USDT', 2000) == (0.03, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_max_pair_stake_amount_okx(default_conf, mocker, leverage_tiers):
|
||||||
|
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="okx")
|
||||||
|
assert exchange.get_max_pair_stake_amount('BNB/BUSD', 1.0) == float('inf')
|
||||||
|
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="okx")
|
||||||
|
exchange._leverage_tiers = leverage_tiers
|
||||||
|
|
||||||
|
assert exchange.get_max_pair_stake_amount('BNB/BUSD', 1.0) == 30000000
|
||||||
|
assert exchange.get_max_pair_stake_amount('BNB/USDT', 1.0) == 50000000
|
||||||
|
assert exchange.get_max_pair_stake_amount('BTC/USDT', 1.0) == 1000000000
|
||||||
|
assert exchange.get_max_pair_stake_amount('BTC/USDT', 1.0, 10.0) == 100000000
|
||||||
|
|
||||||
|
assert exchange.get_max_pair_stake_amount('TTT/USDT', 1.0) == float('inf') # Not in tiers
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_leverage_tiers_okx(default_conf, mocker, markets):
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.fetch_leverage_tiers = MagicMock(side_effect=[
|
||||||
|
{
|
||||||
|
'ADA/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 500,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '500',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 2,
|
||||||
|
'notionalFloor': 501,
|
||||||
|
'notionalCap': 1000,
|
||||||
|
'maintenanceMarginRate': 0.025,
|
||||||
|
'maxLeverage': 50,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.02',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '50',
|
||||||
|
'maxSz': '1000',
|
||||||
|
'minSz': '501',
|
||||||
|
'mmr': '0.015',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '2',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 3,
|
||||||
|
'notionalFloor': 1001,
|
||||||
|
'notionalCap': 2000,
|
||||||
|
'maintenanceMarginRate': 0.03,
|
||||||
|
'maxLeverage': 20,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.05',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '20',
|
||||||
|
'maxSz': '2000',
|
||||||
|
'minSz': '1001',
|
||||||
|
'mmr': '0.02',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '3',
|
||||||
|
'uly': 'ADA-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'ETH/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'tier': 1,
|
||||||
|
'notionalFloor': 0,
|
||||||
|
'notionalCap': 2000,
|
||||||
|
'maintenanceMarginRate': 0.01,
|
||||||
|
'maxLeverage': 75,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.013',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '75',
|
||||||
|
'maxSz': '2000',
|
||||||
|
'minSz': '0',
|
||||||
|
'mmr': '0.01',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '1',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 2,
|
||||||
|
'notionalFloor': 2001,
|
||||||
|
'notionalCap': 4000,
|
||||||
|
'maintenanceMarginRate': 0.015,
|
||||||
|
'maxLeverage': 50,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.02',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '50',
|
||||||
|
'maxSz': '4000',
|
||||||
|
'minSz': '2001',
|
||||||
|
'mmr': '0.015',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '2',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'tier': 3,
|
||||||
|
'notionalFloor': 4001,
|
||||||
|
'notionalCap': 8000,
|
||||||
|
'maintenanceMarginRate': 0.02,
|
||||||
|
'maxLeverage': 20,
|
||||||
|
'info': {
|
||||||
|
'baseMaxLoan': '',
|
||||||
|
'imr': '0.05',
|
||||||
|
'instId': '',
|
||||||
|
'maxLever': '20',
|
||||||
|
'maxSz': '8000',
|
||||||
|
'minSz': '4001',
|
||||||
|
'mmr': '0.02',
|
||||||
|
'optMgnFactor': '0',
|
||||||
|
'quoteMaxLoan': '',
|
||||||
|
'tier': '3',
|
||||||
|
'uly': 'ETH-USDT'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
])
|
||||||
|
default_conf['trading_mode'] = 'futures'
|
||||||
|
default_conf['margin_mode'] = 'isolated'
|
||||||
|
default_conf['stake_currency'] = 'USDT'
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
|
||||||
|
exchange.trading_mode = TradingMode.FUTURES
|
||||||
|
exchange.margin_mode = MarginMode.ISOLATED
|
||||||
|
exchange.markets = markets
|
||||||
|
# Initialization of load_leverage_tiers happens as part of exchange init.
|
||||||
|
assert exchange._leverage_tiers == {
|
||||||
|
'ADA/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 500,
|
||||||
|
'mmr': 0.02,
|
||||||
|
'lev': 75,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 501,
|
||||||
|
'max': 1000,
|
||||||
|
'mmr': 0.025,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 1001,
|
||||||
|
'max': 2000,
|
||||||
|
'mmr': 0.03,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'ETH/USDT:USDT': [
|
||||||
|
{
|
||||||
|
'min': 0,
|
||||||
|
'max': 2000,
|
||||||
|
'mmr': 0.01,
|
||||||
|
'lev': 75,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 2001,
|
||||||
|
'max': 4000,
|
||||||
|
'mmr': 0.015,
|
||||||
|
'lev': 50,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'min': 4001,
|
||||||
|
'max': 8000,
|
||||||
|
'mmr': 0.02,
|
||||||
|
'lev': 20,
|
||||||
|
'maintAmt': None
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
@ -852,6 +852,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
|||||||
'spaces': ['all']
|
'spaces': ['all']
|
||||||
})
|
})
|
||||||
hyperopt = Hyperopt(hyperopt_conf)
|
hyperopt = Hyperopt(hyperopt_conf)
|
||||||
|
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||||
|
|
||||||
|
@ -720,8 +720,8 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker)
|
|||||||
(False, 'futures', 'binance', 'isolated', 0.05, 8.167171717171717),
|
(False, 'futures', 'binance', 'isolated', 0.05, 8.167171717171717),
|
||||||
(True, 'futures', 'gateio', 'isolated', 0.05, 11.7804274688304),
|
(True, 'futures', 'gateio', 'isolated', 0.05, 11.7804274688304),
|
||||||
(False, 'futures', 'gateio', 'isolated', 0.05, 8.181423084697796),
|
(False, 'futures', 'gateio', 'isolated', 0.05, 8.181423084697796),
|
||||||
# (True, 'futures', 'okex', 'isolated', 11.87413417771621),
|
(True, 'futures', 'okx', 'isolated', 0.0, 11.87413417771621),
|
||||||
# (False, 'futures', 'okex', 'isolated', 8.085708510208207),
|
(False, 'futures', 'okx', 'isolated', 0.0, 8.085708510208207),
|
||||||
])
|
])
|
||||||
def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
||||||
limit_order_open, is_short, trading_mode,
|
limit_order_open, is_short, trading_mode,
|
||||||
@ -774,9 +774,16 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
|
|||||||
}),
|
}),
|
||||||
create_order=enter_mm,
|
create_order=enter_mm,
|
||||||
get_min_pair_stake_amount=MagicMock(return_value=1),
|
get_min_pair_stake_amount=MagicMock(return_value=1),
|
||||||
|
get_max_pair_stake_amount=MagicMock(return_value=500000),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
get_funding_fees=MagicMock(return_value=0),
|
get_funding_fees=MagicMock(return_value=0),
|
||||||
name=exchange_name
|
name=exchange_name,
|
||||||
|
get_maintenance_ratio_and_amt=MagicMock(return_value=(0.01, 0.01)),
|
||||||
|
get_max_leverage=MagicMock(return_value=10),
|
||||||
|
)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Okx',
|
||||||
|
get_max_pair_stake_amount=MagicMock(return_value=500000),
|
||||||
)
|
)
|
||||||
pair = 'ETH/USDT'
|
pair = 'ETH/USDT'
|
||||||
|
|
||||||
@ -5088,6 +5095,7 @@ def test_update_funding_fees(
|
|||||||
create_order=enter_mm,
|
create_order=enter_mm,
|
||||||
get_min_pair_stake_amount=MagicMock(return_value=1),
|
get_min_pair_stake_amount=MagicMock(return_value=1),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
|
get_maintenance_ratio_and_amt=MagicMock(return_value=(0.01, 0.01)),
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
Loading…
Reference in New Issue
Block a user