Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions freqtrade/exchange/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -1896,9 +1896,12 @@ def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]:
orders = []
if self.exchange_has("fetchClosedOrders"):
orders = self._api.fetch_closed_orders(pair, since=since_ms)
if self.exchange_has("fetchOpenOrders"):
orders_open = self._api.fetch_open_orders(pair, since=since_ms)
orders.extend(orders_open)
if self.exchange_has("fetchCanceledOrders"):
orders_canceled = self._api.fetch_canceled_orders(pair, since=since_ms)
orders.extend(orders_canceled)
if self.exchange_has("fetchOpenOrders"):
orders_open = self._api.fetch_open_orders(pair, since=since_ms)
orders.extend(orders_open)
return orders

@retrier(retries=0)
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ def handle_onexchange_order(self, trade: Trade) -> bool:
if trade.base_currency
else 0
)
if total < trade.amount:
if total < trade.amount or (total == 0 and trade.amount == 0):
if trade.fully_canceled_entry_order_count == len(trade.orders):
logger.warning(
f"Trade only had fully canceled entry orders. "
Expand Down
69 changes: 69 additions & 0 deletions tests/exchange/test_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,7 @@ def return_value(*args, **kwargs):

api_mock.fetch_orders = MagicMock(side_effect=return_value)
api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]])
api_mock.fetch_canceled_orders = MagicMock(return_value=[])
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])

mocker.patch(f"{EXMS}.exchange_has", return_value=True)
Expand Down Expand Up @@ -2000,6 +2001,8 @@ def has_resp(_, endpoint):
return True
if endpoint == "fetchOpenOrders":
return True
if endpoint == "fetchCanceledOrders":
return True

if exchange_name == "okx":
# Special OKX case is tested separately
Expand All @@ -2012,6 +2015,7 @@ def has_resp(_, endpoint):
assert api_mock.fetch_orders.call_count == 0
assert api_mock.fetch_open_orders.call_count == expected
assert api_mock.fetch_closed_orders.call_count == expected
assert api_mock.fetch_canceled_orders.call_count == expected

mocker.patch(f"{EXMS}.exchange_has", return_value=True)

Expand All @@ -2031,12 +2035,77 @@ def has_resp(_, endpoint):
api_mock.fetch_orders = MagicMock(side_effect=ccxt.NotSupported())
api_mock.fetch_open_orders.reset_mock()
api_mock.fetch_closed_orders.reset_mock()
api_mock.fetch_canceled_orders.reset_mock()

exchange.fetch_orders("mocked", start_time)

assert api_mock.fetch_orders.call_count == expected
assert api_mock.fetch_open_orders.call_count == expected
assert api_mock.fetch_closed_orders.call_count == expected
assert api_mock.fetch_canceled_orders.call_count == expected


@pytest.mark.parametrize("exchange_name", [ex for ex in EXCHANGES if ex != "bybit"])
@pytest.mark.parametrize(
"call_config, expected",
[
# call_config: (fetch_orders, fetch_open, fetch_closed, fetch_canceled)
# expected: (fetch_orders_calls, fetch_open_calls, fetch_closed_calls, fetch_canceled_calls)
((True, False, False, False), (1, 0, 0, 0)),
((False, True, True, False), (0, 1, 1, 0)),
((False, True, False, True), (0, 1, 0, 1)),
((False, True, True, True), (0, 1, 1, 1)),
],
)
def test_fetch_orders_multi(
default_conf, mocker, exchange_name, limit_order, call_config, expected
):
default_conf["dry_run"] = False
api_mock = MagicMock()
call_count = 1

def return_value(*args, **kwargs):
nonlocal call_count
call_count += 2
return [
{**limit_order["buy"], "id": call_count},
{**limit_order["sell"], "id": call_count + 1},
]

api_mock.fetch_orders = MagicMock(side_effect=return_value)
api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]])
api_mock.fetch_canceled_orders = MagicMock(return_value=[limit_order["sell"]])
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])

mocker.patch(f"{EXMS}.exchange_has", return_value=True)
start_time = datetime.now(UTC) - timedelta(days=20)

exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name)

def has_resp(_, endpoint):

if endpoint == "fetchOrders":
return call_config[0]
if endpoint == "fetchClosedOrders":
return call_config[2]
if endpoint == "fetchOpenOrders":
return call_config[1]
if endpoint == "fetchCanceledOrders":
return call_config[3]

if exchange_name == "okx":
# Special OKX case is tested separately
return

mocker.patch(f"{EXMS}.exchange_has", has_resp)

#
resp = exchange.fetch_orders("mocked", start_time)
assert api_mock.fetch_orders.call_count == expected[0]
assert api_mock.fetch_open_orders.call_count == expected[1]
assert api_mock.fetch_closed_orders.call_count == expected[2]
assert api_mock.fetch_canceled_orders.call_count == expected[3]
assert len(resp) == 2 * expected[0] + expected[1] + expected[2] + expected[3]


def test_fetch_trading_fees(default_conf, mocker):
Expand Down
Loading