Merge pull request #1879 from freqtrade/refactor_optimize__init__
Speed up startup time
This commit is contained in:
commit
4fed263885
@ -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(',')
|
||||
|
Loading…
Reference in New Issue
Block a user