Merge pull request #1879 from freqtrade/refactor_optimize__init__
Speed up startup time
This commit is contained in:
		| @@ -340,25 +340,25 @@ class Arguments(object): | ||||
|         Builds and attaches all subcommands | ||||
|         :return: None | ||||
|         """ | ||||
|         from freqtrade.optimize import backtesting, hyperopt, edge_cli | ||||
|         from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge | ||||
|  | ||||
|         subparsers = self.parser.add_subparsers(dest='subparser') | ||||
|  | ||||
|         # Add backtesting subcommand | ||||
|         backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.') | ||||
|         backtesting_cmd.set_defaults(func=backtesting.start) | ||||
|         backtesting_cmd.set_defaults(func=start_backtesting) | ||||
|         self.optimizer_shared_options(backtesting_cmd) | ||||
|         self.backtesting_options(backtesting_cmd) | ||||
|  | ||||
|         # Add edge subcommand | ||||
|         edge_cmd = subparsers.add_parser('edge', help='Edge module.') | ||||
|         edge_cmd.set_defaults(func=edge_cli.start) | ||||
|         edge_cmd.set_defaults(func=start_edge) | ||||
|         self.optimizer_shared_options(edge_cmd) | ||||
|         self.edge_options(edge_cmd) | ||||
|  | ||||
|         # Add hyperopt subcommand | ||||
|         hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.') | ||||
|         hyperopt_cmd.set_defaults(func=hyperopt.start) | ||||
|         hyperopt_cmd.set_defaults(func=start_hyperopt) | ||||
|         self.optimizer_shared_options(hyperopt_cmd) | ||||
|         self.hyperopt_options(hyperopt_cmd) | ||||
|  | ||||
|   | ||||
| @@ -5,19 +5,21 @@ Includes: | ||||
| * load data for a pair (or a list of pairs) from disk | ||||
| * download data from exchange and store to disk | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| import operator | ||||
| from datetime import datetime | ||||
| from pathlib import Path | ||||
| from typing import Optional, List, Dict, Tuple, Any | ||||
| from typing import Any, Dict, List, Optional, Tuple | ||||
|  | ||||
| import arrow | ||||
| from pandas import DataFrame | ||||
|  | ||||
| from freqtrade import misc, OperationalException | ||||
| from freqtrade import OperationalException, misc | ||||
| from freqtrade.arguments import TimeRange | ||||
| from freqtrade.data.converter import parse_ticker_dataframe | ||||
| from freqtrade.exchange import Exchange, timeframe_to_minutes | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| @@ -243,3 +245,39 @@ def download_pair_history(datadir: Optional[Path], | ||||
|             f'Error: {e}' | ||||
|         ) | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]: | ||||
|     """ | ||||
|     Get the maximum timeframe for the given backtest data | ||||
|     :param data: dictionary with preprocessed backtesting data | ||||
|     :return: tuple containing min_date, max_date | ||||
|     """ | ||||
|     timeframe = [ | ||||
|         (arrow.get(frame['date'].min()), arrow.get(frame['date'].max())) | ||||
|         for frame in data.values() | ||||
|     ] | ||||
|     return min(timeframe, key=operator.itemgetter(0))[0], \ | ||||
|         max(timeframe, key=operator.itemgetter(1))[1] | ||||
|  | ||||
|  | ||||
| def validate_backtest_data(data: Dict[str, DataFrame], min_date: datetime, | ||||
|                            max_date: datetime, ticker_interval_mins: int) -> bool: | ||||
|     """ | ||||
|     Validates preprocessed backtesting data for missing values and shows warnings about it that. | ||||
|  | ||||
|     :param data: dictionary with preprocessed backtesting data | ||||
|     :param min_date: start-date of the data | ||||
|     :param max_date: end-date of the data | ||||
|     :param ticker_interval_mins: ticker interval in minutes | ||||
|     """ | ||||
|     # total difference in minutes / interval-minutes | ||||
|     expected_frames = int((max_date - min_date).total_seconds() // 60 // ticker_interval_mins) | ||||
|     found_missing = False | ||||
|     for pair, df in data.items(): | ||||
|         dflen = len(df) | ||||
|         if dflen < expected_frames: | ||||
|             found_missing = True | ||||
|             logger.warning("%s has missing frames: expected %s, got %s, that's %s missing values", | ||||
|                            pair, expected_frames, dflen, expected_frames - dflen) | ||||
|     return found_missing | ||||
|   | ||||
| @@ -13,7 +13,6 @@ from freqtrade import constants, OperationalException | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.arguments import TimeRange | ||||
| from freqtrade.data import history | ||||
| from freqtrade.optimize import get_timeframe | ||||
| from freqtrade.strategy.interface import SellType | ||||
|  | ||||
|  | ||||
| @@ -49,7 +48,6 @@ class Edge(): | ||||
|         self.strategy = strategy | ||||
|         self.ticker_interval = self.strategy.ticker_interval | ||||
|         self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe | ||||
|         self.get_timeframe = get_timeframe | ||||
|         self.advise_sell = self.strategy.advise_sell | ||||
|         self.advise_buy = self.strategy.advise_buy | ||||
|  | ||||
| @@ -117,7 +115,7 @@ class Edge(): | ||||
|         preprocessed = self.tickerdata_to_dataframe(data) | ||||
|  | ||||
|         # Print timeframe | ||||
|         min_date, max_date = self.get_timeframe(preprocessed) | ||||
|         min_date, max_date = history.get_timeframe(preprocessed) | ||||
|         logger.info( | ||||
|             'Measuring data from %s up to %s (%s days) ...', | ||||
|             min_date.isoformat(), | ||||
|   | ||||
| @@ -1,49 +1,115 @@ | ||||
| # pragma pylint: disable=missing-docstring | ||||
|  | ||||
| import logging | ||||
| from datetime import datetime | ||||
| from typing import Dict, Tuple | ||||
| import operator | ||||
| from argparse import Namespace | ||||
| from typing import Any, Dict | ||||
|  | ||||
| import arrow | ||||
| from pandas import DataFrame | ||||
| from filelock import FileLock, Timeout | ||||
|  | ||||
| from freqtrade.optimize.default_hyperopt import DefaultHyperOpts  # noqa: F401 | ||||
| from freqtrade import DependencyException, constants | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.state import RunMode | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]: | ||||
| def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]: | ||||
|     """ | ||||
|     Get the maximum timeframe for the given backtest data | ||||
|     :param data: dictionary with preprocessed backtesting data | ||||
|     :return: tuple containing min_date, max_date | ||||
|     Prepare the configuration for the Hyperopt module | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: Configuration | ||||
|     """ | ||||
|     timeframe = [ | ||||
|         (arrow.get(frame['date'].min()), arrow.get(frame['date'].max())) | ||||
|         for frame in data.values() | ||||
|     ] | ||||
|     return min(timeframe, key=operator.itemgetter(0))[0], \ | ||||
|         max(timeframe, key=operator.itemgetter(1))[1] | ||||
|     configuration = Configuration(args, method) | ||||
|     config = configuration.load_config() | ||||
|  | ||||
|     # Ensure we do not use Exchange credentials | ||||
|     config['exchange']['key'] = '' | ||||
|     config['exchange']['secret'] = '' | ||||
|  | ||||
|     if method == RunMode.BACKTEST: | ||||
|         if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT: | ||||
|             raise DependencyException('stake amount could not be "%s" for backtesting' % | ||||
|                                       constants.UNLIMITED_STAKE_AMOUNT) | ||||
|  | ||||
|     if method == RunMode.HYPEROPT: | ||||
|         # Special cases for Hyperopt | ||||
|         if config.get('strategy') and config.get('strategy') != 'DefaultStrategy': | ||||
|             logger.error("Please don't use --strategy for hyperopt.") | ||||
|             logger.error( | ||||
|                 "Read the documentation at " | ||||
|                 "https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md " | ||||
|                 "to understand how to configure hyperopt.") | ||||
|             raise DependencyException("--strategy configured but not supported for hyperopt") | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def validate_backtest_data(data: Dict[str, DataFrame], min_date: datetime, | ||||
|                            max_date: datetime, ticker_interval_mins: int) -> bool: | ||||
| def start_backtesting(args: Namespace) -> None: | ||||
|     """ | ||||
|     Validates preprocessed backtesting data for missing values and shows warnings about it that. | ||||
|     Start Backtesting script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     # Import here to avoid loading backtesting module when it's not used | ||||
|     from freqtrade.optimize.backtesting import Backtesting | ||||
|  | ||||
|     :param data: dictionary with preprocessed backtesting data | ||||
|     :param min_date: start-date of the data | ||||
|     :param max_date: end-date of the data | ||||
|     :param ticker_interval_mins: ticker interval in minutes | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args, RunMode.BACKTEST) | ||||
|  | ||||
|     logger.info('Starting freqtrade in Backtesting mode') | ||||
|  | ||||
|     # Initialize backtesting object | ||||
|     backtesting = Backtesting(config) | ||||
|     backtesting.start() | ||||
|  | ||||
|  | ||||
| def start_hyperopt(args: Namespace) -> None: | ||||
|     """ | ||||
|     # total difference in minutes / interval-minutes | ||||
|     expected_frames = int((max_date - min_date).total_seconds() // 60 // ticker_interval_mins) | ||||
|     found_missing = False | ||||
|     for pair, df in data.items(): | ||||
|         dflen = len(df) | ||||
|         if dflen < expected_frames: | ||||
|             found_missing = True | ||||
|             logger.warning("%s has missing frames: expected %s, got %s, that's %s missing values", | ||||
|                            pair, expected_frames, dflen, expected_frames - dflen) | ||||
|     return found_missing | ||||
|     Start hyperopt script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     # Import here to avoid loading hyperopt module when it's not used | ||||
|     from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE | ||||
|  | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args, RunMode.HYPEROPT) | ||||
|  | ||||
|     logger.info('Starting freqtrade in Hyperopt mode') | ||||
|  | ||||
|     lock = FileLock(HYPEROPT_LOCKFILE) | ||||
|  | ||||
|     try: | ||||
|         with lock.acquire(timeout=1): | ||||
|  | ||||
|             # Remove noisy log messages | ||||
|             logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING) | ||||
|             logging.getLogger('filelock').setLevel(logging.WARNING) | ||||
|  | ||||
|             # Initialize backtesting object | ||||
|             hyperopt = Hyperopt(config) | ||||
|             hyperopt.start() | ||||
|  | ||||
|     except Timeout: | ||||
|         logger.info("Another running instance of freqtrade Hyperopt detected.") | ||||
|         logger.info("Simultaneous execution of multiple Hyperopt commands is not supported. " | ||||
|                     "Hyperopt module is resource hungry. Please run your Hyperopts sequentially " | ||||
|                     "or on separate machines.") | ||||
|         logger.info("Quitting now.") | ||||
|         # TODO: return False here in order to help freqtrade to exit | ||||
|         # with non-zero exit code... | ||||
|         # Same in Edge and Backtesting start() functions. | ||||
|  | ||||
|  | ||||
| def start_edge(args: Namespace) -> None: | ||||
|     """ | ||||
|     Start Edge script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     from freqtrade.optimize.edge_cli import EdgeCli | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args, RunMode.EDGE) | ||||
|     logger.info('Starting freqtrade in Edge mode') | ||||
|  | ||||
|     # Initialize Edge object | ||||
|     edge_cli = EdgeCli(config) | ||||
|     edge_cli.start() | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
| This module contains the backtesting logic | ||||
| """ | ||||
| import logging | ||||
| from argparse import Namespace | ||||
| from copy import deepcopy | ||||
| from datetime import datetime, timedelta | ||||
| from pathlib import Path | ||||
| @@ -13,10 +12,7 @@ from typing import Any, Dict, List, NamedTuple, Optional | ||||
| from pandas import DataFrame | ||||
| from tabulate import tabulate | ||||
|  | ||||
| from freqtrade import optimize | ||||
| from freqtrade import DependencyException, constants | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.data import history | ||||
| from freqtrade.data.dataprovider import DataProvider | ||||
| from freqtrade.exchange import timeframe_to_minutes | ||||
| @@ -24,8 +20,7 @@ from freqtrade.misc import file_dump_json | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.resolvers import ExchangeResolver, StrategyResolver | ||||
| from freqtrade.state import RunMode | ||||
| from freqtrade.strategy.interface import SellType, IStrategy | ||||
|  | ||||
| from freqtrade.strategy.interface import IStrategy, SellType | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -440,10 +435,10 @@ class Backtesting(object): | ||||
|             logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) | ||||
|             self._set_strategy(strat) | ||||
|  | ||||
|             min_date, max_date = optimize.get_timeframe(data) | ||||
|             min_date, max_date = history.get_timeframe(data) | ||||
|             # Validate dataframe for missing values (mainly at start and end, as fillup is called) | ||||
|             optimize.validate_backtest_data(data, min_date, max_date, | ||||
|                                             timeframe_to_minutes(self.ticker_interval)) | ||||
|             history.validate_backtest_data(data, min_date, max_date, | ||||
|                                            timeframe_to_minutes(self.ticker_interval)) | ||||
|             logger.info( | ||||
|                 'Backtesting with data from %s up to %s (%s days)..', | ||||
|                 min_date.isoformat(), | ||||
| @@ -486,39 +481,3 @@ class Backtesting(object): | ||||
|             print(' Strategy Summary '.center(133, '=')) | ||||
|             print(self._generate_text_table_strategy(all_results)) | ||||
|             print('\nFor more details, please look at the detail tables above') | ||||
|  | ||||
|  | ||||
| def setup_configuration(args: Namespace) -> Dict[str, Any]: | ||||
|     """ | ||||
|     Prepare the configuration for the backtesting | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: Configuration | ||||
|     """ | ||||
|     configuration = Configuration(args, RunMode.BACKTEST) | ||||
|     config = configuration.get_config() | ||||
|  | ||||
|     # Ensure we do not use Exchange credentials | ||||
|     config['exchange']['key'] = '' | ||||
|     config['exchange']['secret'] = '' | ||||
|  | ||||
|     if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT: | ||||
|         raise DependencyException('stake amount could not be "%s" for backtesting' % | ||||
|                                   constants.UNLIMITED_STAKE_AMOUNT) | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def start(args: Namespace) -> None: | ||||
|     """ | ||||
|     Start Backtesting script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args) | ||||
|  | ||||
|     logger.info('Starting freqtrade in Backtesting mode') | ||||
|  | ||||
|     # Initialize backtesting object | ||||
|     backtesting = Backtesting(config) | ||||
|     backtesting.start() | ||||
|   | ||||
| @@ -4,16 +4,13 @@ | ||||
| This module contains the edge backtesting interface | ||||
| """ | ||||
| import logging | ||||
| from argparse import Namespace | ||||
| from typing import Dict, Any | ||||
| from tabulate import tabulate | ||||
| from freqtrade.edge import Edge | ||||
|  | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.exchange import Exchange | ||||
| from freqtrade.resolvers import StrategyResolver | ||||
| from freqtrade.state import RunMode | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -77,34 +74,3 @@ class EdgeCli(object): | ||||
|         if result: | ||||
|             print('')  # blank line for readability | ||||
|             print(self._generate_edge_table(self.edge._cached_pairs)) | ||||
|  | ||||
|  | ||||
| def setup_configuration(args: Namespace) -> Dict[str, Any]: | ||||
|     """ | ||||
|     Prepare the configuration for edge backtesting | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: Configuration | ||||
|     """ | ||||
|     configuration = Configuration(args, RunMode.EDGECLI) | ||||
|     config = configuration.get_config() | ||||
|  | ||||
|     # Ensure we do not use Exchange credentials | ||||
|     config['exchange']['key'] = '' | ||||
|     config['exchange']['secret'] = '' | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def start(args: Namespace) -> None: | ||||
|     """ | ||||
|     Start Edge script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args) | ||||
|     logger.info('Starting freqtrade in Edge mode') | ||||
|  | ||||
|     # Initialize Edge object | ||||
|     edge_cli = EdgeCli(config) | ||||
|     edge_cli.start() | ||||
|   | ||||
| @@ -7,28 +7,22 @@ This module contains the hyperopt logic | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
| from argparse import Namespace | ||||
| from math import exp | ||||
| from operator import itemgetter | ||||
| from pathlib import Path | ||||
| from pprint import pprint | ||||
| from typing import Any, Dict, List | ||||
|  | ||||
| from filelock import Timeout, FileLock | ||||
| from joblib import Parallel, delayed, dump, load, wrap_non_picklable_objects, cpu_count | ||||
| from pandas import DataFrame | ||||
| from skopt import Optimizer | ||||
| from skopt.space import Dimension | ||||
|  | ||||
| from freqtrade import DependencyException | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.data.history import load_data | ||||
| from freqtrade.data.history import load_data, get_timeframe, validate_backtest_data | ||||
| from freqtrade.exchange import timeframe_to_minutes | ||||
| from freqtrade.optimize import get_timeframe, validate_backtest_data | ||||
| from freqtrade.optimize.backtesting import Backtesting | ||||
| from freqtrade.state import RunMode | ||||
| from freqtrade.resolvers import HyperOptResolver | ||||
| from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| @@ -343,62 +337,3 @@ class Hyperopt(Backtesting): | ||||
|  | ||||
|         self.save_trials() | ||||
|         self.log_trials_result() | ||||
|  | ||||
|  | ||||
| def setup_configuration(args: Namespace) -> Dict[str, Any]: | ||||
|     """ | ||||
|     Prepare the configuration for the Hyperopt module | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: Configuration | ||||
|     """ | ||||
|     configuration = Configuration(args, RunMode.HYPEROPT) | ||||
|     config = configuration.load_config() | ||||
|  | ||||
|     # Ensure we do not use Exchange credentials | ||||
|     config['exchange']['key'] = '' | ||||
|     config['exchange']['secret'] = '' | ||||
|  | ||||
|     if config.get('strategy') and config.get('strategy') != 'DefaultStrategy': | ||||
|         logger.error("Please don't use --strategy for hyperopt.") | ||||
|         logger.error( | ||||
|             "Read the documentation at " | ||||
|             "https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md " | ||||
|             "to understand how to configure hyperopt.") | ||||
|         raise DependencyException("--strategy configured but not supported for hyperopt") | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def start(args: Namespace) -> None: | ||||
|     """ | ||||
|     Start Backtesting script | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args) | ||||
|  | ||||
|     logger.info('Starting freqtrade in Hyperopt mode') | ||||
|  | ||||
|     lock = FileLock(HYPEROPT_LOCKFILE) | ||||
|  | ||||
|     try: | ||||
|         with lock.acquire(timeout=1): | ||||
|  | ||||
|             # Remove noisy log messages | ||||
|             logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING) | ||||
|             logging.getLogger('filelock').setLevel(logging.WARNING) | ||||
|  | ||||
|             # Initialize backtesting object | ||||
|             hyperopt = Hyperopt(config) | ||||
|             hyperopt.start() | ||||
|  | ||||
|     except Timeout: | ||||
|         logger.info("Another running instance of freqtrade Hyperopt detected.") | ||||
|         logger.info("Simultaneous execution of multiple Hyperopt commands is not supported. " | ||||
|                     "Hyperopt module is resource hungry. Please run your Hyperopts sequentially " | ||||
|                     "or on separate machines.") | ||||
|         logger.info("Quitting now.") | ||||
|         # TODO: return False here in order to help freqtrade to exit | ||||
|         # with non-zero exit code... | ||||
|         # Same in Edge and Backtesting start() functions. | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| from freqtrade.resolvers.iresolver import IResolver  # noqa: F401 | ||||
| from freqtrade.resolvers.exchange_resolver import ExchangeResolver  # noqa: F401 | ||||
| from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver  # noqa: F401 | ||||
| # Don't import HyperoptResolver to avoid loading the whole Optimize tree | ||||
| # from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver  # noqa: F401 | ||||
| from freqtrade.resolvers.pairlist_resolver import PairListResolver  # noqa: F401 | ||||
| from freqtrade.resolvers.strategy_resolver import StrategyResolver  # noqa: F401 | ||||
|   | ||||
| @@ -18,11 +18,11 @@ class State(Enum): | ||||
| class RunMode(Enum): | ||||
|     """ | ||||
|     Bot running mode (backtest, hyperopt, ...) | ||||
|     can be "live", "dry-run", "backtest", "edgecli", "hyperopt". | ||||
|     can be "live", "dry-run", "backtest", "edge", "hyperopt". | ||||
|     """ | ||||
|     LIVE = "live" | ||||
|     DRY_RUN = "dry_run" | ||||
|     BACKTEST = "backtest" | ||||
|     EDGECLI = "edgecli" | ||||
|     EDGE = "edge" | ||||
|     HYPEROPT = "hyperopt" | ||||
|     OTHER = "other"  # Used for plotting scripts and test | ||||
|   | ||||
| @@ -2,8 +2,7 @@ | ||||
| import logging | ||||
|  | ||||
| from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data | ||||
| from freqtrade.data.history import load_pair_history | ||||
| from freqtrade.optimize import validate_backtest_data, get_timeframe | ||||
| from freqtrade.data.history import load_pair_history, validate_backtest_data, get_timeframe | ||||
| from freqtrade.tests.conftest import log_has | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -2,24 +2,25 @@ | ||||
|  | ||||
| import json | ||||
| import os | ||||
| from pathlib import Path | ||||
| import uuid | ||||
| from pathlib import Path | ||||
| from shutil import copyfile | ||||
|  | ||||
| import arrow | ||||
| from pandas import DataFrame | ||||
| import pytest | ||||
| from pandas import DataFrame | ||||
|  | ||||
| from freqtrade import OperationalException | ||||
| from freqtrade.arguments import TimeRange | ||||
| from freqtrade.data import history | ||||
| from freqtrade.data.history import (download_pair_history, | ||||
|                                     load_cached_data_for_updating, | ||||
|                                     load_tickerdata_file, | ||||
|                                     make_testdata_path, | ||||
|                                     load_tickerdata_file, make_testdata_path, | ||||
|                                     trim_tickerlist) | ||||
| from freqtrade.exchange import timeframe_to_minutes | ||||
| from freqtrade.misc import file_dump_json | ||||
| from freqtrade.tests.conftest import get_patched_exchange, log_has | ||||
| from freqtrade.strategy.default_strategy import DefaultStrategy | ||||
| from freqtrade.tests.conftest import get_patched_exchange, log_has, patch_exchange | ||||
|  | ||||
| # Change this if modifying UNITTEST/BTC testdatafile | ||||
| _BTC_UNITTEST_LENGTH = 13681 | ||||
| @@ -495,3 +496,62 @@ def test_file_dump_json_tofile() -> None: | ||||
|  | ||||
|     # Remove the file | ||||
|     _clean_test_file(file) | ||||
|  | ||||
|  | ||||
| def test_get_timeframe(default_conf, mocker) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='1m', | ||||
|             pairs=['UNITTEST/BTC'] | ||||
|         ) | ||||
|     ) | ||||
|     min_date, max_date = history.get_timeframe(data) | ||||
|     assert min_date.isoformat() == '2017-11-04T23:02:00+00:00' | ||||
|     assert max_date.isoformat() == '2017-11-14T22:58:00+00:00' | ||||
|  | ||||
|  | ||||
| def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='1m', | ||||
|             pairs=['UNITTEST/BTC'], | ||||
|             fill_up_missing=False | ||||
|         ) | ||||
|     ) | ||||
|     min_date, max_date = history.get_timeframe(data) | ||||
|     caplog.clear() | ||||
|     assert history.validate_backtest_data(data, min_date, max_date, | ||||
|                                           timeframe_to_minutes('1m')) | ||||
|     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) | ||||
|  | ||||
|  | ||||
| def test_validate_backtest_data(default_conf, mocker, caplog) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     timerange = TimeRange('index', 'index', 200, 250) | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='5m', | ||||
|             pairs=['UNITTEST/BTC'], | ||||
|             timerange=timerange | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|     min_date, max_date = history.get_timeframe(data) | ||||
|     caplog.clear() | ||||
|     assert not history.validate_backtest_data(data, min_date, max_date, | ||||
|                                               timeframe_to_minutes('5m')) | ||||
|     assert len(caplog.record_tuples) == 0 | ||||
|   | ||||
| @@ -2,17 +2,17 @@ | ||||
| import logging | ||||
| from unittest.mock import MagicMock | ||||
|  | ||||
| from pandas import DataFrame | ||||
| import pytest | ||||
| from pandas import DataFrame | ||||
|  | ||||
|  | ||||
| from freqtrade.optimize import get_timeframe | ||||
| from freqtrade.data.history import get_timeframe | ||||
| from freqtrade.optimize.backtesting import Backtesting | ||||
| from freqtrade.strategy.interface import SellType | ||||
| from freqtrade.tests.optimize import (BTrade, BTContainer, _build_backtest_dataframe, | ||||
|                                       _get_frame_time_from_offset, tests_ticker_interval) | ||||
| from freqtrade.tests.conftest import patch_exchange | ||||
|  | ||||
| from freqtrade.tests.optimize import (BTContainer, BTrade, | ||||
|                                       _build_backtest_dataframe, | ||||
|                                       _get_frame_time_from_offset, | ||||
|                                       tests_ticker_interval) | ||||
|  | ||||
| # Test 1 Minus 8% Close | ||||
| # Test with Stop-loss at 1% | ||||
|   | ||||
| @@ -17,9 +17,9 @@ from freqtrade.data import history | ||||
| from freqtrade.data.btanalysis import evaluate_result_multi | ||||
| from freqtrade.data.converter import parse_ticker_dataframe | ||||
| from freqtrade.data.dataprovider import DataProvider | ||||
| from freqtrade.optimize import get_timeframe | ||||
| from freqtrade.optimize.backtesting import (Backtesting, setup_configuration, | ||||
|                                             start) | ||||
| from freqtrade.data.history import get_timeframe | ||||
| from freqtrade.optimize import setup_configuration, start_backtesting | ||||
| from freqtrade.optimize.backtesting import Backtesting | ||||
| from freqtrade.state import RunMode | ||||
| from freqtrade.strategy.default_strategy import DefaultStrategy | ||||
| from freqtrade.strategy.interface import SellType | ||||
| @@ -178,7 +178,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> | ||||
|         'backtesting' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     config = setup_configuration(get_args(args), RunMode.BACKTEST) | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
|     assert 'stake_amount' in config | ||||
| @@ -228,7 +228,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> | ||||
|         '--export-filename', 'foo_bar.json' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     config = setup_configuration(get_args(args), RunMode.BACKTEST) | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
|     assert 'stake_amount' in config | ||||
| @@ -290,7 +290,7 @@ def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog | ||||
|     ] | ||||
|  | ||||
|     with pytest.raises(DependencyException, match=r'.*stake amount.*'): | ||||
|         setup_configuration(get_args(args)) | ||||
|         setup_configuration(get_args(args), RunMode.BACKTEST) | ||||
|  | ||||
|  | ||||
| def test_start(mocker, fee, default_conf, caplog) -> None: | ||||
| @@ -307,7 +307,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None: | ||||
|         'backtesting' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_backtesting(args) | ||||
|     assert log_has( | ||||
|         'Starting freqtrade in Backtesting mode', | ||||
|         caplog.record_tuples | ||||
| @@ -472,7 +472,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None: | ||||
|         return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) | ||||
|  | ||||
|     mocker.patch('freqtrade.data.history.load_data', mocked_load_data) | ||||
|     mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe) | ||||
|     mocker.patch('freqtrade.data.history.get_timeframe', get_timeframe) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock()) | ||||
|     patch_exchange(mocker) | ||||
|     mocker.patch.multiple( | ||||
| @@ -507,7 +507,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None: | ||||
|         return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) | ||||
|  | ||||
|     mocker.patch('freqtrade.data.history.load_data', MagicMock(return_value={})) | ||||
|     mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe) | ||||
|     mocker.patch('freqtrade.data.history.get_timeframe', get_timeframe) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock()) | ||||
|     patch_exchange(mocker) | ||||
|     mocker.patch.multiple( | ||||
| @@ -847,7 +847,7 @@ def test_backtest_start_live(default_conf, mocker, caplog): | ||||
|         '--disable-max-market-positions' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_backtesting(args) | ||||
|     # check the logs, that will contain the backtest result | ||||
|     exists = [ | ||||
|         'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', | ||||
| @@ -901,7 +901,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): | ||||
|         'TestStrategy', | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_backtesting(args) | ||||
|     # 2 backtests, 4 tables | ||||
|     assert backtestmock.call_count == 2 | ||||
|     assert gen_table_mock.call_count == 4 | ||||
|   | ||||
| @@ -7,7 +7,8 @@ from unittest.mock import MagicMock | ||||
|  | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.edge import PairInfo | ||||
| from freqtrade.optimize.edge_cli import EdgeCli, setup_configuration, start | ||||
| from freqtrade.optimize import start_edge, setup_configuration | ||||
| from freqtrade.optimize.edge_cli import EdgeCli | ||||
| from freqtrade.state import RunMode | ||||
| from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange | ||||
|  | ||||
| @@ -27,8 +28,8 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> | ||||
|         'edge' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     assert config['runmode'] == RunMode.EDGECLI | ||||
|     config = setup_configuration(get_args(args), RunMode.EDGE) | ||||
|     assert config['runmode'] == RunMode.EDGE | ||||
|  | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
| @@ -67,14 +68,14 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N | ||||
|         '--stoplosses=-0.01,-0.10,-0.001' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     config = setup_configuration(get_args(args), RunMode.EDGE) | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
|     assert 'stake_amount' in config | ||||
|     assert 'exchange' in config | ||||
|     assert 'pair_whitelist' in config['exchange'] | ||||
|     assert 'datadir' in config | ||||
|     assert config['runmode'] == RunMode.EDGECLI | ||||
|     assert config['runmode'] == RunMode.EDGE | ||||
|     assert log_has( | ||||
|         'Using data folder: {} ...'.format(config['datadir']), | ||||
|         caplog.record_tuples | ||||
| @@ -106,7 +107,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None: | ||||
|         'edge' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_edge(args) | ||||
|     assert log_has( | ||||
|         'Starting freqtrade in Edge mode', | ||||
|         caplog.record_tuples | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import json | ||||
| import os | ||||
| from datetime import datetime | ||||
| from unittest.mock import MagicMock | ||||
| from filelock import Timeout | ||||
|  | ||||
| import pandas as pd | ||||
| import pytest | ||||
| @@ -11,8 +12,9 @@ from freqtrade import DependencyException | ||||
| from freqtrade.data.converter import parse_ticker_dataframe | ||||
| from freqtrade.data.history import load_tickerdata_file | ||||
| from freqtrade.optimize.default_hyperopt import DefaultHyperOpts | ||||
| from freqtrade.optimize.hyperopt import Hyperopt, setup_configuration, start | ||||
| from freqtrade.resolvers import HyperOptResolver | ||||
| from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE | ||||
| from freqtrade.optimize import setup_configuration, start_hyperopt | ||||
| from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver | ||||
| from freqtrade.state import RunMode | ||||
| from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange | ||||
| from freqtrade.tests.optimize.test_backtesting import get_args | ||||
| @@ -52,7 +54,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca | ||||
|         'hyperopt' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     config = setup_configuration(get_args(args), RunMode.HYPEROPT) | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
|     assert 'stake_amount' in config | ||||
| @@ -100,7 +102,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo | ||||
|         '--print-all' | ||||
|     ] | ||||
|  | ||||
|     config = setup_configuration(get_args(args)) | ||||
|     config = setup_configuration(get_args(args), RunMode.HYPEROPT) | ||||
|     assert 'max_open_trades' in config | ||||
|     assert 'stake_currency' in config | ||||
|     assert 'stake_amount' in config | ||||
| @@ -183,7 +185,7 @@ def test_start(mocker, default_conf, caplog) -> None: | ||||
|         '--epochs', '5' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_hyperopt(args) | ||||
|  | ||||
|     import pprint | ||||
|     pprint.pprint(caplog.record_tuples) | ||||
| @@ -214,7 +216,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: | ||||
|         '--epochs', '5' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start(args) | ||||
|     start_hyperopt(args) | ||||
|  | ||||
|     import pprint | ||||
|     pprint.pprint(caplog.record_tuples) | ||||
| @@ -239,13 +241,35 @@ def test_start_failure(mocker, default_conf, caplog) -> None: | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     with pytest.raises(DependencyException): | ||||
|         start(args) | ||||
|         start_hyperopt(args) | ||||
|     assert log_has( | ||||
|         "Please don't use --strategy for hyperopt.", | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_start_filelock(mocker, default_conf, caplog) -> None: | ||||
|     start_mock = MagicMock(side_effect=Timeout(HYPEROPT_LOCKFILE)) | ||||
|     mocker.patch( | ||||
|         'freqtrade.configuration.Configuration._load_config_file', | ||||
|         lambda *args, **kwargs: default_conf | ||||
|     ) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) | ||||
|     patch_exchange(mocker) | ||||
|  | ||||
|     args = [ | ||||
|         '--config', 'config.json', | ||||
|         'hyperopt', | ||||
|         '--epochs', '5' | ||||
|     ] | ||||
|     args = get_args(args) | ||||
|     start_hyperopt(args) | ||||
|     assert log_has( | ||||
|         "Another running instance of freqtrade Hyperopt detected.", | ||||
|         caplog.record_tuples | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None: | ||||
|  | ||||
|     correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) | ||||
|   | ||||
| @@ -1,66 +0,0 @@ | ||||
| # pragma pylint: disable=missing-docstring, protected-access, C0103 | ||||
| from freqtrade import optimize | ||||
| from freqtrade.arguments import TimeRange | ||||
| from freqtrade.data import history | ||||
| from freqtrade.exchange import timeframe_to_minutes | ||||
| from freqtrade.strategy.default_strategy import DefaultStrategy | ||||
| from freqtrade.tests.conftest import log_has, patch_exchange | ||||
|  | ||||
|  | ||||
| def test_get_timeframe(default_conf, mocker) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='1m', | ||||
|             pairs=['UNITTEST/BTC'] | ||||
|         ) | ||||
|     ) | ||||
|     min_date, max_date = optimize.get_timeframe(data) | ||||
|     assert min_date.isoformat() == '2017-11-04T23:02:00+00:00' | ||||
|     assert max_date.isoformat() == '2017-11-14T22:58:00+00:00' | ||||
|  | ||||
|  | ||||
| def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='1m', | ||||
|             pairs=['UNITTEST/BTC'], | ||||
|             fill_up_missing=False | ||||
|         ) | ||||
|     ) | ||||
|     min_date, max_date = optimize.get_timeframe(data) | ||||
|     caplog.clear() | ||||
|     assert optimize.validate_backtest_data(data, min_date, max_date, | ||||
|                                            timeframe_to_minutes('1m')) | ||||
|     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) | ||||
|  | ||||
|  | ||||
| def test_validate_backtest_data(default_conf, mocker, caplog) -> None: | ||||
|     patch_exchange(mocker) | ||||
|     strategy = DefaultStrategy(default_conf) | ||||
|  | ||||
|     timerange = TimeRange('index', 'index', 200, 250) | ||||
|     data = strategy.tickerdata_to_dataframe( | ||||
|         history.load_data( | ||||
|             datadir=None, | ||||
|             ticker_interval='5m', | ||||
|             pairs=['UNITTEST/BTC'], | ||||
|             timerange=timerange | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|     min_date, max_date = optimize.get_timeframe(data) | ||||
|     caplog.clear() | ||||
|     assert not optimize.validate_backtest_data(data, min_date, max_date, | ||||
|                                                timeframe_to_minutes('5m')) | ||||
|     assert len(caplog.record_tuples) == 0 | ||||
| @@ -19,7 +19,7 @@ def test_parse_args_backtesting(mocker) -> None: | ||||
|     Test that main() can start backtesting and also ensure we can pass some specific arguments | ||||
|     further argument parsing is done in test_arguments.py | ||||
|     """ | ||||
|     backtesting_mock = mocker.patch('freqtrade.optimize.backtesting.start', MagicMock()) | ||||
|     backtesting_mock = mocker.patch('freqtrade.optimize.start_backtesting', MagicMock()) | ||||
|     main(['backtesting']) | ||||
|     assert backtesting_mock.call_count == 1 | ||||
|     call_args = backtesting_mock.call_args[0][0] | ||||
| @@ -32,7 +32,7 @@ def test_parse_args_backtesting(mocker) -> None: | ||||
|  | ||||
|  | ||||
| def test_main_start_hyperopt(mocker) -> None: | ||||
|     hyperopt_mock = mocker.patch('freqtrade.optimize.hyperopt.start', MagicMock()) | ||||
|     hyperopt_mock = mocker.patch('freqtrade.optimize.start_hyperopt', MagicMock()) | ||||
|     main(['hyperopt']) | ||||
|     assert hyperopt_mock.call_count == 1 | ||||
|     call_args = hyperopt_mock.call_args[0][0] | ||||
|   | ||||
| @@ -41,9 +41,10 @@ from freqtrade.arguments import Arguments, TimeRange | ||||
| from freqtrade.data import history | ||||
| from freqtrade.data.btanalysis import BT_DATA_COLUMNS, load_backtest_data | ||||
| from freqtrade.exchange import Exchange | ||||
| from freqtrade.optimize.backtesting import setup_configuration | ||||
| from freqtrade.optimize import setup_configuration | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.resolvers import StrategyResolver | ||||
| from freqtrade.state import RunMode | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| _CONF: Dict[str, Any] = {} | ||||
| @@ -107,7 +108,7 @@ def get_trading_env(args: Namespace): | ||||
|     global _CONF | ||||
|  | ||||
|     # Load the configuration | ||||
|     _CONF.update(setup_configuration(args)) | ||||
|     _CONF.update(setup_configuration(args, RunMode.BACKTEST)) | ||||
|     print(_CONF) | ||||
|  | ||||
|     pairs = args.pairs.split(',') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user