mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
442 lines
15 KiB
Python
442 lines
15 KiB
Python
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]
|
|
EXCHANGE_WS_FIXTURE_TYPE = Tuple[Exchange, str, str]
|
|
|
|
|
|
# Exchanges that should be tested online
|
|
EXCHANGES = {
|
|
"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",
|
|
},
|
|
{
|
|
"symbol": "SOLUSDT",
|
|
"orderId": 3551312894,
|
|
"orderListId": -1,
|
|
"clientOrderId": "x-R4DD3S8297c73a11ccb9dc8f2811ba",
|
|
"transactTime": 1674493798550,
|
|
"price": "15.50000000",
|
|
"origQty": "1.10000000",
|
|
"executedQty": "1.10000000",
|
|
"cummulativeQuoteQty": "17.05",
|
|
"status": "FILLED",
|
|
"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",
|
|
},
|
|
],
|
|
"sample_my_trades": [
|
|
{
|
|
"id": "123412341234",
|
|
"create_time": "167997798",
|
|
"create_time_ms": "167997798825.566200",
|
|
"currency_pair": "ETH_USDT",
|
|
"side": "sell",
|
|
"role": "taker",
|
|
"amount": "0.0115",
|
|
"price": "1712.63",
|
|
"order_id": "1234123412",
|
|
"fee": "0.0",
|
|
"fee_currency": "USDT",
|
|
"point_fee": "0.03939049",
|
|
"gt_fee": "0.0",
|
|
"amend_text": "-",
|
|
}
|
|
],
|
|
},
|
|
"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,
|
|
"orderbook_max_entries": 50,
|
|
"leverage_tiers_public": True,
|
|
"leverage_in_spot_market": True,
|
|
"sample_order": [
|
|
{
|
|
"orderId": "1274754916287346280",
|
|
"orderLinkId": "1666798627015730",
|
|
"symbol": "SOLUSDT",
|
|
"createdTime": "1674493798550",
|
|
"price": "15.5",
|
|
"qty": "1.1",
|
|
"orderType": "Limit",
|
|
"side": "Buy",
|
|
"orderStatus": "New",
|
|
"timeInForce": "GTC",
|
|
"accountId": "5555555",
|
|
"execQty": "0",
|
|
"orderCategory": "0",
|
|
}
|
|
],
|
|
},
|
|
"bitmart": {
|
|
"pair": "BTC/USDT",
|
|
"stake_currency": "USDT",
|
|
"hasQuoteVolume": True,
|
|
"timeframe": "1h",
|
|
"orderbook_max_entries": 50,
|
|
},
|
|
"htx": {
|
|
"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,
|
|
},
|
|
"bingx": {
|
|
"pair": "BTC/USDT",
|
|
"stake_currency": "USDT",
|
|
"hasQuoteVolume": True,
|
|
"timeframe": "1h",
|
|
"futures": False,
|
|
"sample_order": [
|
|
{
|
|
"symbol": "SOL-USDT",
|
|
"orderId": "1762393630149869568",
|
|
"transactTime": "1674493798550",
|
|
"price": "15.5",
|
|
"stopPrice": "0",
|
|
"origQty": "1.1",
|
|
"executedQty": "1.1",
|
|
"cummulativeQuoteQty": "17.05",
|
|
"status": "FILLED",
|
|
"type": "LIMIT",
|
|
"side": "BUY",
|
|
"clientOrderID": "",
|
|
},
|
|
{
|
|
"symbol": "SOL-USDT",
|
|
"orderId": "1762393630149869568",
|
|
"transactTime": "1674493798550",
|
|
"price": "15.5",
|
|
"stopPrice": "0",
|
|
"origQty": "1.1",
|
|
"executedQty": "1.1",
|
|
"cummulativeQuoteQty": "17.05",
|
|
"status": "FILLED",
|
|
"type": "MARKET",
|
|
"side": "BUY",
|
|
"clientOrderID": "",
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
@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,
|
|
"wsProxy": proxy,
|
|
}
|
|
return config1
|
|
|
|
return config
|
|
|
|
|
|
def get_exchange(exchange_name, exchange_conf):
|
|
exchange_conf = set_test_proxy(
|
|
exchange_conf, EXCHANGES[exchange_name].get("use_ci_proxy", False)
|
|
)
|
|
exchange_conf["exchange"]["name"] = exchange_name
|
|
exchange_conf["stake_currency"] = EXCHANGES[exchange_name]["stake_currency"]
|
|
exchange = ExchangeResolver.load_exchange(
|
|
exchange_conf, validate=True, load_leverage_tiers=True
|
|
)
|
|
|
|
return exchange, exchange_name
|
|
|
|
|
|
def get_futures_exchange(exchange_name, exchange_conf, class_mocker):
|
|
if EXCHANGES[exchange_name].get("futures") is not True:
|
|
pytest.skip(f"Exchange {exchange_name} does not support futures.")
|
|
else:
|
|
exchange_conf = deepcopy(exchange_conf)
|
|
exchange_conf = set_test_proxy(
|
|
exchange_conf, EXCHANGES[exchange_name].get("use_ci_proxy", False)
|
|
)
|
|
exchange_conf["trading_mode"] = "futures"
|
|
exchange_conf["margin_mode"] = "isolated"
|
|
|
|
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")
|
|
|
|
return get_exchange(exchange_name, exchange_conf)
|
|
|
|
|
|
@pytest.fixture(params=EXCHANGES, scope="class")
|
|
def exchange(request, exchange_conf, class_mocker):
|
|
class_mocker.patch("freqtrade.exchange.bybit.Bybit.additional_exchange_init")
|
|
return get_exchange(request.param, exchange_conf)
|
|
|
|
|
|
@pytest.fixture(params=EXCHANGES, scope="class")
|
|
def exchange_futures(request, exchange_conf, class_mocker):
|
|
return get_futures_exchange(request.param, exchange_conf, class_mocker)
|
|
|
|
|
|
@pytest.fixture(params=["spot", "futures"], scope="class")
|
|
def exchange_mode(request):
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(params=EXCHANGES, scope="class")
|
|
def exchange_ws(request, exchange_conf, exchange_mode, class_mocker):
|
|
class_mocker.patch("freqtrade.exchange.bybit.Bybit.additional_exchange_init")
|
|
exchange_conf["exchange"]["enable_ws"] = True
|
|
if exchange_mode == "spot":
|
|
exchange, name = get_exchange(request.param, exchange_conf)
|
|
pair = EXCHANGES[request.param]["pair"]
|
|
elif EXCHANGES[request.param].get("futures"):
|
|
exchange, name = get_futures_exchange(
|
|
request.param, exchange_conf, class_mocker=class_mocker
|
|
)
|
|
pair = EXCHANGES[request.param]["futures_pair"]
|
|
else:
|
|
pytest.skip("Exchange does not support futures.")
|
|
|
|
if not exchange._has_watch_ohlcv:
|
|
pytest.skip("Exchange does not support watch_ohlcv.")
|
|
yield exchange, name, pair
|
|
exchange.close()
|