Merge branch 'leverage-tiers' of https://github.com/samgermain/freqtrade into leverage-tiers
This commit is contained in:
commit
dbd2df6406
@ -246,6 +246,7 @@ class Binance(Exchange):
|
||||
raise OperationalException(
|
||||
"Freqtrade only supports isolated futures for leverage trading")
|
||||
|
||||
@retrier
|
||||
def load_leverage_tiers(self) -> Dict[str, List[Dict]]:
|
||||
if self._config['dry_run']:
|
||||
leverage_tiers_path = (
|
||||
|
@ -1858,6 +1858,7 @@ class Exchange:
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
@retrier
|
||||
def load_leverage_tiers(self) -> Dict[str, List[Dict]]:
|
||||
if self.trading_mode == TradingMode.FUTURES and self.exchange_has('fetchLeverageTiers'):
|
||||
try:
|
||||
@ -1874,7 +1875,6 @@ class Exchange:
|
||||
else:
|
||||
return {}
|
||||
|
||||
@retrier
|
||||
def fill_leverage_tiers(self) -> None:
|
||||
"""
|
||||
Assigns property _leverage_tiers to a dictionary of information about the leverage
|
||||
@ -1888,7 +1888,7 @@ class Exchange:
|
||||
self._leverage_tiers[pair] = pair_tiers
|
||||
|
||||
def parse_leverage_tier(self, tier) -> Dict:
|
||||
info = tier['info']
|
||||
info = tier.get('info', {})
|
||||
return {
|
||||
'min': tier['notionalFloor'],
|
||||
'max': tier['notionalCap'],
|
||||
|
@ -1,9 +1,12 @@
|
||||
import logging
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
import ccxt
|
||||
|
||||
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.common import retrier
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -29,6 +32,25 @@ class Okx(Exchange):
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
]
|
||||
|
||||
def _get_params(
|
||||
self,
|
||||
ordertype: str,
|
||||
leverage: float,
|
||||
reduceOnly: bool,
|
||||
time_in_force: str = 'gtc',
|
||||
) -> Dict:
|
||||
# TODO-lev: Test me
|
||||
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(
|
||||
self,
|
||||
pair: str,
|
||||
@ -40,13 +62,22 @@ class Okx(Exchange):
|
||||
raise OperationalException(
|
||||
f"{self.name}.margin_mode must be set for {self.trading_mode.value}"
|
||||
)
|
||||
self._api.set_leverage(
|
||||
leverage,
|
||||
pair,
|
||||
params={
|
||||
"mgnMode": self.margin_mode.value,
|
||||
"posSide": "long" if side == "buy" else "short",
|
||||
})
|
||||
try:
|
||||
# TODO-lev: Test me properly (check mgnMode passed)
|
||||
self._api.set_leverage(
|
||||
leverage=leverage,
|
||||
symbol=pair,
|
||||
params={
|
||||
"mgnMode": self.margin_mode.value,
|
||||
# "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,
|
||||
@ -64,6 +95,7 @@ class Okx(Exchange):
|
||||
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:
|
||||
@ -82,11 +114,9 @@ class Okx(Exchange):
|
||||
f"Initializing leverage_tiers for {len(symbols)} markets. "
|
||||
"This will take about a minute.")
|
||||
|
||||
for symbol in symbols:
|
||||
res = self._api.fetchLeverageTiers(symbol)
|
||||
tiers[symbol] = []
|
||||
for tier in res[symbol]:
|
||||
tiers[symbol].append(self.parse_leverage_tier(tier))
|
||||
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
|
||||
|
@ -231,9 +231,9 @@ def test_list_markets(mocker, markets_static, capsys):
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 10 active markets: "
|
||||
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, NEO/BTC, "
|
||||
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||
assert ("Exchange Bittrex has 12 active markets: "
|
||||
"ADA/USDT:USDT, BLK/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, "
|
||||
"LTC/ETH, LTC/USD, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||
in captured.out)
|
||||
|
||||
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
|
||||
start_list_markets(pargs, False)
|
||||
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)
|
||||
|
||||
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)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 12 markets: "
|
||||
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, "
|
||||
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||
assert ("Exchange Bittrex has 14 markets: "
|
||||
"ADA/USDT:USDT, BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, ETH/USDT:USDT, "
|
||||
"LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
|
||||
in captured.out)
|
||||
|
||||
# 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)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 6 active markets with ETH, LTC as base currencies: "
|
||||
"ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
|
||||
assert ("Exchange Bittrex has 7 active markets with ETH, LTC as base currencies: "
|
||||
"ETH/BTC, ETH/USDT, ETH/USDT:USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, base=LTC
|
||||
@ -323,8 +323,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 3 active markets with USDT, USD as quote currencies: "
|
||||
"ETH/USDT, LTC/USD, XLTCUSDT.\n"
|
||||
assert ("Exchange Bittrex has 5 active markets with USDT, USD as quote currencies: "
|
||||
"ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, LTC/USD, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# active markets, quote=USDT
|
||||
@ -336,8 +336,8 @@ def test_list_markets(mocker, markets_static, capsys):
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 2 active markets with USDT as quote currency: "
|
||||
"ETH/USDT, XLTCUSDT.\n"
|
||||
assert ("Exchange Bittrex has 4 active markets with USDT as quote currency: "
|
||||
"ADA/USDT:USDT, ETH/USDT, ETH/USDT:USDT, XLTCUSDT.\n"
|
||||
in captured.out)
|
||||
|
||||
# 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)
|
||||
captured = capsys.readouterr()
|
||||
assert ("Exchange Bittrex has 10 active markets:\n"
|
||||
assert ("Exchange Bittrex has 12 active markets:\n"
|
||||
in captured.out)
|
||||
|
||||
# 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)
|
||||
captured = capsys.readouterr()
|
||||
assert ('["BLK/BTC","ETH/BTC","ETH/USDT","LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC",'
|
||||
'"TKN/BTC","XLTCUSDT","XRP/BTC"]'
|
||||
assert ('["ADA/USDT:USDT","BLK/BTC","ETH/BTC","ETH/USDT","ETH/USDT:USDT",'
|
||||
'"LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC","TKN/BTC","XLTCUSDT","XRP/BTC"]'
|
||||
in captured.out)
|
||||
|
||||
# Test --print-csv
|
||||
|
@ -891,8 +891,8 @@ def get_markets():
|
||||
'future': True,
|
||||
'swap': True,
|
||||
'margin': True,
|
||||
'linear': False,
|
||||
'inverse': True,
|
||||
'linear': None,
|
||||
'inverse': False,
|
||||
'type': 'spot',
|
||||
'contractSize': None,
|
||||
'taker': 0.0006,
|
||||
@ -1398,7 +1398,9 @@ def markets_static():
|
||||
# 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!
|
||||
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()
|
||||
return {m: all_markets[m] for m in static_markets}
|
||||
|
||||
|
@ -486,7 +486,7 @@ def test_fill_leverage_tiers_binance(default_conf, mocker):
|
||||
api_mock,
|
||||
"binance",
|
||||
"fill_leverage_tiers",
|
||||
"fetch_leverage_tiers"
|
||||
"fetch_leverage_tiers",
|
||||
)
|
||||
|
||||
|
||||
|
@ -341,9 +341,9 @@ class TestCCXTExchange():
|
||||
|
||||
def test_ccxt_get_max_leverage_futures(self, exchange_futures):
|
||||
futures, futures_name = exchange_futures
|
||||
# TODO-lev: binance, gateio, and okx test
|
||||
if futures:
|
||||
leverage_in_market_futures = EXCHANGES[futures_name]['leverage_in_market']['futures']
|
||||
# TODO-lev: binance, gateio, and okx don't have leverage_in_market
|
||||
if leverage_in_market_futures:
|
||||
futures_pair = EXCHANGES[futures_name].get(
|
||||
'futures_pair',
|
||||
|
@ -3003,8 +3003,9 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
# 'XLTCUSDT': 'active': True, not a pair
|
||||
# 'XRP/BTC': 'active': False
|
||||
([], [], False, False, False, False,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC'],
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT',
|
||||
'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', 'ADA/USDT:USDT',
|
||||
'ETH/USDT:USDT'],
|
||||
'all markets'),
|
||||
([], [], False, False, True, False,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||
@ -3012,7 +3013,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
'all markets, only spot pairs'),
|
||||
([], [], False, True, False, False,
|
||||
['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'],
|
||||
'active markets'),
|
||||
([], [], True, False, False, False,
|
||||
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
|
||||
@ -3023,7 +3024,8 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
'TKN/BTC', 'XRP/BTC'],
|
||||
'active pairs'),
|
||||
(['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',
|
||||
'ETH/USDT:USDT'],
|
||||
'all markets, base=ETH, LTC'),
|
||||
(['LTC'], [], False, False, False, False,
|
||||
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT'],
|
||||
@ -3032,13 +3034,13 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT'],
|
||||
'spot markets, base=LTC'),
|
||||
([], ['USDT'], False, False, False, False,
|
||||
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT'],
|
||||
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT', 'ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||
'all markets, quote=USDT'),
|
||||
([], ['USDT'], False, False, False, True,
|
||||
['ETH/USDT', 'LTC/USDT'],
|
||||
['ADA/USDT:USDT', 'ETH/USDT:USDT'],
|
||||
'Futures markets, quote=USDT'),
|
||||
([], ['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'],
|
||||
'all markets, quote=USDT, USD'),
|
||||
([], ['USDT', 'USD'], False, False, True, False,
|
||||
['ETH/USDT', 'LTC/USD', 'LTC/USDT'],
|
||||
@ -4247,7 +4249,7 @@ def test_load_leverage_tiers(mocker, default_conf, leverage_tiers):
|
||||
mocker,
|
||||
default_conf,
|
||||
api_mock,
|
||||
"binance",
|
||||
"ftx",
|
||||
"load_leverage_tiers",
|
||||
"fetch_leverage_tiers",
|
||||
)
|
||||
|
@ -309,7 +309,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets):
|
||||
exchange.trading_mode = TradingMode.FUTURES
|
||||
exchange.margin_mode = MarginMode.ISOLATED
|
||||
exchange.markets = markets
|
||||
assert exchange.load_leverage_tiers() == {
|
||||
# Initialization of load_leverage_tiers happens as part of exchange init.
|
||||
exchange._leverage_tiers == {
|
||||
'ADA/USDT:USDT': [
|
||||
{
|
||||
'tier': 1,
|
||||
|
Loading…
Reference in New Issue
Block a user