Refactor online test fixtures into separate conftest module

This commit is contained in:
Matthias
2023-08-10 07:17:31 +02:00
parent 20763daa74
commit ea257e3cbb
2 changed files with 331 additions and 326 deletions

View File

@@ -0,0 +1,329 @@
from copy import deepcopy
from pathlib import Path
from typing import Tuple
import pytest
from freqtrade.constants import Config
from freqtrade.exchange.exchange import Exchange
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
from tests.conftest import EXMS, get_default_conf_usdt
EXCHANGE_FIXTURE_TYPE = Tuple[Exchange, str]
# Exchanges that should be tested online
EXCHANGES = {
'bittrex': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': False,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
},
'binance': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'use_ci_proxy': True,
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': True,
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
'trades_lookback_hours': 4,
'private_methods': [
'fapiPrivateGetPositionSideDual',
'fapiPrivateGetMultiAssetsMargin'
],
'sample_order': [{
"symbol": "SOLUSDT",
"orderId": 3551312894,
"orderListId": -1,
"clientOrderId": "x-R4DD3S8297c73a11ccb9dc8f2811ba",
"transactTime": 1674493798550,
"price": "15.50000000",
"origQty": "1.10000000",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"workingTime": 1674493798550,
"fills": [],
"selfTradePreventionMode": "NONE",
}]
},
'binanceus': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': False,
'sample_order': [{
"symbol": "SOLUSDT",
"orderId": 3551312894,
"orderListId": -1,
"clientOrderId": "x-R4DD3S8297c73a11ccb9dc8f2811ba",
"transactTime": 1674493798550,
"price": "15.50000000",
"origQty": "1.10000000",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"workingTime": 1674493798550,
"fills": [],
"selfTradePreventionMode": "NONE",
}]
},
'kraken': {
'pair': 'BTC/USD',
'stake_currency': 'USD',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': True,
'trades_lookback_hours': 12,
},
'kucoin': {
'pair': 'XRP/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': True,
'sample_order': [
{'id': '63d6742d0adc5570001d2bbf7'}, # create order
{
'id': '63d6742d0adc5570001d2bbf7',
'symbol': 'SOL-USDT',
'opType': 'DEAL',
'type': 'limit',
'side': 'buy',
'price': '15.5',
'size': '1.1',
'funds': '0',
'dealFunds': '17.05',
'dealSize': '1.1',
'fee': '0.000065252',
'feeCurrency': 'USDT',
'stp': '',
'stop': '',
'stopTriggered': False,
'stopPrice': '0',
'timeInForce': 'GTC',
'postOnly': False,
'hidden': False,
'iceberg': False,
'visibleSize': '0',
'cancelAfter': 0,
'channel': 'API',
'clientOid': '0a053870-11bf-41e5-be61-b272a4cb62e1',
'remark': None,
'tags': 'partner:ccxt',
'isActive': False,
'cancelExist': False,
'createdAt': 1674493798550,
'tradeType': 'TRADE'
}],
},
'gate': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': True,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'sample_order': [
{
"id": "276266139423",
"text": "apiv4",
"create_time": "1674493798",
"update_time": "1674493798",
"create_time_ms": "1674493798550",
"update_time_ms": "1674493798550",
"status": "closed",
"currency_pair": "SOL_USDT",
"type": "limit",
"account": "spot",
"side": "buy",
"amount": "1.1",
"price": "15.5",
"time_in_force": "gtc",
"iceberg": "0",
"left": "0",
"fill_price": "17.05",
"filled_total": "17.05",
"avg_deal_price": "15.5",
"fee": "0.0000018",
"fee_currency": "SOL",
"point_fee": "0",
"gt_fee": "0",
"gt_maker_fee": "0",
"gt_taker_fee": "0.0015",
"gt_discount": True,
"rebated_fee": "0",
"rebated_fee_currency": "USDT"
},
{
# market order
'id': '276401180529',
'text': 'apiv4',
'create_time': '1674493798',
'update_time': '1674493798',
'create_time_ms': '1674493798550',
'update_time_ms': '1674493798550',
'status': 'cancelled',
'currency_pair': 'SOL_USDT',
'type': 'market',
'account': 'spot',
'side': 'buy',
'amount': '17.05',
'price': '0',
'time_in_force': 'ioc',
'iceberg': '0',
'left': '0.0000000016228',
'fill_price': '17.05',
'filled_total': '17.05',
'avg_deal_price': '15.5',
'fee': '0',
'fee_currency': 'SOL',
'point_fee': '0.0199999999967544',
'gt_fee': '0',
'gt_maker_fee': '0',
'gt_taker_fee': '0',
'gt_discount': False,
'rebated_fee': '0',
'rebated_fee_currency': 'USDT'
}
],
},
'okx': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': False,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'private_methods': ['fetch_accounts'],
},
'bybit': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'use_ci_proxy': True,
'timeframe': '1h',
'futures_pair': 'BTC/USDT:USDT',
'futures': True,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'sample_order': [
{
"orderId": "1274754916287346280",
"orderLinkId": "1666798627015730",
"symbol": "SOLUSDT",
"createTime": "1674493798550",
"orderPrice": "15.5",
"orderQty": "1.1",
"orderType": "LIMIT",
"side": "BUY",
"status": "NEW",
"timeInForce": "GTC",
"accountId": "5555555",
"execQty": "0",
"orderCategory": "0"
}
]
},
'huobi': {
'pair': 'ETH/BTC',
'stake_currency': 'BTC',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': False,
},
'bitvavo': {
'pair': 'BTC/EUR',
'stake_currency': 'EUR',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
},
}
@pytest.fixture(scope="class")
def exchange_conf():
config = get_default_conf_usdt((Path(__file__).parent / "testdata").resolve())
config['exchange']['pair_whitelist'] = []
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
config['dry_run'] = False
config['entry_pricing']['use_order_book'] = True
config['exit_pricing']['use_order_book'] = True
return config
def set_test_proxy(config: Config, use_proxy: bool) -> Config:
# Set proxy to test in CI.
import os
if use_proxy and (proxy := os.environ.get('CI_WEB_PROXY')):
config1 = deepcopy(config)
config1['exchange']['ccxt_config'] = {
"httpsProxy": proxy,
}
return config1
return config
@pytest.fixture(params=EXCHANGES, scope="class")
def exchange(request, exchange_conf):
exchange_conf = set_test_proxy(
exchange_conf, EXCHANGES[request.param].get('use_ci_proxy', False))
exchange_conf['exchange']['name'] = request.param
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
exchange = ExchangeResolver.load_exchange(exchange_conf, validate=True)
yield exchange, request.param
@pytest.fixture(params=EXCHANGES, scope="class")
def exchange_futures(request, exchange_conf, class_mocker):
if EXCHANGES[request.param].get('futures') is not True:
pytest.skip(f"Exchange {request.param} does not support futures.")
else:
exchange_conf = set_test_proxy(
exchange_conf, EXCHANGES[request.param].get('use_ci_proxy', False))
exchange_conf = deepcopy(exchange_conf)
exchange_conf['exchange']['name'] = request.param
exchange_conf['trading_mode'] = 'futures'
exchange_conf['margin_mode'] = 'isolated'
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
class_mocker.patch(
'freqtrade.exchange.binance.Binance.fill_leverage_tiers')
class_mocker.patch(f'{EXMS}.fetch_trading_fees')
class_mocker.patch('freqtrade.exchange.okx.Okx.additional_exchange_init')
class_mocker.patch('freqtrade.exchange.binance.Binance.additional_exchange_init')
class_mocker.patch('freqtrade.exchange.bybit.Bybit.additional_exchange_init')
class_mocker.patch(f'{EXMS}.load_cached_leverage_tiers', return_value=None)
class_mocker.patch(f'{EXMS}.cache_leverage_tiers')
exchange = ExchangeResolver.load_exchange(
exchange_conf, validate=True, load_leverage_tiers=True)
yield exchange, request.param

View File

@@ -5,338 +5,14 @@ However, these tests should give a good idea to determine if a new exchange is
suitable to run with freqtrade. suitable to run with freqtrade.
""" """
from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Tuple
import pytest import pytest
from freqtrade.constants import Config
from freqtrade.enums import CandleType from freqtrade.enums import CandleType
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
from freqtrade.exchange.exchange import Exchange, timeframe_to_msecs from freqtrade.exchange.exchange import timeframe_to_msecs
from freqtrade.resolvers.exchange_resolver import ExchangeResolver from tests.exchange_online.conftest import EXCHANGE_FIXTURE_TYPE, EXCHANGES
from tests.conftest import EXMS, get_default_conf_usdt
EXCHANGE_FIXTURE_TYPE = Tuple[Exchange, str]
# Exchanges that should be tested
EXCHANGES = {
'bittrex': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': False,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
},
'binance': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'use_ci_proxy': True,
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': True,
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
'trades_lookback_hours': 4,
'private_methods': [
'fapiPrivateGetPositionSideDual',
'fapiPrivateGetMultiAssetsMargin'
],
'sample_order': [{
"symbol": "SOLUSDT",
"orderId": 3551312894,
"orderListId": -1,
"clientOrderId": "x-R4DD3S8297c73a11ccb9dc8f2811ba",
"transactTime": 1674493798550,
"price": "15.50000000",
"origQty": "1.10000000",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"workingTime": 1674493798550,
"fills": [],
"selfTradePreventionMode": "NONE",
}]
},
'binanceus': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': False,
'sample_order': [{
"symbol": "SOLUSDT",
"orderId": 3551312894,
"orderListId": -1,
"clientOrderId": "x-R4DD3S8297c73a11ccb9dc8f2811ba",
"transactTime": 1674493798550,
"price": "15.50000000",
"origQty": "1.10000000",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"workingTime": 1674493798550,
"fills": [],
"selfTradePreventionMode": "NONE",
}]
},
'kraken': {
'pair': 'BTC/USD',
'stake_currency': 'USD',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': True,
'trades_lookback_hours': 12,
},
'kucoin': {
'pair': 'XRP/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': True,
'sample_order': [
{'id': '63d6742d0adc5570001d2bbf7'}, # create order
{
'id': '63d6742d0adc5570001d2bbf7',
'symbol': 'SOL-USDT',
'opType': 'DEAL',
'type': 'limit',
'side': 'buy',
'price': '15.5',
'size': '1.1',
'funds': '0',
'dealFunds': '17.05',
'dealSize': '1.1',
'fee': '0.000065252',
'feeCurrency': 'USDT',
'stp': '',
'stop': '',
'stopTriggered': False,
'stopPrice': '0',
'timeInForce': 'GTC',
'postOnly': False,
'hidden': False,
'iceberg': False,
'visibleSize': '0',
'cancelAfter': 0,
'channel': 'API',
'clientOid': '0a053870-11bf-41e5-be61-b272a4cb62e1',
'remark': None,
'tags': 'partner:ccxt',
'isActive': False,
'cancelExist': False,
'createdAt': 1674493798550,
'tradeType': 'TRADE'
}],
},
'gate': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': True,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'sample_order': [
{
"id": "276266139423",
"text": "apiv4",
"create_time": "1674493798",
"update_time": "1674493798",
"create_time_ms": "1674493798550",
"update_time_ms": "1674493798550",
"status": "closed",
"currency_pair": "SOL_USDT",
"type": "limit",
"account": "spot",
"side": "buy",
"amount": "1.1",
"price": "15.5",
"time_in_force": "gtc",
"iceberg": "0",
"left": "0",
"fill_price": "17.05",
"filled_total": "17.05",
"avg_deal_price": "15.5",
"fee": "0.0000018",
"fee_currency": "SOL",
"point_fee": "0",
"gt_fee": "0",
"gt_maker_fee": "0",
"gt_taker_fee": "0.0015",
"gt_discount": True,
"rebated_fee": "0",
"rebated_fee_currency": "USDT"
},
{
# market order
'id': '276401180529',
'text': 'apiv4',
'create_time': '1674493798',
'update_time': '1674493798',
'create_time_ms': '1674493798550',
'update_time_ms': '1674493798550',
'status': 'cancelled',
'currency_pair': 'SOL_USDT',
'type': 'market',
'account': 'spot',
'side': 'buy',
'amount': '17.05',
'price': '0',
'time_in_force': 'ioc',
'iceberg': '0',
'left': '0.0000000016228',
'fill_price': '17.05',
'filled_total': '17.05',
'avg_deal_price': '15.5',
'fee': '0',
'fee_currency': 'SOL',
'point_fee': '0.0199999999967544',
'gt_fee': '0',
'gt_maker_fee': '0',
'gt_taker_fee': '0',
'gt_discount': False,
'rebated_fee': '0',
'rebated_fee_currency': 'USDT'
}
],
},
'okx': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': True,
'futures_pair': 'BTC/USDT:USDT',
'hasQuoteVolumeFutures': False,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'private_methods': ['fetch_accounts'],
},
'bybit': {
'pair': 'BTC/USDT',
'stake_currency': 'USDT',
'hasQuoteVolume': True,
'use_ci_proxy': True,
'timeframe': '1h',
'futures_pair': 'BTC/USDT:USDT',
'futures': True,
'leverage_tiers_public': True,
'leverage_in_spot_market': True,
'sample_order': [
{
"orderId": "1274754916287346280",
"orderLinkId": "1666798627015730",
"symbol": "SOLUSDT",
"createTime": "1674493798550",
"orderPrice": "15.5",
"orderQty": "1.1",
"orderType": "LIMIT",
"side": "BUY",
"status": "NEW",
"timeInForce": "GTC",
"accountId": "5555555",
"execQty": "0",
"orderCategory": "0"
}
]
},
'huobi': {
'pair': 'ETH/BTC',
'stake_currency': 'BTC',
'hasQuoteVolume': True,
'timeframe': '1h',
'futures': False,
},
'bitvavo': {
'pair': 'BTC/EUR',
'stake_currency': 'EUR',
'hasQuoteVolume': True,
'timeframe': '1h',
'leverage_tiers_public': False,
'leverage_in_spot_market': False,
},
}
@pytest.fixture(scope="class")
def exchange_conf():
config = get_default_conf_usdt((Path(__file__).parent / "testdata").resolve())
config['exchange']['pair_whitelist'] = []
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
config['dry_run'] = False
config['entry_pricing']['use_order_book'] = True
config['exit_pricing']['use_order_book'] = True
return config
def set_test_proxy(config: Config, use_proxy: bool) -> Config:
# Set proxy to test in CI.
import os
if use_proxy and (proxy := os.environ.get('CI_WEB_PROXY')):
config1 = deepcopy(config)
config1['exchange']['ccxt_config'] = {
"httpsProxy": proxy,
}
return config1
return config
@pytest.fixture(params=EXCHANGES, scope="class")
def exchange(request, exchange_conf):
exchange_conf = set_test_proxy(
exchange_conf, EXCHANGES[request.param].get('use_ci_proxy', False))
exchange_conf['exchange']['name'] = request.param
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
exchange = ExchangeResolver.load_exchange(exchange_conf, validate=True)
yield exchange, request.param
@pytest.fixture(params=EXCHANGES, scope="class")
def exchange_futures(request, exchange_conf, class_mocker):
if EXCHANGES[request.param].get('futures') is not True:
pytest.skip(f"Exchange {request.param} does not support futures.")
else:
exchange_conf = set_test_proxy(
exchange_conf, EXCHANGES[request.param].get('use_ci_proxy', False))
exchange_conf = deepcopy(exchange_conf)
exchange_conf['exchange']['name'] = request.param
exchange_conf['trading_mode'] = 'futures'
exchange_conf['margin_mode'] = 'isolated'
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
class_mocker.patch(
'freqtrade.exchange.binance.Binance.fill_leverage_tiers')
class_mocker.patch(f'{EXMS}.fetch_trading_fees')
class_mocker.patch('freqtrade.exchange.okx.Okx.additional_exchange_init')
class_mocker.patch('freqtrade.exchange.binance.Binance.additional_exchange_init')
class_mocker.patch('freqtrade.exchange.bybit.Bybit.additional_exchange_init')
class_mocker.patch(f'{EXMS}.load_cached_leverage_tiers', return_value=None)
class_mocker.patch(f'{EXMS}.cache_leverage_tiers')
exchange = ExchangeResolver.load_exchange(
exchange_conf, validate=True, load_leverage_tiers=True)
yield exchange, request.param
@pytest.mark.longrun @pytest.mark.longrun