Merge branch 'freqtrade:develop' into feature/stoploss-start-at

This commit is contained in:
Simon Waiblinger
2024-07-05 22:23:56 +02:00
committed by GitHub
92 changed files with 1011 additions and 373 deletions

View File

@@ -167,7 +167,7 @@ def test_list_timeframes(mocker, capsys):
"1h": "hour",
"1d": "day",
}
patch_exchange(mocker, api_mock=api_mock, id="bybit")
patch_exchange(mocker, api_mock=api_mock, exchange="bybit")
args = [
"list-timeframes",
]
@@ -213,7 +213,7 @@ def test_list_timeframes(mocker, capsys):
"1d": "1d",
"3d": "3d",
}
patch_exchange(mocker, api_mock=api_mock, id="binance")
patch_exchange(mocker, api_mock=api_mock, exchange="binance")
# Test with --exchange binance
args = [
"list-timeframes",
@@ -258,7 +258,7 @@ def test_list_timeframes(mocker, capsys):
def test_list_markets(mocker, markets_static, capsys):
api_mock = MagicMock()
patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
patch_exchange(mocker, api_mock=api_mock, exchange="binance", mock_markets=markets_static)
# Test with no --config
args = [
@@ -286,7 +286,7 @@ def test_list_markets(mocker, markets_static, capsys):
"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)
patch_exchange(mocker, api_mock=api_mock, exchange="binance", mock_markets=markets_static)
# Test with --exchange
args = ["list-markets", "--exchange", "binance"]
pargs = get_args(args)
@@ -295,7 +295,7 @@ def test_list_markets(mocker, markets_static, capsys):
captured = capsys.readouterr()
assert re.match("\nExchange Binance has 12 active markets:\n", captured.out)
patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static)
patch_exchange(mocker, api_mock=api_mock, exchange="binance", mock_markets=markets_static)
# Test with --all: all markets
args = [
"list-markets",
@@ -823,7 +823,7 @@ def test_download_data_no_markets(mocker, caplog):
"freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data",
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]),
)
patch_exchange(mocker, id="binance")
patch_exchange(mocker, exchange="binance")
mocker.patch(f"{EXMS}.get_markets", return_value={})
args = [
"download-data",
@@ -952,7 +952,7 @@ def test_download_data_trades(mocker):
def test_download_data_data_invalid(mocker):
patch_exchange(mocker, id="kraken")
patch_exchange(mocker, exchange="kraken")
mocker.patch(f"{EXMS}.get_markets", return_value={})
args = [
"download-data",

View File

@@ -137,7 +137,7 @@ def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days=
random_timestamps_in_seconds = np.random.uniform(_start_timestamp, _end_timestamp, n_rows)
timestamp = pd.to_datetime(random_timestamps_in_seconds, unit="s")
id = [
trade_id = [
f"a{np.random.randint(1e6, 1e7 - 1)}cd{np.random.randint(100, 999)}" for _ in range(n_rows)
]
@@ -155,7 +155,7 @@ def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days=
df = pd.DataFrame(
{
"timestamp": timestamp,
"id": id,
"id": trade_id,
"type": None,
"side": side,
"price": price,
@@ -236,12 +236,12 @@ def patched_configuration_load_config_file(mocker, config) -> None:
def patch_exchange(
mocker, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True
mocker, api_mock=None, exchange="binance", mock_markets=True, mock_supported_modes=True
) -> None:
mocker.patch(f"{EXMS}.validate_config", MagicMock())
mocker.patch(f"{EXMS}.validate_timeframes", MagicMock())
mocker.patch(f"{EXMS}.id", PropertyMock(return_value=id))
mocker.patch(f"{EXMS}.name", PropertyMock(return_value=id.title()))
mocker.patch(f"{EXMS}.id", PropertyMock(return_value=exchange))
mocker.patch(f"{EXMS}.name", PropertyMock(return_value=exchange.title()))
mocker.patch(f"{EXMS}.precisionMode", PropertyMock(return_value=2))
# Temporary patch ...
mocker.patch("freqtrade.exchange.bybit.Bybit.cache_leverage_tiers")
@@ -254,7 +254,8 @@ def patch_exchange(
if mock_supported_modes:
mocker.patch(
f"freqtrade.exchange.{id}.{id.capitalize()}._supported_trading_mode_margin_pairs",
f"freqtrade.exchange.{exchange}.{exchange.capitalize()}"
"._supported_trading_mode_margin_pairs",
PropertyMock(
return_value=[
(TradingMode.MARGIN, MarginMode.CROSS),
@@ -274,10 +275,10 @@ def patch_exchange(
def get_patched_exchange(
mocker, config, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True
mocker, config, api_mock=None, exchange="binance", mock_markets=True, mock_supported_modes=True
) -> Exchange:
patch_exchange(mocker, api_mock, id, mock_markets, mock_supported_modes)
config["exchange"]["name"] = id
patch_exchange(mocker, api_mock, exchange, mock_markets, mock_supported_modes)
config["exchange"]["name"] = exchange
try:
exchange = ExchangeResolver.load_exchange(config, load_leverage_tiers=True)
except ImportError:
@@ -587,6 +588,7 @@ def get_default_conf(testdatadir):
"exchange": {
"name": "binance",
"key": "key",
"enable_ws": False,
"secret": "secret",
"pair_whitelist": ["ETH/BTC", "LTC/BTC", "XRP/BTC", "NEO/BTC"],
"pair_blacklist": [
@@ -628,6 +630,7 @@ def get_default_conf_usdt(testdatadir):
"name": "binance",
"enabled": True,
"key": "key",
"enable_ws": False,
"secret": "secret",
"pair_whitelist": [
"ETH/USDT",

View File

@@ -83,7 +83,7 @@ def test_datahandler_ohlcv_regex(filename, pair, timeframe, candletype):
@pytest.mark.parametrize(
"input,expected",
"pair,expected",
[
("XMR_USDT", "XMR/USDT"),
("BTC_USDT", "BTC/USDT"),
@@ -95,8 +95,8 @@ def test_datahandler_ohlcv_regex(filename, pair, timeframe, candletype):
("UNITTEST_USDT", "UNITTEST/USDT"),
],
)
def test_rebuild_pair_from_filename(input, expected):
assert IDataHandler.rebuild_pair_from_filename(input) == expected
def test_rebuild_pair_from_filename(pair, expected):
assert IDataHandler.rebuild_pair_from_filename(pair) == expected
def test_datahandler_ohlcv_get_available_data(testdatadir):

View File

@@ -250,7 +250,7 @@ def test_refresh(mocker, default_conf):
refresh_mock = MagicMock()
mocker.patch(f"{EXMS}.refresh_latest_ohlcv", refresh_mock)
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
timeframe = default_conf["timeframe"]
pairs = [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe)]

View File

@@ -14,7 +14,7 @@ def test_download_data_main_no_markets(mocker, caplog):
"freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data",
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]),
)
patch_exchange(mocker, id="binance")
patch_exchange(mocker, exchange="binance")
mocker.patch(f"{EXMS}.get_markets", return_value={})
config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE)
config.update({"days": 20, "pairs": ["ETH/BTC", "XRP/BTC"], "timeframes": ["5m", "1h"]})
@@ -91,7 +91,7 @@ def test_download_data_main_trades(mocker):
def test_download_data_main_data_invalid(mocker):
patch_exchange(mocker, id="kraken")
patch_exchange(mocker, exchange="kraken")
mocker.patch(f"{EXMS}.get_markets", return_value={})
config = setup_utils_configuration({"exchange": "kraken"}, RunMode.UTIL_EXCHANGE)
config.update(

View File

@@ -555,7 +555,7 @@ def test_refresh_backtest_ohlcv_data(
mocker.patch.object(Path, "unlink", MagicMock())
default_conf["trading_mode"] = trademode
ex = get_patched_exchange(mocker, default_conf, id="bybit")
ex = get_patched_exchange(mocker, default_conf, exchange="bybit")
timerange = TimeRange.parse_timerange("20190101-20190102")
refresh_backtest_ohlcv_data(
exchange=ex,

View File

@@ -17,7 +17,7 @@ def test_import_kraken_trades_from_csv(testdatadir, tmp_path, caplog, default_co
default_conf_usdt["exchange"]["name"] = "kraken"
patch_exchange(mocker, id="kraken")
patch_exchange(mocker, exchange="kraken")
mocker.patch(
f"{EXMS}.markets",
PropertyMock(

View File

@@ -12,7 +12,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
@pytest.mark.parametrize(
"side,type,time_in_force,expected",
"side,order_type,time_in_force,expected",
[
("buy", "limit", "gtc", {"timeInForce": "GTC"}),
("buy", "limit", "IOC", {"timeInForce": "IOC"}),
@@ -22,9 +22,9 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
("sell", "market", "PO", {}),
],
)
def test__get_params_binance(default_conf, mocker, side, type, time_in_force, expected):
exchange = get_patched_exchange(mocker, default_conf, id="binance")
assert exchange._get_params(side, type, 1, False, time_in_force) == expected
def test__get_params_binance(default_conf, mocker, side, order_type, time_in_force, expected):
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
assert exchange._get_params(side, order_type, 1, False, time_in_force) == expected
@pytest.mark.parametrize("trademode", [TradingMode.FUTURES, TradingMode.SPOT])
@@ -159,7 +159,7 @@ def test_create_stoploss_order_dry_run_binance(default_conf, mocker):
"sl1,sl2,sl3,side", [(1501, 1499, 1501, "sell"), (1499, 1501, 1499, "buy")]
)
def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
order = {
"type": "stop_loss_limit",
"price": 1500,
@@ -378,7 +378,7 @@ def test_fill_leverage_tiers_binance(default_conf, mocker):
default_conf["dry_run"] = False
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="binance")
exchange.fill_leverage_tiers()
assert exchange._leverage_tiers == {
@@ -497,7 +497,7 @@ def test_fill_leverage_tiers_binance_dryrun(default_conf, mocker, leverage_tiers
api_mock = MagicMock()
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="binance")
exchange.fill_leverage_tiers()
assert len(exchange._leverage_tiers.keys()) > 100
for key, value in leverage_tiers.items():
@@ -518,10 +518,10 @@ def test_additional_exchange_init_binance(default_conf, mocker):
OperationalException,
match=r"Hedge Mode is not supported.*\nMulti-Asset Mode is not supported.*",
):
get_patched_exchange(mocker, default_conf, id="binance", api_mock=api_mock)
get_patched_exchange(mocker, default_conf, exchange="binance", api_mock=api_mock)
api_mock.fapiPrivateGetPositionSideDual = MagicMock(return_value={"dualSidePosition": False})
api_mock.fapiPrivateGetMultiAssetsMargin = MagicMock(return_value={"multiAssetsMargin": False})
exchange = get_patched_exchange(mocker, default_conf, id="binance", api_mock=api_mock)
exchange = get_patched_exchange(mocker, default_conf, exchange="binance", api_mock=api_mock)
assert exchange
ccxt_exceptionhandlers(
mocker,
@@ -541,7 +541,7 @@ def test__set_leverage_binance(mocker, default_conf):
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="binance")
exchange._set_leverage(3.2, "BTC/USDT:USDT")
assert api_mock.set_leverage.call_count == 1
# Leverage is rounded to 3.
@@ -574,7 +574,7 @@ async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog, c
]
]
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
# Monkey-patch async function
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
@@ -620,7 +620,7 @@ def test_get_maintenance_ratio_and_amt_binance(
amt,
):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
exchange._leverage_tiers = leverage_tiers
(result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, nominal_value)
assert (round(result_ratio, 8), round(result_amt, 8)) == (mm_ratio, amt)

View File

@@ -39,7 +39,7 @@ def test_get_trades_for_order(default_conf, mocker):
}
]
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
orders = exchange.get_trades_for_order(order_id, "LTC/BTC", since)
assert len(orders) == 1

View File

@@ -18,7 +18,7 @@ def test_additional_exchange_init_bybit(default_conf, mocker, caplog):
api_mock.set_position_mode = MagicMock(return_value={"dualSidePosition": False})
api_mock.is_unified_enabled = MagicMock(return_value=[False, False])
exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
exchange = get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock)
assert api_mock.set_position_mode.call_count == 1
assert api_mock.is_unified_enabled.call_count == 1
assert exchange.unified_account is False
@@ -28,9 +28,9 @@ def test_additional_exchange_init_bybit(default_conf, mocker, caplog):
api_mock.set_position_mode.reset_mock()
api_mock.is_unified_enabled = MagicMock(return_value=[False, True])
with pytest.raises(OperationalException, match=r"Bybit: Unified account is not supported.*"):
get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock)
assert log_has("Bybit: Unified account.", caplog)
# exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
# exchange = get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock)
# assert api_mock.set_position_mode.call_count == 1
# assert api_mock.is_unified_enabled.call_count == 1
# assert exchange.unified_account is True
@@ -45,7 +45,7 @@ async def test_bybit_fetch_funding_rate(default_conf, mocker):
default_conf["margin_mode"] = "isolated"
api_mock = MagicMock()
api_mock.fetch_funding_rate_history = get_mock_coro(return_value=[])
exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
exchange = get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock)
limit = 200
# Test fetch_funding_rate_history (current data)
await exchange._fetch_funding_rate_history(
@@ -77,14 +77,14 @@ async def test_bybit_fetch_funding_rate(default_conf, mocker):
def test_bybit_get_funding_fees(default_conf, mocker):
now = datetime.now(timezone.utc)
exchange = get_patched_exchange(mocker, default_conf, id="bybit")
exchange = get_patched_exchange(mocker, default_conf, exchange="bybit")
exchange._fetch_and_calculate_funding_fees = MagicMock()
exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now)
assert exchange._fetch_and_calculate_funding_fees.call_count == 0
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="bybit")
exchange = get_patched_exchange(mocker, default_conf, exchange="bybit")
exchange._fetch_and_calculate_funding_fees = MagicMock()
exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now)
@@ -105,13 +105,13 @@ def test_bybit_fetch_orders(default_conf, mocker, limit_order):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
start_time = datetime.now(timezone.utc) - timedelta(days=20)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="bybit")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit")
# Not available in dry-run
assert exchange.fetch_orders("mocked", start_time) == []
assert api_mock.fetch_orders.call_count == 0
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="bybit")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit")
res = exchange.fetch_orders("mocked", start_time)
# Bybit will call the endpoint 3 times, as it has a limit of 7 days per call
assert api_mock.fetch_orders.call_count == 3
@@ -136,7 +136,7 @@ def test_bybit_fetch_order_canceled_empty(default_conf_usdt, mocker):
)
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, id="bybit")
exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bybit")
res = exchange.fetch_order("123", "BTC/USDT")
assert res["remaining"] is None

View File

@@ -117,19 +117,19 @@ def ccxt_exceptionhandlers(
with patch("freqtrade.exchange.common.time.sleep"):
with pytest.raises(DDosProtection):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("DDos"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(TemporaryError):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.OperationFailed("DeaDBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(OperationalException):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
@@ -303,7 +303,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
def test_validate_order_time_in_force(default_conf, mocker, caplog):
caplog.set_level(logging.INFO)
# explicitly test bybit, exchanges implementing other policies need separate tests
ex = get_patched_exchange(mocker, default_conf, id="bybit")
ex = get_patched_exchange(mocker, default_conf, exchange="bybit")
tif = {
"buy": "gtc",
"sell": "gtc",
@@ -345,7 +345,7 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
)
def test_price_get_one_pip(default_conf, mocker, price, precision_mode, precision, expected):
markets = PropertyMock(return_value={"ETH/BTC": {"precision": {"price": precision}}})
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
mocker.patch(f"{EXMS}.markets", markets)
mocker.patch(f"{EXMS}.precisionMode", PropertyMock(return_value=precision_mode))
pair = "ETH/BTC"
@@ -353,7 +353,7 @@ def test_price_get_one_pip(default_conf, mocker, price, precision_mode, precisio
def test__get_stake_amount_limit(mocker, default_conf) -> None:
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
stoploss = -0.05
markets = {"ETH/BTC": {"symbol": "ETH/BTC"}}
@@ -462,7 +462,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
markets["ETH/BTC"]["contractSize"] = "0.01"
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=markets))
# Contract size 0.01
@@ -483,7 +483,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
stoploss = -0.05
markets = {"ETH/BTC": {"symbol": "ETH/BTC"}}
@@ -564,7 +564,7 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine):
api_mock.load_markets = get_mock_coro(return_value=initial_markets)
default_conf["exchange"]["markets_refresh_interval"] = 10
exchange = get_patched_exchange(
mocker, default_conf, api_mock, id="binance", mock_markets=False
mocker, default_conf, api_mock, exchange="binance", mock_markets=False
)
lam_spy = mocker.spy(exchange, "_load_async_markets")
assert exchange._last_markets_refresh == dt_ts()
@@ -599,7 +599,7 @@ def test_reload_markets_exception(default_conf, mocker, caplog):
api_mock.load_markets = get_mock_coro(side_effect=ccxt.NetworkError("LoadError"))
default_conf["exchange"]["markets_refresh_interval"] = 10
exchange = get_patched_exchange(
mocker, default_conf, api_mock, id="binance", mock_markets=False
mocker, default_conf, api_mock, exchange="binance", mock_markets=False
)
exchange._last_markets_refresh = 2
@@ -1152,7 +1152,7 @@ def test_exchange_has(default_conf, mocker):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverage):
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
order = exchange.create_dry_run_order(
pair="ETH/BTC", ordertype="limit", side=side, amount=1, rate=200, leverage=leverage
@@ -1246,7 +1246,7 @@ def test_create_dry_run_order_limit_fill(
leverage,
):
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch.multiple(
EXMS,
exchange_has=MagicMock(return_value=True),
@@ -1315,7 +1315,7 @@ def test_create_dry_run_order_market_fill(
default_conf, mocker, side, rate, amount, endprice, exchange_name, order_book_l2_usd, leverage
):
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch.multiple(
EXMS,
exchange_has=MagicMock(return_value=True),
@@ -1364,7 +1364,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
default_conf["margin_mode"] = "isolated"
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange._set_leverage = MagicMock()
exchange.set_margin_mode = MagicMock()
@@ -1392,7 +1392,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
"amount": 1,
}
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.trading_mode = TradingMode.FUTURES
exchange._set_leverage = MagicMock()
exchange.set_margin_mode = MagicMock()
@@ -1411,7 +1411,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_buy_dry_run(default_conf, mocker, exchange_name):
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
order = exchange.create_order(
pair="ETH/BTC",
@@ -1439,7 +1439,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
order = exchange.create_order(
pair="ETH/BTC",
@@ -1483,7 +1483,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
# test exception handling
with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("Not enough funds"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC",
ordertype=order_type,
@@ -1496,7 +1496,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC",
ordertype="limit",
@@ -1509,7 +1509,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC",
ordertype="market",
@@ -1522,7 +1522,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("Network disconnect"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC",
ordertype=order_type,
@@ -1535,7 +1535,7 @@ def test_buy_prod(default_conf, mocker, exchange_name):
with pytest.raises(OperationalException):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("Unknown error"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC",
ordertype=order_type,
@@ -1558,7 +1558,7 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
order_type = "limit"
time_in_force = "ioc"
@@ -1637,7 +1637,7 @@ def test_sell_prod(default_conf, mocker, exchange_name):
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
order = exchange.create_order(
pair="ETH/BTC", ordertype=order_type, side="sell", amount=1, rate=200, leverage=1.0
@@ -1669,14 +1669,14 @@ def test_sell_prod(default_conf, mocker, exchange_name):
# test exception handling
with pytest.raises(InsufficientFundsError):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC", ordertype=order_type, side="sell", amount=1, rate=200, leverage=1.0
)
with pytest.raises(InvalidOrderException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC", ordertype="limit", side="sell", amount=1, rate=200, leverage=1.0
)
@@ -1684,21 +1684,21 @@ def test_sell_prod(default_conf, mocker, exchange_name):
# Market orders don't require price, so the behaviour is slightly different
with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC", ordertype="market", side="sell", amount=1, rate=200, leverage=1.0
)
with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No Connection"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC", ordertype=order_type, side="sell", amount=1, rate=200, leverage=1.0
)
with pytest.raises(OperationalException):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.create_order(
pair="ETH/BTC", ordertype=order_type, side="sell", amount=1, rate=200, leverage=1.0
)
@@ -1715,7 +1715,7 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
order_type = "limit"
time_in_force = "ioc"
@@ -1777,7 +1777,7 @@ def test_get_balances_prod(default_conf, mocker, exchange_name):
return_value={"1ST": balance_item, "2ND": balance_item, "3RD": balance_item}
)
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert len(exchange.get_balances()) == 3
assert exchange.get_balances()["1ST"]["free"] == 10.0
assert exchange.get_balances()["1ST"]["total"] == 10.0
@@ -1798,12 +1798,12 @@ def test_fetch_positions(default_conf, mocker, exchange_name):
{"symbol": "XRP/USDT:USDT", "leverage": 5},
]
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.fetch_positions() == []
default_conf["dry_run"] = False
default_conf["trading_mode"] = "futures"
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
res = exchange.fetch_positions()
assert len(res) == 2
@@ -1830,13 +1830,13 @@ def test_fetch_orders(default_conf, mocker, exchange_name, limit_order):
if exchange_name == "bybit":
expected = 3
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
# Not available in dry-run
assert exchange.fetch_orders("mocked", start_time) == []
assert api_mock.fetch_orders.call_count == 0
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
res = exchange.fetch_orders("mocked", start_time)
assert api_mock.fetch_orders.call_count == expected
assert api_mock.fetch_open_orders.call_count == 0
@@ -1937,7 +1937,7 @@ def test_fetch_trading_fees(default_conf, mocker):
default_conf["margin_mode"] = MarginMode.ISOLATED
api_mock.fetch_trading_fees = MagicMock(return_value=tick)
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert "1INCH/USDT:USDT" in exchange._trading_fees
assert "ETH/USDT:USDT" in exchange._trading_fees
@@ -1952,7 +1952,7 @@ def test_fetch_trading_fees(default_conf, mocker):
)
api_mock.fetch_trading_fees = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_trading_fees()
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
assert exchange.fetch_trading_fees() == {}
@@ -1977,7 +1977,7 @@ def test_fetch_bids_asks(default_conf, mocker):
exchange_name = "binance"
api_mock.fetch_bids_asks = MagicMock(return_value=tick)
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
# retrieve original ticker
bidsasks = exchange.fetch_bids_asks()
@@ -2004,11 +2004,11 @@ def test_fetch_bids_asks(default_conf, mocker):
with pytest.raises(OperationalException):
api_mock.fetch_bids_asks = MagicMock(side_effect=ccxt.NotSupported("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_bids_asks()
api_mock.fetch_bids_asks = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_bids_asks()
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
assert exchange.fetch_bids_asks() == {}
@@ -2034,7 +2034,7 @@ def test_get_tickers(default_conf, mocker, exchange_name, caplog):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
api_mock.fetch_tickers = MagicMock(return_value=tick)
api_mock.fetch_bids_asks = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
# retrieve original ticker
tickers = exchange.get_tickers()
@@ -2064,19 +2064,19 @@ def test_get_tickers(default_conf, mocker, exchange_name, caplog):
with pytest.raises(OperationalException):
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.get_tickers()
caplog.clear()
api_mock.fetch_tickers = MagicMock(side_effect=[ccxt.BadSymbol("SomeSymbol"), []])
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
x = exchange.get_tickers()
assert x == []
assert log_has_re(r"Could not load tickers due to BadSymbol\..*SomeSymbol", caplog)
caplog.clear()
api_mock.fetch_tickers = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.get_tickers()
api_mock.fetch_tickers.reset_mock()
@@ -2084,7 +2084,7 @@ def test_get_tickers(default_conf, mocker, exchange_name, caplog):
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.get_tickers()
assert api_mock.fetch_tickers.call_count == 1
@@ -2107,7 +2107,7 @@ def test_fetch_ticker(default_conf, mocker, exchange_name):
}
api_mock.fetch_ticker = MagicMock(return_value=tick)
api_mock.markets = {"ETH/BTC": {"active": True}}
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
# retrieve original ticker
ticker = exchange.fetch_ticker(pair="ETH/BTC")
@@ -2122,7 +2122,7 @@ def test_fetch_ticker(default_conf, mocker, exchange_name):
"last": 42,
}
api_mock.fetch_ticker = MagicMock(return_value=tick)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
# if not caching the result we should get the same ticker
# if not fetching a new result we should get the cached ticker
@@ -2143,7 +2143,7 @@ def test_fetch_ticker(default_conf, mocker, exchange_name):
)
api_mock.fetch_ticker = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_ticker(pair="ETH/BTC")
with pytest.raises(DependencyException, match=r"Pair XRP/ETH not available"):
@@ -2152,7 +2152,7 @@ def test_fetch_ticker(default_conf, mocker, exchange_name):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test___now_is_time_to_refresh(default_conf, mocker, exchange_name, time_machine):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
pair = "BTC/USDT"
candle_type = CandleType.SPOT
start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=timezone.utc)
@@ -2181,7 +2181,7 @@ def test___now_is_time_to_refresh(default_conf, mocker, exchange_name, time_mach
@pytest.mark.parametrize("exchange_name", EXCHANGES)
@pytest.mark.parametrize("candle_type", ["mark", ""])
def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_type):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
ohlcv = [
[
dt_ts(), # unix timestamp ms
@@ -2236,7 +2236,7 @@ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_
5, # volume (in quote currency)
]
]
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
# Monkey-patch async function
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
@@ -2538,7 +2538,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
]
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
# Monkey-patch async function
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
@@ -2570,7 +2570,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
OperationalException, match=r"Could not fetch historical candle \(OHLCV\) data.*"
):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError("Unknown error"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
await exchange._async_get_candle_history(
pair, "5m", CandleType.SPOT, dt_ts(dt_now() - timedelta(seconds=2000))
)
@@ -2582,7 +2582,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
match=r"Exchange.* does not support fetching " r"historical candle \(OHLCV\) data\..*",
):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported("Not supported"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
await exchange._async_get_candle_history(
pair, "5m", CandleType.SPOT, dt_ts(dt_now() - timedelta(seconds=2000))
)
@@ -2603,7 +2603,7 @@ async def test__async_kucoin_get_candle_history(default_conf, mocker, caplog):
'{"code":"429000","msg":"Too Many Requests"}'
)
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kucoin")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="kucoin")
mocker.patch(f"{EXMS}.name", PropertyMock(return_value="KuCoin"))
msg = "Kucoin 429 error, avoid triggering DDosProtection backoff delay"
@@ -2725,7 +2725,7 @@ def test_fetch_l2_order_book(default_conf, mocker, order_book_l2, exchange_name)
api_mock = MagicMock()
api_mock.fetch_l2_order_book = order_book_l2
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
order_book = exchange.fetch_l2_order_book(pair="ETH/BTC", limit=10)
assert "bids" in order_book
assert "asks" in order_book
@@ -2753,15 +2753,15 @@ def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name):
api_mock = MagicMock()
with pytest.raises(OperationalException):
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported("Not supported"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_l2_order_book(pair="ETH/BTC", limit=50)
with pytest.raises(TemporaryError):
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_l2_order_book(pair="ETH/BTC", limit=50)
with pytest.raises(OperationalException):
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_l2_order_book(pair="ETH/BTC", limit=50)
@@ -3058,7 +3058,7 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na
[1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687],
[1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867],
]
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
sort_mock = mocker.patch("freqtrade.exchange.exchange.sorted", MagicMock(side_effect=sort_data))
# Test the OHLCV data sort
@@ -3128,7 +3128,7 @@ async def test__async_fetch_trades(
default_conf, mocker, caplog, exchange_name, fetch_trades_result
):
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
# Monkey-patch async function
exchange._api_async.fetch_trades = get_mock_coro(fetch_trades_result)
@@ -3182,7 +3182,7 @@ async def test__async_fetch_trades(
api_mock = MagicMock()
with pytest.raises(OperationalException, match=r"Could not fetch trade data*"):
api_mock.fetch_trades = MagicMock(side_effect=ccxt.BaseError("Unknown error"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
await exchange._async_fetch_trades(pair, since=dt_ts(dt_now() - timedelta(seconds=2000)))
exchange.close()
@@ -3191,7 +3191,7 @@ async def test__async_fetch_trades(
match=r"Exchange.* does not support fetching " r"historical trade data\..*",
):
api_mock.fetch_trades = MagicMock(side_effect=ccxt.NotSupported("Not supported"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
await exchange._async_fetch_trades(pair, since=dt_ts(dt_now() - timedelta(seconds=2000)))
exchange.close()
@@ -3203,7 +3203,7 @@ async def test__async_fetch_trades_contract_size(
caplog.set_level(logging.DEBUG)
default_conf["margin_mode"] = "isolated"
default_conf["trading_mode"] = "futures"
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
# Monkey-patch async function
exchange._api_async.fetch_trades = get_mock_coro(
[
@@ -3246,7 +3246,7 @@ async def test__async_fetch_trades_contract_size(
async def test__async_get_trade_history_id(
default_conf, mocker, exchange_name, fetch_trades_result
):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
if exchange._trades_pagination != "id":
exchange.close()
pytest.skip("Exchange does not support pagination by trade id")
@@ -3305,7 +3305,7 @@ async def test__async_get_trade_history_id(
def test__valid_trade_pagination_id(mocker, default_conf_usdt, exchange_name, trade_id, expected):
if exchange_name == "kraken":
pytest.skip("Kraken has a different pagination id format, and an explicit test.")
exchange = get_patched_exchange(mocker, default_conf_usdt, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name)
assert exchange._valid_trade_pagination_id("XRP/USDT", trade_id) == expected
@@ -3324,7 +3324,7 @@ async def test__async_get_trade_history_time(
return fetch_trades_result[-1:]
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
if exchange._trades_pagination != "time":
exchange.close()
pytest.skip("Exchange does not support pagination by timestamp")
@@ -3366,7 +3366,7 @@ async def test__async_get_trade_history_time_empty(
return [], None
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
# Monkey-patch async function
exchange._async_fetch_trades = MagicMock(side_effect=mock_get_trade_hist)
pair = "ETH/BTC"
@@ -3387,7 +3387,7 @@ async def test__async_get_trade_history_time_empty(
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_historic_trades(default_conf, mocker, caplog, exchange_name, trades_history):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
pair = "ETH/BTC"
@@ -3418,7 +3418,7 @@ def test_get_historic_trades_notsupported(
default_conf, mocker, caplog, exchange_name, trades_history
):
mocker.patch(f"{EXMS}.exchange_has", return_value=False)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
pair = "ETH/BTC"
@@ -3432,7 +3432,7 @@ def test_get_historic_trades_notsupported(
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=True)
assert exchange.cancel_order(order_id="123", pair="TKN/BTC") == {}
assert exchange.cancel_stoploss_order(order_id="123", pair="TKN/BTC") == {}
@@ -3467,7 +3467,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
],
)
def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
assert exchange.check_order_canceled_empty(order) == result
@@ -3487,7 +3487,7 @@ def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order,
],
)
def test_is_cancel_order_result_suitable(mocker, default_conf, exchange_name, order, result):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
assert exchange.is_cancel_order_result_suitable(order) == result
@@ -3507,7 +3507,7 @@ def test_cancel_order_with_result(
api_mock = MagicMock()
api_mock.cancel_order = MagicMock(return_value=corder)
api_mock.fetch_order = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
res = exchange.cancel_order_with_result("1234", "ETH/BTC", 1234)
assert isinstance(res, dict)
assert api_mock.cancel_order.call_count == call_corder
@@ -3521,7 +3521,7 @@ def test_cancel_order_with_result_error(default_conf, mocker, exchange_name, cap
api_mock = MagicMock()
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
res = exchange.cancel_order_with_result("1234", "ETH/BTC", 1541)
assert isinstance(res, dict)
@@ -3536,12 +3536,12 @@ def test_cancel_order(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
api_mock = MagicMock()
api_mock.cancel_order = MagicMock(return_value={"id": "123"})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.cancel_order(order_id="_", pair="TKN/BTC") == {"id": "123"}
with pytest.raises(InvalidOrderException):
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.cancel_order(order_id="_", pair="TKN/BTC")
assert api_mock.cancel_order.call_count == 1
@@ -3562,12 +3562,12 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
api_mock = MagicMock()
api_mock.cancel_order = MagicMock(return_value={"id": "123"})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.cancel_stoploss_order(order_id="_", pair="TKN/BTC") == {"id": "123"}
with pytest.raises(InvalidOrderException):
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.cancel_stoploss_order(order_id="_", pair="TKN/BTC")
assert api_mock.cancel_order.call_count == 1
@@ -3591,7 +3591,7 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
mock_prefix = "freqtrade.exchange.okx.Okx"
mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value={"for": 123})
mocker.patch(f"{mock_prefix}.fetch_stoploss_order", return_value={"for": 123})
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
res = {"fee": {}, "status": "canceled", "amount": 1234}
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=res)
@@ -3616,7 +3616,7 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
exc = InvalidOrderException("Did not find order")
mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=exc)
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", side_effect=exc)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=123)
@@ -3630,7 +3630,7 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
order.symbol = "TKN/BTC"
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
exchange._dry_run_open_orders["X"] = order
assert exchange.fetch_order("X", "TKN/BTC").myid == 123
@@ -3640,18 +3640,18 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
default_conf["dry_run"] = False
api_mock = MagicMock()
api_mock.fetch_order = MagicMock(return_value={"id": "123", "amount": 2, "symbol": "TKN/BTC"})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.fetch_order("X", "TKN/BTC") == {"id": "123", "amount": 2, "symbol": "TKN/BTC"}
assert log_has(("API fetch_order: {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}"), caplog)
with pytest.raises(InvalidOrderException):
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_order(order_id="_", pair="TKN/BTC")
assert api_mock.fetch_order.call_count == 1
api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
with patch("freqtrade.exchange.common.time.sleep") as tm:
with pytest.raises(InvalidOrderException):
exchange.fetch_order(order_id="_", pair="TKN/BTC")
@@ -3686,7 +3686,7 @@ def test_fetch_order_emulated(default_conf, mocker, exchange_name, caplog):
order.myid = 123
order.symbol = "TKN/BTC"
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch(f"{EXMS}.exchange_has", return_value=False)
exchange._dry_run_open_orders["X"] = order
# Dry run - regular fetch_order behavior
@@ -3704,7 +3704,7 @@ def test_fetch_order_emulated(default_conf, mocker, exchange_name, caplog):
api_mock.fetch_closed_order = MagicMock(
return_value={"id": "123", "amount": 2, "symbol": "TKN/BTC"}
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.fetch_order("X", "TKN/BTC") == {"id": "123", "amount": 2, "symbol": "TKN/BTC"}
assert log_has(
("API fetch_open_order: {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}"), caplog
@@ -3718,7 +3718,7 @@ def test_fetch_order_emulated(default_conf, mocker, exchange_name, caplog):
api_mock.fetch_closed_order = MagicMock(
return_value={"id": "123", "amount": 2, "symbol": "TKN/BTC"}
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.fetch_order("X", "TKN/BTC") == {"id": "123", "amount": 2, "symbol": "TKN/BTC"}
assert log_has(
("API fetch_closed_order: {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}"), caplog
@@ -3730,12 +3730,12 @@ def test_fetch_order_emulated(default_conf, mocker, exchange_name, caplog):
with pytest.raises(InvalidOrderException):
api_mock.fetch_open_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
api_mock.fetch_closed_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_order(order_id="_", pair="TKN/BTC")
assert api_mock.fetch_open_order.call_count == 1
api_mock.fetch_open_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
ccxt_exceptionhandlers(
mocker,
@@ -3758,7 +3758,7 @@ def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
order = MagicMock()
order.myid = 123
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
exchange._dry_run_open_orders["X"] = order
assert exchange.fetch_stoploss_order("X", "TKN/BTC").myid == 123
@@ -3768,7 +3768,7 @@ def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
api_mock = MagicMock()
api_mock.fetch_order = MagicMock(return_value={"id": "123", "symbol": "TKN/BTC"})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
res = {"id": "123", "symbol": "TKN/BTC"}
if exchange_name == "okx":
res = {"id": "123", "symbol": "TKN/BTC", "type": "stoploss"}
@@ -3779,7 +3779,7 @@ def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
return
with pytest.raises(InvalidOrderException):
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange.fetch_stoploss_order(order_id="_", pair="TKN/BTC")
assert api_mock.fetch_order.call_count == 1
@@ -3797,7 +3797,7 @@ def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
def test_fetch_order_or_stoploss_order(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
fetch_order_mock = MagicMock()
fetch_stoploss_order_mock = MagicMock()
mocker.patch.multiple(
@@ -3824,7 +3824,7 @@ def test_fetch_order_or_stoploss_order(default_conf, mocker):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_name(default_conf, mocker, exchange_name):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
assert exchange.name == exchange_name.title()
assert exchange.id == exchange_name
@@ -3875,7 +3875,7 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name, trading_mode,
]
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
orders = exchange.get_trades_for_order(order_id, "ETH/USDT:USDT", since)
assert len(orders) == 1
@@ -3914,7 +3914,7 @@ def test_get_fee(default_conf, mocker, exchange_name):
api_mock.calculate_fee = MagicMock(
return_value={"type": "taker", "currency": "BTC", "rate": 0.025, "cost": 0.05}
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange._config.pop("fee", None)
assert exchange.get_fee("ETH/BTC") == 0.025
@@ -3932,7 +3932,7 @@ def test_get_fee(default_conf, mocker, exchange_name):
def test_stoploss_order_unsupported_exchange(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id="bitpanda")
exchange = get_patched_exchange(mocker, default_conf, exchange="bitpanda")
with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"):
exchange.create_stoploss(
pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side="sell", leverage=1.0
@@ -3956,7 +3956,7 @@ def test_stoploss_order_unsupported_exchange(default_conf, mocker):
],
)
def test__get_stop_limit_rate(default_conf_usdt, mocker, side, ratio, expected):
exchange = get_patched_exchange(mocker, default_conf_usdt, id="binance")
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange="binance")
order_types = {"stoploss_on_exchange_limit_ratio": ratio}
if isinstance(expected, type) and issubclass(expected, Exception):
@@ -4314,7 +4314,7 @@ def test_get_markets_error(default_conf, mocker):
def test_ohlcv_candle_limit(default_conf, mocker, exchange_name):
if exchange_name == "okx":
pytest.skip("Tested separately for okx")
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
timeframes = ("1m", "5m", "1h")
expected = exchange._ft_has["ohlcv_candle_limit"]
for timeframe in timeframes:
@@ -4383,7 +4383,7 @@ def test_market_is_tradable(
) -> None:
default_conf["trading_mode"] = trademode
mocker.patch(f"{EXMS}.validate_trading_mode_and_margin_mode")
ex = get_patched_exchange(mocker, default_conf, id=exchange)
ex = get_patched_exchange(mocker, default_conf, exchange=exchange)
market = {
"symbol": market_symbol,
"base": base,
@@ -4654,7 +4654,7 @@ def test_get_funding_fees(default_conf_usdt, mocker, exchange_name, caplog):
now = datetime.now(timezone.utc)
default_conf_usdt["trading_mode"] = "futures"
default_conf_usdt["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf_usdt, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name)
exchange._fetch_and_calculate_funding_fees = MagicMock(side_effect=ExchangeError)
assert exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now) == 0.0
assert exchange._fetch_and_calculate_funding_fees.call_count == 1
@@ -4707,7 +4707,7 @@ def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name):
type(api_mock).has = PropertyMock(return_value={"fetchFundingHistory": True})
# mocker.patch(f'{EXMS}.get_funding_fees', lambda pair, since: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
date_time = datetime.strptime("2021-09-01T00:00:01.000Z", "%Y-%m-%dT%H:%M:%S.%fZ")
unix_time = int(date_time.timestamp())
expected_fees = -0.001 # 0.14542341 + -0.14642341
@@ -4737,7 +4737,7 @@ def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name):
def test_get_stake_amount_considering_leverage(
exchange, stake_amount, leverage, min_stake_with_lev, mocker, default_conf
):
exchange = get_patched_exchange(mocker, default_conf, id=exchange)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange)
assert (
exchange._get_stake_amount_considering_leverage(stake_amount, leverage)
== min_stake_with_lev
@@ -4804,7 +4804,7 @@ def test_validate_trading_mode_and_margin_mode(
default_conf, mocker, exchange_name, trading_mode, margin_mode, exception_thrown
):
exchange = get_patched_exchange(
mocker, default_conf, id=exchange_name, mock_supported_modes=False
mocker, default_conf, exchange=exchange_name, mock_supported_modes=False
)
if exception_thrown:
with pytest.raises(OperationalException):
@@ -4831,7 +4831,7 @@ def test_validate_trading_mode_and_margin_mode(
def test__ccxt_config(default_conf, mocker, exchange_name, trading_mode, ccxt_config):
default_conf["trading_mode"] = trading_mode
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
assert exchange._ccxt_config == ccxt_config
@@ -4850,7 +4850,7 @@ def test_get_max_leverage_from_margin(default_conf, mocker, pair, nominal_value,
default_conf["margin_mode"] = "isolated"
api_mock = MagicMock()
type(api_mock).has = PropertyMock(return_value={"fetchLeverageTiers": False})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="gate")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="gate")
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
@@ -4867,7 +4867,7 @@ def test_calculate_funding_fees(
default_conf, mocker, size, funding_rate, mark_price, funding_fee, kraken_fee, time_in_ratio
):
exchange = get_patched_exchange(mocker, default_conf)
kraken = get_patched_exchange(mocker, default_conf, id="kraken")
kraken = get_patched_exchange(mocker, default_conf, exchange="kraken")
prior_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=1))
trade_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc))
funding_rates = DataFrame(
@@ -5092,7 +5092,7 @@ def test__fetch_and_calculate_funding_fees(
type(api_mock).has = PropertyMock(return_value={"fetchOHLCV": True})
type(api_mock).has = PropertyMock(return_value={"fetchFundingRateHistory": True})
ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
ex = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange)
mocker.patch(f"{EXMS}.timeframes", PropertyMock(return_value=["1h", "4h", "8h"]))
funding_fees = ex._fetch_and_calculate_funding_fees(
pair="ADA/USDT:USDT", amount=amount, is_short=True, open_date=d1, close_date=d2
@@ -5106,7 +5106,7 @@ def test__fetch_and_calculate_funding_fees(
# Return empty "refresh_latest"
mocker.patch(f"{EXMS}.refresh_latest_ohlcv", return_value={})
ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
ex = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange)
with pytest.raises(ExchangeError, match="Could not find funding rates."):
ex._fetch_and_calculate_funding_fees(
pair="ADA/USDT:USDT", amount=amount, is_short=False, open_date=d1, close_date=d2
@@ -5137,7 +5137,7 @@ def test__fetch_and_calculate_funding_fees_datetime_called(
type(api_mock).has = PropertyMock(return_value={"fetchOHLCV": True})
type(api_mock).has = PropertyMock(return_value={"fetchFundingRateHistory": True})
mocker.patch(f"{EXMS}.timeframes", PropertyMock(return_value=["4h", "8h"]))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange)
d1 = datetime.strptime("2021-08-31 23:00:01 +0000", "%Y-%m-%d %H:%M:%S %z")
time_machine.move_to("2021-09-01 08:00:00 +00:00")
@@ -5454,7 +5454,7 @@ def test_liquidation_price_is_none(
):
default_conf["trading_mode"] = trading_mode
default_conf["margin_mode"] = margin_mode
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
assert (
exchange.get_liquidation_price(
pair="DOGE/USDT",
@@ -5553,7 +5553,7 @@ def test_liquidation_price_binance(
default_conf["trading_mode"] = trading_mode
default_conf["margin_mode"] = margin_mode
default_conf["liquidation_buffer"] = 0.0
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt))
assert (
pytest.approx(
@@ -5703,7 +5703,7 @@ def test_load_leverage_tiers(mocker, default_conf, exchange_name):
)
# SPOT
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.load_leverage_tiers() == {}
default_conf["trading_mode"] = "futures"
@@ -5712,12 +5712,12 @@ def test_load_leverage_tiers(mocker, default_conf, exchange_name):
if exchange_name != "binance":
# FUTURES has.fetchLeverageTiers == False
type(api_mock).has = PropertyMock(return_value={"fetchLeverageTiers": False})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.load_leverage_tiers() == {}
# FUTURES regular
type(api_mock).has = PropertyMock(return_value={"fetchLeverageTiers": True})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
assert exchange.load_leverage_tiers() == {
"ADA/USDT:USDT": [
{
@@ -5869,13 +5869,13 @@ def test_get_maintenance_ratio_and_amt(
def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
# Test Spot
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
assert exchange.get_max_leverage("BNB/USDT", 100.0) == 1.0
# Test Futures
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="binance")
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
exchange._leverage_tiers = leverage_tiers
@@ -5899,7 +5899,7 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
def test__get_params(mocker, default_conf, exchange_name):
api_mock = MagicMock()
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange._params = {"test": True}
params1 = {"test": True}
@@ -5954,7 +5954,7 @@ def test__get_params(mocker, default_conf, exchange_name):
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)
exchange._params = {"test": True}
assert (
@@ -6171,7 +6171,7 @@ def test_get_liquidation_price(
default_conf_usdt["exchange"]["name"] = exchange_name
default_conf_usdt["margin_mode"] = margin_mode
mocker.patch("freqtrade.exchange.gate.Gate.validate_ordertypes")
exchange = get_patched_exchange(mocker, default_conf_usdt, id=exchange_name)
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name)
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01))
exchange.name = exchange_name

View File

@@ -0,0 +1,69 @@
import asyncio
import threading
from time import sleep
from unittest.mock import AsyncMock, MagicMock
from freqtrade.enums import CandleType
from freqtrade.exchange.exchange_ws import ExchangeWS
def test_exchangews_init(mocker):
config = MagicMock()
ccxt_object = MagicMock()
mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock())
exchange_ws = ExchangeWS(config, ccxt_object)
sleep(0.1)
assert exchange_ws.config == config
assert exchange_ws.ccxt_object == ccxt_object
assert exchange_ws._thread.name == "ccxt_ws"
assert exchange_ws._background_tasks == set()
assert exchange_ws._klines_watching == set()
assert exchange_ws._klines_scheduled == set()
assert exchange_ws.klines_last_refresh == {}
assert exchange_ws.klines_last_request == {}
# Cleanup
exchange_ws.cleanup()
def patch_eventloop_threading(exchange):
is_init = False
def thread_fuck():
nonlocal is_init
exchange._loop = asyncio.new_event_loop()
is_init = True
exchange._loop.run_forever()
x = threading.Thread(target=thread_fuck, daemon=True)
x.start()
while not is_init:
pass
async def test_exchangews_ohlcv(mocker):
config = MagicMock()
ccxt_object = MagicMock()
ccxt_object.watch_ohlcv = AsyncMock()
ccxt_object.close = AsyncMock()
mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock())
exchange_ws = ExchangeWS(config, ccxt_object)
patch_eventloop_threading(exchange_ws)
try:
assert exchange_ws._klines_watching == set()
assert exchange_ws._klines_scheduled == set()
exchange_ws.schedule_ohlcv("ETH/BTC", "1m", CandleType.SPOT)
await asyncio.sleep(0.5)
assert exchange_ws._klines_watching == {("ETH/BTC", "1m", CandleType.SPOT)}
assert exchange_ws._klines_scheduled == {("ETH/BTC", "1m", CandleType.SPOT)}
await asyncio.sleep(0.1)
assert ccxt_object.watch_ohlcv.call_count == 1
except Exception as e:
print(e)
finally:
# Cleanup
exchange_ws.cleanup()

View File

@@ -9,7 +9,7 @@ from tests.conftest import EXMS, get_patched_exchange
@pytest.mark.usefixtures("init_persistence")
def test_fetch_stoploss_order_gate(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id="gate")
exchange = get_patched_exchange(mocker, default_conf, exchange="gate")
fetch_order_mock = MagicMock()
exchange.fetch_order = fetch_order_mock
@@ -23,7 +23,7 @@ def test_fetch_stoploss_order_gate(default_conf, mocker):
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="gate")
exchange = get_patched_exchange(mocker, default_conf, exchange="gate")
exchange.fetch_order = MagicMock(
return_value={
@@ -41,7 +41,7 @@ def test_fetch_stoploss_order_gate(default_conf, mocker):
def test_cancel_stoploss_order_gate(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id="gate")
exchange = get_patched_exchange(mocker, default_conf, exchange="gate")
cancel_order_mock = MagicMock()
exchange.cancel_order = cancel_order_mock
@@ -57,7 +57,7 @@ def test_cancel_stoploss_order_gate(default_conf, mocker):
"sl1,sl2,sl3,side", [(1501, 1499, 1501, "sell"), (1499, 1501, 1499, "buy")]
)
def test_stoploss_adjust_gate(mocker, default_conf, sl1, sl2, sl3, side):
exchange = get_patched_exchange(mocker, default_conf, id="gate")
exchange = get_patched_exchange(mocker, default_conf, exchange="gate")
order = {
"price": 1500,
"stopPrice": 1500,
@@ -111,7 +111,7 @@ def test_fetch_my_trades_gate(mocker, default_conf, takerormaker, rate, cost):
}
]
)
exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id="gate")
exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, exchange="gate")
exchange._trading_fees = tick
trades = exchange.get_trades_for_order("22255", "ETH/USDT:USDT", datetime.now(timezone.utc))
trade = trades[0]

View File

@@ -128,7 +128,7 @@ def test_create_stoploss_order_dry_run_htx(default_conf, mocker):
def test_stoploss_adjust_htx(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id="htx")
exchange = get_patched_exchange(mocker, default_conf, exchange="htx")
order = {
"type": "stop",
"price": 1500,

View File

@@ -32,7 +32,7 @@ def test_kraken_trading_agreement(default_conf, mocker, order_type, time_in_forc
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y, **kwargs: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="kraken")
order = exchange.create_order(
pair="ETH/BTC",
@@ -121,7 +121,7 @@ def test_get_balances_prod(default_conf, mocker):
]
api_mock.fetch_open_orders = MagicMock(return_value=kraken_open_orders)
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="kraken")
balances = exchange.get_balances()
assert len(balances) == 6
@@ -256,7 +256,7 @@ def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side):
"sl1,sl2,sl3,side", [(1501, 1499, 1501, "sell"), (1499, 1501, 1499, "buy")]
)
def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side):
exchange = get_patched_exchange(mocker, default_conf, id="kraken")
exchange = get_patched_exchange(mocker, default_conf, exchange="kraken")
order = {
"type": "market",
"stopLossPrice": 1500,
@@ -278,5 +278,5 @@ def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side):
],
)
def test__valid_trade_pagination_id_kraken(mocker, default_conf_usdt, trade_id, expected):
exchange = get_patched_exchange(mocker, default_conf_usdt, id="kraken")
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange="kraken")
assert exchange._valid_trade_pagination_id("XRP/USDT", trade_id) == expected

View File

@@ -134,7 +134,7 @@ def test_stoploss_order_dry_run_kucoin(default_conf, mocker):
def test_stoploss_adjust_kucoin(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id="kucoin")
exchange = get_patched_exchange(mocker, default_conf, exchange="kucoin")
order = {
"type": "limit",
"price": 1500,
@@ -161,7 +161,7 @@ def test_kucoin_create_order(default_conf, mocker, side, ordertype, rate):
default_conf["dry_run"] = False
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kucoin")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="kucoin")
exchange._set_leverage = MagicMock()
exchange.set_margin_mode = MagicMock()

View File

@@ -12,7 +12,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
def test_okx_ohlcv_candle_limit(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
timeframes = ("1m", "5m", "1h")
start_time = int(datetime(2021, 1, 1, tzinfo=timezone.utc).timestamp() * 1000)
@@ -188,7 +188,7 @@ def test_get_maintenance_ratio_and_amt_okx(
}
),
)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
assert exchange.get_maintenance_ratio_and_amt("ETH/USDT:USDT", 2000) == (0.01, None)
assert exchange.get_maintenance_ratio_and_amt("ETH/USDT:USDT", 2001) == (0.015, None)
assert exchange.get_maintenance_ratio_and_amt("ETH/USDT:USDT", 4001) == (0.02, None)
@@ -199,12 +199,12 @@ def test_get_maintenance_ratio_and_amt_okx(
def test_get_max_pair_stake_amount_okx(default_conf, mocker, leverage_tiers):
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
assert exchange.get_max_pair_stake_amount("BNB/BUSD", 1.0) == float("inf")
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
exchange._leverage_tiers = leverage_tiers
assert exchange.get_max_pair_stake_amount("XRP/USDT:USDT", 1.0) == 30000000
@@ -229,7 +229,7 @@ def test_get_max_pair_stake_amount_okx(default_conf, mocker, leverage_tiers):
],
)
def test__get_posSide(default_conf, mocker, mode, side, reduceonly, result):
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
exchange.net_only = mode == "net"
assert exchange._get_posSide(side, reduceonly) == result
@@ -257,7 +257,7 @@ def test_additional_exchange_init_okx(default_conf, mocker):
]
)
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, id="okx", api_mock=api_mock)
exchange = get_patched_exchange(mocker, default_conf, exchange="okx", api_mock=api_mock)
assert api_mock.fetch_accounts.call_count == 0
exchange.trading_mode = TradingMode.FUTURES
# Default to netOnly
@@ -438,7 +438,7 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmp_path, caplog
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
default_conf["stake_currency"] = "USDT"
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
exchange.trading_mode = TradingMode.FUTURES
exchange.margin_mode = MarginMode.ISOLATED
exchange.markets = markets
@@ -520,7 +520,7 @@ def test__set_leverage_okx(mocker, default_conf):
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
exchange._lev_prep("BTC/USDT:USDT", 3.2, "buy")
assert api_mock.set_leverage.call_count == 1
# Leverage is rounded to 3.
@@ -554,7 +554,7 @@ def test_fetch_stoploss_order_okx(default_conf, mocker):
api_mock = MagicMock()
api_mock.fetch_order = MagicMock()
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
exchange.fetch_stoploss_order("1234", "ETH/BTC")
assert api_mock.fetch_order.call_count == 1
@@ -594,7 +594,7 @@ def test_fetch_stoploss_order_okx(default_conf, mocker):
assert resp["type"] == "stoploss"
default_conf["dry_run"] = True
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={"id": "123455"}))
api_mock.fetch_order.reset_mock()
@@ -614,7 +614,7 @@ def test_fetch_stoploss_order_okx(default_conf, mocker):
"sl1,sl2,sl3,side", [(1501, 1499, 1501, "sell"), (1499, 1501, 1499, "buy")]
)
def test_stoploss_adjust_okx(mocker, default_conf, sl1, sl2, sl3, side):
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
order = {
"type": "stoploss",
"price": 1500,
@@ -625,7 +625,7 @@ def test_stoploss_adjust_okx(mocker, default_conf, sl1, sl2, sl3, side):
def test_stoploss_cancel_okx(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
exchange.cancel_order = MagicMock()
@@ -639,7 +639,7 @@ def test_stoploss_cancel_okx(mocker, default_conf):
def test__get_stop_params_okx(mocker, default_conf):
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="okx")
exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
params = exchange._get_stop_params("ETH/USDT:USDT", 1500, "sell")
assert params["tdMode"] == "isolated"
@@ -660,13 +660,13 @@ def test_fetch_orders_okx(default_conf, mocker, limit_order):
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
start_time = datetime.now(timezone.utc) - timedelta(days=20)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
# Not available in dry-run
assert exchange.fetch_orders("mocked", start_time) == []
assert api_mock.fetch_orders.call_count == 0
default_conf["dry_run"] = False
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx")
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
def has_resp(_, endpoint):
if endpoint == "fetchOrders":

View File

@@ -11,6 +11,8 @@ 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 = {
@@ -360,6 +362,7 @@ def set_test_proxy(config: Config, use_proxy: bool) -> Config:
config1 = deepcopy(config)
config1["exchange"]["ccxt_config"] = {
"httpsProxy": proxy,
"wsProxy": proxy,
}
return config1
@@ -376,7 +379,7 @@ def get_exchange(exchange_name, exchange_conf):
exchange_conf, validate=True, load_leverage_tiers=True
)
yield exchange, exchange_name
return exchange, exchange_name
def get_futures_exchange(exchange_name, exchange_conf, class_mocker):
@@ -398,15 +401,41 @@ def get_futures_exchange(exchange_name, exchange_conf, class_mocker):
class_mocker.patch(f"{EXMS}.load_cached_leverage_tiers", return_value=None)
class_mocker.patch(f"{EXMS}.cache_leverage_tiers")
yield from get_exchange(exchange_name, exchange_conf)
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")
yield from get_exchange(request.param, exchange_conf)
return get_exchange(request.param, exchange_conf)
@pytest.fixture(params=EXCHANGES, scope="class")
def exchange_futures(request, exchange_conf, class_mocker):
yield from get_futures_exchange(request.param, 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()

View File

@@ -0,0 +1,64 @@
"""
Tests in this file do NOT mock network calls, so they are expected to be fluky at times.
However, these tests aim to test ccxt compatibility, specifically regarding websockets.
"""
import logging
from datetime import timedelta
from time import sleep
import pytest
from freqtrade.enums import CandleType
from freqtrade.exchange.exchange_utils import timeframe_to_prev_date
from freqtrade.loggers.set_log_levels import set_loggers
from freqtrade.util.datetime_helpers import dt_now
from tests.conftest import log_has_re
from tests.exchange_online.conftest import EXCHANGE_WS_FIXTURE_TYPE
@pytest.mark.longrun
@pytest.mark.timeout(3 * 60)
class TestCCXTExchangeWs:
def test_ccxt_watch_ohlcv(self, exchange_ws: EXCHANGE_WS_FIXTURE_TYPE, caplog, mocker):
exch, _exchangename, pair = exchange_ws
assert exch._ws_async is not None
timeframe = "1m"
pair_tf = (pair, timeframe, CandleType.SPOT)
m_hist = mocker.spy(exch, "_async_get_historic_ohlcv")
m_cand = mocker.spy(exch, "_async_get_candle_history")
res = exch.refresh_latest_ohlcv([pair_tf])
assert m_cand.call_count == 1
# Currently open candle
next_candle = timeframe_to_prev_date(timeframe, dt_now())
now = next_candle - timedelta(seconds=1)
# Currently closed candle
curr_candle = timeframe_to_prev_date(timeframe, now)
assert pair_tf in exch._exchange_ws._klines_watching
assert pair_tf in exch._exchange_ws._klines_scheduled
assert res[pair_tf] is not None
df1 = res[pair_tf]
caplog.set_level(logging.DEBUG)
set_loggers(1)
assert df1.iloc[-1]["date"] == curr_candle
# Wait until the next candle (might be up to 1 minute).
while True:
caplog.clear()
res = exch.refresh_latest_ohlcv([pair_tf])
df2 = res[pair_tf]
assert df2 is not None
if df2.iloc[-1]["date"] == next_candle:
break
assert df2.iloc[-1]["date"] == curr_candle
sleep(1)
assert m_hist.call_count == 0
# shouldn't have tried fetch_ohlcv a second time.
assert m_cand.call_count == 1
assert log_has_re(r"watch result.*", caplog)

View File

@@ -915,7 +915,7 @@ def test_execute_entry(
default_conf_usdt["margin_mode"] = margin_mode
mocker.patch("freqtrade.exchange.gate.Gate.validate_ordertypes")
patch_RPCManager(mocker)
patch_exchange(mocker, id=exchange_name)
patch_exchange(mocker, exchange=exchange_name)
freqtrade = FreqtradeBot(default_conf_usdt)
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
freqtrade.strategy.leverage = MagicMock(return_value=leverage)
@@ -3810,6 +3810,9 @@ def test_get_real_amount_quote_dust(
def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mocker, fee):
mocker.patch(f"{EXMS}.get_trades_for_order", return_value=[])
# Invalid nested trade object
buy_order_fee["trades"] = [{"amount": None, "cost": 22}]
amount = buy_order_fee["amount"]
trade = Trade(
pair="LTC/ETH",

View File

@@ -4,7 +4,7 @@ from freqtrade.enums import CandleType
@pytest.mark.parametrize(
"input,expected",
"candle_type,expected",
[
("", CandleType.SPOT),
("spot", CandleType.SPOT),
@@ -17,17 +17,17 @@ from freqtrade.enums import CandleType
("premiumIndex", CandleType.PREMIUMINDEX),
],
)
def test_CandleType_from_string(input, expected):
assert CandleType.from_string(input) == expected
def test_CandleType_from_string(candle_type, expected):
assert CandleType.from_string(candle_type) == expected
@pytest.mark.parametrize(
"input,expected",
"candle_type,expected",
[
("futures", CandleType.FUTURES),
("spot", CandleType.SPOT),
("margin", CandleType.SPOT),
],
)
def test_CandleType_get_default(input, expected):
assert CandleType.get_default(input) == expected
def test_CandleType_get_default(candle_type, expected):
assert CandleType.get_default(candle_type) == expected

View File

@@ -429,7 +429,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) ->
backtesting.start()
def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) -> None:
def test_backtesting_no_pair_left(default_conf, mocker) -> None:
mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True))
mocker.patch(
"freqtrade.data.history.history_utils.load_pair_history",
@@ -449,13 +449,6 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
with pytest.raises(OperationalException, match="No pair in whitelist."):
Backtesting(default_conf)
default_conf["pairlists"] = [{"method": "VolumePairList", "number_assets": 5}]
with pytest.raises(
OperationalException,
match=r"VolumePairList not allowed for backtesting\..*StaticPairList.*",
):
Backtesting(default_conf)
default_conf.update(
{
"pairlists": [{"method": "StaticPairList"}],
@@ -469,7 +462,7 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
Backtesting(default_conf)
def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None:
def test_backtesting_pairlist_list(default_conf, mocker, tickers) -> None:
mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True))
mocker.patch(f"{EXMS}.get_tickers", tickers)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
@@ -495,12 +488,6 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
):
Backtesting(default_conf)
default_conf["pairlists"] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
with pytest.raises(
OperationalException, match="PerformanceFilter not allowed for backtesting."
):
Backtesting(default_conf)
default_conf["pairlists"] = [
{"method": "StaticPairList"},
{"method": "PrecisionFilter"},

View File

@@ -38,6 +38,7 @@ TESTABLE_PAIRLISTS = [p for p in AVAILABLE_PAIRLISTS if p not in ["RemotePairLis
@pytest.fixture(scope="function")
def whitelist_conf(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -68,6 +69,7 @@ def whitelist_conf(default_conf):
@pytest.fixture(scope="function")
def whitelist_conf_2(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -94,6 +96,7 @@ def whitelist_conf_2(default_conf):
@pytest.fixture(scope="function")
def whitelist_conf_agefilter(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -773,7 +776,7 @@ def test_VolumePairList_whitelist_gen(
whitelist_result,
caplog,
) -> None:
whitelist_conf["runmode"] = "backtest"
whitelist_conf["runmode"] = "util_exchange"
whitelist_conf["pairlists"] = pairlists
whitelist_conf["stake_currency"] = base_currency
@@ -2387,3 +2390,65 @@ def test_MarketCapPairList_exceptions(mocker, default_conf_usdt):
OperationalException, match="This filter only support marketcap rank up to 250."
):
PairListManager(exchange, default_conf_usdt)
@pytest.mark.parametrize(
"pairlists,expected_error,expected_warning",
[
(
[{"method": "StaticPairList"}],
None, # Error
None, # Warning
),
(
[{"method": "VolumePairList", "number_assets": 10}],
"VolumePairList", # Error
None, # Warning
),
(
[{"method": "MarketCapPairList", "number_assets": 10}],
None, # Error
r"MarketCapPairList.*lookahead.*", # Warning
),
(
[{"method": "StaticPairList"}, {"method": "FullTradesFilter"}],
None, # Error
r"FullTradesFilter do not generate.*", # Warning
),
( # combi, fails and warns
[
{"method": "VolumePairList", "number_assets": 10},
{"method": "MarketCapPairList", "number_assets": 10},
],
"VolumePairList", # Error
r"MarketCapPairList.*lookahead.*", # Warning
),
],
)
def test_backtesting_modes(
mocker, default_conf_usdt, pairlists, expected_error, expected_warning, caplog, markets, tickers
):
default_conf_usdt["runmode"] = "dry_run"
default_conf_usdt["pairlists"] = pairlists
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers,
)
exchange = get_patched_exchange(mocker, default_conf_usdt)
# Dry run mode - works always
PairListManager(exchange, default_conf_usdt)
default_conf_usdt["runmode"] = "backtest"
if expected_error:
with pytest.raises(OperationalException, match=f"Pairlist Handlers {expected_error}.*"):
PairListManager(exchange, default_conf_usdt)
if not expected_error:
PairListManager(exchange, default_conf_usdt)
if expected_warning:
assert log_has_re(f"Pairlist Handlers {expected_warning}", caplog)

View File

@@ -2154,6 +2154,7 @@ def test_api_exchanges(botclient):
"valid": True,
"supported": True,
"comment": "",
"dex": False,
"trade_modes": [
{"trading_mode": "spot", "margin_mode": ""},
{"trading_mode": "futures", "margin_mode": "isolated"},
@@ -2165,9 +2166,19 @@ def test_api_exchanges(botclient):
"name": "mexc",
"valid": True,
"supported": False,
"dex": False,
"comment": "",
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
}
waves = [x for x in response["exchanges"] if x["name"] == "wavesexchange"][0]
assert waves == {
"name": "wavesexchange",
"valid": True,
"supported": False,
"dex": True,
"comment": ANY,
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
}
def test_api_freqaimodels(botclient, tmp_path, mocker):