From 93c1dff71bbae773b922794f956013e0d0b9c908 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Sep 2021 20:43:07 +0200 Subject: [PATCH 01/32] Allow adding new additional headers --- freqtrade/exchange/exchange.py | 9 ++++++++- tests/exchange/test_exchange.py | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ecf3302d8..3dc295563 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -54,6 +54,9 @@ class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) _params: Dict = {} + # Additional headers - added to the ccxt object + _headers: Dict = {} + # Dict to specify which options each exchange implements # This defines defaults, which can be selectively overridden by subclasses using _ft_has # or by specifying them in the configuration. @@ -169,7 +172,7 @@ class Exchange: asyncio.get_event_loop().run_until_complete(self._api_async.close()) def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, - ccxt_kwargs: dict = None) -> ccxt.Exchange: + ccxt_kwargs: Dict = {}) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -188,6 +191,10 @@ class Exchange: } if ccxt_kwargs: logger.info('Applying additional ccxt config: %s', ccxt_kwargs) + if self._headers: + # Inject static headers after the above output to not confuse users. + ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs) + if ccxt_kwargs: ex_config.update(ccxt_kwargs) try: diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 42da5dddc..144063c07 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -108,6 +108,13 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog): assert hasattr(ex._api_async, 'TestKWARG') assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) assert log_has(asynclogmsg, caplog) + # Test additional headers case + Exchange._headers = {'hello': 'world'} + ex = Exchange(conf) + + assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) + assert ex._api.headers == {'hello': 'world'} + Exchange._headers = {} def test_destroy(default_conf, mocker, caplog): From 19ad1654836992365af7536189095662205d768a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:48:53 +0200 Subject: [PATCH 02/32] Add time_in_force for kucoin --- docs/configuration.md | 4 ++-- docs/exchanges.md | 5 +++++ freqtrade/exchange/binance.py | 1 + freqtrade/exchange/exchange.py | 4 +++- freqtrade/exchange/kucoin.py | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 09198e019..6ccea4c73 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -444,8 +444,8 @@ The possible values are: `gtc` (default), `fok` or `ioc`. ``` !!! Warning - This is ongoing work. For now, it is supported only for binance. - Please don't change the default value unless you know what you are doing and have researched the impact of using different values. + This is ongoing work. For now, it is supported only for binance and kucoin. + Please don't change the default value unless you know what you are doing and have researched the impact of using different values for your particular exchange. ### Exchange configuration diff --git a/docs/exchanges.md b/docs/exchanges.md index 5f54a524e..81ad670a3 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -4,6 +4,9 @@ This page combines common gotchas and informations which are exchange-specific a ## Binance +Binance supports [time_in_force](configuration.md#understand-order_time_in_force). + + !!! Tip "Stoploss on Exchange" Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. @@ -115,6 +118,8 @@ Kucoin requires a passphrase for each api key, you will therefore need to add th "password": "your_exchange_api_key_password", ``` +Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force). + ### Kucoin Blacklists For Kucoin, please add `"KCS/"` to your blacklist to avoid issues. diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 0c470cb24..189f5f481 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -18,6 +18,7 @@ class Binance(Exchange): _ft_has: Dict = { "stoploss_on_exchange": True, "order_time_in_force": ['gtc', 'fok', 'ioc'], + "time_in_force_parameter": "timeInForce", "ohlcv_candle_limit": 1000, "trades_pagination": "id", "trades_pagination_arg": "fromId", diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 3dc295563..80f20b17e 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -63,6 +63,7 @@ class Exchange: _ft_has_default: Dict = { "stoploss_on_exchange": False, "order_time_in_force": ["gtc"], + "time_in_force_parameter": "timeInForce", "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, @@ -723,7 +724,8 @@ class Exchange: params = self._params.copy() if time_in_force != 'gtc' and ordertype != 'market': - params.update({'timeInForce': time_in_force}) + param = self._ft_has.get('time_in_force_parameter', '') + params.update({param: time_in_force}) try: # Set the precision for amount and price(rate) as accepted by the exchange diff --git a/freqtrade/exchange/kucoin.py b/freqtrade/exchange/kucoin.py index 22886a1d8..5d818f6a2 100644 --- a/freqtrade/exchange/kucoin.py +++ b/freqtrade/exchange/kucoin.py @@ -21,4 +21,6 @@ class Kucoin(Exchange): _ft_has: Dict = { "l2_limit_range": [20, 100], "l2_limit_range_required": False, + "order_time_in_force": ['gtc', 'fok', 'ioc'], + "time_in_force_parameter": "timeInForce", } From e64ccd8fc111e6d5eeeb193509317f0be00f1d80 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:54:15 +0200 Subject: [PATCH 03/32] Add new_config section for kucoin --- freqtrade/commands/build_config_commands.py | 8 ++++++++ .../templates/subtemplates/exchange_kucoin.j2 | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 freqtrade/templates/subtemplates/exchange_kucoin.j2 diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 1fe90e83a..780ad4161 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -105,6 +105,8 @@ def ask_user_config() -> Dict[str, Any]: "bittrex", "kraken", "ftx", + "kucoin", + "gateio", Separator(), "other", ], @@ -128,6 +130,12 @@ def ask_user_config() -> Dict[str, Any]: "message": "Insert Exchange Secret", "when": lambda x: not x['dry_run'] }, + { + "type": "password", + "name": "exchange_key_password", + "message": "Insert Exchange API Key password", + "when": lambda x: not x['dry_run'] and x['exchange_name'] == 'kucoin' + }, { "type": "confirm", "name": "telegram", diff --git a/freqtrade/templates/subtemplates/exchange_kucoin.j2 b/freqtrade/templates/subtemplates/exchange_kucoin.j2 new file mode 100644 index 000000000..f9dfff663 --- /dev/null +++ b/freqtrade/templates/subtemplates/exchange_kucoin.j2 @@ -0,0 +1,18 @@ +"exchange": { + "name": "{{ exchange_name | lower }}", + "key": "{{ exchange_key }}", + "secret": "{{ exchange_secret }}", + "password": "{{ exchange_key_password }}", + "ccxt_config": { + "enableRateLimit": true + "rateLimit": 200 + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 200 + }, + "pair_whitelist": [ + ], + "pair_blacklist": [ + ] +} From 68f13173bcaf4d88c2835abc66f9d0b92bf7ce7e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:59:08 +0200 Subject: [PATCH 04/32] Update new-config templates to use USDT by default --- freqtrade/commands/build_config_commands.py | 4 ++-- .../subtemplates/exchange_binance.j2 | 15 ------------- .../subtemplates/exchange_bittrex.j2 | 10 --------- .../templates/subtemplates/exchange_kraken.j2 | 22 ++----------------- 4 files changed, 4 insertions(+), 47 deletions(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 780ad4161..faa8a98f4 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -61,13 +61,13 @@ def ask_user_config() -> Dict[str, Any]: "type": "text", "name": "stake_currency", "message": "Please insert your stake currency:", - "default": 'BTC', + "default": 'USDT', }, { "type": "text", "name": "stake_amount", "message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):", - "default": "0.01", + "default": "100", "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val), "filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"' if val == UNLIMITED_STAKE_AMOUNT diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 38ba4fa5c..217002a7c 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -8,21 +8,6 @@ "rateLimit": 200 }, "pair_whitelist": [ - "ALGO/BTC", - "ATOM/BTC", - "BAT/BTC", - "BCH/BTC", - "BRD/BTC", - "EOS/BTC", - "ETH/BTC", - "IOTA/BTC", - "LINK/BTC", - "LTC/BTC", - "NEO/BTC", - "NXS/BTC", - "XMR/BTC", - "XRP/BTC", - "XTZ/BTC" ], "pair_blacklist": [ "BNB/BTC", diff --git a/freqtrade/templates/subtemplates/exchange_bittrex.j2 b/freqtrade/templates/subtemplates/exchange_bittrex.j2 index 7b27318ca..0394790ce 100644 --- a/freqtrade/templates/subtemplates/exchange_bittrex.j2 +++ b/freqtrade/templates/subtemplates/exchange_bittrex.j2 @@ -15,16 +15,6 @@ "rateLimit": 500 }, "pair_whitelist": [ - "ETH/BTC", - "LTC/BTC", - "ETC/BTC", - "DASH/BTC", - "ZEC/BTC", - "XLM/BTC", - "XRP/BTC", - "TRX/BTC", - "ADA/BTC", - "XMR/BTC" ], "pair_blacklist": [ ] diff --git a/freqtrade/templates/subtemplates/exchange_kraken.j2 b/freqtrade/templates/subtemplates/exchange_kraken.j2 index 7139a0830..4d0e4c1ff 100644 --- a/freqtrade/templates/subtemplates/exchange_kraken.j2 +++ b/freqtrade/templates/subtemplates/exchange_kraken.j2 @@ -7,28 +7,10 @@ "ccxt_async_config": { "enableRateLimit": true, "rateLimit": 1000 + // Enable the below for downoading data. + //"rateLimit": 3100 }, "pair_whitelist": [ - "ADA/EUR", - "ATOM/EUR", - "BAT/EUR", - "BCH/EUR", - "BTC/EUR", - "DAI/EUR", - "DASH/EUR", - "EOS/EUR", - "ETC/EUR", - "ETH/EUR", - "LINK/EUR", - "LTC/EUR", - "QTUM/EUR", - "REP/EUR", - "WAVES/EUR", - "XLM/EUR", - "XMR/EUR", - "XRP/EUR", - "XTZ/EUR", - "ZEC/EUR" ], "pair_blacklist": [ From c489e6825c3cc0affeb46e2a2f1f78d119e933f5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 09:18:15 +0200 Subject: [PATCH 05/32] Simplify binance blacklist --- .../templates/subtemplates/exchange_binance.j2 | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 217002a7c..3022464c7 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -10,17 +10,6 @@ "pair_whitelist": [ ], "pair_blacklist": [ - "BNB/BTC", - "BNB/BUSD", - "BNB/ETH", - "BNB/EUR", - "BNB/NGN", - "BNB/PAX", - "BNB/RUB", - "BNB/TRY", - "BNB/TUSD", - "BNB/USDC", - "BNB/USDS", - "BNB/USDT" + "BNB/.*", ] } From b4130dfabbdd0a68119080f33c990b34d4116d77 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 09:23:26 +0200 Subject: [PATCH 06/32] Use volumePairlist instead of staticPairlist in generated config --- freqtrade/templates/base_config.json.j2 | 9 ++++++++- freqtrade/templates/subtemplates/exchange_binance.j2 | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index a5782f7cd..68eebdbd4 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -1,3 +1,10 @@ +{%set volume_pairlist = '{ + "method": "VolumePairList", + "number_assets": 20, + "sort_key": "quoteVolume", + "min_value": 0, + "refresh_period": 1800 + }' %} { "max_open_trades": {{ max_open_trades }}, "stake_currency": "{{ stake_currency }}", @@ -29,7 +36,7 @@ }, {{ exchange | indent(4) }}, "pairlists": [ - {"method": "StaticPairList"} + {{ '{"method": "StaticPairList"}' if exchange_name == 'bittrex' else volume_pairlist }} ], "edge": { "enabled": false, diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 3022464c7..de58b6f72 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -10,6 +10,6 @@ "pair_whitelist": [ ], "pair_blacklist": [ - "BNB/.*", + "BNB/.*" ] } From 2f92838c39639e4770b95258ca0357d4e69c7d40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 16:18:47 +0200 Subject: [PATCH 07/32] Properly close parenteses in exchange doc --- docs/exchanges.md | 5 ++++- mkdocs.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 81ad670a3..42a850acd 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -6,7 +6,6 @@ This page combines common gotchas and informations which are exchange-specific a Binance supports [time_in_force](configuration.md#understand-order_time_in_force). - !!! Tip "Stoploss on Exchange" Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. @@ -116,6 +115,8 @@ Kucoin requires a passphrase for each api key, you will therefore need to add th "key": "your_exchange_key", "secret": "your_exchange_secret", "password": "your_exchange_api_key_password", + // ... +} ``` Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force). @@ -163,6 +164,8 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t "order_time_in_force": ["gtc", "fok"], "ohlcv_candle_limit": 200 } + //... +} ``` !!! Warning diff --git a/mkdocs.yml b/mkdocs.yml index 854939ca0..05156168f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,10 +23,10 @@ nav: - Hyperopt: hyperopt.md - Utility Sub-commands: utils.md - Plotting: plotting.md + - Exchange-specific Notes: exchanges.md - Data Analysis: - Jupyter Notebooks: data-analysis.md - Strategy analysis: strategy_analysis_example.md - - Exchange-specific Notes: exchanges.md - Advanced Topics: - Advanced Post-installation Tasks: advanced-setup.md - Edge Positioning: edge.md From 103a8e827ea7e280b93d806d355ca1157ed55692 Mon Sep 17 00:00:00 2001 From: Rikj000 Date: Fri, 3 Sep 2021 16:37:36 +0200 Subject: [PATCH 08/32] =?UTF-8?q?=E2=9A=A1=20`setup.sh`=20-=20Use=20`build?= =?UTF-8?q?=5Fhelpers/install=5Fta-lib.sh`=20for=20TA-Lib=20installation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_helpers/install_ta-lib.sh | 2 +- setup.sh | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index dd87cf105..590b7d961 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -13,7 +13,7 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then && ./configure --prefix=${INSTALL_LOC}/ \ && make -j$(nproc) \ && which sudo && sudo make install || make install \ - && cd .. + && cd .. && rm -rf ./ta-lib/ else echo "TA-lib already installed, skipping installation" fi diff --git a/setup.sh b/setup.sh index e5f81578d..217500569 100755 --- a/setup.sh +++ b/setup.sh @@ -95,19 +95,7 @@ function install_talib() { return fi - cd build_helpers - tar zxvf ta-lib-0.4.0-src.tar.gz - cd ta-lib - sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h - ./configure --prefix=/usr/local - make - sudo make install - if [ -x "$(command -v apt-get)" ]; then - echo "Updating library path using ldconfig" - sudo ldconfig - fi - cd .. && rm -rf ./ta-lib/ - cd .. + cd build_helpers && ./install_ta-lib.sh && cd .. } function install_mac_newer_python_dependencies() { From 493fb3507316296f90f6c6384a1e84d366b82efa Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 22:05:40 +0200 Subject: [PATCH 09/32] Fix uvicorn not working properly on windows --- freqtrade/rpc/api_server/uvicorn_threaded.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/uvicorn_threaded.py b/freqtrade/rpc/api_server/uvicorn_threaded.py index b63999f51..79af659c7 100644 --- a/freqtrade/rpc/api_server/uvicorn_threaded.py +++ b/freqtrade/rpc/api_server/uvicorn_threaded.py @@ -5,6 +5,20 @@ import time import uvicorn +def asyncio_setup() -> None: # pragma: no cover + # Set eventloop for win32 setups + # Reverts a change done in uvicorn 0.15.0 - which now sets the eventloop + # via policy. + import sys + + if sys.version_info >= (3, 8) and sys.platform == "win32": + import asyncio + import selectors + selector = selectors.SelectSelector() + loop = asyncio.SelectorEventLoop(selector) + asyncio.set_event_loop(loop) + + class UvicornServer(uvicorn.Server): """ Multithreaded server - as found in https://github.com/encode/uvicorn/issues/742 @@ -28,7 +42,7 @@ class UvicornServer(uvicorn.Server): try: import uvloop # noqa except ImportError: # pragma: no cover - from uvicorn.loops.asyncio import asyncio_setup + asyncio_setup() else: asyncio.set_event_loop(uvloop.new_event_loop()) From eb0362c29ef7f125db4fda5590485c4c9f002f7b Mon Sep 17 00:00:00 2001 From: Rik Helsen Date: Fri, 3 Sep 2021 23:52:40 +0200 Subject: [PATCH 10/32] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20=EF=B8=8F`install=5F?= =?UTF-8?q?ta-lib.sh`=20-=20Run=20`ldconfig`=20after=20`make=20install`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_helpers/install_ta-lib.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 590b7d961..d12b16364 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -12,9 +12,12 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \ && ./configure --prefix=${INSTALL_LOC}/ \ && make -j$(nproc) \ - && which sudo && sudo make install || make install \ - && cd .. && rm -rf ./ta-lib/ + && which sudo && sudo make install || make install + if [ -x "$(command -v apt-get)" ]; then + echo "Updating library path using ldconfig" + sudo ldconfig + fi + cd .. && rm -rf ./ta-lib/ else echo "TA-lib already installed, skipping installation" fi -# && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \ From 4e049f65f227c7e2bf103db5cd9f97988c7a2250 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 09:14:44 +0200 Subject: [PATCH 11/32] Exclude some parts from coverage that can't really be tested --- freqtrade/__init__.py | 2 +- freqtrade/loggers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index e96e7f530..2747efc96 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -22,7 +22,7 @@ if __version__ == 'develop': # subprocess.check_output( # ['git', 'log', '--format="%h"', '-n 1'], # stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"') - except Exception: + except Exception: # pragma: no cover # git not available, ignore try: # Try Fallback to freqtrade_commit file (created by CI while building docker image) diff --git a/freqtrade/loggers.py b/freqtrade/loggers.py index fbb05d879..5c5831695 100644 --- a/freqtrade/loggers.py +++ b/freqtrade/loggers.py @@ -87,7 +87,7 @@ def setup_logging(config: Dict[str, Any]) -> None: # syslog config. The messages should be equal for this. handler_sl.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s')) logging.root.addHandler(handler_sl) - elif s[0] == 'journald': + elif s[0] == 'journald': # pragma: no cover try: from systemd.journal import JournaldLogHandler except ImportError: From 2173ff0133db834cbda72a2d35fa6a07e5d574b9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 09:16:19 +0200 Subject: [PATCH 12/32] Update PR template to not link to issues in changelog --- .github/PULL_REQUEST_TEMPLATE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 20ef27f0f..7c0655b20 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,14 +2,16 @@ Thank you for sending your pull request. But first, have you included unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) ## Summary + Explain in one sentence the goal of this PR Solve the issue: #___ ## Quick changelog -- -- +- +- ## What's new? + *Explain in details what this PR solve or improve. You can include visuals.* From a8f28ffb11a48ce1a8bbb26b209bfd9fa34705d0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 15:34:57 +0200 Subject: [PATCH 13/32] Increase test coverage --- tests/strategy/test_interface.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 128599668..250dcf63d 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -735,11 +735,16 @@ def test_auto_hyperopt_interface(default_conf): PairLocks.timeframe = default_conf['timeframe'] strategy = StrategyResolver.load_strategy(default_conf) + with pytest.raises(OperationalException): + next(strategy.enumerate_parameters('deadBeef')) + assert strategy.buy_rsi.value == strategy.buy_params['buy_rsi'] # PlusDI is NOT in the buy-params, so default should be used assert strategy.buy_plusdi.value == 0.5 assert strategy.sell_rsi.value == strategy.sell_params['sell_rsi'] + assert repr(strategy.sell_rsi) == 'IntParameter(74)' + # Parameter is disabled - so value from sell_param dict will NOT be used. assert strategy.sell_minusdi.value == 0.5 all_params = strategy.detect_all_parameters() From c519ecf8df2fae0e8d789c5abacb6662620d73e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 15:40:21 +0200 Subject: [PATCH 14/32] Exclude more untestable sections from coverage --- freqtrade/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 2fd3d32bb..6593fbcb6 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -9,7 +9,7 @@ from typing import Any, List # check min. python version -if sys.version_info < (3, 7): +if sys.version_info < (3, 7): # pragma: no cover sys.exit("Freqtrade requires Python version >= 3.7") from freqtrade.commands import Arguments @@ -46,7 +46,7 @@ def main(sysargv: List[str] = None) -> None: "`freqtrade --help` or `freqtrade --help`." ) - except SystemExit as e: + except SystemExit as e: # pragma: no cover return_code = e except KeyboardInterrupt: logger.info('SIGINT received, aborting ...') @@ -60,5 +60,5 @@ def main(sysargv: List[str] = None) -> None: sys.exit(return_code) -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover main() From 1d24d3d5ee68624f2706e2a45ea939c87fa0b3ab Mon Sep 17 00:00:00 2001 From: lenik terenin Date: Sun, 5 Sep 2021 22:41:58 +0900 Subject: [PATCH 15/32] case insensitive blacklist Allow "btc/usdt" pairs in blacklist to match to "BTC/USDT" pairs that come from the exchange. --- freqtrade/plugins/pairlist/pairlist_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/pairlist_helpers.py b/freqtrade/plugins/pairlist/pairlist_helpers.py index 924bfb293..1de27fcbd 100644 --- a/freqtrade/plugins/pairlist/pairlist_helpers.py +++ b/freqtrade/plugins/pairlist/pairlist_helpers.py @@ -17,7 +17,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str], if keep_invalid: for pair_wc in wildcardpl: try: - comp = re.compile(pair_wc) + comp = re.compile(pair_wc, re.IGNORECASE) result_partial = [ pair for pair in available_pairs if re.fullmatch(comp, pair) ] @@ -33,7 +33,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str], else: for pair_wc in wildcardpl: try: - comp = re.compile(pair_wc) + comp = re.compile(pair_wc, re.IGNORECASE) result += [ pair for pair in available_pairs if re.fullmatch(comp, pair) ] From 4daa4b9e63960da073e8e2d5f7e00906d4117cf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:10 +0000 Subject: [PATCH 16/32] Bump pytest from 6.2.4 to 6.2.5 Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.4 to 6.2.5. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.4...6.2.5) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 67ee0035b..34d5607f3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==3.9.2 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.4.1 mypy==0.910 -pytest==6.2.4 +pytest==6.2.5 pytest-asyncio==0.15.1 pytest-cov==2.12.1 pytest-mock==3.6.1 From 771193cbe47b0bd812e87662bdc141b911e316a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:19 +0000 Subject: [PATCH 17/32] Bump plotly from 5.3.0 to 5.3.1 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.3.0 to 5.3.1. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.3.0...v5.3.1) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index 62836a729..8e17232b0 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.3.0 +plotly==5.3.1 From 44f8d7abf2ba21c3eb0e1c0f5804d8bafb9d2e31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:23 +0000 Subject: [PATCH 18/32] Bump ccxt from 1.55.56 to 1.55.83 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.55.56 to 1.55.83. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.55.56...1.55.83) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f77edddfe..e2bed0f9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.56 +ccxt==1.55.83 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From 765e72715bf739504e53140cd29eb24ef40dda44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:28 +0000 Subject: [PATCH 19/32] Bump mkdocs-material from 7.2.5 to 7.2.6 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.5 to 7.2.6. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.5...7.2.6) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index d820c9412..9927740c2 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.5 +mkdocs-material==7.2.6 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From a04875eb55beab2f9d86534434daf2465b9f0d78 Mon Sep 17 00:00:00 2001 From: EnzovdWetering <36194995+EnzovdWetering@users.noreply.github.com> Date: Mon, 6 Sep 2021 17:53:44 +0200 Subject: [PATCH 20/32] Update edge.md Typo fix --- docs/edge.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/edge.md b/docs/edge.md index 237ff36f6..4402d767f 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -3,7 +3,7 @@ The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss. !!! Warning - WHen using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data. + When using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data. !!! Note `Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file. From 6bd495a32a58b6da236f53da2d166f683a21cfff Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 6 Sep 2021 19:53:32 +0200 Subject: [PATCH 21/32] Fix 0Exception error happens when wrong stake-currency is selected and /profit is called --- freqtrade/rpc/rpc.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 95a37452b..ca2e84e48 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -403,8 +403,11 @@ class RPC: # Doing the sum is not right - overall profit needs to be based on initial capital profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0 starting_balance = self._freqtrade.wallets.get_starting_balance() - profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance - profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance + profit_closed_ratio_fromstart = 0 + profit_all_ratio_fromstart = 0 + if starting_balance: + profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance + profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance profit_all_fiat = self._fiat_converter.convert_amount( profit_all_coin_sum, From 10d0987f49b8179f840895882bccebb732098192 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 6 Sep 2021 19:54:34 +0200 Subject: [PATCH 22/32] Fix docs for custom hyperopt space --- docs/advanced-hyperopt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 8f233438b..4a2bafd2e 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -80,7 +80,7 @@ To override a pre-defined space (`roi_space`, `generate_roi_table`, `stoploss_sp class MyAwesomeStrategy(IStrategy): class HyperOpt: # Define a custom stoploss space. - def stoploss_space(self): + def stoploss_space(): return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')] ``` From 880474594e5357eea9b47b8d1c8449a968bce4c3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 7 Sep 2021 06:51:31 +0200 Subject: [PATCH 23/32] have ftuser use `/bin/bash` in dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4c4722452..f7e26efe3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN mkdir /freqtrade \ && apt-get update \ && apt-get -y install sudo libatlas3-base curl sqlite3 libhdf5-serial-dev \ && apt-get clean \ - && useradd -u 1000 -G sudo -U -m ftuser \ + && useradd -u 1000 -G sudo -U -m -s /bin/bash ftuser \ && chown ftuser:ftuser /freqtrade \ # Allow sudoers && echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers From 79ca6135a2cb599de9fa4fde397226d63dda2832 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Tue, 7 Sep 2021 21:53:38 -0600 Subject: [PATCH 24/32] added caplog clears to freqtradebot tests --- tests/test_freqtradebot.py | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 75b67e59c..ddd031f77 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -107,6 +107,7 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) + caplog.clear() def test_order_dict_live(default_conf, mocker, caplog) -> None: @@ -140,6 +141,7 @@ def test_order_dict_live(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) + caplog.clear() def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: @@ -415,6 +417,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord assert freqtrade.create_trade('ETH/BTC') assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) + caplog.clear() def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -478,6 +481,7 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) + caplog.clear() def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, @@ -518,11 +522,13 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, # 0 trades, but it's not because of pairlock. assert n == 0 assert not log_has_re(message, caplog) + caplog.clear() PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because') n = freqtrade.enter_positions() assert n == 0 assert log_has_re(message, caplog) + caplog.clear() def test_create_trade_no_signal(default_conf, fee, mocker) -> None: @@ -1086,6 +1092,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog) assert trade.stoploss_order_id is None assert trade.is_open is False + caplog.clear() mocker.patch( 'freqtrade.exchange.Binance.stoploss', @@ -1115,6 +1122,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) assert freqtrade.handle_stoploss_on_exchange(trade) is False assert stoploss.call_count == 0 + caplog.clear() def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, @@ -1154,6 +1162,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog) assert trade.stoploss_order_id is None assert trade.is_open is True + caplog.clear() def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, @@ -1202,6 +1211,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert rpc_mock.call_count == 2 assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' + caplog.clear() def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, @@ -1428,6 +1438,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -1662,6 +1673,7 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) # create_trade should be called once for every pair in the whitelist. assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) + caplog.clear() def test_enter_positions_exception(mocker, default_conf, caplog) -> None: @@ -1701,6 +1713,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: # test amount modified by fee-logic n = freqtrade.exit_positions(trades) assert n == 0 + caplog.clear() def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1721,6 +1734,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) n = freqtrade.exit_positions(trades) assert n == 0 assert log_has('Unable to sell trade ETH/BTC: ', caplog) + caplog.clear() def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1743,10 +1757,12 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No ) assert not freqtrade.update_trade_state(trade, None) assert log_has_re(r'Orderid for trade .* is empty.', caplog) + caplog.clear() # Add datetime explicitly since sqlalchemy defaults apply only once written to database freqtrade.update_trade_state(trade, '123') # Test amount not modified by fee-logic assert not log_has_re(r'Applying fee to .*', caplog) + caplog.clear() assert trade.open_order_id is None assert trade.amount == limit_buy_order['amount'] @@ -1764,6 +1780,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No freqtrade.update_trade_state(trade, '123') assert log_has_re('Found open order for.*', caplog) + caplog.clear() def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, @@ -1814,6 +1831,7 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_ assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] assert log_has_re(r'Applying fee on amount for .*', caplog) + caplog.clear() def test_update_trade_state_exception(mocker, default_conf, @@ -1832,6 +1850,7 @@ def test_update_trade_state_exception(mocker, default_conf, ) freqtrade.update_trade_state(trade, trade.open_order_id) assert log_has('Could not update trade amount: ', caplog) + caplog.clear() def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: @@ -1848,6 +1867,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None freqtrade.update_trade_state(trade, trade.open_order_id) assert grm_mock.call_count == 0 assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) + caplog.clear() def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open, @@ -2016,6 +2036,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) + caplog.clear() def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, @@ -2048,6 +2069,7 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) + caplog.clear() def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order, @@ -2088,6 +2110,7 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog): assert log_has_re(r'Strategy caused the following exception.*', caplog) assert ftbot.strategy.bot_loop_start.call_count == 1 assert ftbot.strategy.analyze.call_count == 1 + caplog.clear() def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2202,6 +2225,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o nb_trades = len(trades) assert nb_trades == 0 assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) + caplog.clear() def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2336,6 +2360,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert rpc_mock.call_count == 1 assert open_trade.is_open is True assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog) + caplog.clear() def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, @@ -2404,6 +2429,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap assert trades[0].open_order_id is None assert trades[0].fee_updated('buy') assert pytest.approx(trades[0].fee_open) == 0.001 + caplog.clear() def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee, @@ -2444,6 +2470,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, limit_buy_order_old_partial['remaining']) assert trades[0].open_order_id is None assert trades[0].fee_open == fee() + caplog.clear() def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None: @@ -2472,6 +2499,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog) + caplog.clear() def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: @@ -2515,6 +2543,7 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) + caplog.clear() @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], @@ -2536,6 +2565,7 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 + caplog.clear() @pytest.mark.parametrize('cancelorder', [ @@ -2905,6 +2935,7 @@ def test_execute_trade_exit_sloe_cancel_exception( sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) + caplog.clear() def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, @@ -3300,6 +3331,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert freqtrade.handle_trade(trade) is True assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert trade.amount != amnt + caplog.clear() def test__safe_sell_amount(default_conf, fee, caplog, mocker): @@ -3330,6 +3362,7 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 + caplog.clear() def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): @@ -3386,6 +3419,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo freqtrade.enter_positions() assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) + caplog.clear() def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3477,6 +3511,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, " "initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value + caplog.clear() def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3524,6 +3559,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000138501 + caplog.clear() mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3537,6 +3573,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) + caplog.clear() def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3584,6 +3621,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000138501 + caplog.clear() mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3598,6 +3636,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value + caplog.clear() def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3648,6 +3687,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000098910 + caplog.clear() # price rises above the offset (rises 12% when the offset is 5.5%) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', @@ -3661,6 +3701,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000117705 + caplog.clear() def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3722,6 +3763,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', caplog) + caplog.clear() def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee, @@ -3747,6 +3789,7 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe assert walletmock.call_count == 1 assert log_has_re(r'Fee amount for Trade.* was in base currency ' '- Eating Fee 0.008 into dust', caplog) + caplog.clear() def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee): @@ -3769,6 +3812,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', caplog) + caplog.clear() def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -3862,6 +3906,7 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None + caplog.clear() def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, @@ -3897,6 +3942,7 @@ def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None + caplog.clear() def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, @@ -3925,6 +3971,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', caplog) + caplog.clear() def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -4168,6 +4215,7 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None with pytest.raises(PricingError): freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) + caplog.clear() def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: @@ -4238,6 +4286,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.handle_trade(trade) assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*', caplog) + caplog.clear() def test_startup_state(default_conf, mocker): @@ -4296,6 +4345,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4339,6 +4389,7 @@ def test_update_open_orders(mocker, default_conf, fee, caplog): freqtrade.update_open_orders() assert not log_has_re(r"Error updating Order .*", caplog) + caplog.clear() freqtrade.config['dry_run'] = False freqtrade.update_open_orders() @@ -4454,6 +4505,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4591,6 +4643,7 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): freqtrade.refind_lost_order(trades[4]) assert log_has(f"Error updating {order['id']}.", caplog) + caplog.clear() def test_get_valid_price(mocker, default_conf) -> None: From aac05029e1d13936f89c10f0ed8ccdaec3561ed7 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:20:52 -0600 Subject: [PATCH 25/32] safe_sell_amount -> safe_exit_amount --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 259270483..0fc40cf45 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1039,7 +1039,7 @@ class FreqtradeBot(LoggingMixin): ) return reason - def _safe_sell_amount(self, pair: str, amount: float) -> float: + def _safe_exit_amount(self, pair: str, amount: float) -> float: """ Get sellable amount. Should be trade.amount - but will fall back to the available amount if necessary. @@ -1111,7 +1111,7 @@ class FreqtradeBot(LoggingMixin): # but we allow this value to be changed) order_type = self.strategy.order_types.get("forcesell", order_type) - amount = self._safe_sell_amount(trade.pair, trade.amount) + amount = self._safe_exit_amount(trade.pair, trade.amount) time_in_force = self.strategy.order_time_in_force['sell'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 75b67e59c..109cb01c2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3302,7 +3302,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert trade.amount != amnt -def test__safe_sell_amount(default_conf, fee, caplog, mocker): +def test__safe_exit_amount(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -3322,17 +3322,17 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): patch_get_signal(freqtrade) wallet_update.reset_mock() - assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 caplog.clear() wallet_update.reset_mock() - assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet + assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 -def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): +def test__safe_exit_amount_error(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -3350,7 +3350,7 @@ def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) with pytest.raises(DependencyException, match=r"Not enough amount to sell."): - assert freqtrade._safe_sell_amount(trade.pair, trade.amount) + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: From be93c75e44fcaa1df2094b1d6200b0c884abf596 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:14:16 -0600 Subject: [PATCH 26/32] reupdate_buy_order_fees -> reupdate_enter_order_fees --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0fc40cf45..0f16162cd 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -296,9 +296,9 @@ class FreqtradeBot(LoggingMixin): if sell_order: self.refind_lost_order(trade) else: - self.reupdate_buy_order_fees(trade) + self.reupdate_enter_order_fees(trade) - def reupdate_buy_order_fees(self, trade: Trade): + def reupdate_enter_order_fees(self, trade: Trade): """ Get buy order from database, and try to reupdate. Handles trades where the initial fee-update did not work. diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 109cb01c2..88ed11558 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4420,14 +4420,14 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): +def test_reupdate_enter_order_fees(mocker, default_conf, fee, caplog): freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state') create_mock_trades(fee) trades = Trade.get_trades().all() - freqtrade.reupdate_buy_order_fees(trades[0]) + freqtrade.reupdate_enter_order_fees(trades[0]) assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 1 assert mock_uts.call_args_list[0][0][0] == trades[0] @@ -4450,7 +4450,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): ) Trade.query.session.add(trade) - freqtrade.reupdate_buy_order_fees(trade) + freqtrade.reupdate_enter_order_fees(trade) assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) @@ -4460,7 +4460,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): def test_handle_insufficient_funds(mocker, default_conf, fee): freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order') - mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_buy_order_fees') + mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_enter_order_fees') create_mock_trades(fee) trades = Trade.get_trades().all() From e0092a85e9769eeb7e73bbb6f6018dbd494ea1a0 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:53:09 -0600 Subject: [PATCH 27/32] handle_cancel_buy/sell -> handle_cancel_enter/exit --- freqtrade/freqtradebot.py | 12 +++++------ freqtrade/rpc/rpc.py | 4 ++-- tests/test_freqtradebot.py | 44 +++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0f16162cd..bf07ac0bc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -906,7 +906,7 @@ class FreqtradeBot(LoggingMixin): default_retval=False)(pair=trade.pair, trade=trade, order=order))): - self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['TIMEOUT']) + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and ( fully_cancelled @@ -915,7 +915,7 @@ class FreqtradeBot(LoggingMixin): default_retval=False)(pair=trade.pair, trade=trade, order=order))): - self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['TIMEOUT']) + self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT']) def cancel_all_open_orders(self) -> None: """ @@ -931,13 +931,13 @@ class FreqtradeBot(LoggingMixin): continue if order['side'] == 'buy': - self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) elif order['side'] == 'sell': - self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) + self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) Trade.commit() - def handle_cancel_buy(self, trade: Trade, order: Dict, reason: str) -> bool: + def handle_cancel_enter(self, trade: Trade, order: Dict, reason: str) -> bool: """ Buy cancel - cancel order :return: True if order was fully cancelled @@ -998,7 +998,7 @@ class FreqtradeBot(LoggingMixin): reason=reason) return was_trade_fully_canceled - def handle_cancel_sell(self, trade: Trade, order: Dict, reason: str) -> str: + def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str: """ Sell cancel - cancel order and update trade :return: Reason for cancel diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index ca2e84e48..caf7345a2 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -548,12 +548,12 @@ class RPC: order = self._freqtrade.exchange.fetch_order(trade.open_order_id, trade.pair) if order['side'] == 'buy': - fully_canceled = self._freqtrade.handle_cancel_buy( + fully_canceled = self._freqtrade.handle_cancel_enter( trade, order, CANCEL_REASON['FORCE_SELL']) if order['side'] == 'sell': # Cancel order - so it is placed anew with a fresh price. - self._freqtrade.handle_cancel_sell(trade, order, CANCEL_REASON['FORCE_SELL']) + self._freqtrade.handle_cancel_exit(trade, order, CANCEL_REASON['FORCE_SELL']) if not fully_canceled: # Get current rate and execute sell diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 88ed11558..1b5381264 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2453,8 +2453,8 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', - handle_cancel_buy=MagicMock(), - handle_cancel_sell=MagicMock(), + handle_cancel_enter=MagicMock(), + handle_cancel_exit=MagicMock(), ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -2474,7 +2474,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke caplog) -def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: +def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_buy_order = deepcopy(limit_buy_order) @@ -2493,34 +2493,34 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() caplog.clear() limit_buy_order['filled'] = 0.01 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 0 assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog) caplog.clear() cancel_order_mock.reset_mock() limit_buy_order['filled'] = 2 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 # Order remained open for some reason (cancel failed) cancel_buy_order['status'] = 'open' cancel_order_mock = MagicMock(return_value=cancel_buy_order) mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], indirect=['limit_buy_order_canceled_empty']) -def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, - limit_buy_order_canceled_empty) -> None: +def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, + limit_buy_order_canceled_empty) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = mocker.patch( @@ -2532,7 +2532,7 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, reason = CANCEL_REASON['TIMEOUT'] trade = MagicMock() trade.pair = 'LTC/ETH' - assert freqtrade.handle_cancel_buy(trade, limit_buy_order_canceled_empty, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason) assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 @@ -2544,8 +2544,8 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, 'String Return value', 123 ]) -def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order, - cancelorder) -> None: +def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, + cancelorder) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock(return_value=cancelorder) @@ -2563,16 +2563,16 @@ def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order, limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() limit_buy_order['filled'] = 1.0 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 -def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None: +def test_handle_cancel_exit_limit(mocker, default_conf, fee) -> None: send_msg_mock = patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2598,26 +2598,26 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None: 'amount': 1, 'status': "open"} reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_sell(trade, order, reason) + assert freqtrade.handle_cancel_exit(trade, order, reason) assert cancel_order_mock.call_count == 1 assert send_msg_mock.call_count == 1 send_msg_mock.reset_mock() order['amount'] = 2 - assert freqtrade.handle_cancel_sell(trade, order, reason + assert freqtrade.handle_cancel_exit(trade, order, reason ) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] # Assert cancel_order was not called (callcount remains unchanged) assert cancel_order_mock.call_count == 1 assert send_msg_mock.call_count == 1 - assert freqtrade.handle_cancel_sell(trade, order, reason + assert freqtrade.handle_cancel_exit(trade, order, reason ) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] # Message should not be iterated again assert trade.sell_order_status == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] assert send_msg_mock.call_count == 1 -def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None: +def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch( @@ -2630,7 +2630,7 @@ def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None: order = {'remaining': 1, 'amount': 1, 'status': "open"} - assert freqtrade.handle_cancel_sell(trade, order, reason) == 'error cancelling order' + assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order' def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: @@ -4304,8 +4304,8 @@ def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limi mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=[ ExchangeError(), limit_sell_order, limit_buy_order, limit_sell_order]) - buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_buy') - sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_sell') + buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter') + sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit') freqtrade = get_patched_freqtradebot(mocker, default_conf) create_mock_trades(fee) From e1f846f22f28a9f76df1e8c47a79225ca2bc6e89 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:49:04 -0600 Subject: [PATCH 28/32] sell_lock -> exit_lock --- freqtrade/freqtradebot.py | 6 +++--- freqtrade/rpc/rpc.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bf07ac0bc..0a0b20f0c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -99,7 +99,7 @@ class FreqtradeBot(LoggingMixin): self.state = State[initial_state.upper()] if initial_state else State.STOPPED # Protect sell-logic from forcesell and vice versa - self._sell_lock = Lock() + self._exit_lock = Lock() LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) def notify_status(self, msg: str) -> None: @@ -166,14 +166,14 @@ class FreqtradeBot(LoggingMixin): self.strategy.analyze(self.active_pair_whitelist) - with self._sell_lock: + with self._exit_lock: # Check and handle any timed out open orders self.check_handle_timedout() # Protect from collisions with forcesell. # Without this, freqtrade my try to recreate stoploss_on_exchange orders # while selling is in process, since telegram messages arrive in an different thread. - with self._sell_lock: + with self._exit_lock: trades = Trade.get_open_trades() # First process current opened trades (positions) self.exit_positions(trades) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index caf7345a2..b7b1fe603 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -566,7 +566,7 @@ class RPC: if self._freqtrade.state != State.RUNNING: raise RPCException('trader is not running') - with self._freqtrade._sell_lock: + with self._freqtrade._exit_lock: if trade_id == 'all': # Execute sell for all open orders for trade in Trade.get_open_trades(): @@ -628,7 +628,7 @@ class RPC: Handler for delete . Delete the given trade and close eventually existing open orders. """ - with self._freqtrade._sell_lock: + with self._freqtrade._exit_lock: c_count = 0 trade = Trade.get_trades(trade_filter=[Trade.id == trade_id]).first() if not trade: From 362dc20406c0419246cbe459518636337567cf9b Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:45:55 -0600 Subject: [PATCH 29/32] notify_buy -> notify_enter, notify_sell -> notify_exit --- freqtrade/freqtradebot.py | 26 +++++++++++++------------- tests/test_freqtradebot.py | 6 +++--- tests/test_integration.py | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0a0b20f0c..b67ae9f00 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -590,11 +590,11 @@ class FreqtradeBot(LoggingMixin): # Updating wallets self.wallets.update() - self._notify_buy(trade, order_type) + self._notify_enter(trade, order_type) return True - def _notify_buy(self, trade: Trade, order_type: str) -> None: + def _notify_enter(self, trade: Trade, order_type: str) -> None: """ Sends rpc notification when a buy occurred. """ @@ -617,7 +617,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_buy_cancel(self, trade: Trade, order_type: str, reason: str) -> None: + def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str) -> None: """ Sends rpc notification when a buy cancel occurred. """ @@ -643,7 +643,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_buy_fill(self, trade: Trade) -> None: + def _notify_enter_fill(self, trade: Trade) -> None: msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_FILL, @@ -782,7 +782,7 @@ class FreqtradeBot(LoggingMixin): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_sell(trade, "stoploss") + self._notify_exit(trade, "stoploss") return True if trade.open_order_id or not trade.is_open: @@ -994,8 +994,8 @@ class FreqtradeBot(LoggingMixin): reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}" self.wallets.update() - self._notify_buy_cancel(trade, order_type=self.strategy.order_types['buy'], - reason=reason) + self._notify_enter_cancel(trade, order_type=self.strategy.order_types['buy'], + reason=reason) return was_trade_fully_canceled def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str: @@ -1032,7 +1032,7 @@ class FreqtradeBot(LoggingMixin): reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] self.wallets.update() - self._notify_sell_cancel( + self._notify_exit_cancel( trade, order_type=self.strategy.order_types['sell'], reason=reason @@ -1150,11 +1150,11 @@ class FreqtradeBot(LoggingMixin): self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_sell(trade, order_type) + self._notify_exit(trade, order_type) return True - def _notify_sell(self, trade: Trade, order_type: str, fill: bool = False) -> None: + def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False) -> None: """ Sends rpc notification when a sell occurred. """ @@ -1196,7 +1196,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_sell_cancel(self, trade: Trade, order_type: str, reason: str) -> None: + def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str) -> None: """ Sends rpc notification when a sell cancel occurred. """ @@ -1291,13 +1291,13 @@ class FreqtradeBot(LoggingMixin): # Updating wallets when order is closed if not trade.is_open: if not stoploss_order and not trade.open_order_id: - self._notify_sell(trade, '', True) + self._notify_exit(trade, '', True) self.protections.stop_per_pair(trade.pair) self.protections.global_stop() self.wallets.update() elif not trade.open_order_id: # Buy fill - self._notify_buy_fill(trade) + self._notify_enter_fill(trade) return False diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 1b5381264..26d06d53a 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2485,7 +2485,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> N mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) freqtrade = FreqtradeBot(default_conf) - freqtrade._notify_buy_cancel = MagicMock() + freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() trade.pair = 'LTC/USDT' @@ -2526,7 +2526,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, cancel_order_mock = mocker.patch( 'freqtrade.exchange.Exchange.cancel_order_with_result', return_value=limit_buy_order_canceled_empty) - nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_buy_cancel') + nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_enter_cancel') freqtrade = FreqtradeBot(default_conf) reason = CANCEL_REASON['TIMEOUT'] @@ -2555,7 +2555,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, ) freqtrade = FreqtradeBot(default_conf) - freqtrade._notify_buy_cancel = MagicMock() + freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() trade.pair = 'LTC/USDT' diff --git a/tests/test_integration.py b/tests/test_integration.py index 215927098..a3484d438 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -70,7 +70,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee, mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', create_stoploss_order=MagicMock(return_value=True), - _notify_sell=MagicMock(), + _notify_exit=MagicMock(), ) mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock) wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) @@ -154,7 +154,7 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', create_stoploss_order=MagicMock(return_value=True), - _notify_sell=MagicMock(), + _notify_exit=MagicMock(), ) should_sell_mock = MagicMock(side_effect=[ SellCheckTuple(sell_type=SellType.NONE), From a1c9a4d619f3dfe5f123e928487f5dcdf1d351fc Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:40:22 -0600 Subject: [PATCH 30/32] freqtradebot local name changes --- freqtrade/freqtradebot.py | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b67ae9f00..5800befba 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -476,21 +476,21 @@ class FreqtradeBot(LoggingMixin): time_in_force = self.strategy.order_time_in_force['buy'] if price: - buy_limit_requested = price + enter_limit_requested = price else: # Calculate price - proposed_buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") + proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=proposed_buy_rate)( + default_retval=proposed_enter_rate)( pair=pair, current_time=datetime.now(timezone.utc), - proposed_rate=proposed_buy_rate) + proposed_rate=proposed_enter_rate) - buy_limit_requested = self.get_valid_price(custom_entry_price, proposed_buy_rate) + enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) - if not buy_limit_requested: + if not enter_limit_requested: raise PricingError('Could not determine buy price.') - min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, buy_limit_requested, + min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested, self.strategy.stoploss) if not self.edge: @@ -498,7 +498,7 @@ class FreqtradeBot(LoggingMixin): stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), - current_rate=buy_limit_requested, proposed_stake=stake_amount, + current_rate=enter_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) @@ -508,27 +508,27 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: " f"{stake_amount} ...") - amount = stake_amount / buy_limit_requested + amount = stake_amount / enter_limit_requested order_type = self.strategy.order_types['buy'] if forcebuy: # Forcebuy can define a different ordertype order_type = self.strategy.order_types.get('forcebuy', order_type) if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( - pair=pair, order_type=order_type, amount=amount, rate=buy_limit_requested, + pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, time_in_force=time_in_force, current_time=datetime.now(timezone.utc)): logger.info(f"User requested abortion of buying {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) order = self.exchange.create_order(pair=pair, ordertype=order_type, side="buy", - amount=amount, rate=buy_limit_requested, + amount=amount, rate=enter_limit_requested, time_in_force=time_in_force) order_obj = Order.parse_from_ccxt_object(order, pair, 'buy') order_id = order['id'] order_status = order.get('status', None) # we assume the order is executed at the price requested - buy_limit_filled_price = buy_limit_requested + enter_limit_filled_price = enter_limit_requested amount_requested = amount if order_status == 'expired' or order_status == 'rejected': @@ -551,13 +551,13 @@ class FreqtradeBot(LoggingMixin): ) stake_amount = order['cost'] amount = safe_value_fallback(order, 'filled', 'amount') - buy_limit_filled_price = safe_value_fallback(order, 'average', 'price') + enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') # in case of FOK the order may be filled immediately and fully elif order_status == 'closed': stake_amount = order['cost'] amount = safe_value_fallback(order, 'filled', 'amount') - buy_limit_filled_price = safe_value_fallback(order, 'average', 'price') + enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') @@ -569,8 +569,8 @@ class FreqtradeBot(LoggingMixin): amount_requested=amount_requested, fee_open=fee, fee_close=fee, - open_rate=buy_limit_filled_price, - open_rate_requested=buy_limit_requested, + open_rate=enter_limit_filled_price, + open_rate_requested=enter_limit_requested, open_date=datetime.utcnow(), exchange=self.exchange.id, open_order_id=order_id, @@ -713,8 +713,8 @@ class FreqtradeBot(LoggingMixin): ) logger.debug('checking sell') - sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") - if self._check_and_execute_sell(trade, sell_rate, buy, sell): + exit_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") + if self._check_and_execute_exit(trade, exit_rate, buy, sell): return True logger.debug('Found no sell signal for %s.', trade) @@ -744,7 +744,7 @@ class FreqtradeBot(LoggingMixin): except InvalidOrderException as e: trade.stoploss_order_id = None logger.error(f'Unable to place a stoploss order on exchange. {e}') - logger.warning('Selling the trade forcefully') + logger.warning('Exiting the trade forcefully') self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple( sell_type=SellType.EMERGENCY_SELL)) @@ -851,19 +851,19 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") - def _check_and_execute_sell(self, trade: Trade, sell_rate: float, - buy: bool, sell: bool) -> bool: + def _check_and_execute_exit(self, trade: Trade, exit_rate: float, + enter: bool, exit_: bool) -> bool: """ - Check and execute sell + Check and execute exit """ should_sell = self.strategy.should_sell( - trade, sell_rate, datetime.now(timezone.utc), buy, sell, + trade, exit_rate, datetime.now(timezone.utc), buy, sell, force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 ) if should_sell.sell_flag: logger.info(f'Executing Sell for {trade.pair}. Reason: {should_sell.sell_type}') - self.execute_trade_exit(trade, sell_rate, should_sell) + self.execute_trade_exit(trade, exit_rate, should_sell) return True return False From b2f289e4040ae676830987c3436ba85f8b29667d Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 02:16:25 -0600 Subject: [PATCH 31/32] Fixed freqtradebot failing tests --- freqtrade/freqtradebot.py | 2 +- tests/test_freqtradebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5800befba..7f668273c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -852,7 +852,7 @@ class FreqtradeBot(LoggingMixin): f"for pair {trade.pair}.") def _check_and_execute_exit(self, trade: Trade, exit_rate: float, - enter: bool, exit_: bool) -> bool: + buy: bool, sell: bool) -> bool: """ Check and execute exit """ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 26d06d53a..3c5a8cfae 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1190,7 +1190,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert trade.stoploss_order_id is None assert trade.sell_reason == SellType.EMERGENCY_SELL.value assert log_has("Unable to place a stoploss order on exchange. ", caplog) - assert log_has("Selling the trade forcefully", caplog) + assert log_has("Exiting the trade forcefully", caplog) # Should call a market sell assert create_order_mock.call_count == 2 From 366247dff3bdd360b5b5fdc43c36e1396c902d91 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 9 Sep 2021 02:16:24 -0600 Subject: [PATCH 32/32] removed caplog.clears at end of functions in test_freqtradebot --- tests/test_freqtradebot.py | 45 -------------------------------------- 1 file changed, 45 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index ddd031f77..a9760978e 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -107,7 +107,6 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - caplog.clear() def test_order_dict_live(default_conf, mocker, caplog) -> None: @@ -141,7 +140,6 @@ def test_order_dict_live(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - caplog.clear() def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: @@ -417,7 +415,6 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord assert freqtrade.create_trade('ETH/BTC') assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) - caplog.clear() def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -481,7 +478,6 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) - caplog.clear() def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, @@ -528,7 +524,6 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, n = freqtrade.enter_positions() assert n == 0 assert log_has_re(message, caplog) - caplog.clear() def test_create_trade_no_signal(default_conf, fee, mocker) -> None: @@ -1122,7 +1117,6 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) assert freqtrade.handle_stoploss_on_exchange(trade) is False assert stoploss.call_count == 0 - caplog.clear() def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, @@ -1162,7 +1156,6 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog) assert trade.stoploss_order_id is None assert trade.is_open is True - caplog.clear() def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, @@ -1211,7 +1204,6 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert rpc_mock.call_count == 2 assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' - caplog.clear() def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, @@ -1438,7 +1430,6 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -1673,7 +1664,6 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) # create_trade should be called once for every pair in the whitelist. assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) - caplog.clear() def test_enter_positions_exception(mocker, default_conf, caplog) -> None: @@ -1713,7 +1703,6 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: # test amount modified by fee-logic n = freqtrade.exit_positions(trades) assert n == 0 - caplog.clear() def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1734,7 +1723,6 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) n = freqtrade.exit_positions(trades) assert n == 0 assert log_has('Unable to sell trade ETH/BTC: ', caplog) - caplog.clear() def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1780,7 +1768,6 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No freqtrade.update_trade_state(trade, '123') assert log_has_re('Found open order for.*', caplog) - caplog.clear() def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, @@ -1831,7 +1818,6 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_ assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] assert log_has_re(r'Applying fee on amount for .*', caplog) - caplog.clear() def test_update_trade_state_exception(mocker, default_conf, @@ -1850,7 +1836,6 @@ def test_update_trade_state_exception(mocker, default_conf, ) freqtrade.update_trade_state(trade, trade.open_order_id) assert log_has('Could not update trade amount: ', caplog) - caplog.clear() def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: @@ -1867,7 +1852,6 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None freqtrade.update_trade_state(trade, trade.open_order_id) assert grm_mock.call_count == 0 assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) - caplog.clear() def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open, @@ -2036,7 +2020,6 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) - caplog.clear() def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, @@ -2069,7 +2052,6 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) - caplog.clear() def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order, @@ -2110,7 +2092,6 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog): assert log_has_re(r'Strategy caused the following exception.*', caplog) assert ftbot.strategy.bot_loop_start.call_count == 1 assert ftbot.strategy.analyze.call_count == 1 - caplog.clear() def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2225,7 +2206,6 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o nb_trades = len(trades) assert nb_trades == 0 assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) - caplog.clear() def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2360,7 +2340,6 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert rpc_mock.call_count == 1 assert open_trade.is_open is True assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog) - caplog.clear() def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, @@ -2429,7 +2408,6 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap assert trades[0].open_order_id is None assert trades[0].fee_updated('buy') assert pytest.approx(trades[0].fee_open) == 0.001 - caplog.clear() def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee, @@ -2470,7 +2448,6 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, limit_buy_order_old_partial['remaining']) assert trades[0].open_order_id is None assert trades[0].fee_open == fee() - caplog.clear() def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None: @@ -2499,7 +2476,6 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog) - caplog.clear() def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: @@ -2543,7 +2519,6 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) - caplog.clear() @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], @@ -2565,7 +2540,6 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 - caplog.clear() @pytest.mark.parametrize('cancelorder', [ @@ -2935,7 +2909,6 @@ def test_execute_trade_exit_sloe_cancel_exception( sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) - caplog.clear() def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, @@ -3331,7 +3304,6 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert freqtrade.handle_trade(trade) is True assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert trade.amount != amnt - caplog.clear() def test__safe_sell_amount(default_conf, fee, caplog, mocker): @@ -3362,7 +3334,6 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 - caplog.clear() def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): @@ -3419,7 +3390,6 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo freqtrade.enter_positions() assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) - caplog.clear() def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3511,7 +3481,6 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, " "initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value - caplog.clear() def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3573,7 +3542,6 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) - caplog.clear() def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3636,7 +3604,6 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value - caplog.clear() def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3701,7 +3668,6 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000117705 - caplog.clear() def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3763,7 +3729,6 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', caplog) - caplog.clear() def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee, @@ -3789,7 +3754,6 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe assert walletmock.call_count == 1 assert log_has_re(r'Fee amount for Trade.* was in base currency ' '- Eating Fee 0.008 into dust', caplog) - caplog.clear() def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee): @@ -3812,7 +3776,6 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', caplog) - caplog.clear() def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -3906,7 +3869,6 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None - caplog.clear() def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, @@ -3942,7 +3904,6 @@ def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None - caplog.clear() def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, @@ -3971,7 +3932,6 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', caplog) - caplog.clear() def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -4215,7 +4175,6 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None with pytest.raises(PricingError): freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) - caplog.clear() def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: @@ -4286,7 +4245,6 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.handle_trade(trade) assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*', caplog) - caplog.clear() def test_startup_state(default_conf, mocker): @@ -4345,7 +4303,6 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4505,7 +4462,6 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4643,7 +4599,6 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): freqtrade.refind_lost_order(trades[4]) assert log_has(f"Error updating {order['id']}.", caplog) - caplog.clear() def test_get_valid_price(mocker, default_conf) -> None: