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: 1 addition & 8 deletions freqtrade/exchange/okx.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,7 @@ def get_order_id_conditional(self, order: CcxtOrder) -> str:
return order["id"]

def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict:
params1 = {"stop": True}
# 'ordType': 'conditional'
#
return self.cancel_order(
order_id=order_id,
pair=pair,
params=params1,
)
return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True})

def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]:
orders = []
Expand Down
18 changes: 5 additions & 13 deletions tests/exchange/test_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -3901,37 +3901,29 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
default_conf["dry_run"] = False
mock_prefix = "freqtrade.exchange.gate.Gate"
if exchange_name == "okx":
mock_prefix = "freqtrade.exchange.okx.Okx"
mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value={"for": 123})
mocker.patch(f"{mock_prefix}.fetch_stoploss_order", return_value={"for": 123})
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch.object(exchange, "fetch_stoploss_order", return_value={"for": 123})

res = {"fee": {}, "status": "canceled", "amount": 1234}
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=res)
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", return_value=res)
mocker.patch.object(exchange, "cancel_stoploss_order", return_value=res)
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
assert co == res

mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value="canceled")
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", return_value="canceled")
mocker.patch.object(exchange, "cancel_stoploss_order", return_value="canceled")
# Fall back to fetch_stoploss_order
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
assert co == {"for": 123}

exc = InvalidOrderException("")
mocker.patch(f"{EXMS}.fetch_stoploss_order", side_effect=exc)
mocker.patch(f"{mock_prefix}.fetch_stoploss_order", side_effect=exc)
mocker.patch.object(exchange, "fetch_stoploss_order", side_effect=exc)
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
assert co["amount"] == 555
assert co == {"id": "_", "fee": {}, "status": "canceled", "amount": 555, "info": {}}

with pytest.raises(InvalidOrderException):
exc = InvalidOrderException("Did not find order")
mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=exc)
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", side_effect=exc)
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
mocker.patch.object(exchange, "cancel_stoploss_order", side_effect=exc)
exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=123)


Expand Down
9 changes: 6 additions & 3 deletions tests/freqtradebot/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,10 @@ def patch_stoploss(order_id, *args, **kwargs):
cancel_order_mock = MagicMock(side_effect=patch_stoploss)
mocker.patch.multiple(
EXMS,
create_stoploss=stoploss,
fetch_ticker=ticker,
get_fee=fee,
amount_to_precision=lambda s, x, y: y,
price_to_precision=lambda s, x, y: y,
fetch_stoploss_order=stoploss_order_mock,
cancel_stoploss_order_with_result=cancel_order_mock,
)

mocker.patch.multiple(
Expand All @@ -80,6 +77,12 @@ def patch_stoploss(order_id, *args, **kwargs):
mocker.patch("freqtrade.wallets.Wallets.check_exit_amount", return_value=True)

freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch.multiple(
freqtrade.exchange,
create_stoploss=stoploss,
fetch_stoploss_order=stoploss_order_mock,
cancel_stoploss_order_with_result=cancel_order_mock,
)
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
# Switch ordertype to market to close trade immediately
freqtrade.strategy.order_types["exit"] = "market"
Expand Down
69 changes: 46 additions & 23 deletions tests/freqtradebot/test_stoploss_on_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def test_handle_stoploss_on_exchange(
trade.is_open = True

hanging_stoploss_order = MagicMock(return_value={"id": "13434334", "status": "open"})
mocker.patch(f"{EXMS}.fetch_stoploss_order", hanging_stoploss_order)
mocker.patch.object(freqtrade.exchange, "fetch_stoploss_order", hanging_stoploss_order)

assert freqtrade.handle_stoploss_on_exchange(trade) is False
hanging_stoploss_order.assert_called_once_with("13434334", trade.pair)
Expand All @@ -116,7 +116,7 @@ def test_handle_stoploss_on_exchange(
trade.is_open = True

canceled_stoploss_order = MagicMock(return_value={"id": "13434334", "status": "canceled"})
mocker.patch(f"{EXMS}.fetch_stoploss_order", canceled_stoploss_order)
mocker.patch.object(freqtrade.exchange, "fetch_stoploss_order", canceled_stoploss_order)
stoploss.reset_mock()
amount_before = trade.amount

Expand Down Expand Up @@ -149,7 +149,7 @@ def test_handle_stoploss_on_exchange(
"amount": enter_order["amount"],
}
)
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_hit)
mocker.patch.object(freqtrade.exchange, "fetch_stoploss_order", stoploss_order_hit)
freqtrade.strategy.order_filled = MagicMock(return_value=None)
assert freqtrade.handle_stoploss_on_exchange(trade) is True
assert log_has_re(r"STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.", caplog)
Expand All @@ -158,7 +158,7 @@ def test_handle_stoploss_on_exchange(
assert freqtrade.strategy.order_filled.call_count == 1
caplog.clear()

mocker.patch(f"{EXMS}.create_stoploss", side_effect=ExchangeError())
mocker.patch.object(freqtrade.exchange, "create_stoploss", side_effect=ExchangeError())
trade.is_open = True
freqtrade.handle_stoploss_on_exchange(trade)
assert log_has("Unable to place a stoploss order on exchange.", caplog)
Expand All @@ -168,8 +168,13 @@ def test_handle_stoploss_on_exchange(
# It should try to add stoploss order
stop_order_dict.update({"id": "105"})
stoploss.reset_mock()
mocker.patch(f"{EXMS}.fetch_stoploss_order", side_effect=InvalidOrderException())
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
mocker.patch.multiple(
freqtrade.exchange,
fetch_stoploss_order=MagicMock(
side_effect=InvalidOrderException(),
),
create_stoploss=stoploss,
)
freqtrade.handle_stoploss_on_exchange(trade)
assert len(trade.open_sl_orders) == 1
assert stoploss.call_count == 1
Expand All @@ -179,8 +184,7 @@ def test_handle_stoploss_on_exchange(
trade.is_open = False
trade.open_sl_orders[-1].ft_is_open = False
stoploss.reset_mock()
mocker.patch(f"{EXMS}.fetch_order")
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
mocker.patch.multiple(freqtrade.exchange, fetch_order=MagicMock(), create_stoploss=stoploss)
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.has_open_sl_orders is False
assert stoploss.call_count == 0
Expand Down Expand Up @@ -644,8 +648,11 @@ def test_handle_stoploss_on_exchange_trailing(
stoploss_order_cancel = deepcopy(stoploss_order_hanging)
stoploss_order_cancel["status"] = "canceled"

mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value=stoploss_order_hanging)
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=stoploss_order_cancel)
mocker.patch.multiple(
freqtrade.exchange,
fetch_stoploss_order=MagicMock(return_value=stoploss_order_hanging),
cancel_stoploss_order=MagicMock(return_value=stoploss_order_cancel),
)

# stoploss initially at 5%
assert freqtrade.handle_trade(trade) is False
Expand All @@ -671,9 +678,12 @@ def test_handle_stoploss_on_exchange_trailing(
return_value={"id": "13434334", "status": "canceled", "fee": {}, "amount": trade.amount}
)
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
mocker.patch(f"{EXMS}.fetch_stoploss_order")
mocker.patch(f"{EXMS}.cancel_stoploss_order", cancel_order_mock)
mocker.patch(f"{EXMS}.create_stoploss", stoploss_order_mock)
mocker.patch.multiple(
freqtrade.exchange,
fetch_stoploss_order=MagicMock(),
cancel_stoploss_order=cancel_order_mock,
create_stoploss=stoploss_order_mock,
)

# stoploss should not be updated as the interval is 60 seconds
assert freqtrade.handle_trade(trade) is False
Expand Down Expand Up @@ -711,8 +721,9 @@ def test_handle_stoploss_on_exchange_trailing(
}
),
)
mocker.patch(
f"{EXMS}.cancel_stoploss_order_with_result",
mocker.patch.object(
freqtrade.exchange,
"cancel_stoploss_order_with_result",
return_value={"id": "so1", "status": "canceled"},
)
assert len(trade.open_sl_orders) == 1
Expand Down Expand Up @@ -786,8 +797,12 @@ def test_handle_stoploss_on_exchange_trailing_error(
order_date=dt_now(),
)
)
mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=InvalidOrderException())
mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value=stoploss_order_hanging)
mocker.patch.object(
freqtrade.exchange, "cancel_stoploss_order", side_effect=InvalidOrderException()
)
mocker.patch.object(
freqtrade.exchange, "fetch_stoploss_order", return_value=stoploss_order_hanging
)
time_machine.shift(timedelta(minutes=50))
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/USDT.*", caplog)
Expand All @@ -799,8 +814,8 @@ def test_handle_stoploss_on_exchange_trailing_error(

# Fail creating stoploss order
caplog.clear()
cancel_mock = mocker.patch(f"{EXMS}.cancel_stoploss_order")
mocker.patch(f"{EXMS}.create_stoploss", side_effect=ExchangeError())
cancel_mock = mocker.patch.object(freqtrade.exchange, "cancel_stoploss_order")
mocker.patch.object(freqtrade.exchange, "create_stoploss", side_effect=ExchangeError())
time_machine.shift(timedelta(minutes=50))
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert cancel_mock.call_count == 2
Expand Down Expand Up @@ -932,8 +947,11 @@ def fetch_stoploss_order_mock(order_id, *args, **kwargs):

cancel_order_mock = MagicMock()
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
mocker.patch(f"{EXMS}.cancel_stoploss_order", cancel_order_mock)
mocker.patch(f"{EXMS}.create_stoploss", stoploss_order_mock)
mocker.patch.multiple(
freqtrade.exchange,
cancel_stoploss_order=cancel_order_mock,
create_stoploss=stoploss_order_mock,
)

# stoploss should not be updated as the interval is 60 seconds
assert freqtrade.handle_trade(trade) is False
Expand Down Expand Up @@ -1054,7 +1072,9 @@ def test_execute_trade_exit_sloe_cancel_exception(
mocker, default_conf_usdt, ticker_usdt, fee, caplog
) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=InvalidOrderException())
mocker.patch.object(
freqtrade.exchange, "cancel_stoploss_order", side_effect=InvalidOrderException()
)
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=300))
create_order_mock = MagicMock(
side_effect=[
Expand Down Expand Up @@ -1114,12 +1134,15 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
get_fee=fee,
amount_to_precision=lambda s, x, y: y,
price_to_precision=lambda s, x, y: y,
)
freqtrade = FreqtradeBot(default_conf_usdt)
mocker.patch.multiple(
freqtrade.exchange,
create_stoploss=stoploss,
cancel_stoploss_order=cancel_order,
_dry_is_price_crossed=MagicMock(side_effect=[True, False]),
)

freqtrade = FreqtradeBot(default_conf_usdt)
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)

Expand Down
15 changes: 11 additions & 4 deletions tests/rpc/test_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,14 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
cancel_order=cancel_mock,
cancel_stoploss_order=stoploss_mock,
)

freqtradebot = get_patched_freqtradebot(mocker, default_conf)
mocker.patch.multiple(
freqtradebot.exchange,
cancel_order=cancel_mock,
cancel_stoploss_order=stoploss_mock,
)
freqtradebot.strategy.order_types["stoploss_on_exchange"] = True
create_mock_trades(fee, is_short)
rpc = RPC(freqtradebot)
Expand Down Expand Up @@ -426,13 +429,17 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
assert stoploss_mock.call_count == 1
assert res["cancel_order_count"] == 1

stoploss_mock = mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=InvalidOrderException)
stoploss_mock = mocker.patch.object(
freqtradebot.exchange, "cancel_stoploss_order", side_effect=InvalidOrderException
)

res = rpc._rpc_delete("3")
assert stoploss_mock.call_count == 1
stoploss_mock.reset_mock()

cancel_mock = mocker.patch(f"{EXMS}.cancel_order", side_effect=InvalidOrderException)
cancel_mock = mocker.patch.object(
freqtradebot.exchange, "cancel_order", side_effect=InvalidOrderException
)

res = rpc._rpc_delete("4")
assert cancel_mock.call_count == 1
Expand Down
3 changes: 1 addition & 2 deletions tests/rpc/test_rpc_apiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1034,8 +1034,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
stoploss_mock = MagicMock()
cancel_mock = MagicMock()
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
ftbot.exchange,
cancel_order=cancel_mock,
cancel_stoploss_order=stoploss_mock,
)
Expand Down
Loading