diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 625033645..b961c2809 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -11,167 +11,160 @@ from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has_re from tests.exchange.test_exchange import ccxt_exceptionhandlers -@pytest.mark.parametrize('side,type,time_in_force,expected', [ - ('buy', 'limit', 'gtc', {'timeInForce': 'GTC'}), - ('buy', 'limit', 'IOC', {'timeInForce': 'IOC'}), - ('buy', 'market', 'IOC', {}), - ('buy', 'limit', 'PO', {'timeInForce': 'PO'}), - ('sell', 'limit', 'PO', {'timeInForce': 'PO'}), - ('sell', 'market', 'PO', {}), - ]) +@pytest.mark.parametrize( + "side,type,time_in_force,expected", + [ + ("buy", "limit", "gtc", {"timeInForce": "GTC"}), + ("buy", "limit", "IOC", {"timeInForce": "IOC"}), + ("buy", "market", "IOC", {}), + ("buy", "limit", "PO", {"timeInForce": "PO"}), + ("sell", "limit", "PO", {"timeInForce": "PO"}), + ("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') + exchange = get_patched_exchange(mocker, default_conf, id="binance") assert exchange._get_params(side, type, 1, False, time_in_force) == expected -@pytest.mark.parametrize('trademode', [TradingMode.FUTURES, TradingMode.SPOT]) -@pytest.mark.parametrize('limitratio,expected,side', [ - (None, 220 * 0.99, "sell"), - (0.99, 220 * 0.99, "sell"), - (0.98, 220 * 0.98, "sell"), - (None, 220 * 1.01, "buy"), - (0.99, 220 * 1.01, "buy"), - (0.98, 220 * 1.02, "buy"), -]) +@pytest.mark.parametrize("trademode", [TradingMode.FUTURES, TradingMode.SPOT]) +@pytest.mark.parametrize( + "limitratio,expected,side", + [ + (None, 220 * 0.99, "sell"), + (0.99, 220 * 0.99, "sell"), + (0.98, 220 * 0.98, "sell"), + (None, 220 * 1.01, "buy"), + (0.99, 220 * 1.01, "buy"), + (0.98, 220 * 1.02, "buy"), + ], +) def test_create_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode): api_mock = MagicMock() - order_id = f'test_prod_buy_{randint(0, 10 ** 6)}' - order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'stop' + order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_type = "stop_loss_limit" if trademode == TradingMode.SPOT else "stop" - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - } - }) - default_conf['dry_run'] = False - default_conf['margin_mode'] = MarginMode.ISOLATED - default_conf['trading_mode'] = trademode - 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) + api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) + default_conf["dry_run"] = False + default_conf["margin_mode"] = MarginMode.ISOLATED + default_conf["trading_mode"] = trademode + 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, 'binance') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "binance") with pytest.raises(InvalidOrderException): order = exchange.create_stoploss( - pair='ETH/BTC', + pair="ETH/BTC", amount=1, stop_price=190, side=side, - order_types={'stoploss': 'limit', 'stoploss_on_exchange_limit_ratio': 1.05}, - leverage=1.0 + order_types={"stoploss": "limit", "stoploss_on_exchange_limit_ratio": 1.05}, + leverage=1.0, ) api_mock.create_order.reset_mock() - order_types = {'stoploss': 'limit', 'stoploss_price_type': 'mark'} + order_types = {"stoploss": "limit", "stoploss_price_type": "mark"} if limitratio is not None: - order_types.update({'stoploss_on_exchange_limit_ratio': limitratio}) + order_types.update({"stoploss_on_exchange_limit_ratio": limitratio}) order = exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types=order_types, - side=side, - leverage=1.0 + pair="ETH/BTC", amount=1, stop_price=220, order_types=order_types, side=side, leverage=1.0 ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' - assert api_mock.create_order.call_args_list[0][1]['type'] == order_type - assert api_mock.create_order.call_args_list[0][1]['side'] == side - assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert api_mock.create_order.call_args_list[0][1]["symbol"] == "ETH/BTC" + assert api_mock.create_order.call_args_list[0][1]["type"] == order_type + assert api_mock.create_order.call_args_list[0][1]["side"] == side + assert api_mock.create_order.call_args_list[0][1]["amount"] == 1 # Price should be 1% below stopprice - assert api_mock.create_order.call_args_list[0][1]['price'] == expected + assert api_mock.create_order.call_args_list[0][1]["price"] == expected if trademode == TradingMode.SPOT: - params_dict = {'stopPrice': 220} + params_dict = {"stopPrice": 220} else: - params_dict = {'stopPrice': 220, 'reduceOnly': True, 'workingType': 'MARK_PRICE'} - assert api_mock.create_order.call_args_list[0][1]['params'] == params_dict + params_dict = {"stopPrice": 220, "reduceOnly": True, "workingType": "MARK_PRICE"} + assert api_mock.create_order.call_args_list[0][1]["params"] == params_dict # test exception handling with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "binance") exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side=side, - leverage=1.0) + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( - side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.") + ) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "binance") exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side=side, - leverage=1.0 + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 ) - ccxt_exceptionhandlers(mocker, default_conf, api_mock, "binance", - "create_stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, - side=side, leverage=1.0) + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "binance", + "create_stoploss", + "create_order", + retries=1, + pair="ETH/BTC", + amount=1, + stop_price=220, + order_types={}, + side=side, + leverage=1.0, + ) def test_create_stoploss_order_dry_run_binance(default_conf, mocker): api_mock = MagicMock() - order_type = 'stop_loss_limit' - default_conf['dry_run'] = True - 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) + order_type = "stop_loss_limit" + default_conf["dry_run"] = True + 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, 'binance') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "binance") with pytest.raises(InvalidOrderException): order = exchange.create_stoploss( - pair='ETH/BTC', + pair="ETH/BTC", amount=1, stop_price=190, side="sell", - order_types={'stoploss_on_exchange_limit_ratio': 1.05}, - leverage=1.0 + order_types={"stoploss_on_exchange_limit_ratio": 1.05}, + leverage=1.0, ) api_mock.create_order.reset_mock() order = exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side="sell", - leverage=1.0 + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side="sell", leverage=1.0 ) - assert 'id' in order - assert 'info' in order - assert 'type' in order + assert "id" in order + assert "info" in order + assert "type" in order - assert order['type'] == order_type - assert order['price'] == 220 - assert order['amount'] == 1 + assert order["type"] == order_type + assert order["price"] == 220 + assert order["amount"] == 1 -@pytest.mark.parametrize('sl1,sl2,sl3,side', [ - (1501, 1499, 1501, "sell"), - (1499, 1501, 1499, "buy") -]) +@pytest.mark.parametrize( + "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, id="binance") order = { - 'type': 'stop_loss_limit', - 'price': 1500, - 'stopPrice': 1500, - 'info': {'stopPrice': 1500}, + "type": "stop_loss_limit", + "price": 1500, + "stopPrice": 1500, + "info": {"stopPrice": 1500}, } assert exchange.stoploss_adjust(sl1, order, side=side) assert not exchange.stoploss_adjust(sl2, order, side=side) @@ -179,314 +172,316 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side): def test_fill_leverage_tiers_binance(default_conf, mocker): api_mock = MagicMock() - api_mock.fetch_leverage_tiers = MagicMock(return_value={ - 'ADA/BUSD': [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", - "maintMarginRatio": "0.5", - "cum": "1527500.0" - } - } - ], - "ZEC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - }) - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED + api_mock.fetch_leverage_tiers = MagicMock( + return_value={ + "ADA/BUSD": [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 100000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "info": { + "bracket": "1", + "initialLeverage": "20", + "maxNotional": "100000", + "minNotional": "0", + "maintMarginRatio": "0.025", + "cum": "0.0", + }, + }, + { + "tier": 2, + "minNotional": 100000, + "maxNotional": 500000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "info": { + "bracket": "2", + "initialLeverage": "10", + "maxNotional": "500000", + "minNotional": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0", + }, + }, + { + "tier": 3, + "minNotional": 500000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "info": { + "bracket": "3", + "initialLeverage": "5", + "maxNotional": "1000000", + "minNotional": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0", + }, + }, + { + "tier": 4, + "minNotional": 1000000, + "maxNotional": 2000000, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3, + "info": { + "bracket": "4", + "initialLeverage": "3", + "maxNotional": "2000000", + "minNotional": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0", + }, + }, + { + "tier": 5, + "minNotional": 2000000, + "maxNotional": 5000000, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2, + "info": { + "bracket": "5", + "initialLeverage": "2", + "maxNotional": "5000000", + "minNotional": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0", + }, + }, + { + "tier": 6, + "minNotional": 5000000, + "maxNotional": 30000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "info": { + "bracket": "6", + "initialLeverage": "1", + "maxNotional": "30000000", + "minNotional": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0", + }, + }, + ], + "ZEC/USDT": [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 50000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50, + "info": { + "bracket": "1", + "initialLeverage": "50", + "maxNotional": "50000", + "minNotional": "0", + "maintMarginRatio": "0.01", + "cum": "0.0", + }, + }, + { + "tier": 2, + "minNotional": 50000, + "maxNotional": 150000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "info": { + "bracket": "2", + "initialLeverage": "20", + "maxNotional": "150000", + "minNotional": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0", + }, + }, + { + "tier": 3, + "minNotional": 150000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "info": { + "bracket": "3", + "initialLeverage": "10", + "maxNotional": "250000", + "minNotional": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0", + }, + }, + { + "tier": 4, + "minNotional": 250000, + "maxNotional": 500000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "info": { + "bracket": "4", + "initialLeverage": "5", + "maxNotional": "500000", + "minNotional": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0", + }, + }, + { + "tier": 5, + "minNotional": 500000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4, + "info": { + "bracket": "5", + "initialLeverage": "4", + "maxNotional": "1000000", + "minNotional": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0", + }, + }, + { + "tier": 6, + "minNotional": 1000000, + "maxNotional": 2000000, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2, + "info": { + "bracket": "6", + "initialLeverage": "2", + "maxNotional": "2000000", + "minNotional": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0", + }, + }, + { + "tier": 7, + "minNotional": 2000000, + "maxNotional": 30000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "info": { + "bracket": "7", + "initialLeverage": "1", + "maxNotional": "30000000", + "minNotional": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0", + }, + }, + ], + } + ) + 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.fill_leverage_tiers() assert exchange._leverage_tiers == { - 'ADA/BUSD': [ + "ADA/BUSD": [ { "minNotional": 0, "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, - "maintAmt": 0.0 + "maintAmt": 0.0, }, { "minNotional": 100000, "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, - "maintAmt": 2500.0 + "maintAmt": 2500.0, }, { "minNotional": 500000, "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, - "maintAmt": 27500.0 + "maintAmt": 27500.0, }, { "minNotional": 1000000, "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, - "maintAmt": 77500.0 + "maintAmt": 77500.0, }, { "minNotional": 2000000, "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, - "maintAmt": 277500.0 + "maintAmt": 277500.0, }, { "minNotional": 5000000, "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, - "maintAmt": 1527500.0 - } + "maintAmt": 1527500.0, + }, ], "ZEC/USDT": [ { - 'minNotional': 0, - 'maxNotional': 50000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 50, - 'maintAmt': 0.0 + "minNotional": 0, + "maxNotional": 50000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50, + "maintAmt": 0.0, }, { - 'minNotional': 50000, - 'maxNotional': 150000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 20, - 'maintAmt': 750.0 + "minNotional": 50000, + "maxNotional": 150000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "maintAmt": 750.0, }, { - 'minNotional': 150000, - 'maxNotional': 250000, - 'maintenanceMarginRate': 0.05, - 'maxLeverage': 10, - 'maintAmt': 4500.0 + "minNotional": 150000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "maintAmt": 4500.0, }, { - 'minNotional': 250000, - 'maxNotional': 500000, - 'maintenanceMarginRate': 0.1, - 'maxLeverage': 5, - 'maintAmt': 17000.0 + "minNotional": 250000, + "maxNotional": 500000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "maintAmt": 17000.0, }, { - 'minNotional': 500000, - 'maxNotional': 1000000, - 'maintenanceMarginRate': 0.125, - 'maxLeverage': 4, - 'maintAmt': 29500.0 + "minNotional": 500000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4, + "maintAmt": 29500.0, }, { - 'minNotional': 1000000, - 'maxNotional': 2000000, - 'maintenanceMarginRate': 0.25, - 'maxLeverage': 2, - 'maintAmt': 154500.0 + "minNotional": 1000000, + "maxNotional": 2000000, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2, + "maintAmt": 154500.0, }, { - 'minNotional': 2000000, - 'maxNotional': 30000000, - 'maintenanceMarginRate': 0.5, - 'maxLeverage': 1, - 'maintAmt': 654500.0 + "minNotional": 2000000, + "maxNotional": 30000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "maintAmt": 654500.0, }, - ] + ], } api_mock = MagicMock() api_mock.load_leverage_tiers = MagicMock() - type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) + type(api_mock).has = PropertyMock(return_value={"fetchLeverageTiers": True}) ccxt_exceptionhandlers( mocker, @@ -500,8 +495,8 @@ def test_fill_leverage_tiers_binance(default_conf, mocker): 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 + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.ISOLATED exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") exchange.fill_leverage_tiers() assert len(exchange._leverage_tiers.keys()) > 100 @@ -516,35 +511,42 @@ def test_additional_exchange_init_binance(default_conf, mocker): api_mock = MagicMock() api_mock.fapiPrivateGetPositionSideDual = MagicMock(return_value={"dualSidePosition": True}) api_mock.fapiPrivateGetMultiAssetsMargin = MagicMock(return_value={"multiAssetsMargin": True}) - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED - with pytest.raises(OperationalException, - match=r"Hedge Mode is not supported.*\nMulti-Asset Mode is not supported.*"): + default_conf["dry_run"] = False + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.ISOLATED + with pytest.raises( + 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) 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) assert exchange - ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'binance', - "additional_exchange_init", "fapiPrivateGetPositionSideDual") + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "binance", + "additional_exchange_init", + "fapiPrivateGetPositionSideDual", + ) def test__set_leverage_binance(mocker, default_conf): - api_mock = MagicMock() api_mock.set_leverage = MagicMock() - type(api_mock).has = PropertyMock(return_value={'setLeverage': True}) - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED + type(api_mock).has = PropertyMock(return_value={"setLeverage": True}) + 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._set_leverage(3.2, 'BTC/USDT:USDT') + exchange._set_leverage(3.2, "BTC/USDT:USDT") assert api_mock.set_leverage.call_count == 1 # Leverage is rounded to 3. - assert api_mock.set_leverage.call_args_list[0][1]['leverage'] == 3 - assert api_mock.set_leverage.call_args_list[0][1]['symbol'] == 'BTC/USDT:USDT' + assert api_mock.set_leverage.call_args_list[0][1]["leverage"] == 3 + assert api_mock.set_leverage.call_args_list[0][1]["symbol"] == "BTC/USDT:USDT" ccxt_exceptionhandlers( mocker, @@ -559,7 +561,7 @@ def test__set_leverage_binance(mocker, default_conf): @pytest.mark.asyncio -@pytest.mark.parametrize('candle_type', [CandleType.MARK, '']) +@pytest.mark.parametrize("candle_type", [CandleType.MARK, ""]) async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog, candle_type): ohlcv = [ [ @@ -572,22 +574,24 @@ 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, id="binance") # Monkey-patch async function exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - pair = 'ETH/BTC' + pair = "ETH/BTC" respair, restf, restype, res, _ = await exchange._async_get_historic_ohlcv( - pair, "5m", 1500000000000, is_new_pair=False, candle_type=candle_type) + pair, "5m", 1500000000000, is_new_pair=False, candle_type=candle_type + ) assert respair == pair - assert restf == '5m' + assert restf == "5m" assert restype == candle_type # Call with very old timestamp - causes tons of requests assert exchange._api_async.fetch_ohlcv.call_count > 400 # assert res == ohlcv exchange._api_async.fetch_ohlcv.reset_mock() _, _, _, res, _ = await exchange._async_get_historic_ohlcv( - pair, "5m", 1500000000000, is_new_pair=True, candle_type=candle_type) + pair, "5m", 1500000000000, is_new_pair=True, candle_type=candle_type + ) # Called twice - one "init" call - and one to get the actual data. assert exchange._api_async.fetch_ohlcv.call_count == 2 @@ -595,14 +599,17 @@ async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog, c assert log_has_re(r"Candle-data for ETH/BTC available starting with .*", caplog) -@pytest.mark.parametrize('pair,nominal_value,mm_ratio,amt', [ - ("XRP/USDT:USDT", 0.0, 0.025, 0), - ("BNB/USDT:USDT", 100.0, 0.0065, 0), - ("BTC/USDT:USDT", 170.30, 0.004, 0), - ("XRP/USDT:USDT", 999999.9, 0.1, 27500.0), - ("BNB/USDT:USDT", 5000000.0, 0.15, 233035.0), - ("BTC/USDT:USDT", 600000000, 0.5, 1.997038E8), -]) +@pytest.mark.parametrize( + "pair,nominal_value,mm_ratio,amt", + [ + ("XRP/USDT:USDT", 0.0, 0.025, 0), + ("BNB/USDT:USDT", 100.0, 0.0065, 0), + ("BTC/USDT:USDT", 170.30, 0.004, 0), + ("XRP/USDT:USDT", 999999.9, 0.1, 27500.0), + ("BNB/USDT:USDT", 5000000.0, 0.15, 233035.0), + ("BTC/USDT:USDT", 600000000, 0.5, 1.997038e8), + ], +) def test_get_maintenance_ratio_and_amt_binance( default_conf, mocker, @@ -612,7 +619,7 @@ def test_get_maintenance_ratio_and_amt_binance( mm_ratio, amt, ): - mocker.patch(f'{EXMS}.exchange_has', return_value=True) + mocker.patch(f"{EXMS}.exchange_has", return_value=True) exchange = get_patched_exchange(mocker, default_conf, id="binance") exchange._leverage_tiers = leverage_tiers (result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, nominal_value) diff --git a/tests/exchange/test_bitpanda.py b/tests/exchange/test_bitpanda.py index de44be986..83561b914 100644 --- a/tests/exchange/test_bitpanda.py +++ b/tests/exchange/test_bitpanda.py @@ -5,43 +5,50 @@ from tests.conftest import EXMS, get_patched_exchange def test_get_trades_for_order(default_conf, mocker): - exchange_name = 'bitpanda' - order_id = 'ABCD-ABCD' + exchange_name = "bitpanda" + order_id = "ABCD-ABCD" since = datetime(2018, 5, 5, 0, 0, 0) default_conf["dry_run"] = False - mocker.patch(f'{EXMS}.exchange_has', return_value=True) + mocker.patch(f"{EXMS}.exchange_has", return_value=True) api_mock = MagicMock() - api_mock.fetch_my_trades = MagicMock(return_value=[{'id': 'TTR67E-3PFBD-76IISV', - 'order': 'ABCD-ABCD', - 'info': {'pair': 'XLTCZBTC', - 'time': 1519860024.4388, - 'type': 'buy', - 'ordertype': 'limit', - 'price': '20.00000', - 'cost': '38.62000', - 'fee': '0.06179', - 'vol': '5', - 'id': 'ABCD-ABCD'}, - 'timestamp': 1519860024438, - 'datetime': '2018-02-28T23:20:24.438Z', - 'symbol': 'LTC/BTC', - 'type': 'limit', - 'side': 'buy', - 'price': 165.0, - 'amount': 0.2340606, - 'fee': {'cost': 0.06179, 'currency': 'BTC'} - }]) + api_mock.fetch_my_trades = MagicMock( + return_value=[ + { + "id": "TTR67E-3PFBD-76IISV", + "order": "ABCD-ABCD", + "info": { + "pair": "XLTCZBTC", + "time": 1519860024.4388, + "type": "buy", + "ordertype": "limit", + "price": "20.00000", + "cost": "38.62000", + "fee": "0.06179", + "vol": "5", + "id": "ABCD-ABCD", + }, + "timestamp": 1519860024438, + "datetime": "2018-02-28T23:20:24.438Z", + "symbol": "LTC/BTC", + "type": "limit", + "side": "buy", + "price": 165.0, + "amount": 0.2340606, + "fee": {"cost": 0.06179, "currency": "BTC"}, + } + ] + ) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since) + orders = exchange.get_trades_for_order(order_id, "LTC/BTC", since) assert len(orders) == 1 - assert orders[0]['price'] == 165 + assert orders[0]["price"] == 165 assert api_mock.fetch_my_trades.call_count == 1 # since argument should be assert isinstance(api_mock.fetch_my_trades.call_args[0][1], int) - assert api_mock.fetch_my_trades.call_args[0][0] == 'LTC/BTC' + assert api_mock.fetch_my_trades.call_args[0][0] == "LTC/BTC" # Same test twice, hardcoded number and doing the same calculation assert api_mock.fetch_my_trades.call_args[0][1] == 1525478395000 # bitpanda requires "to" argument. - assert 'to' in api_mock.fetch_my_trades.call_args[1]['params'] + assert "to" in api_mock.fetch_my_trades.call_args[1]["params"] diff --git a/tests/exchange/test_bybit.py b/tests/exchange/test_bybit.py index 556547d88..8f09b049d 100644 --- a/tests/exchange/test_bybit.py +++ b/tests/exchange/test_bybit.py @@ -11,9 +11,9 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers def test_additional_exchange_init_bybit(default_conf, mocker, caplog): - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED + default_conf["dry_run"] = False + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.ISOLATED api_mock = MagicMock() api_mock.set_position_mode = MagicMock(return_value={"dualSidePosition": False}) api_mock.is_unified_enabled = MagicMock(return_value=[False, False]) @@ -35,82 +35,84 @@ def test_additional_exchange_init_bybit(default_conf, mocker, caplog): # assert api_mock.is_unified_enabled.call_count == 1 # assert exchange.unified_account is True - ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'bybit', - "additional_exchange_init", "set_position_mode") + ccxt_exceptionhandlers( + mocker, default_conf, api_mock, "bybit", "additional_exchange_init", "set_position_mode" + ) async def test_bybit_fetch_funding_rate(default_conf, mocker): - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + default_conf["trading_mode"] = "futures" + 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, id="bybit", api_mock=api_mock) limit = 200 # Test fetch_funding_rate_history (current data) await exchange._fetch_funding_rate_history( - pair='BTC/USDT:USDT', - timeframe='4h', + pair="BTC/USDT:USDT", + timeframe="4h", limit=limit, - ) + ) assert api_mock.fetch_funding_rate_history.call_count == 1 - assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == 'BTC/USDT:USDT' + assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == "BTC/USDT:USDT" kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1] - assert kwargs['since'] is None + assert kwargs["since"] is None api_mock.fetch_funding_rate_history.reset_mock() since_ms = 1610000000000 # Test fetch_funding_rate_history (current data) await exchange._fetch_funding_rate_history( - pair='BTC/USDT:USDT', - timeframe='4h', + pair="BTC/USDT:USDT", + timeframe="4h", limit=limit, since_ms=since_ms, - ) + ) assert api_mock.fetch_funding_rate_history.call_count == 1 - assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == 'BTC/USDT:USDT' + assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == "BTC/USDT:USDT" kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1] - assert kwargs['since'] == since_ms + assert kwargs["since"] == since_ms 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, id="bybit") exchange._fetch_and_calculate_funding_fees = MagicMock() - exchange.get_funding_fees('BTC/USDT:USDT', 1, False, now) + 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') + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + exchange = get_patched_exchange(mocker, default_conf, id="bybit") exchange._fetch_and_calculate_funding_fees = MagicMock() - exchange.get_funding_fees('BTC/USDT:USDT', 1, False, now) + exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now) assert exchange._fetch_and_calculate_funding_fees.call_count == 1 def test_bybit_fetch_orders(default_conf, mocker, limit_order): - api_mock = MagicMock() - api_mock.fetch_orders = MagicMock(return_value=[ - limit_order['buy'], - limit_order['sell'], - ]) - api_mock.fetch_open_orders = MagicMock(return_value=[limit_order['buy']]) - api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order['buy']]) + api_mock.fetch_orders = MagicMock( + return_value=[ + limit_order["buy"], + limit_order["sell"], + ] + ) + api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]]) + api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]]) - mocker.patch(f'{EXMS}.exchange_has', return_value=True) + 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, id="bybit") # Not available in dry-run - assert exchange.fetch_orders('mocked', start_time) == [] + assert exchange.fetch_orders("mocked", start_time) == [] assert api_mock.fetch_orders.call_count == 0 - default_conf['dry_run'] = False + default_conf["dry_run"] = False - exchange = get_patched_exchange(mocker, default_conf, api_mock, id='bybit') - res = exchange.fetch_orders('mocked', start_time) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id="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 assert api_mock.fetch_open_orders.call_count == 0 @@ -119,53 +121,59 @@ def test_bybit_fetch_orders(default_conf, mocker, limit_order): def test_bybit_fetch_order_canceled_empty(default_conf_usdt, mocker): - default_conf_usdt['dry_run'] = False + default_conf_usdt["dry_run"] = False api_mock = MagicMock() - api_mock.fetch_order = MagicMock(return_value={ - 'id': '123', - 'symbol': 'BTC/USDT', - 'status': 'canceled', - 'filled': 0.0, - 'remaining': 0.0, - 'amount': 20.0, - }) + api_mock.fetch_order = MagicMock( + return_value={ + "id": "123", + "symbol": "BTC/USDT", + "status": "canceled", + "filled": 0.0, + "remaining": 0.0, + "amount": 20.0, + } + ) 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, id="bybit") - res = exchange.fetch_order('123', 'BTC/USDT') - assert res['remaining'] is None - assert res['filled'] == 0.0 - assert res['amount'] == 20.0 - assert res['status'] == 'canceled' + res = exchange.fetch_order("123", "BTC/USDT") + assert res["remaining"] is None + assert res["filled"] == 0.0 + assert res["amount"] == 20.0 + assert res["status"] == "canceled" - api_mock.fetch_order = MagicMock(return_value={ - 'id': '123', - 'symbol': 'BTC/USDT', - 'status': 'canceled', - 'filled': 0.0, - 'remaining': 20.0, - 'amount': 20.0, - }) + api_mock.fetch_order = MagicMock( + return_value={ + "id": "123", + "symbol": "BTC/USDT", + "status": "canceled", + "filled": 0.0, + "remaining": 20.0, + "amount": 20.0, + } + ) # Don't touch orders which return correctly. - res1 = exchange.fetch_order('123', 'BTC/USDT') - assert res1['remaining'] == 20.0 - assert res1['filled'] == 0.0 - assert res1['amount'] == 20.0 - assert res1['status'] == 'canceled' + res1 = exchange.fetch_order("123", "BTC/USDT") + assert res1["remaining"] == 20.0 + assert res1["filled"] == 0.0 + assert res1["amount"] == 20.0 + assert res1["status"] == "canceled" # Reverse test - remaining is not touched - api_mock.fetch_order = MagicMock(return_value={ - 'id': '124', - 'symbol': 'BTC/USDT', - 'status': 'open', - 'filled': 0.0, - 'remaining': 20.0, - 'amount': 20.0, - }) - res2 = exchange.fetch_order('123', 'BTC/USDT') - assert res2['remaining'] == 20.0 - assert res2['filled'] == 0.0 - assert res2['amount'] == 20.0 - assert res2['status'] == 'open' + api_mock.fetch_order = MagicMock( + return_value={ + "id": "124", + "symbol": "BTC/USDT", + "status": "open", + "filled": 0.0, + "remaining": 20.0, + "amount": 20.0, + } + ) + res2 = exchange.fetch_order("123", "BTC/USDT") + assert res2["remaining"] == 20.0 + assert res2["filled"] == 0.0 + assert res2["amount"] == 20.0 + assert res2["status"] == "open" diff --git a/tests/exchange/test_exchange_utils.py b/tests/exchange/test_exchange_utils.py index ae8f99932..7fe8cb707 100644 --- a/tests/exchange/test_exchange_utils.py +++ b/tests/exchange/test_exchange_utils.py @@ -32,90 +32,97 @@ from tests.conftest import log_has_re def test_check_exchange(default_conf, caplog) -> None: # Test an officially supported by Freqtrade team exchange - default_conf['runmode'] = RunMode.DRY_RUN - default_conf.get('exchange').update({'name': 'BINANCE'}) - assert check_exchange(default_conf) - assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", - caplog) - caplog.clear() - - # Test an officially supported by Freqtrade team exchange - default_conf.get('exchange').update({'name': 'binance'}) + default_conf["runmode"] = RunMode.DRY_RUN + default_conf.get("exchange").update({"name": "BINANCE"}) assert check_exchange(default_conf) assert log_has_re( - r"Exchange \"binance\" is officially supported by the Freqtrade development team\.", - caplog) + r"Exchange .* is officially supported by the Freqtrade development team\.", caplog + ) caplog.clear() # Test an officially supported by Freqtrade team exchange - default_conf.get('exchange').update({'name': 'binanceus'}) + default_conf.get("exchange").update({"name": "binance"}) + assert check_exchange(default_conf) + assert log_has_re( + r"Exchange \"binance\" is officially supported by the Freqtrade development team\.", caplog + ) + caplog.clear() + + # Test an officially supported by Freqtrade team exchange + default_conf.get("exchange").update({"name": "binanceus"}) assert check_exchange(default_conf) assert log_has_re( r"Exchange \"binanceus\" is officially supported by the Freqtrade development team\.", - caplog) + caplog, + ) caplog.clear() # Test an officially supported by Freqtrade team exchange - with remapping - default_conf.get('exchange').update({'name': 'okx'}) + default_conf.get("exchange").update({"name": "okx"}) assert check_exchange(default_conf) assert log_has_re( - r"Exchange \"okx\" is officially supported by the Freqtrade development team\.", - caplog) + r"Exchange \"okx\" is officially supported by the Freqtrade development team\.", caplog + ) caplog.clear() # Test an available exchange, supported by ccxt - default_conf.get('exchange').update({'name': 'huobijp'}) + default_conf.get("exchange").update({"name": "huobijp"}) assert check_exchange(default_conf) - assert log_has_re(r"Exchange .* is known to the ccxt library, available for the bot, " - r"but not officially supported " - r"by the Freqtrade development team\. .*", caplog) + assert log_has_re( + r"Exchange .* is known to the ccxt library, available for the bot, " + r"but not officially supported " + r"by the Freqtrade development team\. .*", + caplog, + ) caplog.clear() # Test a 'bad' exchange, which known to have serious problems - default_conf.get('exchange').update({'name': 'bitmex'}) - with pytest.raises(OperationalException, - match=r"Exchange .* will not work with Freqtrade\..*"): + default_conf.get("exchange").update({"name": "bitmex"}) + with pytest.raises(OperationalException, match=r"Exchange .* will not work with Freqtrade\..*"): check_exchange(default_conf) caplog.clear() # Test a 'bad' exchange with check_for_bad=False - default_conf.get('exchange').update({'name': 'bitmex'}) + default_conf.get("exchange").update({"name": "bitmex"}) assert check_exchange(default_conf, False) - assert log_has_re(r"Exchange .* is known to the ccxt library, available for the bot, " - r"but not officially supported " - r"by the Freqtrade development team\. .*", caplog) + assert log_has_re( + r"Exchange .* is known to the ccxt library, available for the bot, " + r"but not officially supported " + r"by the Freqtrade development team\. .*", + caplog, + ) caplog.clear() # Test an invalid exchange - default_conf.get('exchange').update({'name': 'unknown_exchange'}) + default_conf.get("exchange").update({"name": "unknown_exchange"}) with pytest.raises( OperationalException, match=r'Exchange "unknown_exchange" is not known to the ccxt library ' - r'and therefore not available for the bot.*' + r"and therefore not available for the bot.*", ): check_exchange(default_conf) # Test no exchange... - default_conf.get('exchange').update({'name': ''}) - default_conf['runmode'] = RunMode.PLOT + default_conf.get("exchange").update({"name": ""}) + default_conf["runmode"] = RunMode.PLOT assert check_exchange(default_conf) # Test no exchange... - default_conf.get('exchange').update({'name': ''}) - default_conf['runmode'] = RunMode.UTIL_EXCHANGE - with pytest.raises(OperationalException, - match=r'This command requires a configured exchange.*'): + default_conf.get("exchange").update({"name": ""}) + default_conf["runmode"] = RunMode.UTIL_EXCHANGE + with pytest.raises( + OperationalException, match=r"This command requires a configured exchange.*" + ): check_exchange(default_conf) def test_date_minus_candles(): - date = datetime(2019, 8, 12, 13, 25, 0, tzinfo=timezone.utc) assert date_minus_candles("5m", 3, date) == date - timedelta(minutes=15) assert date_minus_candles("5m", 5, date) == date - timedelta(minutes=25) assert date_minus_candles("1m", 6, date) == date - timedelta(minutes=6) assert date_minus_candles("1h", 3, date) == date - timedelta(hours=3, minutes=25) - assert date_minus_candles("1h", 3) == timeframe_to_prev_date('1h') - timedelta(hours=3) + assert date_minus_candles("1h", 3) == timeframe_to_prev_date("1h") - timedelta(hours=3) def test_timeframe_to_minutes(): @@ -139,17 +146,20 @@ def test_timeframe_to_msecs(): assert timeframe_to_msecs("1d") == 86400000 -@pytest.mark.parametrize("timeframe,expected", [ - ("1s", '1s'), - ("15s", '15s'), - ("5m", '300s'), - ("10m", '600s'), - ("1h", '3600s'), - ("1d", '86400s'), - ("1w", '1W-MON'), - ("1M", '1MS'), - ("1y", '1YS'), -]) +@pytest.mark.parametrize( + "timeframe,expected", + [ + ("1s", "1s"), + ("15s", "15s"), + ("5m", "300s"), + ("10m", "600s"), + ("1h", "3600s"), + ("1d", "86400s"), + ("1w", "1W-MON"), + ("1M", "1MS"), + ("1y", "1YS"), + ], +) def test_timeframe_to_resample_freq(timeframe, expected): assert timeframe_to_resample_freq(timeframe) == expected @@ -179,9 +189,9 @@ def test_timeframe_to_prev_date(): assert timeframe_to_prev_date("5m") < date # Does not round time = datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc) - assert timeframe_to_prev_date('5m', time) == time + assert timeframe_to_prev_date("5m", time) == time time = datetime(2019, 8, 12, 13, 0, 0, tzinfo=timezone.utc) - assert timeframe_to_prev_date('1h', time) == time + assert timeframe_to_prev_date("1h", time) == time def test_timeframe_to_next_date(): @@ -212,35 +222,43 @@ def test_timeframe_to_next_date(): assert timeframe_to_next_date("5m", date) == date + timedelta(minutes=5) -@pytest.mark.parametrize("amount,precision_mode,precision,expected", [ - (2.34559, DECIMAL_PLACES, 4, 2.3455), - (2.34559, DECIMAL_PLACES, 5, 2.34559), - (2.34559, DECIMAL_PLACES, 3, 2.345), - (2.9999, DECIMAL_PLACES, 3, 2.999), - (2.9909, DECIMAL_PLACES, 3, 2.990), - (2.9909, DECIMAL_PLACES, 0, 2), - (29991.5555, DECIMAL_PLACES, 0, 29991), - (29991.5555, DECIMAL_PLACES, -1, 29990), - (29991.5555, DECIMAL_PLACES, -2, 29900), - # Tests for - (2.34559, SIGNIFICANT_DIGITS, 4, 2.345), - (2.34559, SIGNIFICANT_DIGITS, 5, 2.3455), - (2.34559, SIGNIFICANT_DIGITS, 3, 2.34), - (2.9999, SIGNIFICANT_DIGITS, 3, 2.99), - (2.9909, SIGNIFICANT_DIGITS, 3, 2.99), - (0.0000077723, SIGNIFICANT_DIGITS, 5, 0.0000077723), - (0.0000077723, SIGNIFICANT_DIGITS, 3, 0.00000777), - (0.0000077723, SIGNIFICANT_DIGITS, 1, 0.000007), - # Tests for Tick-size - (2.34559, TICK_SIZE, 0.0001, 2.3455), - (2.34559, TICK_SIZE, 0.00001, 2.34559), - (2.34559, TICK_SIZE, 0.001, 2.345), - (2.9999, TICK_SIZE, 0.001, 2.999), - (2.9909, TICK_SIZE, 0.001, 2.990), - (2.9909, TICK_SIZE, 0.005, 2.99), - (2.9999, TICK_SIZE, 0.005, 2.995), -]) -def test_amount_to_precision(amount, precision_mode, precision, expected,): +@pytest.mark.parametrize( + "amount,precision_mode,precision,expected", + [ + (2.34559, DECIMAL_PLACES, 4, 2.3455), + (2.34559, DECIMAL_PLACES, 5, 2.34559), + (2.34559, DECIMAL_PLACES, 3, 2.345), + (2.9999, DECIMAL_PLACES, 3, 2.999), + (2.9909, DECIMAL_PLACES, 3, 2.990), + (2.9909, DECIMAL_PLACES, 0, 2), + (29991.5555, DECIMAL_PLACES, 0, 29991), + (29991.5555, DECIMAL_PLACES, -1, 29990), + (29991.5555, DECIMAL_PLACES, -2, 29900), + # Tests for + (2.34559, SIGNIFICANT_DIGITS, 4, 2.345), + (2.34559, SIGNIFICANT_DIGITS, 5, 2.3455), + (2.34559, SIGNIFICANT_DIGITS, 3, 2.34), + (2.9999, SIGNIFICANT_DIGITS, 3, 2.99), + (2.9909, SIGNIFICANT_DIGITS, 3, 2.99), + (0.0000077723, SIGNIFICANT_DIGITS, 5, 0.0000077723), + (0.0000077723, SIGNIFICANT_DIGITS, 3, 0.00000777), + (0.0000077723, SIGNIFICANT_DIGITS, 1, 0.000007), + # Tests for Tick-size + (2.34559, TICK_SIZE, 0.0001, 2.3455), + (2.34559, TICK_SIZE, 0.00001, 2.34559), + (2.34559, TICK_SIZE, 0.001, 2.345), + (2.9999, TICK_SIZE, 0.001, 2.999), + (2.9909, TICK_SIZE, 0.001, 2.990), + (2.9909, TICK_SIZE, 0.005, 2.99), + (2.9999, TICK_SIZE, 0.005, 2.995), + ], +) +def test_amount_to_precision( + amount, + precision_mode, + precision, + expected, +): """ Test rounds down """ @@ -252,107 +270,115 @@ def test_amount_to_precision(amount, precision_mode, precision, expected,): assert amount_to_precision(amount, precision, precision_mode) == expected -@pytest.mark.parametrize("price,precision_mode,precision,expected,rounding_mode", [ - # Tests for DECIMAL_PLACES, ROUND_UP - (2.34559, DECIMAL_PLACES, 4, 2.3456, ROUND_UP), - (2.34559, DECIMAL_PLACES, 5, 2.34559, ROUND_UP), - (2.34559, DECIMAL_PLACES, 3, 2.346, ROUND_UP), - (2.9999, DECIMAL_PLACES, 3, 3.000, ROUND_UP), - (2.9909, DECIMAL_PLACES, 3, 2.991, ROUND_UP), - (2.9901, DECIMAL_PLACES, 3, 2.991, ROUND_UP), - (2.34559, DECIMAL_PLACES, 5, 2.34559, ROUND_DOWN), - (2.34559, DECIMAL_PLACES, 4, 2.3455, ROUND_DOWN), - (2.9901, DECIMAL_PLACES, 3, 2.990, ROUND_DOWN), - (0.00299, DECIMAL_PLACES, 3, 0.002, ROUND_DOWN), - # Tests for DECIMAL_PLACES, ROUND - (2.345600000000001, DECIMAL_PLACES, 4, 2.3456, ROUND), - (2.345551, DECIMAL_PLACES, 4, 2.3456, ROUND), - (2.49, DECIMAL_PLACES, 0, 2., ROUND), - (2.51, DECIMAL_PLACES, 0, 3., ROUND), - (5.1, DECIMAL_PLACES, -1, 10., ROUND), - (4.9, DECIMAL_PLACES, -1, 0., ROUND), - (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000007, ROUND), - (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000072, ROUND), - (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000078, ROUND), - # Tests for TICK_SIZE, ROUND_UP - (2.34559, TICK_SIZE, 0.0001, 2.3456, ROUND_UP), - (2.34559, TICK_SIZE, 0.00001, 2.34559, ROUND_UP), - (2.34559, TICK_SIZE, 0.001, 2.346, ROUND_UP), - (2.9999, TICK_SIZE, 0.001, 3.000, ROUND_UP), - (2.9909, TICK_SIZE, 0.001, 2.991, ROUND_UP), - (2.9909, TICK_SIZE, 0.001, 2.990, ROUND_DOWN), - (2.9909, TICK_SIZE, 0.005, 2.995, ROUND_UP), - (2.9973, TICK_SIZE, 0.005, 3.0, ROUND_UP), - (2.9977, TICK_SIZE, 0.005, 3.0, ROUND_UP), - (234.43, TICK_SIZE, 0.5, 234.5, ROUND_UP), - (234.43, TICK_SIZE, 0.5, 234.0, ROUND_DOWN), - (234.53, TICK_SIZE, 0.5, 235.0, ROUND_UP), - (234.53, TICK_SIZE, 0.5, 234.5, ROUND_DOWN), - (0.891534, TICK_SIZE, 0.0001, 0.8916, ROUND_UP), - (64968.89, TICK_SIZE, 0.01, 64968.89, ROUND_UP), - (0.000000003483, TICK_SIZE, 1e-12, 0.000000003483, ROUND_UP), - # Tests for TICK_SIZE, ROUND - (2.49, TICK_SIZE, 1., 2., ROUND), - (2.51, TICK_SIZE, 1., 3., ROUND), - (2.000000051, TICK_SIZE, 0.0000001, 2.0000001, ROUND), - (2.000000049, TICK_SIZE, 0.0000001, 2., ROUND), - (2.9909, TICK_SIZE, 0.005, 2.990, ROUND), - (2.9973, TICK_SIZE, 0.005, 2.995, ROUND), - (2.9977, TICK_SIZE, 0.005, 3.0, ROUND), - (234.24, TICK_SIZE, 0.5, 234., ROUND), - (234.26, TICK_SIZE, 0.5, 234.5, ROUND), - # Tests for TRUNCATTE - (2.34559, DECIMAL_PLACES, 4, 2.3455, TRUNCATE), - (2.34559, DECIMAL_PLACES, 5, 2.34559, TRUNCATE), - (2.34559, DECIMAL_PLACES, 3, 2.345, TRUNCATE), - (2.9999, DECIMAL_PLACES, 3, 2.999, TRUNCATE), - (2.9909, DECIMAL_PLACES, 3, 2.990, TRUNCATE), - (2.9909, TICK_SIZE, 0.001, 2.990, TRUNCATE), - (2.9909, TICK_SIZE, 0.01, 2.99, TRUNCATE), - (2.9909, TICK_SIZE, 0.1, 2.9, TRUNCATE), - # Tests for Significant - (2.34559, SIGNIFICANT_DIGITS, 4, 2.345, TRUNCATE), - (2.34559, SIGNIFICANT_DIGITS, 5, 2.3455, TRUNCATE), - (2.34559, SIGNIFICANT_DIGITS, 3, 2.34, TRUNCATE), - (2.9999, SIGNIFICANT_DIGITS, 3, 2.99, TRUNCATE), - (2.9909, SIGNIFICANT_DIGITS, 2, 2.9, TRUNCATE), - (0.00000777, SIGNIFICANT_DIGITS, 2, 0.0000077, TRUNCATE), - (0.00000729, SIGNIFICANT_DIGITS, 2, 0.0000072, TRUNCATE), - # ROUND - (722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND), - (790.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND), - (722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND), - (722.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND_UP), - (722.2, SIGNIFICANT_DIGITS, 2, 730.0, ROUND_UP), - (777.7, SIGNIFICANT_DIGITS, 2, 780.0, ROUND_UP), - (777.7, SIGNIFICANT_DIGITS, 3, 778.0, ROUND_UP), - (722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND_DOWN), - (722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND_DOWN), - (777.7, SIGNIFICANT_DIGITS, 2, 770.0, ROUND_DOWN), - (777.7, SIGNIFICANT_DIGITS, 3, 777.0, ROUND_DOWN), - - (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000008, ROUND_UP), - (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000073, ROUND_UP), - (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000078, ROUND_UP), - (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000007, ROUND_DOWN), - (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000072, ROUND_DOWN), - (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000077, ROUND_DOWN), -]) +@pytest.mark.parametrize( + "price,precision_mode,precision,expected,rounding_mode", + [ + # Tests for DECIMAL_PLACES, ROUND_UP + (2.34559, DECIMAL_PLACES, 4, 2.3456, ROUND_UP), + (2.34559, DECIMAL_PLACES, 5, 2.34559, ROUND_UP), + (2.34559, DECIMAL_PLACES, 3, 2.346, ROUND_UP), + (2.9999, DECIMAL_PLACES, 3, 3.000, ROUND_UP), + (2.9909, DECIMAL_PLACES, 3, 2.991, ROUND_UP), + (2.9901, DECIMAL_PLACES, 3, 2.991, ROUND_UP), + (2.34559, DECIMAL_PLACES, 5, 2.34559, ROUND_DOWN), + (2.34559, DECIMAL_PLACES, 4, 2.3455, ROUND_DOWN), + (2.9901, DECIMAL_PLACES, 3, 2.990, ROUND_DOWN), + (0.00299, DECIMAL_PLACES, 3, 0.002, ROUND_DOWN), + # Tests for DECIMAL_PLACES, ROUND + (2.345600000000001, DECIMAL_PLACES, 4, 2.3456, ROUND), + (2.345551, DECIMAL_PLACES, 4, 2.3456, ROUND), + (2.49, DECIMAL_PLACES, 0, 2.0, ROUND), + (2.51, DECIMAL_PLACES, 0, 3.0, ROUND), + (5.1, DECIMAL_PLACES, -1, 10.0, ROUND), + (4.9, DECIMAL_PLACES, -1, 0.0, ROUND), + (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000007, ROUND), + (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000072, ROUND), + (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000078, ROUND), + # Tests for TICK_SIZE, ROUND_UP + (2.34559, TICK_SIZE, 0.0001, 2.3456, ROUND_UP), + (2.34559, TICK_SIZE, 0.00001, 2.34559, ROUND_UP), + (2.34559, TICK_SIZE, 0.001, 2.346, ROUND_UP), + (2.9999, TICK_SIZE, 0.001, 3.000, ROUND_UP), + (2.9909, TICK_SIZE, 0.001, 2.991, ROUND_UP), + (2.9909, TICK_SIZE, 0.001, 2.990, ROUND_DOWN), + (2.9909, TICK_SIZE, 0.005, 2.995, ROUND_UP), + (2.9973, TICK_SIZE, 0.005, 3.0, ROUND_UP), + (2.9977, TICK_SIZE, 0.005, 3.0, ROUND_UP), + (234.43, TICK_SIZE, 0.5, 234.5, ROUND_UP), + (234.43, TICK_SIZE, 0.5, 234.0, ROUND_DOWN), + (234.53, TICK_SIZE, 0.5, 235.0, ROUND_UP), + (234.53, TICK_SIZE, 0.5, 234.5, ROUND_DOWN), + (0.891534, TICK_SIZE, 0.0001, 0.8916, ROUND_UP), + (64968.89, TICK_SIZE, 0.01, 64968.89, ROUND_UP), + (0.000000003483, TICK_SIZE, 1e-12, 0.000000003483, ROUND_UP), + # Tests for TICK_SIZE, ROUND + (2.49, TICK_SIZE, 1.0, 2.0, ROUND), + (2.51, TICK_SIZE, 1.0, 3.0, ROUND), + (2.000000051, TICK_SIZE, 0.0000001, 2.0000001, ROUND), + (2.000000049, TICK_SIZE, 0.0000001, 2.0, ROUND), + (2.9909, TICK_SIZE, 0.005, 2.990, ROUND), + (2.9973, TICK_SIZE, 0.005, 2.995, ROUND), + (2.9977, TICK_SIZE, 0.005, 3.0, ROUND), + (234.24, TICK_SIZE, 0.5, 234.0, ROUND), + (234.26, TICK_SIZE, 0.5, 234.5, ROUND), + # Tests for TRUNCATTE + (2.34559, DECIMAL_PLACES, 4, 2.3455, TRUNCATE), + (2.34559, DECIMAL_PLACES, 5, 2.34559, TRUNCATE), + (2.34559, DECIMAL_PLACES, 3, 2.345, TRUNCATE), + (2.9999, DECIMAL_PLACES, 3, 2.999, TRUNCATE), + (2.9909, DECIMAL_PLACES, 3, 2.990, TRUNCATE), + (2.9909, TICK_SIZE, 0.001, 2.990, TRUNCATE), + (2.9909, TICK_SIZE, 0.01, 2.99, TRUNCATE), + (2.9909, TICK_SIZE, 0.1, 2.9, TRUNCATE), + # Tests for Significant + (2.34559, SIGNIFICANT_DIGITS, 4, 2.345, TRUNCATE), + (2.34559, SIGNIFICANT_DIGITS, 5, 2.3455, TRUNCATE), + (2.34559, SIGNIFICANT_DIGITS, 3, 2.34, TRUNCATE), + (2.9999, SIGNIFICANT_DIGITS, 3, 2.99, TRUNCATE), + (2.9909, SIGNIFICANT_DIGITS, 2, 2.9, TRUNCATE), + (0.00000777, SIGNIFICANT_DIGITS, 2, 0.0000077, TRUNCATE), + (0.00000729, SIGNIFICANT_DIGITS, 2, 0.0000072, TRUNCATE), + # ROUND + (722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND), + (790.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND), + (722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND), + (722.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND_UP), + (722.2, SIGNIFICANT_DIGITS, 2, 730.0, ROUND_UP), + (777.7, SIGNIFICANT_DIGITS, 2, 780.0, ROUND_UP), + (777.7, SIGNIFICANT_DIGITS, 3, 778.0, ROUND_UP), + (722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND_DOWN), + (722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND_DOWN), + (777.7, SIGNIFICANT_DIGITS, 2, 770.0, ROUND_DOWN), + (777.7, SIGNIFICANT_DIGITS, 3, 777.0, ROUND_DOWN), + (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000008, ROUND_UP), + (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000073, ROUND_UP), + (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000078, ROUND_UP), + (0.000007222, SIGNIFICANT_DIGITS, 1, 0.000007, ROUND_DOWN), + (0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000072, ROUND_DOWN), + (0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000077, ROUND_DOWN), + ], +) def test_price_to_precision(price, precision_mode, precision, expected, rounding_mode): - assert price_to_precision( - price, precision, precision_mode, rounding_mode=rounding_mode) == expected + assert ( + price_to_precision(price, precision, precision_mode, rounding_mode=rounding_mode) + == expected + ) -@pytest.mark.parametrize('amount,precision,precision_mode,contract_size,expected', [ - (1.17, 1.0, 4, 0.01, 1.17), # Tick size - (1.17, 1.0, 2, 0.01, 1.17), # - (1.16, 1.0, 4, 0.01, 1.16), # - (1.16, 1.0, 2, 0.01, 1.16), # - (1.13, 1.0, 2, 0.01, 1.13), # - (10.988, 1.0, 2, 10, 10), - (10.988, 1.0, 4, 10, 10), -]) -def test_amount_to_contract_precision_standalone(amount, precision, precision_mode, contract_size, - expected): +@pytest.mark.parametrize( + "amount,precision,precision_mode,contract_size,expected", + [ + (1.17, 1.0, 4, 0.01, 1.17), # Tick size + (1.17, 1.0, 2, 0.01, 1.17), # + (1.16, 1.0, 4, 0.01, 1.16), # + (1.16, 1.0, 2, 0.01, 1.16), # + (1.13, 1.0, 2, 0.01, 1.13), # + (10.988, 1.0, 2, 10, 10), + (10.988, 1.0, 4, 10, 10), + ], +) +def test_amount_to_contract_precision_standalone( + amount, precision, precision_mode, contract_size, expected +): res = amount_to_contract_precision(amount, precision, precision_mode, contract_size) assert pytest.approx(res) == expected diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index 3cb5a9a3e..b4e021a5d 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -9,103 +9,113 @@ 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, id="gate") fetch_order_mock = MagicMock() exchange.fetch_order = fetch_order_mock - exchange.fetch_stoploss_order('1234', 'ETH/BTC') + exchange.fetch_stoploss_order("1234", "ETH/BTC") assert fetch_order_mock.call_count == 1 - assert fetch_order_mock.call_args_list[0][1]['order_id'] == '1234' - assert fetch_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' - assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True} + assert fetch_order_mock.call_args_list[0][1]["order_id"] == "1234" + assert fetch_order_mock.call_args_list[0][1]["pair"] == "ETH/BTC" + assert fetch_order_mock.call_args_list[0][1]["params"] == {"stop": True} - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + 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, id="gate") - exchange.fetch_order = MagicMock(return_value={ - 'status': 'closed', - 'id': '1234', - 'stopPrice': 5.62, - 'info': { - 'trade_id': '222555' + exchange.fetch_order = MagicMock( + return_value={ + "status": "closed", + "id": "1234", + "stopPrice": 5.62, + "info": {"trade_id": "222555"}, } - }) + ) - exchange.fetch_stoploss_order('1234', 'ETH/BTC') + exchange.fetch_stoploss_order("1234", "ETH/BTC") assert exchange.fetch_order.call_count == 2 - assert exchange.fetch_order.call_args_list[0][1]['order_id'] == '1234' - assert exchange.fetch_order.call_args_list[1][1]['order_id'] == '222555' + assert exchange.fetch_order.call_args_list[0][1]["order_id"] == "1234" + assert exchange.fetch_order.call_args_list[1][1]["order_id"] == "222555" 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, id="gate") cancel_order_mock = MagicMock() exchange.cancel_order = cancel_order_mock - exchange.cancel_stoploss_order('1234', 'ETH/BTC') + exchange.cancel_stoploss_order("1234", "ETH/BTC") assert cancel_order_mock.call_count == 1 - assert cancel_order_mock.call_args_list[0][1]['order_id'] == '1234' - assert cancel_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' - assert cancel_order_mock.call_args_list[0][1]['params'] == {'stop': True} + assert cancel_order_mock.call_args_list[0][1]["order_id"] == "1234" + assert cancel_order_mock.call_args_list[0][1]["pair"] == "ETH/BTC" + assert cancel_order_mock.call_args_list[0][1]["params"] == {"stop": True} -@pytest.mark.parametrize('sl1,sl2,sl3,side', [ - (1501, 1499, 1501, "sell"), - (1499, 1501, 1499, "buy") -]) +@pytest.mark.parametrize( + "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, id="gate") order = { - 'price': 1500, - 'stopPrice': 1500, + "price": 1500, + "stopPrice": 1500, } assert exchange.stoploss_adjust(sl1, order, side) assert not exchange.stoploss_adjust(sl2, order, side) -@pytest.mark.parametrize('takerormaker,rate,cost', [ - ('taker', 0.0005, 0.0001554325), - ('maker', 0.0, 0.0), -]) +@pytest.mark.parametrize( + "takerormaker,rate,cost", + [ + ("taker", 0.0005, 0.0001554325), + ("maker", 0.0, 0.0), + ], +) def test_fetch_my_trades_gate(mocker, default_conf, takerormaker, rate, cost): - mocker.patch(f'{EXMS}.exchange_has', return_value=True) - tick = {'ETH/USDT:USDT': { - 'info': {'user_id': '', - 'taker_fee': '0.0018', - 'maker_fee': '0.0018', - 'gt_discount': False, - 'gt_taker_fee': '0', - 'gt_maker_fee': '0', - 'loan_fee': '0.18', - 'point_type': '1', - 'futures_taker_fee': '0.0005', - 'futures_maker_fee': '0'}, - 'symbol': 'ETH/USDT:USDT', - 'maker': 0.0, - 'taker': 0.0005} - } - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED + mocker.patch(f"{EXMS}.exchange_has", return_value=True) + tick = { + "ETH/USDT:USDT": { + "info": { + "user_id": "", + "taker_fee": "0.0018", + "maker_fee": "0.0018", + "gt_discount": False, + "gt_taker_fee": "0", + "gt_maker_fee": "0", + "loan_fee": "0.18", + "point_type": "1", + "futures_taker_fee": "0.0005", + "futures_maker_fee": "0", + }, + "symbol": "ETH/USDT:USDT", + "maker": 0.0, + "taker": 0.0005, + } + } + default_conf["dry_run"] = False + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.ISOLATED api_mock = MagicMock() - api_mock.fetch_my_trades = MagicMock(return_value=[{ - 'fee': {'cost': None}, - 'price': 3108.65, - 'cost': 0.310865, - 'order': '22255', - 'takerOrMaker': takerormaker, - 'amount': 1, # 1 contract - }]) - exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id='gate') + api_mock.fetch_my_trades = MagicMock( + return_value=[ + { + "fee": {"cost": None}, + "price": 3108.65, + "cost": 0.310865, + "order": "22255", + "takerOrMaker": takerormaker, + "amount": 1, # 1 contract + } + ] + ) + exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id="gate") exchange._trading_fees = tick - trades = exchange.get_trades_for_order('22255', 'ETH/USDT:USDT', datetime.now(timezone.utc)) + trades = exchange.get_trades_for_order("22255", "ETH/USDT:USDT", datetime.now(timezone.utc)) trade = trades[0] - assert trade['fee'] - assert trade['fee']['rate'] == rate - assert trade['fee']['currency'] == 'USDT' - assert trade['fee']['cost'] == cost + assert trade["fee"] + assert trade["fee"]["rate"] == rate + assert trade["fee"]["currency"] == "USDT" + assert trade["fee"]["cost"] == cost diff --git a/tests/exchange/test_htx.py b/tests/exchange/test_htx.py index ac136618f..807d9b28f 100644 --- a/tests/exchange/test_htx.py +++ b/tests/exchange/test_htx.py @@ -9,108 +9,132 @@ from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers -@pytest.mark.parametrize('limitratio,expected,side', [ - (None, 220 * 0.99, "sell"), - (0.99, 220 * 0.99, "sell"), - (0.98, 220 * 0.98, "sell"), -]) +@pytest.mark.parametrize( + "limitratio,expected,side", + [ + (None, 220 * 0.99, "sell"), + (0.99, 220 * 0.99, "sell"), + (0.98, 220 * 0.98, "sell"), + ], +) def test_create_stoploss_order_htx(default_conf, mocker, limitratio, expected, side): api_mock = MagicMock() - order_id = f'test_prod_buy_{randint(0, 10 ** 6)}' - order_type = 'stop-limit' + order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_type = "stop-limit" - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - } - }) - 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, **kwargs: y) + api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) + 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, **kwargs: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'htx') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "htx") with pytest.raises(InvalidOrderException): - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190, - order_types={'stoploss_on_exchange_limit_ratio': 1.05}, - side=side, - leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", + amount=1, + stop_price=190, + order_types={"stoploss_on_exchange_limit_ratio": 1.05}, + side=side, + leverage=1.0, + ) api_mock.create_order.reset_mock() - order_types = {} if limitratio is None else {'stoploss_on_exchange_limit_ratio': limitratio} + order_types = {} if limitratio is None else {"stoploss_on_exchange_limit_ratio": limitratio} order = exchange.create_stoploss( - pair='ETH/BTC', amount=1, stop_price=220, order_types=order_types, side=side, leverage=1.0) + pair="ETH/BTC", amount=1, stop_price=220, order_types=order_types, side=side, leverage=1.0 + ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' - assert api_mock.create_order.call_args_list[0][1]['type'] == order_type - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' - assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert api_mock.create_order.call_args_list[0][1]["symbol"] == "ETH/BTC" + assert api_mock.create_order.call_args_list[0][1]["type"] == order_type + assert api_mock.create_order.call_args_list[0][1]["side"] == "sell" + assert api_mock.create_order.call_args_list[0][1]["amount"] == 1 # Price should be 1% below stopprice - assert api_mock.create_order.call_args_list[0][1]['price'] == expected - assert api_mock.create_order.call_args_list[0][1]['params'] == {"stopPrice": 220, - "operator": "lte", - } + assert api_mock.create_order.call_args_list[0][1]["price"] == expected + assert api_mock.create_order.call_args_list[0][1]["params"] == { + "stopPrice": 220, + "operator": "lte", + } # test exception handling with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'htx') - exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side=side, leverage=1.0) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "htx") + exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( - side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side=side, leverage=1.0) + side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.") + ) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "binance") + exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) - ccxt_exceptionhandlers(mocker, default_conf, api_mock, "htx", - "create_stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, - side=side, leverage=1.0) + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "htx", + "create_stoploss", + "create_order", + retries=1, + pair="ETH/BTC", + amount=1, + stop_price=220, + order_types={}, + side=side, + leverage=1.0, + ) def test_create_stoploss_order_dry_run_htx(default_conf, mocker): api_mock = MagicMock() - order_type = 'stop-limit' - default_conf['dry_run'] = True - 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) + order_type = "stop-limit" + default_conf["dry_run"] = True + 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, 'htx') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "htx") with pytest.raises(InvalidOrderException): - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190, - order_types={'stoploss_on_exchange_limit_ratio': 1.05}, - side='sell', leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", + amount=1, + stop_price=190, + order_types={"stoploss_on_exchange_limit_ratio": 1.05}, + side="sell", + leverage=1.0, + ) api_mock.create_order.reset_mock() - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side='sell', leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side="sell", leverage=1.0 + ) - assert 'id' in order - assert 'info' in order - assert 'type' in order + assert "id" in order + assert "info" in order + assert "type" in order - assert order['type'] == order_type - assert order['price'] == 220 - assert order['amount'] == 1 + assert order["type"] == order_type + assert order["price"] == 220 + assert order["amount"] == 1 def test_stoploss_adjust_htx(mocker, default_conf): - exchange = get_patched_exchange(mocker, default_conf, id='htx') + exchange = get_patched_exchange(mocker, default_conf, id="htx") order = { - 'type': 'stop', - 'price': 1500, - 'stopPrice': '1500', + "type": "stop", + "price": 1500, + "stopPrice": "1500", } - assert exchange.stoploss_adjust(1501, order, 'sell') - assert not exchange.stoploss_adjust(1499, order, 'sell') + assert exchange.stoploss_adjust(1501, order, "sell") + assert not exchange.stoploss_adjust(1499, order, "sell") # Test with invalid order case - assert exchange.stoploss_adjust(1501, order, 'sell') + assert exchange.stoploss_adjust(1501, order, "sell") diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index 760e18982..e9130f22d 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -9,276 +9,274 @@ from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers -STOPLOSS_ORDERTYPE = 'stop-loss' -STOPLOSS_LIMIT_ORDERTYPE = 'stop-loss-limit' +STOPLOSS_ORDERTYPE = "stop-loss" +STOPLOSS_LIMIT_ORDERTYPE = "stop-loss-limit" -@pytest.mark.parametrize("order_type,time_in_force,expected_params", [ - ('limit', 'ioc', {'timeInForce': 'IOC', 'trading_agreement': 'agree'}), - ('limit', 'PO', {'postOnly': True, 'trading_agreement': 'agree'}), - ('market', None, {'trading_agreement': 'agree'}) -]) +@pytest.mark.parametrize( + "order_type,time_in_force,expected_params", + [ + ("limit", "ioc", {"timeInForce": "IOC", "trading_agreement": "agree"}), + ("limit", "PO", {"postOnly": True, "trading_agreement": "agree"}), + ("market", None, {"trading_agreement": "agree"}), + ], +) def test_kraken_trading_agreement(default_conf, mocker, order_type, time_in_force, expected_params): api_mock = MagicMock() - order_id = f'test_prod_{order_type}_{randint(0, 10 ** 6)}' + order_id = f"test_prod_{order_type}_{randint(0, 10 ** 6)}" api_mock.options = {} - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'symbol': 'ETH/BTC', - 'info': { - 'foo': 'bar' - } - }) - default_conf['dry_run'] = False + api_mock.create_order = MagicMock( + return_value={"id": order_id, "symbol": "ETH/BTC", "info": {"foo": "bar"}} + ) + 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, **kwargs: y) + 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") order = exchange.create_order( - pair='ETH/BTC', + pair="ETH/BTC", ordertype=order_type, side="buy", amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force + time_in_force=time_in_force, ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert api_mock.create_order.call_args[0][0] == "ETH/BTC" assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'buy' + assert api_mock.create_order.call_args[0][2] == "buy" assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == (200 if order_type == 'limit' else None) + assert api_mock.create_order.call_args[0][4] == (200 if order_type == "limit" else None) assert api_mock.create_order.call_args[0][5] == expected_params def test_get_balances_prod(default_conf, mocker): - balance_item = { - 'free': None, - 'total': 10.0, - 'used': 0.0 - } + balance_item = {"free": None, "total": 10.0, "used": 0.0} api_mock = MagicMock() - api_mock.fetch_balance = MagicMock(return_value={ - '1ST': balance_item.copy(), - '2ST': balance_item.copy(), - '3ST': balance_item.copy(), - '4ST': balance_item.copy(), - 'EUR': balance_item.copy(), - 'timestamp': 123123 - }) - kraken_open_orders = [{'symbol': '1ST/EUR', - 'type': 'limit', - 'side': 'sell', - 'price': 20, - 'cost': 0.0, - 'amount': 1.0, - 'filled': 0.0, - 'average': 0.0, - 'remaining': 1.0, - }, - {'status': 'open', - 'symbol': '2ST/EUR', - 'type': 'limit', - 'side': 'sell', - 'price': 20.0, - 'cost': 0.0, - 'amount': 2.0, - 'filled': 0.0, - 'average': 0.0, - 'remaining': 2.0, - }, - {'status': 'open', - 'symbol': '2ST/USD', - 'type': 'limit', - 'side': 'sell', - 'price': 20.0, - 'cost': 0.0, - 'amount': 2.0, - 'filled': 0.0, - 'average': 0.0, - 'remaining': 2.0, - }, - {'status': 'open', - 'symbol': '3ST/EUR', - 'type': 'limit', - 'side': 'buy', - 'price': 0.02, - 'cost': 0.0, - 'amount': 100.0, - 'filled': 0.0, - 'average': 0.0, - 'remaining': 100.0, - }] + api_mock.fetch_balance = MagicMock( + return_value={ + "1ST": balance_item.copy(), + "2ST": balance_item.copy(), + "3ST": balance_item.copy(), + "4ST": balance_item.copy(), + "EUR": balance_item.copy(), + "timestamp": 123123, + } + ) + kraken_open_orders = [ + { + "symbol": "1ST/EUR", + "type": "limit", + "side": "sell", + "price": 20, + "cost": 0.0, + "amount": 1.0, + "filled": 0.0, + "average": 0.0, + "remaining": 1.0, + }, + { + "status": "open", + "symbol": "2ST/EUR", + "type": "limit", + "side": "sell", + "price": 20.0, + "cost": 0.0, + "amount": 2.0, + "filled": 0.0, + "average": 0.0, + "remaining": 2.0, + }, + { + "status": "open", + "symbol": "2ST/USD", + "type": "limit", + "side": "sell", + "price": 20.0, + "cost": 0.0, + "amount": 2.0, + "filled": 0.0, + "average": 0.0, + "remaining": 2.0, + }, + { + "status": "open", + "symbol": "3ST/EUR", + "type": "limit", + "side": "buy", + "price": 0.02, + "cost": 0.0, + "amount": 100.0, + "filled": 0.0, + "average": 0.0, + "remaining": 100.0, + }, + ] api_mock.fetch_open_orders = MagicMock(return_value=kraken_open_orders) - default_conf['dry_run'] = False + default_conf["dry_run"] = False exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") balances = exchange.get_balances() assert len(balances) == 6 - assert balances['1ST']['free'] == 9.0 - assert balances['1ST']['total'] == 10.0 - assert balances['1ST']['used'] == 1.0 + assert balances["1ST"]["free"] == 9.0 + assert balances["1ST"]["total"] == 10.0 + assert balances["1ST"]["used"] == 1.0 - assert balances['2ST']['free'] == 6.0 - assert balances['2ST']['total'] == 10.0 - assert balances['2ST']['used'] == 4.0 + assert balances["2ST"]["free"] == 6.0 + assert balances["2ST"]["total"] == 10.0 + assert balances["2ST"]["used"] == 4.0 - assert balances['3ST']['free'] == 10.0 - assert balances['3ST']['total'] == 10.0 - assert balances['3ST']['used'] == 0.0 + assert balances["3ST"]["free"] == 10.0 + assert balances["3ST"]["total"] == 10.0 + assert balances["3ST"]["used"] == 0.0 - assert balances['4ST']['free'] == 10.0 - assert balances['4ST']['total'] == 10.0 - assert balances['4ST']['used'] == 0.0 + assert balances["4ST"]["free"] == 10.0 + assert balances["4ST"]["total"] == 10.0 + assert balances["4ST"]["used"] == 0.0 - assert balances['EUR']['free'] == 8.0 - assert balances['EUR']['total'] == 10.0 - assert balances['EUR']['used'] == 2.0 - ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken", - "get_balances", "fetch_balance") + assert balances["EUR"]["free"] == 8.0 + assert balances["EUR"]["total"] == 10.0 + assert balances["EUR"]["used"] == 2.0 + ccxt_exceptionhandlers( + mocker, default_conf, api_mock, "kraken", "get_balances", "fetch_balance" + ) -@pytest.mark.parametrize('ordertype', ['market', 'limit']) -@pytest.mark.parametrize('side,adjustedprice', [ - ("sell", 217.8), - ("buy", 222.2), -]) +@pytest.mark.parametrize("ordertype", ["market", "limit"]) +@pytest.mark.parametrize( + "side,adjustedprice", + [ + ("sell", 217.8), + ("buy", 222.2), + ], +) def test_create_stoploss_order_kraken(default_conf, mocker, ordertype, side, adjustedprice): api_mock = MagicMock() - order_id = f'test_prod_buy_{randint(0, 10 ** 6)}' + order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - } - }) + api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) - 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, **kwargs: y) + 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, **kwargs: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kraken") order = exchange.create_stoploss( - pair='ETH/BTC', + pair="ETH/BTC", amount=1, stop_price=220, side=side, - order_types={ - 'stoploss': ordertype, - 'stoploss_on_exchange_limit_ratio': 0.99 - }, - leverage=1.0 + order_types={"stoploss": ordertype, "stoploss_on_exchange_limit_ratio": 0.99}, + leverage=1.0, ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' - assert api_mock.create_order.call_args_list[0][1]['type'] == ordertype - assert api_mock.create_order.call_args_list[0][1]['params'] == { - 'trading_agreement': 'agree', - 'stopLossPrice': 220 + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert api_mock.create_order.call_args_list[0][1]["symbol"] == "ETH/BTC" + assert api_mock.create_order.call_args_list[0][1]["type"] == ordertype + assert api_mock.create_order.call_args_list[0][1]["params"] == { + "trading_agreement": "agree", + "stopLossPrice": 220, } - assert api_mock.create_order.call_args_list[0][1]['side'] == side - assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 - if ordertype == 'limit': - assert api_mock.create_order.call_args_list[0][1]['price'] == adjustedprice + assert api_mock.create_order.call_args_list[0][1]["side"] == side + assert api_mock.create_order.call_args_list[0][1]["amount"] == 1 + if ordertype == "limit": + assert api_mock.create_order.call_args_list[0][1]["price"] == adjustedprice else: - assert api_mock.create_order.call_args_list[0][1]['price'] is None + assert api_mock.create_order.call_args_list[0][1]["price"] is None # test exception handling with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kraken") exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side=side, - leverage=1.0 + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 ) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( - side_effect=ccxt.InvalidOrder("kraken Order would trigger immediately.")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') + side_effect=ccxt.InvalidOrder("kraken Order would trigger immediately.") + ) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kraken") exchange.create_stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side=side, - leverage=1.0 + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 ) - ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken", - "create_stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, - side=side, leverage=1.0) - - -@pytest.mark.parametrize('side', ['buy', 'sell']) -def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side): - api_mock = MagicMock() - default_conf['dry_run'] = True - 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, 'kraken') - - api_mock.create_order.reset_mock() - - order = exchange.create_stoploss( - pair='ETH/BTC', + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "kraken", + "create_stoploss", + "create_order", + retries=1, + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, - leverage=1.0 + leverage=1.0, ) - assert 'id' in order - assert 'info' in order - assert 'type' in order - assert order['type'] == 'market' - assert order['price'] == 220 - assert order['amount'] == 1 +@pytest.mark.parametrize("side", ["buy", "sell"]) +def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side): + api_mock = MagicMock() + default_conf["dry_run"] = True + 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, "kraken") + + api_mock.create_order.reset_mock() + + order = exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) + + assert "id" in order + assert "info" in order + assert "type" in order + + assert order["type"] == "market" + assert order["price"] == 220 + assert order["amount"] == 1 -@pytest.mark.parametrize('sl1,sl2,sl3,side', [ - (1501, 1499, 1501, "sell"), - (1499, 1501, 1499, "buy") -]) +@pytest.mark.parametrize( + "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, id="kraken") order = { - 'type': 'market', - 'stopLossPrice': 1500, + "type": "market", + "stopLossPrice": 1500, } assert exchange.stoploss_adjust(sl1, order, side=side) assert not exchange.stoploss_adjust(sl2, order, side=side) # diff. order type ... - order['type'] = 'limit' + order["type"] = "limit" assert exchange.stoploss_adjust(sl3, order, side=side) -@pytest.mark.parametrize('trade_id, expected', [ - ('1234', False), - ('170544369512007228', False), - ('1705443695120072285', True), - ('170544369512007228555', True), -]) +@pytest.mark.parametrize( + "trade_id, expected", + [ + ("1234", False), + ("170544369512007228", False), + ("1705443695120072285", True), + ("170544369512007228555", True), + ], +) def test__valid_trade_pagination_id_kraken(mocker, default_conf_usdt, trade_id, expected): - exchange = get_patched_exchange(mocker, default_conf_usdt, id='kraken') - assert exchange._valid_trade_pagination_id('XRP/USDT', trade_id) == expected + exchange = get_patched_exchange(mocker, default_conf_usdt, id="kraken") + assert exchange._valid_trade_pagination_id("XRP/USDT", trade_id) == expected diff --git a/tests/exchange/test_kucoin.py b/tests/exchange/test_kucoin.py index a74b77859..1d297505c 100644 --- a/tests/exchange/test_kucoin.py +++ b/tests/exchange/test_kucoin.py @@ -9,161 +9,169 @@ from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers -@pytest.mark.parametrize('order_type', ['market', 'limit']) -@pytest.mark.parametrize('limitratio,expected,side', [ - (None, 220 * 0.99, "sell"), - (0.99, 220 * 0.99, "sell"), - (0.98, 220 * 0.98, "sell"), -]) +@pytest.mark.parametrize("order_type", ["market", "limit"]) +@pytest.mark.parametrize( + "limitratio,expected,side", + [ + (None, 220 * 0.99, "sell"), + (0.99, 220 * 0.99, "sell"), + (0.98, 220 * 0.98, "sell"), + ], +) def test_create_stoploss_order_kucoin(default_conf, mocker, limitratio, expected, side, order_type): api_mock = MagicMock() - order_id = f'test_prod_buy_{randint(0, 10 ** 6)}' + order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - } - }) - 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, **kwargs: y) + api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) + 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, **kwargs: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kucoin') - if order_type == 'limit': + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kucoin") + if order_type == "limit": with pytest.raises(InvalidOrderException): - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190, - order_types={ - 'stoploss': order_type, - 'stoploss_on_exchange_limit_ratio': 1.05}, - side=side, leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", + amount=1, + stop_price=190, + order_types={"stoploss": order_type, "stoploss_on_exchange_limit_ratio": 1.05}, + side=side, + leverage=1.0, + ) api_mock.create_order.reset_mock() - order_types = {'stoploss': order_type} + order_types = {"stoploss": order_type} if limitratio is not None: - order_types.update({'stoploss_on_exchange_limit_ratio': limitratio}) - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types=order_types, side=side, leverage=1.0) + order_types.update({"stoploss_on_exchange_limit_ratio": limitratio}) + order = exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types=order_types, side=side, leverage=1.0 + ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' - assert api_mock.create_order.call_args_list[0][1]['type'] == order_type - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' - assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert api_mock.create_order.call_args_list[0][1]["symbol"] == "ETH/BTC" + assert api_mock.create_order.call_args_list[0][1]["type"] == order_type + assert api_mock.create_order.call_args_list[0][1]["side"] == "sell" + assert api_mock.create_order.call_args_list[0][1]["amount"] == 1 # Price should be 1% below stopprice - if order_type == 'limit': - assert api_mock.create_order.call_args_list[0][1]['price'] == expected + if order_type == "limit": + assert api_mock.create_order.call_args_list[0][1]["price"] == expected else: - assert api_mock.create_order.call_args_list[0][1]['price'] is None + assert api_mock.create_order.call_args_list[0][1]["price"] is None - assert api_mock.create_order.call_args_list[0][1]['params'] == { - 'stopPrice': 220, - 'stop': 'loss' + assert api_mock.create_order.call_args_list[0][1]["params"] == { + "stopPrice": 220, + "stop": "loss", } # test exception handling with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kucoin') - exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side=side, leverage=1.0) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kucoin") + exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( - side_effect=ccxt.InvalidOrder("kucoin Order would trigger immediately.")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kucoin') - exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side=side, leverage=1.0) + side_effect=ccxt.InvalidOrder("kucoin Order would trigger immediately.") + ) + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kucoin") + exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side=side, leverage=1.0 + ) - ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kucoin", - "create_stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, - side=side, leverage=1.0) + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "kucoin", + "create_stoploss", + "create_order", + retries=1, + pair="ETH/BTC", + amount=1, + stop_price=220, + order_types={}, + side=side, + leverage=1.0, + ) def test_stoploss_order_dry_run_kucoin(default_conf, mocker): api_mock = MagicMock() - order_type = 'market' - default_conf['dry_run'] = True - 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) + order_type = "market" + default_conf["dry_run"] = True + 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, 'kucoin') + exchange = get_patched_exchange(mocker, default_conf, api_mock, "kucoin") with pytest.raises(InvalidOrderException): - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190, - order_types={'stoploss': 'limit', - 'stoploss_on_exchange_limit_ratio': 1.05}, - side='sell', leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", + amount=1, + stop_price=190, + order_types={"stoploss": "limit", "stoploss_on_exchange_limit_ratio": 1.05}, + side="sell", + leverage=1.0, + ) api_mock.create_order.reset_mock() - order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={}, side='sell', leverage=1.0) + order = exchange.create_stoploss( + pair="ETH/BTC", amount=1, stop_price=220, order_types={}, side="sell", leverage=1.0 + ) - assert 'id' in order - assert 'info' in order - assert 'type' in order + assert "id" in order + assert "info" in order + assert "type" in order - assert order['type'] == order_type - assert order['price'] == 220 - assert order['amount'] == 1 + assert order["type"] == order_type + assert order["price"] == 220 + assert order["amount"] == 1 def test_stoploss_adjust_kucoin(mocker, default_conf): - exchange = get_patched_exchange(mocker, default_conf, id='kucoin') + exchange = get_patched_exchange(mocker, default_conf, id="kucoin") order = { - 'type': 'limit', - 'price': 1500, - 'stopPrice': 1500, - 'info': {'stopPrice': 1500, 'stop': "limit"}, + "type": "limit", + "price": 1500, + "stopPrice": 1500, + "info": {"stopPrice": 1500, "stop": "limit"}, } - assert exchange.stoploss_adjust(1501, order, 'sell') - assert not exchange.stoploss_adjust(1499, order, 'sell') + assert exchange.stoploss_adjust(1501, order, "sell") + assert not exchange.stoploss_adjust(1499, order, "sell") # Test with invalid order case - order['stopPrice'] = None - assert exchange.stoploss_adjust(1501, order, 'sell') + order["stopPrice"] = None + assert exchange.stoploss_adjust(1501, order, "sell") @pytest.mark.parametrize("side", ["buy", "sell"]) -@pytest.mark.parametrize("ordertype,rate", [ - ("market", None), - ("market", 200), - ("limit", 200), - ("stop_loss_limit", 200) -]) +@pytest.mark.parametrize( + "ordertype,rate", [("market", None), ("market", 200), ("limit", 200), ("stop_loss_limit", 200)] +) def test_kucoin_create_order(default_conf, mocker, side, ordertype, rate): api_mock = MagicMock() - order_id = f'test_prod_{side}_{randint(0, 10 ** 6)}' - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - }, - 'symbol': 'XRP/USDT', - 'amount': 1 - }) - 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') + order_id = f"test_prod_{side}_{randint(0, 10 ** 6)}" + api_mock.create_order = MagicMock( + return_value={"id": order_id, "info": {"foo": "bar"}, "symbol": "XRP/USDT", "amount": 1} + ) + 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._set_leverage = MagicMock() exchange.set_margin_mode = MagicMock() order = exchange.create_order( - pair='XRP/USDT', - ordertype=ordertype, - side=side, - amount=1, - rate=rate, - leverage=1.0 + pair="XRP/USDT", ordertype=ordertype, side=side, amount=1, rate=rate, leverage=1.0 ) - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert order['amount'] == 1 + assert "id" in order + assert "info" in order + assert order["id"] == order_id + assert order["amount"] == 1 # Status must be faked to open for kucoin. - assert order['status'] == 'open' + assert order["status"] == "open" diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 69e7e498b..305b16ea2 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -12,8 +12,8 @@ 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') - timeframes = ('1m', '5m', '1h') + exchange = get_patched_exchange(mocker, default_conf, id="okx") + timeframes = ("1m", "5m", "1h") start_time = int(datetime(2021, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) for timeframe in timeframes: @@ -26,14 +26,24 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker): assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 100 assert exchange.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 100 assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 100 - one_call = int((datetime.now(timezone.utc) - timedelta( - minutes=290 * timeframe_to_minutes(timeframe))).timestamp() * 1000) + one_call = int( + ( + datetime.now(timezone.utc) + - timedelta(minutes=290 * timeframe_to_minutes(timeframe)) + ).timestamp() + * 1000 + ) assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT, one_call) == 300 assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, one_call) == 300 - one_call = int((datetime.now(timezone.utc) - timedelta( - minutes=320 * timeframe_to_minutes(timeframe))).timestamp() * 1000) + one_call = int( + ( + datetime.now(timezone.utc) + - timedelta(minutes=320 * timeframe_to_minutes(timeframe)) + ).timestamp() + * 1000 + ) assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT, one_call) == 100 assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, one_call) == 100 @@ -43,200 +53,210 @@ def test_get_maintenance_ratio_and_amt_okx( mocker, ): api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - default_conf['dry_run'] = False + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + default_conf["dry_run"] = False mocker.patch.multiple( - 'freqtrade.exchange.okx.Okx', + "freqtrade.exchange.okx.Okx", exchange_has=MagicMock(return_value=True), - load_leverage_tiers=MagicMock(return_value={ - 'ETH/USDT:USDT': [ - { - 'tier': 1, - 'minNotional': 0, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '2000', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ETH-USDT' - } - }, - { - 'tier': 2, - 'minNotional': 2001, - 'maxNotional': 4000, - 'maintenanceMarginRate': 0.015, - 'maxLeverage': 50, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.02', - 'instId': '', - 'maxLever': '50', - 'maxSz': '4000', - 'minSz': '2001', - 'mmr': '0.015', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '2', - 'uly': 'ETH-USDT' - } - }, - { - 'tier': 3, - 'minNotional': 4001, - 'maxNotional': 8000, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 20, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.05', - 'instId': '', - 'maxLever': '20', - 'maxSz': '8000', - 'minSz': '4001', - 'mmr': '0.02', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '3', - 'uly': 'ETH-USDT' - } - }, - ], - 'ADA/USDT:USDT': [ - { - 'tier': 1, - 'minNotional': 0, - 'maxNotional': 500, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '500', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ADA-USDT' - } - }, - { - 'tier': 2, - 'minNotional': 501, - 'maxNotional': 1000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 50, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.02', - 'instId': '', - 'maxLever': '50', - 'maxSz': '1000', - 'minSz': '501', - 'mmr': '0.015', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '2', - 'uly': 'ADA-USDT' - } - }, - { - 'tier': 3, - 'minNotional': 1001, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.03, - 'maxLeverage': 20, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.05', - 'instId': '', - 'maxLever': '20', - 'maxSz': '2000', - 'minSz': '1001', - 'mmr': '0.02', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '3', - 'uly': 'ADA-USDT' - } - }, - ] - }) + load_leverage_tiers=MagicMock( + return_value={ + "ETH/USDT:USDT": [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 2000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75, + "info": { + "baseMaxLoan": "", + "imr": "0.013", + "instId": "", + "maxLever": "75", + "maxSz": "2000", + "minSz": "0", + "mmr": "0.01", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "1", + "uly": "ETH-USDT", + }, + }, + { + "tier": 2, + "minNotional": 2001, + "maxNotional": 4000, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50, + "info": { + "baseMaxLoan": "", + "imr": "0.02", + "instId": "", + "maxLever": "50", + "maxSz": "4000", + "minSz": "2001", + "mmr": "0.015", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "2", + "uly": "ETH-USDT", + }, + }, + { + "tier": 3, + "minNotional": 4001, + "maxNotional": 8000, + "maintenanceMarginRate": 0.02, + "maxLeverage": 20, + "info": { + "baseMaxLoan": "", + "imr": "0.05", + "instId": "", + "maxLever": "20", + "maxSz": "8000", + "minSz": "4001", + "mmr": "0.02", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "3", + "uly": "ETH-USDT", + }, + }, + ], + "ADA/USDT:USDT": [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 500, + "maintenanceMarginRate": 0.02, + "maxLeverage": 75, + "info": { + "baseMaxLoan": "", + "imr": "0.013", + "instId": "", + "maxLever": "75", + "maxSz": "500", + "minSz": "0", + "mmr": "0.01", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "1", + "uly": "ADA-USDT", + }, + }, + { + "tier": 2, + "minNotional": 501, + "maxNotional": 1000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 50, + "info": { + "baseMaxLoan": "", + "imr": "0.02", + "instId": "", + "maxLever": "50", + "maxSz": "1000", + "minSz": "501", + "mmr": "0.015", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "2", + "uly": "ADA-USDT", + }, + }, + { + "tier": 3, + "minNotional": 1001, + "maxNotional": 2000, + "maintenanceMarginRate": 0.03, + "maxLeverage": 20, + "info": { + "baseMaxLoan": "", + "imr": "0.05", + "instId": "", + "maxLever": "20", + "maxSz": "2000", + "minSz": "1001", + "mmr": "0.02", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "3", + "uly": "ADA-USDT", + }, + }, + ], + } + ), ) exchange = get_patched_exchange(mocker, default_conf, api_mock, id="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) - assert exchange.get_maintenance_ratio_and_amt('ETH/USDT:USDT', 8000) == (0.02, None) + 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) + assert exchange.get_maintenance_ratio_and_amt("ETH/USDT:USDT", 8000) == (0.02, None) - assert exchange.get_maintenance_ratio_and_amt('ADA/USDT:USDT', 1) == (0.02, None) - assert exchange.get_maintenance_ratio_and_amt('ADA/USDT:USDT', 2000) == (0.03, None) + assert exchange.get_maintenance_ratio_and_amt("ADA/USDT:USDT", 1) == (0.02, None) + assert exchange.get_maintenance_ratio_and_amt("ADA/USDT:USDT", 2000) == (0.03, None) def test_get_max_pair_stake_amount_okx(default_conf, mocker, leverage_tiers): - exchange = get_patched_exchange(mocker, default_conf, id="okx") - assert exchange.get_max_pair_stake_amount('BNB/BUSD', 1.0) == float('inf') + assert exchange.get_max_pair_stake_amount("BNB/BUSD", 1.0) == float("inf") - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" exchange = get_patched_exchange(mocker, default_conf, id="okx") exchange._leverage_tiers = leverage_tiers - assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 1.0) == 30000000 - assert exchange.get_max_pair_stake_amount('BNB/USDT:USDT', 1.0) == 50000000 - assert exchange.get_max_pair_stake_amount('BTC/USDT:USDT', 1.0) == 1000000000 - assert exchange.get_max_pair_stake_amount('BTC/USDT:USDT', 1.0, 10.0) == 100000000 + assert exchange.get_max_pair_stake_amount("XRP/USDT:USDT", 1.0) == 30000000 + assert exchange.get_max_pair_stake_amount("BNB/USDT:USDT", 1.0) == 50000000 + assert exchange.get_max_pair_stake_amount("BTC/USDT:USDT", 1.0) == 1000000000 + assert exchange.get_max_pair_stake_amount("BTC/USDT:USDT", 1.0, 10.0) == 100000000 - assert exchange.get_max_pair_stake_amount('TTT/USDT:USDT', 1.0) == float('inf') # Not in tiers + assert exchange.get_max_pair_stake_amount("TTT/USDT:USDT", 1.0) == float("inf") # Not in tiers -@pytest.mark.parametrize('mode,side,reduceonly,result', [ - ('net', 'buy', False, 'net'), - ('net', 'sell', True, 'net'), - ('net', 'sell', False, 'net'), - ('net', 'buy', True, 'net'), - ('longshort', 'buy', False, 'long'), - ('longshort', 'sell', True, 'long'), - ('longshort', 'sell', False, 'short'), - ('longshort', 'buy', True, 'short'), -]) +@pytest.mark.parametrize( + "mode,side,reduceonly,result", + [ + ("net", "buy", False, "net"), + ("net", "sell", True, "net"), + ("net", "sell", False, "net"), + ("net", "buy", True, "net"), + ("longshort", "buy", False, "long"), + ("longshort", "sell", True, "long"), + ("longshort", "sell", False, "short"), + ("longshort", "buy", True, "short"), + ], +) def test__get_posSide(default_conf, mocker, mode, side, reduceonly, result): - exchange = get_patched_exchange(mocker, default_conf, id="okx") - exchange.net_only = mode == 'net' + exchange.net_only = mode == "net" assert exchange._get_posSide(side, reduceonly) == result def test_additional_exchange_init_okx(default_conf, mocker): api_mock = MagicMock() - api_mock.fetch_accounts = MagicMock(return_value=[ - {'id': '2555', - 'type': '2', - 'currency': None, - 'info': {'acctLv': '2', - 'autoLoan': False, - 'ctIsoMode': 'automatic', - 'greeksType': 'PA', - 'level': 'Lv1', - 'levelTmp': '', - 'mgnIsoMode': 'automatic', - 'posMode': 'long_short_mode', - 'uid': '2555'}}]) - default_conf['dry_run'] = False + api_mock.fetch_accounts = MagicMock( + return_value=[ + { + "id": "2555", + "type": "2", + "currency": None, + "info": { + "acctLv": "2", + "autoLoan": False, + "ctIsoMode": "automatic", + "greeksType": "PA", + "level": "Lv1", + "levelTmp": "", + "mgnIsoMode": "automatic", + "posMode": "long_short_mode", + "uid": "2555", + }, + } + ] + ) + default_conf["dry_run"] = False exchange = get_patched_exchange(mocker, default_conf, id="okx", api_mock=api_mock) assert api_mock.fetch_accounts.call_count == 0 exchange.trading_mode = TradingMode.FUTURES @@ -246,225 +266,237 @@ def test_additional_exchange_init_okx(default_conf, mocker): assert api_mock.fetch_accounts.call_count == 1 assert not exchange.net_only - api_mock.fetch_accounts = MagicMock(return_value=[ - {'id': '2555', - 'type': '2', - 'currency': None, - 'info': {'acctLv': '2', - 'autoLoan': False, - 'ctIsoMode': 'automatic', - 'greeksType': 'PA', - 'level': 'Lv1', - 'levelTmp': '', - 'mgnIsoMode': 'automatic', - 'posMode': 'net_mode', - 'uid': '2555'}}]) + api_mock.fetch_accounts = MagicMock( + return_value=[ + { + "id": "2555", + "type": "2", + "currency": None, + "info": { + "acctLv": "2", + "autoLoan": False, + "ctIsoMode": "automatic", + "greeksType": "PA", + "level": "Lv1", + "levelTmp": "", + "mgnIsoMode": "automatic", + "posMode": "net_mode", + "uid": "2555", + }, + } + ] + ) exchange.additional_exchange_init() assert api_mock.fetch_accounts.call_count == 1 assert exchange.net_only - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'okx', - "additional_exchange_init", "fetch_accounts") + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + ccxt_exceptionhandlers( + mocker, default_conf, api_mock, "okx", "additional_exchange_init", "fetch_accounts" + ) def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmp_path, caplog, time_machine): - - default_conf['datadir'] = tmp_path + default_conf["datadir"] = tmp_path # fd_mock = mocker.patch('freqtrade.exchange.exchange.file_dump_json') api_mock = MagicMock() - type(api_mock).has = PropertyMock(return_value={ - 'fetchLeverageTiers': False, - 'fetchMarketLeverageTiers': True, - }) - api_mock.fetch_market_leverage_tiers = AsyncMock(side_effect=[ - [ - { - 'tier': 1, - 'minNotional': 0, - 'maxNotional': 500, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '500', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ADA-USDT' - } - }, - { - 'tier': 2, - 'minNotional': 501, - 'maxNotional': 1000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 50, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.02', - 'instId': '', - 'maxLever': '50', - 'maxSz': '1000', - 'minSz': '501', - 'mmr': '0.015', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '2', - 'uly': 'ADA-USDT' - } - }, - { - 'tier': 3, - 'minNotional': 1001, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.03, - 'maxLeverage': 20, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.05', - 'instId': '', - 'maxLever': '20', - 'maxSz': '2000', - 'minSz': '1001', - 'mmr': '0.02', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '3', - 'uly': 'ADA-USDT' - } - }, - ], - TemporaryError("this Failed"), - [ - { - 'tier': 1, - 'minNotional': 0, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '2000', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ETH-USDT' - } - }, - { - 'tier': 2, - 'minNotional': 2001, - 'maxNotional': 4000, - 'maintenanceMarginRate': 0.015, - 'maxLeverage': 50, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.02', - 'instId': '', - 'maxLever': '50', - 'maxSz': '4000', - 'minSz': '2001', - 'mmr': '0.015', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '2', - 'uly': 'ETH-USDT' - } - }, - { - 'tier': 3, - 'minNotional': 4001, - 'maxNotional': 8000, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 20, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.05', - 'instId': '', - 'maxLever': '20', - 'maxSz': '8000', - 'minSz': '4001', - 'mmr': '0.02', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '3', - 'uly': 'ETH-USDT' - } - }, + type(api_mock).has = PropertyMock( + return_value={ + "fetchLeverageTiers": False, + "fetchMarketLeverageTiers": True, + } + ) + api_mock.fetch_market_leverage_tiers = AsyncMock( + side_effect=[ + [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 500, + "maintenanceMarginRate": 0.02, + "maxLeverage": 75, + "info": { + "baseMaxLoan": "", + "imr": "0.013", + "instId": "", + "maxLever": "75", + "maxSz": "500", + "minSz": "0", + "mmr": "0.01", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "1", + "uly": "ADA-USDT", + }, + }, + { + "tier": 2, + "minNotional": 501, + "maxNotional": 1000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 50, + "info": { + "baseMaxLoan": "", + "imr": "0.02", + "instId": "", + "maxLever": "50", + "maxSz": "1000", + "minSz": "501", + "mmr": "0.015", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "2", + "uly": "ADA-USDT", + }, + }, + { + "tier": 3, + "minNotional": 1001, + "maxNotional": 2000, + "maintenanceMarginRate": 0.03, + "maxLeverage": 20, + "info": { + "baseMaxLoan": "", + "imr": "0.05", + "instId": "", + "maxLever": "20", + "maxSz": "2000", + "minSz": "1001", + "mmr": "0.02", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "3", + "uly": "ADA-USDT", + }, + }, + ], + TemporaryError("this Failed"), + [ + { + "tier": 1, + "minNotional": 0, + "maxNotional": 2000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75, + "info": { + "baseMaxLoan": "", + "imr": "0.013", + "instId": "", + "maxLever": "75", + "maxSz": "2000", + "minSz": "0", + "mmr": "0.01", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "1", + "uly": "ETH-USDT", + }, + }, + { + "tier": 2, + "minNotional": 2001, + "maxNotional": 4000, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50, + "info": { + "baseMaxLoan": "", + "imr": "0.02", + "instId": "", + "maxLever": "50", + "maxSz": "4000", + "minSz": "2001", + "mmr": "0.015", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "2", + "uly": "ETH-USDT", + }, + }, + { + "tier": 3, + "minNotional": 4001, + "maxNotional": 8000, + "maintenanceMarginRate": 0.02, + "maxLeverage": 20, + "info": { + "baseMaxLoan": "", + "imr": "0.05", + "instId": "", + "maxLever": "20", + "maxSz": "8000", + "minSz": "4001", + "mmr": "0.02", + "optMgnFactor": "0", + "quoteMaxLoan": "", + "tier": "3", + "uly": "ETH-USDT", + }, + }, + ], ] - ]) - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - default_conf['stake_currency'] = 'USDT' + ) + 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.trading_mode = TradingMode.FUTURES exchange.margin_mode = MarginMode.ISOLATED exchange.markets = markets # Initialization of load_leverage_tiers happens as part of exchange init. assert exchange._leverage_tiers == { - 'ADA/USDT:USDT': [ + "ADA/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 500, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 75, - 'maintAmt': None + "minNotional": 0, + "maxNotional": 500, + "maintenanceMarginRate": 0.02, + "maxLeverage": 75, + "maintAmt": None, }, { - 'minNotional': 501, - 'maxNotional': 1000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 50, - 'maintAmt': None + "minNotional": 501, + "maxNotional": 1000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 50, + "maintAmt": None, }, { - 'minNotional': 1001, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.03, - 'maxLeverage': 20, - 'maintAmt': None + "minNotional": 1001, + "maxNotional": 2000, + "maintenanceMarginRate": 0.03, + "maxLeverage": 20, + "maintAmt": None, }, ], - 'ETH/USDT:USDT': [ + "ETH/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 2000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 75, - 'maintAmt': None + "minNotional": 0, + "maxNotional": 2000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75, + "maintAmt": None, }, { - 'minNotional': 2001, - 'maxNotional': 4000, - 'maintenanceMarginRate': 0.015, - 'maxLeverage': 50, - 'maintAmt': None + "minNotional": 2001, + "maxNotional": 4000, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50, + "maintAmt": None, }, { - 'minNotional': 4001, - 'maxNotional': 8000, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 20, - 'maintAmt': None + "minNotional": 4001, + "maxNotional": 8000, + "maintenanceMarginRate": 0.02, + "maxLeverage": 20, + "maintAmt": None, }, ], } - filename = (default_conf['datadir'] / - f"futures/leverage_tiers_{default_conf['stake_currency']}.json") + filename = ( + default_conf["datadir"] / f"futures/leverage_tiers_{default_conf['stake_currency']}.json" + ) assert filename.is_file() - logmsg = 'Cached leverage tiers are outdated. Will update.' + logmsg = "Cached leverage tiers are outdated. Will update." assert not log_has(logmsg, caplog) api_mock.fetch_market_leverage_tiers.reset_mock() @@ -481,25 +513,25 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmp_path, caplog def test__set_leverage_okx(mocker, default_conf): - api_mock = MagicMock() api_mock.set_leverage = MagicMock() - type(api_mock).has = PropertyMock(return_value={'setLeverage': True}) - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED + type(api_mock).has = PropertyMock(return_value={"setLeverage": True}) + 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="okx") - exchange._lev_prep('BTC/USDT:USDT', 3.2, 'buy') + exchange._lev_prep("BTC/USDT:USDT", 3.2, "buy") assert api_mock.set_leverage.call_count == 1 # Leverage is rounded to 3. - assert api_mock.set_leverage.call_args_list[0][1]['leverage'] == 3.2 - assert api_mock.set_leverage.call_args_list[0][1]['symbol'] == 'BTC/USDT:USDT' - assert api_mock.set_leverage.call_args_list[0][1]['params'] == { - 'mgnMode': 'isolated', - 'posSide': 'net'} + assert api_mock.set_leverage.call_args_list[0][1]["leverage"] == 3.2 + assert api_mock.set_leverage.call_args_list[0][1]["symbol"] == "BTC/USDT:USDT" + assert api_mock.set_leverage.call_args_list[0][1]["params"] == { + "mgnMode": "isolated", + "posSide": "net", + } api_mock.set_leverage = MagicMock(side_effect=ccxt.NetworkError()) - exchange._lev_prep('BTC/USDT:USDT', 3.2, 'buy') + exchange._lev_prep("BTC/USDT:USDT", 3.2, "buy") assert api_mock.fetch_leverage.call_count == 1 api_mock.fetch_leverage = MagicMock(side_effect=ccxt.NetworkError()) @@ -512,23 +544,23 @@ def test__set_leverage_okx(mocker, default_conf): "set_leverage", pair="XRP/USDT:USDT", leverage=5.0, - side='buy' + side="buy", ) @pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_okx(default_conf, mocker): - default_conf['dry_run'] = False + default_conf["dry_run"] = False 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, id="okx") - exchange.fetch_stoploss_order('1234', 'ETH/BTC') + exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_order.call_count == 1 - assert api_mock.fetch_order.call_args_list[0][0][0] == '1234' - assert api_mock.fetch_order.call_args_list[0][0][1] == 'ETH/BTC' - assert api_mock.fetch_order.call_args_list[0][1]['params'] == {'stop': True} + assert api_mock.fetch_order.call_args_list[0][0][0] == "1234" + assert api_mock.fetch_order.call_args_list[0][0][1] == "ETH/BTC" + assert api_mock.fetch_order.call_args_list[0][1]["params"] == {"stop": True} api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound) api_mock.fetch_open_orders = MagicMock(return_value=[]) @@ -536,7 +568,7 @@ def test_fetch_stoploss_order_okx(default_conf, mocker): api_mock.fetch_canceled_orders = MagicMock(creturn_value=[]) with pytest.raises(RetryableOrderError): - exchange.fetch_stoploss_order('1234', 'ETH/BTC') + exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_order.call_count == 1 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 1 @@ -547,33 +579,29 @@ def test_fetch_stoploss_order_okx(default_conf, mocker): api_mock.fetch_closed_orders.reset_mock() api_mock.fetch_canceled_orders.reset_mock() - api_mock.fetch_closed_orders = MagicMock(return_value=[ - { - 'id': '1234', - 'status': 'closed', - 'info': {'ordId': '123455'} - } - ]) - mocker.patch(f"{EXMS}.fetch_order", MagicMock(return_value={'id': '123455'})) - resp = exchange.fetch_stoploss_order('1234', 'ETH/BTC') + api_mock.fetch_closed_orders = MagicMock( + return_value=[{"id": "1234", "status": "closed", "info": {"ordId": "123455"}}] + ) + mocker.patch(f"{EXMS}.fetch_order", MagicMock(return_value={"id": "123455"})) + resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_order.call_count == 1 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 1 assert api_mock.fetch_canceled_orders.call_count == 0 - assert resp['id'] == '1234' - assert resp['id_stop'] == '123455' - assert resp['type'] == 'stoploss' + assert resp["id"] == "1234" + assert resp["id_stop"] == "123455" + assert resp["type"] == "stoploss" - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf, api_mock, id='okx') - dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={'id': '123455'})) + default_conf["dry_run"] = True + exchange = get_patched_exchange(mocker, default_conf, api_mock, id="okx") + dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={"id": "123455"})) api_mock.fetch_order.reset_mock() api_mock.fetch_open_orders.reset_mock() api_mock.fetch_closed_orders.reset_mock() api_mock.fetch_canceled_orders.reset_mock() - resp = exchange.fetch_stoploss_order('1234', 'ETH/BTC') + resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_order.call_count == 0 assert api_mock.fetch_open_orders.call_count == 0 @@ -582,105 +610,105 @@ def test_fetch_stoploss_order_okx(default_conf, mocker): assert dro_mock.call_count == 1 -@pytest.mark.parametrize('sl1,sl2,sl3,side', [ - (1501, 1499, 1501, "sell"), - (1499, 1501, 1499, "buy") -]) +@pytest.mark.parametrize( + "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, id="okx") order = { - 'type': 'stoploss', - 'price': 1500, - 'stopLossPrice': 1500, + "type": "stoploss", + "price": 1500, + "stopLossPrice": 1500, } assert exchange.stoploss_adjust(sl1, order, side=side) assert not exchange.stoploss_adjust(sl2, order, side=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, id="okx") exchange.cancel_order = MagicMock() - exchange.cancel_stoploss_order('1234', 'ETH/USDT') + exchange.cancel_stoploss_order("1234", "ETH/USDT") assert exchange.cancel_order.call_count == 1 - assert exchange.cancel_order.call_args_list[0][1]['order_id'] == '1234' - assert exchange.cancel_order.call_args_list[0][1]['pair'] == 'ETH/USDT' - assert exchange.cancel_order.call_args_list[0][1]['params'] == {'stop': True} + assert exchange.cancel_order.call_args_list[0][1]["order_id"] == "1234" + assert exchange.cancel_order.call_args_list[0][1]["pair"] == "ETH/USDT" + assert exchange.cancel_order.call_args_list[0][1]["params"] == {"stop": True} 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') - params = exchange._get_stop_params('ETH/USDT:USDT', 1500, 'sell') + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + exchange = get_patched_exchange(mocker, default_conf, id="okx") + params = exchange._get_stop_params("ETH/USDT:USDT", 1500, "sell") - assert params['tdMode'] == 'isolated' - assert params['posSide'] == 'net' + assert params["tdMode"] == "isolated" + assert params["posSide"] == "net" def test_fetch_orders_okx(default_conf, mocker, limit_order): - api_mock = MagicMock() - api_mock.fetch_orders = MagicMock(return_value=[ - limit_order['buy'], - limit_order['sell'], - ]) - api_mock.fetch_open_orders = MagicMock(return_value=[limit_order['buy']]) - api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order['buy']]) + api_mock.fetch_orders = MagicMock( + return_value=[ + limit_order["buy"], + limit_order["sell"], + ] + ) + api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]]) + api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]]) - mocker.patch(f'{EXMS}.exchange_has', return_value=True) + 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, id="okx") # Not available in dry-run - assert exchange.fetch_orders('mocked', start_time) == [] + assert exchange.fetch_orders("mocked", start_time) == [] assert api_mock.fetch_orders.call_count == 0 - default_conf['dry_run'] = False + 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, id="okx") def has_resp(_, endpoint): - if endpoint == 'fetchOrders': + if endpoint == "fetchOrders": return False - if endpoint == 'fetchClosedOrders': + if endpoint == "fetchClosedOrders": return True - if endpoint == 'fetchOpenOrders': + if endpoint == "fetchOpenOrders": return True - mocker.patch(f'{EXMS}.exchange_has', has_resp) + mocker.patch(f"{EXMS}.exchange_has", has_resp) - history_params = {'method': 'privateGetTradeOrdersHistoryArchive'} + history_params = {"method": "privateGetTradeOrdersHistoryArchive"} # happy path without fetchOrders - exchange.fetch_orders('mocked', start_time) + exchange.fetch_orders("mocked", start_time) assert api_mock.fetch_orders.call_count == 0 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 2 - assert 'params' not in api_mock.fetch_closed_orders.call_args_list[0][1] - assert api_mock.fetch_closed_orders.call_args_list[1][1]['params'] == history_params + assert "params" not in api_mock.fetch_closed_orders.call_args_list[0][1] + assert api_mock.fetch_closed_orders.call_args_list[1][1]["params"] == history_params api_mock.fetch_open_orders.reset_mock() api_mock.fetch_closed_orders.reset_mock() # regular closed_orders endpoint only has history for 7 days. - exchange.fetch_orders('mocked', datetime.now(timezone.utc) - timedelta(days=6)) + exchange.fetch_orders("mocked", datetime.now(timezone.utc) - timedelta(days=6)) assert api_mock.fetch_orders.call_count == 0 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 1 - assert 'params' not in api_mock.fetch_closed_orders.call_args_list[0][1] + assert "params" not in api_mock.fetch_closed_orders.call_args_list[0][1] - mocker.patch(f'{EXMS}.exchange_has', return_value=True) + mocker.patch(f"{EXMS}.exchange_has", return_value=True) # Unhappy path - first fetch-orders call fails. api_mock.fetch_orders = MagicMock(side_effect=ccxt.NotSupported()) api_mock.fetch_open_orders.reset_mock() api_mock.fetch_closed_orders.reset_mock() - exchange.fetch_orders('mocked', start_time) + exchange.fetch_orders("mocked", start_time) assert api_mock.fetch_orders.call_count == 1 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 2 - assert 'params' not in api_mock.fetch_closed_orders.call_args_list[0][1] - assert api_mock.fetch_closed_orders.call_args_list[1][1]['params'] == history_params + assert "params" not in api_mock.fetch_closed_orders.call_args_list[0][1] + assert api_mock.fetch_closed_orders.call_args_list[1][1]["params"] == history_params