freqtrade_origin/tests/exchange/test_bybit.py

180 lines
6.7 KiB
Python
Raw Normal View History

from datetime import datetime, timedelta, timezone
2022-12-30 06:47:00 +00:00
from unittest.mock import MagicMock
2024-02-18 10:44:54 +00:00
import pytest
2023-01-26 18:58:42 +00:00
from freqtrade.enums.marginmode import MarginMode
from freqtrade.enums.tradingmode import TradingMode
2024-02-18 10:44:54 +00:00
from freqtrade.exceptions import OperationalException
2024-02-18 10:40:50 +00:00
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has
2023-01-26 18:58:42 +00:00
from tests.exchange.test_exchange import ccxt_exceptionhandlers
2024-02-18 10:40:50 +00:00
def test_additional_exchange_init_bybit(default_conf, mocker, caplog):
2024-05-12 13:50:21 +00:00
default_conf["dry_run"] = False
default_conf["trading_mode"] = TradingMode.FUTURES
default_conf["margin_mode"] = MarginMode.ISOLATED
2023-01-26 18:58:42 +00:00
api_mock = MagicMock()
api_mock.set_position_mode = MagicMock(return_value={"dualSidePosition": False})
2024-02-18 10:40:50 +00:00
api_mock.is_unified_enabled = MagicMock(return_value=[False, False])
exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
2023-01-26 18:58:42 +00:00
assert api_mock.set_position_mode.call_count == 1
2024-02-18 10:40:50 +00:00
assert api_mock.is_unified_enabled.call_count == 1
assert exchange.unified_account is False
assert log_has("Bybit: Standard account.", caplog)
api_mock.set_position_mode.reset_mock()
api_mock.is_unified_enabled = MagicMock(return_value=[False, True])
2024-02-18 10:44:54 +00:00
with pytest.raises(OperationalException, match=r"Bybit: Unified account is not supported.*"):
get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
2024-02-18 10:40:50 +00:00
assert log_has("Bybit: Unified account.", caplog)
2024-02-18 10:44:54 +00:00
# exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
# assert api_mock.set_position_mode.call_count == 1
# assert api_mock.is_unified_enabled.call_count == 1
# assert exchange.unified_account is True
2024-05-12 13:50:21 +00:00
ccxt_exceptionhandlers(
mocker, default_conf, api_mock, "bybit", "additional_exchange_init", "set_position_mode"
)
2022-12-30 06:47:00 +00:00
async def test_bybit_fetch_funding_rate(default_conf, mocker):
2024-05-12 13:50:21 +00:00
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
2022-12-30 06:47:00 +00:00
api_mock = MagicMock()
api_mock.fetch_funding_rate_history = get_mock_coro(return_value=[])
2024-05-12 13:50:21 +00:00
exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
limit = 200
2022-12-30 06:47:00 +00:00
# Test fetch_funding_rate_history (current data)
await exchange._fetch_funding_rate_history(
2024-05-12 13:50:21 +00:00
pair="BTC/USDT:USDT",
timeframe="4h",
2022-12-30 06:47:00 +00:00
limit=limit,
2024-05-12 13:50:21 +00:00
)
2022-12-30 06:47:00 +00:00
assert api_mock.fetch_funding_rate_history.call_count == 1
2024-05-12 13:50:21 +00:00
assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == "BTC/USDT:USDT"
2022-12-30 06:47:00 +00:00
kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1]
2024-05-12 13:50:21 +00:00
assert kwargs["since"] is None
2022-12-30 06:47:00 +00:00
api_mock.fetch_funding_rate_history.reset_mock()
since_ms = 1610000000000
# Test fetch_funding_rate_history (current data)
await exchange._fetch_funding_rate_history(
2024-05-12 13:50:21 +00:00
pair="BTC/USDT:USDT",
timeframe="4h",
2022-12-30 06:47:00 +00:00
limit=limit,
since_ms=since_ms,
2024-05-12 13:50:21 +00:00
)
2022-12-30 06:47:00 +00:00
assert api_mock.fetch_funding_rate_history.call_count == 1
2024-05-12 13:50:21 +00:00
assert api_mock.fetch_funding_rate_history.call_args_list[0][0][0] == "BTC/USDT:USDT"
2022-12-30 06:47:00 +00:00
kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1]
2024-05-12 13:50:21 +00:00
assert kwargs["since"] == since_ms
2023-02-26 14:41:42 +00:00
def test_bybit_get_funding_fees(default_conf, mocker):
now = datetime.now(timezone.utc)
2024-05-12 13:50:21 +00:00
exchange = get_patched_exchange(mocker, default_conf, id="bybit")
2023-02-26 14:41:42 +00:00
exchange._fetch_and_calculate_funding_fees = MagicMock()
2024-05-12 13:50:21 +00:00
exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now)
2023-02-26 14:41:42 +00:00
assert exchange._fetch_and_calculate_funding_fees.call_count == 0
2024-05-12 13:50:21 +00:00
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf, id="bybit")
2023-02-26 14:41:42 +00:00
exchange._fetch_and_calculate_funding_fees = MagicMock()
2024-05-12 13:50:21 +00:00
exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now)
2023-02-26 14:41:42 +00:00
assert exchange._fetch_and_calculate_funding_fees.call_count == 1
def test_bybit_fetch_orders(default_conf, mocker, limit_order):
api_mock = MagicMock()
2024-05-12 13:50:21 +00:00
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)
start_time = datetime.now(timezone.utc) - timedelta(days=20)
2024-05-12 13:50:21 +00:00
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="bybit")
# Not available in dry-run
2024-05-12 13:50:21 +00:00
assert exchange.fetch_orders("mocked", start_time) == []
assert api_mock.fetch_orders.call_count == 0
2024-05-12 13:50:21 +00:00
default_conf["dry_run"] = False
2024-05-12 13:50:21 +00:00
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
assert api_mock.fetch_closed_orders.call_count == 0
assert len(res) == 2 * 3
def test_bybit_fetch_order_canceled_empty(default_conf_usdt, mocker):
2024-05-12 13:50:21 +00:00
default_conf_usdt["dry_run"] = False
api_mock = MagicMock()
2024-05-12 13:50:21 +00:00
api_mock.fetch_order = MagicMock(
return_value={
"id": "123",
"symbol": "BTC/USDT",
"status": "canceled",
"filled": 0.0,
"remaining": 0.0,
"amount": 20.0,
}
)
2024-02-18 14:39:13 +00:00
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
2024-05-12 13:50:21 +00:00
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"
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.
2024-05-12 13:50:21 +00:00
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
2024-05-12 13:50:21 +00:00
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"