Merge branch 'develop' into config-allow-comments
This commit is contained in:
		| @@ -59,8 +59,8 @@ working directory. | ||||
|  | ||||
| The bot allows you to use multiple configuration files by specifying multiple | ||||
| `-c/--config` options in the command line. Configuration parameters | ||||
| defined in the last configuration file override parameters with the same name | ||||
| defined in the previous configuration file specified in the command line. | ||||
| defined in the latter configuration files override parameters with the same name | ||||
| defined in the previous configuration files specified in the command line earlier. | ||||
|  | ||||
| For example, you can make a separate configuration file with your key and secrete | ||||
| for the Exchange you use for trading, specify default configuration file with | ||||
| @@ -259,12 +259,13 @@ optional arguments: | ||||
|   --continue            Continue hyperopt from previous runs. By default, | ||||
|                         temporary files will be removed and hyperopt will | ||||
|                         start from scratch. | ||||
|   --hyperopt-loss       NAME | ||||
|                         Specify the class name of the hyperopt loss function | ||||
|   --hyperopt-loss NAME  Specify the class name of the hyperopt loss function | ||||
|                         class (IHyperOptLoss). Different functions can | ||||
|                         generate completely different results, since the | ||||
|                         target for optimization is different. (default: | ||||
|                         `DefaultHyperOptLoss`). | ||||
|                         target for optimization is different. Built-in | ||||
|                         Hyperopt-loss-functions are: DefaultHyperOptLoss, | ||||
|                         OnlyProfitHyperOptLoss, SharpeHyperOptLoss. | ||||
|                         (default: `DefaultHyperOptLoss`). | ||||
| ``` | ||||
|  | ||||
| ## Edge commands | ||||
|   | ||||
| @@ -31,6 +31,16 @@ df = load_trades_from_db("sqlite:///tradesv3.sqlite") | ||||
| df.groupby("pair")["sell_reason"].value_counts() | ||||
| ``` | ||||
|  | ||||
| ### Load multiple configuration files | ||||
|  | ||||
| This option can be usefull to inspect the results of passing in multiple configs in case of problems | ||||
|  | ||||
| ``` python | ||||
| from freqtrade.configuration import Configuration | ||||
| config = Configuration.from_files(["config1.json", "config2.json"]) | ||||
| print(config) | ||||
| ``` | ||||
|  | ||||
| ## Strategy debugging example | ||||
|  | ||||
| Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data. | ||||
|   | ||||
| @@ -17,6 +17,29 @@ Alternatively (if your system is not supported by the setup.sh script), follow t | ||||
|  | ||||
| This will install all required tools for development, including `pytest`, `flake8`, `mypy`, and `coveralls`. | ||||
|  | ||||
| ### Tests | ||||
|  | ||||
| New code should be covered by basic unittests. Depending on the complexity of the feature, Reviewers may request more in-depth unittests. | ||||
| If necessary, the Freqtrade team can assist and give guidance with writing good tests (however please don't expect anyone to write the tests for you). | ||||
|  | ||||
| #### Checking log content in tests | ||||
|  | ||||
| Freqtrade uses 2 main methods to check log content in tests, `log_has()` and `log_has_re()` (to check using regex, in case of dynamic log-messages). | ||||
| These are available from `conftest.py` and can be imported in any test module. | ||||
|  | ||||
| A sample check looks as follows: | ||||
|  | ||||
| ``` python | ||||
| from freqtrade.tests.conftest import log_has, log_has_re | ||||
|  | ||||
| def test_method_to_test(caplog): | ||||
|     method_to_test() | ||||
|  | ||||
|     assert log_has("This event happened", caplog) | ||||
|     # Check regex with trailing number ... | ||||
|     assert log_has_re(r"This dynamic event happened and produced \d+", caplog) | ||||
| ``` | ||||
|  | ||||
| ## Modules | ||||
|  | ||||
| ### Dynamic Pairlist | ||||
|   | ||||
| @@ -164,7 +164,11 @@ By default, FreqTrade uses a loss function, which has been with freqtrade since | ||||
| A different loss function can be specified by using the `--hyperopt-loss <Class-name>` argument. | ||||
| This class should be in its own file within the `user_data/hyperopts/` directory. | ||||
|  | ||||
| Currently, the following loss functions are builtin: `DefaultHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function), `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on the trade returns) and `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration). | ||||
| Currently, the following loss functions are builtin: | ||||
|  | ||||
| * `DefaultHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function) | ||||
| * `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration) | ||||
| * `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on the trade returns) | ||||
|  | ||||
| ### Creating and using a custom loss function | ||||
|  | ||||
|   | ||||
| @@ -226,7 +226,9 @@ AVAILABLE_CLI_OPTIONS = { | ||||
|         '--hyperopt-loss', | ||||
|         help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' | ||||
|         'Different functions can generate completely different results, ' | ||||
|         'since the target for optimization is different. (default: `%(default)s`).', | ||||
|         'since the target for optimization is different. Built-in Hyperopt-loss-functions are: ' | ||||
|         'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.' | ||||
|         '(default: `%(default)s`).', | ||||
|         metavar='NAME', | ||||
|         default=constants.DEFAULT_HYPEROPT_LOSS, | ||||
|     ), | ||||
|   | ||||
| @@ -4,7 +4,7 @@ This module contains the configuration class | ||||
| import logging | ||||
| import warnings | ||||
| from argparse import Namespace | ||||
| from typing import Any, Callable, Dict, Optional | ||||
| from typing import Any, Callable, Dict, List, Optional | ||||
|  | ||||
| from freqtrade import OperationalException, constants | ||||
| from freqtrade.configuration.check_exchange import check_exchange | ||||
| @@ -39,43 +39,43 @@ class Configuration(object): | ||||
|  | ||||
|         return self.config | ||||
|  | ||||
|     def _load_config_files(self) -> Dict[str, Any]: | ||||
|     @staticmethod | ||||
|     def from_files(files: List[str]) -> Dict[str, Any]: | ||||
|         """ | ||||
|         Iterate through the config files passed in the args, | ||||
|         loading all of them and merging their contents. | ||||
|         Iterate through the config files passed in, loading all of them | ||||
|         and merging their contents. | ||||
|         Files are loaded in sequence, parameters in later configuration files | ||||
|         override the same parameter from an earlier file (last definition wins). | ||||
|         :param files: List of file paths | ||||
|         :return: configuration dictionary | ||||
|         """ | ||||
|         # Keep this method as staticmethod, so it can be used from interactive environments | ||||
|         config: Dict[str, Any] = {} | ||||
|  | ||||
|         # We expect here a list of config filenames | ||||
|         for path in self.args.config: | ||||
|             logger.info('Using config: %s ...', path) | ||||
|         for path in files: | ||||
|             logger.info(f'Using config: {path} ...') | ||||
|  | ||||
|             # Merge config options, overwriting old values | ||||
|             config = deep_merge_dicts(load_config_file(path), config) | ||||
|  | ||||
|         return config | ||||
|  | ||||
|     def _normalize_config(self, config: Dict[str, Any]) -> None: | ||||
|         """ | ||||
|         Make config more canonical -- i.e. for example add missing parts that we expect | ||||
|         to be normally in it... | ||||
|         """ | ||||
|         # Normalize config | ||||
|         if 'internals' not in config: | ||||
|             config['internals'] = {} | ||||
|  | ||||
|         # validate configuration before returning | ||||
|         logger.info('Validating configuration ...') | ||||
|         validate_config_schema(config) | ||||
|  | ||||
|         return config | ||||
|  | ||||
|     def load_config(self) -> Dict[str, Any]: | ||||
|         """ | ||||
|         Extract information for sys.argv and load the bot configuration | ||||
|         :return: Configuration dictionary | ||||
|         """ | ||||
|         # Load all configs | ||||
|         config: Dict[str, Any] = self._load_config_files() | ||||
|  | ||||
|         # Make resulting config more canonical | ||||
|         self._normalize_config(config) | ||||
|  | ||||
|         logger.info('Validating configuration ...') | ||||
|         validate_config_schema(config) | ||||
|         config: Dict[str, Any] = Configuration.from_files(self.args.config) | ||||
|  | ||||
|         self._validate_config_consistency(config) | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,7 @@ from freqtrade.exchange import timeframe_to_minutes | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.rpc import RPCManager, RPCMessageType | ||||
| from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver | ||||
| from freqtrade.state import State | ||||
| from freqtrade.state import State, RunMode | ||||
| from freqtrade.strategy.interface import SellType, IStrategy | ||||
| from freqtrade.wallets import Wallets | ||||
|  | ||||
| @@ -75,6 +75,12 @@ class FreqtradeBot(object): | ||||
|         persistence.init(self.config.get('db_url', None), | ||||
|                          clean_open_orders=self.config.get('dry_run', False)) | ||||
|  | ||||
|         # Stoploss on exchange does not make sense, therefore we need to disable that. | ||||
|         if (self.dataprovider.runmode == RunMode.DRY_RUN and | ||||
|            self.strategy.order_types.get('stoploss_on_exchange', False)): | ||||
|             logger.info("Disabling stoploss_on_exchange during dry-run.") | ||||
|             self.strategy.order_types['stoploss_on_exchange'] = False | ||||
|             config['order_types']['stoploss_on_exchange'] = False | ||||
|         # Set initial bot state from config | ||||
|         initial_state = self.config.get('initial_state') | ||||
|         self.state = State[initial_state.upper()] if initial_state else State.STOPPED | ||||
|   | ||||
| @@ -14,36 +14,48 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt | ||||
| class DefaultHyperOpts(IHyperOpt): | ||||
|     """ | ||||
|     Default hyperopt provided by the Freqtrade bot. | ||||
|     You can override it with your own hyperopt | ||||
|     You can override it with your own Hyperopt | ||||
|     """ | ||||
|     @staticmethod | ||||
|     def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Add several indicators needed for buy and sell strategies defined below. | ||||
|         """ | ||||
|         # ADX | ||||
|         dataframe['adx'] = ta.ADX(dataframe) | ||||
|         # MACD | ||||
|         macd = ta.MACD(dataframe) | ||||
|         dataframe['macd'] = macd['macd'] | ||||
|         dataframe['macdsignal'] = macd['macdsignal'] | ||||
|         # MFI | ||||
|         dataframe['mfi'] = ta.MFI(dataframe) | ||||
|         # RSI | ||||
|         dataframe['rsi'] = ta.RSI(dataframe) | ||||
|         # Stochastic Fast | ||||
|         stoch_fast = ta.STOCHF(dataframe) | ||||
|         dataframe['fastd'] = stoch_fast['fastd'] | ||||
|         # Minus-DI | ||||
|         dataframe['minus_di'] = ta.MINUS_DI(dataframe) | ||||
|         # Bollinger bands | ||||
|         bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) | ||||
|         dataframe['bb_lowerband'] = bollinger['lower'] | ||||
|         dataframe['bb_upperband'] = bollinger['upper'] | ||||
|         # SAR | ||||
|         dataframe['sar'] = ta.SAR(dataframe) | ||||
|  | ||||
|         return dataframe | ||||
|  | ||||
|     @staticmethod | ||||
|     def buy_strategy_generator(params: Dict[str, Any]) -> Callable: | ||||
|         """ | ||||
|         Define the buy strategy parameters to be used by hyperopt | ||||
|         Define the buy strategy parameters to be used by Hyperopt. | ||||
|         """ | ||||
|         def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|             """ | ||||
|             Buy strategy Hyperopt will build and use | ||||
|             Buy strategy Hyperopt will build and use. | ||||
|             """ | ||||
|             conditions = [] | ||||
|  | ||||
|             # GUARDS AND TRENDS | ||||
|             if 'mfi-enabled' in params and params['mfi-enabled']: | ||||
|                 conditions.append(dataframe['mfi'] < params['mfi-value']) | ||||
| @@ -79,7 +91,7 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def indicator_space() -> List[Dimension]: | ||||
|         """ | ||||
|         Define your Hyperopt space for searching strategy parameters | ||||
|         Define your Hyperopt space for searching buy strategy parameters. | ||||
|         """ | ||||
|         return [ | ||||
|             Integer(10, 25, name='mfi-value'), | ||||
| @@ -96,14 +108,14 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def sell_strategy_generator(params: Dict[str, Any]) -> Callable: | ||||
|         """ | ||||
|         Define the sell strategy parameters to be used by hyperopt | ||||
|         Define the sell strategy parameters to be used by Hyperopt. | ||||
|         """ | ||||
|         def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|             """ | ||||
|             Sell strategy Hyperopt will build and use | ||||
|             Sell strategy Hyperopt will build and use. | ||||
|             """ | ||||
|             # print(params) | ||||
|             conditions = [] | ||||
|  | ||||
|             # GUARDS AND TRENDS | ||||
|             if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: | ||||
|                 conditions.append(dataframe['mfi'] > params['sell-mfi-value']) | ||||
| @@ -139,7 +151,7 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def sell_indicator_space() -> List[Dimension]: | ||||
|         """ | ||||
|         Define your Hyperopt space for searching sell strategy parameters | ||||
|         Define your Hyperopt space for searching sell strategy parameters. | ||||
|         """ | ||||
|         return [ | ||||
|             Integer(75, 100, name='sell-mfi-value'), | ||||
| @@ -157,9 +169,9 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|  | ||||
|     def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Based on TA indicators. Should be a copy of from strategy | ||||
|         must align to populate_indicators in this file | ||||
|         Only used when --spaces does not include buy | ||||
|         Based on TA indicators. Should be a copy of same method from strategy. | ||||
|         Must align to populate_indicators in this file. | ||||
|         Only used when --spaces does not include buy space. | ||||
|         """ | ||||
|         dataframe.loc[ | ||||
|             ( | ||||
| @@ -174,9 +186,9 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|  | ||||
|     def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Based on TA indicators. Should be a copy of from strategy | ||||
|         must align to populate_indicators in this file | ||||
|         Only used when --spaces does not include sell | ||||
|         Based on TA indicators. Should be a copy of same method from strategy. | ||||
|         Must align to populate_indicators in this file. | ||||
|         Only used when --spaces does not include sell space. | ||||
|         """ | ||||
|         dataframe.loc[ | ||||
|             ( | ||||
| @@ -186,4 +198,5 @@ class DefaultHyperOpts(IHyperOpt): | ||||
|                 (dataframe['fastd'] > 54) | ||||
|             ), | ||||
|             'sell'] = 1 | ||||
|  | ||||
|         return dataframe | ||||
|   | ||||
| @@ -34,13 +34,13 @@ def log_has(line, logs): | ||||
|     # caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar') | ||||
|     # and we want to match line against foobar in the tuple | ||||
|     return reduce(lambda a, b: a or b, | ||||
|                   filter(lambda x: x[2] == line, logs), | ||||
|                   filter(lambda x: x[2] == line, logs.record_tuples), | ||||
|                   False) | ||||
|  | ||||
|  | ||||
| def log_has_re(line, logs): | ||||
|     return reduce(lambda a, b: a or b, | ||||
|                   filter(lambda x: re.match(line, x[2]), logs), | ||||
|                   filter(lambda x: re.match(line, x[2]), logs.record_tuples), | ||||
|                   False) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ def test_parse_ticker_dataframe(ticker_history_list, caplog): | ||||
|     dataframe = parse_ticker_dataframe(ticker_history_list, '5m', | ||||
|                                        pair="UNITTEST/BTC", fill_missing=True) | ||||
|     assert dataframe.columns.tolist() == columns | ||||
|     assert log_has('Parsing tickerlist to dataframe', caplog.record_tuples) | ||||
|     assert log_has('Parsing tickerlist to dataframe', caplog) | ||||
|  | ||||
|  | ||||
| def test_ohlcv_fill_up_missing_data(caplog): | ||||
| @@ -34,8 +34,7 @@ def test_ohlcv_fill_up_missing_data(caplog): | ||||
|     assert (data.columns == data2.columns).all() | ||||
|  | ||||
|     assert log_has(f"Missing data fillup for UNITTEST/BTC: before: " | ||||
|                    f"{len(data)} - after: {len(data2)}", | ||||
|                    caplog.record_tuples) | ||||
|                    f"{len(data)} - after: {len(data2)}", caplog) | ||||
|  | ||||
|     # Test fillup actually fixes invalid backtest data | ||||
|     min_date, max_date = get_timeframe({'UNITTEST/BTC': data}) | ||||
| @@ -97,8 +96,7 @@ def test_ohlcv_fill_up_missing_data2(caplog): | ||||
|     assert (data.columns == data2.columns).all() | ||||
|  | ||||
|     assert log_has(f"Missing data fillup for UNITTEST/BTC: before: " | ||||
|                    f"{len(data)} - after: {len(data2)}", | ||||
|                    caplog.record_tuples) | ||||
|                    f"{len(data)} - after: {len(data2)}", caplog) | ||||
|  | ||||
|  | ||||
| def test_ohlcv_drop_incomplete(caplog): | ||||
| @@ -140,11 +138,11 @@ def test_ohlcv_drop_incomplete(caplog): | ||||
|     data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC", | ||||
|                                   fill_missing=False, drop_incomplete=False) | ||||
|     assert len(data) == 4 | ||||
|     assert not log_has("Dropping last candle", caplog.record_tuples) | ||||
|     assert not log_has("Dropping last candle", caplog) | ||||
|  | ||||
|     # Drop last candle | ||||
|     data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC", | ||||
|                                   fill_missing=False, drop_incomplete=True) | ||||
|     assert len(data) == 3 | ||||
|  | ||||
|     assert log_has("Dropping last candle", caplog.record_tuples) | ||||
|     assert log_has("Dropping last candle", caplog) | ||||
|   | ||||
| @@ -64,8 +64,7 @@ def test_load_data_30min_ticker(mocker, caplog, default_conf) -> None: | ||||
|     assert isinstance(ld, DataFrame) | ||||
|     assert not log_has( | ||||
|         'Download history data for pair: "UNITTEST/BTC", interval: 30m ' | ||||
|         'and store in None.', | ||||
|         caplog.record_tuples | ||||
|         'and store in None.', caplog | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -76,8 +75,7 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf) -> None: | ||||
|     assert log_has( | ||||
|         'No history data for pair: "UNITTEST/BTC", interval: 7m. ' | ||||
|         'Use --refresh-pairs-cached option or download_backtest_data.py ' | ||||
|         'script to download the data', | ||||
|         caplog.record_tuples | ||||
|         'script to download the data', caplog | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -89,8 +87,7 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None: | ||||
|     assert os.path.isfile(file) is True | ||||
|     assert not log_has( | ||||
|         'Download history data for pair: "UNITTEST/BTC", interval: 1m ' | ||||
|         'and store in None.', | ||||
|         caplog.record_tuples | ||||
|         'and store in None.', caplog | ||||
|     ) | ||||
|     _clean_test_file(file) | ||||
|  | ||||
| @@ -113,8 +110,7 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau | ||||
|     assert log_has( | ||||
|         'No history data for pair: "MEME/BTC", interval: 1m. ' | ||||
|         'Use --refresh-pairs-cached option or download_backtest_data.py ' | ||||
|         'script to download the data', | ||||
|         caplog.record_tuples | ||||
|         'script to download the data', caplog | ||||
|     ) | ||||
|  | ||||
|     # download a new pair if refresh_pairs is set | ||||
| @@ -126,8 +122,7 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau | ||||
|     assert os.path.isfile(file) is True | ||||
|     assert log_has( | ||||
|         'Download history data for pair: "MEME/BTC", interval: 1m ' | ||||
|         'and store in None.', | ||||
|         caplog.record_tuples | ||||
|         'and store in None.', caplog | ||||
|     ) | ||||
|     with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'): | ||||
|         history.load_pair_history(datadir=None, | ||||
| @@ -149,7 +144,7 @@ def test_load_data_live(default_conf, mocker, caplog) -> None: | ||||
|                       exchange=exchange) | ||||
|     assert refresh_mock.call_count == 1 | ||||
|     assert len(refresh_mock.call_args_list[0][0][0]) == 2 | ||||
|     assert log_has('Live: Downloading data for all defined pairs ...', caplog.record_tuples) | ||||
|     assert log_has('Live: Downloading data for all defined pairs ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_load_data_live_noexchange(default_conf, mocker, caplog) -> None: | ||||
| @@ -350,8 +345,7 @@ def test_download_backtesting_data_exception(ticker_history, mocker, caplog, def | ||||
|     _clean_test_file(file1_5) | ||||
|     assert log_has( | ||||
|         'Failed to download history data for pair: "MEME/BTC", interval: 1m. ' | ||||
|         'Error: File Error', | ||||
|         caplog.record_tuples | ||||
|         'Error: File Error', caplog | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -380,7 +374,7 @@ def test_load_partial_missing(caplog) -> None: | ||||
|     start_real = tickerdata['UNITTEST/BTC'].iloc[0, 0] | ||||
|     assert log_has(f'Missing data at start for pair ' | ||||
|                    f'UNITTEST/BTC, data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|     # Make sure we start fresh - test missing data at end | ||||
|     caplog.clear() | ||||
|     start = arrow.get('2018-01-10T00:00:00') | ||||
| @@ -396,7 +390,7 @@ def test_load_partial_missing(caplog) -> None: | ||||
|     end_real = arrow.get(tickerdata['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5) | ||||
|     assert log_has(f'Missing data at end for pair ' | ||||
|                    f'UNITTEST/BTC, data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_init(default_conf, mocker) -> None: | ||||
| @@ -560,7 +554,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None: | ||||
|     assert len(caplog.record_tuples) == 1 | ||||
|     assert log_has( | ||||
|         "UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values", | ||||
|         caplog.record_tuples) | ||||
|         caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_backtest_data(default_conf, mocker, caplog) -> None: | ||||
|   | ||||
| @@ -311,7 +311,7 @@ def test_edge_process_no_data(mocker, edge_conf, caplog): | ||||
|  | ||||
|     assert not edge.calculate() | ||||
|     assert len(edge._cached_pairs) == 0 | ||||
|     assert log_has("No data found. Edge is stopped ...", caplog.record_tuples) | ||||
|     assert log_has("No data found. Edge is stopped ...", caplog) | ||||
|     assert edge._last_updated == 0 | ||||
|  | ||||
|  | ||||
| @@ -326,7 +326,7 @@ def test_edge_process_no_trades(mocker, edge_conf, caplog): | ||||
|  | ||||
|     assert not edge.calculate() | ||||
|     assert len(edge._cached_pairs) == 0 | ||||
|     assert log_has("No trades found.", caplog.record_tuples) | ||||
|     assert log_has("No trades found.", caplog) | ||||
|  | ||||
|  | ||||
| def test_edge_init_error(mocker, edge_conf,): | ||||
|   | ||||
| @@ -62,7 +62,7 @@ async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fu | ||||
| def test_init(default_conf, mocker, caplog): | ||||
|     caplog.set_level(logging.INFO) | ||||
|     get_patched_exchange(mocker, default_conf) | ||||
|     assert log_has('Instance is running with dry_run enabled', caplog.record_tuples) | ||||
|     assert log_has('Instance is running with dry_run enabled', caplog) | ||||
|  | ||||
|  | ||||
| def test_init_ccxt_kwargs(default_conf, mocker, caplog): | ||||
| @@ -71,8 +71,7 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog): | ||||
|     conf = copy.deepcopy(default_conf) | ||||
|     conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True} | ||||
|     ex = Exchange(conf) | ||||
|     assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", caplog) | ||||
|     assert ex._api_async.aiohttp_trust_env | ||||
|     assert not ex._api.aiohttp_trust_env | ||||
|  | ||||
| @@ -81,20 +80,18 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog): | ||||
|     conf = copy.deepcopy(default_conf) | ||||
|     conf['exchange']['ccxt_config'] = {'TestKWARG': 11} | ||||
|     ex = Exchange(conf) | ||||
|     assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", | ||||
|                        caplog.record_tuples) | ||||
|     assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", caplog) | ||||
|     assert not ex._api_async.aiohttp_trust_env | ||||
|     assert hasattr(ex._api, 'TestKWARG') | ||||
|     assert ex._api.TestKWARG == 11 | ||||
|     assert not hasattr(ex._api_async, 'TestKWARG') | ||||
|     assert log_has("Applying additional ccxt config: {'TestKWARG': 11}", | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has("Applying additional ccxt config: {'TestKWARG': 11}", caplog) | ||||
|  | ||||
|  | ||||
| def test_destroy(default_conf, mocker, caplog): | ||||
|     caplog.set_level(logging.DEBUG) | ||||
|     get_patched_exchange(mocker, default_conf) | ||||
|     assert log_has('Exchange object destroyed, closing async loop', caplog.record_tuples) | ||||
|     assert log_has('Exchange object destroyed, closing async loop', caplog) | ||||
|  | ||||
|  | ||||
| def test_init_exception(default_conf, mocker): | ||||
| @@ -120,8 +117,7 @@ def test_exchange_resolver(default_conf, mocker, caplog): | ||||
|     mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) | ||||
|     exchange = ExchangeResolver('Bittrex', default_conf).exchange | ||||
|     assert isinstance(exchange, Exchange) | ||||
|     assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", | ||||
|                       caplog.record_tuples) | ||||
|     assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     exchange = ExchangeResolver('kraken', default_conf).exchange | ||||
| @@ -129,7 +125,7 @@ def test_exchange_resolver(default_conf, mocker, caplog): | ||||
|     assert isinstance(exchange, Kraken) | ||||
|     assert not isinstance(exchange, Binance) | ||||
|     assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", | ||||
|                           caplog.record_tuples) | ||||
|                           caplog) | ||||
|  | ||||
|     exchange = ExchangeResolver('binance', default_conf).exchange | ||||
|     assert isinstance(exchange, Exchange) | ||||
| @@ -137,7 +133,7 @@ def test_exchange_resolver(default_conf, mocker, caplog): | ||||
|     assert not isinstance(exchange, Kraken) | ||||
|  | ||||
|     assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", | ||||
|                           caplog.record_tuples) | ||||
|                           caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_order_time_in_force(default_conf, mocker, caplog): | ||||
| @@ -249,8 +245,7 @@ def test__load_async_markets(default_conf, mocker, caplog): | ||||
|     exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef")) | ||||
|     exchange._load_async_markets() | ||||
|  | ||||
|     assert log_has('Could not load async markets. Reason: deadbeef', | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has('Could not load async markets. Reason: deadbeef', caplog) | ||||
|  | ||||
|  | ||||
| def test__load_markets(default_conf, mocker, caplog): | ||||
| @@ -262,7 +257,7 @@ def test__load_markets(default_conf, mocker, caplog): | ||||
|     mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) | ||||
|     mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) | ||||
|     Exchange(default_conf) | ||||
|     assert log_has('Unable to initialize markets. Reason: SomeError', caplog.record_tuples) | ||||
|     assert log_has('Unable to initialize markets. Reason: SomeError', caplog) | ||||
|  | ||||
|     expected_return = {'ETH/BTC': 'available'} | ||||
|     api_mock = MagicMock() | ||||
| @@ -298,7 +293,7 @@ def test__reload_markets(default_conf, mocker, caplog): | ||||
|     exchange._last_markets_refresh = arrow.utcnow().timestamp - 15 * 60 | ||||
|     exchange._reload_markets() | ||||
|     assert exchange.markets == updated_markets | ||||
|     assert log_has('Performing scheduled market reload..', caplog.record_tuples) | ||||
|     assert log_has('Performing scheduled market reload..', caplog) | ||||
|  | ||||
|  | ||||
| def test__reload_markets_exception(default_conf, mocker, caplog): | ||||
| @@ -312,7 +307,7 @@ def test__reload_markets_exception(default_conf, mocker, caplog): | ||||
|     # less than 10 minutes have passed, no reload | ||||
|     exchange._reload_markets() | ||||
|     assert exchange._last_markets_refresh == 0 | ||||
|     assert log_has_re(r"Could not reload markets.*", caplog.record_tuples) | ||||
|     assert log_has_re(r"Could not reload markets.*", caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_pairs(default_conf, mocker):  # test exchange.validate_pairs directly | ||||
| @@ -357,8 +352,7 @@ def test_validate_pairs_exception(default_conf, mocker, caplog): | ||||
|  | ||||
|     mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) | ||||
|     Exchange(default_conf) | ||||
|     assert log_has('Unable to validate pairs (assuming they are correct).', | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has('Unable to validate pairs (assuming they are correct).', caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_pairs_restricted(default_conf, mocker, caplog): | ||||
| @@ -374,8 +368,7 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog): | ||||
|     Exchange(default_conf) | ||||
|     assert log_has(f"Pair XRP/BTC is restricted for some users on this exchange." | ||||
|                    f"Please check if you are impacted by this restriction " | ||||
|                    f"on the exchange and eventually remove XRP/BTC from your whitelist.", | ||||
|                    caplog.record_tuples) | ||||
|                    f"on the exchange and eventually remove XRP/BTC from your whitelist.", caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_timeframes(default_conf, mocker): | ||||
| @@ -1060,7 +1053,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: | ||||
|     assert not exchange._klines | ||||
|     exchange.refresh_latest_ohlcv(pairs) | ||||
|  | ||||
|     assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog.record_tuples) | ||||
|     assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog) | ||||
|     assert exchange._klines | ||||
|     assert exchange._api_async.fetch_ohlcv.call_count == 2 | ||||
|     for pair in pairs: | ||||
| @@ -1079,7 +1072,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: | ||||
|  | ||||
|     assert exchange._api_async.fetch_ohlcv.call_count == 2 | ||||
|     assert log_has(f"Using cached ohlcv data for pair {pairs[0][0]}, interval {pairs[0][1]} ...", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| @@ -1109,7 +1102,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_ | ||||
|     assert res[1] == "5m" | ||||
|     assert res[2] == tick | ||||
|     assert exchange._api_async.fetch_ohlcv.call_count == 1 | ||||
|     assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog.record_tuples) | ||||
|     assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog) | ||||
|  | ||||
|     # exchange = Exchange(default_conf) | ||||
|     await async_ccxt_exception(mocker, default_conf, MagicMock(), | ||||
| @@ -1168,8 +1161,8 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog): | ||||
|     # Test that each is in list at least once as order is not guaranteed | ||||
|     assert type(res[0]) is tuple or type(res[1]) is tuple | ||||
|     assert type(res[0]) is TypeError or type(res[1]) is TypeError | ||||
|     assert log_has("Error loading ETH/BTC. Result was [[]].", caplog.record_tuples) | ||||
|     assert log_has("Async code raised an exception: TypeError", caplog.record_tuples) | ||||
|     assert log_has("Error loading ETH/BTC. Result was [[]].", caplog) | ||||
|     assert log_has("Async code raised an exception: TypeError", caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("exchange_name", EXCHANGES) | ||||
|   | ||||
| @@ -181,21 +181,18 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) | ||||
|  | ||||
|     assert 'live' not in config | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog) | ||||
|  | ||||
|     assert 'position_stacking' not in config | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' not in config | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' not in config | ||||
|     assert 'export' not in config | ||||
| @@ -235,43 +232,31 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> | ||||
|     assert 'datadir' in config | ||||
|     assert config['runmode'] == RunMode.BACKTEST | ||||
|  | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'live' in config | ||||
|     assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -l/--live detected ...', caplog) | ||||
|  | ||||
|     assert 'position_stacking' in config | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'use_max_market_positions' in config | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' in config | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' in config | ||||
|     assert log_has( | ||||
|         'Parameter --timerange detected: {} ...'.format(config['timerange']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) | ||||
|  | ||||
|     assert 'export' in config | ||||
|     assert log_has( | ||||
|         'Parameter --export detected: {} ...'.format(config['export']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog) | ||||
|     assert 'exportfilename' in config | ||||
|     assert log_has( | ||||
|         'Storing backtest results to {} ...'.format(config['exportfilename']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Storing backtest results to {} ...'.format(config['exportfilename']), caplog) | ||||
|  | ||||
|  | ||||
| def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None: | ||||
| @@ -303,10 +288,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None: | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start_backtesting(args) | ||||
|     assert log_has( | ||||
|         'Starting freqtrade in Backtesting mode', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Starting freqtrade in Backtesting mode', caplog) | ||||
|     assert start_mock.call_count == 1 | ||||
|  | ||||
|  | ||||
| @@ -360,7 +342,7 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No | ||||
|     with pytest.raises(OperationalException): | ||||
|         Backtesting(default_conf) | ||||
|     log_has("Ticker-interval needs to be set in either configuration " | ||||
|             "or as cli argument `--ticker-interval 5m`", caplog.record_tuples) | ||||
|             "or as cli argument `--ticker-interval 5m`", caplog) | ||||
|  | ||||
|  | ||||
| def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None: | ||||
| @@ -511,7 +493,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None: | ||||
|         'up to 2017-11-14T22:59:00+00:00 (0 days)..' | ||||
|     ] | ||||
|     for line in exists: | ||||
|         assert log_has(line, caplog.record_tuples) | ||||
|         assert log_has(line, caplog) | ||||
|  | ||||
|  | ||||
| def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None: | ||||
| @@ -539,7 +521,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None: | ||||
|     backtesting.start() | ||||
|     # check the logs, that will contain the backtest result | ||||
|  | ||||
|     assert log_has('No data found. Terminating.', caplog.record_tuples) | ||||
|     assert log_has('No data found. Terminating.', caplog) | ||||
|  | ||||
|  | ||||
| def test_backtest(default_conf, fee, mocker) -> None: | ||||
| @@ -876,7 +858,7 @@ def test_backtest_start_live(default_conf, mocker, caplog): | ||||
|     ] | ||||
|  | ||||
|     for line in exists: | ||||
|         assert log_has(line, caplog.record_tuples) | ||||
|         assert log_has(line, caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.filterwarnings("ignore:DEPRECATED") | ||||
| @@ -936,4 +918,4 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): | ||||
|     ] | ||||
|  | ||||
|     for line in exists: | ||||
|         assert log_has(line, caplog.record_tuples) | ||||
|         assert log_has(line, caplog) | ||||
|   | ||||
| @@ -29,15 +29,12 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' not in config | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' not in config | ||||
|     assert 'stoploss_range' not in config | ||||
| @@ -69,21 +66,15 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert config['runmode'] == RunMode.EDGE | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' in config | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|     assert 'timerange' in config | ||||
|     assert log_has( | ||||
|         'Parameter --timerange detected: {} ...'.format(config['timerange']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) | ||||
|  | ||||
|  | ||||
| def test_start(mocker, fee, edge_conf, caplog) -> None: | ||||
| @@ -100,10 +91,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None: | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start_edge(args) | ||||
|     assert log_has( | ||||
|         'Starting freqtrade in Edge mode', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Starting freqtrade in Edge mode', caplog) | ||||
|     assert start_mock.call_count == 1 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -79,21 +79,18 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) | ||||
|     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) | ||||
|  | ||||
|     assert 'live' not in config | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog) | ||||
|  | ||||
|     assert 'position_stacking' not in config | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' not in config | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' not in config | ||||
|     assert 'runmode' in config | ||||
| @@ -130,41 +127,32 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo | ||||
|     assert 'datadir' in config | ||||
|     assert config['runmode'] == RunMode.HYPEROPT | ||||
|  | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'position_stacking' in config | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'use_max_market_positions' in config | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' in config | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' in config | ||||
|     assert log_has( | ||||
|         'Parameter --timerange detected: {} ...'.format(config['timerange']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) | ||||
|  | ||||
|     assert 'epochs' in config | ||||
|     assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'spaces' in config | ||||
|     assert log_has( | ||||
|         'Parameter -s/--spaces detected: {}'.format(config['spaces']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter -s/--spaces detected: {}'.format(config['spaces']), caplog) | ||||
|     assert 'print_all' in config | ||||
|     assert log_has('Parameter --print-all detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --print-all detected ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_hyperoptresolver(mocker, default_conf, caplog) -> None: | ||||
| @@ -181,9 +169,9 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: | ||||
|     assert not hasattr(x, 'populate_buy_trend') | ||||
|     assert not hasattr(x, 'populate_sell_trend') | ||||
|     assert log_has("Custom Hyperopt does not provide populate_sell_trend. " | ||||
|                    "Using populate_sell_trend from DefaultStrategy.", caplog.record_tuples) | ||||
|                    "Using populate_sell_trend from DefaultStrategy.", caplog) | ||||
|     assert log_has("Custom Hyperopt does not provide populate_buy_trend. " | ||||
|                    "Using populate_buy_trend from DefaultStrategy.", caplog.record_tuples) | ||||
|                    "Using populate_buy_trend from DefaultStrategy.", caplog) | ||||
|     assert hasattr(x, "ticker_interval") | ||||
|  | ||||
|  | ||||
| @@ -229,10 +217,7 @@ def test_start(mocker, default_conf, caplog) -> None: | ||||
|     import pprint | ||||
|     pprint.pprint(caplog.record_tuples) | ||||
|  | ||||
|     assert log_has( | ||||
|         'Starting freqtrade in Hyperopt mode', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Starting freqtrade in Hyperopt mode', caplog) | ||||
|     assert start_mock.call_count == 1 | ||||
|  | ||||
|  | ||||
| @@ -257,7 +242,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: | ||||
|     import pprint | ||||
|     pprint.pprint(caplog.record_tuples) | ||||
|  | ||||
|     assert log_has('No data found. Terminating.', caplog.record_tuples) | ||||
|     assert log_has('No data found. Terminating.', caplog) | ||||
|  | ||||
|  | ||||
| def test_start_failure(mocker, default_conf, caplog) -> None: | ||||
| @@ -275,10 +260,7 @@ def test_start_failure(mocker, default_conf, caplog) -> None: | ||||
|     args = get_args(args) | ||||
|     with pytest.raises(DependencyException): | ||||
|         start_hyperopt(args) | ||||
|     assert log_has( | ||||
|         "Please don't use --strategy for hyperopt.", | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has("Please don't use --strategy for hyperopt.", caplog) | ||||
|  | ||||
|  | ||||
| def test_start_filelock(mocker, default_conf, caplog) -> None: | ||||
| @@ -294,10 +276,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start_hyperopt(args) | ||||
|     assert log_has( | ||||
|         "Another running instance of freqtrade Hyperopt detected.", | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has("Another running instance of freqtrade Hyperopt detected.", caplog) | ||||
|  | ||||
|  | ||||
| def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: | ||||
| @@ -401,10 +380,7 @@ def test_save_trials_saves_trials(mocker, hyperopt, caplog) -> None: | ||||
|     hyperopt.save_trials() | ||||
|  | ||||
|     trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') | ||||
|     assert log_has( | ||||
|         'Saving 1 evaluations to \'{}\''.format(trials_file), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Saving 1 evaluations to \'{}\''.format(trials_file), caplog) | ||||
|     mock_dump.assert_called_once() | ||||
|  | ||||
|  | ||||
| @@ -413,10 +389,7 @@ def test_read_trials_returns_trials_file(mocker, hyperopt, caplog) -> None: | ||||
|     mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials) | ||||
|     hyperopt_trial = hyperopt.read_trials() | ||||
|     trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') | ||||
|     assert log_has( | ||||
|         'Reading Trials from \'{}\''.format(trials_file), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Reading Trials from \'{}\''.format(trials_file), caplog) | ||||
|     assert hyperopt_trial == trials | ||||
|     mock_load.assert_called_once() | ||||
|  | ||||
| @@ -626,7 +599,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog): | ||||
|     Hyperopt(default_conf) | ||||
|  | ||||
|     assert unlinkmock.call_count == 2 | ||||
|     assert log_has(f"Removing `{TICKERDATA_PICKLE}`.", caplog.record_tuples) | ||||
|     assert log_has(f"Removing `{TICKERDATA_PICKLE}`.", caplog) | ||||
|  | ||||
|  | ||||
| def test_continue_hyperopt(mocker, default_conf, caplog): | ||||
| @@ -643,4 +616,4 @@ def test_continue_hyperopt(mocker, default_conf, caplog): | ||||
|     Hyperopt(default_conf) | ||||
|  | ||||
|     assert unlinkmock.call_count == 0 | ||||
|     assert log_has(f"Continuing on previous hyperopt results.", caplog.record_tuples) | ||||
|     assert log_has(f"Continuing on previous hyperopt results.", caplog) | ||||
|   | ||||
| @@ -91,7 +91,7 @@ def test_fiat_convert_unsupported_crypto(mocker, caplog): | ||||
|     mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[]) | ||||
|     fiat_convert = CryptoToFiatConverter() | ||||
|     assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0 | ||||
|     assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples) | ||||
|     assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog) | ||||
|  | ||||
|  | ||||
| def test_fiat_convert_get_price(mocker): | ||||
| @@ -190,7 +190,7 @@ def test_fiat_invalid_response(mocker, caplog): | ||||
|     length_cryptomap = len(fiat_convert._cryptomap) | ||||
|     assert length_cryptomap == 0 | ||||
|     assert log_has('Could not load FIAT Cryptocurrency map for the following problem: TypeError', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_convert_amount(mocker): | ||||
|   | ||||
| @@ -148,8 +148,8 @@ def test_api_run(default_conf, mocker, caplog): | ||||
|     assert isinstance(server_mock.call_args_list[0][0][2], Flask) | ||||
|     assert hasattr(apiserver, "srv") | ||||
|  | ||||
|     assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog.record_tuples) | ||||
|     assert log_has("Starting Local Rest Server.", caplog.record_tuples) | ||||
|     assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog) | ||||
|     assert log_has("Starting Local Rest Server.", caplog) | ||||
|  | ||||
|     # Test binding to public | ||||
|     caplog.clear() | ||||
| @@ -165,22 +165,20 @@ def test_api_run(default_conf, mocker, caplog): | ||||
|     assert server_mock.call_args_list[0][0][0] == "0.0.0.0" | ||||
|     assert server_mock.call_args_list[0][0][1] == "8089" | ||||
|     assert isinstance(server_mock.call_args_list[0][0][2], Flask) | ||||
|     assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog.record_tuples) | ||||
|     assert log_has("Starting Local Rest Server.", caplog.record_tuples) | ||||
|     assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog) | ||||
|     assert log_has("Starting Local Rest Server.", caplog) | ||||
|     assert log_has("SECURITY WARNING - Local Rest Server listening to external connections", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|     assert log_has("SECURITY WARNING - This is insecure please set to your loopback," | ||||
|                    "e.g 127.0.0.1 in config.json", | ||||
|                    caplog.record_tuples) | ||||
|                    "e.g 127.0.0.1 in config.json", caplog) | ||||
|     assert log_has("SECURITY WARNING - No password for local REST Server defined. " | ||||
|                    "Please make sure that this is intentional!", | ||||
|                    caplog.record_tuples) | ||||
|                    "Please make sure that this is intentional!", caplog) | ||||
|  | ||||
|     # Test crashing flask | ||||
|     caplog.clear() | ||||
|     mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock(side_effect=Exception)) | ||||
|     apiserver.run() | ||||
|     assert log_has("Api server failed to start.", caplog.record_tuples) | ||||
|     assert log_has("Api server failed to start.", caplog) | ||||
|  | ||||
|  | ||||
| def test_api_cleanup(default_conf, mocker, caplog): | ||||
| @@ -199,7 +197,7 @@ def test_api_cleanup(default_conf, mocker, caplog): | ||||
|  | ||||
|     apiserver.cleanup() | ||||
|     assert stop_mock.shutdown.call_count == 1 | ||||
|     assert log_has("Stopping API Server", caplog.record_tuples) | ||||
|     assert log_has("Stopping API Server", caplog) | ||||
|  | ||||
|  | ||||
| def test_api_reloadconf(botclient): | ||||
|   | ||||
| @@ -19,7 +19,7 @@ def test_init_telegram_disabled(mocker, default_conf, caplog) -> None: | ||||
|     default_conf['telegram']['enabled'] = False | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert not log_has('Enabling rpc.telegram ...', caplog.record_tuples) | ||||
|     assert not log_has('Enabling rpc.telegram ...', caplog) | ||||
|     assert rpc_manager.registered_modules == [] | ||||
|  | ||||
|  | ||||
| @@ -28,7 +28,7 @@ def test_init_telegram_enabled(mocker, default_conf, caplog) -> None: | ||||
|     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert log_has('Enabling rpc.telegram ...', caplog.record_tuples) | ||||
|     assert log_has('Enabling rpc.telegram ...', caplog) | ||||
|     len_modules = len(rpc_manager.registered_modules) | ||||
|     assert len_modules == 1 | ||||
|     assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules] | ||||
| @@ -43,7 +43,7 @@ def test_cleanup_telegram_disabled(mocker, default_conf, caplog) -> None: | ||||
|     rpc_manager = RPCManager(freqtradebot) | ||||
|     rpc_manager.cleanup() | ||||
|  | ||||
|     assert not log_has('Cleaning up rpc.telegram ...', caplog.record_tuples) | ||||
|     assert not log_has('Cleaning up rpc.telegram ...', caplog) | ||||
|     assert telegram_mock.call_count == 0 | ||||
|  | ||||
|  | ||||
| @@ -59,7 +59,7 @@ def test_cleanup_telegram_enabled(mocker, default_conf, caplog) -> None: | ||||
|     assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules] | ||||
|  | ||||
|     rpc_manager.cleanup() | ||||
|     assert log_has('Cleaning up rpc.telegram ...', caplog.record_tuples) | ||||
|     assert log_has('Cleaning up rpc.telegram ...', caplog) | ||||
|     assert 'telegram' not in [mod.name for mod in rpc_manager.registered_modules] | ||||
|     assert telegram_mock.call_count == 1 | ||||
|  | ||||
| @@ -75,7 +75,7 @@ def test_send_msg_telegram_disabled(mocker, default_conf, caplog) -> None: | ||||
|         'status': 'test' | ||||
|     }) | ||||
|  | ||||
|     assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog.record_tuples) | ||||
|     assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog) | ||||
|     assert telegram_mock.call_count == 0 | ||||
|  | ||||
|  | ||||
| @@ -90,7 +90,7 @@ def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None: | ||||
|         'status': 'test' | ||||
|     }) | ||||
|  | ||||
|     assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog.record_tuples) | ||||
|     assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog) | ||||
|     assert telegram_mock.call_count == 1 | ||||
|  | ||||
|  | ||||
| @@ -100,7 +100,7 @@ def test_init_webhook_disabled(mocker, default_conf, caplog) -> None: | ||||
|     default_conf['webhook'] = {'enabled': False} | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert not log_has('Enabling rpc.webhook ...', caplog.record_tuples) | ||||
|     assert not log_has('Enabling rpc.webhook ...', caplog) | ||||
|     assert rpc_manager.registered_modules == [] | ||||
|  | ||||
|  | ||||
| @@ -110,7 +110,7 @@ def test_init_webhook_enabled(mocker, default_conf, caplog) -> None: | ||||
|     default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"} | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert log_has('Enabling rpc.webhook ...', caplog.record_tuples) | ||||
|     assert log_has('Enabling rpc.webhook ...', caplog) | ||||
|     assert len(rpc_manager.registered_modules) == 1 | ||||
|     assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules] | ||||
|  | ||||
| @@ -144,7 +144,7 @@ def test_init_apiserver_disabled(mocker, default_conf, caplog) -> None: | ||||
|     default_conf['telegram']['enabled'] = False | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert not log_has('Enabling rpc.api_server', caplog.record_tuples) | ||||
|     assert not log_has('Enabling rpc.api_server', caplog) | ||||
|     assert rpc_manager.registered_modules == [] | ||||
|     assert run_mock.call_count == 0 | ||||
|  | ||||
| @@ -160,7 +160,7 @@ def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None: | ||||
|                                   "listen_port": "8080"} | ||||
|     rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) | ||||
|  | ||||
|     assert log_has('Enabling rpc.api_server', caplog.record_tuples) | ||||
|     assert log_has('Enabling rpc.api_server', caplog) | ||||
|     assert len(rpc_manager.registered_modules) == 1 | ||||
|     assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules] | ||||
|     assert run_mock.call_count == 1 | ||||
|   | ||||
| @@ -76,7 +76,7 @@ def test_init(default_conf, mocker, caplog) -> None: | ||||
|                   "['performance'], ['daily'], ['count'], ['reload_conf'], " \ | ||||
|                   "['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]" | ||||
|  | ||||
|     assert log_has(message_str, caplog.record_tuples) | ||||
|     assert log_has(message_str, caplog) | ||||
|  | ||||
|  | ||||
| def test_cleanup(default_conf, mocker) -> None: | ||||
| @@ -102,18 +102,9 @@ def test_authorized_only(default_conf, mocker, caplog) -> None: | ||||
|     dummy = DummyCls(bot) | ||||
|     dummy.dummy_handler(bot=MagicMock(), update=update) | ||||
|     assert dummy.state['called'] is True | ||||
|     assert log_has( | ||||
|         'Executing handler: dummy_handler for chat_id: 0', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has( | ||||
|         'Rejected unauthorized message from: 0', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has( | ||||
|         'Exception occurred within Telegram module', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) | ||||
|     assert not log_has('Rejected unauthorized message from: 0', caplog) | ||||
|     assert not log_has('Exception occurred within Telegram module', caplog) | ||||
|  | ||||
|  | ||||
| def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: | ||||
| @@ -128,18 +119,9 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: | ||||
|     dummy = DummyCls(bot) | ||||
|     dummy.dummy_handler(bot=MagicMock(), update=update) | ||||
|     assert dummy.state['called'] is False | ||||
|     assert not log_has( | ||||
|         'Executing handler: dummy_handler for chat_id: 3735928559', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has( | ||||
|         'Rejected unauthorized message from: 3735928559', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has( | ||||
|         'Exception occurred within Telegram module', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) | ||||
|     assert log_has('Rejected unauthorized message from: 3735928559', caplog) | ||||
|     assert not log_has('Exception occurred within Telegram module', caplog) | ||||
|  | ||||
|  | ||||
| def test_authorized_only_exception(default_conf, mocker, caplog) -> None: | ||||
| @@ -156,18 +138,9 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: | ||||
|  | ||||
|     dummy.dummy_exception(bot=MagicMock(), update=update) | ||||
|     assert dummy.state['called'] is False | ||||
|     assert not log_has( | ||||
|         'Executing handler: dummy_handler for chat_id: 0', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has( | ||||
|         'Rejected unauthorized message from: 0', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has( | ||||
|         'Exception occurred within Telegram module', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert not log_has('Executing handler: dummy_handler for chat_id: 0', caplog) | ||||
|     assert not log_has('Rejected unauthorized message from: 0', caplog) | ||||
|     assert log_has('Exception occurred within Telegram module', caplog) | ||||
|  | ||||
|  | ||||
| def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: | ||||
| @@ -1440,7 +1413,4 @@ def test__send_msg_network_error(default_conf, mocker, caplog) -> None: | ||||
|  | ||||
|     # Bot should've tried to send it twice | ||||
|     assert len(bot.method_calls) == 2 | ||||
|     assert log_has( | ||||
|         'Telegram NetworkError: Oh snap! Trying one more time.', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Telegram NetworkError: Oh snap! Trying one more time.', caplog) | ||||
|   | ||||
| @@ -115,7 +115,7 @@ def test_exception_send_msg(default_conf, mocker, caplog): | ||||
|     webhook = Webhook(get_patched_freqtradebot(mocker, default_conf)) | ||||
|     webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION}) | ||||
|     assert log_has(f"Message type {RPCMessageType.BUY_NOTIFICATION} not configured for webhooks", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     default_conf["webhook"] = get_webhook_dict() | ||||
|     default_conf["webhook"]["webhookbuy"]["value1"] = "{DEADBEEF:8f}" | ||||
| @@ -135,7 +135,7 @@ def test_exception_send_msg(default_conf, mocker, caplog): | ||||
|     } | ||||
|     webhook.send_msg(msg) | ||||
|     assert log_has("Problem calling Webhook. Please check your webhook configuration. " | ||||
|                    "Exception: 'DEADBEEF'", caplog.record_tuples) | ||||
|                    "Exception: 'DEADBEEF'", caplog) | ||||
|  | ||||
|     msg_mock = MagicMock() | ||||
|     mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) | ||||
| @@ -164,4 +164,4 @@ def test__send_msg(default_conf, mocker, caplog): | ||||
|     post = MagicMock(side_effect=RequestException) | ||||
|     mocker.patch("freqtrade.rpc.webhook.post", post) | ||||
|     webhook._send_msg(msg) | ||||
|     assert log_has('Could not call webhook url. Exception: ', caplog.record_tuples) | ||||
|     assert log_has('Could not call webhook url. Exception: ', caplog) | ||||
|   | ||||
| @@ -49,12 +49,12 @@ def test_returns_latest_sell_signal(mocker, default_conf, ticker_history): | ||||
| def test_get_signal_empty(default_conf, mocker, caplog): | ||||
|     assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], | ||||
|                                                   DataFrame()) | ||||
|     assert log_has('Empty ticker history for pair foo', caplog.record_tuples) | ||||
|     assert log_has('Empty ticker history for pair foo', caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'], | ||||
|                                                   []) | ||||
|     assert log_has('Empty ticker history for pair bar', caplog.record_tuples) | ||||
|     assert log_has('Empty ticker history for pair bar', caplog) | ||||
|  | ||||
|  | ||||
| def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_history): | ||||
| @@ -65,7 +65,7 @@ def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_hi | ||||
|     ) | ||||
|     assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], | ||||
|                                                   ticker_history) | ||||
|     assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples) | ||||
|     assert log_has('Unable to analyze ticker for pair foo: xyz', caplog) | ||||
|  | ||||
|  | ||||
| def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history): | ||||
| @@ -76,7 +76,7 @@ def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history | ||||
|     ) | ||||
|     assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], | ||||
|                                                   ticker_history) | ||||
|     assert log_has('Empty dataframe for pair xyz', caplog.record_tuples) | ||||
|     assert log_has('Empty dataframe for pair xyz', caplog) | ||||
|  | ||||
|  | ||||
| def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history): | ||||
| @@ -91,10 +91,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history): | ||||
|     ) | ||||
|     assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], | ||||
|                                                   ticker_history) | ||||
|     assert log_has( | ||||
|         'Outdated history for pair xyz. Last tick is 16 minutes old', | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) | ||||
|  | ||||
|  | ||||
| def test_get_signal_handles_exceptions(mocker, default_conf): | ||||
| @@ -237,9 +234,8 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: | ||||
|     assert buy_mock.call_count == 1 | ||||
|     assert buy_mock.call_count == 1 | ||||
|  | ||||
|     assert log_has('TA Analysis Launched', caplog.record_tuples) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', | ||||
|                        caplog.record_tuples) | ||||
|     assert log_has('TA Analysis Launched', caplog) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     strategy.analyze_ticker(ticker_history, {'pair': 'ETH/BTC'}) | ||||
| @@ -247,9 +243,8 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: | ||||
|     assert ind_mock.call_count == 2 | ||||
|     assert buy_mock.call_count == 2 | ||||
|     assert buy_mock.call_count == 2 | ||||
|     assert log_has('TA Analysis Launched', caplog.record_tuples) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', | ||||
|                        caplog.record_tuples) | ||||
|     assert log_has('TA Analysis Launched', caplog) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', caplog) | ||||
|  | ||||
|  | ||||
| def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -> None: | ||||
| @@ -275,9 +270,8 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) - | ||||
|     assert ind_mock.call_count == 1 | ||||
|     assert buy_mock.call_count == 1 | ||||
|     assert buy_mock.call_count == 1 | ||||
|     assert log_has('TA Analysis Launched', caplog.record_tuples) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', | ||||
|                        caplog.record_tuples) | ||||
|     assert log_has('TA Analysis Launched', caplog) | ||||
|     assert not log_has('Skipping TA Analysis for already analyzed candle', caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     ret = strategy._analyze_ticker_internal(ticker_history, {'pair': 'ETH/BTC'}) | ||||
| @@ -290,6 +284,5 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) - | ||||
|     assert 'sell' in ret.columns | ||||
|     assert ret['buy'].sum() == 0 | ||||
|     assert ret['sell'].sum() == 0 | ||||
|     assert not log_has('TA Analysis Launched', caplog.record_tuples) | ||||
|     assert log_has('Skipping TA Analysis for already analyzed candle', | ||||
|                    caplog.record_tuples) | ||||
|     assert not log_has('TA Analysis Launched', caplog) | ||||
|     assert log_has('Skipping TA Analysis for already analyzed candle', caplog) | ||||
|   | ||||
| @@ -15,7 +15,7 @@ from freqtrade.resolvers import StrategyResolver | ||||
| from freqtrade.strategy import import_strategy | ||||
| from freqtrade.strategy.default_strategy import DefaultStrategy | ||||
| from freqtrade.strategy.interface import IStrategy | ||||
| from freqtrade.tests.conftest import log_has_re | ||||
| from freqtrade.tests.conftest import log_has, log_has_re | ||||
|  | ||||
|  | ||||
| def test_import_strategy(caplog): | ||||
| @@ -35,12 +35,8 @@ def test_import_strategy(caplog): | ||||
|     assert imported_strategy.__module__ == 'freqtrade.strategy' | ||||
|     assert imported_strategy.some_method() == 42 | ||||
|  | ||||
|     assert ( | ||||
|         'freqtrade.strategy', | ||||
|         logging.DEBUG, | ||||
|         'Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy ' | ||||
|         'as freqtrade.strategy.DefaultStrategy', | ||||
|     ) in caplog.record_tuples | ||||
|     assert log_has('Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy ' | ||||
|                    'as freqtrade.strategy.DefaultStrategy', caplog) | ||||
|  | ||||
|  | ||||
| def test_search_strategy(): | ||||
| @@ -76,8 +72,7 @@ def test_load_strategy_base64(result, caplog): | ||||
|     assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) | ||||
|     # Make sure strategy was loaded from base64 (using temp directory)!! | ||||
|     assert log_has_re(r"Using resolved strategy TestStrategy from '" | ||||
|                       + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", | ||||
|                       caplog.record_tuples) | ||||
|                       + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", caplog) | ||||
|  | ||||
|  | ||||
| def test_load_strategy_invalid_directory(result, caplog): | ||||
| @@ -85,7 +80,7 @@ def test_load_strategy_invalid_directory(result, caplog): | ||||
|     extra_dir = Path.cwd() / 'some/path' | ||||
|     resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir) | ||||
|  | ||||
|     assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog.record_tuples) | ||||
|     assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) | ||||
|  | ||||
|     assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) | ||||
|  | ||||
| @@ -105,7 +100,7 @@ def test_load_staticmethod_importerror(mocker, caplog): | ||||
|                        match=r"Impossible to load Strategy 'DefaultStrategy'. " | ||||
|                              r"This class does not exist or contains Python code errors."): | ||||
|         StrategyResolver() | ||||
|     assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog.record_tuples) | ||||
|     assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy(result): | ||||
| @@ -143,10 +138,7 @@ def test_strategy_override_minimal_roi(caplog): | ||||
|     resolver = StrategyResolver(config) | ||||
|  | ||||
|     assert resolver.strategy.minimal_roi[0] == 0.5 | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'minimal_roi' with value in config file: {'0': 0.5}." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'minimal_roi' with value in config file: {'0': 0.5}.", caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_stoploss(caplog): | ||||
| @@ -158,10 +150,7 @@ def test_strategy_override_stoploss(caplog): | ||||
|     resolver = StrategyResolver(config) | ||||
|  | ||||
|     assert resolver.strategy.stoploss == -0.5 | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'stoploss' with value in config file: -0.5." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'stoploss' with value in config file: -0.5.", caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_trailing_stop(caplog): | ||||
| @@ -174,10 +163,7 @@ def test_strategy_override_trailing_stop(caplog): | ||||
|  | ||||
|     assert resolver.strategy.trailing_stop | ||||
|     assert isinstance(resolver.strategy.trailing_stop, bool) | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'trailing_stop' with value in config file: True." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'trailing_stop' with value in config file: True.", caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_trailing_stop_positive(caplog): | ||||
| @@ -191,16 +177,12 @@ def test_strategy_override_trailing_stop_positive(caplog): | ||||
|     resolver = StrategyResolver(config) | ||||
|  | ||||
|     assert resolver.strategy.trailing_stop_positive == -0.1 | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'trailing_stop_positive' with value in config file: -0.1." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'trailing_stop_positive' with value in config file: -0.1.", | ||||
|                    caplog) | ||||
|  | ||||
|     assert resolver.strategy.trailing_stop_positive_offset == -0.2 | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'trailing_stop_positive' with value in config file: -0.1." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'trailing_stop_positive' with value in config file: -0.1.", | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_ticker_interval(caplog): | ||||
| @@ -215,10 +197,8 @@ def test_strategy_override_ticker_interval(caplog): | ||||
|  | ||||
|     assert resolver.strategy.ticker_interval == 60 | ||||
|     assert resolver.strategy.stake_currency == 'ETH' | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'ticker_interval' with value in config file: 60." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'ticker_interval' with value in config file: 60.", | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_process_only_new_candles(caplog): | ||||
| @@ -231,10 +211,8 @@ def test_strategy_override_process_only_new_candles(caplog): | ||||
|     resolver = StrategyResolver(config) | ||||
|  | ||||
|     assert resolver.strategy.process_only_new_candles | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'process_only_new_candles' with value in config file: True." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'process_only_new_candles' with value in config file: True.", | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_order_types(caplog): | ||||
| @@ -257,12 +235,9 @@ def test_strategy_override_order_types(caplog): | ||||
|     for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: | ||||
|         assert resolver.strategy.order_types[method] == order_types[method] | ||||
|  | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'order_types' with value in config file:" | ||||
|     assert log_has("Override strategy 'order_types' with value in config file:" | ||||
|                    " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," | ||||
|             " 'stoploss_on_exchange': True}." | ||||
|             ) in caplog.record_tuples | ||||
|                    " 'stoploss_on_exchange': True}.", caplog) | ||||
|  | ||||
|     config = { | ||||
|         'strategy': 'DefaultStrategy', | ||||
| @@ -293,11 +268,8 @@ def test_strategy_override_order_tif(caplog): | ||||
|     for method in ['buy', 'sell']: | ||||
|         assert resolver.strategy.order_time_in_force[method] == order_time_in_force[method] | ||||
|  | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'order_time_in_force' with value in config file:" | ||||
|             " {'buy': 'fok', 'sell': 'gtc'}." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'order_time_in_force' with value in config file:" | ||||
|                    " {'buy': 'fok', 'sell': 'gtc'}.", caplog) | ||||
|  | ||||
|     config = { | ||||
|         'strategy': 'DefaultStrategy', | ||||
| @@ -332,10 +304,7 @@ def test_strategy_override_use_sell_signal(caplog): | ||||
|  | ||||
|     assert resolver.strategy.use_sell_signal | ||||
|     assert isinstance(resolver.strategy.use_sell_signal, bool) | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'use_sell_signal' with value in config file: True." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'use_sell_signal' with value in config file: True.", caplog) | ||||
|  | ||||
|  | ||||
| def test_strategy_override_use_sell_profit_only(caplog): | ||||
| @@ -360,10 +329,7 @@ def test_strategy_override_use_sell_profit_only(caplog): | ||||
|  | ||||
|     assert resolver.strategy.sell_profit_only | ||||
|     assert isinstance(resolver.strategy.sell_profit_only, bool) | ||||
|     assert ('freqtrade.resolvers.strategy_resolver', | ||||
|             logging.INFO, | ||||
|             "Override strategy 'sell_profit_only' with value in config file: True." | ||||
|             ) in caplog.record_tuples | ||||
|     assert log_has("Override strategy 'sell_profit_only' with value in config file: True.", caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.filterwarnings("ignore:deprecated") | ||||
|   | ||||
| @@ -72,7 +72,7 @@ def test__args_to_config(caplog): | ||||
|         # No warnings ... | ||||
|         configuration._args_to_config(config, argname="strategy_path", logstring="DeadBeef") | ||||
|         assert len(w) == 0 | ||||
|         assert log_has("DeadBeef", caplog.record_tuples) | ||||
|         assert log_has("DeadBeef", caplog) | ||||
|         assert config['strategy_path'] == "TestTest" | ||||
|  | ||||
|     configuration = Configuration(args) | ||||
| @@ -84,7 +84,7 @@ def test__args_to_config(caplog): | ||||
|         assert len(w) == 1 | ||||
|         assert issubclass(w[-1].category, DeprecationWarning) | ||||
|         assert "DEPRECATED: Going away soon!" in str(w[-1].message) | ||||
|         assert log_has("DeadBeef", caplog.record_tuples) | ||||
|         assert log_has("DeadBeef", caplog) | ||||
|         assert config['strategy_path'] == "TestTest" | ||||
|  | ||||
|  | ||||
| @@ -98,7 +98,7 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None: | ||||
|  | ||||
|     assert validated_conf['max_open_trades'] == 0 | ||||
|     assert 'internals' in validated_conf | ||||
|     assert log_has('Validating configuration ...', caplog.record_tuples) | ||||
|     assert log_has('Validating configuration ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: | ||||
| @@ -130,7 +130,36 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: | ||||
|     assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] | ||||
|  | ||||
|     assert 'internals' in validated_conf | ||||
|     assert log_has('Validating configuration ...', caplog.record_tuples) | ||||
|     assert log_has('Validating configuration ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_from_config(default_conf, mocker, caplog) -> None: | ||||
|     conf1 = deepcopy(default_conf) | ||||
|     conf2 = deepcopy(default_conf) | ||||
|     del conf1['exchange']['key'] | ||||
|     del conf1['exchange']['secret'] | ||||
|     del conf2['exchange']['name'] | ||||
|     conf2['exchange']['pair_whitelist'] += ['NANO/BTC'] | ||||
|     conf2['fiat_display_currency'] = "EUR" | ||||
|     config_files = [conf1, conf2] | ||||
|  | ||||
|     configsmock = MagicMock(side_effect=config_files) | ||||
|     mocker.patch( | ||||
|         'freqtrade.configuration.configuration.load_config_file', | ||||
|         configsmock | ||||
|     ) | ||||
|  | ||||
|     validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json']) | ||||
|  | ||||
|     exchange_conf = default_conf['exchange'] | ||||
|     assert validated_conf['exchange']['name'] == exchange_conf['name'] | ||||
|     assert validated_conf['exchange']['key'] == exchange_conf['key'] | ||||
|     assert validated_conf['exchange']['secret'] == exchange_conf['secret'] | ||||
|     assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist'] | ||||
|     assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] | ||||
|     assert validated_conf['fiat_display_currency'] == "EUR" | ||||
|     assert 'internals' in validated_conf | ||||
|     assert log_has('Validating configuration ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None: | ||||
| @@ -143,7 +172,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> | ||||
|  | ||||
|     assert validated_conf['max_open_trades'] > 999999999 | ||||
|     assert validated_conf['max_open_trades'] == float('inf') | ||||
|     assert log_has('Validating configuration ...', caplog.record_tuples) | ||||
|     assert log_has('Validating configuration ...', caplog) | ||||
|     assert "runmode" in validated_conf | ||||
|     assert validated_conf['runmode'] == RunMode.DRY_RUN | ||||
|  | ||||
| @@ -280,8 +309,8 @@ def test_show_info(default_conf, mocker, caplog) -> None: | ||||
|     configuration = Configuration(args) | ||||
|     configuration.get_config() | ||||
|  | ||||
|     assert log_has('Using DB: "sqlite:///tmp/testdb"', caplog.record_tuples) | ||||
|     assert log_has('Dry run is enabled', caplog.record_tuples) | ||||
|     assert log_has('Using DB: "sqlite:///tmp/testdb"', caplog) | ||||
|     assert log_has('Dry run is enabled', caplog) | ||||
|  | ||||
|  | ||||
| def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None: | ||||
| @@ -303,21 +332,18 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -i/--ticker-interval detected ...', caplog) | ||||
|  | ||||
|     assert 'live' not in config | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -l/--live detected ...', caplog) | ||||
|  | ||||
|     assert 'position_stacking' not in config | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs' not in config | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|  | ||||
|     assert 'timerange' not in config | ||||
|     assert 'export' not in config | ||||
| @@ -355,37 +381,28 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'live' in config | ||||
|     assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -l/--live detected ...', caplog) | ||||
|  | ||||
|     assert 'position_stacking'in config | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --enable-position-stacking detected ...', caplog) | ||||
|  | ||||
|     assert 'use_max_market_positions' in config | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter --disable-max-market-positions detected ...', caplog) | ||||
|     assert log_has('max_open_trades set to unlimited ...', caplog) | ||||
|  | ||||
|     assert 'refresh_pairs'in config | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) | ||||
|     assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) | ||||
|     assert 'timerange' in config | ||||
|     assert log_has( | ||||
|         'Parameter --timerange detected: {} ...'.format(config['timerange']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) | ||||
|  | ||||
|     assert 'export' in config | ||||
|     assert log_has( | ||||
|         'Parameter --export detected: {} ...'.format(config['export']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog) | ||||
|  | ||||
|  | ||||
| def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> None: | ||||
| @@ -415,16 +432,13 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert log_has( | ||||
|         'Using data directory: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) | ||||
|     assert 'ticker_interval' in config | ||||
|     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'strategy_list' in config | ||||
|     assert log_has('Using strategy list of 2 Strategies', caplog.record_tuples) | ||||
|     assert log_has('Using strategy list of 2 Strategies', caplog) | ||||
|  | ||||
|     assert 'position_stacking' not in config | ||||
|  | ||||
| @@ -433,10 +447,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non | ||||
|     assert 'timerange' not in config | ||||
|  | ||||
|     assert 'export' in config | ||||
|     assert log_has( | ||||
|         'Parameter --export detected: {} ...'.format(config['export']), | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|     assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog) | ||||
|  | ||||
|  | ||||
| def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: | ||||
| @@ -455,11 +466,11 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: | ||||
|     assert 'epochs' in config | ||||
|     assert int(config['epochs']) == 10 | ||||
|     assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     assert 'spaces' in config | ||||
|     assert config['spaces'] == ['all'] | ||||
|     assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) | ||||
|     assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog) | ||||
|     assert "runmode" in config | ||||
|     assert config['runmode'] == RunMode.HYPEROPT | ||||
|  | ||||
| @@ -469,38 +480,35 @@ def test_check_exchange(default_conf, caplog) -> None: | ||||
|     default_conf.get('exchange').update({'name': 'BITTREX'}) | ||||
|     assert check_exchange(default_conf) | ||||
|     assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", | ||||
|                       caplog.record_tuples) | ||||
|                       caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     # Test an officially supported by Freqtrade team exchange | ||||
|     default_conf.get('exchange').update({'name': 'binance'}) | ||||
|     assert check_exchange(default_conf) | ||||
|     assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", | ||||
|                       caplog.record_tuples) | ||||
|                       caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     # Test an available exchange, supported by ccxt | ||||
|     default_conf.get('exchange').update({'name': 'kraken'}) | ||||
|     assert check_exchange(default_conf) | ||||
|     assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " | ||||
|                       r"by the Freqtrade development team\. .*", | ||||
|                       caplog.record_tuples) | ||||
|                       r"by the Freqtrade development team\. .*", caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     # Test a 'bad' exchange, which known to have serious problems | ||||
|     default_conf.get('exchange').update({'name': 'bitmex'}) | ||||
|     assert not check_exchange(default_conf) | ||||
|     assert log_has_re(r"Exchange .* is known to not work with the bot yet\. " | ||||
|                       r"Use it only for development and testing purposes\.", | ||||
|                       caplog.record_tuples) | ||||
|                       r"Use it only for development and testing purposes\.", caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     # Test a 'bad' exchange with check_for_bad=False | ||||
|     default_conf.get('exchange').update({'name': 'bitmex'}) | ||||
|     assert check_exchange(default_conf, False) | ||||
|     assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " | ||||
|                       r"by the Freqtrade development team\. .*", | ||||
|                       caplog.record_tuples) | ||||
|                       r"by the Freqtrade development team\. .*", caplog) | ||||
|     caplog.clear() | ||||
|  | ||||
|     # Test an invalid exchange | ||||
| @@ -526,7 +534,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: | ||||
|     validated_conf = configuration.load_config() | ||||
|  | ||||
|     assert validated_conf.get('verbosity') == 3 | ||||
|     assert log_has('Verbosity set to 3', caplog.record_tuples) | ||||
|     assert log_has('Verbosity set to 3', caplog) | ||||
|  | ||||
|  | ||||
| def test_set_loggers() -> None: | ||||
| @@ -592,7 +600,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None: | ||||
|     validated_conf = configuration.load_config() | ||||
|  | ||||
|     assert validated_conf.get('forcebuy_enable') | ||||
|     assert log_has('`forcebuy` RPC message enabled.', caplog.record_tuples) | ||||
|     assert log_has('`forcebuy` RPC message enabled.', caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_default_conf(default_conf) -> None: | ||||
| @@ -605,7 +613,7 @@ def test_create_datadir(mocker, default_conf, caplog) -> None: | ||||
|  | ||||
|     create_datadir(default_conf, '/foo/bar') | ||||
|     assert md.call_args[1]['parents'] is True | ||||
|     assert log_has('Created data directory: /foo/bar', caplog.record_tuples) | ||||
|     assert log_has('Created data directory: /foo/bar', caplog) | ||||
|  | ||||
|  | ||||
| def test_validate_tsl(default_conf): | ||||
|   | ||||
| @@ -16,7 +16,7 @@ from freqtrade.data.dataprovider import DataProvider | ||||
| from freqtrade.freqtradebot import FreqtradeBot | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.rpc import RPCMessageType | ||||
| from freqtrade.state import State | ||||
| from freqtrade.state import State, RunMode | ||||
| from freqtrade.strategy.interface import SellCheckTuple, SellType | ||||
| from freqtrade.tests.conftest import (get_patched_freqtradebot, | ||||
|                                       get_patched_worker, log_has, log_has_re, | ||||
| @@ -63,7 +63,7 @@ def test_cleanup(mocker, default_conf, caplog) -> None: | ||||
|     mocker.patch('freqtrade.persistence.cleanup', mock_cleanup) | ||||
|     freqtrade = get_patched_freqtradebot(mocker, default_conf) | ||||
|     freqtrade.cleanup() | ||||
|     assert log_has('Cleaning up modules ...', caplog.record_tuples) | ||||
|     assert log_has('Cleaning up modules ...', caplog) | ||||
|     assert mock_cleanup.call_count == 1 | ||||
|  | ||||
|  | ||||
| @@ -76,7 +76,7 @@ def test_worker_running(mocker, default_conf, caplog) -> None: | ||||
|  | ||||
|     state = worker._worker(old_state=None) | ||||
|     assert state is State.RUNNING | ||||
|     assert log_has('Changing state to: RUNNING', caplog.record_tuples) | ||||
|     assert log_has('Changing state to: RUNNING', caplog) | ||||
|     assert mock_throttle.call_count == 1 | ||||
|     # Check strategy is loaded, and received a dataprovider object | ||||
|     assert worker.freqtrade.strategy | ||||
| @@ -93,7 +93,7 @@ def test_worker_stopped(mocker, default_conf, caplog) -> None: | ||||
|     worker.state = State.STOPPED | ||||
|     state = worker._worker(old_state=State.RUNNING) | ||||
|     assert state is State.STOPPED | ||||
|     assert log_has('Changing state to: STOPPED', caplog.record_tuples) | ||||
|     assert log_has('Changing state to: STOPPED', caplog) | ||||
|     assert mock_throttle.call_count == 0 | ||||
|     assert mock_sleep.call_count == 1 | ||||
|  | ||||
| @@ -111,7 +111,7 @@ def test_throttle(mocker, default_conf, caplog) -> None: | ||||
|  | ||||
|     assert result == 42 | ||||
|     assert end - start > 0.1 | ||||
|     assert log_has('Throttling throttled_func for 0.10 seconds', caplog.record_tuples) | ||||
|     assert log_has('Throttling throttled_func for 0.10 seconds', caplog) | ||||
|  | ||||
|     result = worker._throttle(throttled_func, min_secs=-1) | ||||
|     assert result == 42 | ||||
| @@ -130,7 +130,77 @@ def test_throttle_with_assets(mocker, default_conf) -> None: | ||||
|     assert result == -1 | ||||
|  | ||||
|  | ||||
| def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: | ||||
| def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: | ||||
|     patch_RPCManager(mocker) | ||||
|     patch_exchange(mocker) | ||||
|     mocker.patch.multiple( | ||||
|         'freqtrade.exchange.Exchange', | ||||
|         get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) | ||||
|     ) | ||||
|     conf = default_conf.copy() | ||||
|     conf['runmode'] = RunMode.DRY_RUN | ||||
|     conf['order_types'] = { | ||||
|         'buy': 'market', | ||||
|         'sell': 'limit', | ||||
|         'stoploss': 'limit', | ||||
|         'stoploss_on_exchange': True, | ||||
|     } | ||||
|  | ||||
|     freqtrade = FreqtradeBot(conf) | ||||
|     assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog) | ||||
|     assert not freqtrade.strategy.order_types['stoploss_on_exchange'] | ||||
|  | ||||
|     caplog.clear() | ||||
|     # is left untouched | ||||
|     conf = default_conf.copy() | ||||
|     conf['runmode'] = RunMode.DRY_RUN | ||||
|     conf['order_types'] = { | ||||
|         'buy': 'market', | ||||
|         'sell': 'limit', | ||||
|         'stoploss': 'limit', | ||||
|         'stoploss_on_exchange': False, | ||||
|     } | ||||
|     freqtrade = FreqtradeBot(conf) | ||||
|     assert not freqtrade.strategy.order_types['stoploss_on_exchange'] | ||||
|     assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) | ||||
|  | ||||
|  | ||||
| def test_order_dict_live(default_conf, mocker, caplog) -> None: | ||||
|     patch_RPCManager(mocker) | ||||
|     patch_exchange(mocker) | ||||
|     mocker.patch.multiple( | ||||
|         'freqtrade.exchange.Exchange', | ||||
|         get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) | ||||
|     ) | ||||
|     conf = default_conf.copy() | ||||
|     conf['runmode'] = RunMode.LIVE | ||||
|     conf['order_types'] = { | ||||
|         'buy': 'market', | ||||
|         'sell': 'limit', | ||||
|         'stoploss': 'limit', | ||||
|         'stoploss_on_exchange': True, | ||||
|     } | ||||
|  | ||||
|     freqtrade = FreqtradeBot(conf) | ||||
|     assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) | ||||
|     assert freqtrade.strategy.order_types['stoploss_on_exchange'] | ||||
|  | ||||
|     caplog.clear() | ||||
|     # is left untouched | ||||
|     conf = default_conf.copy() | ||||
|     conf['runmode'] = RunMode.LIVE | ||||
|     conf['order_types'] = { | ||||
|         'buy': 'market', | ||||
|         'sell': 'limit', | ||||
|         'stoploss': 'limit', | ||||
|         'stoploss_on_exchange': False, | ||||
|     } | ||||
|     freqtrade = FreqtradeBot(conf) | ||||
|     assert not freqtrade.strategy.order_types['stoploss_on_exchange'] | ||||
|     assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) | ||||
|  | ||||
|  | ||||
| def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: | ||||
|     patch_RPCManager(mocker) | ||||
|     patch_exchange(mocker) | ||||
|     mocker.patch.multiple( | ||||
| @@ -262,7 +332,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker, | ||||
|  | ||||
|     # stoploss shoud be hit | ||||
|     assert freqtrade.handle_trade(trade) is True | ||||
|     assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog.record_tuples) | ||||
|     assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog) | ||||
|     assert trade.sell_reason == SellType.STOP_LOSS.value | ||||
|  | ||||
|  | ||||
| @@ -582,8 +652,7 @@ def test_create_trade_no_pairs_let(default_conf, ticker, limit_buy_order, fee, | ||||
|  | ||||
|     assert freqtrade.create_trade() | ||||
|     assert not freqtrade.create_trade() | ||||
|     assert log_has("No currency pair in whitelist, but checking to sell open trades.", | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has("No currency pair in whitelist, but checking to sell open trades.", caplog) | ||||
|  | ||||
|  | ||||
| def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, | ||||
| @@ -602,7 +671,7 @@ def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_orde | ||||
|     patch_get_signal(freqtrade) | ||||
|  | ||||
|     assert not freqtrade.create_trade() | ||||
|     assert log_has("Whitelist is empty.", caplog.record_tuples) | ||||
|     assert log_has("Whitelist is empty.", caplog) | ||||
|  | ||||
|  | ||||
| def test_create_trade_no_signal(default_conf, fee, mocker) -> None: | ||||
| @@ -657,8 +726,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, | ||||
|     assert trade.amount == 90.99181073703367 | ||||
|  | ||||
|     assert log_has( | ||||
|         'Buy signal found: about create a new trade with stake_amount: 0.001 ...', | ||||
|         caplog.record_tuples | ||||
|         'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -1025,7 +1093,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, | ||||
|     }) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) | ||||
|     assert freqtrade.handle_stoploss_on_exchange(trade) is True | ||||
|     assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog.record_tuples) | ||||
|     assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog) | ||||
|     assert trade.stoploss_order_id is None | ||||
|     assert trade.is_open is False | ||||
|  | ||||
| @@ -1034,7 +1102,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, | ||||
|         side_effect=DependencyException() | ||||
|     ) | ||||
|     freqtrade.handle_stoploss_on_exchange(trade) | ||||
|     assert log_has('Unable to place a stoploss order on exchange: ', caplog.record_tuples) | ||||
|     assert log_has('Unable to place a stoploss order on exchange: ', caplog) | ||||
|  | ||||
|     # Fifth case: get_order returns InvalidOrder | ||||
|     # It should try to add stoploss order | ||||
| @@ -1196,8 +1264,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c | ||||
|     mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) | ||||
|     freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) | ||||
|     assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", | ||||
|                       caplog.record_tuples) | ||||
|     assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog) | ||||
|  | ||||
|     # Still try to create order | ||||
|     assert stoploss_limit.call_count == 1 | ||||
| @@ -1208,8 +1275,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c | ||||
|     mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException()) | ||||
|     freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) | ||||
|     assert cancel_mock.call_count == 1 | ||||
|     assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", | ||||
|                       caplog.record_tuples) | ||||
|     assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", caplog) | ||||
|  | ||||
|  | ||||
| def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, | ||||
| @@ -1340,7 +1406,7 @@ def test_process_maybe_execute_buy_exception(mocker, default_conf, caplog) -> No | ||||
|         MagicMock(side_effect=DependencyException) | ||||
|     ) | ||||
|     freqtrade.process_maybe_execute_buy() | ||||
|     log_has('Unable to create trade:', caplog.record_tuples) | ||||
|     assert log_has('Unable to create trade: ', caplog) | ||||
|  | ||||
|  | ||||
| def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None: | ||||
| @@ -1358,8 +1424,7 @@ def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplo | ||||
|     assert not freqtrade.process_maybe_execute_sell(trade) | ||||
|     # Test amount not modified by fee-logic | ||||
|     assert not log_has( | ||||
|         'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), | ||||
|         caplog.record_tuples | ||||
|         'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog | ||||
|     ) | ||||
|  | ||||
|     mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) | ||||
| @@ -1382,7 +1447,7 @@ def test_process_maybe_execute_sell_exception(mocker, default_conf, | ||||
|         side_effect=DependencyException() | ||||
|     ) | ||||
|     freqtrade.process_maybe_execute_sell(trade) | ||||
|     assert log_has('Unable to sell trade: ', caplog.record_tuples) | ||||
|     assert log_has('Unable to sell trade: ', caplog) | ||||
|  | ||||
|  | ||||
| def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: | ||||
| @@ -1401,7 +1466,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No | ||||
|     trade.open_fee = 0.001 | ||||
|     freqtrade.update_trade_state(trade) | ||||
|     # Test amount not modified by fee-logic | ||||
|     assert not log_has_re(r'Applying fee to .*', caplog.record_tuples) | ||||
|     assert not log_has_re(r'Applying fee to .*', caplog) | ||||
|     assert trade.open_order_id is None | ||||
|     assert trade.amount == limit_buy_order['amount'] | ||||
|  | ||||
| @@ -1418,7 +1483,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No | ||||
|     # Assert we call handle_trade() if trade is feasible for execution | ||||
|     freqtrade.update_trade_state(trade) | ||||
|  | ||||
|     assert log_has_re('Found open order for.*', caplog.record_tuples) | ||||
|     assert log_has_re('Found open order for.*', caplog) | ||||
|  | ||||
|  | ||||
| def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, mocker): | ||||
| @@ -1457,7 +1522,7 @@ def test_update_trade_state_exception(mocker, default_conf, | ||||
|         side_effect=OperationalException() | ||||
|     ) | ||||
|     freqtrade.update_trade_state(trade) | ||||
|     assert log_has('Could not update trade amount: ', caplog.record_tuples) | ||||
|     assert log_has('Could not update trade amount: ', caplog) | ||||
|  | ||||
|  | ||||
| def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: | ||||
| @@ -1473,7 +1538,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None | ||||
|     grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock()) | ||||
|     freqtrade.update_trade_state(trade) | ||||
|     assert grm_mock.call_count == 0 | ||||
|     assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog.record_tuples) | ||||
|     assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) | ||||
|  | ||||
|  | ||||
| def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker): | ||||
| @@ -1632,7 +1697,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, | ||||
|     # if ROI is reached we must sell | ||||
|     patch_get_signal(freqtrade, value=(False, True)) | ||||
|     assert freqtrade.handle_trade(trade) | ||||
|     assert log_has('Required profit reached. Selling..', caplog.record_tuples) | ||||
|     assert log_has('Required profit reached. Selling..', caplog) | ||||
|  | ||||
|  | ||||
| def test_handle_trade_experimental( | ||||
| @@ -1662,7 +1727,7 @@ def test_handle_trade_experimental( | ||||
|  | ||||
|     patch_get_signal(freqtrade, value=(False, True)) | ||||
|     assert freqtrade.handle_trade(trade) | ||||
|     assert log_has('Sell signal received. Selling..', caplog.record_tuples) | ||||
|     assert log_has('Sell signal received. Selling..', caplog) | ||||
|  | ||||
|  | ||||
| def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, | ||||
| @@ -1768,7 +1833,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, | ||||
|     trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() | ||||
|     nb_trades = len(trades) | ||||
|     assert nb_trades == 0 | ||||
|     assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog.record_tuples) | ||||
|     assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog) | ||||
|  | ||||
|  | ||||
| def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, | ||||
| @@ -1881,7 +1946,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, | ||||
|     assert cancel_order_mock.call_count == 0 | ||||
|     assert rpc_mock.call_count == 1 | ||||
|     assert trade_sell.is_open is True | ||||
|     assert log_has_re("Sell order canceled on exchange for Trade.*", caplog.record_tuples) | ||||
|     assert log_has_re("Sell order canceled on exchange for Trade.*", caplog) | ||||
|  | ||||
|  | ||||
| def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, | ||||
| @@ -1959,7 +2024,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) - | ||||
|     freqtrade.check_handle_timedout() | ||||
|     assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, ' | ||||
|                       r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most ' | ||||
|                       r'recent call last\):\n.*', caplog.record_tuples) | ||||
|                       r'recent call last\):\n.*', caplog) | ||||
|  | ||||
|  | ||||
| def test_handle_timedout_limit_buy(mocker, default_conf) -> None: | ||||
| @@ -2183,7 +2248,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, | ||||
|     freqtrade.execute_sell(trade=trade, limit=1234, | ||||
|                            sell_reason=SellType.STOP_LOSS) | ||||
|     assert sellmock.call_count == 1 | ||||
|     assert log_has('Could not cancel stoploss order abcd', caplog.record_tuples) | ||||
|     assert log_has('Could not cancel stoploss order abcd', caplog) | ||||
|  | ||||
|  | ||||
| def test_execute_sell_with_stoploss_on_exchange(default_conf, | ||||
| @@ -2609,7 +2674,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, | ||||
|     assert freqtrade.handle_trade(trade) is True | ||||
|     assert log_has( | ||||
|         f'HIT STOP: current price at 0.000012, stop loss is 0.000015, ' | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) | ||||
|     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value | ||||
|  | ||||
|  | ||||
| @@ -2651,9 +2716,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets | ||||
|                  })) | ||||
|     # stop-loss not reached, adjusted stoploss | ||||
|     assert freqtrade.handle_trade(trade) is False | ||||
|     assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has(f'adjusted stop loss', caplog.record_tuples) | ||||
|     assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', caplog) | ||||
|     assert log_has(f'adjusted stop loss', caplog) | ||||
|     assert trade.stop_loss == 0.0000138501 | ||||
|  | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_ticker', | ||||
| @@ -2667,7 +2731,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets | ||||
|     assert log_has( | ||||
|         f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' | ||||
|         f'stop loss is {trade.stop_loss:.6f}, ' | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) | ||||
|  | ||||
|  | ||||
| def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, | ||||
| @@ -2710,9 +2774,8 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, | ||||
|                  })) | ||||
|     # stop-loss not reached, adjusted stoploss | ||||
|     assert freqtrade.handle_trade(trade) is False | ||||
|     assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has(f'adjusted stop loss', caplog.record_tuples) | ||||
|     assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', caplog) | ||||
|     assert log_has(f'adjusted stop loss', caplog) | ||||
|     assert trade.stop_loss == 0.0000138501 | ||||
|  | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_ticker', | ||||
| @@ -2726,7 +2789,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, | ||||
|     assert log_has( | ||||
|         f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' | ||||
|         f'stop loss is {trade.stop_loss:.6f}, ' | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) | ||||
|         f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog) | ||||
|     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value | ||||
|  | ||||
|  | ||||
| @@ -2777,7 +2840,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, | ||||
|     # stop-loss should not be adjusted as offset is not reached yet | ||||
|     assert freqtrade.handle_trade(trade) is False | ||||
|  | ||||
|     assert not log_has(f'adjusted stop loss', caplog.record_tuples) | ||||
|     assert not log_has(f'adjusted stop loss', caplog) | ||||
|     assert trade.stop_loss == 0.0000098910 | ||||
|  | ||||
|     # price rises above the offset (rises 12% when the offset is 5.5%) | ||||
| @@ -2789,9 +2852,8 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, | ||||
|                  })) | ||||
|  | ||||
|     assert freqtrade.handle_trade(trade) is False | ||||
|     assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has(f'adjusted stop loss', caplog.record_tuples) | ||||
|     assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', caplog) | ||||
|     assert log_has(f'adjusted stop loss', caplog) | ||||
|     assert trade.stop_loss == 0.0000117705 | ||||
|  | ||||
|  | ||||
| @@ -2850,7 +2912,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca | ||||
|     assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) | ||||
|     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) from Trades', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker): | ||||
| @@ -2873,7 +2935,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker): | ||||
|     assert freqtrade.get_real_amount(trade, buy_order_fee) == amount | ||||
|     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.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mocker): | ||||
| @@ -2962,7 +3024,7 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c | ||||
|     assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) | ||||
|     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) from Trades', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker): | ||||
| @@ -2988,7 +3050,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee | ||||
|     assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004 | ||||
|     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) from Order', | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, mocker): | ||||
|   | ||||
| @@ -60,8 +60,8 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None: | ||||
|     # Test Main + the KeyboardInterrupt exception | ||||
|     with pytest.raises(SystemExit): | ||||
|         main(args) | ||||
|     assert log_has('Using config: config.json.example ...', caplog.record_tuples) | ||||
|     assert log_has('Fatal exception!', caplog.record_tuples) | ||||
|     assert log_has('Using config: config.json.example ...', caplog) | ||||
|     assert log_has('Fatal exception!', caplog) | ||||
|  | ||||
|  | ||||
| def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: | ||||
| @@ -77,8 +77,8 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: | ||||
|     # Test Main + the KeyboardInterrupt exception | ||||
|     with pytest.raises(SystemExit): | ||||
|         main(args) | ||||
|     assert log_has('Using config: config.json.example ...', caplog.record_tuples) | ||||
|     assert log_has('SIGINT received, aborting ...', caplog.record_tuples) | ||||
|     assert log_has('Using config: config.json.example ...', caplog) | ||||
|     assert log_has('SIGINT received, aborting ...', caplog) | ||||
|  | ||||
|  | ||||
| def test_main_operational_exception(mocker, default_conf, caplog) -> None: | ||||
| @@ -97,8 +97,8 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: | ||||
|     # Test Main + the KeyboardInterrupt exception | ||||
|     with pytest.raises(SystemExit): | ||||
|         main(args) | ||||
|     assert log_has('Using config: config.json.example ...', caplog.record_tuples) | ||||
|     assert log_has('Oh snap!', caplog.record_tuples) | ||||
|     assert log_has('Using config: config.json.example ...', caplog) | ||||
|     assert log_has('Oh snap!', caplog) | ||||
|  | ||||
|  | ||||
| def test_main_reload_conf(mocker, default_conf, caplog) -> None: | ||||
| @@ -121,7 +121,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: | ||||
|     with pytest.raises(SystemExit): | ||||
|         main(['-c', 'config.json.example']) | ||||
|  | ||||
|     assert log_has('Using config: config.json.example ...', caplog.record_tuples) | ||||
|     assert log_has('Using config: config.json.example ...', caplog) | ||||
|     assert worker_mock.call_count == 4 | ||||
|     assert reconfigure_mock.call_count == 1 | ||||
|     assert isinstance(worker.freqtrade, FreqtradeBot) | ||||
|   | ||||
| @@ -151,7 +151,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee, caplog): | ||||
|     assert trade.close_date is None | ||||
|     assert log_has("LIMIT_BUY has been fulfilled for Trade(id=2, " | ||||
|                    "pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     caplog.clear() | ||||
|     trade.open_order_id = 'something' | ||||
| @@ -162,7 +162,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee, caplog): | ||||
|     assert trade.close_date is not None | ||||
|     assert log_has("LIMIT_SELL has been fulfilled for Trade(id=2, " | ||||
|                    "pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures("init_persistence") | ||||
| @@ -184,7 +184,7 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): | ||||
|     assert trade.close_date is None | ||||
|     assert log_has("MARKET_BUY has been fulfilled for Trade(id=1, " | ||||
|                    "pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|     caplog.clear() | ||||
|     trade.open_order_id = 'something' | ||||
| @@ -195,7 +195,7 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): | ||||
|     assert trade.close_date is not None | ||||
|     assert log_has("MARKET_SELL has been fulfilled for Trade(id=1, " | ||||
|                    "pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures("init_persistence") | ||||
| @@ -558,10 +558,9 @@ def test_migrate_new(mocker, default_conf, fee, caplog): | ||||
|     assert trade.ticker_interval is None | ||||
|     assert trade.stoploss_order_id is None | ||||
|     assert trade.stoploss_last_update is None | ||||
|     assert log_has("trying trades_bak1", caplog.record_tuples) | ||||
|     assert log_has("trying trades_bak2", caplog.record_tuples) | ||||
|     assert log_has("Running database migration - backup available as trades_bak2", | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has("trying trades_bak1", caplog) | ||||
|     assert log_has("trying trades_bak2", caplog) | ||||
|     assert log_has("Running database migration - backup available as trades_bak2", caplog) | ||||
|  | ||||
|  | ||||
| def test_migrate_mid_state(mocker, default_conf, fee, caplog): | ||||
| @@ -621,9 +620,8 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog): | ||||
|     assert trade.max_rate == 0.0 | ||||
|     assert trade.stop_loss == 0.0 | ||||
|     assert trade.initial_stop_loss == 0.0 | ||||
|     assert log_has("trying trades_bak0", caplog.record_tuples) | ||||
|     assert log_has("Running database migration - backup available as trades_bak0", | ||||
|                    caplog.record_tuples) | ||||
|     assert log_has("trying trades_bak0", caplog) | ||||
|     assert log_has("Running database migration - backup available as trades_bak0", caplog) | ||||
|  | ||||
|  | ||||
| def test_adjust_stop_loss(fee): | ||||
|   | ||||
| @@ -87,7 +87,7 @@ def test_add_indicators(default_conf, caplog): | ||||
|     # No indicator found | ||||
|     fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators=['no_indicator'], data=data) | ||||
|     assert fig == fig3 | ||||
|     assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog.record_tuples) | ||||
|     assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) | ||||
|  | ||||
|  | ||||
| def test_plot_trades(caplog): | ||||
| @@ -95,7 +95,7 @@ def test_plot_trades(caplog): | ||||
|     # nothing happens when no trades are available | ||||
|     fig = plot_trades(fig1, None) | ||||
|     assert fig == fig1 | ||||
|     assert log_has("No trades found.", caplog.record_tuples) | ||||
|     assert log_has("No trades found.", caplog) | ||||
|     pair = "ADA/BTC" | ||||
|     filename = history.make_testdata_path(None) / "backtest-result_test.json" | ||||
|     trades = load_backtest_data(filename) | ||||
| @@ -150,8 +150,8 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, c | ||||
|     assert row_mock.call_count == 2 | ||||
|     assert trades_mock.call_count == 1 | ||||
|  | ||||
|     assert log_has("No buy-signals found.", caplog.record_tuples) | ||||
|     assert log_has("No sell-signals found.", caplog.record_tuples) | ||||
|     assert log_has("No buy-signals found.", caplog) | ||||
|     assert log_has("No sell-signals found.", caplog) | ||||
|  | ||||
|  | ||||
| def test_generate_candlestick_graph_no_trades(default_conf, mocker): | ||||
| @@ -216,7 +216,7 @@ def test_generate_plot_file(mocker, caplog): | ||||
|     assert (plot_mock.call_args_list[0][1]['filename'] | ||||
|             == "user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html") | ||||
|     assert log_has("Stored plot as user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html", | ||||
|                    caplog.record_tuples) | ||||
|                    caplog) | ||||
|  | ||||
|  | ||||
| def test_add_profit(): | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| # requirements without requirements installable via conda | ||||
| # mainly used for Raspberry pi installs | ||||
| ccxt==1.18.1021 | ||||
| ccxt==1.18.1043 | ||||
| SQLAlchemy==1.3.6 | ||||
| python-telegram-bot==11.1.0 | ||||
| arrow==0.14.4 | ||||
| arrow==0.14.5 | ||||
| cachetools==3.1.1 | ||||
| requests==2.22.0 | ||||
| urllib3==1.25.3 | ||||
| @@ -23,7 +23,7 @@ filelock==3.0.12 | ||||
| py_find_1st==1.1.4 | ||||
|  | ||||
| #Load ticker files 30% faster | ||||
| python-rapidjson==0.7.2 | ||||
| python-rapidjson==0.8.0 | ||||
|  | ||||
| # Notify systemd | ||||
| sdnotify==0.3.2 | ||||
|   | ||||
| @@ -3,4 +3,4 @@ | ||||
|  | ||||
| numpy==1.17.0 | ||||
| pandas==0.25.0 | ||||
| scipy==1.3.0 | ||||
| scipy==1.3.1 | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement | ||||
|  | ||||
| from functools import reduce | ||||
| from math import exp | ||||
| from typing import Any, Callable, Dict, List | ||||
| from datetime import datetime | ||||
|  | ||||
| import numpy as np# noqa F401 | ||||
| import numpy as np | ||||
| import talib.abstract as ta | ||||
| from pandas import DataFrame | ||||
| from skopt.space import Categorical, Dimension, Integer, Real | ||||
| @@ -16,7 +15,7 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt | ||||
|  | ||||
| class SampleHyperOpts(IHyperOpt): | ||||
|     """ | ||||
|     This is a sample hyperopt to inspire you. | ||||
|     This is a sample Hyperopt to inspire you. | ||||
|     Feel free to customize it. | ||||
|  | ||||
|     More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md | ||||
| @@ -37,32 +36,44 @@ class SampleHyperOpts(IHyperOpt): | ||||
|     """ | ||||
|     @staticmethod | ||||
|     def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Add several indicators needed for buy and sell strategies defined below. | ||||
|         """ | ||||
|         # ADX | ||||
|         dataframe['adx'] = ta.ADX(dataframe) | ||||
|         # MACD | ||||
|         macd = ta.MACD(dataframe) | ||||
|         dataframe['macd'] = macd['macd'] | ||||
|         dataframe['macdsignal'] = macd['macdsignal'] | ||||
|         # MFI | ||||
|         dataframe['mfi'] = ta.MFI(dataframe) | ||||
|         # RSI | ||||
|         dataframe['rsi'] = ta.RSI(dataframe) | ||||
|         # Stochastic Fast | ||||
|         stoch_fast = ta.STOCHF(dataframe) | ||||
|         dataframe['fastd'] = stoch_fast['fastd'] | ||||
|         # Minus-DI | ||||
|         dataframe['minus_di'] = ta.MINUS_DI(dataframe) | ||||
|         # Bollinger bands | ||||
|         bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) | ||||
|         dataframe['bb_lowerband'] = bollinger['lower'] | ||||
|         dataframe['bb_upperband'] = bollinger['upper'] | ||||
|         # SAR | ||||
|         dataframe['sar'] = ta.SAR(dataframe) | ||||
|  | ||||
|         return dataframe | ||||
|  | ||||
|     @staticmethod | ||||
|     def buy_strategy_generator(params: Dict[str, Any]) -> Callable: | ||||
|         """ | ||||
|         Define the buy strategy parameters to be used by hyperopt | ||||
|         Define the buy strategy parameters to be used by Hyperopt. | ||||
|         """ | ||||
|         def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|             """ | ||||
|             Buy strategy Hyperopt will build and use | ||||
|             Buy strategy Hyperopt will build and use. | ||||
|             """ | ||||
|             conditions = [] | ||||
|  | ||||
|             # GUARDS AND TRENDS | ||||
|             if 'mfi-enabled' in params and params['mfi-enabled']: | ||||
|                 conditions.append(dataframe['mfi'] < params['mfi-value']) | ||||
| @@ -98,7 +109,7 @@ class SampleHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def indicator_space() -> List[Dimension]: | ||||
|         """ | ||||
|         Define your Hyperopt space for searching strategy parameters | ||||
|         Define your Hyperopt space for searching buy strategy parameters. | ||||
|         """ | ||||
|         return [ | ||||
|             Integer(10, 25, name='mfi-value'), | ||||
| @@ -115,14 +126,14 @@ class SampleHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def sell_strategy_generator(params: Dict[str, Any]) -> Callable: | ||||
|         """ | ||||
|         Define the sell strategy parameters to be used by hyperopt | ||||
|         Define the sell strategy parameters to be used by Hyperopt. | ||||
|         """ | ||||
|         def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|             """ | ||||
|             Sell strategy Hyperopt will build and use | ||||
|             Sell strategy Hyperopt will build and use. | ||||
|             """ | ||||
|             # print(params) | ||||
|             conditions = [] | ||||
|  | ||||
|             # GUARDS AND TRENDS | ||||
|             if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: | ||||
|                 conditions.append(dataframe['mfi'] > params['sell-mfi-value']) | ||||
| @@ -158,7 +169,7 @@ class SampleHyperOpts(IHyperOpt): | ||||
|     @staticmethod | ||||
|     def sell_indicator_space() -> List[Dimension]: | ||||
|         """ | ||||
|         Define your Hyperopt space for searching sell strategy parameters | ||||
|         Define your Hyperopt space for searching sell strategy parameters. | ||||
|         """ | ||||
|         return [ | ||||
|             Integer(75, 100, name='sell-mfi-value'), | ||||
| @@ -176,9 +187,9 @@ class SampleHyperOpts(IHyperOpt): | ||||
|  | ||||
|     def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Based on TA indicators. Should be a copy of from strategy | ||||
|         must align to populate_indicators in this file | ||||
|         Only used when --spaces does not include buy | ||||
|         Based on TA indicators. Should be a copy of same method from strategy. | ||||
|         Must align to populate_indicators in this file. | ||||
|         Only used when --spaces does not include buy space. | ||||
|         """ | ||||
|         dataframe.loc[ | ||||
|             ( | ||||
| @@ -193,9 +204,9 @@ class SampleHyperOpts(IHyperOpt): | ||||
|  | ||||
|     def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||
|         """ | ||||
|         Based on TA indicators. Should be a copy of from strategy | ||||
|         must align to populate_indicators in this file | ||||
|         Only used when --spaces does not include sell | ||||
|         Based on TA indicators. Should be a copy of same method from strategy. | ||||
|         Must align to populate_indicators in this file. | ||||
|         Only used when --spaces does not include sell space. | ||||
|         """ | ||||
|         dataframe.loc[ | ||||
|             ( | ||||
| @@ -205,4 +216,5 @@ class SampleHyperOpts(IHyperOpt): | ||||
|                 (dataframe['fastd'] > 54) | ||||
|             ), | ||||
|             'sell'] = 1 | ||||
|  | ||||
|         return dataframe | ||||
|   | ||||
		Reference in New Issue
	
	Block a user