mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-12-13 19:31:15 +00:00
Compare commits
34 Commits
fix/dynami
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6848f9197e | ||
|
|
6d2c30abca | ||
|
|
93bde7dc46 | ||
|
|
97e2e0a405 | ||
|
|
12206f028b | ||
|
|
4c7944ac77 | ||
|
|
d4ced7b416 | ||
|
|
451eef5c99 | ||
|
|
ae8f059de0 | ||
|
|
878bd7cbc7 | ||
|
|
6d017c9a6c | ||
|
|
4c3d9b8c70 | ||
|
|
23a4260859 | ||
|
|
5919736904 | ||
|
|
732610e200 | ||
|
|
3689b52309 | ||
|
|
01fbf31405 | ||
|
|
1e187e0945 | ||
|
|
e4fc5df1cf | ||
|
|
253950deb6 | ||
|
|
ed92d6beb9 | ||
|
|
ebb362d9fa | ||
|
|
f23fad420e | ||
|
|
aaf23221ff | ||
|
|
156c1a99a9 | ||
|
|
cb55ef5c59 | ||
|
|
6540fbb8e7 | ||
|
|
412392aea9 | ||
|
|
e3229935f6 | ||
|
|
b1ee115b77 | ||
|
|
d6060f04bc | ||
|
|
1ae5310d2f | ||
|
|
417a0817a7 | ||
|
|
8ca25b1757 |
@@ -31,7 +31,7 @@ repos:
|
|||||||
- types-requests==2.32.4.20250913
|
- types-requests==2.32.4.20250913
|
||||||
- types-tabulate==0.9.0.20241207
|
- types-tabulate==0.9.0.20241207
|
||||||
- types-python-dateutil==2.9.0.20251115
|
- types-python-dateutil==2.9.0.20251115
|
||||||
- scipy-stubs==1.16.3.1
|
- scipy-stubs==1.16.3.2
|
||||||
- SQLAlchemy==2.0.44
|
- SQLAlchemy==2.0.44
|
||||||
# stages: [push]
|
# stages: [push]
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ repos:
|
|||||||
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: 'v0.14.7'
|
rev: 'v0.14.8'
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ This software is for educational purposes only. Do not risk money which
|
|||||||
you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS
|
you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS
|
||||||
AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
|
AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
|
||||||
|
|
||||||
Always start by running a trading bot in Dry-run and do not engage money
|
Always start by running a trading bot in Dry-Run and do not engage money
|
||||||
before you understand how it works and what profit/loss you should
|
before you understand how it works and what profit/loss you should
|
||||||
expect.
|
expect.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ hesitate to read the source code and understand the mechanism of this bot.
|
|||||||
|
|
||||||
## Supported Exchange marketplaces
|
## Supported Exchange marketplaces
|
||||||
|
|
||||||
Please read the [exchange specific notes](docs/exchanges.md) to learn about eventual, special configurations needed for each exchange.
|
Please read the [exchange-specific notes](docs/exchanges.md) to learn about special configurations that maybe needed for each exchange.
|
||||||
|
|
||||||
- [X] [Binance](https://www.binance.com/)
|
- [X] [Binance](https://www.binance.com/)
|
||||||
- [X] [BingX](https://bingx.com/invite/0EM9RX)
|
- [X] [BingX](https://bingx.com/invite/0EM9RX)
|
||||||
|
|||||||
@@ -31,9 +31,14 @@ The Order-type will be ignored if only one mode is available.
|
|||||||
--8<-- "includes/exchange-features.md"
|
--8<-- "includes/exchange-features.md"
|
||||||
|
|
||||||
!!! Note "Tight stoploss"
|
!!! Note "Tight stoploss"
|
||||||
<ins>Do not set too low/tight stoploss value when using stop loss on exchange!</ins>
|
Do not set too low/tight stoploss value when using stop loss on exchange!
|
||||||
If set to low/tight you will have greater risk of missing fill on the order and stoploss will not work.
|
If set to low/tight you will have greater risk of missing fill on the order and stoploss will not work.
|
||||||
|
|
||||||
|
!!! Warning "Loose stoploss"
|
||||||
|
Using stoploss on exchange with a very wide stoploss (e.g. -1) may fail to place the stoploss order on exchange due to exchange limitations.
|
||||||
|
In that case, the bot will fallback to using the `emergency_exit` order type to place a market order as placing the stoploss order failed.
|
||||||
|
Freqtrade currently does not implement a limitation to avoid this situation, so please ensure your stoploss values are within reasonable limits for your exchange or disable stoploss on exchange.
|
||||||
|
|
||||||
### stoploss_on_exchange and stoploss_on_exchange_limit_ratio
|
### stoploss_on_exchange and stoploss_on_exchange_limit_ratio
|
||||||
|
|
||||||
Enable or Disable stop loss on exchange.
|
Enable or Disable stop loss on exchange.
|
||||||
|
|||||||
@@ -644,7 +644,7 @@ Each of these methods are called right before placing an order on the exchange.
|
|||||||
If your custom pricing function return None or an invalid value, price will fall back to `proposed_rate`, which is based on the regular pricing configuration.
|
If your custom pricing function return None or an invalid value, price will fall back to `proposed_rate`, which is based on the regular pricing configuration.
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
Using custom_entry_price, the Trade object will be available as soon as the first entry order associated with the trade is created, for the first entry, `trade` parameter value will be `None`.
|
When using `custom_entry_price()`, the Trade object will be available as soon as the first entry order associated with the trade is created, for the first entry, `trade` parameter value will be `None`.
|
||||||
|
|
||||||
### Custom order entry and exit price example
|
### Custom order entry and exit price example
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from freqtrade.exchange.common import MAP_EXCHANGE_CHILDCLASS
|
|||||||
from freqtrade.exchange.exchange import Exchange
|
from freqtrade.exchange.exchange import Exchange
|
||||||
|
|
||||||
# isort: on
|
# isort: on
|
||||||
from freqtrade.exchange.binance import Binance
|
from freqtrade.exchange.binance import Binance, Binanceus, Binanceusdm
|
||||||
from freqtrade.exchange.bingx import Bingx
|
from freqtrade.exchange.bingx import Bingx
|
||||||
from freqtrade.exchange.bitget import Bitget
|
from freqtrade.exchange.bitget import Bitget
|
||||||
from freqtrade.exchange.bitmart import Bitmart
|
from freqtrade.exchange.bitmart import Bitmart
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from freqtrade.exchange.binance_public_data import (
|
|||||||
download_archive_trades,
|
download_archive_trades,
|
||||||
)
|
)
|
||||||
from freqtrade.exchange.common import retrier
|
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.exchange.exchange_utils_timeframe import timeframe_to_msecs
|
||||||
from freqtrade.misc import deep_merge_dicts, json_load
|
from freqtrade.misc import deep_merge_dicts, json_load
|
||||||
from freqtrade.util import FtTTLCache
|
from freqtrade.util import FtTTLCache
|
||||||
@@ -145,6 +145,20 @@ class Binance(Exchange):
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from 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})
|
||||||
|
return self.fetch_order(order_id, pair, params)
|
||||||
|
|
||||||
|
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(
|
def get_historic_ohlcv(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
@@ -544,3 +558,26 @@ class Binance(Exchange):
|
|||||||
cache[ft_symbol] = delist_dt
|
cache[ft_symbol] = delist_dt
|
||||||
|
|
||||||
return cache.get(pair, None)
|
return cache.get(pair, None)
|
||||||
|
|
||||||
|
|
||||||
|
class Binanceusdm(Binance):
|
||||||
|
"""Binacne USDM Exchange
|
||||||
|
Same as Binance - only futures trading is supported (via ccxt).
|
||||||
|
|
||||||
|
Not actually necessary, binance should be preferred.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||||
|
(TradingMode.FUTURES, MarginMode.CROSS),
|
||||||
|
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Binanceus(Binance):
|
||||||
|
"""Binance US exchange class.
|
||||||
|
Minimal adjustment to disable futures trading for the US subsidiary of Binance
|
||||||
|
"""
|
||||||
|
|
||||||
|
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||||
|
(TradingMode.SPOT, MarginMode.NONE),
|
||||||
|
]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -45,8 +45,6 @@ BAD_EXCHANGES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MAP_EXCHANGE_CHILDCLASS = {
|
MAP_EXCHANGE_CHILDCLASS = {
|
||||||
"binanceus": "binance",
|
|
||||||
"binanceusdm": "binance",
|
|
||||||
"okex": "okx",
|
"okex": "okx",
|
||||||
"gateio": "gate",
|
"gateio": "gate",
|
||||||
"huboi": "htx",
|
"huboi": "htx",
|
||||||
@@ -54,6 +52,8 @@ MAP_EXCHANGE_CHILDCLASS = {
|
|||||||
|
|
||||||
SUPPORTED_EXCHANGES = [
|
SUPPORTED_EXCHANGES = [
|
||||||
"binance",
|
"binance",
|
||||||
|
"binanceus",
|
||||||
|
"binanceusdm",
|
||||||
"bingx",
|
"bingx",
|
||||||
"bitmart",
|
"bitmart",
|
||||||
"bitget",
|
"bitget",
|
||||||
|
|||||||
@@ -1834,9 +1834,9 @@ class Exchange:
|
|||||||
if self._config["dry_run"] or self.trading_mode != TradingMode.FUTURES:
|
if self._config["dry_run"] or self.trading_mode != TradingMode.FUTURES:
|
||||||
return []
|
return []
|
||||||
try:
|
try:
|
||||||
symbols = []
|
symbols = None
|
||||||
if pair:
|
if pair:
|
||||||
symbols.append(pair)
|
symbols = [pair]
|
||||||
positions: list[CcxtPosition] = self._api.fetch_positions(symbols)
|
positions: list[CcxtPosition] = self._api.fetch_positions(symbols)
|
||||||
self._log_exchange_response("fetch_positions", positions)
|
self._log_exchange_response("fetch_positions", positions)
|
||||||
return positions
|
return positions
|
||||||
|
|||||||
@@ -266,14 +266,7 @@ class Okx(Exchange):
|
|||||||
return order["id"]
|
return order["id"]
|
||||||
|
|
||||||
def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict:
|
def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict:
|
||||||
params1 = {"stop": True}
|
return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True})
|
||||||
# 'ordType': 'conditional'
|
|
||||||
#
|
|
||||||
return self.cancel_order(
|
|
||||||
order_id=order_id,
|
|
||||||
pair=pair,
|
|
||||||
params=params1,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]:
|
def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]:
|
||||||
orders = []
|
orders = []
|
||||||
|
|||||||
@@ -2011,14 +2011,14 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
def _safe_exit_amount(self, trade: Trade, pair: str, amount: float) -> float:
|
def _safe_exit_amount(self, trade: Trade, pair: str, amount: float) -> float:
|
||||||
"""
|
"""
|
||||||
Get sellable amount.
|
Get exitable amount.
|
||||||
Should be trade.amount - but will fall back to the available amount if necessary.
|
Should be trade.amount - but will fall back to the available amount if necessary.
|
||||||
This should cover cases where get_real_amount() was not able to update the amount
|
This should cover cases where get_real_amount() was not able to update the amount
|
||||||
for whatever reason.
|
for whatever reason.
|
||||||
:param trade: Trade we're working with
|
:param trade: Trade we're working with
|
||||||
:param pair: Pair we're trying to sell
|
:param pair: Pair we're trying to exit
|
||||||
:param amount: amount we expect to be available
|
:param amount: amount we expect to be available
|
||||||
:return: amount to sell
|
:return: amount to exit
|
||||||
:raise: DependencyException: if available balance is not within 2% of the available amount.
|
:raise: DependencyException: if available balance is not within 2% of the available amount.
|
||||||
"""
|
"""
|
||||||
# Update wallets to ensure amounts tied up in a stoploss is now free!
|
# Update wallets to ensure amounts tied up in a stoploss is now free!
|
||||||
@@ -2058,7 +2058,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
"""
|
"""
|
||||||
Executes a trade exit for the given trade and limit
|
Executes a trade exit for the given trade and limit
|
||||||
:param trade: Trade instance
|
:param trade: Trade instance
|
||||||
:param limit: limit rate for the sell order
|
:param limit: limit rate for the exit order
|
||||||
:param exit_check: CheckTuple with signal and reason
|
:param exit_check: CheckTuple with signal and reason
|
||||||
:return: True if it succeeds False
|
:return: True if it succeeds False
|
||||||
"""
|
"""
|
||||||
@@ -2101,7 +2101,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
order_type = ordertype or self.strategy.order_types[exit_type]
|
order_type = ordertype or self.strategy.order_types[exit_type]
|
||||||
if exit_check.exit_type == ExitType.EMERGENCY_EXIT:
|
if exit_check.exit_type == ExitType.EMERGENCY_EXIT:
|
||||||
# Emergency sells (default to market!)
|
# Emergency exits (default to market!)
|
||||||
order_type = self.strategy.order_types.get("emergency_exit", "market")
|
order_type = self.strategy.order_types.get("emergency_exit", "market")
|
||||||
|
|
||||||
amount = self._safe_exit_amount(trade, trade.pair, sub_trade_amt or trade.amount)
|
amount = self._safe_exit_amount(trade, trade.pair, sub_trade_amt or trade.amount)
|
||||||
@@ -2130,7 +2130,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Execute sell and update trade record
|
# Execute exit and update trade record
|
||||||
order = self.exchange.create_order(
|
order = self.exchange.create_order(
|
||||||
pair=trade.pair,
|
pair=trade.pair,
|
||||||
ordertype=order_type,
|
ordertype=order_type,
|
||||||
@@ -2157,7 +2157,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.exit_reason = exit_reason
|
trade.exit_reason = exit_reason
|
||||||
|
|
||||||
self._notify_exit(trade, order_type, sub_trade=bool(sub_trade_amt), order=order_obj)
|
self._notify_exit(trade, order_type, sub_trade=bool(sub_trade_amt), order=order_obj)
|
||||||
# In case of market sell orders the order can be closed immediately
|
# In case of market exit orders the order can be closed immediately
|
||||||
if order.get("status", "unknown") in ("closed", "expired"):
|
if order.get("status", "unknown") in ("closed", "expired"):
|
||||||
self.update_trade_state(trade, order_obj.order_id, order)
|
self.update_trade_state(trade, order_obj.order_id, order)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
-r requirements-freqai-rl.txt
|
-r requirements-freqai-rl.txt
|
||||||
-r docs/requirements-docs.txt
|
-r docs/requirements-docs.txt
|
||||||
|
|
||||||
ruff==0.14.6
|
ruff==0.14.7
|
||||||
mypy==1.18.2
|
mypy==1.19.0
|
||||||
pre-commit==4.5.0
|
pre-commit==4.5.0
|
||||||
pytest==9.0.1
|
pytest==9.0.1
|
||||||
pytest-asyncio==1.3.0
|
pytest-asyncio==1.3.0
|
||||||
@@ -24,7 +24,7 @@ time-machine==3.1.0
|
|||||||
nbconvert==7.16.6
|
nbconvert==7.16.6
|
||||||
|
|
||||||
# mypy types
|
# mypy types
|
||||||
scipy-stubs==1.16.3.1 # keep in sync with `scipy` in `requirements-hyperopt.txt`
|
scipy-stubs==1.16.3.2 # keep in sync with `scipy` in `requirements-hyperopt.txt`
|
||||||
types-cachetools==6.2.0.20251022
|
types-cachetools==6.2.0.20251022
|
||||||
types-filelock==3.2.7
|
types-filelock==3.2.7
|
||||||
types-requests==2.32.4.20250913
|
types-requests==2.32.4.20250913
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ft-pandas-ta==0.3.16
|
|||||||
ta-lib==0.6.8
|
ta-lib==0.6.8
|
||||||
technical==1.5.3
|
technical==1.5.3
|
||||||
|
|
||||||
ccxt==4.5.24
|
ccxt==4.5.27
|
||||||
cryptography==46.0.3
|
cryptography==46.0.3
|
||||||
aiohttp==3.13.2
|
aiohttp==3.13.2
|
||||||
SQLAlchemy==2.0.44
|
SQLAlchemy==2.0.44
|
||||||
@@ -17,7 +17,7 @@ httpx>=0.24.1
|
|||||||
humanize==4.14.0
|
humanize==4.14.0
|
||||||
cachetools==6.2.2
|
cachetools==6.2.2
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
urllib3==2.5.0
|
urllib3==2.6.0
|
||||||
certifi==2025.11.12
|
certifi==2025.11.12
|
||||||
jsonschema==4.25.1
|
jsonschema==4.25.1
|
||||||
tabulate==0.9.0
|
tabulate==0.9.0
|
||||||
@@ -37,7 +37,7 @@ orjson==3.11.4
|
|||||||
sdnotify==0.3.2
|
sdnotify==0.3.2
|
||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.122.0
|
fastapi==0.123.0
|
||||||
pydantic==2.12.5
|
pydantic==2.12.5
|
||||||
uvicorn==0.38.0
|
uvicorn==0.38.0
|
||||||
pyjwt==2.10.1
|
pyjwt==2.10.1
|
||||||
|
|||||||
@@ -3901,37 +3901,29 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
|
|||||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
|
def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
|
||||||
default_conf["dry_run"] = False
|
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)
|
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}
|
res = {"fee": {}, "status": "canceled", "amount": 1234}
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=res)
|
mocker.patch.object(exchange, "cancel_stoploss_order", return_value=res)
|
||||||
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", return_value=res)
|
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
||||||
assert co == res
|
assert co == res
|
||||||
|
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value="canceled")
|
mocker.patch.object(exchange, "cancel_stoploss_order", return_value="canceled")
|
||||||
mocker.patch(f"{mock_prefix}.cancel_stoploss_order", return_value="canceled")
|
|
||||||
# Fall back to fetch_stoploss_order
|
# Fall back to fetch_stoploss_order
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
||||||
assert co == {"for": 123}
|
assert co == {"for": 123}
|
||||||
|
|
||||||
exc = InvalidOrderException("")
|
exc = InvalidOrderException("")
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", side_effect=exc)
|
mocker.patch.object(exchange, "fetch_stoploss_order", side_effect=exc)
|
||||||
mocker.patch(f"{mock_prefix}.fetch_stoploss_order", side_effect=exc)
|
|
||||||
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
co = exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=555)
|
||||||
assert co["amount"] == 555
|
assert co["amount"] == 555
|
||||||
assert co == {"id": "_", "fee": {}, "status": "canceled", "amount": 555, "info": {}}
|
assert co == {"id": "_", "fee": {}, "status": "canceled", "amount": 555, "info": {}}
|
||||||
|
|
||||||
with pytest.raises(InvalidOrderException):
|
with pytest.raises(InvalidOrderException):
|
||||||
exc = InvalidOrderException("Did not find order")
|
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)
|
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)
|
exchange.cancel_stoploss_order_with_result(order_id="_", pair="TKN/BTC", amount=123)
|
||||||
|
|
||||||
|
|
||||||
@@ -4116,7 +4108,7 @@ def test_fetch_order_or_stoploss_order(default_conf, mocker):
|
|||||||
fetch_order_mock = MagicMock()
|
fetch_order_mock = MagicMock()
|
||||||
fetch_stoploss_order_mock = MagicMock()
|
fetch_stoploss_order_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
exchange,
|
||||||
fetch_order=fetch_order_mock,
|
fetch_order=fetch_order_mock,
|
||||||
fetch_stoploss_order=fetch_stoploss_order_mock,
|
fetch_stoploss_order=fetch_stoploss_order_mock,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -60,13 +60,10 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
cancel_order_mock = MagicMock(side_effect=patch_stoploss)
|
cancel_order_mock = MagicMock(side_effect=patch_stoploss)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
EXMS,
|
||||||
create_stoploss=stoploss,
|
|
||||||
fetch_ticker=ticker,
|
fetch_ticker=ticker,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
amount_to_precision=lambda s, x, y: y,
|
amount_to_precision=lambda s, x, y: y,
|
||||||
price_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(
|
mocker.patch.multiple(
|
||||||
@@ -80,6 +77,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)
|
mocker.patch("freqtrade.wallets.Wallets.check_exit_amount", return_value=True)
|
||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
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
|
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
||||||
# Switch ordertype to market to close trade immediately
|
# Switch ordertype to market to close trade immediately
|
||||||
freqtrade.strategy.order_types["exit"] = "market"
|
freqtrade.strategy.order_types["exit"] = "market"
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def test_handle_stoploss_on_exchange(
|
|||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
hanging_stoploss_order = MagicMock(return_value={"id": "13434334", "status": "open"})
|
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
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
hanging_stoploss_order.assert_called_once_with("13434334", trade.pair)
|
hanging_stoploss_order.assert_called_once_with("13434334", trade.pair)
|
||||||
@@ -116,7 +116,7 @@ def test_handle_stoploss_on_exchange(
|
|||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
canceled_stoploss_order = MagicMock(return_value={"id": "13434334", "status": "canceled"})
|
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()
|
stoploss.reset_mock()
|
||||||
amount_before = trade.amount
|
amount_before = trade.amount
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ def test_handle_stoploss_on_exchange(
|
|||||||
"amount": enter_order["amount"],
|
"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)
|
freqtrade.strategy.order_filled = MagicMock(return_value=None)
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is True
|
assert freqtrade.handle_stoploss_on_exchange(trade) is True
|
||||||
assert log_has_re(r"STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.", caplog)
|
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
|
assert freqtrade.strategy.order_filled.call_count == 1
|
||||||
caplog.clear()
|
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
|
trade.is_open = True
|
||||||
freqtrade.handle_stoploss_on_exchange(trade)
|
freqtrade.handle_stoploss_on_exchange(trade)
|
||||||
assert log_has("Unable to place a stoploss order on exchange.", caplog)
|
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
|
# It should try to add stoploss order
|
||||||
stop_order_dict.update({"id": "105"})
|
stop_order_dict.update({"id": "105"})
|
||||||
stoploss.reset_mock()
|
stoploss.reset_mock()
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", side_effect=InvalidOrderException())
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
|
freqtrade.exchange,
|
||||||
|
fetch_stoploss_order=MagicMock(
|
||||||
|
side_effect=InvalidOrderException(),
|
||||||
|
),
|
||||||
|
create_stoploss=stoploss,
|
||||||
|
)
|
||||||
freqtrade.handle_stoploss_on_exchange(trade)
|
freqtrade.handle_stoploss_on_exchange(trade)
|
||||||
assert len(trade.open_sl_orders) == 1
|
assert len(trade.open_sl_orders) == 1
|
||||||
assert stoploss.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
@@ -179,8 +184,7 @@ def test_handle_stoploss_on_exchange(
|
|||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
trade.open_sl_orders[-1].ft_is_open = False
|
trade.open_sl_orders[-1].ft_is_open = False
|
||||||
stoploss.reset_mock()
|
stoploss.reset_mock()
|
||||||
mocker.patch(f"{EXMS}.fetch_order")
|
mocker.patch.multiple(freqtrade.exchange, fetch_order=MagicMock(), create_stoploss=stoploss)
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
|
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
assert trade.has_open_sl_orders is False
|
assert trade.has_open_sl_orders is False
|
||||||
assert stoploss.call_count == 0
|
assert stoploss.call_count == 0
|
||||||
@@ -252,9 +256,12 @@ def test_handle_stoploss_on_exchange_emergency(
|
|||||||
stoploss = MagicMock(side_effect=InvalidOrderException())
|
stoploss = MagicMock(side_effect=InvalidOrderException())
|
||||||
assert trade.has_open_sl_orders is True
|
assert trade.has_open_sl_orders is True
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order_with_result", side_effect=InvalidOrderException())
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_cancelled)
|
freqtrade.exchange,
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss)
|
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 freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
assert trade.has_open_sl_orders is False
|
assert trade.has_open_sl_orders is False
|
||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
@@ -311,7 +318,7 @@ def test_handle_stoploss_on_exchange_partial(
|
|||||||
"amount": enter_order["amount"],
|
"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
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
# Stoploss filled partially ...
|
# Stoploss filled partially ...
|
||||||
assert trade.amount == 15
|
assert trade.amount == 15
|
||||||
@@ -383,8 +390,11 @@ def test_handle_stoploss_on_exchange_partial_cancel_here(
|
|||||||
"amount": enter_order["amount"],
|
"amount": enter_order["amount"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", stoploss_order_hit)
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order_with_result", stoploss_order_cancel)
|
freqtrade.exchange,
|
||||||
|
fetch_stoploss_order=stoploss_order_hit,
|
||||||
|
cancel_stoploss_order_with_result=stoploss_order_cancel,
|
||||||
|
)
|
||||||
time_machine.shift(timedelta(minutes=15))
|
time_machine.shift(timedelta(minutes=15))
|
||||||
|
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
@@ -408,20 +418,20 @@ def test_handle_sle_cancel_cant_recreate(
|
|||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
EXMS,
|
||||||
fetch_ticker=MagicMock(return_value={"bid": 1.9, "ask": 2.2, "last": 1.9}),
|
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(
|
create_order=MagicMock(
|
||||||
side_effect=[
|
side_effect=[
|
||||||
enter_order,
|
enter_order,
|
||||||
exit_order,
|
exit_order,
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
get_fee=fee,
|
|
||||||
)
|
|
||||||
mocker.patch.multiple(
|
|
||||||
EXMS,
|
|
||||||
fetch_stoploss_order=MagicMock(return_value={"status": "canceled", "id": "100"}),
|
fetch_stoploss_order=MagicMock(return_value={"status": "canceled", "id": "100"}),
|
||||||
create_stoploss=MagicMock(side_effect=ExchangeError()),
|
create_stoploss=MagicMock(side_effect=ExchangeError()),
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
@@ -644,8 +654,11 @@ def test_handle_stoploss_on_exchange_trailing(
|
|||||||
stoploss_order_cancel = deepcopy(stoploss_order_hanging)
|
stoploss_order_cancel = deepcopy(stoploss_order_hanging)
|
||||||
stoploss_order_cancel["status"] = "canceled"
|
stoploss_order_cancel["status"] = "canceled"
|
||||||
|
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value=stoploss_order_hanging)
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=stoploss_order_cancel)
|
freqtrade.exchange,
|
||||||
|
fetch_stoploss_order=MagicMock(return_value=stoploss_order_hanging),
|
||||||
|
cancel_stoploss_order=MagicMock(return_value=stoploss_order_cancel),
|
||||||
|
)
|
||||||
|
|
||||||
# stoploss initially at 5%
|
# stoploss initially at 5%
|
||||||
assert freqtrade.handle_trade(trade) is False
|
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}
|
return_value={"id": "13434334", "status": "canceled", "fee": {}, "amount": trade.amount}
|
||||||
)
|
)
|
||||||
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
|
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order")
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", cancel_order_mock)
|
freqtrade.exchange,
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss_order_mock)
|
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
|
# stoploss should not be updated as the interval is 60 seconds
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
@@ -711,8 +727,9 @@ def test_handle_stoploss_on_exchange_trailing(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
mocker.patch(
|
mocker.patch.object(
|
||||||
f"{EXMS}.cancel_stoploss_order_with_result",
|
freqtrade.exchange,
|
||||||
|
"cancel_stoploss_order_with_result",
|
||||||
return_value={"id": "so1", "status": "canceled"},
|
return_value={"id": "so1", "status": "canceled"},
|
||||||
)
|
)
|
||||||
assert len(trade.open_sl_orders) == 1
|
assert len(trade.open_sl_orders) == 1
|
||||||
@@ -786,8 +803,12 @@ def test_handle_stoploss_on_exchange_trailing_error(
|
|||||||
order_date=dt_now(),
|
order_date=dt_now(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", side_effect=InvalidOrderException())
|
mocker.patch.object(
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", return_value=stoploss_order_hanging)
|
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))
|
time_machine.shift(timedelta(minutes=50))
|
||||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
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)
|
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
|
# Fail creating stoploss order
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
cancel_mock = mocker.patch(f"{EXMS}.cancel_stoploss_order")
|
cancel_mock = mocker.patch.object(freqtrade.exchange, "cancel_stoploss_order")
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", side_effect=ExchangeError())
|
mocker.patch.object(freqtrade.exchange, "create_stoploss", side_effect=ExchangeError())
|
||||||
time_machine.shift(timedelta(minutes=50))
|
time_machine.shift(timedelta(minutes=50))
|
||||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
||||||
assert cancel_mock.call_count == 2
|
assert cancel_mock.call_count == 2
|
||||||
@@ -846,20 +867,9 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
|||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
EXMS,
|
||||||
fetch_ticker=MagicMock(return_value={"bid": 1.9, "ask": 2.2, "last": 1.9}),
|
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,
|
get_fee=fee,
|
||||||
is_cancel_order_result_suitable=MagicMock(return_value=True),
|
is_cancel_order_result_suitable=MagicMock(return_value=True),
|
||||||
)
|
)
|
||||||
mocker.patch.multiple(
|
|
||||||
EXMS,
|
|
||||||
create_stoploss=stoploss,
|
|
||||||
stoploss_adjust=MagicMock(return_value=True),
|
|
||||||
)
|
|
||||||
|
|
||||||
# enabling TSL
|
# enabling TSL
|
||||||
default_conf_usdt["use_custom_stoploss"] = True
|
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
|
default_conf_usdt["minimal_roi"]["0"] = 999999999
|
||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
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
|
# enabling stoploss on exchange
|
||||||
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
||||||
@@ -912,8 +933,11 @@ def test_handle_stoploss_on_exchange_custom_stop(
|
|||||||
x["id"] = order_id
|
x["id"] = order_id
|
||||||
return x
|
return x
|
||||||
|
|
||||||
mocker.patch(f"{EXMS}.fetch_stoploss_order", MagicMock(fetch_stoploss_order_mock))
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", return_value=slo_canceled)
|
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_trade(trade) is False
|
||||||
assert freqtrade.handle_stoploss_on_exchange(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()
|
cancel_order_mock = MagicMock()
|
||||||
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
|
stoploss_order_mock = MagicMock(return_value={"id": "so1", "status": "open"})
|
||||||
mocker.patch(f"{EXMS}.cancel_stoploss_order", cancel_order_mock)
|
mocker.patch.multiple(
|
||||||
mocker.patch(f"{EXMS}.create_stoploss", stoploss_order_mock)
|
freqtrade.exchange,
|
||||||
|
cancel_stoploss_order=cancel_order_mock,
|
||||||
|
create_stoploss=stoploss_order_mock,
|
||||||
|
)
|
||||||
|
|
||||||
# stoploss should not be updated as the interval is 60 seconds
|
# stoploss should not be updated as the interval is 60 seconds
|
||||||
assert freqtrade.handle_trade(trade) is False
|
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
|
mocker, default_conf_usdt, ticker_usdt, fee, caplog
|
||||||
) -> None:
|
) -> None:
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
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))
|
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=300))
|
||||||
create_order_mock = MagicMock(
|
create_order_mock = MagicMock(
|
||||||
side_effect=[
|
side_effect=[
|
||||||
@@ -1114,12 +1143,15 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
amount_to_precision=lambda s, x, y: y,
|
amount_to_precision=lambda s, x, y: y,
|
||||||
price_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,
|
create_stoploss=stoploss,
|
||||||
cancel_stoploss_order=cancel_order,
|
cancel_stoploss_order=cancel_order,
|
||||||
_dry_is_price_crossed=MagicMock(side_effect=[True, False]),
|
_dry_is_price_crossed=MagicMock(side_effect=[True, False]),
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
|
||||||
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
freqtrade.strategy.order_types["stoploss_on_exchange"] = True
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
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,
|
"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)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade.has_open_sl_orders is False
|
assert trade.has_open_sl_orders is False
|
||||||
|
|||||||
@@ -386,11 +386,14 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
|
|||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
EXMS,
|
||||||
markets=PropertyMock(return_value=markets),
|
markets=PropertyMock(return_value=markets),
|
||||||
cancel_order=cancel_mock,
|
|
||||||
cancel_stoploss_order=stoploss_mock,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
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
|
freqtradebot.strategy.order_types["stoploss_on_exchange"] = True
|
||||||
create_mock_trades(fee, is_short)
|
create_mock_trades(fee, is_short)
|
||||||
rpc = RPC(freqtradebot)
|
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 stoploss_mock.call_count == 1
|
||||||
assert res["cancel_order_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")
|
res = rpc._rpc_delete("3")
|
||||||
assert stoploss_mock.call_count == 1
|
assert stoploss_mock.call_count == 1
|
||||||
stoploss_mock.reset_mock()
|
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")
|
res = rpc._rpc_delete("4")
|
||||||
assert cancel_mock.call_count == 1
|
assert cancel_mock.call_count == 1
|
||||||
|
|||||||
@@ -1034,8 +1034,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
|||||||
stoploss_mock = MagicMock()
|
stoploss_mock = MagicMock()
|
||||||
cancel_mock = MagicMock()
|
cancel_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
EXMS,
|
ftbot.exchange,
|
||||||
markets=PropertyMock(return_value=markets),
|
|
||||||
cancel_order=cancel_mock,
|
cancel_order=cancel_mock,
|
||||||
cancel_stoploss_order=stoploss_mock,
|
cancel_stoploss_order=stoploss_mock,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user