Merge branch 'leverage-tiers' of https://github.com/samgermain/freqtrade into leverage-tiers

This commit is contained in:
Sam Germain 2022-02-16 03:50:24 -06:00
commit dbd2df6406
9 changed files with 81 additions and 45 deletions

View File

@ -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 = (

View File

@ -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'],

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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",
)

View File

@ -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',

View File

@ -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",
)

View File

@ -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,