Merge pull request #1879 from freqtrade/refactor_optimize__init__

Speed up startup time
This commit is contained in:
Matthias 2019-05-29 06:18:57 +02:00 committed by GitHub
commit 4fed263885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 283 additions and 301 deletions

View File

@ -340,25 +340,25 @@ class Arguments(object):
Builds and attaches all subcommands Builds and attaches all subcommands
:return: None :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') subparsers = self.parser.add_subparsers(dest='subparser')
# Add backtesting subcommand # Add backtesting subcommand
backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.') 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.optimizer_shared_options(backtesting_cmd)
self.backtesting_options(backtesting_cmd) self.backtesting_options(backtesting_cmd)
# Add edge subcommand # Add edge subcommand
edge_cmd = subparsers.add_parser('edge', help='Edge module.') 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.optimizer_shared_options(edge_cmd)
self.edge_options(edge_cmd) self.edge_options(edge_cmd)
# Add hyperopt subcommand # Add hyperopt subcommand
hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.') 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.optimizer_shared_options(hyperopt_cmd)
self.hyperopt_options(hyperopt_cmd) self.hyperopt_options(hyperopt_cmd)

View File

@ -5,19 +5,21 @@ Includes:
* load data for a pair (or a list of pairs) from disk * load data for a pair (or a list of pairs) from disk
* download data from exchange and store to disk * download data from exchange and store to disk
""" """
import logging import logging
import operator
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Optional, List, Dict, Tuple, Any from typing import Any, Dict, List, Optional, Tuple
import arrow import arrow
from pandas import DataFrame from pandas import DataFrame
from freqtrade import misc, OperationalException from freqtrade import OperationalException, misc
from freqtrade.arguments import TimeRange from freqtrade.arguments import TimeRange
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.exchange import Exchange, timeframe_to_minutes from freqtrade.exchange import Exchange, timeframe_to_minutes
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -243,3 +245,39 @@ def download_pair_history(datadir: Optional[Path],
f'Error: {e}' f'Error: {e}'
) )
return False 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

View File

@ -13,7 +13,6 @@ from freqtrade import constants, OperationalException
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.arguments import TimeRange from freqtrade.arguments import TimeRange
from freqtrade.data import history from freqtrade.data import history
from freqtrade.optimize import get_timeframe
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
@ -49,7 +48,6 @@ class Edge():
self.strategy = strategy self.strategy = strategy
self.ticker_interval = self.strategy.ticker_interval self.ticker_interval = self.strategy.ticker_interval
self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe
self.get_timeframe = get_timeframe
self.advise_sell = self.strategy.advise_sell self.advise_sell = self.strategy.advise_sell
self.advise_buy = self.strategy.advise_buy self.advise_buy = self.strategy.advise_buy
@ -117,7 +115,7 @@ class Edge():
preprocessed = self.tickerdata_to_dataframe(data) preprocessed = self.tickerdata_to_dataframe(data)
# Print timeframe # Print timeframe
min_date, max_date = self.get_timeframe(preprocessed) min_date, max_date = history.get_timeframe(preprocessed)
logger.info( logger.info(
'Measuring data from %s up to %s (%s days) ...', 'Measuring data from %s up to %s (%s days) ...',
min_date.isoformat(), min_date.isoformat(),

View File

@ -1,49 +1,115 @@
# pragma pylint: disable=missing-docstring
import logging import logging
from datetime import datetime from argparse import Namespace
from typing import Dict, Tuple from typing import Any, Dict
import operator
import arrow from filelock import FileLock, Timeout
from pandas import DataFrame
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__) 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 Prepare the configuration for the Hyperopt module
:param data: dictionary with preprocessed backtesting data :param args: Cli args from Arguments()
:return: tuple containing min_date, max_date :return: Configuration
""" """
timeframe = [ configuration = Configuration(args, method)
(arrow.get(frame['date'].min()), arrow.get(frame['date'].max())) config = configuration.load_config()
for frame in data.values()
] # Ensure we do not use Exchange credentials
return min(timeframe, key=operator.itemgetter(0))[0], \ config['exchange']['key'] = ''
max(timeframe, key=operator.itemgetter(1))[1] 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, def start_backtesting(args: Namespace) -> None:
max_date: datetime, ticker_interval_mins: int) -> bool:
""" """
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 # Initialize configuration
:param min_date: start-date of the data config = setup_configuration(args, RunMode.BACKTEST)
:param max_date: end-date of the data
:param ticker_interval_mins: ticker interval in minutes 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 Start hyperopt script
expected_frames = int((max_date - min_date).total_seconds() // 60 // ticker_interval_mins) :param args: Cli args from Arguments()
found_missing = False :return: None
for pair, df in data.items(): """
dflen = len(df) # Import here to avoid loading hyperopt module when it's not used
if dflen < expected_frames: from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE
found_missing = True
logger.warning("%s has missing frames: expected %s, got %s, that's %s missing values", # Initialize configuration
pair, expected_frames, dflen, expected_frames - dflen) config = setup_configuration(args, RunMode.HYPEROPT)
return found_missing
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()

View File

@ -4,7 +4,6 @@
This module contains the backtesting logic This module contains the backtesting logic
""" """
import logging import logging
from argparse import Namespace
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
@ -13,10 +12,7 @@ from typing import Any, Dict, List, NamedTuple, Optional
from pandas import DataFrame from pandas import DataFrame
from tabulate import tabulate from tabulate import tabulate
from freqtrade import optimize
from freqtrade import DependencyException, constants
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.data import history from freqtrade.data import history
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.exchange import timeframe_to_minutes 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.persistence import Trade
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.strategy.interface import IStrategy, SellType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -440,10 +435,10 @@ class Backtesting(object):
logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) logger.info("Running backtesting for Strategy %s", strat.get_strategy_name())
self._set_strategy(strat) 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) # Validate dataframe for missing values (mainly at start and end, as fillup is called)
optimize.validate_backtest_data(data, min_date, max_date, history.validate_backtest_data(data, min_date, max_date,
timeframe_to_minutes(self.ticker_interval)) timeframe_to_minutes(self.ticker_interval))
logger.info( logger.info(
'Backtesting with data from %s up to %s (%s days)..', 'Backtesting with data from %s up to %s (%s days)..',
min_date.isoformat(), min_date.isoformat(),
@ -486,39 +481,3 @@ class Backtesting(object):
print(' Strategy Summary '.center(133, '=')) print(' Strategy Summary '.center(133, '='))
print(self._generate_text_table_strategy(all_results)) print(self._generate_text_table_strategy(all_results))
print('\nFor more details, please look at the detail tables above') 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()

View File

@ -4,16 +4,13 @@
This module contains the edge backtesting interface This module contains the edge backtesting interface
""" """
import logging import logging
from argparse import Namespace
from typing import Dict, Any from typing import Dict, Any
from tabulate import tabulate from tabulate import tabulate
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
from freqtrade.state import RunMode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -77,34 +74,3 @@ class EdgeCli(object):
if result: if result:
print('') # blank line for readability print('') # blank line for readability
print(self._generate_edge_table(self.edge._cached_pairs)) 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()

View File

@ -7,28 +7,22 @@ This module contains the hyperopt logic
import logging import logging
import os import os
import sys import sys
from argparse import Namespace
from math import exp from math import exp
from operator import itemgetter from operator import itemgetter
from pathlib import Path from pathlib import Path
from pprint import pprint from pprint import pprint
from typing import Any, Dict, List 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 joblib import Parallel, delayed, dump, load, wrap_non_picklable_objects, cpu_count
from pandas import DataFrame from pandas import DataFrame
from skopt import Optimizer from skopt import Optimizer
from skopt.space import Dimension from skopt.space import Dimension
from freqtrade import DependencyException
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.data.history import load_data, get_timeframe, validate_backtest_data
from freqtrade.data.history import load_data
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.optimize import get_timeframe, validate_backtest_data
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
from freqtrade.state import RunMode from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.resolvers import HyperOptResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -343,62 +337,3 @@ class Hyperopt(Backtesting):
self.save_trials() self.save_trials()
self.log_trials_result() 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.

View File

@ -1,5 +1,6 @@
from freqtrade.resolvers.iresolver import IResolver # noqa: F401 from freqtrade.resolvers.iresolver import IResolver # noqa: F401
from freqtrade.resolvers.exchange_resolver import ExchangeResolver # 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.pairlist_resolver import PairListResolver # noqa: F401
from freqtrade.resolvers.strategy_resolver import StrategyResolver # noqa: F401 from freqtrade.resolvers.strategy_resolver import StrategyResolver # noqa: F401

View File

@ -18,11 +18,11 @@ class State(Enum):
class RunMode(Enum): class RunMode(Enum):
""" """
Bot running mode (backtest, hyperopt, ...) Bot running mode (backtest, hyperopt, ...)
can be "live", "dry-run", "backtest", "edgecli", "hyperopt". can be "live", "dry-run", "backtest", "edge", "hyperopt".
""" """
LIVE = "live" LIVE = "live"
DRY_RUN = "dry_run" DRY_RUN = "dry_run"
BACKTEST = "backtest" BACKTEST = "backtest"
EDGECLI = "edgecli" EDGE = "edge"
HYPEROPT = "hyperopt" HYPEROPT = "hyperopt"
OTHER = "other" # Used for plotting scripts and test OTHER = "other" # Used for plotting scripts and test

View File

@ -2,8 +2,7 @@
import logging import logging
from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data
from freqtrade.data.history import load_pair_history from freqtrade.data.history import load_pair_history, validate_backtest_data, get_timeframe
from freqtrade.optimize import validate_backtest_data, get_timeframe
from freqtrade.tests.conftest import log_has from freqtrade.tests.conftest import log_has

View File

@ -2,24 +2,25 @@
import json import json
import os import os
from pathlib import Path
import uuid import uuid
from pathlib import Path
from shutil import copyfile from shutil import copyfile
import arrow import arrow
from pandas import DataFrame
import pytest import pytest
from pandas import DataFrame
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.arguments import TimeRange from freqtrade.arguments import TimeRange
from freqtrade.data import history from freqtrade.data import history
from freqtrade.data.history import (download_pair_history, from freqtrade.data.history import (download_pair_history,
load_cached_data_for_updating, load_cached_data_for_updating,
load_tickerdata_file, load_tickerdata_file, make_testdata_path,
make_testdata_path,
trim_tickerlist) trim_tickerlist)
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import file_dump_json 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 # Change this if modifying UNITTEST/BTC testdatafile
_BTC_UNITTEST_LENGTH = 13681 _BTC_UNITTEST_LENGTH = 13681
@ -495,3 +496,62 @@ def test_file_dump_json_tofile() -> None:
# Remove the file # Remove the file
_clean_test_file(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

View File

@ -2,17 +2,17 @@
import logging import logging
from unittest.mock import MagicMock from unittest.mock import MagicMock
from pandas import DataFrame
import pytest import pytest
from pandas import DataFrame
from freqtrade.data.history import get_timeframe
from freqtrade.optimize import get_timeframe
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
from freqtrade.strategy.interface import SellType 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.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 1 Minus 8% Close
# Test with Stop-loss at 1% # Test with Stop-loss at 1%

View File

@ -17,9 +17,9 @@ from freqtrade.data import history
from freqtrade.data.btanalysis import evaluate_result_multi from freqtrade.data.btanalysis import evaluate_result_multi
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.optimize import get_timeframe from freqtrade.data.history import get_timeframe
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration, from freqtrade.optimize import setup_configuration, start_backtesting
start) from freqtrade.optimize.backtesting import Backtesting
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
@ -178,7 +178,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
'backtesting' 'backtesting'
] ]
config = setup_configuration(get_args(args)) config = setup_configuration(get_args(args), RunMode.BACKTEST)
assert 'max_open_trades' in config assert 'max_open_trades' in config
assert 'stake_currency' in config assert 'stake_currency' in config
assert 'stake_amount' 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' '--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 'max_open_trades' in config
assert 'stake_currency' in config assert 'stake_currency' in config
assert 'stake_amount' 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.*'): 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: def test_start(mocker, fee, default_conf, caplog) -> None:
@ -307,7 +307,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
'backtesting' 'backtesting'
] ]
args = get_args(args) args = get_args(args)
start(args) start_backtesting(args)
assert log_has( assert log_has(
'Starting freqtrade in Backtesting mode', 'Starting freqtrade in Backtesting mode',
caplog.record_tuples 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) 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.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()) mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( 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) 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.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()) mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -847,7 +847,7 @@ def test_backtest_start_live(default_conf, mocker, caplog):
'--disable-max-market-positions' '--disable-max-market-positions'
] ]
args = get_args(args) args = get_args(args)
start(args) start_backtesting(args)
# check the logs, that will contain the backtest result # check the logs, that will contain the backtest result
exists = [ exists = [
'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
@ -901,7 +901,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
'TestStrategy', 'TestStrategy',
] ]
args = get_args(args) args = get_args(args)
start(args) start_backtesting(args)
# 2 backtests, 4 tables # 2 backtests, 4 tables
assert backtestmock.call_count == 2 assert backtestmock.call_count == 2
assert gen_table_mock.call_count == 4 assert gen_table_mock.call_count == 4

View File

@ -7,7 +7,8 @@ from unittest.mock import MagicMock
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.edge import PairInfo 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.state import RunMode
from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange 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' 'edge'
] ]
config = setup_configuration(get_args(args)) config = setup_configuration(get_args(args), RunMode.EDGE)
assert config['runmode'] == RunMode.EDGECLI assert config['runmode'] == RunMode.EDGE
assert 'max_open_trades' in config assert 'max_open_trades' in config
assert 'stake_currency' 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' '--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 'max_open_trades' in config
assert 'stake_currency' in config assert 'stake_currency' in config
assert 'stake_amount' in config assert 'stake_amount' in config
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert config['runmode'] == RunMode.EDGECLI assert config['runmode'] == RunMode.EDGE
assert log_has( assert log_has(
'Using data folder: {} ...'.format(config['datadir']), 'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples caplog.record_tuples
@ -106,7 +107,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None:
'edge' 'edge'
] ]
args = get_args(args) args = get_args(args)
start(args) start_edge(args)
assert log_has( assert log_has(
'Starting freqtrade in Edge mode', 'Starting freqtrade in Edge mode',
caplog.record_tuples caplog.record_tuples

View File

@ -3,6 +3,7 @@ import json
import os import os
from datetime import datetime from datetime import datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
from filelock import Timeout
import pandas as pd import pandas as pd
import pytest import pytest
@ -11,8 +12,9 @@ from freqtrade import DependencyException
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file from freqtrade.data.history import load_tickerdata_file
from freqtrade.optimize.default_hyperopt import DefaultHyperOpts from freqtrade.optimize.default_hyperopt import DefaultHyperOpts
from freqtrade.optimize.hyperopt import Hyperopt, setup_configuration, start from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE
from freqtrade.resolvers import HyperOptResolver from freqtrade.optimize import setup_configuration, start_hyperopt
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange
from freqtrade.tests.optimize.test_backtesting import get_args 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' 'hyperopt'
] ]
config = setup_configuration(get_args(args)) config = setup_configuration(get_args(args), RunMode.HYPEROPT)
assert 'max_open_trades' in config assert 'max_open_trades' in config
assert 'stake_currency' in config assert 'stake_currency' in config
assert 'stake_amount' in config assert 'stake_amount' in config
@ -100,7 +102,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
'--print-all' '--print-all'
] ]
config = setup_configuration(get_args(args)) config = setup_configuration(get_args(args), RunMode.HYPEROPT)
assert 'max_open_trades' in config assert 'max_open_trades' in config
assert 'stake_currency' in config assert 'stake_currency' in config
assert 'stake_amount' in config assert 'stake_amount' in config
@ -183,7 +185,7 @@ def test_start(mocker, default_conf, caplog) -> None:
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
start(args) start_hyperopt(args)
import pprint import pprint
pprint.pprint(caplog.record_tuples) pprint.pprint(caplog.record_tuples)
@ -214,7 +216,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
start(args) start_hyperopt(args)
import pprint import pprint
pprint.pprint(caplog.record_tuples) pprint.pprint(caplog.record_tuples)
@ -239,13 +241,35 @@ def test_start_failure(mocker, default_conf, caplog) -> None:
] ]
args = get_args(args) args = get_args(args)
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
start(args) start_hyperopt(args)
assert log_has( assert log_has(
"Please don't use --strategy for hyperopt.", "Please don't use --strategy for hyperopt.",
caplog.record_tuples 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: def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None:
correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20)

View File

@ -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

View File

@ -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 Test that main() can start backtesting and also ensure we can pass some specific arguments
further argument parsing is done in test_arguments.py 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']) main(['backtesting'])
assert backtesting_mock.call_count == 1 assert backtesting_mock.call_count == 1
call_args = backtesting_mock.call_args[0][0] 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: 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']) main(['hyperopt'])
assert hyperopt_mock.call_count == 1 assert hyperopt_mock.call_count == 1
call_args = hyperopt_mock.call_args[0][0] call_args = hyperopt_mock.call_args[0][0]

View File

@ -41,9 +41,10 @@ from freqtrade.arguments import Arguments, TimeRange
from freqtrade.data import history from freqtrade.data import history
from freqtrade.data.btanalysis import BT_DATA_COLUMNS, load_backtest_data from freqtrade.data.btanalysis import BT_DATA_COLUMNS, load_backtest_data
from freqtrade.exchange import Exchange 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.persistence import Trade
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
from freqtrade.state import RunMode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_CONF: Dict[str, Any] = {} _CONF: Dict[str, Any] = {}
@ -107,7 +108,7 @@ def get_trading_env(args: Namespace):
global _CONF global _CONF
# Load the configuration # Load the configuration
_CONF.update(setup_configuration(args)) _CONF.update(setup_configuration(args, RunMode.BACKTEST))
print(_CONF) print(_CONF)
pairs = args.pairs.split(',') pairs = args.pairs.split(',')