Improve futures detection, add ccxt-compat test

This commit is contained in:
Matthias 2021-11-15 19:43:43 +01:00
parent 4e9b83e170
commit 75eccea88d
6 changed files with 39 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import json
import logging import logging
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
import arrow import arrow
import ccxt import ccxt
@ -119,6 +119,10 @@ class Binance(Exchange):
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
def market_is_future(self, market: Dict[str, Any]) -> bool:
# TODO-lev: This should be unified in ccxt to "swap"...
return market.get('future', False) is True
@retrier @retrier
def fill_leverage_brackets(self): def fill_leverage_brackets(self):
""" """

View File

@ -340,7 +340,7 @@ 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('future', False) is True or market.get('futures') is True return market.get('swap', 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

View File

@ -159,3 +159,7 @@ class Ftx(Exchange):
if order['type'] == 'stop': if order['type'] == 'stop':
return safe_value_fallback2(order, order, 'id_stop', 'id') return safe_value_fallback2(order, order, 'id_stop', 'id')
return order['id'] return order['id']
def market_is_future(self, market: Dict[str, Any]) -> bool:
# TODO-lev: This should be unified in ccxt to "swap"...
return market.get('future', False) is True

View File

@ -778,6 +778,7 @@ def get_markets():
'quote': 'USDT', 'quote': 'USDT',
'spot': True, 'spot': True,
'future': True, 'future': True,
'swap': True,
'margin': True, 'margin': True,
'type': 'spot', 'type': 'spot',
'precision': { 'precision': {
@ -805,6 +806,7 @@ def get_markets():
'active': False, 'active': False,
'spot': True, 'spot': True,
'future': True, 'future': True,
'swap': True,
'margin': True, 'margin': True,
'type': 'spot', 'type': 'spot',
'precision': { 'precision': {

View File

@ -26,6 +26,7 @@ EXCHANGES = {
'pair': 'BTC/USDT', 'pair': 'BTC/USDT',
'hasQuoteVolume': True, 'hasQuoteVolume': True,
'timeframe': '5m', 'timeframe': '5m',
'futures': True,
}, },
'kraken': { 'kraken': {
'pair': 'BTC/USDT', 'pair': 'BTC/USDT',
@ -82,13 +83,19 @@ def exchange(request, exchange_conf):
@pytest.fixture(params=EXCHANGES, scope="class") @pytest.fixture(params=EXCHANGES, scope="class")
def exchange_futures(request, exchange_conf): def exchange_futures(request, exchange_conf, class_mocker):
if not EXCHANGES[request.param].get('futures') is True: if not EXCHANGES[request.param].get('futures') is True:
yield None, request.param yield None, request.param
else: else:
exchange_conf['exchange']['name'] = request.param exchange_conf['exchange']['name'] = request.param
exchange_conf['trading_mode'] = 'futures' exchange_conf['trading_mode'] = 'futures'
exchange_conf['collateral'] = 'cross' exchange_conf['collateral'] = 'cross'
# TODO-lev This mock should no longer be necessary once futures are enabled.
class_mocker.patch(
'freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_collateral')
class_mocker.patch(
'freqtrade.exchange.binance.Binance.fill_leverage_brackets')
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True) exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
yield exchange, request.param yield exchange, request.param
@ -103,6 +110,20 @@ class TestCCXTExchange():
markets = exchange.markets markets = exchange.markets
assert pair in markets assert pair in markets
assert isinstance(markets[pair], dict) assert isinstance(markets[pair], dict)
assert exchange.market_is_spot(markets[pair])
def test_load_markets_futures(self, exchange_futures):
exchange, exchangename = exchange_futures
if not exchange:
# exchange_futures only returns values for supported exchanges
return
pair = EXCHANGES[exchangename]['pair']
pair = EXCHANGES[exchangename].get('futures_pair', pair)
markets = exchange.markets
assert pair in markets
assert isinstance(markets[pair], dict)
assert exchange.market_is_future(markets[pair])
def test_ccxt_fetch_tickers(self, exchange): def test_ccxt_fetch_tickers(self, exchange):
exchange, exchangename = exchange exchange, exchangename = exchange

View File

@ -2985,6 +2985,10 @@ def test_timeframe_to_next_date():
("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False), ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False),
("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'margin', {}, False), ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'margin', {}, False),
("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True), ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True),
("BTC/USDT:USDT", 'BTC', 'USD', "okex", False, False, True, 'spot', {}, False),
("BTC/USDT:USDT", 'BTC', 'USD', "okex", False, False, True, 'margin', {}, False),
("BTC/USDT:USDT", 'BTC', 'USD', "okex", False, False, True, 'futures', {}, True),
]) ])
def test_market_is_tradable( def test_market_is_tradable(
mocker, default_conf, market_symbol, base, mocker, default_conf, market_symbol, base,
@ -2999,6 +3003,7 @@ def test_market_is_tradable(
'quote': quote, 'quote': quote,
'spot': spot, 'spot': spot,
'future': futures, 'future': futures,
'swap': futures,
'margin': margin, 'margin': margin,
**(add_dict), **(add_dict),
} }