Skip to content

Commit f3d03dd

Browse files
committed
feat: ensure uniqueness among fetched orders
closes freqtrade#11786
1 parent ecdd84e commit f3d03dd

File tree

3 files changed

+35
-11
lines changed

3 files changed

+35
-11
lines changed

freqtrade/exchange/exchange.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,8 @@ def fetch_orders(
17931793
orders += self._fetch_orders(pair, since)
17941794
# Since with 1 minute overlap
17951795
since = since + timedelta(minutes=limit - 1)
1796+
# Ensure each order is unique based on order id
1797+
orders = list({order["id"]: order for order in orders}.values())
17961798
return orders
17971799

17981800
else:

tests/exchange/test_bybit.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,27 @@ def test_bybit_fetch_orders(default_conf, mocker, limit_order):
9696
limit_order["sell"],
9797
]
9898
)
99-
api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]])
100-
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])
99+
api_mock.fetch_open_orders = MagicMock(
100+
side_effect=[
101+
[{**limit_order["buy"], "id": 1}],
102+
[{**limit_order["buy"], "id": 2}],
103+
[{**limit_order["buy"], "id": 3}],
104+
]
105+
)
106+
api_mock.fetch_closed_orders = MagicMock(
107+
side_effect=[
108+
[{**limit_order["buy"], "id": 5}],
109+
[{**limit_order["buy"], "id": 6}],
110+
[{**limit_order["buy"], "id": 7}],
111+
]
112+
)
101113

102-
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
114+
def exchange_has(value):
115+
if value == "fetchOrders":
116+
return False
117+
return True
118+
119+
mocker.patch(f"{EXMS}.exchange_has", side_effect=exchange_has)
103120
start_time = datetime.now(timezone.utc) - timedelta(days=20)
104121

105122
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit")
@@ -111,9 +128,9 @@ def test_bybit_fetch_orders(default_conf, mocker, limit_order):
111128
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit")
112129
res = exchange.fetch_orders("mocked", start_time)
113130
# Bybit will call the endpoint 3 times, as it has a limit of 7 days per call
114-
assert api_mock.fetch_orders.call_count == 3
115-
assert api_mock.fetch_open_orders.call_count == 0
116-
assert api_mock.fetch_closed_orders.call_count == 0
131+
assert api_mock.fetch_orders.call_count == 0
132+
assert api_mock.fetch_open_orders.call_count == 3
133+
assert api_mock.fetch_closed_orders.call_count == 3
117134
assert len(res) == 2 * 3
118135

119136

tests/exchange/test_exchange.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,12 +1726,17 @@ def test_fetch_positions(default_conf, mocker, exchange_name):
17261726
@pytest.mark.parametrize("exchange_name", EXCHANGES)
17271727
def test_fetch_orders(default_conf, mocker, exchange_name, limit_order):
17281728
api_mock = MagicMock()
1729-
api_mock.fetch_orders = MagicMock(
1730-
return_value=[
1731-
limit_order["buy"],
1732-
limit_order["sell"],
1729+
call_count = 1
1730+
1731+
def return_value(*args, **kwargs):
1732+
nonlocal call_count
1733+
call_count += 2
1734+
return [
1735+
{**limit_order["buy"], "id": call_count},
1736+
{**limit_order["sell"], "id": call_count + 1},
17331737
]
1734-
)
1738+
1739+
api_mock.fetch_orders = MagicMock(side_effect=return_value)
17351740
api_mock.fetch_open_orders = MagicMock(return_value=[limit_order["buy"]])
17361741
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])
17371742

0 commit comments

Comments
 (0)