redid get_max_leverage
This commit is contained in:
parent
720a86778e
commit
a99cf2eeed
@ -153,27 +153,24 @@ class Binance(Exchange):
|
|||||||
try:
|
try:
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
leverage_brackets_path = (
|
leverage_brackets_path = (
|
||||||
Path(__file__).parent / 'binance_leverage_brackets.json'
|
Path(__file__).parent / 'binance_leverage_tiers.json'
|
||||||
)
|
)
|
||||||
with open(leverage_brackets_path) as json_file:
|
with open(leverage_brackets_path) as json_file:
|
||||||
leverage_brackets = json.load(json_file)
|
leverage_brackets = json.load(json_file)
|
||||||
else:
|
else:
|
||||||
leverage_brackets = self._api.load_leverage_brackets()
|
leverage_brackets = self._api.fetch_leverage_tiers()
|
||||||
|
|
||||||
for pair, brkts in leverage_brackets.items():
|
for pair, tiers in leverage_brackets.items():
|
||||||
[amt, old_ratio] = [0.0, 0.0]
|
|
||||||
brackets = []
|
brackets = []
|
||||||
for [notional_floor, mm_ratio] in brkts:
|
for tier in tiers:
|
||||||
amt = (
|
info = tier['info']
|
||||||
(float(notional_floor) * (float(mm_ratio) - float(old_ratio)))
|
brackets.append({
|
||||||
+ amt
|
'min': tier['notionalFloor'],
|
||||||
) if old_ratio else 0.0
|
'max': tier['notionalCap'],
|
||||||
old_ratio = mm_ratio
|
'mmr': tier['maintenanceMarginRatio'],
|
||||||
brackets.append((
|
'lev': tier['maxLeverage'],
|
||||||
float(notional_floor),
|
'maintAmt': float(info['cum']) if 'cum' in info else None,
|
||||||
float(mm_ratio),
|
})
|
||||||
amt,
|
|
||||||
))
|
|
||||||
self._leverage_brackets[pair] = brackets
|
self._leverage_brackets[pair] = brackets
|
||||||
except ccxt.DDoSProtection as e:
|
except ccxt.DDoSProtection as e:
|
||||||
raise DDosProtection(e) from e
|
raise DDosProtection(e) from e
|
||||||
@ -189,29 +186,69 @@ class Binance(Exchange):
|
|||||||
:param pair: The base/quote currency pair being traded
|
:param pair: The base/quote currency pair being traded
|
||||||
:stake_amount: The total value of the traders margin_mode in quote currency
|
: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 self.trading_mode == TradingMode.SPOT:
|
||||||
if pair not in self._leverage_brackets:
|
|
||||||
return 1.0
|
return 1.0
|
||||||
pair_brackets = self._leverage_brackets[pair]
|
|
||||||
num_brackets = len(pair_brackets)
|
if self._api.has['fetchLeverageTiers']:
|
||||||
min_amount = 0.0
|
|
||||||
for bracket_num in range(num_brackets):
|
# Checks and edge cases
|
||||||
[notional_floor, mm_ratio, _] = pair_brackets[bracket_num]
|
if stake_amount is None:
|
||||||
lev = 1.0
|
raise OperationalException(
|
||||||
if mm_ratio != 0:
|
'binance.get_max_leverage requires argument stake_amount')
|
||||||
lev = 1.0/mm_ratio
|
if pair not in self._leverage_brackets: # Not a leveraged market
|
||||||
|
return 1.0
|
||||||
|
if stake_amount == 0:
|
||||||
|
return self._leverage_brackets[pair][0]['lev'] # Max lev for lowest amount
|
||||||
|
|
||||||
|
pair_brackets = self._leverage_brackets[pair]
|
||||||
|
num_brackets = len(pair_brackets)
|
||||||
|
|
||||||
|
for bracket_index in range(num_brackets):
|
||||||
|
|
||||||
|
bracket = pair_brackets[bracket_index]
|
||||||
|
lev = bracket['lev']
|
||||||
|
|
||||||
|
if bracket_index < num_brackets - 1:
|
||||||
|
next_bracket = pair_brackets[bracket_index+1]
|
||||||
|
next_floor = next_bracket['min'] / next_bracket['lev']
|
||||||
|
if next_floor > stake_amount: # Next bracket min too high for stake amount
|
||||||
|
return min((bracket['max'] / stake_amount), lev)
|
||||||
|
#
|
||||||
|
# With the two leverage brackets 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 bracket
|
||||||
|
if stake_amount > bracket['max']: # If stake is > than max tradeable amount
|
||||||
|
raise InvalidOrderException(f'Amount {stake_amount} too high for {pair}')
|
||||||
|
else:
|
||||||
|
return bracket['lev']
|
||||||
|
|
||||||
|
raise OperationalException(
|
||||||
|
'Looped through all tiers without finding a max leverage. Should never be reached'
|
||||||
|
)
|
||||||
|
|
||||||
|
else: # Search markets.limits for max lev
|
||||||
|
market = self.markets[pair]
|
||||||
|
if market['limits']['leverage']['max'] is not None:
|
||||||
|
return market['limits']['leverage']['max']
|
||||||
else:
|
else:
|
||||||
logger.warning(f"mm_ratio for {pair} with notional floor {notional_floor} is 0")
|
return 1.0 # Default if max leverage cannot be found
|
||||||
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(
|
||||||
|
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
@ -91,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[Tuple[float, float, Optional(float)]]] = {}
|
self._leverage_brackets: 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)
|
||||||
|
|
||||||
@ -2193,7 +2193,7 @@ class Exchange:
|
|||||||
if nominal_value >= notional_floor:
|
if nominal_value >= notional_floor:
|
||||||
return (mm_ratio, amt)
|
return (mm_ratio, amt)
|
||||||
raise OperationalException("nominal value can not be lower than 0")
|
raise OperationalException("nominal value can not be lower than 0")
|
||||||
# The lowest notional_floor for any pair in loadLeverageBrackets is always 0 because it
|
# The lowest notional_floor for any pair in fetch_leverage_tiers is always 0 because it
|
||||||
# describes the min amt for a bracket, and the lowest bracket will always go down to 0
|
# describes the min amt for a bracket, and the lowest bracket will always go down to 0
|
||||||
else:
|
else:
|
||||||
info = self.markets[pair]['info']
|
info = self.markets[pair]['info']
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3486,9 +3486,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),
|
||||||
|
("okex", 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 +3499,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),
|
||||||
|
@ -24,25 +24,25 @@ from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re
|
|||||||
|
|
||||||
def generate_result_metrics():
|
def generate_result_metrics():
|
||||||
return {
|
return {
|
||||||
'trade_count': 1,
|
'trade_count': 1,
|
||||||
'total_trades': 1,
|
'total_trades': 1,
|
||||||
'avg_profit': 0.1,
|
'avg_profit': 0.1,
|
||||||
'total_profit': 0.001,
|
'total_profit': 0.001,
|
||||||
'profit': 0.01,
|
'profit': 0.01,
|
||||||
'duration': 20.0,
|
'duration': 20.0,
|
||||||
'wins': 1,
|
'wins': 1,
|
||||||
'draws': 0,
|
'draws': 0,
|
||||||
'losses': 0,
|
'losses': 0,
|
||||||
'profit_mean': 0.01,
|
'profit_mean': 0.01,
|
||||||
'profit_total_abs': 0.001,
|
'profit_total_abs': 0.001,
|
||||||
'profit_total': 0.01,
|
'profit_total': 0.01,
|
||||||
'holding_avg': timedelta(minutes=20),
|
'holding_avg': timedelta(minutes=20),
|
||||||
'max_drawdown': 0.001,
|
'max_drawdown': 0.001,
|
||||||
'max_drawdown_abs': 0.001,
|
'max_drawdown_abs': 0.001,
|
||||||
'loss': 0.001,
|
'loss': 0.001,
|
||||||
'is_initial_point': 0.001,
|
'is_initial_point': 0.001,
|
||||||
'is_best': 1,
|
'is_best': 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
|
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> 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)
|
||||||
|
|
||||||
|
@ -5085,6 +5085,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