From 13da8f936812bbfd255c4dfce4dd451dc9134c46 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Mon, 9 Nov 2020 08:34:40 +0000 Subject: [PATCH 01/37] Added ConstPairList handler to skip validation of pairs if you want to backtest a pair that's not live any more, e.g. expiring contracts. --- freqtrade/constants.py | 2 +- freqtrade/pairlist/ConstPairList.py | 60 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 freqtrade/pairlist/ConstPairList.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index dc5384f6f..21308b2dc 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -23,7 +23,7 @@ ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] -AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', +AVAILABLE_PAIRLISTS = ['ConstPairList', 'StaticPairList', 'VolumePairList', 'AgeFilter', 'PrecisionFilter', 'PriceFilter', 'ShuffleFilter', 'SpreadFilter'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] diff --git a/freqtrade/pairlist/ConstPairList.py b/freqtrade/pairlist/ConstPairList.py new file mode 100644 index 000000000..e5b009c55 --- /dev/null +++ b/freqtrade/pairlist/ConstPairList.py @@ -0,0 +1,60 @@ +""" +Const Pair List provider + +Provides pair white list as it configured in config without checking for active markets +""" +import logging +from typing import Any, Dict, List + +from freqtrade.exceptions import OperationalException +from freqtrade.pairlist.IPairList import IPairList + + +logger = logging.getLogger(__name__) + + +class ConstPairList(IPairList): + + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + if self._pairlist_pos != 0: + raise OperationalException(f"{self.name} can only be used in the first position " + "in the list of Pairlist Handlers.") + + @property + def needstickers(self) -> bool: + """ + Boolean property defining if tickers are necessary. + If no Pairlist requires tickers, an empty List is passed + as tickers argument to filter_pairlist + """ + return False + + def short_desc(self) -> str: + """ + Short whitelist method description - used for startup-messages + -> Please overwrite in subclasses + """ + return f"{self.name}" + + def gen_pairlist(self, cached_pairlist: List[str], tickers: Dict) -> List[str]: + """ + Generate the pairlist + :param cached_pairlist: Previously generated pairlist (cached) + :param tickers: Tickers (from exchange.get_tickers()). + :return: List of pairs + """ + return self._config['exchange']['pair_whitelist'] + + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + """ + Filters and sorts pairlist and returns the whitelist again. + Called on each bot iteration - please use internal caching if necessary + :param pairlist: pairlist to filter or sort + :param tickers: Tickers (from exchange.get_tickers()). May be cached. + :return: new whitelist + """ + return pairlist From 916776bb53642b25c4be09bca82ef0c1467798d8 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Mon, 9 Nov 2020 08:37:38 +0000 Subject: [PATCH 02/37] Option to skip exchange validation, required to backtest pairs that are not live on the exchange any more. --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 659ff59bc..baa379db1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -115,7 +115,7 @@ class Exchange: logger.info('Using Exchange "%s"', self.name) - if validate: + if validate and not exchange_config.get('skip_validation'): # Check if timeframe is available self.validate_timeframes(config.get('timeframe')) From 2640dfee9387613480e66da00a4fac5b260c1e41 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Thu, 12 Nov 2020 11:27:30 +0000 Subject: [PATCH 03/37] Revert "Added ConstPairList handler to skip validation of pairs if you want to backtest a pair that's not live any more, e.g. expiring contracts." This reverts commit 13da8f936812bbfd255c4dfce4dd451dc9134c46. --- freqtrade/constants.py | 2 +- freqtrade/pairlist/ConstPairList.py | 60 ----------------------------- 2 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 freqtrade/pairlist/ConstPairList.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 21308b2dc..dc5384f6f 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -23,7 +23,7 @@ ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] -AVAILABLE_PAIRLISTS = ['ConstPairList', 'StaticPairList', 'VolumePairList', +AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'PrecisionFilter', 'PriceFilter', 'ShuffleFilter', 'SpreadFilter'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] diff --git a/freqtrade/pairlist/ConstPairList.py b/freqtrade/pairlist/ConstPairList.py deleted file mode 100644 index e5b009c55..000000000 --- a/freqtrade/pairlist/ConstPairList.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Const Pair List provider - -Provides pair white list as it configured in config without checking for active markets -""" -import logging -from typing import Any, Dict, List - -from freqtrade.exceptions import OperationalException -from freqtrade.pairlist.IPairList import IPairList - - -logger = logging.getLogger(__name__) - - -class ConstPairList(IPairList): - - def __init__(self, exchange, pairlistmanager, - config: Dict[str, Any], pairlistconfig: Dict[str, Any], - pairlist_pos: int) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - - if self._pairlist_pos != 0: - raise OperationalException(f"{self.name} can only be used in the first position " - "in the list of Pairlist Handlers.") - - @property - def needstickers(self) -> bool: - """ - Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed - as tickers argument to filter_pairlist - """ - return False - - def short_desc(self) -> str: - """ - Short whitelist method description - used for startup-messages - -> Please overwrite in subclasses - """ - return f"{self.name}" - - def gen_pairlist(self, cached_pairlist: List[str], tickers: Dict) -> List[str]: - """ - Generate the pairlist - :param cached_pairlist: Previously generated pairlist (cached) - :param tickers: Tickers (from exchange.get_tickers()). - :return: List of pairs - """ - return self._config['exchange']['pair_whitelist'] - - def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: - """ - Filters and sorts pairlist and returns the whitelist again. - Called on each bot iteration - please use internal caching if necessary - :param pairlist: pairlist to filter or sort - :param tickers: Tickers (from exchange.get_tickers()). May be cached. - :return: new whitelist - """ - return pairlist From 2424ac94c27c146a553aed362c04ac1850733381 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Thu, 12 Nov 2020 11:29:46 +0000 Subject: [PATCH 04/37] skip the check for active markets with flag for existing StaticPairList --- freqtrade/pairlist/StaticPairList.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/pairlist/StaticPairList.py b/freqtrade/pairlist/StaticPairList.py index aa6268ba3..3b6440763 100644 --- a/freqtrade/pairlist/StaticPairList.py +++ b/freqtrade/pairlist/StaticPairList.py @@ -24,6 +24,8 @@ class StaticPairList(IPairList): raise OperationalException(f"{self.name} can only be used in the first position " "in the list of Pairlist Handlers.") + self._allow_inactive = self._pairlistconfig.get('allow_inactive', False) + @property def needstickers(self) -> bool: """ @@ -47,7 +49,10 @@ class StaticPairList(IPairList): :param tickers: Tickers (from exchange.get_tickers()). :return: List of pairs """ - return self._whitelist_for_active_markets(self._config['exchange']['pair_whitelist']) + if self._allow_inactive: + return self._config['exchange']['pair_whitelist'] + else: + return self._whitelist_for_active_markets(self._config['exchange']['pair_whitelist']) def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ From 2d6bfe1592ab61258d523065cdf4b38cb3857521 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Thu, 12 Nov 2020 11:32:45 +0000 Subject: [PATCH 05/37] only skip pair validation rather than all of it --- freqtrade/exchange/exchange.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index baa379db1..0e982a06f 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -115,7 +115,7 @@ class Exchange: logger.info('Using Exchange "%s"', self.name) - if validate and not exchange_config.get('skip_validation'): + if validate: # Check if timeframe is available self.validate_timeframes(config.get('timeframe')) @@ -124,7 +124,8 @@ class Exchange: # Check if all pairs are available self.validate_stakecurrency(config['stake_currency']) - self.validate_pairs(config['exchange']['pair_whitelist']) + if not exchange_config.get('skip_pair_validation'): + self.validate_pairs(config['exchange']['pair_whitelist']) self.validate_ordertypes(config.get('order_types', {})) self.validate_order_time_in_force(config.get('order_time_in_force', {})) self.validate_required_startup_candles(config.get('startup_candle_count', 0)) From 6ebc2f38974eff34140d63b2955f1e1b54f82b3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Nov 2020 05:40:25 +0000 Subject: [PATCH 06/37] Bump mkdocs-material from 6.1.4 to 6.1.5 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 6.1.4 to 6.1.5. - [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/6.1.4...6.1.5) 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 f034b0b36..cd294bef6 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,3 +1,3 @@ -mkdocs-material==6.1.4 +mkdocs-material==6.1.5 mdx_truly_sane_lists==1.2 pymdown-extensions==8.0.1 From e52c181a2a2a8a369a69fdff2f758767bb42b969 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Nov 2020 05:40:35 +0000 Subject: [PATCH 07/37] Bump flask-jwt-extended from 3.24.1 to 3.25.0 Bumps [flask-jwt-extended](https://github.com/vimalloc/flask-jwt-extended) from 3.24.1 to 3.25.0. - [Release notes](https://github.com/vimalloc/flask-jwt-extended/releases) - [Commits](https://github.com/vimalloc/flask-jwt-extended/compare/3.24.1...3.25.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4622afa15..ea33cdf3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ sdnotify==0.3.2 # Api server flask==1.1.2 -flask-jwt-extended==3.24.1 +flask-jwt-extended==3.25.0 flask-cors==3.0.9 # Support for colorized terminal output From 23947cf30b0dcfc7aab066d48237e0dcd229fb6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Nov 2020 05:40:50 +0000 Subject: [PATCH 08/37] Bump requests from 2.24.0 to 2.25.0 Bumps [requests](https://github.com/psf/requests) from 2.24.0 to 2.25.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.24.0...v2.25.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4622afa15..e56dcf4d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ SQLAlchemy==1.3.20 python-telegram-bot==13.0 arrow==0.17.0 cachetools==4.1.1 -requests==2.24.0 +requests==2.25.0 urllib3==1.25.11 wrapt==1.12.1 jsonschema==3.2.0 From f092a923990515976cfccabc59010d8ccddfd5ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Nov 2020 07:51:10 +0000 Subject: [PATCH 09/37] Bump urllib3 from 1.25.11 to 1.26.2 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.25.11 to 1.26.2. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/master/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.25.11...1.26.2) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5ce060819..d60a0ebba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ python-telegram-bot==13.0 arrow==0.17.0 cachetools==4.1.1 requests==2.25.0 -urllib3==1.25.11 +urllib3==1.26.2 wrapt==1.12.1 jsonschema==3.2.0 TA-Lib==0.4.19 From 3f2addb729f0d462ec11a40197eba4f00c17706a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Nov 2020 09:17:22 +0000 Subject: [PATCH 10/37] Bump ccxt from 1.37.41 to 1.37.69 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.37.41 to 1.37.69. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/doc/exchanges-by-country.rst) - [Commits](https://github.com/ccxt/ccxt/compare/1.37.41...1.37.69) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d60a0ebba..67b69a5dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.19.4 pandas==1.1.4 -ccxt==1.37.41 +ccxt==1.37.69 aiohttp==3.7.2 SQLAlchemy==1.3.20 python-telegram-bot==13.0 From 9621734adc37eb6f6edbd24907e42d35628d70ea Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 17 Nov 2020 06:53:38 +0100 Subject: [PATCH 11/37] Allow setting datafromat via configuration closes #3953 --- freqtrade/commands/cli_options.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 4769bccde..619a300ae 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -354,13 +354,11 @@ AVAILABLE_CLI_OPTIONS = { '--data-format-ohlcv', help='Storage format for downloaded candle (OHLCV) data. (default: `%(default)s`).', choices=constants.AVAILABLE_DATAHANDLERS, - default='json' ), "dataformat_trades": Arg( '--data-format-trades', help='Storage format for downloaded trades data. (default: `%(default)s`).', choices=constants.AVAILABLE_DATAHANDLERS, - default='jsongz' ), "exchange": Arg( '--exchange', From 4a215821cdc2c3017c15fff2082a2b5e58773c61 Mon Sep 17 00:00:00 2001 From: Samaoo Date: Tue, 17 Nov 2020 14:07:24 +0100 Subject: [PATCH 12/37] Fix typo in windows installation docs --- docs/windows_installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/windows_installation.md b/docs/windows_installation.md index 924459a6d..0c1eeef64 100644 --- a/docs/windows_installation.md +++ b/docs/windows_installation.md @@ -50,7 +50,7 @@ freqtrade error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools ``` -Unfortunately, many packages requiring compilation don't provide a pre-build wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. +Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or [docker](docker.md) first. From 854d0c481f0b7569f4e28418a64e088aeb6c0bd4 Mon Sep 17 00:00:00 2001 From: Samaoo Date: Tue, 17 Nov 2020 14:14:42 +0100 Subject: [PATCH 13/37] Update windows_installation.md --- docs/windows_installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/windows_installation.md b/docs/windows_installation.md index 0c1eeef64..5341ce96b 100644 --- a/docs/windows_installation.md +++ b/docs/windows_installation.md @@ -52,6 +52,6 @@ error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. -The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or [docker](docker.md) first. +The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building C code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or [docker](docker.md) first. --- From 52c9a2c37f62196d43f09b41fb0a90dc187a37e2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Nov 2020 07:30:28 +0100 Subject: [PATCH 14/37] Convert np to None when loading hdf5 trades to allow duplicate detection --- freqtrade/data/history/hdf5datahandler.py | 4 +++- tests/data/test_history.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/history/hdf5datahandler.py b/freqtrade/data/history/hdf5datahandler.py index f6cf9e0d9..00e41673d 100644 --- a/freqtrade/data/history/hdf5datahandler.py +++ b/freqtrade/data/history/hdf5datahandler.py @@ -3,6 +3,7 @@ import re from pathlib import Path from typing import List, Optional +import numpy as np import pandas as pd from freqtrade import misc @@ -175,7 +176,8 @@ class HDF5DataHandler(IDataHandler): if timerange.stoptype == 'date': where.append(f"timestamp < {timerange.stopts * 1e3}") - trades = pd.read_hdf(filename, key=key, mode="r", where=where) + trades: pd.DataFrame = pd.read_hdf(filename, key=key, mode="r", where=where) + trades[['id', 'type']] = trades[['id', 'type']].replace({np.nan: None}) return trades.values.tolist() def trades_purge(self, pair: str) -> bool: diff --git a/tests/data/test_history.py b/tests/data/test_history.py index bbc6e55b4..538a0840f 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -724,6 +724,8 @@ def test_hdf5datahandler_trades_load(testdatadir): trades2 = dh._trades_load('XRP/ETH', timerange) assert len(trades) > len(trades2) + # Check that ID is None (If it's nan, it's wrong) + assert trades2[0][2] is None # unfiltered load has trades before starttime assert len([t for t in trades if t[0] < timerange.startts * 1000]) >= 0 From f88fe5d950daf3ab68f9153648bf8d94f20593b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Nov 2020 19:14:43 +0100 Subject: [PATCH 15/37] Document new "allow_inactive" option --- docs/includes/pairlists.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index ae4ec818d..aebd084ab 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -35,6 +35,10 @@ It uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklis ], ``` +By default, only currently enabled pairs are allowed. +To skip pair validation against active markets, set `"allow_inactive": true` within the `StaticPairList` configuration. +This can be useful for backtesting expired pairs (like quarterly spot-markets). + #### Volume Pair List `VolumePairList` employs sorting/filtering of pairs by their trading volume. It selects `number_assets` top pairs with sorting based on the `sort_key` (which can only be `quoteVolume`). From 97e58a42f4caf1803af411937123461bda7ca244 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Nov 2020 19:17:31 +0100 Subject: [PATCH 16/37] Update documentation with new options --- docs/configuration.md | 1 + docs/includes/pairlists.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 47362e525..080ddd046 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -87,6 +87,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `exchange.ccxt_sync_config` | Additional CCXT parameters passed to the regular (sync) ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
**Datatype:** Dict | `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
**Datatype:** Dict | `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded.
*Defaults to `60` minutes.*
**Datatype:** Positive Integer +| `exchange.skip_pair_validation` | Skip pairlist validation on startup.
*Defaults to `false`
**Datatype:** Boolean | `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation. | `experimental.block_bad_exchanges` | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now.
*Defaults to `true`.*
**Datatype:** Boolean | `pairlists` | Define one or more pairlists to be used. [More information below](#pairlists-and-pairlist-handlers).
*Defaults to `StaticPairList`.*
**Datatype:** List of Dicts diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index aebd084ab..e6a9fc1a8 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -38,6 +38,7 @@ It uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklis By default, only currently enabled pairs are allowed. To skip pair validation against active markets, set `"allow_inactive": true` within the `StaticPairList` configuration. This can be useful for backtesting expired pairs (like quarterly spot-markets). +This option must be configured along with `exchange.skip_pair_validation` in the exchange configuration. #### Volume Pair List From aa0c3dced8bac13b35e241f287d1d9a4cbcecd31 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Nov 2020 13:14:02 +0100 Subject: [PATCH 17/37] Improve order types documentation --- docs/configuration.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 080ddd046..56ba13414 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -314,22 +314,21 @@ Configuration: } ``` -!!! Note +!!! Note "Market order support" Not all exchanges support "market" orders. The following message will be shown if your exchange does not support market orders: - `"Exchange does not support market orders."` + `"Exchange does not support market orders."` and the bot will refuse to start. -!!! Note - Stoploss on exchange interval is not mandatory. Do not change its value if you are +!!! Warning "Using market orders" + Please carefully read the section [Market order pricing](#market-order-pricing) section when using market orders. + +!!! Note "Stoploss on exchange" + `stoploss_on_exchange_interval` is not mandatory. Do not change its value if you are unsure of what you are doing. For more information about how stoploss works please refer to [the stoploss documentation](stoploss.md). -!!! Note If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order. -!!! Warning "Using market orders" - Please read the section [Market order pricing](#market-order-pricing) section when using market orders. - !!! Warning "Warning: stoploss_on_exchange failures" If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however this is not advised. From 5ed85963a950eada9571b2f6f19130ff15878157 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 10:39:49 +0100 Subject: [PATCH 18/37] Allow forcebuy price to be a string by converting it to float fix #3970 --- freqtrade/rpc/api_server.py | 2 ++ freqtrade/rpc/rpc.py | 2 +- tests/rpc/test_rpc.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index 7f4773d57..384d7c6c2 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -508,6 +508,8 @@ class ApiServer(RPC): """ asset = request.json.get("pair") price = request.json.get("price", None) + price = float(price) if price is not None else price + trade = self._rpc_forcebuy(asset, price) if trade: return jsonify(trade.to_json()) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 90564a19d..e608a2274 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -524,7 +524,7 @@ class RPC: stake_currency = self._freqtrade.config.get('stake_currency') if not self._freqtrade.exchange.get_pair_quote_currency(pair) == stake_currency: raise RPCException( - f'Wrong pair selected. Please pairs with stake {stake_currency} pairs only') + f'Wrong pair selected. Only pairs with stake-currency {stake_currency} allowed.') # check if valid pair # check if pair already has an open pair diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 23ca53e53..47e0f763d 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -868,7 +868,8 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> assert trade.open_rate == 0.0001 # Test buy pair not with stakes - with pytest.raises(RPCException, match=r'Wrong pair selected. Please pairs with stake.*'): + with pytest.raises(RPCException, + match=r'Wrong pair selected. Only pairs with stake-currency.*'): rpc._rpc_forcebuy('LTC/ETH', 0.0001) pair = 'XRP/BTC' From 83861fabdea798100c5322a9efe5db4f7a23c84a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 10:52:15 +0100 Subject: [PATCH 19/37] Fix #3967, move TradeList type to constants --- freqtrade/constants.py | 3 +++ freqtrade/data/converter.py | 10 +++++---- freqtrade/data/history/hdf5datahandler.py | 4 ++-- freqtrade/data/history/idatahandler.py | 5 +---- freqtrade/data/history/jsondatahandler.py | 4 ++-- tests/data/test_converter.py | 27 ++++++++++++++++++++++- 6 files changed, 40 insertions(+), 13 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index dc5384f6f..3271dda39 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -365,3 +365,6 @@ CANCEL_REASON = { # List of pairs with their timeframes PairWithTimeframe = Tuple[str, str] ListPairsWithTimeframes = List[PairWithTimeframe] + +# Type for trades list +TradeList = List[List] diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 38fa670e9..09930950a 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List import pandas as pd from pandas import DataFrame, to_datetime -from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS +from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, TradeList logger = logging.getLogger(__name__) @@ -168,7 +168,7 @@ def trades_remove_duplicates(trades: List[List]) -> List[List]: return [i for i, _ in itertools.groupby(sorted(trades, key=itemgetter(0)))] -def trades_dict_to_list(trades: List[Dict]) -> List[List]: +def trades_dict_to_list(trades: List[Dict]) -> TradeList: """ Convert fetch_trades result into a List (to be more memory efficient). :param trades: List of trades, as returned by ccxt.fetch_trades. @@ -177,16 +177,18 @@ def trades_dict_to_list(trades: List[Dict]) -> List[List]: return [[t[col] for col in DEFAULT_TRADES_COLUMNS] for t in trades] -def trades_to_ohlcv(trades: List, timeframe: str) -> DataFrame: +def trades_to_ohlcv(trades: TradeList, timeframe: str) -> DataFrame: """ Converts trades list to OHLCV list - TODO: This should get a dedicated test :param trades: List of trades, as returned by ccxt.fetch_trades. :param timeframe: Timeframe to resample data to :return: OHLCV Dataframe. + :raises: Valueerror if no trades are provided """ from freqtrade.exchange import timeframe_to_minutes timeframe_minutes = timeframe_to_minutes(timeframe) + if not trades: + raise ValueError('Trade-list empty.') df = pd.DataFrame(trades, columns=DEFAULT_TRADES_COLUMNS) df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms', utc=True,) diff --git a/freqtrade/data/history/hdf5datahandler.py b/freqtrade/data/history/hdf5datahandler.py index 00e41673d..d116637e7 100644 --- a/freqtrade/data/history/hdf5datahandler.py +++ b/freqtrade/data/history/hdf5datahandler.py @@ -9,9 +9,9 @@ import pandas as pd from freqtrade import misc from freqtrade.configuration import TimeRange from freqtrade.constants import (DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, - ListPairsWithTimeframes) + ListPairsWithTimeframes, TradeList) -from .idatahandler import IDataHandler, TradeList +from .idatahandler import IDataHandler logger = logging.getLogger(__name__) diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index a170a9dc5..070d9039d 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -13,16 +13,13 @@ from typing import List, Optional, Type from pandas import DataFrame from freqtrade.configuration import TimeRange -from freqtrade.constants import ListPairsWithTimeframes +from freqtrade.constants import ListPairsWithTimeframes, TradeList from freqtrade.data.converter import clean_ohlcv_dataframe, trades_remove_duplicates, trim_dataframe from freqtrade.exchange import timeframe_to_seconds logger = logging.getLogger(__name__) -# Type for trades list -TradeList = List[List] - class IDataHandler(ABC): diff --git a/freqtrade/data/history/jsondatahandler.py b/freqtrade/data/history/jsondatahandler.py index 6436aa13d..9122170d5 100644 --- a/freqtrade/data/history/jsondatahandler.py +++ b/freqtrade/data/history/jsondatahandler.py @@ -8,10 +8,10 @@ from pandas import DataFrame, read_json, to_datetime from freqtrade import misc from freqtrade.configuration import TimeRange -from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, ListPairsWithTimeframes +from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, ListPairsWithTimeframes, TradeList from freqtrade.data.converter import trades_dict_to_list -from .idatahandler import IDataHandler, TradeList +from .idatahandler import IDataHandler logger = logging.getLogger(__name__) diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index fdba7900f..4fdcce4d2 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -1,10 +1,13 @@ # pragma pylint: disable=missing-docstring, C0103 import logging +import pytest + from freqtrade.configuration.timerange import TimeRange from freqtrade.data.converter import (convert_ohlcv_format, convert_trades_format, ohlcv_fill_up_missing_data, ohlcv_to_dataframe, - trades_dict_to_list, trades_remove_duplicates, trim_dataframe) + trades_dict_to_list, trades_remove_duplicates, + trades_to_ohlcv, trim_dataframe) from freqtrade.data.history import (get_timerange, load_data, load_pair_history, validate_backtest_data) from tests.conftest import log_has @@ -26,6 +29,28 @@ def test_ohlcv_to_dataframe(ohlcv_history_list, caplog): assert log_has('Converting candle (OHLCV) data to dataframe for pair UNITTEST/BTC.', caplog) +def test_trades_to_ohlcv(ohlcv_history_list, caplog): + + caplog.set_level(logging.DEBUG) + with pytest.raises(ValueError, match="Trade-list empty."): + trades_to_ohlcv([], '1m') + + trades = [ + [1570752011620, "13519807", None, "sell", 0.00141342, 23.0, 0.03250866], + [1570752011620, "13519808", None, "sell", 0.00141266, 54.0, 0.07628364], + [1570752017964, "13519809", None, "sell", 0.00141266, 8.0, 0.01130128]] + + df = trades_to_ohlcv(trades, '1m') + assert not df.empty + assert len(df) == 1 + assert 'open' in df.columns + assert 'high' in df.columns + assert 'low' in df.columns + assert 'close' in df.columns + assert df.loc[:, 'high'][0] == 0.00141342 + assert df.loc[:, 'low'][0] == 0.00141266 + + def test_ohlcv_fill_up_missing_data(testdatadir, caplog): data = load_pair_history(datadir=testdatadir, timeframe='1m', From e8e3ca0c3c114637a460fdddeea5fca155bdf534 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 10:57:19 +0100 Subject: [PATCH 20/37] Catch ValueError from trade_conversion closes #3967 --- freqtrade/data/converter.py | 2 +- freqtrade/data/history/history_utils.py | 9 ++++++--- tests/data/test_history.py | 6 ++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 09930950a..d4053abaa 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -183,7 +183,7 @@ def trades_to_ohlcv(trades: TradeList, timeframe: str) -> DataFrame: :param trades: List of trades, as returned by ccxt.fetch_trades. :param timeframe: Timeframe to resample data to :return: OHLCV Dataframe. - :raises: Valueerror if no trades are provided + :raises: ValueError if no trades are provided """ from freqtrade.exchange import timeframe_to_minutes timeframe_minutes = timeframe_to_minutes(timeframe) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index a420b9dcc..17b510b92 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -356,9 +356,12 @@ def convert_trades_to_ohlcv(pairs: List[str], timeframes: List[str], if erase: if data_handler_ohlcv.ohlcv_purge(pair, timeframe): logger.info(f'Deleting existing data for pair {pair}, interval {timeframe}.') - ohlcv = trades_to_ohlcv(trades, timeframe) - # Store ohlcv - data_handler_ohlcv.ohlcv_store(pair, timeframe, data=ohlcv) + try: + ohlcv = trades_to_ohlcv(trades, timeframe) + # Store ohlcv + data_handler_ohlcv.ohlcv_store(pair, timeframe, data=ohlcv) + except ValueError: + logger.exception(f'Could not convert {pair} to OHLCV.') def get_timerange(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]: diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 538a0840f..905798041 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -620,6 +620,12 @@ def test_convert_trades_to_ohlcv(mocker, default_conf, testdatadir, caplog): _clean_test_file(file1) _clean_test_file(file5) + assert not log_has('Could not convert NoDatapair to OHLCV.', caplog) + + convert_trades_to_ohlcv(['NoDatapair'], timeframes=['1m', '5m'], + datadir=testdatadir, timerange=tr, erase=True) + assert log_has('Could not convert NoDatapair to OHLCV.', caplog) + def test_datahandler_ohlcv_get_pairs(testdatadir): pairs = JsonDataHandler.ohlcv_get_pairs(testdatadir, '5m') From 89ea8dbef2a73c050649d45db6185de8fb8cf789 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 11:13:44 +0100 Subject: [PATCH 21/37] Update slack invite --- CONTRIBUTING.md | 3 +-- README.md | 2 +- docs/developer.md | 2 +- docs/faq.md | 2 +- docs/index.md | 2 +- docs/strategy-customization.md | 2 -- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 399588f88..6b4e8adaf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,8 +12,7 @@ Few pointers for contributions: - New features need to contain unit tests, must conform to PEP8 (max-line-length = 100) and should be documented with the introduction PR. - PR's can be declared as `[WIP]` - which signify Work in Progress Pull Requests (which are not finished). -If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) -or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. +If you are unsure, discuss the feature on our [discord server](https://discord.gg/MA9v74M), on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. ## Getting started diff --git a/README.md b/README.md index c9f4d0a52..4daa1854a 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ to understand the requirements before sending your pull-requests. Coding is not a necessity to contribute - maybe start with improving our documentation? Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase. -**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. +**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/MA9v74M) or [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. **Important:** Always create your PR against the `develop` branch, not `stable`. diff --git a/docs/developer.md b/docs/developer.md index 8ef816d5d..c253f4460 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -2,7 +2,7 @@ This page is intended for developers of Freqtrade, people who want to contribute to the Freqtrade codebase or documentation, or people who want to understand the source code of the application they're running. -All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) where you can ask questions. +All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/MA9v74M) or [slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg) where you can ask questions. ## Documentation diff --git a/docs/faq.md b/docs/faq.md index a775060de..aa33218fb 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -145,7 +145,7 @@ freqtrade hyperopt --hyperop SampleHyperopt --hyperopt-loss SharpeHyperOptLossDa ### Why does it take a long time to run hyperopt? -* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) - or the Freqtrade [discord community](https://discord.gg/X89cVG). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. +* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg) - or the Freqtrade [discord community](https://discord.gg/X89cVG). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. * If you wonder why it can take from 20 minutes to days to do 1000 epochs here are some answers: diff --git a/docs/index.md b/docs/index.md index 5608587db..c6697d165 100644 --- a/docs/index.md +++ b/docs/index.md @@ -63,7 +63,7 @@ Alternatively For any questions not covered by the documentation or for further information about the bot, we encourage you to join our passionate Slack community. -Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) to join the Freqtrade Slack channel. +Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg) to join the Freqtrade Slack channel. Alternatively, check out the newly created [discord server](https://discord.gg/MA9v74M). diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 6c7d78864..db007985f 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -770,8 +770,6 @@ To get additional Ideas for strategies, head over to our [strategy repository](h Feel free to use any of them as inspiration for your own strategies. We're happy to accept Pull Requests containing new Strategies to that repo. -We also got a *strategy-sharing* channel in our [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) which is a great place to get and/or share ideas. - ## Next step Now you have a perfect strategy you probably want to backtest it. From 4d60a4cf4e9a850a1c03ac2923e163b6969564fd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 11:32:46 +0100 Subject: [PATCH 22/37] Add warning to StochRSI in sample strategy closes #2961 --- freqtrade/templates/sample_strategy.py | 2 ++ freqtrade/templates/subtemplates/indicators_full.j2 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 44590dbbe..b3f9fef07 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -184,6 +184,8 @@ class SampleStrategy(IStrategy): dataframe['fastk'] = stoch_fast['fastk'] # # Stochastic RSI + # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. + # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. # stoch_rsi = ta.STOCHRSI(dataframe) # dataframe['fastd_rsi'] = stoch_rsi['fastd'] # dataframe['fastk_rsi'] = stoch_rsi['fastk'] diff --git a/freqtrade/templates/subtemplates/indicators_full.j2 b/freqtrade/templates/subtemplates/indicators_full.j2 index 60a358bec..57d2ca665 100644 --- a/freqtrade/templates/subtemplates/indicators_full.j2 +++ b/freqtrade/templates/subtemplates/indicators_full.j2 @@ -62,6 +62,8 @@ dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastk'] = stoch_fast['fastk'] # # Stochastic RSI +# Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. +# STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. # stoch_rsi = ta.STOCHRSI(dataframe) # dataframe['fastd_rsi'] = stoch_rsi['fastd'] # dataframe['fastk_rsi'] = stoch_rsi['fastk'] From 73f0e6e704fc85c57f0b7d982a1ff0df0fae7fa5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Nov 2020 11:40:28 +0100 Subject: [PATCH 23/37] Improve wording for discord server fix link to correct docker install guide --- README.md | 10 ++++------ docs/index.md | 13 +++++-------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4daa1854a..8526b5c91 100644 --- a/README.md +++ b/README.md @@ -132,15 +132,13 @@ The project is currently setup in two main branches: ## Support -### Help / Slack / Discord +### Help / Discord / Slack -For any questions not covered by the documentation or for further information about the bot, we encourage you to join our slack channel. +For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel. -- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE). +Please check out our [discord server](https://discord.gg/MA9v74M). -Alternatively, check out the newly created [discord server](https://discord.gg/MA9v74M). - -*Note*: Since the discord server is relatively new, answers to questions might be slightly delayed as currently the user base quite small. +You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg). ### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) diff --git a/docs/index.md b/docs/index.md index c6697d165..f63aeb6b8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -59,17 +59,14 @@ Alternatively ## Support -### Help / Slack / Discord +### Help / Discord / Slack -For any questions not covered by the documentation or for further information about the bot, we encourage you to join our passionate Slack community. +For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel. -Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg) to join the Freqtrade Slack channel. +Please check out our [discord server](https://discord.gg/MA9v74M). -Alternatively, check out the newly created [discord server](https://discord.gg/MA9v74M). - -!!! Note - Since the discord server is relatively new, answers to questions might be slightly delayed as currently the user base quite small. +You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-jaut7r4m-Y17k4x5mcQES9a9swKuxbg). ## Ready to try? -Begin by reading our installation guide [for docker](docker.md) (recommended), or for [installation without docker](installation.md). +Begin by reading our installation guide [for docker](docker_quickstart.md) (recommended), or for [installation without docker](installation.md). From ec330112552afc3902bef12b6ea3884a39a16193 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:50:43 +0000 Subject: [PATCH 24/37] Bump ccxt from 1.37.69 to 1.38.13 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.37.69 to 1.38.13. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/doc/exchanges-by-country.rst) - [Commits](https://github.com/ccxt/ccxt/compare/1.37.69...1.38.13) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 67b69a5dd..7f2ac98b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.19.4 pandas==1.1.4 -ccxt==1.37.69 +ccxt==1.38.13 aiohttp==3.7.2 SQLAlchemy==1.3.20 python-telegram-bot==13.0 From 83b4cd7b39f1ea2eba4d820043f47b7a42c96fd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:50:51 +0000 Subject: [PATCH 25/37] Bump mkdocs-material from 6.1.5 to 6.1.6 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 6.1.5 to 6.1.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/6.1.5...6.1.6) 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 cd294bef6..87bc6dfdd 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,3 +1,3 @@ -mkdocs-material==6.1.5 +mkdocs-material==6.1.6 mdx_truly_sane_lists==1.2 pymdown-extensions==8.0.1 From be4807d85c24c719f87a3ee43d13012d5988de41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:50:52 +0000 Subject: [PATCH 26/37] Bump questionary from 1.8.0 to 1.8.1 Bumps [questionary](https://github.com/tmbo/questionary) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/tmbo/questionary/releases) - [Commits](https://github.com/tmbo/questionary/compare/1.8.0...1.8.1) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 67b69a5dd..66fa04824 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,5 +35,5 @@ flask-cors==3.0.9 # Support for colorized terminal output colorama==0.4.4 # Building config files interactively -questionary==1.8.0 +questionary==1.8.1 prompt-toolkit==3.0.8 From 7c7a8190abf5548ba54cc54141929668492ac56b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:50:54 +0000 Subject: [PATCH 27/37] Bump python-rapidjson from 0.9.3 to 0.9.4 Bumps [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) from 0.9.3 to 0.9.4. - [Release notes](https://github.com/python-rapidjson/python-rapidjson/releases) - [Changelog](https://github.com/python-rapidjson/python-rapidjson/blob/master/CHANGES.rst) - [Commits](https://github.com/python-rapidjson/python-rapidjson/compare/v0.9.3...v0.9.4) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 67b69a5dd..487b19159 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ blosc==1.9.2 py_find_1st==1.1.4 # Load ticker files 30% faster -python-rapidjson==0.9.3 +python-rapidjson==0.9.4 # Notify systemd sdnotify==0.3.2 From 56629d882e1d5fc39c00de14f5995af9ce16ce6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:51:03 +0000 Subject: [PATCH 28/37] Bump coveralls from 2.1.2 to 2.2.0 Bumps [coveralls](https://github.com/coveralls-clients/coveralls-python) from 2.1.2 to 2.2.0. - [Release notes](https://github.com/coveralls-clients/coveralls-python/releases) - [Changelog](https://github.com/coveralls-clients/coveralls-python/blob/master/CHANGELOG.md) - [Commits](https://github.com/coveralls-clients/coveralls-python/compare/2.1.2...2.2.0) 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 1c96a880a..e681274c8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt -r requirements-hyperopt.txt -coveralls==2.1.2 +coveralls==2.2.0 flake8==3.8.4 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.1.0 From 1ec99e6b76010c0f0471d009b64e7f3bef0c433d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 08:05:45 +0000 Subject: [PATCH 29/37] Bump aiohttp from 3.7.2 to 3.7.3 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.7.2 to 3.7.3. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.7.2...v3.7.3) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fa3024e30..7490688d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.19.4 pandas==1.1.4 ccxt==1.38.13 -aiohttp==3.7.2 +aiohttp==3.7.3 SQLAlchemy==1.3.20 python-telegram-bot==13.0 arrow==0.17.0 From 730c9ce4719ee257b62a149d2c807c5da43e07d8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Nov 2020 06:57:11 +0100 Subject: [PATCH 30/37] Add Max_open_trades to summary metrics --- docs/backtesting.md | 9 +++++++-- freqtrade/optimize/optimize_reports.py | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 84911568b..277b11083 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -162,6 +162,8 @@ A backtesting result will look like that: |-----------------------+---------------------| | Backtesting from | 2019-01-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 | +| Max open trades | 3 | +| | | | Total trades | 429 | | First trade | 2019-01-01 18:30:00 | | First trade Pair | EOS/USDT | @@ -233,6 +235,8 @@ It contains some useful key metrics about performance of your strategy on backte |-----------------------+---------------------| | Backtesting from | 2019-01-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 | +| Max open trades | 3 | +| | | | Total trades | 429 | | First trade | 2019-01-01 18:30:00 | | First trade Pair | EOS/USDT | @@ -251,16 +255,17 @@ It contains some useful key metrics about performance of your strategy on backte ``` +- `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option). +- `Max open trades`: Setting of `max_open_trades` (or `--max-open-trades`) - to clearly see settings for this. - `Total trades`: Identical to the total trades of the backtest output table. - `First trade`: First trade entered. - `First trade pair`: Which pair was part of the first trade. -- `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option). - `Total Profit %`: Total profit per stake amount. Aligned to the TOTAL column of the first table. - `Trades per day`: Total trades divided by the backtesting duration in days (this will give you information about how many trades to expect from the strategy). - `Best day` / `Worst day`: Best and worst day based on daily profit. - `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades. - `Max Drawdown`: Maximum drawdown experienced. For example, the value of 50% means that from highest to subsequent lowest point, a 50% drop was experienced). -- `Drawdown Start` / `Drawdown End`: Start and end datetimes for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). +- `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). - `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column. ### Assumptions made by backtesting diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index c977a991b..fc04cbd93 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -396,6 +396,8 @@ def text_table_add_metrics(strat_results: Dict) -> str: metrics = [ ('Backtesting from', strat_results['backtest_start'].strftime(DATETIME_PRINT_FORMAT)), ('Backtesting to', strat_results['backtest_end'].strftime(DATETIME_PRINT_FORMAT)), + ('Max open trades', strat_results['max_open_trades']), + ('', ''), # Empty line to improve readability ('Total trades', strat_results['total_trades']), ('First trade', min_trade['open_date'].strftime(DATETIME_PRINT_FORMAT)), ('First trade Pair', min_trade['pair']), From 006436a18d2d2c821ca4a51ff1e604074ddacf9e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Nov 2020 07:47:35 +0100 Subject: [PATCH 31/37] Require use_sell_signal to be true for edge Otherwise edge will have strange results, as edge runs with sell signal, while the bot runs without sell signal, causing results to be invalid closes #3900 --- freqtrade/configuration/config_validation.py | 4 ++++ tests/test_configuration.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index d4612d8e0..ab21bc686 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -137,6 +137,10 @@ def _validate_edge(conf: Dict[str, Any]) -> None: "Edge and VolumePairList are incompatible, " "Edge will override whatever pairs VolumePairlist selects." ) + if not conf.get('ask_strategy', {}).get('use_sell_signal', True): + raise OperationalException( + "Edge requires `use_sell_signal` to be True, otherwise no sells will happen." + ) def _validate_whitelist(conf: Dict[str, Any]) -> None: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 7d6c81f74..9594b6413 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -812,6 +812,21 @@ def test_validate_edge(edge_conf): validate_config_consistency(edge_conf) +def test_validate_edge2(edge_conf): + edge_conf.update({"ask_strategy": { + "use_sell_signal": True, + }}) + # Passes test + validate_config_consistency(edge_conf) + + edge_conf.update({"ask_strategy": { + "use_sell_signal": False, + }}) + with pytest.raises(OperationalException, match="Edge requires `use_sell_signal` to be True, " + "otherwise no sells will happen."): + validate_config_consistency(edge_conf) + + def test_validate_whitelist(default_conf): default_conf['runmode'] = RunMode.DRY_RUN # Test regular case - has whitelist and uses StaticPairlist From bd98ff6332f9bd3d4ea73d1ee18446b48f0e187e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Nov 2020 20:24:51 +0100 Subject: [PATCH 32/37] Update docstring in all pairlists --- freqtrade/pairlist/AgeFilter.py | 2 +- freqtrade/pairlist/IPairList.py | 2 +- freqtrade/pairlist/PrecisionFilter.py | 2 +- freqtrade/pairlist/PriceFilter.py | 2 +- freqtrade/pairlist/ShuffleFilter.py | 2 +- freqtrade/pairlist/SpreadFilter.py | 2 +- freqtrade/pairlist/StaticPairList.py | 2 +- freqtrade/pairlist/VolumePairList.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/pairlist/AgeFilter.py b/freqtrade/pairlist/AgeFilter.py index 19cf1c090..20635a9ed 100644 --- a/freqtrade/pairlist/AgeFilter.py +++ b/freqtrade/pairlist/AgeFilter.py @@ -37,7 +37,7 @@ class AgeFilter(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return True diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index 6b5bd11e7..c869e499b 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -68,7 +68,7 @@ class IPairList(ABC): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index cf853397b..29e32fd44 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -32,7 +32,7 @@ class PrecisionFilter(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return True diff --git a/freqtrade/pairlist/PriceFilter.py b/freqtrade/pairlist/PriceFilter.py index 8cd57ee1d..bef1c0a15 100644 --- a/freqtrade/pairlist/PriceFilter.py +++ b/freqtrade/pairlist/PriceFilter.py @@ -35,7 +35,7 @@ class PriceFilter(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return True diff --git a/freqtrade/pairlist/ShuffleFilter.py b/freqtrade/pairlist/ShuffleFilter.py index eb4f6dcc3..28778db7b 100644 --- a/freqtrade/pairlist/ShuffleFilter.py +++ b/freqtrade/pairlist/ShuffleFilter.py @@ -25,7 +25,7 @@ class ShuffleFilter(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return False diff --git a/freqtrade/pairlist/SpreadFilter.py b/freqtrade/pairlist/SpreadFilter.py index 2527a3131..a636b90bd 100644 --- a/freqtrade/pairlist/SpreadFilter.py +++ b/freqtrade/pairlist/SpreadFilter.py @@ -24,7 +24,7 @@ class SpreadFilter(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return True diff --git a/freqtrade/pairlist/StaticPairList.py b/freqtrade/pairlist/StaticPairList.py index 3b6440763..2879cb364 100644 --- a/freqtrade/pairlist/StaticPairList.py +++ b/freqtrade/pairlist/StaticPairList.py @@ -30,7 +30,7 @@ class StaticPairList(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return False diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 44e5c52d7..7d3c2c653 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -49,7 +49,7 @@ class VolumePairList(IPairList): def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed + If no Pairlist requires tickers, an empty Dict is passed as tickers argument to filter_pairlist """ return True From ceb50a78071c7295b486de887161875126bb3f72 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Nov 2020 07:57:23 +0100 Subject: [PATCH 33/37] use exception handler when downloading data closes #3992 --- freqtrade/data/history/history_utils.py | 12 +++++------- tests/data/test_history.py | 5 +---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 17b510b92..3b8b5a2f0 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -214,10 +214,9 @@ def _download_pair_history(datadir: Path, data_handler.ohlcv_store(pair, timeframe, data=data) return True - except Exception as e: - logger.error( - f'Failed to download history data for pair: "{pair}", timeframe: {timeframe}. ' - f'Error: {e}' + except Exception: + logger.exception( + f'Failed to download history data for pair: "{pair}", timeframe: {timeframe}.' ) return False @@ -304,10 +303,9 @@ def _download_trades_history(exchange: Exchange, logger.info(f"New Amount of trades: {len(trades)}") return True - except Exception as e: - logger.error( + except Exception: + logger.exception( f'Failed to download historic trades for pair: "{pair}". ' - f'Error: {e}' ) return False diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 905798041..99b22adda 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -312,10 +312,7 @@ def test_download_backtesting_data_exception(ohlcv_history, mocker, caplog, # clean files freshly downloaded _clean_test_file(file1_1) _clean_test_file(file1_5) - assert log_has( - 'Failed to download history data for pair: "MEME/BTC", timeframe: 1m. ' - 'Error: File Error', caplog - ) + assert log_has('Failed to download history data for pair: "MEME/BTC", timeframe: 1m.', caplog) def test_load_partial_missing(testdatadir, caplog) -> None: From 99b67348b206a75556d35f3994ec293b3d543dac Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Nov 2020 14:30:58 +0100 Subject: [PATCH 34/37] Add test for double-logging --- tests/test_configuration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 9594b6413..47d393860 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -678,6 +678,9 @@ def test_set_loggers_syslog(mocker): assert [x for x in logger.handlers if type(x) == logging.handlers.SysLogHandler] assert [x for x in logger.handlers if type(x) == logging.StreamHandler] assert [x for x in logger.handlers if type(x) == logging.handlers.BufferingHandler] + # setting up logging again should NOT cause the loggers to be added a second time. + setup_logging(config) + assert len(logger.handlers) == 3 # reset handlers to not break pytest logger.handlers = orig_handlers From 0104c9fde68601e694969006029cd1b17e8c015c Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Nov 2020 14:31:34 +0100 Subject: [PATCH 35/37] Fix double logging --- freqtrade/loggers.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/freqtrade/loggers.py b/freqtrade/loggers.py index 169cd2610..fbb05d879 100644 --- a/freqtrade/loggers.py +++ b/freqtrade/loggers.py @@ -37,6 +37,13 @@ def _set_loggers(verbosity: int = 0, api_verbosity: str = 'info') -> None: ) +def get_existing_handlers(handlertype): + """ + Returns Existing handler or None (if the handler has not yet been added to the root handlers). + """ + return next((h for h in logging.root.handlers if isinstance(h, handlertype)), None) + + def setup_logging_pre() -> None: """ Early setup for logging. @@ -71,18 +78,24 @@ def setup_logging(config: Dict[str, Any]) -> None: # config['logfilename']), which defaults to '/dev/log', applicable for most # of the systems. address = (s[1], int(s[2])) if len(s) > 2 else s[1] if len(s) > 1 else '/dev/log' - handler = SysLogHandler(address=address) + handler_sl = get_existing_handlers(SysLogHandler) + if handler_sl: + logging.root.removeHandler(handler_sl) + handler_sl = SysLogHandler(address=address) # No datetime field for logging into syslog, to allow syslog # to perform reduction of repeating messages if this is set in the # syslog config. The messages should be equal for this. - handler.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s')) - logging.root.addHandler(handler) + handler_sl.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s')) + logging.root.addHandler(handler_sl) elif s[0] == 'journald': try: from systemd.journal import JournaldLogHandler except ImportError: raise OperationalException("You need the systemd python package be installed in " "order to use logging to journald.") + handler_jd = get_existing_handlers(JournaldLogHandler) + if handler_jd: + logging.root.removeHandler(handler_jd) handler_jd = JournaldLogHandler() # No datetime field for logging into journald, to allow syslog # to perform reduction of repeating messages if this is set in the @@ -90,6 +103,9 @@ def setup_logging(config: Dict[str, Any]) -> None: handler_jd.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s')) logging.root.addHandler(handler_jd) else: + handler_rf = get_existing_handlers(RotatingFileHandler) + if handler_rf: + logging.root.removeHandler(handler_rf) handler_rf = RotatingFileHandler(logfile, maxBytes=1024 * 1024 * 10, # 10Mb backupCount=10) From b9980330a5469aa99160c8e40815a0c8707e0482 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Nov 2020 14:53:25 +0100 Subject: [PATCH 36/37] Add explicit test for FileHandler --- tests/test_configuration.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 47d393860..3501f1f3d 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -663,7 +663,7 @@ def test_set_loggers() -> None: @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") -def test_set_loggers_syslog(mocker): +def test_set_loggers_syslog(): logger = logging.getLogger() orig_handlers = logger.handlers logger.handlers = [] @@ -685,6 +685,30 @@ def test_set_loggers_syslog(mocker): logger.handlers = orig_handlers +def test_set_loggers_Filehandler(tmpdir): + logger = logging.getLogger() + orig_handlers = logger.handlers + logger.handlers = [] + logfile = Path(tmpdir) / 'ft_logfile.log' + config = {'verbosity': 2, + 'logfile': str(logfile), + } + + setup_logging_pre() + setup_logging(config) + assert len(logger.handlers) == 3 + assert [x for x in logger.handlers if type(x) == logging.handlers.RotatingFileHandler] + assert [x for x in logger.handlers if type(x) == logging.StreamHandler] + assert [x for x in logger.handlers if type(x) == logging.handlers.BufferingHandler] + # setting up logging again should NOT cause the loggers to be added a second time. + setup_logging(config) + assert len(logger.handlers) == 3 + # reset handlers to not break pytest + if logfile.exists: + logfile.unlink() + logger.handlers = orig_handlers + + @pytest.mark.skip(reason="systemd is not installed on every system, so we're not testing this.") def test_set_loggers_journald(mocker): logger = logging.getLogger() From 46389e343bc0314427823023604c55bf212686e2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Nov 2020 15:10:17 +0100 Subject: [PATCH 37/37] Skip filehandler test on windows - as that causes a permission-error --- tests/test_configuration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 3501f1f3d..e6c91a96e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -685,6 +685,7 @@ def test_set_loggers_syslog(): logger.handlers = orig_handlers +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_set_loggers_Filehandler(tmpdir): logger = logging.getLogger() orig_handlers = logger.handlers