Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0beb76ce48 | |||
| f002ce67b1 | |||
| 5fdc8acbe9 | |||
| 1811f9581f | |||
| 66235f3198 | |||
| 28517085ca | |||
| c9ed79d2c8 | |||
| 043574f558 | |||
| fc5680d95c | |||
| eabb7c98ce |
@@ -1,6 +1,6 @@
|
||||
"""Freqtrade bot"""
|
||||
|
||||
__version__ = "2025.11"
|
||||
__version__ = "2025.11.2"
|
||||
|
||||
if "dev" in __version__:
|
||||
from pathlib import Path
|
||||
|
||||
@@ -17,7 +17,7 @@ from freqtrade.exchange.binance_public_data import (
|
||||
download_archive_trades,
|
||||
)
|
||||
from freqtrade.exchange.common import retrier
|
||||
from freqtrade.exchange.exchange_types import FtHas, Tickers
|
||||
from freqtrade.exchange.exchange_types import CcxtOrder, FtHas, Tickers
|
||||
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_msecs
|
||||
from freqtrade.misc import deep_merge_dicts, json_load
|
||||
from freqtrade.util import FtTTLCache
|
||||
@@ -145,6 +145,34 @@ class Binance(Exchange):
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def fetch_stoploss_order(
|
||||
self, order_id: str, pair: str, params: dict | None = None
|
||||
) -> CcxtOrder:
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
params = params or {}
|
||||
params.update({"stop": True})
|
||||
order = self.fetch_order(order_id, pair, params)
|
||||
if self.trading_mode == TradingMode.FUTURES and order.get("status", "open") == "closed":
|
||||
# Places a real order - which we need to fetch explicitly.
|
||||
|
||||
if new_orderid := order.get("info", {}).get("actualOrderId"):
|
||||
order1 = self.fetch_order(order_id=new_orderid, pair=pair, params={})
|
||||
order1["id_stop"] = order1["id"]
|
||||
order1["id"] = order_id
|
||||
order1["type"] = "stoploss"
|
||||
order1["stopPrice"] = order.get("stopPrice")
|
||||
order1["status_stop"] = "triggered"
|
||||
|
||||
return order1
|
||||
|
||||
return order
|
||||
|
||||
def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict:
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
params = params or {}
|
||||
params.update({"stop": True})
|
||||
return self.cancel_order(order_id=order_id, pair=pair, params=params)
|
||||
|
||||
def get_historic_ohlcv(
|
||||
self,
|
||||
pair: str,
|
||||
|
||||
@@ -1362,8 +1362,9 @@ class Exchange:
|
||||
amount: float,
|
||||
rate: float,
|
||||
leverage: float,
|
||||
reduceOnly: bool = False,
|
||||
time_in_force: str = "GTC",
|
||||
reduceOnly: bool = False,
|
||||
initial_order: bool = True,
|
||||
) -> CcxtOrder:
|
||||
if self._config["dry_run"]:
|
||||
dry_order = self.create_dry_run_order(
|
||||
@@ -1380,7 +1381,7 @@ class Exchange:
|
||||
rate_for_order = self.price_to_precision(pair, rate) if needs_price else None
|
||||
|
||||
if not reduceOnly:
|
||||
self._lev_prep(pair, leverage, side)
|
||||
self._lev_prep(pair, leverage, side, accept_fail=not initial_order)
|
||||
|
||||
order = self._api.create_order(
|
||||
pair,
|
||||
|
||||
@@ -44,8 +44,9 @@ class Kucoin(Exchange):
|
||||
amount: float,
|
||||
rate: float,
|
||||
leverage: float,
|
||||
reduceOnly: bool = False,
|
||||
time_in_force: str = "GTC",
|
||||
reduceOnly: bool = False,
|
||||
initial_order: bool = True,
|
||||
) -> CcxtOrder:
|
||||
res = super().create_order(
|
||||
pair=pair,
|
||||
@@ -56,6 +57,7 @@ class Kucoin(Exchange):
|
||||
leverage=leverage,
|
||||
reduceOnly=reduceOnly,
|
||||
time_in_force=time_in_force,
|
||||
initial_order=initial_order,
|
||||
)
|
||||
# Kucoin returns only the order-id.
|
||||
# ccxt returns status = 'closed' at the moment - which is information ccxt invented.
|
||||
|
||||
@@ -937,6 +937,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
reduceOnly=False,
|
||||
time_in_force=time_in_force,
|
||||
leverage=leverage,
|
||||
initial_order=trade is None,
|
||||
)
|
||||
order_obj = Order.parse_from_ccxt_object(order, pair, side, amount, enter_limit_requested)
|
||||
order_obj.ft_order_tag = enter_tag
|
||||
@@ -2131,6 +2132,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
leverage=trade.leverage,
|
||||
reduceOnly=self.trading_mode == TradingMode.FUTURES,
|
||||
time_in_force=time_in_force,
|
||||
initial_order=False,
|
||||
)
|
||||
except InsufficientFundsError as e:
|
||||
logger.warning(f"Unable to place order {e}.")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from freqtrade_client.ft_rest_client import FtRestClient
|
||||
|
||||
|
||||
__version__ = "2025.11"
|
||||
__version__ = "2025.11.2"
|
||||
|
||||
if "dev" in __version__:
|
||||
from pathlib import Path
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ ft-pandas-ta==0.3.16
|
||||
ta-lib==0.6.8
|
||||
technical==1.5.3
|
||||
|
||||
ccxt==4.5.20
|
||||
ccxt==4.5.27
|
||||
cryptography==46.0.3
|
||||
aiohttp==3.13.2
|
||||
SQLAlchemy==2.0.44
|
||||
|
||||
@@ -3826,37 +3826,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)
|
||||
|
||||
|
||||
@@ -4041,7 +4033,7 @@ def test_fetch_order_or_stoploss_order(default_conf, mocker):
|
||||
fetch_order_mock = MagicMock()
|
||||
fetch_stoploss_order_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
exchange,
|
||||
fetch_order=fetch_order_mock,
|
||||
fetch_stoploss_order=fetch_stoploss_order_mock,
|
||||
)
|
||||
|
||||
@@ -515,7 +515,8 @@ EXCHANGES = {
|
||||
],
|
||||
},
|
||||
"hyperliquid": {
|
||||
"pair": "UBTC/USDC",
|
||||
# TODO: Should be UBTC/USDC - probably needs a fix in ccxt
|
||||
"pair": "BTC/USDC",
|
||||
"stake_currency": "USDC",
|
||||
"hasQuoteVolume": False,
|
||||
"timeframe": "30m",
|
||||
|
||||
@@ -53,13 +53,10 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
cancel_order_mock = MagicMock()
|
||||
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(
|
||||
@@ -73,6 +70,12 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
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"
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -252,9 +256,12 @@ def test_handle_stoploss_on_exchange_emergency(
|
||||
stoploss = MagicMock(side_effect=InvalidOrderException())
|
||||
assert trade.has_open_sl_orders is True
|
||||
Trade.commit()
|
||||
mocker.patch(f"{EXMS}.cancel_stoploss_order_with_result", side_effect=InvalidOrderException())
|
||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_cancelled)
|
||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
|
||||
mocker.patch.multiple(
|
||||
freqtrade.exchange,
|
||||
cancel_stoploss_order_with_result=MagicMock(side_effect=InvalidOrderException()),
|
||||
fetch_stoploss_order=stoploss_order_cancelled,
|
||||
create_stoploss=stoploss,
|
||||
)
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
assert trade.has_open_sl_orders is False
|
||||
assert trade.is_open is False
|
||||
@@ -311,7 +318,7 @@ def test_handle_stoploss_on_exchange_partial(
|
||||
"amount": enter_order["amount"],
|
||||
}
|
||||
)
|
||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_hit)
|
||||
mocker.patch.multiple(freqtrade.exchange, fetch_stoploss_order=stoploss_order_hit)
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
# Stoploss filled partially ...
|
||||
assert trade.amount == 15
|
||||
@@ -383,8 +390,11 @@ def test_handle_stoploss_on_exchange_partial_cancel_here(
|
||||
"amount": enter_order["amount"],
|
||||
}
|
||||
)
|
||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_hit)
|
||||
mocker.patch(f"{EXMS}.cancel_stoploss_order_with_result", stoploss_order_cancel)
|
||||
mocker.patch.multiple(
|
||||
freqtrade.exchange,
|
||||
fetch_stoploss_order=stoploss_order_hit,
|
||||
cancel_stoploss_order_with_result=stoploss_order_cancel,
|
||||
)
|
||||
time_machine.shift(timedelta(minutes=15))
|
||||
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
@@ -408,20 +418,20 @@ def test_handle_sle_cancel_cant_recreate(
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
fetch_ticker=MagicMock(return_value={"bid": 1.9, "ask": 2.2, "last": 1.9}),
|
||||
get_fee=fee,
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
mocker.patch.multiple(
|
||||
freqtrade.exchange,
|
||||
create_order=MagicMock(
|
||||
side_effect=[
|
||||
enter_order,
|
||||
exit_order,
|
||||
]
|
||||
),
|
||||
get_fee=fee,
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
fetch_stoploss_order=MagicMock(return_value={"status": "canceled", "id": "100"}),
|
||||
create_stoploss=MagicMock(side_effect=ExchangeError()),
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||
|
||||
freqtrade.enter_positions()
|
||||
@@ -644,8 +654,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
|
||||
@@ -671,9 +684,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
|
||||
@@ -711,8 +727,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
|
||||
@@ -786,8 +803,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)
|
||||
@@ -799,8 +820,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
|
||||
@@ -846,20 +867,9 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
fetch_ticker=MagicMock(return_value={"bid": 1.9, "ask": 2.2, "last": 1.9}),
|
||||
create_order=MagicMock(
|
||||
side_effect=[
|
||||
enter_order,
|
||||
exit_order,
|
||||
]
|
||||
),
|
||||
get_fee=fee,
|
||||
is_cancel_order_result_suitable=MagicMock(return_value=True),
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
create_stoploss=stoploss,
|
||||
stoploss_adjust=MagicMock(return_value=True),
|
||||
)
|
||||
|
||||
# enabling TSL
|
||||
default_conf_usdt["use_custom_stoploss"] = True
|
||||
@@ -868,6 +878,17 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
default_conf_usdt["minimal_roi"]["0"] = 999999999
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
mocker.patch.multiple(
|
||||
freqtrade.exchange,
|
||||
create_order=MagicMock(
|
||||
side_effect=[
|
||||
enter_order,
|
||||
exit_order,
|
||||
]
|
||||
),
|
||||
create_stoploss=stoploss,
|
||||
stoploss_adjust=MagicMock(return_value=True),
|
||||
)
|
||||
|
||||
# enabling stoploss on exchange
|
||||
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
||||
@@ -912,8 +933,11 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
x["id"] = order_id
|
||||
return x
|
||||
|
||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", MagicMock(fetch_stoploss_order_mock))
|
||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=slo_canceled)
|
||||
mocker.patch.multiple(
|
||||
freqtrade.exchange,
|
||||
fetch_stoploss_order=MagicMock(fetch_stoploss_order_mock),
|
||||
cancel_stoploss_order=MagicMock(return_value=slo_canceled),
|
||||
)
|
||||
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
@@ -932,8 +956,11 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
||||
|
||||
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
|
||||
@@ -1054,7 +1081,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=[
|
||||
@@ -1114,12 +1143,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)
|
||||
|
||||
@@ -1208,7 +1240,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
|
||||
"trades": None,
|
||||
}
|
||||
)
|
||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_executed)
|
||||
mocker.patch.object(freqtrade.exchange, "fetch_stoploss_order", stoploss_executed)
|
||||
|
||||
freqtrade.exit_positions(trades)
|
||||
assert trade.has_open_sl_orders is False
|
||||
|
||||
+11
-4
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user