From 5b9711c00259de9eb587f6ab864a1d9a71878410 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 14 Aug 2019 13:25:49 +0300 Subject: [PATCH 001/227] adaptive roi_space --- freqtrade/optimize/hyperopt.py | 3 +- freqtrade/optimize/hyperopt_interface.py | 115 +++++++++++++++++++---- freqtrade/resolvers/hyperopt_resolver.py | 2 +- 3 files changed, 99 insertions(+), 21 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 550f13e28..89e1accdd 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -23,7 +23,8 @@ from skopt.space import Dimension from freqtrade.configuration import Arguments from freqtrade.data.history import load_data, get_timeframe from freqtrade.optimize.backtesting import Backtesting -# Import IHyperOptLoss to allow users import from this file +# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules +from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F4 from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F4 from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver, HyperOptLossResolver diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index f1f123653..b81dc25e3 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -2,6 +2,8 @@ IHyperOpt interface This module defines the interface to apply for hyperopts """ +import logging +import math from abc import ABC, abstractmethod from typing import Dict, Any, Callable, List @@ -9,15 +11,18 @@ from typing import Dict, Any, Callable, List from pandas import DataFrame from skopt.space import Dimension, Integer, Real +from freqtrade.exchange import timeframe_to_minutes + + +logger = logging.getLogger(__name__) + class IHyperOpt(ABC): """ Interface for freqtrade hyperopts - Defines the mandatory structure must follow any custom strategies + Defines the mandatory structure must follow any custom hyperopts - Attributes you can use: - minimal_roi -> Dict: Minimal ROI designed for the strategy - stoploss -> float: optimal stoploss designed for the strategy + Class attributes you can use: ticker_interval -> int: value of the ticker interval to use for the strategy """ ticker_interval: str @@ -75,6 +80,83 @@ class IHyperOpt(ABC): return roi_table + @staticmethod + def roi_space() -> List[Dimension]: + """ + Create a ROI space. + + Defines values to search for each ROI steps. + + This method implements adaptive roi hyperspace with varied + ranges for parameters which automatically adapts to the + ticker interval used. + + It's used by Freqtrade by default, if no custom roi_space method is defined. + """ + + # Default scaling coefficients for the roi hyperspace. Can be changed + # to adjust resulting ranges of the ROI tables. + # Increase if you need wider ranges in the roi hyperspace, decrease if shorter + # ranges are needed. + roi_t_alpha = 1.0 + roi_p_alpha = 1.0 + + ticker_interval_mins = timeframe_to_minutes(IHyperOpt.ticker_interval) + + # We define here limits for the ROI space parameters automagically adapted to the + # ticker_interval used by the bot: + # + # * 'roi_t' (limits for the time intervals in the ROI tables) components + # are scaled linearly. + # * 'roi_p' (limits for the ROI value steps) components are scaled logarithmically. + # + # The scaling is designed so that it maps exactly to the legacy Freqtrade roi_space() + # method for the 5m ticker interval. + roi_t_scale = ticker_interval_mins / 5 + roi_p_scale = math.log1p(ticker_interval_mins) / math.log1p(5) + roi_limits = { + 'roi_t1_min': int(10 * roi_t_scale * roi_t_alpha), + 'roi_t1_max': int(120 * roi_t_scale * roi_t_alpha), + 'roi_t2_min': int(10 * roi_t_scale * roi_t_alpha), + 'roi_t2_max': int(60 * roi_t_scale * roi_t_alpha), + 'roi_t3_min': int(10 * roi_t_scale * roi_t_alpha), + 'roi_t3_max': int(40 * roi_t_scale * roi_t_alpha), + 'roi_p1_min': 0.01 * roi_p_scale * roi_p_alpha, + 'roi_p1_max': 0.04 * roi_p_scale * roi_p_alpha, + 'roi_p2_min': 0.01 * roi_p_scale * roi_p_alpha, + 'roi_p2_max': 0.07 * roi_p_scale * roi_p_alpha, + 'roi_p3_min': 0.01 * roi_p_scale * roi_p_alpha, + 'roi_p3_max': 0.20 * roi_p_scale * roi_p_alpha, + } + logger.debug(f"Using roi space limits: {roi_limits}") + p = { + 'roi_t1': roi_limits['roi_t1_min'], + 'roi_t2': roi_limits['roi_t2_min'], + 'roi_t3': roi_limits['roi_t3_min'], + 'roi_p1': roi_limits['roi_p1_min'], + 'roi_p2': roi_limits['roi_p2_min'], + 'roi_p3': roi_limits['roi_p3_min'], + } + logger.info(f"Min roi table: {IHyperOpt.generate_roi_table(p)}") + p = { + 'roi_t1': roi_limits['roi_t1_max'], + 'roi_t2': roi_limits['roi_t2_max'], + 'roi_t3': roi_limits['roi_t3_max'], + 'roi_p1': roi_limits['roi_p1_max'], + 'roi_p2': roi_limits['roi_p2_max'], + 'roi_p3': roi_limits['roi_p3_max'], + } + logger.info(f"Max roi table: {IHyperOpt.generate_roi_table(p)}") + + return [ + Integer(roi_limits['roi_t1_min'], roi_limits['roi_t1_max'], name='roi_t1'), + Integer(roi_limits['roi_t2_min'], roi_limits['roi_t2_max'], name='roi_t2'), + Integer(roi_limits['roi_t3_min'], roi_limits['roi_t3_max'], name='roi_t3'), + Real(roi_limits['roi_p1_min'], roi_limits['roi_p1_max'], name='roi_p1'), + Real(roi_limits['roi_p2_min'], roi_limits['roi_p2_max'], name='roi_p2'), + Real(roi_limits['roi_p3_min'], roi_limits['roi_p3_max'], name='roi_p3'), + ] + @staticmethod def stoploss_space() -> List[Dimension]: """ @@ -87,19 +169,14 @@ class IHyperOpt(ABC): Real(-0.5, -0.02, name='stoploss'), ] - @staticmethod - def roi_space() -> List[Dimension]: - """ - Create a ROI space. + # This is needed for proper unpickling the class attribute ticker_interval + # which is set to the actual value by the resolver. + # Why do I still need such shamanic mantras in modern python? + def __getstate__(self): + state = self.__dict__.copy() + state['ticker_interval'] = self.ticker_interval + return state - Defines values to search for each ROI steps. - You may override it in your custom Hyperopt class. - """ - return [ - Integer(10, 120, name='roi_t1'), - Integer(10, 60, name='roi_t2'), - Integer(10, 40, name='roi_t3'), - Real(0.01, 0.04, name='roi_p1'), - Real(0.01, 0.07, name='roi_p2'), - Real(0.01, 0.20, name='roi_p3'), - ] + def __setstate__(self, state): + self.__dict__.update(state) + IHyperOpt.ticker_interval = state['ticker_interval'] diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 5027d7ddf..540e1ca55 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -34,7 +34,7 @@ class HyperOptResolver(IResolver): self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path')) # Assign ticker_interval to be used in hyperopt - self.hyperopt.__class__.ticker_interval = str(config['ticker_interval']) + IHyperOpt.ticker_interval = str(config['ticker_interval']) if not hasattr(self.hyperopt, 'populate_buy_trend'): logger.warning("Custom Hyperopt does not provide populate_buy_trend. " From a12876da92e1f2db9c29cbdb91c30250de897cf5 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 20 Aug 2019 22:17:21 +0300 Subject: [PATCH 002/227] fine printing for floats in the roi tables (round to 5 digits after the decimal point) --- freqtrade/misc.py | 7 +++++++ freqtrade/optimize/hyperopt.py | 4 +++- freqtrade/optimize/hyperopt_interface.py | 7 ++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 05946e008..ed9f5c1e9 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -113,3 +113,10 @@ def deep_merge_dicts(source, destination): destination[key] = value return destination + + +def round_dict(d, n): + """ + Rounds float values in the dict to n digits after the decimal point. + """ + return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()} diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 89e1accdd..d20134e0d 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -22,6 +22,7 @@ from skopt.space import Dimension from freqtrade.configuration import Arguments from freqtrade.data.history import load_data, get_timeframe +from freqtrade.misc import round_dict from freqtrade.optimize.backtesting import Backtesting # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F4 @@ -147,7 +148,8 @@ class Hyperopt(Backtesting): indent=4) if self.has_space('roi'): print("ROI table:") - pprint(self.custom_hyperopt.generate_roi_table(params), indent=4) + # Round printed values to 5 digits after the decimal point + pprint(round_dict(self.custom_hyperopt.generate_roi_table(params), 5), indent=4) if self.has_space('stoploss'): print(f"Stoploss: {params.get('stoploss')}") diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index b81dc25e3..4202e5325 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -12,6 +12,7 @@ from pandas import DataFrame from skopt.space import Dimension, Integer, Real from freqtrade.exchange import timeframe_to_minutes +from freqtrade.misc import round_dict logger = logging.getLogger(__name__) @@ -128,7 +129,7 @@ class IHyperOpt(ABC): 'roi_p3_min': 0.01 * roi_p_scale * roi_p_alpha, 'roi_p3_max': 0.20 * roi_p_scale * roi_p_alpha, } - logger.debug(f"Using roi space limits: {roi_limits}") + logger.debug(f"Using roi space limits: {round_dict(roi_limits, 5)}") p = { 'roi_t1': roi_limits['roi_t1_min'], 'roi_t2': roi_limits['roi_t2_min'], @@ -137,7 +138,7 @@ class IHyperOpt(ABC): 'roi_p2': roi_limits['roi_p2_min'], 'roi_p3': roi_limits['roi_p3_min'], } - logger.info(f"Min roi table: {IHyperOpt.generate_roi_table(p)}") + logger.info(f"Min roi table: {round_dict(IHyperOpt.generate_roi_table(p), 5)}") p = { 'roi_t1': roi_limits['roi_t1_max'], 'roi_t2': roi_limits['roi_t2_max'], @@ -146,7 +147,7 @@ class IHyperOpt(ABC): 'roi_p2': roi_limits['roi_p2_max'], 'roi_p3': roi_limits['roi_p3_max'], } - logger.info(f"Max roi table: {IHyperOpt.generate_roi_table(p)}") + logger.info(f"Max roi table: {round_dict(IHyperOpt.generate_roi_table(p), 5)}") return [ Integer(roi_limits['roi_t1_min'], roi_limits['roi_t1_max'], name='roi_t1'), From cadf573170c8f4a9a541a500d9a70404d1eff6ec Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 20 Aug 2019 22:24:59 +0300 Subject: [PATCH 003/227] round printed stoploss value as well --- freqtrade/optimize/hyperopt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d20134e0d..eaa3a7e1d 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -151,7 +151,8 @@ class Hyperopt(Backtesting): # Round printed values to 5 digits after the decimal point pprint(round_dict(self.custom_hyperopt.generate_roi_table(params), 5), indent=4) if self.has_space('stoploss'): - print(f"Stoploss: {params.get('stoploss')}") + # Also round to 5 digits after the decimal point + print(f"Stoploss: {round(params.get('stoploss'), 5)}") def log_results(self, results) -> None: """ From 31669fde03f86f197d1d8d804aa1a7b84dfcd2e9 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 20 Aug 2019 23:28:16 +0300 Subject: [PATCH 004/227] test adjusted --- freqtrade/tests/optimize/test_hyperopt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 1c4e2445c..72c2741fd 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -417,7 +417,8 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: parallel = mocker.patch( 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}]) + MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', + 'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}}]) ) patch_exchange(mocker) From fcb0ff1b60fa196e6244d68a8c3470d0fdd546e0 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 20 Aug 2019 23:42:44 +0300 Subject: [PATCH 005/227] do not round values in the debug message --- freqtrade/optimize/hyperopt_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 4202e5325..2b86e018e 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -129,7 +129,7 @@ class IHyperOpt(ABC): 'roi_p3_min': 0.01 * roi_p_scale * roi_p_alpha, 'roi_p3_max': 0.20 * roi_p_scale * roi_p_alpha, } - logger.debug(f"Using roi space limits: {round_dict(roi_limits, 5)}") + logger.debug(f"Using roi space limits: {roi_limits}") p = { 'roi_t1': roi_limits['roi_t1_min'], 'roi_t2': roi_limits['roi_t2_min'], From 69c2b12879d05dba7867240ddb52897c0c3f54d4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:02:03 +0200 Subject: [PATCH 006/227] Move plot_dataframe as freqtrade submodule --- freqtrade/configuration/arguments.py | 10 ++++++ freqtrade/plot/plot_utils.py | 15 +++++++++ freqtrade/plot/plotting.py | 46 ++++++++++++++++++++++++++-- freqtrade/utils.py | 3 -- 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 freqtrade/plot/plot_utils.py diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 2c76afa8f..61c899f93 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -119,6 +119,7 @@ class Arguments(object): hyperopt_cmd.set_defaults(func=start_hyperopt) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) + # Create userdir subcommand create_userdir_cmd = subparsers.add_parser('create-userdir', help="Create user-data directory.") create_userdir_cmd.set_defaults(func=start_create_userdir) @@ -139,3 +140,12 @@ class Arguments(object): ) download_data_cmd.set_defaults(func=start_download_data) self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) + + # Add Plotting subcommand + from freqtrade.plot.plot_utils import start_plot_dataframe + plot_dataframe_cmd = subparsers.add_parser( + 'plot-dataframe', + help='Plot candles with indicators.' + ) + plot_dataframe_cmd.set_defaults(func=start_plot_dataframe) + self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py new file mode 100644 index 000000000..2a06ca185 --- /dev/null +++ b/freqtrade/plot/plot_utils.py @@ -0,0 +1,15 @@ +from argparse import Namespace + +from freqtrade.state import RunMode +from freqtrade.utils import setup_utils_configuration + + +def start_plot_dataframe(args: Namespace) -> None: + """ + Plotting dataframe + """ + # Import here to avoid errors if plot-dependencies are not installed. + from freqtrade.plot.plotting import analyse_and_plot_pairs + config = setup_utils_configuration(args, RunMode.OTHER) + + analyse_and_plot_pairs(config) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 9dc6b9551..f50987972 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -1,13 +1,14 @@ import logging from pathlib import Path -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional import pandas as pd from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import (combine_tickers_with_mean, - create_cum_profit, load_trades) + create_cum_profit, + extract_trades_of_period, load_trades) from freqtrade.exchange import Exchange from freqtrade.resolvers import ExchangeResolver, StrategyResolver @@ -321,3 +322,44 @@ def store_plot_file(fig, filename: str, directory: Path, auto_open: bool = False plot(fig, filename=str(_filename), auto_open=auto_open) logger.info(f"Stored plot as {_filename}") + + +def analyse_and_plot_pairs(config: Dict[str, Any]): + """ + From configuration provided + - Initializes plot-script + -Get tickers data + -Generate Dafaframes populated with indicators and signals + -Load trades excecuted on same periods + -Generate Plotly plot objects + -Generate plot files + :return: None + """ + plot_elements = init_plotscript(config) + trades = plot_elements['trades'] + strategy = plot_elements["strategy"] + + pair_counter = 0 + for pair, data in plot_elements["tickers"].items(): + pair_counter += 1 + logger.info("analyse pair %s", pair) + tickers = {} + tickers[pair] = data + + dataframe = strategy.analyze_ticker(tickers[pair], {'pair': pair}) + + trades_pair = trades.loc[trades['pair'] == pair] + trades_pair = extract_trades_of_period(dataframe, trades_pair) + + fig = generate_candlestick_graph( + pair=pair, + data=dataframe, + trades=trades_pair, + indicators1=config["indicators1"].split(","), + indicators2=config["indicators2"].split(",") + ) + + store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']), + directory=config['user_data_dir'] / "plot") + + logger.info('End of ploting process %s plots generated', pair_counter) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 56e60ec82..07daaf074 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -108,6 +108,3 @@ def start_download_data(args: Namespace) -> None: logger.info( f"Pairs [{','.join(pairs_not_available)}] not available " f"on exchange {config['exchange']['name']}.") - - # configuration.resolve_pairs_list() - print(config) From f8c72feea8d8bcafc6dbe165a8548cd16b103aaf Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:21:48 +0200 Subject: [PATCH 007/227] Add some initial tests for plot_dataframe --- freqtrade/configuration/arguments.py | 6 ++---- freqtrade/plot/plot_utils.py | 2 +- freqtrade/plot/plotting.py | 10 +++++----- freqtrade/tests/test_plotting.py | 18 +++++++++++++++++- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 61c899f93..7b4a6f63f 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -34,10 +34,8 @@ ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"] -ARGS_PLOT_DATAFRAME = (ARGS_COMMON + ARGS_STRATEGY + - ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", - "trade_source", "export", "exportfilename", "timerange", - "refresh_pairs"]) +ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", + "trade_source", "export", "exportfilename", "timerange", "refresh_pairs"] ARGS_PLOT_PROFIT = (ARGS_COMMON + ARGS_STRATEGY + ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"]) diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py index 2a06ca185..125a1f46c 100644 --- a/freqtrade/plot/plot_utils.py +++ b/freqtrade/plot/plot_utils.py @@ -6,7 +6,7 @@ from freqtrade.utils import setup_utils_configuration def start_plot_dataframe(args: Namespace) -> None: """ - Plotting dataframe + Plotting dataframe helper """ # Import here to avoid errors if plot-dependencies are not installed. from freqtrade.plot.plotting import analyse_and_plot_pairs diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index f50987972..cb13e3147 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -328,11 +328,11 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): """ From configuration provided - Initializes plot-script - -Get tickers data - -Generate Dafaframes populated with indicators and signals - -Load trades excecuted on same periods - -Generate Plotly plot objects - -Generate plot files + - Get tickers data + - Generate Dafaframes populated with indicators and signals based on configured strategy + - Load trades excecuted during the selected period + - Generate Plotly plot objects + - Generate plot files :return: None """ plot_elements = init_plotscript(config) diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index a78e38c1f..02660341e 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -9,13 +9,14 @@ from plotly.subplots import make_subplots from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data +from freqtrade.plot.plot_utils import start_plot_dataframe from freqtrade.plot.plotting import (add_indicators, add_profit, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, plot_trades, store_plot_file) from freqtrade.strategy.default_strategy import DefaultStrategy -from freqtrade.tests.conftest import log_has, log_has_re +from freqtrade.tests.conftest import get_args, log_has, log_has_re def fig_generating_mock(fig, *args, **kwargs): @@ -270,3 +271,18 @@ def test_generate_profit_graph(): for pair in pairs: profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}") assert isinstance(profit_pair, go.Scattergl) + + +def test_start_plot_dataframe(mocker): + aup = mocker.patch("freqtrade.plot.plotting.analyse_and_plot_pairs", MagicMock()) + args = [ + "--config", "config.json.example", + "plot-dataframe", + "--pairs", "ETH/BTC" + ] + start_plot_dataframe(get_args(args)) + + assert aup.call_count == 1 + called_config = aup.call_args_list[0][0][0] + assert "pairs" in called_config + assert called_config['pairs'] == ["ETH/BTC"] From 99b2be90fd19b4344c35256c25e998c0e307c624 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:24:57 +0200 Subject: [PATCH 008/227] Cleanup plotting (if you have backtest results, no need to download data!) --- freqtrade/configuration/arguments.py | 2 +- freqtrade/plot/plotting.py | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 7b4a6f63f..da231dd32 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -35,7 +35,7 @@ ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"] ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", - "trade_source", "export", "exportfilename", "timerange", "refresh_pairs"] + "trade_source", "export", "exportfilename", "timerange"] ARGS_PLOT_PROFIT = (ARGS_COMMON + ARGS_STRATEGY + ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"]) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index cb13e3147..02c341869 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List import pandas as pd @@ -9,8 +9,7 @@ from freqtrade.data import history from freqtrade.data.btanalysis import (combine_tickers_with_mean, create_cum_profit, extract_trades_of_period, load_trades) -from freqtrade.exchange import Exchange -from freqtrade.resolvers import ExchangeResolver, StrategyResolver +from freqtrade.resolvers import StrategyResolver logger = logging.getLogger(__name__) @@ -29,12 +28,6 @@ def init_plotscript(config): Initialize objects needed for plotting :return: Dict with tickers, trades, pairs and strategy """ - exchange: Optional[Exchange] = None - - # Exchange is only needed when downloading data! - if config.get("refresh_pairs", False): - exchange = ExchangeResolver(config.get('exchange', {}).get('name'), - config).exchange strategy = StrategyResolver(config).strategy if "pairs" in config: @@ -49,9 +42,7 @@ def init_plotscript(config): datadir=Path(str(config.get("datadir"))), pairs=pairs, ticker_interval=config['ticker_interval'], - refresh_pairs=config.get('refresh_pairs', False), timerange=timerange, - exchange=exchange, ) trades = load_trades(config) From 29076acc699cf17a164d2671029f52c9754f7d29 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:43:28 +0200 Subject: [PATCH 009/227] Add test for analyse_and_plot --- freqtrade/plot/plotting.py | 2 +- freqtrade/tests/test_plotting.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 02c341869..a475fd18c 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -353,4 +353,4 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']), directory=config['user_data_dir'] / "plot") - logger.info('End of ploting process %s plots generated', pair_counter) + logger.info('End of plotting process. %s plots generated', pair_counter) diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 02660341e..39578d441 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -11,6 +11,7 @@ from freqtrade.data import history from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data from freqtrade.plot.plot_utils import start_plot_dataframe from freqtrade.plot.plotting import (add_indicators, add_profit, + analyse_and_plot_pairs, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, @@ -286,3 +287,31 @@ def test_start_plot_dataframe(mocker): called_config = aup.call_args_list[0][0][0] assert "pairs" in called_config assert called_config['pairs'] == ["ETH/BTC"] + + +def test_analyse_and_plot_pairs(default_conf, mocker, caplog): + default_conf['trade_source'] = 'file' + default_conf["datadir"] = history.make_testdata_path(None) + default_conf['exportfilename'] = str( + history.make_testdata_path(None) / "backtest-result_test.json") + default_conf['indicators1'] = "sma5,ema10" + default_conf['indicators2'] = "macd" + default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"] + + candle_mock = MagicMock() + store_mock = MagicMock() + mocker.patch.multiple( + "freqtrade.plot.plotting", + generate_candlestick_graph=candle_mock, + store_plot_file=store_mock + ) + analyse_and_plot_pairs(default_conf) + + # Both mocks should be called once per pair + assert candle_mock.call_count == 2 + assert store_mock.call_count == 2 + + assert candle_mock.call_args_list[0][1]['indicators1'] == ['sma5', 'ema10'] + assert candle_mock.call_args_list[0][1]['indicators2'] == ['macd'] + + assert log_has("End of plotting process. 2 plots generated", caplog) From f7cb75ff9362a8c955a0f1c73926781b72a3a2e9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:51:00 +0200 Subject: [PATCH 010/227] Add plot-profit command --- freqtrade/configuration/arguments.py | 13 ++++++++++--- freqtrade/plot/plot_utils.py | 13 ++++++++++++- freqtrade/plot/plotting.py | 21 ++++++++++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index da231dd32..794e8466d 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -37,8 +37,7 @@ ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", " ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", "trade_source", "export", "exportfilename", "timerange"] -ARGS_PLOT_PROFIT = (ARGS_COMMON + ARGS_STRATEGY + - ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"]) +ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"] NO_CONF_REQURIED = ["start_download_data"] @@ -140,10 +139,18 @@ class Arguments(object): self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) # Add Plotting subcommand - from freqtrade.plot.plot_utils import start_plot_dataframe + from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit plot_dataframe_cmd = subparsers.add_parser( 'plot-dataframe', help='Plot candles with indicators.' ) plot_dataframe_cmd.set_defaults(func=start_plot_dataframe) self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) + + # Plot profit + plot_profit_cmd = subparsers.add_parser( + 'plot-profit', + help='Generate plot showing profits.' + ) + plot_profit_cmd.set_defaults(func=start_plot_profit) + self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py index 125a1f46c..11bc9f552 100644 --- a/freqtrade/plot/plot_utils.py +++ b/freqtrade/plot/plot_utils.py @@ -6,10 +6,21 @@ from freqtrade.utils import setup_utils_configuration def start_plot_dataframe(args: Namespace) -> None: """ - Plotting dataframe helper + Entrypoint for dataframe plotting """ # Import here to avoid errors if plot-dependencies are not installed. from freqtrade.plot.plotting import analyse_and_plot_pairs config = setup_utils_configuration(args, RunMode.OTHER) analyse_and_plot_pairs(config) + + +def start_plot_profit(args: Namespace) -> None: + """ + Entrypoint for plot_profit + """ + # Import here to avoid errors if plot-dependencies are not installed. + from freqtrade.plot.plotting import plot_profit + config = setup_utils_configuration(args, RunMode.OTHER) + + plot_profit(config) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index a475fd18c..62cbf0baa 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -19,7 +19,7 @@ try: from plotly.offline import plot import plotly.graph_objects as go except ImportError: - logger.exception("Module plotly not found \n Please install using `pip install plotly`") + logger.exception("Module plotly not found \n Please install using `pip3 install plotly`") exit(1) @@ -354,3 +354,22 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): directory=config['user_data_dir'] / "plot") logger.info('End of plotting process. %s plots generated', pair_counter) + + +def plot_profit(config: Dict[str, Any]) -> None: + """ + Plots the total profit for all pairs. + Note, the profit calculation isn't realistic. + But should be somewhat proportional, and therefor useful + in helping out to find a good algorithm. + """ + plot_elements = init_plotscript(config) + trades = plot_elements['trades'] + # Filter trades to relevant pairs + trades = trades[trades['pair'].isin(plot_elements["pairs"])] + + # Create an average close price of all the pairs that were involved. + # this could be useful to gauge the overall market trend + fig = generate_profit_graph(plot_elements["pairs"], plot_elements["tickers"], trades) + store_plot_file(fig, filename='freqtrade-profit-plot.html', + directory=config['user_data_dir'] / "plot", auto_open=True) From c559f957030d87e312ad4c3f283fe6527d946852 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 16:51:09 +0200 Subject: [PATCH 011/227] Add test for plot-profit --- freqtrade/tests/test_plotting.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 39578d441..705cde975 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -315,3 +315,18 @@ def test_analyse_and_plot_pairs(default_conf, mocker, caplog): assert candle_mock.call_args_list[0][1]['indicators2'] == ['macd'] assert log_has("End of plotting process. 2 plots generated", caplog) + + +def start_plot_profit(mocker): + aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) + args = [ + "--config", "config.json.example", + "plot-profit", + "--pairs", "ETH/BTC" + ] + start_plot_profit(get_args(args)) + + assert aup.call_count == 1 + called_config = aup.call_args_list[0][0][0] + assert "pairs" in called_config + assert called_config['pairs'] == ["ETH/BTC"] From 0ef13be577b2bcbddcb61eea3122d5da7a7d5953 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 17:02:22 +0200 Subject: [PATCH 012/227] Test plot_profit --- freqtrade/tests/test_plotting.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 705cde975..90306ffdb 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -15,7 +15,7 @@ from freqtrade.plot.plotting import (add_indicators, add_profit, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, - plot_trades, store_plot_file) + plot_profit, plot_trades, store_plot_file) from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.tests.conftest import get_args, log_has, log_has_re @@ -330,3 +330,27 @@ def start_plot_profit(mocker): called_config = aup.call_args_list[0][0][0] assert "pairs" in called_config assert called_config['pairs'] == ["ETH/BTC"] + + +def test_plot_profit(default_conf, mocker, caplog): + default_conf['trade_source'] = 'file' + default_conf["datadir"] = history.make_testdata_path(None) + default_conf['exportfilename'] = str( + history.make_testdata_path(None) / "backtest-result_test.json") + default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"] + + profit_mock = MagicMock() + store_mock = MagicMock() + mocker.patch.multiple( + "freqtrade.plot.plotting", + generate_profit_graph=profit_mock, + store_plot_file=store_mock + ) + plot_profit(default_conf) + + # Plot-profit generates one combined plot + assert profit_mock.call_count == 1 + assert store_mock.call_count == 1 + + assert profit_mock.call_args_list[0][0][0] == default_conf['pairs'] + assert store_mock.call_args_list[0][1]['auto_open'] == True From f8ddb1060725482dc914f2065a092ae4ec482b6a Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 17:09:58 +0200 Subject: [PATCH 013/227] switch indicators to nargs argument type --- freqtrade/configuration/cli_options.py | 10 ++++++---- freqtrade/plot/plotting.py | 4 ++-- freqtrade/tests/test_plotting.py | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index d7e4e61b1..bf1ec3620 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -292,14 +292,16 @@ AVAILABLE_CLI_OPTIONS = { "indicators1": Arg( '--indicators1', help='Set indicators from your strategy you want in the first row of the graph. ' - 'Comma-separated list. Example: `ema3,ema5`. Default: `%(default)s`.', - default='sma,ema3,ema5', + 'Space-separated list. Example: `ema3 ema5`. Default: `%(default)s`.', + default=['sma', 'ema3', 'ema5'], + nargs='+', ), "indicators2": Arg( '--indicators2', help='Set indicators from your strategy you want in the third row of the graph. ' - 'Comma-separated list. Example: `fastd,fastk`. Default: `%(default)s`.', - default='macd,macdsignal', + 'Space-separated list. Example: `fastd fastk`. Default: `%(default)s`.', + default=['macd', 'macdsignal'], + nargs='+', ), "plot_limit": Arg( '--plot-limit', diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 62cbf0baa..a057d3d94 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -346,8 +346,8 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): pair=pair, data=dataframe, trades=trades_pair, - indicators1=config["indicators1"].split(","), - indicators2=config["indicators2"].split(",") + indicators1=config["indicators1"], + indicators2=config["indicators2"], ) store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']), diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 90306ffdb..2790fc7db 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -294,8 +294,8 @@ def test_analyse_and_plot_pairs(default_conf, mocker, caplog): default_conf["datadir"] = history.make_testdata_path(None) default_conf['exportfilename'] = str( history.make_testdata_path(None) / "backtest-result_test.json") - default_conf['indicators1'] = "sma5,ema10" - default_conf['indicators2'] = "macd" + default_conf['indicators1'] = ["sma5", "ema10"] + default_conf['indicators2'] = ["macd"] default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"] candle_mock = MagicMock() @@ -353,4 +353,4 @@ def test_plot_profit(default_conf, mocker, caplog): assert store_mock.call_count == 1 assert profit_mock.call_args_list[0][0][0] == default_conf['pairs'] - assert store_mock.call_args_list[0][1]['auto_open'] == True + assert store_mock.call_args_list[0][1]['auto_open'] is True From 518d7dfde8d9f858945fa52b6d7d79d2d1e23d5b Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 17:11:16 +0200 Subject: [PATCH 014/227] Replace plot-scripts with pointers to the new commands --- scripts/plot_dataframe.py | 99 ++------------------------------------- scripts/plot_profit.py | 65 ++----------------------- 2 files changed, 10 insertions(+), 154 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index db4f99d61..62c4bc39f 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -1,100 +1,11 @@ #!/usr/bin/env python3 -""" -Script to display when the bot will buy on specific pair(s) -Use `python plot_dataframe.py --help` to display the command line arguments - -Indicators recommended -Row 1: sma, ema3, ema5, ema10, ema50 -Row 3: macd, rsi, fisher_rsi, mfi, slowd, slowk, fastd, fastk - -Example of usage: -> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/ - --indicators1 sma,ema3 --indicators2 fastk,fastd -""" -import logging import sys -from typing import Any, Dict, List - -from freqtrade.configuration import Arguments -from freqtrade.configuration.arguments import ARGS_PLOT_DATAFRAME -from freqtrade.data.btanalysis import extract_trades_of_period -from freqtrade.optimize import setup_configuration -from freqtrade.plot.plotting import (init_plotscript, generate_candlestick_graph, - store_plot_file, - generate_plot_filename) -from freqtrade.state import RunMode - -logger = logging.getLogger(__name__) -def analyse_and_plot_pairs(config: Dict[str, Any]): - """ - From arguments provided in cli: - -Initialise backtest env - -Get tickers data - -Generate Dafaframes populated with indicators and signals - -Load trades excecuted on same periods - -Generate Plotly plot objects - -Generate plot files - :return: None - """ - plot_elements = init_plotscript(config) - trades = plot_elements['trades'] - strategy = plot_elements["strategy"] +print("This script has been integrated into freqtrade " + "and its functionality is available by calling `freqtrade plot-dataframe`.") +print("Please check the documentation on https://www.freqtrade.io/en/latest/plotting/ " + "for details.") - pair_counter = 0 - for pair, data in plot_elements["tickers"].items(): - pair_counter += 1 - logger.info("analyse pair %s", pair) - tickers = {} - tickers[pair] = data - - dataframe = strategy.analyze_ticker(tickers[pair], {'pair': pair}) - - trades_pair = trades.loc[trades['pair'] == pair] - trades_pair = extract_trades_of_period(dataframe, trades_pair) - - fig = generate_candlestick_graph( - pair=pair, - data=dataframe, - trades=trades_pair, - indicators1=config["indicators1"].split(","), - indicators2=config["indicators2"].split(",") - ) - - store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']), - directory=config['user_data_dir'] / "plot") - - logger.info('End of ploting process %s plots generated', pair_counter) - - -def plot_parse_args(args: List[str]) -> Dict[str, Any]: - """ - Parse args passed to the script - :param args: Cli arguments - :return: args: Array with all arguments - """ - arguments = Arguments(args, 'Graph dataframe') - arguments._build_args(optionlist=ARGS_PLOT_DATAFRAME) - parsed_args = arguments._parse_args() - - # Load the configuration - config = setup_configuration(parsed_args, RunMode.OTHER) - return config - - -def main(sysargv: List[str]) -> None: - """ - This function will initiate the bot and start the trading loop. - :return: None - """ - logger.info('Starting Plot Dataframe') - analyse_and_plot_pairs( - plot_parse_args(sysargv) - ) - exit() - - -if __name__ == '__main__': - main(sys.argv[1:]) +sys.exit(1) diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 578ddf15f..c9a23c1ee 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -1,66 +1,11 @@ #!/usr/bin/env python3 -""" -Script to display profits -Use `python plot_profit.py --help` to display the command line arguments -""" -import logging import sys -from typing import Any, Dict, List - -from freqtrade.configuration import Arguments -from freqtrade.configuration.arguments import ARGS_PLOT_PROFIT -from freqtrade.optimize import setup_configuration -from freqtrade.plot.plotting import init_plotscript, generate_profit_graph, store_plot_file -from freqtrade.state import RunMode - -logger = logging.getLogger(__name__) -def plot_profit(config: Dict[str, Any]) -> None: - """ - Plots the total profit for all pairs. - Note, the profit calculation isn't realistic. - But should be somewhat proportional, and therefor useful - in helping out to find a good algorithm. - """ - plot_elements = init_plotscript(config) - trades = plot_elements['trades'] - # Filter trades to relevant pairs - trades = trades[trades['pair'].isin(plot_elements["pairs"])] +print("This script has been integrated into freqtrade " + "and its functionality is available by calling `freqtrade plot-profit`.") +print("Please check the documentation on https://www.freqtrade.io/en/latest/plotting/ " + "for details.") - # Create an average close price of all the pairs that were involved. - # this could be useful to gauge the overall market trend - fig = generate_profit_graph(plot_elements["pairs"], plot_elements["tickers"], trades) - store_plot_file(fig, filename='freqtrade-profit-plot.html', - directory=config['user_data_dir'] / "plot", auto_open=True) - - -def plot_parse_args(args: List[str]) -> Dict[str, Any]: - """ - Parse args passed to the script - :param args: Cli arguments - :return: args: Array with all arguments - """ - arguments = Arguments(args, 'Graph profits') - arguments._build_args(optionlist=ARGS_PLOT_PROFIT) - parsed_args = arguments._parse_args() - - # Load the configuration - config = setup_configuration(parsed_args, RunMode.OTHER) - return config - - -def main(sysargv: List[str]) -> None: - """ - This function will initiate the bot and start the trading loop. - :return: None - """ - logger.info('Starting Plot Dataframe') - plot_profit( - plot_parse_args(sysargv) - ) - - -if __name__ == '__main__': - main(sys.argv[1:]) +sys.exit(1) From 1b374fcf7e8271fc600fc4959e70b2f33c3da26f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 17:16:33 +0200 Subject: [PATCH 015/227] Improve plotting documentation --- docs/plotting.md | 104 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index 5a1e9757a..5e3b47311 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -12,25 +12,67 @@ pip install -U -r requirements-plot.txt ## Plot price and indicators -Usage for the price plotter: +Usage for the candlestick plotting: + +``` +usage: freqtrade plot-dataframe [-h] [-p PAIRS [PAIRS ...]] + [--indicators1 INDICATORS1 [INDICATORS1 ...]] + [--indicators2 INDICATORS2 [INDICATORS2 ...]] + [--plot-limit INT] [--db-url PATH] + [--trade-source {DB,file}] [--export EXPORT] + [--export-filename PATH] + [--timerange TIMERANGE] + +optional arguments: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Show profits for only these pairs. Pairs are space- + separated. + --indicators1 INDICATORS1 [INDICATORS1 ...] + Set indicators from your strategy you want in the + first row of the graph. Space-separated list. Example: + `ema3 ema5`. Default: `['sma', 'ema3', 'ema5']`. + --indicators2 INDICATORS2 [INDICATORS2 ...] + Set indicators from your strategy you want in the + third row of the graph. Space-separated list. Example: + `fastd fastk`. Default: `['macd', 'macdsignal']`. + --plot-limit INT Specify tick limit for plotting. Notice: too high + values cause huge files. Default: 750. + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite://` for Dry Run). + --trade-source {DB,file} + Specify the source for trades (Can be DB or file + (backtest file)) Default: file + --export EXPORT Export backtest results, argument are: trades. + Example: `--export=trades` + --export-filename PATH + Save backtest results to the file with this filename + (default: `user_data/backtest_results/backtest- + result.json`). Requires `--export` to be set as well. + Example: `--export-filename=user_data/backtest_results + /backtest_today.json` + --timerange TIMERANGE + Specify what timerange of data to use. -``` bash -python3 script/plot_dataframe.py [-h] [-p pairs] ``` Example ``` bash -python3 scripts/plot_dataframe.py -p BTC/ETH +freqtrade plot-dataframe -p BTC/ETH ``` -The `-p` pairs argument can be used to specify pairs you would like to plot. +The `--pairs` argument can be used to specify pairs you would like to plot. + +!!! Note + Generates one plot-file per pair. Specify custom indicators. Use `--indicators1` for the main plot and `--indicators2` for the subplot below (if values are in a different range than prices). ``` bash -python3 scripts/plot_dataframe.py -p BTC/ETH --indicators1 sma,ema --indicators2 macd +freqtrade plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd ``` ### Advanced use @@ -38,32 +80,32 @@ python3 scripts/plot_dataframe.py -p BTC/ETH --indicators1 sma,ema --indicators2 To plot multiple pairs, separate them with a comma: ``` bash -python3 scripts/plot_dataframe.py -p BTC/ETH,XRP/ETH +freqtrade plot-dataframe -p BTC/ETH XRP/ETH ``` To plot a timerange (to zoom in): ``` bash -python3 scripts/plot_dataframe.py -p BTC/ETH --timerange=20180801-20180805 +freqtrade plot-dataframe -p BTC/ETH --timerange=20180801-20180805 ``` To plot trades stored in a database use `--db-url` argument: ``` bash -python3 scripts/plot_dataframe.py --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB +freqtrade plot-dataframe --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB ``` To plot trades from a backtesting result, use `--export-filename ` ``` bash -python3 scripts/plot_dataframe.py --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH +freqtrade plot-dataframe --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH ``` To plot a custom strategy the strategy should have first be backtested. The results may then be plotted with the -s argument: ``` bash -python3 scripts/plot_dataframe.py -s Strategy_Name -p BTC/ETH --datadir user_data/data// +freqtrade plot-dataframe -s Strategy_Name -p BTC/ETH --datadir user_data/data// ``` ## Plot profit @@ -76,26 +118,50 @@ The profit plotter shows a picture with three plots: more of an estimate. 3) Each pair individually profit -The first graph is good to get a grip of how the overall market -progresses. +The first graph is good to get a grip of how the overall market progresses. The second graph will show how your algorithm works or doesn't. Perhaps you want an algorithm that steadily makes small profits, or one that acts less seldom, but makes big swings. -The third graph can be useful to spot outliers, events in pairs -that makes profit spikes. +The third graph can be useful to spot outliers, events in pairs that makes profit spikes. Usage for the profit plotter: -``` bash -python3 script/plot_profit.py [-h] [-p pair] [--datadir directory] [--ticker_interval num] +``` +usage: freqtrade plot-profit [-h] [-p PAIRS [PAIRS ...]] + [--timerange TIMERANGE] [--export EXPORT] + [--export-filename PATH] [--db-url PATH] + [--trade-source {DB,file}] + +optional arguments: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Show profits for only these pairs. Pairs are space- + separated. + --timerange TIMERANGE + Specify what timerange of data to use. + --export EXPORT Export backtest results, argument are: trades. + Example: `--export=trades` + --export-filename PATH + Save backtest results to the file with this filename + (default: `user_data/backtest_results/backtest- + result.json`). Requires `--export` to be set as well. + Example: `--export-filename=user_data/backtest_results + /backtest_today.json` + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite://` for Dry Run). + --trade-source {DB,file} + Specify the source for trades (Can be DB or file + (backtest file)) Default: file + ``` -The `-p` pair argument, can be used to plot a single pair +The `--pairs` argument, can be used to limit the pairs that are considered for this calculation. Example ``` bash -python3 scripts/plot_profit.py --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p LTC/BTC +freqtrade plot-profit --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p LTC/BTC ``` From 545e5c5bc6f1589770d2b8305133f6cfaf2c1a17 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 20:17:36 +0200 Subject: [PATCH 016/227] simplify load_trades call --- freqtrade/data/btanalysis.py | 10 +++++----- freqtrade/plot/plotting.py | 5 ++++- freqtrade/tests/data/test_btanalysis.py | 11 +++++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 36d8aedbb..992100cbd 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -112,16 +112,16 @@ def load_trades_from_db(db_url: str) -> pd.DataFrame: return trades -def load_trades(config) -> pd.DataFrame: +def load_trades(source: str, db_url: str, exportfilename: str) -> pd.DataFrame: """ Based on configuration option "trade_source": * loads data from DB (using `db_url`) * loads data from backtestfile (using `exportfilename`) """ - if config["trade_source"] == "DB": - return load_trades_from_db(config["db_url"]) - elif config["trade_source"] == "file": - return load_backtest_data(Path(config["exportfilename"])) + if source == "DB": + return load_trades_from_db(db_url) + elif source == "file": + return load_backtest_data(Path(exportfilename)) def extract_trades_of_period(dataframe: pd.DataFrame, trades: pd.DataFrame) -> pd.DataFrame: diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index a057d3d94..900e9e90c 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -45,7 +45,10 @@ def init_plotscript(config): timerange=timerange, ) - trades = load_trades(config) + trades = load_trades(config['trade_source'], + db_url=config.get('db_url'), + exportfilename=config.get('exportfilename'), + ) return {"tickers": tickers, "trades": trades, "pairs": pairs, diff --git a/freqtrade/tests/data/test_btanalysis.py b/freqtrade/tests/data/test_btanalysis.py index cf8cae566..90602b4fc 100644 --- a/freqtrade/tests/data/test_btanalysis.py +++ b/freqtrade/tests/data/test_btanalysis.py @@ -89,17 +89,20 @@ def test_load_trades(default_conf, mocker): db_mock = mocker.patch("freqtrade.data.btanalysis.load_trades_from_db", MagicMock()) bt_mock = mocker.patch("freqtrade.data.btanalysis.load_backtest_data", MagicMock()) - default_conf['trade_source'] = "DB" - load_trades(default_conf) + load_trades("DB", + db_url=default_conf.get('db_url'), + exportfilename=default_conf.get('exportfilename'), + ) assert db_mock.call_count == 1 assert bt_mock.call_count == 0 db_mock.reset_mock() bt_mock.reset_mock() - default_conf['trade_source'] = "file" default_conf['exportfilename'] = "testfile.json" - load_trades(default_conf) + load_trades("file", + db_url=default_conf.get('db_url'), + exportfilename=default_conf.get('exportfilename'),) assert db_mock.call_count == 0 assert bt_mock.call_count == 1 From 9f29ad77bd45efc8d53421d726f45f59fbb4d256 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 20:23:38 +0200 Subject: [PATCH 017/227] fix test after plot_dataframe change --- freqtrade/tests/test_arguments.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 24f11e32e..e80703418 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -4,7 +4,6 @@ import argparse import pytest from freqtrade.configuration import Arguments -from freqtrade.configuration.arguments import ARGS_PLOT_DATAFRAME from freqtrade.configuration.cli_options import check_int_positive @@ -149,16 +148,17 @@ def test_download_data_options() -> None: def test_plot_dataframe_options() -> None: args = [ - '--indicators1', 'sma10,sma100', - '--indicators2', 'macd,fastd,fastk', + '-c', 'config.json.example', + 'plot-dataframe', + '--indicators1', 'sma10', 'sma100', + '--indicators2', 'macd', 'fastd', 'fastk', '--plot-limit', '30', '-p', 'UNITTEST/BTC', ] - arguments = Arguments(args, '') - arguments._build_args(ARGS_PLOT_DATAFRAME) - pargs = arguments._parse_args() - assert pargs.indicators1 == "sma10,sma100" - assert pargs.indicators2 == "macd,fastd,fastk" + pargs = Arguments(args, '').get_parsed_arg() + + assert pargs.indicators1 == ["sma10", "sma100"] + assert pargs.indicators2 == ["macd", "fastd", "fastk"] assert pargs.plot_limit == 30 assert pargs.pairs == ["UNITTEST/BTC"] From 395414ccde7a426ea9d696ef902bbaacc8587937 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Aug 2019 20:32:06 +0200 Subject: [PATCH 018/227] Refactor init_plotscript a bit (strategy is not needed for plot_profit) --- freqtrade/configuration/arguments.py | 5 +++-- freqtrade/plot/plotting.py | 15 +++++++++------ freqtrade/tests/test_plotting.py | 1 - 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 794e8466d..4bd4410a6 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -35,9 +35,10 @@ ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"] ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", - "trade_source", "export", "exportfilename", "timerange"] + "trade_source", "export", "exportfilename", "timerange", "ticker_interval"] -ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"] +ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", + "trade_source", "ticker_interval"] NO_CONF_REQURIED = ["start_download_data"] diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 900e9e90c..5966f8ae9 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -26,10 +26,9 @@ except ImportError: def init_plotscript(config): """ Initialize objects needed for plotting - :return: Dict with tickers, trades, pairs and strategy + :return: Dict with tickers, trades and pairs """ - strategy = StrategyResolver(config).strategy if "pairs" in config: pairs = config["pairs"] else: @@ -41,7 +40,7 @@ def init_plotscript(config): tickers = history.load_data( datadir=Path(str(config.get("datadir"))), pairs=pairs, - ticker_interval=config['ticker_interval'], + ticker_interval=config.get('ticker_interval', '5m'), timerange=timerange, ) @@ -49,10 +48,10 @@ def init_plotscript(config): db_url=config.get('db_url'), exportfilename=config.get('exportfilename'), ) + return {"tickers": tickers, "trades": trades, "pairs": pairs, - "strategy": strategy, } @@ -329,9 +328,10 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): - Generate plot files :return: None """ + strategy = StrategyResolver(config).strategy + plot_elements = init_plotscript(config) trades = plot_elements['trades'] - strategy = plot_elements["strategy"] pair_counter = 0 for pair, data in plot_elements["tickers"].items(): @@ -367,7 +367,10 @@ def plot_profit(config: Dict[str, Any]) -> None: in helping out to find a good algorithm. """ plot_elements = init_plotscript(config) - trades = plot_elements['trades'] + trades = load_trades(config['trade_source'], + db_url=config.get('db_url'), + exportfilename=config.get('exportfilename'), + ) # Filter trades to relevant pairs trades = trades[trades['pair'].isin(plot_elements["pairs"])] diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 2790fc7db..9f53809d1 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -51,7 +51,6 @@ def test_init_plotscript(default_conf, mocker): assert "tickers" in ret assert "trades" in ret assert "pairs" in ret - assert "strategy" in ret default_conf['pairs'] = ["POWR/BTC", "XLM/BTC"] ret = init_plotscript(default_conf) From d711b8c0e9851bddb7a509a426765bafd85d58d7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Aug 2019 20:42:59 +0200 Subject: [PATCH 019/227] Plot-profit should have subtitles per subplot --- freqtrade/plot/plotting.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 5966f8ae9..1cc63ba1b 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -274,7 +274,8 @@ def generate_profit_graph(pairs: str, tickers: Dict[str, pd.DataFrame], name='Avg close price', ) - fig = make_subplots(rows=3, cols=1, shared_xaxes=True, row_width=[1, 1, 1]) + fig = make_subplots(rows=3, cols=1, shared_xaxes=True, row_width=[1, 1, 1], + subplot_titles=["AVG Close Price", "Combined Profit", "Protit per pair"]) fig['layout'].update(title="Profit plot") fig.add_trace(avgclose, 1, 1) From 2ae398913d0fbfe232dec90fd8c3aa76c78dd124 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Aug 2019 20:43:39 +0200 Subject: [PATCH 020/227] Fix bug in bt-analysis when multiple trades sell at the same time --- freqtrade/data/btanalysis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 992100cbd..b03bdb74d 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -157,7 +157,8 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str) -> :param trades: DataFrame containing trades (requires columns close_time and profitperc) :return: Returns df with one additional column, col_name, containing the cumulative profit. """ - df[col_name] = trades.set_index('close_time')['profitperc'].cumsum() + # Use groupby/sum().cumsum() to avoid errors when multiple trades sold at the same candle. + df[col_name] = trades.groupby('close_time')['profitperc'].sum().cumsum() # Set first value to 0 df.loc[df.iloc[0].name, col_name] = 0 # FFill to get continuous From fb498795ad2de3bdca74e0719e68d4e980b49134 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 14:49:35 +0200 Subject: [PATCH 021/227] Improve profit-plot styling --- freqtrade/plot/plotting.py | 10 ++++++++-- freqtrade/tests/test_plotting.py | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 1cc63ba1b..d65e8c69d 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -274,9 +274,15 @@ def generate_profit_graph(pairs: str, tickers: Dict[str, pd.DataFrame], name='Avg close price', ) - fig = make_subplots(rows=3, cols=1, shared_xaxes=True, row_width=[1, 1, 1], - subplot_titles=["AVG Close Price", "Combined Profit", "Protit per pair"]) + fig = make_subplots(rows=3, cols=1, shared_xaxes=True, + row_width=[1, 1, 1], + vertical_spacing=0.05, + subplot_titles=["AVG Close Price", "Combined Profit", "Profit per pair"]) fig['layout'].update(title="Profit plot") + fig['layout']['yaxis1'].update(title='Price') + fig['layout']['yaxis2'].update(title='Profit') + fig['layout']['yaxis3'].update(title='Profit') + fig['layout']['xaxis']['rangeslider'].update(visible=False) fig.add_trace(avgclose, 1, 1) fig = add_profit(fig, 2, df_comb, 'cum_profit', 'Profit') diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 9f53809d1..52ed6f3e7 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -259,6 +259,10 @@ def test_generate_profit_graph(): assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Profit plot" + assert fig.layout.yaxis.title.text == "Price" + assert fig.layout.yaxis2.title.text == "Profit" + assert fig.layout.yaxis3.title.text == "Profit" + figure = fig.layout.figure assert len(figure.data) == 4 From 661cd65bdddca204676f812d49f29d0e6f562f49 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 15:09:33 +0200 Subject: [PATCH 022/227] Improvements to plot documentation --- docs/assets/plot-dataframe.png | Bin 0 -> 177349 bytes docs/assets/plot-profit.png | Bin 0 -> 119925 bytes docs/plotting.md | 76 ++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 docs/assets/plot-dataframe.png create mode 100644 docs/assets/plot-profit.png diff --git a/docs/assets/plot-dataframe.png b/docs/assets/plot-dataframe.png new file mode 100644 index 0000000000000000000000000000000000000000..eb90a173469bafbd11afd28c31c8e3eccd9b9e5b GIT binary patch literal 177349 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

y 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

CLz8o1U8Dr=!D`r6ufzSh$S!FWYCieMFEQ`j3 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 literal 0 HcmV?d00001 diff --git a/docs/plotting.md b/docs/plotting.md index 5e3b47311..00fdd33a4 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -2,9 +2,9 @@ This page explains how to plot prices, indicators and profits. -## Installation +## Installation / Setup -Plotting scripts use Plotly library. Install/upgrade it with: +Plotting modules use the Plotly library. You can install / upgrade this by running the following command: ``` bash pip install -U -r requirements-plot.txt @@ -12,7 +12,15 @@ pip install -U -r requirements-plot.txt ## Plot price and indicators -Usage for the candlestick plotting: +Plot dataframe shows an interactive graph with three subplots: + +* Main plot with candlestics and indicators following price (sma/ema) +* Volume bars +* Additional indicators as specified by `--indicators2` + +![plot-dataframe](assets/plot-dataframe.png) + +Possible arguments: ``` usage: freqtrade plot-dataframe [-h] [-p PAIRS [PAIRS ...]] @@ -57,7 +65,7 @@ optional arguments: ``` -Example +Example: ``` bash freqtrade plot-dataframe -p BTC/ETH @@ -71,62 +79,58 @@ The `--pairs` argument can be used to specify pairs you would like to plot. Specify custom indicators. Use `--indicators1` for the main plot and `--indicators2` for the subplot below (if values are in a different range than prices). +!!! tip + You will almost certainly want to specify a custom strategy! This can be done by adding `--strategy ClassName` to the command. + ``` bash -freqtrade plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd +freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd ``` -### Advanced use +### Further usage examples -To plot multiple pairs, separate them with a comma: +To plot multiple pairs, separate them with a space: ``` bash -freqtrade plot-dataframe -p BTC/ETH XRP/ETH +freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH XRP/ETH ``` -To plot a timerange (to zoom in): +To plot a timerange (to zoom in) ``` bash -freqtrade plot-dataframe -p BTC/ETH --timerange=20180801-20180805 +freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --timerange=20180801-20180805 ``` -To plot trades stored in a database use `--db-url` argument: +To plot trades stored in a database use `--db-url` in combination with `--trade-source DB`: ``` bash -freqtrade plot-dataframe --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB +freqtrade --strategy AwesomeStrategy plot-dataframe --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB ``` To plot trades from a backtesting result, use `--export-filename ` ``` bash -freqtrade plot-dataframe --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH -``` - -To plot a custom strategy the strategy should have first be backtested. -The results may then be plotted with the -s argument: - -``` bash -freqtrade plot-dataframe -s Strategy_Name -p BTC/ETH --datadir user_data/data// +freqtrade --strategy AwesomeStrategy plot-dataframe --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH ``` ## Plot profit -The profit plotter shows a picture with three plots: +![plot-profit](assets/plot-profit.png) + +Plot profit shows an interactive graph with three plots: 1) Average closing price for all pairs 2) The summarized profit made by backtesting. - Note that this is not the real-world profit, but - more of an estimate. -3) Each pair individually profit + Note that this is not the real-world profit, but more of an estimate. +3) Profit for each individual pair The first graph is good to get a grip of how the overall market progresses. -The second graph will show how your algorithm works or doesn't. -Perhaps you want an algorithm that steadily makes small profits, -or one that acts less seldom, but makes big swings. +The second graph will show if your algorithm works or doesn't. +Perhaps you want an algorithm that steadily makes small profits, or one that acts less often, but makes big swings. -The third graph can be useful to spot outliers, events in pairs that makes profit spikes. +The third graph can be useful to spot outliers, events in pairs that cause profit spikes. -Usage for the profit plotter: +Usage for the plot-profit module: ``` usage: freqtrade plot-profit [-h] [-p PAIRS [PAIRS ...]] @@ -160,7 +164,19 @@ optional arguments: The `--pairs` argument, can be used to limit the pairs that are considered for this calculation. -Example +Examples: + +Use custom backtest-export file + +``` bash +freqtrade plot-profit -p LTC/BTC --export-filename user_data/backtest_results/backtest-result-Strategy005.json +``` + +Use custom database + +``` bash +freqtrade plot-profit -p LTC/BTC --db-url sqlite:///tradesv3.sqlite --trade-source DB +``` ``` bash freqtrade plot-profit --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p LTC/BTC From 1336781f8f0261308ba935b56d8402acf434a4f7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 15:09:43 +0200 Subject: [PATCH 023/227] Reorder points in documentation to group analysis points --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index b5e759432..869c6565c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,12 +14,12 @@ nav: - Backtesting: backtesting.md - Hyperopt: hyperopt.md - Edge positioning: edge.md - - Plotting: plotting.md - - Deprecated features: deprecated.md - FAQ: faq.md - Data Analysis: data-analysis.md + - Plotting: plotting.md - SQL Cheatsheet: sql_cheatsheet.md - Sandbox testing: sandbox-testing.md + - Deprecated features: deprecated.md - Contributors guide: developer.md theme: name: material From 240936eb1981985b0c7a9fbf5ee7cc05f1a3799d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 15:21:16 +0200 Subject: [PATCH 024/227] Small fixes --- docs/assets/plot-profit.png | Bin 119925 -> 123698 bytes freqtrade/plot/plotting.py | 6 +++--- freqtrade/tests/test_plotting.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/assets/plot-profit.png b/docs/assets/plot-profit.png index 6e8664a1e273f197f39db42e6072bd6745d98d97..88d69a2d479f8c1d42744567acf028cd8c36ced0 100644 GIT binary patch literal 123698 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%0Dm%EnmKHbxml|J(ADtNd=d`eTZdDC5bcanVG*V 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 diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index d65e8c69d..b0b8e3df9 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -278,7 +278,7 @@ def generate_profit_graph(pairs: str, tickers: Dict[str, pd.DataFrame], row_width=[1, 1, 1], vertical_spacing=0.05, subplot_titles=["AVG Close Price", "Combined Profit", "Profit per pair"]) - fig['layout'].update(title="Profit plot") + fig['layout'].update(title="Freqtrade Profit plot") fig['layout']['yaxis1'].update(title='Price') fig['layout']['yaxis2'].update(title='Profit') fig['layout']['yaxis3'].update(title='Profit') @@ -375,8 +375,8 @@ def plot_profit(config: Dict[str, Any]) -> None: """ plot_elements = init_plotscript(config) trades = load_trades(config['trade_source'], - db_url=config.get('db_url'), - exportfilename=config.get('exportfilename'), + db_url=str(config.get('db_url')), + exportfilename=str(config.get('exportfilename')), ) # Filter trades to relevant pairs trades = trades[trades['pair'].isin(plot_elements["pairs"])] diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index 52ed6f3e7..cc9625e3f 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -258,7 +258,7 @@ def test_generate_profit_graph(): fig = generate_profit_graph(pairs, tickers, trades) assert isinstance(fig, go.Figure) - assert fig.layout.title.text == "Profit plot" + assert fig.layout.title.text == "Freqtrade Profit plot" assert fig.layout.yaxis.title.text == "Price" assert fig.layout.yaxis2.title.text == "Profit" assert fig.layout.yaxis3.title.text == "Profit" From 3121206afe547ad9ead3267e0d33a456493c837c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 15:35:43 +0200 Subject: [PATCH 025/227] correct wrongly named test --- freqtrade/tests/test_plotting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py index cc9625e3f..df208d4d2 100644 --- a/freqtrade/tests/test_plotting.py +++ b/freqtrade/tests/test_plotting.py @@ -9,7 +9,7 @@ from plotly.subplots import make_subplots from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data -from freqtrade.plot.plot_utils import start_plot_dataframe +from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit from freqtrade.plot.plotting import (add_indicators, add_profit, analyse_and_plot_pairs, generate_candlestick_graph, @@ -320,7 +320,7 @@ def test_analyse_and_plot_pairs(default_conf, mocker, caplog): assert log_has("End of plotting process. 2 plots generated", caplog) -def start_plot_profit(mocker): +def test_start_plot_profit(mocker): aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) args = [ "--config", "config.json.example", From 8a17615b5a96fd59ed39ba317e4acb741d0f881b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 24 Aug 2019 19:41:11 +0200 Subject: [PATCH 026/227] move exceptionhandling from create_order() to calling functions --- freqtrade/exchange/exchange.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a8e974991..53723ad51 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -320,7 +320,7 @@ class Exchange(object): if (order_types.get("stoploss_on_exchange") and not self._ft_has.get("stoploss_on_exchange", False)): raise OperationalException( - 'On exchange stoploss is not supported for %s.' % self.name + f'On exchange stoploss is not supported for {self.name}.' ) def validate_order_time_in_force(self, order_time_in_force: Dict) -> None: @@ -469,11 +469,26 @@ class Exchange(object): params = self._params.copy() params.update({'stopPrice': stop_price}) - - order = self.create_order(pair, ordertype, 'sell', amount, rate, params) - logger.info('stoploss limit order added for %s. ' - 'stop price: %s. limit: %s', pair, stop_price, rate) - return order + try: + order = self.create_order(pair, ordertype, 'sell', amount, rate, params) + logger.info('stoploss limit order added for %s. ' + 'stop price: %s. limit: %s', pair, stop_price, rate) + return order + except ccxt.InsufficientFunds as e: + raise DependencyException( + f'Insufficient funds to create {ordertype} sell order on market {pair}.' + f'Tried to sell amount {amount} at rate {rate}.' + f'Message: {e}') from e + except ccxt.InvalidOrder as e: + raise DependencyException( + f'Could not create {ordertype} sell order on market {pair}. ' + f'Tried to sell amount {amount} at rate {rate}.' + f'Message: {e}') from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e @retrier def get_balance(self, currency: str) -> float: From ea179a8e38a2dbaaf38e7b6a1904c174215aab83 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 09:43:10 +0200 Subject: [PATCH 027/227] stoploss_limit shall not use `create_order()` It needs to handle exceptions differently --- freqtrade/exchange/exchange.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 53723ad51..7a384b7cc 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -470,7 +470,12 @@ class Exchange(object): params = self._params.copy() params.update({'stopPrice': stop_price}) try: - order = self.create_order(pair, ordertype, 'sell', amount, rate, params) + amount = self.symbol_amount_prec(pair, amount) + + rate = self.symbol_price_prec(pair, rate) + + order = self._api.create_order(pair, ordertype, 'sell', + amount, rate, params) logger.info('stoploss limit order added for %s. ' 'stop price: %s. limit: %s', pair, stop_price, rate) return order From defa1c027d911c348586fbf7c0419113a122d796 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 09:50:37 +0200 Subject: [PATCH 028/227] Move stoploss_limit to binance subclass --- freqtrade/exchange/binance.py | 52 ++++++++++++++++++++++++++++++++++ freqtrade/exchange/exchange.py | 48 ++++--------------------------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 18e754e3f..b63ef59c8 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -2,6 +2,9 @@ import logging from typing import Dict +import ccxt + +from freqtrade import DependencyException, OperationalException, TemporaryError from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) @@ -25,3 +28,52 @@ class Binance(Exchange): limit = min(list(filter(lambda x: limit <= x, limit_range))) return super().get_order_book(pair, limit) + + def stoploss_limit(self, pair: str, amount: float, stop_price: float, rate: float) -> Dict: + """ + creates a stoploss limit order. + this stoploss-limit is binance-specific. + It may work with a limited number of other exchanges, but this has not been tested yet. + + """ + ordertype = "stop_loss_limit" + + stop_price = self.symbol_price_prec(pair, stop_price) + + # Ensure rate is less than stop price + if stop_price <= rate: + raise OperationalException( + 'In stoploss limit order, stop price should be more than limit price') + + if self._config['dry_run']: + dry_order = self.dry_run_order( + pair, ordertype, "sell", amount, stop_price) + return dry_order + + params = self._params.copy() + params.update({'stopPrice': stop_price}) + try: + amount = self.symbol_amount_prec(pair, amount) + + rate = self.symbol_price_prec(pair, rate) + + order = self._api.create_order(pair, ordertype, 'sell', + amount, rate, params) + logger.info('stoploss limit order added for %s. ' + 'stop price: %s. limit: %s', pair, stop_price, rate) + return order + except ccxt.InsufficientFunds as e: + raise DependencyException( + f'Insufficient funds to create {ordertype} sell order on market {pair}.' + f'Tried to sell amount {amount} at rate {rate}.' + f'Message: {e}') from e + except ccxt.InvalidOrder as e: + raise DependencyException( + f'Could not create {ordertype} sell order on market {pair}. ' + f'Tried to sell amount {amount} at rate {rate}.' + f'Message: {e}') from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7a384b7cc..534e2ea65 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -450,50 +450,14 @@ class Exchange(object): def stoploss_limit(self, pair: str, amount: float, stop_price: float, rate: float) -> Dict: """ creates a stoploss limit order. - NOTICE: it is not supported by all exchanges. only binance is tested for now. - TODO: implementation maybe needs to be moved to the binance subclass + Since ccxt does not unify stoploss-limit orders yet, this needs to be implemented in each + exchange's subclass. + The exception below should never raise, since we disallow + starting the bot in validate_ordertypes() + Note: Changes to this interface need to be applied to all sub-classes too. """ - ordertype = "stop_loss_limit" - stop_price = self.symbol_price_prec(pair, stop_price) - - # Ensure rate is less than stop price - if stop_price <= rate: - raise OperationalException( - 'In stoploss limit order, stop price should be more than limit price') - - if self._config['dry_run']: - dry_order = self.dry_run_order( - pair, ordertype, "sell", amount, stop_price) - return dry_order - - params = self._params.copy() - params.update({'stopPrice': stop_price}) - try: - amount = self.symbol_amount_prec(pair, amount) - - rate = self.symbol_price_prec(pair, rate) - - order = self._api.create_order(pair, ordertype, 'sell', - amount, rate, params) - logger.info('stoploss limit order added for %s. ' - 'stop price: %s. limit: %s', pair, stop_price, rate) - return order - except ccxt.InsufficientFunds as e: - raise DependencyException( - f'Insufficient funds to create {ordertype} sell order on market {pair}.' - f'Tried to sell amount {amount} at rate {rate}.' - f'Message: {e}') from e - except ccxt.InvalidOrder as e: - raise DependencyException( - f'Could not create {ordertype} sell order on market {pair}. ' - f'Tried to sell amount {amount} at rate {rate}.' - f'Message: {e}') from e - except (ccxt.NetworkError, ccxt.ExchangeError) as e: - raise TemporaryError( - f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e - except ccxt.BaseError as e: - raise OperationalException(e) from e + raise OperationalException(f"stoploss_limit not implemented for {self.name}.") @retrier def get_balance(self, currency: str) -> float: From 067c122bf3e4e53d45eee4b48e6083a531ef5e66 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 09:52:14 +0200 Subject: [PATCH 029/227] Adapt test to use Binance class --- freqtrade/tests/test_freqtradebot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index dab7a9ff7..1119157c4 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -2414,7 +2414,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y) mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit) + mocker.patch('freqtrade.exchange.Binance.stoploss_limit', stoploss_limit) freqtrade = FreqtradeBot(default_conf) freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -2454,7 +2454,6 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, freqtrade.process_maybe_execute_sell(trade) assert trade.stoploss_order_id is None assert trade.is_open is False - print(trade.sell_reason) assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value assert rpc_mock.call_count == 2 From 2c66b33fd107e0829cba668002d549a56bac4c56 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 09:57:21 +0200 Subject: [PATCH 030/227] Adapt some tests to use Binance subclass for stoplosslimit --- freqtrade/exchange/exchange.py | 2 +- freqtrade/tests/exchange/test_exchange.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 534e2ea65..6c93f9128 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -457,7 +457,7 @@ class Exchange(object): Note: Changes to this interface need to be applied to all sub-classes too. """ - raise OperationalException(f"stoploss_limit not implemented for {self.name}.") + raise OperationalException(f"stoploss_limit is not implemented for {self.name}.") @retrier def get_balance(self, currency: str) -> float: diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e453b5dca..0dc07e409 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1474,17 +1474,17 @@ def test_stoploss_limit_order(default_conf, mocker): # test exception handling with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) with pytest.raises(TemporaryError): api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) with pytest.raises(OperationalException): @@ -1493,6 +1493,12 @@ def test_stoploss_limit_order(default_conf, mocker): exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) +def test_stoploss_limit_order_unsupported_exchange(default_conf, mocker): + exchange = get_patched_exchange(mocker, default_conf, 'bittrex') + with pytest.raises(OperationalException, match=r"stoploss_limit is not implemented .*"): + exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + def test_stoploss_limit_order_dry_run(default_conf, mocker): api_mock = MagicMock() order_type = 'stop_loss_limit' From cbf09b5ad9781f42b0b836d45fbbd3936576c59f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:07:47 +0200 Subject: [PATCH 031/227] Improve docstring for Exception --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 14f0bb819..5ccc2ff3c 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -11,7 +11,7 @@ class DependencyException(Exception): class OperationalException(Exception): """ - Requires manual intervention. + Requires manual intervention and will usually stop the bot. This happens when an exchange returns an unexpected error during runtime or given configuration is invalid. """ From a4c8b5bf5da8228c3300e3d4c83c763e290b0a57 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:08:06 +0200 Subject: [PATCH 032/227] Move binance-specific test to test_binance.py --- freqtrade/exchange/binance.py | 5 +- freqtrade/tests/exchange/test_binance.py | 90 +++++++++++++++++++++++ freqtrade/tests/exchange/test_exchange.py | 78 -------------------- 3 files changed, 93 insertions(+), 80 deletions(-) create mode 100644 freqtrade/tests/exchange/test_binance.py diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index b63ef59c8..5834f26cd 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -50,9 +50,10 @@ class Binance(Exchange): pair, ordertype, "sell", amount, stop_price) return dry_order - params = self._params.copy() - params.update({'stopPrice': stop_price}) try: + params = self._params.copy() + params.update({'stopPrice': stop_price}) + amount = self.symbol_amount_prec(pair, amount) rate = self.symbol_price_prec(pair, rate) diff --git a/freqtrade/tests/exchange/test_binance.py b/freqtrade/tests/exchange/test_binance.py new file mode 100644 index 000000000..4afb7fcc4 --- /dev/null +++ b/freqtrade/tests/exchange/test_binance.py @@ -0,0 +1,90 @@ +from random import randint +from unittest.mock import MagicMock + +import ccxt +import pytest + +from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade.tests.conftest import get_patched_exchange + + +def test_stoploss_limit_order(default_conf, mocker): + api_mock = MagicMock() + order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) + order_type = 'stop_loss_limit' + + api_mock.create_order = MagicMock(return_value={ + 'id': order_id, + 'info': { + 'foo': 'bar' + } + }) + + default_conf['dry_run'] = False + mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y) + mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y) + + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + + with pytest.raises(OperationalException): + order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=190, rate=200) + + api_mock.create_order.reset_mock() + + order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + assert 'id' in order + assert 'info' in order + assert order['id'] == order_id + assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' + assert api_mock.create_order.call_args[0][1] == order_type + assert api_mock.create_order.call_args[0][2] == 'sell' + assert api_mock.create_order.call_args[0][3] == 1 + assert api_mock.create_order.call_args[0][4] == 200 + assert api_mock.create_order.call_args[0][5] == {'stopPrice': 220} + + # test exception handling + with pytest.raises(DependencyException): + api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + with pytest.raises(DependencyException): + api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + with pytest.raises(TemporaryError): + api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + with pytest.raises(OperationalException, match=r".*DeadBeef.*"): + api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + +def test_stoploss_limit_order_dry_run(default_conf, mocker): + api_mock = MagicMock() + order_type = 'stop_loss_limit' + default_conf['dry_run'] = True + mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y) + mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y) + + exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') + + with pytest.raises(OperationalException): + order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=190, rate=200) + + api_mock.create_order.reset_mock() + + order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) + + assert 'id' in order + assert 'info' in order + assert 'type' in order + + assert order['type'] == order_type + assert order['price'] == 220 + assert order['amount'] == 1 diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 0dc07e409..afd7b441a 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1436,62 +1436,6 @@ def test_get_fee(default_conf, mocker, exchange_name): 'get_fee', 'calculate_fee') -def test_stoploss_limit_order(default_conf, mocker): - api_mock = MagicMock() - order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - order_type = 'stop_loss_limit' - - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - } - }) - - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y) - - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - - with pytest.raises(OperationalException): - order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=190, rate=200) - - api_mock.create_order.reset_mock() - - order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'sell' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == 200 - assert api_mock.create_order.call_args[0][5] == {'stopPrice': 220} - - # test exception handling - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - with pytest.raises(TemporaryError): - api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - with pytest.raises(OperationalException): - api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - def test_stoploss_limit_order_unsupported_exchange(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, 'bittrex') @@ -1499,29 +1443,7 @@ def test_stoploss_limit_order_unsupported_exchange(default_conf, mocker): exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) -def test_stoploss_limit_order_dry_run(default_conf, mocker): - api_mock = MagicMock() - order_type = 'stop_loss_limit' - default_conf['dry_run'] = True - mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - - with pytest.raises(OperationalException): - order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=190, rate=200) - - api_mock.create_order.reset_mock() - - order = exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - assert 'id' in order - assert 'info' in order - assert 'type' in order - - assert order['type'] == order_type - assert order['price'] == 220 - assert order['amount'] == 1 def test_merge_ft_has_dict(default_conf, mocker): From 5e12b05424df197a5d724d9bb164b139f775076a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:13:35 +0200 Subject: [PATCH 033/227] Improve test coverage --- freqtrade/tests/exchange/test_exchange.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index afd7b441a..d9f350c7b 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -101,18 +101,21 @@ def test_destroy(default_conf, mocker, caplog): def test_init_exception(default_conf, mocker): default_conf['exchange']['name'] = 'wrong_exchange_name' - with pytest.raises( - OperationalException, - match='Exchange {} is not supported'.format(default_conf['exchange']['name'])): + with pytest.raises(OperationalException, + match=f"Exchange {default_conf['exchange']['name']} is not supported"): Exchange(default_conf) default_conf['exchange']['name'] = 'binance' - with pytest.raises( - OperationalException, - match='Exchange {} is not supported'.format(default_conf['exchange']['name'])): + with pytest.raises(OperationalException, + match=f"Exchange {default_conf['exchange']['name']} is not supported"): mocker.patch("ccxt.binance", MagicMock(side_effect=AttributeError)) Exchange(default_conf) + with pytest.raises(OperationalException, + match=r"Initialization of ccxt failed. Reason: DeadBeef"): + mocker.patch("ccxt.binance", MagicMock(side_effect=ccxt.BaseError("DeadBeef"))) + Exchange(default_conf) + def test_exchange_resolver(default_conf, mocker, caplog): mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock())) @@ -1436,16 +1439,12 @@ def test_get_fee(default_conf, mocker, exchange_name): 'get_fee', 'calculate_fee') - def test_stoploss_limit_order_unsupported_exchange(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, 'bittrex') with pytest.raises(OperationalException, match=r"stoploss_limit is not implemented .*"): exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - - - def test_merge_ft_has_dict(default_conf, mocker): mocker.patch.multiple('freqtrade.exchange.Exchange', _init_ccxt=MagicMock(return_value=MagicMock()), From 565a543b7b58437f5ecdca63bc96702b0f8c50f9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:34:56 +0200 Subject: [PATCH 034/227] Use ccxt base methods to round timeframe --- freqtrade/exchange/exchange.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a8e974991..6abf1f270 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -14,6 +14,7 @@ from typing import Any, Dict, List, Optional, Tuple import arrow import ccxt import ccxt.async_support as ccxt_async +from ccxt.base.decimal_to_precision import ROUND_UP, ROUND_DOWN from pandas import DataFrame from freqtrade import (DependencyException, InvalidOrderException, @@ -824,11 +825,9 @@ def timeframe_to_prev_date(timeframe: str, date: datetime = None) -> datetime: """ if not date: date = datetime.now(timezone.utc) - timeframe_secs = timeframe_to_seconds(timeframe) - # Get offset based on timerame_secs - offset = date.timestamp() % timeframe_secs - # Subtract seconds passed since last offset - new_timestamp = date.timestamp() - offset + + new_timestamp = ccxt.Exchange.round_timeframe(timeframe, date.timestamp() * 1000, + ROUND_DOWN) // 1000 return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) @@ -839,9 +838,8 @@ def timeframe_to_next_date(timeframe: str, date: datetime = None) -> datetime: :param date: date to use. Defaults to utcnow() :returns: date of next candle (with utc timezone) """ - prevdate = timeframe_to_prev_date(timeframe, date) - timeframe_secs = timeframe_to_seconds(timeframe) - - # Add one interval to previous candle - new_timestamp = prevdate.timestamp() + timeframe_secs + if not date: + date = datetime.now(timezone.utc) + new_timestamp = ccxt.Exchange.round_timeframe(timeframe, date.timestamp() * 1000, + ROUND_UP) // 1000 return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) From 8f8acf5b06410b0c425aa6e66bb9f4de695ef320 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:43:37 +0200 Subject: [PATCH 035/227] Update ccxt to have this implemented --- requirements-common.txt | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements-common.txt b/requirements-common.txt index 3d80c3ef5..bd3afd8de 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.18.1068 +ccxt==1.18.1084 SQLAlchemy==1.3.7 python-telegram-bot==11.1.0 arrow==0.14.5 diff --git a/setup.py b/setup.py index 631c8b654..b48bddd56 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ setup(name='freqtrade', tests_require=['pytest', 'pytest-mock', 'pytest-cov'], install_requires=[ # from requirements-common.txt - 'ccxt>=1.18', + 'ccxt>=1.18.1080', 'SQLAlchemy', 'python-telegram-bot', 'arrow', @@ -76,7 +76,7 @@ setup(name='freqtrade', 'plot': plot, 'all': all_extra, 'jupyter': jupyter, - + }, include_package_data=True, zip_safe=False, From e603cca7a590b8cd5229a0c827bf5f812eab0fbc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 10:53:56 +0200 Subject: [PATCH 036/227] Testing with now() should not pass in date/time --- freqtrade/tests/exchange/test_exchange.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e453b5dca..dad2d9c37 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1604,7 +1604,7 @@ def test_timeframe_to_prev_date(): assert timeframe_to_prev_date(interval, date) == result date = datetime.now(tz=timezone.utc) - assert timeframe_to_prev_date("5m", date) < date + assert timeframe_to_prev_date("5m") < date def test_timeframe_to_next_date(): @@ -1629,4 +1629,4 @@ def test_timeframe_to_next_date(): assert timeframe_to_next_date(interval, date) == result date = datetime.now(tz=timezone.utc) - assert timeframe_to_next_date("5m", date) > date + assert timeframe_to_next_date("5m") > date From 3232251fea8f05c309169c9449261dc720ea5366 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 15:01:27 +0200 Subject: [PATCH 037/227] Refactor downloading ohlcv from utils to history --- freqtrade/data/history.py | 29 +++++++++++++++++++++++++++++ freqtrade/utils.py | 32 ++++++++------------------------ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 007357d9a..aff9f5c74 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -280,6 +280,35 @@ def download_pair_history(datadir: Optional[Path], return False +def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes: List[str], + dl_path: Path, timerange: TimeRange, + erase=False) -> List[str]: + """ + Refresh stored ohlcv data for backtesting and hyperopt operations. + Used by freqtrade download-data + :return: Pairs not available + """ + pairs_not_available = [] + for pair in pairs: + if pair not in exchange.markets: + pairs_not_available.append(pair) + logger.info(f"Skipping pair {pair}...") + continue + for ticker_interval in timeframes: + + dl_file = pair_data_filename(dl_path, pair, ticker_interval) + if erase and dl_file.exists(): + logger.info( + f'Deleting existing data for pair {pair}, interval {ticker_interval}.') + dl_file.unlink() + + logger.info(f'Downloading pair {pair}, interval {ticker_interval}.') + download_pair_history(datadir=dl_path, exchange=exchange, + pair=pair, ticker_interval=str(ticker_interval), + timerange=timerange) + return pairs_not_available + + def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]: """ Get the maximum timeframe for the given backtest data diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 56e60ec82..162493a3f 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -2,13 +2,13 @@ import logging import sys from argparse import Namespace from pathlib import Path -from typing import Any, Dict +from typing import Any, Dict, List import arrow from freqtrade.configuration import Configuration, TimeRange from freqtrade.configuration.directory_operations import create_userdata_dir -from freqtrade.data.history import download_pair_history +from freqtrade.data.history import refresh_backtest_ohlcv_data from freqtrade.exchange import available_exchanges from freqtrade.resolvers import ExchangeResolver from freqtrade.state import RunMode @@ -75,39 +75,23 @@ def start_download_data(args: Namespace) -> None: logger.info(f'About to download pairs: {config["pairs"]}, ' f'intervals: {config["timeframes"]} to {dl_path}') - pairs_not_available = [] + pairs_not_available: List[str] = [] try: # Init exchange exchange = ExchangeResolver(config['exchange']['name'], config).exchange - for pair in config["pairs"]: - if pair not in exchange.markets: - pairs_not_available.append(pair) - logger.info(f"Skipping pair {pair}...") - continue - for ticker_interval in config["timeframes"]: - pair_print = pair.replace('/', '_') - filename = f'{pair_print}-{ticker_interval}.json' - dl_file = dl_path.joinpath(filename) - if config.get("erase") and dl_file.exists(): - logger.info( - f'Deleting existing data for pair {pair}, interval {ticker_interval}.') - dl_file.unlink() - - logger.info(f'Downloading pair {pair}, interval {ticker_interval}.') - download_pair_history(datadir=dl_path, exchange=exchange, - pair=pair, ticker_interval=str(ticker_interval), - timerange=timerange) + pairs_not_available = refresh_backtest_ohlcv_data( + exchange, pairs=config["pairs"], timeframes=config["timeframes"], + dl_path=Path(config['datadir']), timerange=timerange, erase=config.get("erase")) except KeyboardInterrupt: sys.exit("SIGINT received, aborting ...") finally: if pairs_not_available: - logger.info( - f"Pairs [{','.join(pairs_not_available)}] not available " - f"on exchange {config['exchange']['name']}.") + logger.info(f"Pairs [{','.join(pairs_not_available)}] not available " + f"on exchange {config['exchange']['name']}.") # configuration.resolve_pairs_list() print(config) From da7da2ce524e0bd41576251af7d1a48ee71184ee Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 15:02:40 +0200 Subject: [PATCH 038/227] Change tests to split function --- freqtrade/tests/data/test_history.py | 43 +++++++++++++- freqtrade/tests/test_utils.py | 87 ++++++---------------------- 2 files changed, 61 insertions(+), 69 deletions(-) diff --git a/freqtrade/tests/data/test_history.py b/freqtrade/tests/data/test_history.py index 7360f3c1c..ec4a05e63 100644 --- a/freqtrade/tests/data/test_history.py +++ b/freqtrade/tests/data/test_history.py @@ -5,7 +5,7 @@ import os import uuid from pathlib import Path from shutil import copyfile -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock import arrow import pytest @@ -17,6 +17,7 @@ from freqtrade.data import history from freqtrade.data.history import (download_pair_history, load_cached_data_for_updating, load_tickerdata_file, make_testdata_path, + refresh_backtest_ohlcv_data, trim_tickerlist) from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import file_dump_json @@ -558,3 +559,43 @@ def test_validate_backtest_data(default_conf, mocker, caplog) -> None: assert not history.validate_backtest_data(data['UNITTEST/BTC'], 'UNITTEST/BTC', min_date, max_date, timeframe_to_minutes('5m')) assert len(caplog.record_tuples) == 0 + + +def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog): + dl_mock = mocker.patch('freqtrade.data.history.download_pair_history', MagicMock()) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets) + ) + mocker.patch.object(Path, "exists", MagicMock(return_value=True)) + mocker.patch.object(Path, "unlink", MagicMock()) + + ex = get_patched_exchange(mocker, default_conf) + timerange = TimeRange.parse_timerange("20190101-20190102") + refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"], + timeframes=["1m", "5m"], dl_path=make_testdata_path(None), + timerange=timerange, erase=True + ) + + assert dl_mock.call_count == 4 + assert dl_mock.call_args[1]['timerange'].starttype == 'date' + + assert log_has("Downloading pair ETH/BTC, interval 1m.", caplog) + + +def test_download_data_no_markets(mocker, default_conf, caplog): + dl_mock = mocker.patch('freqtrade.data.history.download_pair_history', MagicMock()) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + ex = get_patched_exchange(mocker, default_conf) + timerange = TimeRange.parse_timerange("20190101-20190102") + unav_pairs = refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"], + timeframes=["1m", "5m"], + dl_path=make_testdata_path(None), + timerange=timerange, erase=False + ) + + assert dl_mock.call_count == 0 + assert "ETH/BTC" in unav_pairs + assert "XRP/BTC" in unav_pairs + assert log_has("Skipping pair ETH/BTC...", caplog) diff --git a/freqtrade/tests/test_utils.py b/freqtrade/tests/test_utils.py index d04e62b28..9e09fd298 100644 --- a/freqtrade/tests/test_utils.py +++ b/freqtrade/tests/test_utils.py @@ -1,5 +1,4 @@ import re -from pathlib import Path from unittest.mock import MagicMock, PropertyMock import pytest @@ -70,74 +69,8 @@ def test_create_datadir(caplog, mocker): assert len(caplog.record_tuples) == 0 -def test_download_data(mocker, markets, caplog): - dl_mock = mocker.patch('freqtrade.utils.download_pair_history', MagicMock()) - patch_exchange(mocker) - mocker.patch( - 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets) - ) - mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - mocker.patch.object(Path, "unlink", MagicMock()) - - args = [ - "download-data", - "--exchange", "binance", - "--pairs", "ETH/BTC", "XRP/BTC", - "--erase", - ] - start_download_data(get_args(args)) - - assert dl_mock.call_count == 4 - assert dl_mock.call_args[1]['timerange'].starttype is None - assert dl_mock.call_args[1]['timerange'].stoptype is None - assert log_has("Deleting existing data for pair ETH/BTC, interval 1m.", caplog) - assert log_has("Downloading pair ETH/BTC, interval 1m.", caplog) - - -def test_download_data_days(mocker, markets, caplog): - dl_mock = mocker.patch('freqtrade.utils.download_pair_history', MagicMock()) - patch_exchange(mocker) - mocker.patch( - 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets) - ) - mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - mocker.patch.object(Path, "unlink", MagicMock()) - - args = [ - "download-data", - "--exchange", "binance", - "--pairs", "ETH/BTC", "XRP/BTC", - "--days", "20", - ] - - start_download_data(get_args(args)) - - assert dl_mock.call_count == 4 - assert dl_mock.call_args[1]['timerange'].starttype == 'date' - - assert log_has("Downloading pair ETH/BTC, interval 1m.", caplog) - - -def test_download_data_no_markets(mocker, caplog): - dl_mock = mocker.patch('freqtrade.utils.download_pair_history', MagicMock()) - patch_exchange(mocker) - mocker.patch( - 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) - ) - args = [ - "download-data", - "--exchange", "binance", - "--pairs", "ETH/BTC", "XRP/BTC", - ] - start_download_data(get_args(args)) - - assert dl_mock.call_count == 0 - assert log_has("Skipping pair ETH/BTC...", caplog) - assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange binance.", caplog) - - def test_download_data_keyboardInterrupt(mocker, caplog, markets): - dl_mock = mocker.patch('freqtrade.utils.download_pair_history', + dl_mock = mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data', MagicMock(side_effect=KeyboardInterrupt)) patch_exchange(mocker) mocker.patch( @@ -152,3 +85,21 @@ def test_download_data_keyboardInterrupt(mocker, caplog, markets): start_download_data(get_args(args)) assert dl_mock.call_count == 1 + + +def test_download_data_no_markets(mocker, caplog): + dl_mock = mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data', + MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "download-data", + "--exchange", "binance", + "--pairs", "ETH/BTC", "XRP/BTC", + "--days", "20" + ] + start_download_data(get_args(args)) + assert dl_mock.call_args[1]['timerange'].starttype == "date" + assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange binance.", caplog) From 513e84880e4a0e60b3f1e04edd8637908c874818 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Aug 2019 20:38:51 +0200 Subject: [PATCH 039/227] Don't escape ticks where it's not needed --- freqtrade/freqtradebot.py | 6 +++--- freqtrade/optimize/hyperopt.py | 6 +++--- freqtrade/persistence.py | 4 ++-- freqtrade/tests/optimize/test_hyperopt.py | 4 ++-- freqtrade/tests/test_configuration.py | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e88b9db6a..8857b95da 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -216,7 +216,7 @@ class FreqtradeBot(object): if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: open_trades = len(Trade.get_open_trades()) if open_trades >= self.config['max_open_trades']: - logger.warning('Can\'t open a new trade: max number of trades is reached') + logger.warning("Can't open a new trade: max number of trades is reached") return None return available_amount / (self.config['max_open_trades'] - open_trades) @@ -351,8 +351,8 @@ class FreqtradeBot(object): min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: logger.warning( - f'Can\'t open a new trade for {pair_s}: stake amount ' - f'is too small ({stake_amount} < {min_stake_amount})' + f"Can't open a new trade for {pair_s}: stake amount " + f"is too small ({stake_amount} < {min_stake_amount})" ) return False diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 62a6ab27b..9c3f085b6 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -124,14 +124,14 @@ class Hyperopt: Save hyperopt trials to file """ if self.trials: - logger.info('Saving %d evaluations to \'%s\'', len(self.trials), self.trials_file) + logger.info("Saving %d evaluations to '%s'", len(self.trials), self.trials_file) dump(self.trials, self.trials_file) def read_trials(self) -> List: """ Read hyperopt trials file """ - logger.info('Reading Trials from \'%s\'', self.trials_file) + logger.info("Reading Trials from '%s'", self.trials_file) trials = load(self.trials_file) self.trials_file.unlink() return trials @@ -379,7 +379,7 @@ class Hyperopt: self.load_previous_results() cpus = cpu_count() - logger.info(f'Found {cpus} CPU cores. Let\'s make them scream!') + logger.info(f"Found {cpus} CPU cores. Let's make them scream!") config_jobs = self.config.get('hyperopt_jobs', -1) logger.info(f'Number of parallel jobs set as: {config_jobs}') diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index c844bbc4c..dff7e4ff6 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -48,8 +48,8 @@ def init(db_url: str, clean_open_orders: bool = False) -> None: try: engine = create_engine(db_url, **kwargs) except NoSuchModuleError: - raise OperationalException(f'Given value for db_url: \'{db_url}\' ' - f'is no valid database URL! (See {_SQL_DOCS_URL})') + raise OperationalException(f"Given value for db_url: '{db_url}' " + f"is no valid database URL! (See {_SQL_DOCS_URL})") session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True)) Trade.session = session() diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 7b525454c..f9ebf552d 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -381,7 +381,7 @@ def test_save_trials_saves_trials(mocker, hyperopt, caplog) -> None: hyperopt.save_trials() trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') - assert log_has('Saving 1 evaluations to \'{}\''.format(trials_file), caplog) + assert log_has("Saving 1 evaluations to '{}'".format(trials_file), caplog) mock_dump.assert_called_once() @@ -390,7 +390,7 @@ def test_read_trials_returns_trials_file(mocker, hyperopt, caplog) -> None: mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials) hyperopt_trial = hyperopt.read_trials() trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') - assert log_has('Reading Trials from \'{}\''.format(trials_file), caplog) + assert log_has("Reading Trials from '{}'".format(trials_file), caplog) assert hyperopt_trial == trials mock_load.assert_called_once() diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index 10ce7e8cf..d98ff9ad0 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -41,14 +41,14 @@ def test_load_config_invalid_pair(default_conf) -> None: def test_load_config_missing_attributes(default_conf) -> None: default_conf.pop('exchange') - with pytest.raises(ValidationError, match=r'.*\'exchange\' is a required property.*'): + with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"): validate_config_schema(default_conf) def test_load_config_incorrect_stake_amount(default_conf) -> None: default_conf['stake_amount'] = 'fake' - with pytest.raises(ValidationError, match=r'.*\'fake\' does not match \'unlimited\'.*'): + with pytest.raises(ValidationError, match=r".*'fake' does not match 'unlimited'.*"): validate_config_schema(default_conf) @@ -472,7 +472,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: assert 'spaces' in config assert config['spaces'] == ['all'] - assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog) + assert log_has("Parameter -s/--spaces detected: ['all']", caplog) assert "runmode" in config assert config['runmode'] == RunMode.HYPEROPT @@ -722,7 +722,7 @@ def test_load_config_default_exchange(all_conf) -> None: assert 'exchange' not in all_conf with pytest.raises(ValidationError, - match=r'\'exchange\' is a required property'): + match=r"'exchange' is a required property"): validate_config_schema(all_conf) @@ -736,7 +736,7 @@ def test_load_config_default_exchange_name(all_conf) -> None: assert 'name' not in all_conf['exchange'] with pytest.raises(ValidationError, - match=r'\'name\' is a required property'): + match=r"'name' is a required property"): validate_config_schema(all_conf) From bfc68ec792dd230eda42bd6b55c2014c6d9631e1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 25 Aug 2019 23:36:42 +0300 Subject: [PATCH 040/227] minor cleanup in Backtesting --- freqtrade/optimize/backtesting.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 568615b53..f558c82f7 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -64,6 +64,12 @@ class Backtesting(object): self.config['dry_run'] = True self.strategylist: List[IStrategy] = [] + if "ticker_interval" not in self.config: + raise OperationalException("Ticker-interval needs to be set in either configuration " + "or as cli argument `--ticker-interval 5m`") + self.ticker_interval = str(self.config.get('ticker_interval')) + self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) + self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange self.fee = self.exchange.get_fee() @@ -89,12 +95,6 @@ class Backtesting(object): Load strategy into backtesting """ self.strategy = strategy - if "ticker_interval" not in self.config: - raise OperationalException("Ticker-interval needs to be set in either configuration " - "or as cli argument `--ticker-interval 5m`") - - self.ticker_interval = self.config.get('ticker_interval') - self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) self.advise_buy = strategy.advise_buy self.advise_sell = strategy.advise_sell # Set stoploss_on_exchange to false for backtesting, From e5da5f7fe79e7ae2a2957f22bce426b97445dd20 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:07:03 +0000 Subject: [PATCH 041/227] Bump mkdocs-material from 4.4.0 to 4.4.1 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 4.4.0 to 4.4.1. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/4.4.0...4.4.1) Signed-off-by: dependabot-preview[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index ce76d52e5..10ea068ad 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1 +1 @@ -mkdocs-material==4.4.0 \ No newline at end of file +mkdocs-material==4.4.1 \ No newline at end of file From 75e3d22043abb0e96a03e2f2201c39261b970de2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:07:07 +0000 Subject: [PATCH 042/227] Bump pytest from 5.1.0 to 5.1.1 Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/5.1.0...5.1.1) Signed-off-by: dependabot-preview[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 6436c60e4..a7b0d358d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ flake8==3.7.8 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 mypy==0.720 -pytest==5.1.0 +pytest==5.1.1 pytest-asyncio==0.10.0 pytest-cov==2.7.1 pytest-mock==1.10.4 From 6b233eb862d79eaca0dd9a6734d4e6e287bbb3da Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:07:24 +0000 Subject: [PATCH 043/227] Bump ccxt from 1.18.1068 to 1.18.1085 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1068 to 1.18.1085. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/1.18.1068...1.18.1085) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 3d80c3ef5..c8a9c2f74 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.18.1068 +ccxt==1.18.1085 SQLAlchemy==1.3.7 python-telegram-bot==11.1.0 arrow==0.14.5 From 6af51358025a9a318cc04c2122b856485ac9f3ce Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 08:07:30 +0000 Subject: [PATCH 044/227] Bump pandas from 0.25.0 to 0.25.1 Bumps [pandas](https://github.com/pandas-dev/pandas) from 0.25.0 to 0.25.1. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v0.25.0...v0.25.1) Signed-off-by: dependabot-preview[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9d558b5b8..e5015d620 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,5 @@ -r requirements-common.txt numpy==1.17.0 -pandas==0.25.0 +pandas==0.25.1 scipy==1.3.1 From 92011f82945f2e36fa73d0bcaaa1ea13fa20a301 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Aug 2019 19:44:33 +0200 Subject: [PATCH 045/227] Introduce strategy_version variable --- freqtrade/resolvers/strategy_resolver.py | 4 ++++ freqtrade/strategy/default_strategy.py | 1 + freqtrade/strategy/interface.py | 5 +++++ freqtrade/tests/strategy/test_strategy.py | 25 +++++++++++++++++++++++ user_data/strategies/test_strategy.py | 7 +++++-- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 37aa96b68..4e6ef154b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -153,6 +153,10 @@ class StrategyResolver(IResolver): strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) + if any([x == 2 for x in [strategy._populate_fun_len, + strategy._buy_fun_len, + strategy._sell_fun_len]]): + strategy.strategy_version = 1 try: return import_strategy(strategy, config=config) diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 5c7d50a65..51ef49193 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -13,6 +13,7 @@ class DefaultStrategy(IStrategy): Default Strategy provided by freqtrade bot. You can override it with your own strategy """ + strategy_version: int = 2 # Minimal ROI designed for the strategy minimal_roi = { diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 99f5f26de..4133d31a9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -60,6 +60,11 @@ class IStrategy(ABC): stoploss -> float: optimal stoploss designed for the strategy ticker_interval -> str: value of the ticker interval to use for the strategy """ + # Strategy interface version + # Default to version 2 + # version 1 is the initial interface without metadata dict + # Version 2 populate_* include metadata dict + strategy_version: int = 2 _populate_fun_len: int = 0 _buy_fun_len: int = 0 diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 240b83b8b..28acdea61 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -380,6 +380,31 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert resolver.strategy._populate_fun_len == 2 assert resolver.strategy._buy_fun_len == 2 assert resolver.strategy._sell_fun_len == 2 + assert resolver.strategy.strategy_version == 1 + + indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) + assert isinstance(indicator_df, DataFrame) + assert 'adx' in indicator_df.columns + + buydf = resolver.strategy.advise_buy(result, metadata=metadata) + assert isinstance(buydf, DataFrame) + assert 'buy' in buydf.columns + + selldf = resolver.strategy.advise_sell(result, metadata=metadata) + assert isinstance(selldf, DataFrame) + assert 'sell' in selldf + + +def test_strategy_versioning(result, monkeypatch, default_conf): + default_conf.update({'strategy': 'DefaultStrategy'}) + resolver = StrategyResolver(default_conf) + metadata = {'pair': 'ETH/BTC'} + + # Make sure we are using a legacy function + assert resolver.strategy._populate_fun_len == 3 + assert resolver.strategy._buy_fun_len == 3 + assert resolver.strategy._sell_fun_len == 3 + assert resolver.strategy.strategy_version == 2 indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) assert isinstance(indicator_df, DataFrame) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index d8ff790b2..876a2845a 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -28,6 +28,9 @@ class TestStrategy(IStrategy): - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, populate_sell_trend, hyperopt_space, buy_strategy_generator """ + # Strategy intervace version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + strategy_version: int = 2 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" @@ -256,14 +259,14 @@ class TestStrategy(IStrategy): # Retrieve best bid and best ask # ------------------------------------ """ - # first check if dataprovider is available + # first check if dataprovider is available if self.dp: if self.dp.runmode in ('live', 'dry_run'): ob = self.dp.orderbook(metadata['pair'], 1) dataframe['best_bid'] = ob['bids'][0][0] dataframe['best_ask'] = ob['asks'][0][0] """ - + return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: From 0e62b8bd8502d70c9675bcbf0327fde076494461 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Aug 2019 20:16:03 +0200 Subject: [PATCH 046/227] Update strategy_version to INTERFACE_VERSION --- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/strategy/default_strategy.py | 2 +- freqtrade/strategy/interface.py | 4 ++-- freqtrade/tests/strategy/test_strategy.py | 6 +++--- user_data/strategies/test_strategy.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 4e6ef154b..514e9f22b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -156,7 +156,7 @@ class StrategyResolver(IResolver): if any([x == 2 for x in [strategy._populate_fun_len, strategy._buy_fun_len, strategy._sell_fun_len]]): - strategy.strategy_version = 1 + strategy.INTERFACE_VERSION = 1 try: return import_strategy(strategy, config=config) diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 51ef49193..4907f20ed 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -13,7 +13,7 @@ class DefaultStrategy(IStrategy): Default Strategy provided by freqtrade bot. You can override it with your own strategy """ - strategy_version: int = 2 + INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy minimal_roi = { diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 4133d31a9..3f2478cc0 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -62,9 +62,9 @@ class IStrategy(ABC): """ # Strategy interface version # Default to version 2 - # version 1 is the initial interface without metadata dict + # Version 1 is the initial interface without metadata dict # Version 2 populate_* include metadata dict - strategy_version: int = 2 + INTERFACE_VERSION: int = 2 _populate_fun_len: int = 0 _buy_fun_len: int = 0 diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 28acdea61..b1a6a6548 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -380,7 +380,7 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert resolver.strategy._populate_fun_len == 2 assert resolver.strategy._buy_fun_len == 2 assert resolver.strategy._sell_fun_len == 2 - assert resolver.strategy.strategy_version == 1 + assert resolver.strategy.INTERFACE_VERSION == 1 indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) assert isinstance(indicator_df, DataFrame) @@ -395,7 +395,7 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert 'sell' in selldf -def test_strategy_versioning(result, monkeypatch, default_conf): +def test_strategy_interface_versioning(result, monkeypatch, default_conf): default_conf.update({'strategy': 'DefaultStrategy'}) resolver = StrategyResolver(default_conf) metadata = {'pair': 'ETH/BTC'} @@ -404,7 +404,7 @@ def test_strategy_versioning(result, monkeypatch, default_conf): assert resolver.strategy._populate_fun_len == 3 assert resolver.strategy._buy_fun_len == 3 assert resolver.strategy._sell_fun_len == 3 - assert resolver.strategy.strategy_version == 2 + assert resolver.strategy.INTERFACE_VERSION == 2 indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) assert isinstance(indicator_df, DataFrame) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 876a2845a..8e2bf8973 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -30,7 +30,7 @@ class TestStrategy(IStrategy): """ # Strategy intervace version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. - strategy_version: int = 2 + INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" From d9c2b7d460dbb9a1eeee7fc53aa43822ecc420e3 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 26 Aug 2019 22:31:24 +0300 Subject: [PATCH 047/227] fix fetching ticker_interval from strategy --- freqtrade/optimize/backtesting.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index f558c82f7..4fba47243 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -64,12 +64,6 @@ class Backtesting(object): self.config['dry_run'] = True self.strategylist: List[IStrategy] = [] - if "ticker_interval" not in self.config: - raise OperationalException("Ticker-interval needs to be set in either configuration " - "or as cli argument `--ticker-interval 5m`") - self.ticker_interval = str(self.config.get('ticker_interval')) - self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) - self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange self.fee = self.exchange.get_fee() @@ -87,6 +81,12 @@ class Backtesting(object): # No strategy list specified, only one strategy self.strategylist.append(StrategyResolver(self.config).strategy) + if "ticker_interval" not in self.config: + raise OperationalException("Ticker-interval needs to be set in either configuration " + "or as cli argument `--ticker-interval 5m`") + self.ticker_interval = str(self.config.get('ticker_interval')) + self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) + # Load one (first) strategy self._set_strategy(self.strategylist[0]) From a504abf00c67d9f79904d265ddca3d5d72d9a305 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 27 Aug 2019 04:12:00 +0300 Subject: [PATCH 048/227] minor: improvements in confuguration.md --- docs/configuration.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 2d21de942..22b112627 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -61,8 +61,9 @@ Mandatory parameters are marked as **Required**. | `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). | `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.sandbox` | false | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details. -| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. -| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. +| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. Keep it in secrete, do not disclose publicly. +| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. Keep it in secrete, do not disclose publicly. +| `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. Keep it in secrete, do not disclose publicly. | `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) @@ -76,8 +77,8 @@ Mandatory parameters are marked as **Required**. | `pairlist.method` | StaticPairList | Use static or dynamic volume-based pairlist. [More information below](#dynamic-pairlists). | `pairlist.config` | None | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). | `telegram.enabled` | true | **Required.** Enable or not the usage of Telegram. -| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. -| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. +| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. Keep it in secrete, do not disclose publicly. +| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. Keep it in secrete, do not disclose publicly. | `webhook.enabled` | false | Enable usage of Webhook notifications | `webhook.url` | false | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. | `webhook.webhookbuy` | false | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. From d66fb86449eccca42637177d0764f1a2eebe5ae2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Aug 2019 06:32:01 +0200 Subject: [PATCH 049/227] Add documentation for interface_version --- docs/strategy-customization.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 9e32ded18..5cf3784ef 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -36,9 +36,14 @@ A strategy file contains all the information needed to build a good strategy: - Minimal ROI recommended - Stoploss strongly recommended -The bot also include a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`. +The bot includes a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`. You can test it with the parameter: `--strategy TestStrategy` +Additionally, there is an attribute called `INTERFACE_VERSION`, which defines the version of the strategy interface the bot should use. +The current version is 2 - which is also the default when it's not set explicitly in the strategy. + +Future versions will require this to be set. + ```bash freqtrade --strategy AwesomeStrategy ``` From 51fbeed71f882902254b23d2459a92cffd35f1f7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Aug 2019 06:41:07 +0200 Subject: [PATCH 050/227] Rename TestStrategy to SampleStrategy --- docs/backtesting.md | 10 +++++----- docs/data-analysis.md | 2 +- docs/strategy-customization.md | 15 +++++++-------- freqtrade/tests/optimize/test_backtesting.py | 6 +++--- freqtrade/tests/optimize/test_hyperopt.py | 2 +- freqtrade/tests/strategy/legacy_strategy.py | 2 +- freqtrade/tests/strategy/test_strategy.py | 12 ++++++------ freqtrade/tests/test_arguments.py | 2 +- .../notebooks/strategy_analysis_example.ipynb | 2 +- .../{test_strategy.py => sample_strategy.py} | 9 ++++----- 10 files changed, 30 insertions(+), 32 deletions(-) rename user_data/strategies/{test_strategy.py => sample_strategy.py} (98%) diff --git a/docs/backtesting.md b/docs/backtesting.md index 13d19f0ca..3da76c0ce 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -79,18 +79,18 @@ freqtrade backtesting --datadir user_data/data/bittrex-20180101 #### With a (custom) strategy file ```bash -freqtrade -s TestStrategy backtesting +freqtrade -s SampleStrategy backtesting ``` -Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory. +Where `-s SampleStrategy` refers to the class name within the strategy file `sample_strategy.py` found in the `freqtrade/user_data/strategies` directory. #### Comparing multiple Strategies ```bash -freqtrade backtesting --strategy-list TestStrategy1 AwesomeStrategy --ticker-interval 5m +freqtrade backtesting --strategy-list SampleStrategy1 AwesomeStrategy --ticker-interval 5m ``` -Where `TestStrategy1` and `AwesomeStrategy` refer to class names of strategies. +Where `SampleStrategy1` and `AwesomeStrategy` refer to class names of strategies. #### Exporting trades to file @@ -103,7 +103,7 @@ The exported trades can be used for [further analysis](#further-backtest-result- #### Exporting trades to file specifying a custom filename ```bash -freqtrade backtesting --export trades --export-filename=backtest_teststrategy.json +freqtrade backtesting --export trades --export-filename=backtest_samplestrategy.json ``` #### Running backtest with smaller testset diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 2f077edb7..f6277cac2 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -139,7 +139,7 @@ You can override strategy settings as demonstrated below. # Define some constants ticker_interval = "5m" # Name of the strategy class -strategy_name = 'TestStrategy' +strategy_name = 'SampleStrategy' # Path to user data user_data_dir = 'user_data' # Location of the strategy diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 9e32ded18..a08304abb 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -24,7 +24,7 @@ strategy file will be updated on Github. Put your custom strategy file into the directory `user_data/strategies`. Best copy the test-strategy and modify this copy to avoid having bot-updates override your changes. -`cp user_data/strategies/test_strategy.py user_data/strategies/awesome-strategy.py` +`cp user_data/strategies/sample_strategy.py user_data/strategies/awesome-strategy.py` ### Anatomy of a strategy @@ -36,14 +36,14 @@ A strategy file contains all the information needed to build a good strategy: - Minimal ROI recommended - Stoploss strongly recommended -The bot also include a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`. -You can test it with the parameter: `--strategy TestStrategy` +The bot also include a sample strategy called `SampleStrategy` you can update: `user_data/strategies/sample_strategy.py`. +You can test it with the parameter: `--strategy SampleStrategy` ```bash freqtrade --strategy AwesomeStrategy ``` -**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py) +**For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py) file as reference.** !!! Note Strategies and Backtesting @@ -109,9 +109,8 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame return dataframe ``` - !!! Note "Want more indicator examples?" - Look into the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py).
+ Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py). Then uncomment indicators you need. ### Buy signal rules @@ -122,7 +121,7 @@ It's important to always return the dataframe without removing/modifying the col This will method will also define a new column, `"buy"`, which needs to contain 1 for buys, and 0 for "no action". -Sample from `user_data/strategies/test_strategy.py`: +Sample from `user_data/strategies/sample_strategy.py`: ```python def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -152,7 +151,7 @@ It's important to always return the dataframe without removing/modifying the col This will method will also define a new column, `"sell"`, which needs to contain 1 for sells, and 0 for "no action". -Sample from `user_data/strategies/test_strategy.py`: +Sample from `user_data/strategies/sample_strategy.py`: ```python def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 5c942ab72..52eae9df0 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -330,7 +330,7 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No patch_exchange(mocker) del default_conf['ticker_interval'] default_conf['strategy_list'] = ['DefaultStrategy', - 'TestStrategy'] + 'SampleStrategy'] mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) with pytest.raises(OperationalException): @@ -877,7 +877,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): '--disable-max-market-positions', '--strategy-list', 'DefaultStrategy', - 'TestStrategy', + 'SampleStrategy', ] args = get_args(args) start_backtesting(args) @@ -898,7 +898,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): 'up to 2017-11-14T22:58:00+00:00 (0 days)..', 'Parameter --enable-position-stacking detected ...', 'Running backtesting for Strategy DefaultStrategy', - 'Running backtesting for Strategy TestStrategy', + 'Running backtesting for Strategy SampleStrategy', ] for line in exists: diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index f9ebf552d..9583de510 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -254,7 +254,7 @@ def test_start_failure(mocker, default_conf, caplog) -> None: args = [ '--config', 'config.json', - '--strategy', 'TestStrategy', + '--strategy', 'SampleStrategy', 'hyperopt', '--epochs', '5' ] diff --git a/freqtrade/tests/strategy/legacy_strategy.py b/freqtrade/tests/strategy/legacy_strategy.py index 2cd13b791..af1b617a6 100644 --- a/freqtrade/tests/strategy/legacy_strategy.py +++ b/freqtrade/tests/strategy/legacy_strategy.py @@ -15,7 +15,7 @@ class TestStrategyLegacy(IStrategy): """ This is a test strategy using the legacy function headers, which will be removed in a future update. - Please do not use this as a template, but refer to user_data/strategy/TestStrategy.py + Please do not use this as a template, but refer to user_data/strategy/sample_strategy.py for a uptodate version of this template. """ diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 240b83b8b..33c6e1128 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -61,27 +61,27 @@ def test_search_strategy(): def test_load_strategy(default_conf, result): - default_conf.update({'strategy': 'TestStrategy'}) + default_conf.update({'strategy': 'SampleStrategy'}) resolver = StrategyResolver(default_conf) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) def test_load_strategy_base64(result, caplog, default_conf): - with open("user_data/strategies/test_strategy.py", "rb") as file: + with open("user_data/strategies/sample_strategy.py", "rb") as file: encoded_string = urlsafe_b64encode(file.read()).decode("utf-8") - default_conf.update({'strategy': 'TestStrategy:{}'.format(encoded_string)}) + default_conf.update({'strategy': 'SampleStrategy:{}'.format(encoded_string)}) resolver = StrategyResolver(default_conf) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) # Make sure strategy was loaded from base64 (using temp directory)!! - assert log_has_re(r"Using resolved strategy TestStrategy from '" - + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", caplog) + assert log_has_re(r"Using resolved strategy SampleStrategy from '" + + tempfile.gettempdir() + r"/.*/SampleStrategy\.py'\.\.\.", caplog) def test_load_strategy_invalid_directory(result, caplog, default_conf): resolver = StrategyResolver(default_conf) extra_dir = Path.cwd() / 'some/path' - resolver._load_strategy('TestStrategy', config=default_conf, extra_dir=extra_dir) + resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 24f11e32e..381457bbd 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -102,7 +102,7 @@ def test_parse_args_backtesting_custom() -> None: '--refresh-pairs-cached', '--strategy-list', 'DefaultStrategy', - 'TestStrategy' + 'SampleStrategy' ] call_args = Arguments(args, '').get_parsed_arg() assert call_args.config == ['test_conf.json'] diff --git a/user_data/notebooks/strategy_analysis_example.ipynb b/user_data/notebooks/strategy_analysis_example.ipynb index 014f4ca90..89d71fe9d 100644 --- a/user_data/notebooks/strategy_analysis_example.ipynb +++ b/user_data/notebooks/strategy_analysis_example.ipynb @@ -52,7 +52,7 @@ "# Define some constants\n", "ticker_interval = \"5m\"\n", "# Name of the strategy class\n", - "strategy_name = 'TestStrategy'\n", + "strategy_name = 'SampleStrategy'\n", "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/sample_strategy.py similarity index 98% rename from user_data/strategies/test_strategy.py rename to user_data/strategies/sample_strategy.py index d8ff790b2..db16dff79 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/sample_strategy.py @@ -11,10 +11,9 @@ import numpy # noqa # This class is a sample. Feel free to customize it. -class TestStrategy(IStrategy): - __test__ = False # pytest expects to find tests here because of the name +class SampleStrategy(IStrategy): """ - This is a test strategy to inspire you. + This is an example strategy to inspire you. More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md You can: @@ -256,14 +255,14 @@ class TestStrategy(IStrategy): # Retrieve best bid and best ask # ------------------------------------ """ - # first check if dataprovider is available + # first check if dataprovider is available if self.dp: if self.dp.runmode in ('live', 'dry_run'): ob = self.dp.orderbook(metadata['pair'], 1) dataframe['best_bid'] = ob['bids'][0][0] dataframe['best_ask'] = ob['asks'][0][0] """ - + return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: From 756f44fcbdb0395025468d4b3f4806fff99a5918 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 28 Aug 2019 00:20:32 +0300 Subject: [PATCH 051/227] highlight really important notifications --- docs/configuration.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 22b112627..fcd6c2bf6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -61,9 +61,9 @@ Mandatory parameters are marked as **Required**. | `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). | `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.sandbox` | false | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details. -| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. Keep it in secrete, do not disclose publicly. -| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. Keep it in secrete, do not disclose publicly. -| `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. Keep it in secrete, do not disclose publicly. +| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** +| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** +| `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. ***Keep it in secrete, do not disclose publicly.*** | `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) @@ -77,8 +77,8 @@ Mandatory parameters are marked as **Required**. | `pairlist.method` | StaticPairList | Use static or dynamic volume-based pairlist. [More information below](#dynamic-pairlists). | `pairlist.config` | None | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). | `telegram.enabled` | true | **Required.** Enable or not the usage of Telegram. -| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. Keep it in secrete, do not disclose publicly. -| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. Keep it in secrete, do not disclose publicly. +| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** +| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** | `webhook.enabled` | false | Enable usage of Webhook notifications | `webhook.url` | false | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. | `webhook.webhookbuy` | false | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. From 8923c02222d3887ac10b012a5825cedb19c13e5c Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Aug 2019 06:07:18 +0200 Subject: [PATCH 052/227] docstring wording --- user_data/strategies/sample_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_data/strategies/sample_strategy.py b/user_data/strategies/sample_strategy.py index db16dff79..4610a2e41 100644 --- a/user_data/strategies/sample_strategy.py +++ b/user_data/strategies/sample_strategy.py @@ -13,7 +13,7 @@ import numpy # noqa # This class is a sample. Feel free to customize it. class SampleStrategy(IStrategy): """ - This is an example strategy to inspire you. + This is a sample strategy to inspire you. More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md You can: From c38f3a2b9a89fe2e6c76a8e098aa4d11b31c5614 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Aug 2019 07:05:48 +0200 Subject: [PATCH 053/227] Apply dynamic versioning to develop --- freqtrade/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 5ccc2ff3c..175b689f9 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,15 @@ """ FreqTrade bot """ -__version__ = '2019.7-dev' +__version__ = 'develop' + +if __version__ == 'develop': + + try: + import subprocess + __version__ = str(subprocess.check_output( + ["git", "describe"], stderr=subprocess.DEVNULL).rstrip()) + except Exception: + # git not available, ignore + pass class DependencyException(Exception): From 68adfc6607ba790ad3d26d8b1ef332d3190f886c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Aug 2019 06:42:56 +0200 Subject: [PATCH 054/227] Init exchange before datadir ... --- freqtrade/configuration/configuration.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index f5480c53a..e58ce0de1 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -160,6 +160,11 @@ class Configuration(object): Extract information for sys.argv and load directory configurations --user-data, --datadir """ + # Check exchange parameter here - otherwise `datadir` might be wrong. + if "exchange" in self.args and self.args.exchange: + config['exchange']['name'] = self.args.exchange + logger.info(f"Using exchange {config['exchange']['name']}") + if 'user_data_dir' in self.args and self.args.user_data_dir: config.update({'user_data_dir': self.args.user_data_dir}) elif 'user_data_dir' not in config: @@ -297,10 +302,6 @@ class Configuration(object): self._args_to_config(config, argname='days', logstring='Detected --days: {}') - if "exchange" in self.args and self.args.exchange: - config['exchange']['name'] = self.args.exchange - logger.info(f"Using exchange {config['exchange']['name']}") - def _process_runmode(self, config: Dict[str, Any]) -> None: if not self.runmode: @@ -361,7 +362,7 @@ class Configuration(object): config['pairs'] = config.get('exchange', {}).get('pair_whitelist') else: # Fall back to /dl_path/pairs.json - pairs_file = Path(config['datadir']) / config['exchange']['name'].lower() / "pairs.json" + pairs_file = Path(config['datadir']) / "pairs.json" if pairs_file.exists(): with pairs_file.open('r') as f: config['pairs'] = json_load(f) From 6b3d25b54b4f47ebd3b658aeaee3ff2501a23d14 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Aug 2019 06:45:20 +0200 Subject: [PATCH 055/227] Fix datadir init when used wiht --exchange --- freqtrade/tests/test_configuration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index d98ff9ad0..b8bc62eb6 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -871,3 +871,4 @@ def test_pairlist_resolving_fallback(mocker): assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] assert config['exchange']['name'] == 'binance' + assert config['datadir'] == str(Path.cwd() / "user_data/data/binance") From cabe2910062610ee91768f7d111c9e8e1894705e Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Aug 2019 06:54:28 +0200 Subject: [PATCH 056/227] Fix test-leakage by not copying config correctly --- freqtrade/configuration/configuration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index e58ce0de1..b1bd3ef1c 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -4,6 +4,7 @@ This module contains the configuration class import logging import warnings from argparse import Namespace +from copy import deepcopy from pathlib import Path from typing import Any, Callable, Dict, List, Optional @@ -56,7 +57,7 @@ class Configuration(object): config: Dict[str, Any] = {} if not files: - return constants.MINIMAL_CONFIG.copy() + return deepcopy(constants.MINIMAL_CONFIG) # We expect here a list of config filenames for path in files: From b6b7dcd61c28722f87f34bffefd84c0982b995ce Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Aug 2019 06:59:19 +0200 Subject: [PATCH 057/227] Test NotImplemented is cought correctly --- freqtrade/tests/rpc/test_rpc_manager.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/freqtrade/tests/rpc/test_rpc_manager.py b/freqtrade/tests/rpc/test_rpc_manager.py index 468e3e8e5..d34d76524 100644 --- a/freqtrade/tests/rpc/test_rpc_manager.py +++ b/freqtrade/tests/rpc/test_rpc_manager.py @@ -115,6 +115,22 @@ def test_init_webhook_enabled(mocker, default_conf, caplog) -> None: assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules] +def test_send_msg_webhook_CustomMessagetype(mocker, default_conf, caplog) -> None: + caplog.set_level(logging.DEBUG) + default_conf['telegram']['enabled'] = False + default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"} + mocker.patch('freqtrade.rpc.webhook.Webhook.send_msg', + MagicMock(side_effect=NotImplementedError)) + rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) + + assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules] + rpc_manager.send_msg({'type': RPCMessageType.CUSTOM_NOTIFICATION, + 'status': 'TestMessage'}) + assert log_has( + "Message type RPCMessageType.CUSTOM_NOTIFICATION not implemented by handler webhook.", + caplog) + + def test_startupmessages_telegram_enabled(mocker, default_conf, caplog) -> None: telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) From d977695d48594f7f7c10610ad9798398b2e9eae0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Aug 2019 07:02:26 +0200 Subject: [PATCH 058/227] Catch NotImplementedError when sending messages (RPC should not crash your bot!) --- freqtrade/rpc/rpc_manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index fad532aa0..d6e7b174d 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -56,7 +56,10 @@ class RPCManager(object): logger.info('Sending rpc message: %s', msg) for mod in self.registered_modules: logger.debug('Forwarding message to rpc.%s', mod.name) - mod.send_msg(msg) + try: + mod.send_msg(msg) + except NotImplementedError: + logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.") def startup_messages(self, config, pairlist) -> None: if config.get('dry_run', False): From 75dc174c76992ae7d762e2183acd798f111435be Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Aug 2019 07:02:57 +0200 Subject: [PATCH 059/227] support all messagetypes in webhook --- freqtrade/rpc/webhook.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index bfc82b8d6..37ca466de 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -43,7 +43,9 @@ class Webhook(RPC): valuedict = self._config['webhook'].get('webhookbuy', None) elif msg['type'] == RPCMessageType.SELL_NOTIFICATION: valuedict = self._config['webhook'].get('webhooksell', None) - elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION: + elif msg['type'] in(RPCMessageType.STATUS_NOTIFICATION, + RPCMessageType.CUSTOM_NOTIFICATION, + RPCMessageType.WARNING_NOTIFICATION): valuedict = self._config['webhook'].get('webhookstatus', None) else: raise NotImplementedError('Unknown message type: {}'.format(msg['type'])) From d060d277457b26e1e7229e04ccb3d6a95ef6e90d Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Aug 2019 07:05:22 +0200 Subject: [PATCH 060/227] Add test for all messagetypes --- freqtrade/tests/rpc/test_rpc_webhook.py | 33 ++++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc_webhook.py b/freqtrade/tests/rpc/test_rpc_webhook.py index cc491d4dd..1c6c07e16 100644 --- a/freqtrade/tests/rpc/test_rpc_webhook.py +++ b/freqtrade/tests/rpc/test_rpc_webhook.py @@ -91,21 +91,24 @@ def test_send_msg(default_conf, mocker): assert (msg_mock.call_args[0][0]["value3"] == default_conf["webhook"]["webhooksell"]["value3"].format(**msg)) - # Test notification - msg = { - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': 'Unfilled sell order for BTC cancelled due to timeout' - } - msg_mock = MagicMock() - mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) - webhook.send_msg(msg) - assert msg_mock.call_count == 1 - assert (msg_mock.call_args[0][0]["value1"] == - default_conf["webhook"]["webhookstatus"]["value1"].format(**msg)) - assert (msg_mock.call_args[0][0]["value2"] == - default_conf["webhook"]["webhookstatus"]["value2"].format(**msg)) - assert (msg_mock.call_args[0][0]["value3"] == - default_conf["webhook"]["webhookstatus"]["value3"].format(**msg)) + for msgtype in [RPCMessageType.STATUS_NOTIFICATION, + RPCMessageType.WARNING_NOTIFICATION, + RPCMessageType.CUSTOM_NOTIFICATION]: + # Test notification + msg = { + 'type': msgtype, + 'status': 'Unfilled sell order for BTC cancelled due to timeout' + } + msg_mock = MagicMock() + mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) + webhook.send_msg(msg) + assert msg_mock.call_count == 1 + assert (msg_mock.call_args[0][0]["value1"] == + default_conf["webhook"]["webhookstatus"]["value1"].format(**msg)) + assert (msg_mock.call_args[0][0]["value2"] == + default_conf["webhook"]["webhookstatus"]["value2"].format(**msg)) + assert (msg_mock.call_args[0][0]["value3"] == + default_conf["webhook"]["webhookstatus"]["value3"].format(**msg)) def test_exception_send_msg(default_conf, mocker, caplog): From 423805c9caac2a0754b2cdbbbd9aab85abfce654 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Aug 2019 20:42:14 +0200 Subject: [PATCH 061/227] Small documentation improvements --- docs/plotting.md | 12 ++++++------ freqtrade/configuration/arguments.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index 00fdd33a4..d3247488c 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -12,7 +12,7 @@ pip install -U -r requirements-plot.txt ## Plot price and indicators -Plot dataframe shows an interactive graph with three subplots: +The `freqtrade plot-dataframe` subcommand shows an interactive graph with three subplots: * Main plot with candlestics and indicators following price (sma/ema) * Volume bars @@ -71,10 +71,10 @@ Example: freqtrade plot-dataframe -p BTC/ETH ``` -The `--pairs` argument can be used to specify pairs you would like to plot. +The `-p/--pairs` argument can be used to specify pairs you would like to plot. !!! Note - Generates one plot-file per pair. + The `freqtrade plot-dataframe` subcommand generates one plot-file per pair. Specify custom indicators. Use `--indicators1` for the main plot and `--indicators2` for the subplot below (if values are in a different range than prices). @@ -116,7 +116,7 @@ freqtrade --strategy AwesomeStrategy plot-dataframe --export-filename user_data/ ![plot-profit](assets/plot-profit.png) -Plot profit shows an interactive graph with three plots: +The `freqtrade plot-profit` subcommand shows an interactive graph with three plots: 1) Average closing price for all pairs 2) The summarized profit made by backtesting. @@ -130,7 +130,7 @@ Perhaps you want an algorithm that steadily makes small profits, or one that act The third graph can be useful to spot outliers, events in pairs that cause profit spikes. -Usage for the plot-profit module: +Possible options for the `freqtrade plot-profit` subcommand: ``` usage: freqtrade plot-profit [-h] [-p PAIRS [PAIRS ...]] @@ -162,7 +162,7 @@ optional arguments: ``` -The `--pairs` argument, can be used to limit the pairs that are considered for this calculation. +The `-p/--pairs` argument, can be used to limit the pairs that are considered for this calculation. Examples: diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 4bd4410a6..f77832387 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -117,7 +117,7 @@ class Arguments(object): hyperopt_cmd.set_defaults(func=start_hyperopt) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) - # Create userdir subcommand + # add create-userdir subcommand create_userdir_cmd = subparsers.add_parser('create-userdir', help="Create user-data directory.") create_userdir_cmd.set_defaults(func=start_create_userdir) From f278fcfc3fb41b2cc9be482824699bc875bd8bfa Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:14:57 +0200 Subject: [PATCH 062/227] Use plot-runmode for plot scripts --- freqtrade/plot/plot_utils.py | 4 ++-- freqtrade/state.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py index 11bc9f552..507e86d9d 100644 --- a/freqtrade/plot/plot_utils.py +++ b/freqtrade/plot/plot_utils.py @@ -10,7 +10,7 @@ def start_plot_dataframe(args: Namespace) -> None: """ # Import here to avoid errors if plot-dependencies are not installed. from freqtrade.plot.plotting import analyse_and_plot_pairs - config = setup_utils_configuration(args, RunMode.OTHER) + config = setup_utils_configuration(args, RunMode.PLOT) analyse_and_plot_pairs(config) @@ -21,6 +21,6 @@ def start_plot_profit(args: Namespace) -> None: """ # Import here to avoid errors if plot-dependencies are not installed. from freqtrade.plot.plotting import plot_profit - config = setup_utils_configuration(args, RunMode.OTHER) + config = setup_utils_configuration(args, RunMode.PLOT) plot_profit(config) diff --git a/freqtrade/state.py b/freqtrade/state.py index ce2683a77..d4a2adba0 100644 --- a/freqtrade/state.py +++ b/freqtrade/state.py @@ -25,4 +25,5 @@ class RunMode(Enum): BACKTEST = "backtest" EDGE = "edge" HYPEROPT = "hyperopt" + PLOT = "plot" OTHER = "other" # Used for plotting scripts and test From 1760a8dfbc16d3dcb2e94878c47214f860094dfd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:15:10 +0200 Subject: [PATCH 063/227] Use subparser-name to exclude from config requires --- freqtrade/configuration/arguments.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index f77832387..2c814b342 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -40,7 +40,7 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_ ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "ticker_interval"] -NO_CONF_REQURIED = ["start_download_data"] +NO_CONF_REQURIED = ["download-data", "plot-dataframe", "plot-profit"] class Arguments(object): @@ -79,8 +79,7 @@ class Arguments(object): # (see https://bugs.python.org/issue16399) # Allow no-config for certain commands (like downloading / plotting) if (not self._no_default_config and parsed_arg.config is None - and not (hasattr(parsed_arg, 'func') - and parsed_arg.func.__name__ in NO_CONF_REQURIED)): + and not ('subparser' in parsed_arg and parsed_arg.subparser in NO_CONF_REQURIED)): parsed_arg.config = [constants.DEFAULT_CONFIG] return parsed_arg From d48f03c32e1e5ec722bed20efb1d621d4476a3cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:15:30 +0200 Subject: [PATCH 064/227] check_exchange is not required for plotting --- freqtrade/configuration/check_exchange.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index 70f4cfa33..61e862a9c 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -5,6 +5,7 @@ from freqtrade import OperationalException from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason, is_exchange_available, is_exchange_bad, is_exchange_officially_supported) +from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -19,6 +20,10 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: raises an exception if the exchange if not supported by ccxt and thus is not known for the Freqtrade at all. """ + + if config['runmode'] in [RunMode.PLOT] and not config.get('exchange', {}).get('name'): + # Skip checking exchange in plot mode, since it requires no exchange + return True logger.info("Checking exchange...") exchange = config.get('exchange', {}).get('name').lower() From c9e15c2f86b7e7b1c8a2ae26d3c303f35f6bb70d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:19:59 +0200 Subject: [PATCH 065/227] Add test for new check_exchange branch --- freqtrade/tests/test_configuration.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index b8bc62eb6..5a70715a4 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -479,6 +479,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: def test_check_exchange(default_conf, caplog) -> None: # Test an officially supported by Freqtrade team exchange + default_conf['runmode'] = RunMode.DRY_RUN default_conf.get('exchange').update({'name': 'BITTREX'}) assert check_exchange(default_conf) assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", @@ -523,6 +524,11 @@ def test_check_exchange(default_conf, caplog) -> None: ): check_exchange(default_conf) + # Test no exchange... + default_conf.get('exchange').update({'name': ''}) + default_conf['runmode'] = RunMode.PLOT + assert check_exchange(default_conf) + def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) From 736deaae32dc5c462345c95c988000ce2f02a7f8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:26:34 +0200 Subject: [PATCH 066/227] Add test with plot command without configuration --- freqtrade/tests/test_arguments.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index fd984a504..558642894 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -163,6 +163,20 @@ def test_plot_dataframe_options() -> None: assert pargs.pairs == ["UNITTEST/BTC"] +def test_plot_profit_options() -> None: + args = [ + 'plot-profit', + '-p', 'UNITTEST/BTC', + '--trade-source', 'DB', + "--db-url", "sqlite:///whatever.sqlite", + ] + pargs = Arguments(args, '').get_parsed_arg() + + assert pargs.trade_source == "DB" + assert pargs.pairs == ["UNITTEST/BTC"] + assert pargs.db_url == "sqlite:///whatever.sqlite" + + def test_check_int_positive() -> None: assert check_int_positive("3") == 3 assert check_int_positive("1") == 1 From 2886fa288ad073ed7aeb5507c8dab35a00836320 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 15:31:47 +0200 Subject: [PATCH 067/227] fix documentation --- docs/plotting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plotting.md b/docs/plotting.md index d3247488c..61bf7b74a 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -80,7 +80,7 @@ Specify custom indicators. Use `--indicators1` for the main plot and `--indicators2` for the subplot below (if values are in a different range than prices). !!! tip - You will almost certainly want to specify a custom strategy! This can be done by adding `--strategy ClassName` to the command. + You will almost certainly want to specify a custom strategy! This can be done by adding `-s Classname` / `--strategy ClassName` to the command. ``` bash freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd From f0c0f5618b1614978b9003be6165035c7e50ef34 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 16:11:04 +0200 Subject: [PATCH 068/227] Abstract creating stoploss-orders from stoploss-logic --- freqtrade/freqtradebot.py | 67 +++++++++++++--------------- freqtrade/tests/test_freqtradebot.py | 2 +- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8857b95da..1d78f0a21 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -617,6 +617,26 @@ class FreqtradeBot(object): logger.debug('Found no sell signal for %s.', trade) return False + def create_stoploss_order(self, trade: Trade, stop_price: float, rate: float) -> bool: + """ + Abstracts creating stoploss orders from the logic. + Handles errors and updates the trade database object. + :return: True if the order succeeded, and False in case of problems. + """ + # Limit price threshold: As limit price should always be below price + LIMIT_PRICE_PCT = 0.99 + + try: + stoploss_order = self.exchange.stoploss_limit(pair=trade.pair, amount=trade.amount, + stop_price=stop_price, + rate=rate * LIMIT_PRICE_PCT) + trade.stoploss_order_id = str(stoploss_order['id']) + return True + except DependencyException: + trade.stoploss_order_id = None + logger.exception('Unable to place a stoploss order on exchange.') + return False + def handle_stoploss_on_exchange(self, trade: Trade) -> bool: """ Check if trade is fulfilled in which case the stoploss @@ -638,9 +658,6 @@ class FreqtradeBot(object): # If trade open order id does not exist: buy order is fulfilled buy_order_fulfilled = not trade.open_order_id - # Limit price threshold: As limit price should always be below price - limit_price_pct = 0.99 - # If buy order is fulfilled but there is no stoploss, we add a stoploss on exchange if (buy_order_fulfilled and not stoploss_order): if self.edge: @@ -650,34 +667,18 @@ class FreqtradeBot(object): stop_price = trade.open_rate * (1 + stoploss) - # limit price should be less than stop price. - limit_price = stop_price * limit_price_pct - - try: - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, stop_price=stop_price, rate=limit_price - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) + if self.create_stoploss_order(trade=trade, stop_price=stop_price, rate=stop_price): trade.stoploss_last_update = datetime.now() return False - except DependencyException as exception: - trade.stoploss_order_id = None - logger.warning('Unable to place a stoploss order on exchange: %s', exception) - # If stoploss order is canceled for some reason we add it if stoploss_order and stoploss_order['status'] == 'canceled': - try: - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, - stop_price=trade.stop_loss, rate=trade.stop_loss * limit_price_pct - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) + if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, + rate=trade.stop_loss): return False - except DependencyException as exception: + else: trade.stoploss_order_id = None - logger.warning('Stoploss order was cancelled, ' - 'but unable to recreate one: %s', exception) + logger.warning('Stoploss order was cancelled, but unable to recreate one.') # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] == 'closed': @@ -720,17 +721,13 @@ class FreqtradeBot(object): logger.exception(f"Could not cancel stoploss order {order['id']} " f"for pair {trade.pair}") - try: - # creating the new one - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, - stop_price=trade.stop_loss, rate=trade.stop_loss * 0.99 - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) - except DependencyException: - trade.stoploss_order_id = None - logger.exception(f"Could not create trailing stoploss order " - f"for pair {trade.pair}.") + # Create new stoploss order + if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, + rate=trade.stop_loss): + return False + else: + logger.warning(f"Could not create trailing stoploss order " + f"for pair {trade.pair}.") def _check_and_execute_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1119157c4..7b4023065 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1153,7 +1153,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, side_effect=DependencyException() ) freqtrade.handle_stoploss_on_exchange(trade) - assert log_has('Unable to place a stoploss order on exchange: ', caplog) + assert log_has('Unable to place a stoploss order on exchange.', caplog) assert trade.stoploss_order_id is None # Fifth case: get_order returns InvalidOrder From 7fc156648a88f974d278af8be947877fb92739c8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 16:15:39 +0200 Subject: [PATCH 069/227] simplify stoploss_oe code --- freqtrade/freqtradebot.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1d78f0a21..881e489be 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -655,15 +655,10 @@ class FreqtradeBot(object): except InvalidOrderException as exception: logger.warning('Unable to fetch stoploss order: %s', exception) - # If trade open order id does not exist: buy order is fulfilled - buy_order_fulfilled = not trade.open_order_id - # If buy order is fulfilled but there is no stoploss, we add a stoploss on exchange - if (buy_order_fulfilled and not stoploss_order): - if self.edge: - stoploss = self.edge.stoploss(pair=trade.pair) - else: - stoploss = self.strategy.stoploss + if (not trade.open_order_id and not stoploss_order): + + stoploss = self.edge.stoploss(pair=trade.pair) if self.edge else self.strategy.stoploss stop_price = trade.open_rate * (1 + stoploss) From ee808abfea45c594334e8c14540a2842df520073 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:07:09 +0200 Subject: [PATCH 070/227] Add emergency_sell as sell reason --- config_full.json.example | 1 + docs/configuration.md | 3 +++ freqtrade/constants.py | 1 + freqtrade/strategy/interface.py | 1 + 4 files changed, 6 insertions(+) diff --git a/config_full.json.example b/config_full.json.example index b6451859c..957967042 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -38,6 +38,7 @@ "order_types": { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 diff --git a/docs/configuration.md b/docs/configuration.md index fcd6c2bf6..dc4c365f6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -205,6 +205,7 @@ Values set in the configuration file overwrites values set in the strategy. If this is configured, all 4 values (`buy`, `sell`, `stoploss` and `stoploss_on_exchange`) need to be present, otherwise the bot will warn about it and fail to start. +`emergencysell` is an optional value, which defaults to `market` and is used when creating stoploss on exchange orders fails. The below is the default which is used if this is not configured in either strategy or configuration file. Syntax for Strategy: @@ -213,6 +214,7 @@ Syntax for Strategy: order_types = { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": False, "stoploss_on_exchange_interval": 60 @@ -225,6 +227,7 @@ Configuration: "order_types": { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 05ee99c1b..d7ace131c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -121,6 +121,7 @@ CONF_SCHEMA = { 'properties': { 'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'emergencysell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss_on_exchange': {'type': 'boolean'}, 'stoploss_on_exchange_interval': {'type': 'number'} diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 3f2478cc0..6c6593a53 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -39,6 +39,7 @@ class SellType(Enum): TRAILING_STOP_LOSS = "trailing_stop_loss" SELL_SIGNAL = "sell_signal" FORCE_SELL = "force_sell" + EMERGENCY_SELL = "emergency_sell" NONE = "" From 9f53e9f5dde06f2edcd9eb8fa9399daaa3e96f8b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:08:35 +0200 Subject: [PATCH 071/227] Raise InvalidOrder error when stoploss-creation fails --- freqtrade/exchange/binance.py | 7 +++++-- freqtrade/freqtradebot.py | 6 ++++++ freqtrade/tests/exchange/test_binance.py | 8 +++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 5834f26cd..c3021c50b 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -4,7 +4,8 @@ from typing import Dict import ccxt -from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) @@ -69,7 +70,9 @@ class Binance(Exchange): f'Tried to sell amount {amount} at rate {rate}.' f'Message: {e}') from e except ccxt.InvalidOrder as e: - raise DependencyException( + # Errors: + # `binance Order would trigger immediately.` + raise InvalidOrderException( f'Could not create {ordertype} sell order on market {pair}. ' f'Tried to sell amount {amount} at rate {rate}.' f'Message: {e}') from e diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 881e489be..ebc1fd4e8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -632,6 +632,12 @@ class FreqtradeBot(object): rate=rate * LIMIT_PRICE_PCT) trade.stoploss_order_id = str(stoploss_order['id']) return True + except InvalidOrderException: + trade.stoploss_order_id = None + logger.exception('Unable to place a stoploss order on exchange.') + logger.warning('Selling the trade forcefully') + self.execute_sell(trade, trade.stop_loss, sell_reason=SellType.EMERGENCY_SELL) + except DependencyException: trade.stoploss_order_id = None logger.exception('Unable to place a stoploss order on exchange.') diff --git a/freqtrade/tests/exchange/test_binance.py b/freqtrade/tests/exchange/test_binance.py index 4afb7fcc4..6518c8523 100644 --- a/freqtrade/tests/exchange/test_binance.py +++ b/freqtrade/tests/exchange/test_binance.py @@ -4,7 +4,8 @@ from unittest.mock import MagicMock import ccxt import pytest -from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.tests.conftest import get_patched_exchange @@ -49,8 +50,9 @@ def test_stoploss_limit_order(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) + with pytest.raises(InvalidOrderException): + api_mock.create_order = MagicMock( + side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) From 292df115e86b144db713566fea31b57f2d572f68 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:09:07 +0200 Subject: [PATCH 072/227] Support selling via emergencysell --- freqtrade/freqtradebot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ebc1fd4e8..cd0ea289a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -881,9 +881,14 @@ class FreqtradeBot(object): except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") + ordertype = self.strategy.order_types[sell_type] + if sell_reason == SellType.EMERGENCY_SELL: + # Emergencysells (default to market!) + ordertype = self.strategy.order_types.get("emergencysell", "market") + # Execute sell and update trade record order = self.exchange.sell(pair=str(trade.pair), - ordertype=self.strategy.order_types[sell_type], + ordertype=ordertype, amount=trade.amount, rate=limit, time_in_force=self.strategy.order_time_in_force['sell'] ) From 7c0a49a6f9c04418f0b8f94fc8cf7cf67e6377ef Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:17:58 +0200 Subject: [PATCH 073/227] _notify_sell needs ordertype seperately --- freqtrade/freqtradebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cd0ea289a..6fe2c7f15 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -688,7 +688,7 @@ class FreqtradeBot(object): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) - self._notify_sell(trade) + self._notify_sell(trade, "stoploss") return True # Finally we check if stoploss on exchange should be moved up because of trailing. @@ -904,9 +904,9 @@ class FreqtradeBot(object): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) - self._notify_sell(trade) + self._notify_sell(trade, ordertype) - def _notify_sell(self, trade: Trade): + def _notify_sell(self, trade: Trade, order_type: str): """ Sends rpc notification when a sell occured. """ @@ -923,7 +923,7 @@ class FreqtradeBot(object): 'pair': trade.pair, 'gain': gain, 'limit': trade.close_rate_requested, - 'order_type': self.strategy.order_types['sell'], + 'order_type': order_type, 'amount': trade.amount, 'open_rate': trade.open_rate, 'current_rate': current_rate, From 6aab3fe25acab7998eeed07ff7ab58620c5e409a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:18:15 +0200 Subject: [PATCH 074/227] Add test for stoploss order handling behaviour --- freqtrade/tests/test_freqtradebot.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 7b4023065..e4e5104d0 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1201,6 +1201,50 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert trade.is_open is True +def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, + markets, limit_buy_order, limit_sell_order): + rpc_mock = patch_RPCManager(mocker) + patch_exchange(mocker) + sell_mock = MagicMock(return_value={'id': limit_sell_order['id']}) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.00001172, + 'ask': 0.00001173, + 'last': 0.00001172 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + sell=sell_mock, + get_fee=fee, + markets=PropertyMock(return_value=markets), + get_order=MagicMock(return_value={'status': 'canceled'}), + stoploss_limit=MagicMock(side_effect=InvalidOrderException()), + ) + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + + freqtrade.create_trades() + trade = Trade.query.first() + caplog.clear() + freqtrade.create_stoploss_order(trade, 200, 199) + assert trade.stoploss_order_id is None + assert trade.sell_reason == SellType.EMERGENCY_SELL.value + assert log_has("Unable to place a stoploss order on exchange.", caplog) + assert log_has("Selling the trade forcefully", caplog) + + # Should call a market sell + assert sell_mock.call_count == 1 + assert sell_mock.call_args[1]['ordertype'] == 'market' + assert sell_mock.call_args[1]['pair'] == trade.pair + assert sell_mock.call_args[1]['amount'] == trade.amount + + # Rpc is sending first buy, then sell + assert rpc_mock.call_count == 2 + assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value + assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' + + def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, markets, limit_buy_order, limit_sell_order) -> None: # When trailing stoploss is set From 9d7ebc65e734a7cbcf14e54fe8dc458f2137c97f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:21:45 +0200 Subject: [PATCH 075/227] Move return statement to correct intend --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6fe2c7f15..ab3a1df28 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -641,7 +641,7 @@ class FreqtradeBot(object): except DependencyException: trade.stoploss_order_id = None logger.exception('Unable to place a stoploss order on exchange.') - return False + return False def handle_stoploss_on_exchange(self, trade: Trade) -> bool: """ From 514860ac3b5700f33b3e786893e916761a47541f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:02 +0200 Subject: [PATCH 076/227] Improve documentation --- docs/configuration.md | 27 +++++++++++++++------------ docs/strategy-customization.md | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index dc4c365f6..1f64da306 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -192,19 +192,20 @@ end up paying more then would probably have been necessary. ### Understand order_types -The `order_types` configuration parameter contains a dict mapping order-types to -market-types as well as stoploss on or off exchange type and stoploss on exchange -update interval in seconds. This allows to buy using limit orders, sell using +The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and stoploss on exchange update interval in seconds. + +This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. In case stoploss on exchange and `trailing_stop` are -both set, then the bot will use `stoploss_on_exchange_interval` to check it periodically -and update it if necessary (e.x. in case of trailing stoploss). -This can be set in the configuration file or in the strategy. -Values set in the configuration file overwrites values set in the strategy. +both set, then the bot will use `stoploss_on_exchange_interval` to check the stoploss periodically +and update it if necessary (e.g. in case of trailing stoploss). +`order_types` can be set in the configuration file or in the strategy. +`order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place. + +If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and +`stoploss_on_exchange`) need to be present, otherwise the bot will fail to start. -If this is configured, all 4 values (`buy`, `sell`, `stoploss` and -`stoploss_on_exchange`) need to be present, otherwise the bot will warn about it and fail to start. `emergencysell` is an optional value, which defaults to `market` and is used when creating stoploss on exchange orders fails. The below is the default which is used if this is not configured in either strategy or configuration file. @@ -242,11 +243,13 @@ Configuration: !!! Note Stoploss on exchange interval is not mandatory. Do not change its value if you are unsure of what you are doing. For more information about how stoploss works please - read [the stoploss documentation](stoploss.md). + refer to [the stoploss documentation](stoploss.md). !!! Note - In case of stoploss on exchange if the stoploss is cancelled manually then - the bot would recreate one. + If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot wil create a new order. + +!!! Warning stoploss_on_exchange failures + If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting `emergencysell` - however this is not advised. ### Understand order_time_in_force diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c7da19659..e07585800 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -224,7 +224,7 @@ This would signify a stoploss of -10%. For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md). -If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order dict, so your stoploss is on the exchange and cannot be missed for network-problems (or other problems). +If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order_types dictionary, so your stoploss is on the exchange and cannot be missed due to network-problems (or other problems). For more information on order_types please look [here](configuration.md#understand-order_types). From f91557f549f21e56136c358b782e759a1f4233cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:17 +0200 Subject: [PATCH 077/227] Add space to exception message --- freqtrade/exchange/binance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index c3021c50b..14f409659 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -67,14 +67,14 @@ class Binance(Exchange): except ccxt.InsufficientFunds as e: raise DependencyException( f'Insufficient funds to create {ordertype} sell order on market {pair}.' - f'Tried to sell amount {amount} at rate {rate}.' + f'Tried to sell amount {amount} at rate {rate}. ' f'Message: {e}') from e except ccxt.InvalidOrder as e: # Errors: # `binance Order would trigger immediately.` raise InvalidOrderException( f'Could not create {ordertype} sell order on market {pair}. ' - f'Tried to sell amount {amount} at rate {rate}.' + f'Tried to sell amount {amount} at rate {rate}. ' f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( From 771519e311170ca82de985c871a0f8f7db6870d1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:36 +0200 Subject: [PATCH 078/227] Don't show stacktrace in case of invalidorder Error This is handled gracefully by emergency-selling --- freqtrade/freqtradebot.py | 4 ++-- freqtrade/tests/test_freqtradebot.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ab3a1df28..7c403a29b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -632,9 +632,9 @@ class FreqtradeBot(object): rate=rate * LIMIT_PRICE_PCT) trade.stoploss_order_id = str(stoploss_order['id']) return True - except InvalidOrderException: + except InvalidOrderException as e: trade.stoploss_order_id = None - logger.exception('Unable to place a stoploss order on exchange.') + logger.error(f'Unable to place a stoploss order on exchange. {e}') logger.warning('Selling the trade forcefully') self.execute_sell(trade, trade.stop_loss, sell_reason=SellType.EMERGENCY_SELL) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index e4e5104d0..af69600b8 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1230,7 +1230,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, freqtrade.create_stoploss_order(trade, 200, 199) assert trade.stoploss_order_id is None assert trade.sell_reason == SellType.EMERGENCY_SELL.value - assert log_has("Unable to place a stoploss order on exchange.", caplog) + assert log_has("Unable to place a stoploss order on exchange. ", caplog) assert log_has("Selling the trade forcefully", caplog) # Should call a market sell From 20c9c93b3e197fe9fcfdce1e09599d163e1e4ba1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:25:05 +0200 Subject: [PATCH 079/227] Improve docstring --- freqtrade/freqtradebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7c403a29b..53f052f76 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -621,6 +621,7 @@ class FreqtradeBot(object): """ Abstracts creating stoploss orders from the logic. Handles errors and updates the trade database object. + Force-sells the pair (using EmergencySell reason) in case of Problems creating the order. :return: True if the order succeeded, and False in case of problems. """ # Limit price threshold: As limit price should always be below price From aae9c3194fe937c9785ef6bce2c22d8d05b7ee9c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 17:48:06 +0200 Subject: [PATCH 080/227] Reenable stoploss_on_exchange for dry-run --- freqtrade/exchange/exchange.py | 5 ++++- freqtrade/freqtradebot.py | 8 +------- freqtrade/tests/test_freqtradebot.py | 3 +-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e20546856..d48d18ebf 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -367,7 +367,7 @@ class Exchange(object): def dry_run_order(self, pair: str, ordertype: str, side: str, amount: float, rate: float, params: Dict = {}) -> Dict[str, Any]: order_id = f'dry_run_{side}_{randint(0, 10**6)}' - dry_order = { # TODO: additional entry should be added for stoploss limit + dry_order = { "id": order_id, 'pair': pair, 'price': rate, @@ -382,6 +382,7 @@ class Exchange(object): "info": {} } self._store_dry_order(dry_order) + # Copy order and close it - so the returned order is open unless it's a market order return dry_order def _store_dry_order(self, dry_order: Dict) -> None: @@ -392,6 +393,8 @@ class Exchange(object): "filled": closed_order["amount"], "remaining": 0 }) + if closed_order["type"] in ["stop_loss_limit"]: + closed_order["info"].update({"stopPrice": closed_order["price"]}) self._dry_run_open_orders[closed_order["id"]] = closed_order def create_order(self, pair: str, ordertype: str, side: str, amount: float, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8857b95da..440f64766 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -21,7 +21,7 @@ from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver -from freqtrade.state import State, RunMode +from freqtrade.state import State from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.wallets import Wallets @@ -79,12 +79,6 @@ class FreqtradeBot(object): persistence.init(self.config.get('db_url', None), clean_open_orders=self.config.get('dry_run', False)) - # Stoploss on exchange does not make sense, therefore we need to disable that. - if (self.dataprovider.runmode == RunMode.DRY_RUN and - self.strategy.order_types.get('stoploss_on_exchange', False)): - logger.info("Disabling stoploss_on_exchange during dry-run.") - self.strategy.order_types['stoploss_on_exchange'] = False - config['order_types']['stoploss_on_exchange'] = False # Set initial bot state from config initial_state = self.config.get('initial_state') self.state = State[initial_state.upper()] if initial_state else State.STOPPED diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1119157c4..871432a44 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -147,8 +147,7 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: } freqtrade = FreqtradeBot(conf) - assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog) - assert not freqtrade.strategy.order_types['stoploss_on_exchange'] + assert freqtrade.strategy.order_types['stoploss_on_exchange'] caplog.clear() # is left untouched From 949ab2a17c725fe6ae611cba68e37cd929d46a97 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:27:37 +0000 Subject: [PATCH 081/227] Bump mkdocs-material from 4.4.1 to 4.4.2 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 4.4.1 to 4.4.2. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/4.4.1...4.4.2) Signed-off-by: dependabot-preview[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 10ea068ad..28ebc5916 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1 +1 @@ -mkdocs-material==4.4.1 \ No newline at end of file +mkdocs-material==4.4.2 \ No newline at end of file From 89f5cf8291a57b72376319a67770f417d1df353a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:28:47 +0000 Subject: [PATCH 082/227] Bump numpy from 1.17.0 to 1.17.1 Bumps [numpy](https://github.com/numpy/numpy) from 1.17.0 to 1.17.1. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/master/doc/HOWTO_RELEASE.rst.txt) - [Commits](https://github.com/numpy/numpy/compare/v1.17.0...v1.17.1) Signed-off-by: dependabot-preview[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e5015d620..cf264ee81 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # Load common requirements -r requirements-common.txt -numpy==1.17.0 +numpy==1.17.1 pandas==0.25.1 scipy==1.3.1 From 51ad05efdbb5f331499eeb2663d5a1c7d20388c3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:29:07 +0000 Subject: [PATCH 083/227] Bump pytest from 5.1.1 to 5.1.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.1.1 to 5.1.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/5.1.1...5.1.2) Signed-off-by: dependabot-preview[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index a7b0d358d..1b9bf7570 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ flake8==3.7.8 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 mypy==0.720 -pytest==5.1.1 +pytest==5.1.2 pytest-asyncio==0.10.0 pytest-cov==2.7.1 pytest-mock==1.10.4 From bf4e3f55f463bb9327c3f16e32e54545e5ca88cb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 08:29:26 +0000 Subject: [PATCH 084/227] Bump sqlalchemy from 1.3.7 to 1.3.8 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.3.7 to 1.3.8. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index c8a9c2f74..3aacd3766 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,7 +1,7 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs ccxt==1.18.1085 -SQLAlchemy==1.3.7 +SQLAlchemy==1.3.8 python-telegram-bot==11.1.0 arrow==0.14.5 cachetools==3.1.1 From 3f6c0ba6d6369489f0d0f2eb24be453b85718faf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 11:56:28 +0000 Subject: [PATCH 085/227] Bump arrow from 0.14.5 to 0.14.6 Bumps [arrow](https://github.com/crsmithdev/arrow) from 0.14.5 to 0.14.6. - [Release notes](https://github.com/crsmithdev/arrow/releases) - [Changelog](https://github.com/crsmithdev/arrow/blob/master/CHANGELOG.md) - [Commits](https://github.com/crsmithdev/arrow/compare/0.14.5...0.14.6) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 3aacd3766..1cae33a5e 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -3,7 +3,7 @@ ccxt==1.18.1085 SQLAlchemy==1.3.8 python-telegram-bot==11.1.0 -arrow==0.14.5 +arrow==0.14.6 cachetools==3.1.1 requests==2.22.0 urllib3==1.25.3 From 04335ddd8970a3c6d21b0de7985923a1784bcf6c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 12:42:07 +0000 Subject: [PATCH 086/227] Bump ccxt from 1.18.1085 to 1.18.1115 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1085 to 1.18.1115. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/1.18.1085...1.18.1115) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 1cae33a5e..92e4f42ef 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.18.1085 +ccxt==1.18.1115 SQLAlchemy==1.3.8 python-telegram-bot==11.1.0 arrow==0.14.6 From 05789c4b925d7c4b6c674be57335ccfb1134b5d5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2019 13:44:52 +0000 Subject: [PATCH 087/227] Bump python-telegram-bot from 11.1.0 to 12.0.0 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 11.1.0 to 12.0.0. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v11.1.0...v12.0.0) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 92e4f42ef..7b6befaf6 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -2,7 +2,7 @@ # mainly used for Raspberry pi installs ccxt==1.18.1115 SQLAlchemy==1.3.8 -python-telegram-bot==11.1.0 +python-telegram-bot==12.0.0 arrow==0.14.6 cachetools==3.1.1 requests==2.22.0 From 9c60ab796df28f114405d7ba0cf8dda5307fe1a2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Sep 2019 20:14:41 +0200 Subject: [PATCH 088/227] Adapt telegram api to new interface of telegram-bot-12.0.0 --- freqtrade/rpc/telegram.py | 101 +++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index fe4929780..a5e8265da 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -31,7 +31,7 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]: """ def wrapper(self, *args, **kwargs): """ Decorator logic """ - update = kwargs.get('update') or args[1] + update = kwargs.get('update') or args[0] # Reject unauthorized messages chat_id = int(self._config['telegram']['chat_id']) @@ -79,7 +79,8 @@ class Telegram(RPC): registers all known command handlers and starts polling for message updates """ - self._updater = Updater(token=self._config['telegram']['token'], workers=0) + self._updater = Updater(token=self._config['telegram']['token'], workers=0, + use_context=True) # Register command handler and start telegram message polling handles = [ @@ -175,7 +176,7 @@ class Telegram(RPC): self._send_msg(message) @authorized_only - def _status(self, bot: Bot, update: Update) -> None: + def _status(self, update: Update, context: CallbackContext) -> None: """ Handler for /status. Returns the current TradeThread status @@ -221,13 +222,13 @@ class Telegram(RPC): messages.append("\n".join([l for l in lines if l]).format(**r)) for msg in messages: - self._send_msg(msg, bot=bot) + self._send_msg(msg) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _status_table(self, bot: Bot, update: Update) -> None: + def _status_table(self, update: Update, context: CallbackContext) -> None: """ Handler for /status table. Returns the current TradeThread status in table format @@ -240,10 +241,10 @@ class Telegram(RPC): message = tabulate(df_statuses, headers='keys', tablefmt='simple') self._send_msg(f"

{message}
", parse_mode=ParseMode.HTML) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _daily(self, bot: Bot, update: Update) -> None: + def _daily(self, update: Update, context: CallbackContext) -> None: """ Handler for /daily Returns a daily profit (in BTC) over the last n days. @@ -272,12 +273,12 @@ class Telegram(RPC): ], tablefmt='simple') message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' - self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML) + self._send_msg(message, parse_mode=ParseMode.HTML) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _profit(self, bot: Bot, update: Update) -> None: + def _profit(self, update: Update, context: CallbackContext) -> None: """ Handler for /profit. Returns a cumulative profit statistics. @@ -317,12 +318,12 @@ class Telegram(RPC): f"*Latest Trade opened:* `{latest_trade_date}`\n" \ f"*Avg. Duration:* `{avg_duration}`\n" \ f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`" - self._send_msg(markdown_msg, bot=bot) + self._send_msg(markdown_msg) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _balance(self, bot: Bot, update: Update) -> None: + def _balance(self, update: Update, context: CallbackContext) -> None: """ Handler for /balance """ try: result = self._rpc_balance(self._config.get('fiat_display_currency', '')) @@ -339,7 +340,7 @@ class Telegram(RPC): # Handle overflowing messsage length if len(output + curr_output) >= MAX_TELEGRAM_MESSAGE_LENGTH: - self._send_msg(output, bot=bot) + self._send_msg(output) output = curr_output else: output += curr_output @@ -347,12 +348,12 @@ class Telegram(RPC): output += "\n*Estimated Value*:\n" \ "\t`BTC: {total: .8f}`\n" \ "\t`{symbol}: {value: .2f}`\n".format(**result) - self._send_msg(output, bot=bot) + self._send_msg(output) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _start(self, bot: Bot, update: Update) -> None: + def _start(self, update: Update, context: CallbackContext) -> None: """ Handler for /start. Starts TradeThread @@ -361,10 +362,10 @@ class Telegram(RPC): :return: None """ msg = self._rpc_start() - self._send_msg('Status: `{status}`'.format(**msg), bot=bot) + self._send_msg('Status: `{status}`'.format(**msg)) @authorized_only - def _stop(self, bot: Bot, update: Update) -> None: + def _stop(self, update: Update, context: CallbackContext) -> None: """ Handler for /stop. Stops TradeThread @@ -373,10 +374,10 @@ class Telegram(RPC): :return: None """ msg = self._rpc_stop() - self._send_msg('Status: `{status}`'.format(**msg), bot=bot) + self._send_msg('Status: `{status}`'.format(**msg)) @authorized_only - def _reload_conf(self, bot: Bot, update: Update) -> None: + def _reload_conf(self, update: Update, context: CallbackContext) -> None: """ Handler for /reload_conf. Triggers a config file reload @@ -385,10 +386,10 @@ class Telegram(RPC): :return: None """ msg = self._rpc_reload_conf() - self._send_msg('Status: `{status}`'.format(**msg), bot=bot) + self._send_msg('Status: `{status}`'.format(**msg)) @authorized_only - def _stopbuy(self, bot: Bot, update: Update) -> None: + def _stopbuy(self, update: Update, context: CallbackContext) -> None: """ Handler for /stop_buy. Sets max_open_trades to 0 and gracefully sells all open trades @@ -397,10 +398,10 @@ class Telegram(RPC): :return: None """ msg = self._rpc_stopbuy() - self._send_msg('Status: `{status}`'.format(**msg), bot=bot) + self._send_msg('Status: `{status}`'.format(**msg)) @authorized_only - def _forcesell(self, bot: Bot, update: Update) -> None: + def _forcesell(self, update: Update, context: CallbackContext) -> None: """ Handler for /forcesell . Sells the given trade at current price @@ -412,13 +413,13 @@ class Telegram(RPC): trade_id = update.message.text.replace('/forcesell', '').strip() try: msg = self._rpc_forcesell(trade_id) - self._send_msg('Forcesell Result: `{result}`'.format(**msg), bot=bot) + self._send_msg('Forcesell Result: `{result}`'.format(**msg)) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _forcebuy(self, bot: Bot, update: Update) -> None: + def _forcebuy(self, update: Update, context: CallbackContext) -> None: """ Handler for /forcebuy . Buys a pair trade at the given or current price @@ -433,10 +434,10 @@ class Telegram(RPC): try: self._rpc_forcebuy(pair, price) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _performance(self, bot: Bot, update: Update) -> None: + def _performance(self, update: Update, context: CallbackContext) -> None: """ Handler for /performance. Shows a performance statistic from finished trades @@ -455,10 +456,10 @@ class Telegram(RPC): message = 'Performance:\n{}'.format(stats) self._send_msg(message, parse_mode=ParseMode.HTML) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _count(self, bot: Bot, update: Update) -> None: + def _count(self, update: Update, context: CallbackContext) -> None: """ Handler for /count. Returns the number of trades running @@ -475,10 +476,10 @@ class Telegram(RPC): logger.debug(message) self._send_msg(message, parse_mode=ParseMode.HTML) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _whitelist(self, bot: Bot, update: Update) -> None: + def _whitelist(self, update: Update, context: CallbackContext) -> None: """ Handler for /whitelist Shows the currently active whitelist @@ -492,17 +493,17 @@ class Telegram(RPC): logger.debug(message) self._send_msg(message) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _blacklist(self, bot: Bot, update: Update, args: List[str]) -> None: + def _blacklist(self, update: Update, context: CallbackContext) -> None: """ Handler for /blacklist Shows the currently active blacklist """ try: - blacklist = self._rpc_blacklist(args) + blacklist = self._rpc_blacklist(context.args) message = f"Blacklist contains {blacklist['length']} pairs\n" message += f"`{', '.join(blacklist['blacklist'])}`" @@ -510,10 +511,10 @@ class Telegram(RPC): logger.debug(message) self._send_msg(message) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _edge(self, bot: Bot, update: Update) -> None: + def _edge(self, update: Update, context: CallbackContext) -> None: """ Handler for /edge Shows information related to Edge @@ -522,12 +523,12 @@ class Telegram(RPC): edge_pairs = self._rpc_edge() edge_pairs_tab = tabulate(edge_pairs, headers='keys', tablefmt='simple') message = f'Edge only validated following pairs:\n
{edge_pairs_tab}
' - self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML) + self._send_msg(message, parse_mode=ParseMode.HTML) except RPCException as e: - self._send_msg(str(e), bot=bot) + self._send_msg(str(e)) @authorized_only - def _help(self, bot: Bot, update: Update) -> None: + def _help(self, update: Update, context: CallbackContext) -> None: """ Handler for /help. Show commands of the bot @@ -559,10 +560,10 @@ class Telegram(RPC): "*/help:* `This help message`\n" \ "*/version:* `Show version`" - self._send_msg(message, bot=bot) + self._send_msg(message) @authorized_only - def _version(self, bot: Bot, update: Update) -> None: + def _version(self, update: Update, context: CallbackContext) -> None: """ Handler for /version. Show version information @@ -570,10 +571,9 @@ class Telegram(RPC): :param update: message update :return: None """ - self._send_msg('*Version:* `{}`'.format(__version__), bot=bot) + self._send_msg('*Version:* `{}`'.format(__version__)) - def _send_msg(self, msg: str, bot: Bot = None, - parse_mode: ParseMode = ParseMode.MARKDOWN) -> None: + def _send_msg(self, msg: str, parse_mode: ParseMode = ParseMode.MARKDOWN) -> None: """ Send given markdown message :param msg: message @@ -581,7 +581,6 @@ class Telegram(RPC): :param parse_mode: telegram parse mode :return: None """ - bot = bot or self._updater.bot keyboard = [['/daily', '/profit', '/balance'], ['/status', '/status table', '/performance'], @@ -591,7 +590,7 @@ class Telegram(RPC): try: try: - bot.send_message( + self._updater.bot.send_message( self._config['telegram']['chat_id'], text=msg, parse_mode=parse_mode, @@ -604,7 +603,7 @@ class Telegram(RPC): 'Telegram NetworkError: %s! Trying one more time.', network_err.message ) - bot.send_message( + self._updater.bot.send_message( self._config['telegram']['chat_id'], text=msg, parse_mode=parse_mode, From 8cad90f9e6b884a04b9813814e730e864c8ba0da Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Sep 2019 20:17:23 +0200 Subject: [PATCH 089/227] Adapt to new api --- freqtrade/rpc/telegram.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index a5e8265da..4b64ee31e 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -4,12 +4,12 @@ This module manage Telegram communication """ import logging -from typing import Any, Callable, Dict, List +from typing import Any, Callable, Dict from tabulate import tabulate -from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update +from telegram import ParseMode, ReplyKeyboardMarkup, Update from telegram.error import NetworkError, TelegramError -from telegram.ext import CommandHandler, Updater +from telegram.ext import CommandHandler, Updater, CallbackContext from freqtrade.__init__ import __version__ from freqtrade.rpc import RPC, RPCException, RPCMessageType @@ -97,7 +97,7 @@ class Telegram(RPC): CommandHandler('reload_conf', self._reload_conf), CommandHandler('stopbuy', self._stopbuy), CommandHandler('whitelist', self._whitelist), - CommandHandler('blacklist', self._blacklist, pass_args=True), + CommandHandler('blacklist', self._blacklist), CommandHandler('edge', self._edge), CommandHandler('help', self._help), CommandHandler('version', self._version), @@ -185,11 +185,8 @@ class Telegram(RPC): :return: None """ - # Check if additional parameters are passed - params = update.message.text.replace('/status', '').split(' ') \ - if update.message.text else [] - if 'table' in params: - self._status_table(bot, update) + if 'table' in context.args: + self._status_table(update, context) return try: @@ -255,8 +252,8 @@ class Telegram(RPC): stake_cur = self._config['stake_currency'] fiat_disp_cur = self._config.get('fiat_display_currency', '') try: - timescale = int(update.message.text.replace('/daily', '').strip()) - except (TypeError, ValueError): + timescale = int(context.args[0]) + except (TypeError, ValueError, IndexError): timescale = 7 try: stats = self._rpc_daily_profit( From 3b15cce07a33d2bf2d27649a929e9f62ab310aec Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Sep 2019 20:17:47 +0200 Subject: [PATCH 090/227] Handle arguments uniformly (by using context.args) --- freqtrade/rpc/telegram.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 4b64ee31e..d11fc02ab 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -407,7 +407,7 @@ class Telegram(RPC): :return: None """ - trade_id = update.message.text.replace('/forcesell', '').strip() + trade_id = context.args[0] if len(context.args) > 0 else None try: msg = self._rpc_forcesell(trade_id) self._send_msg('Forcesell Result: `{result}`'.format(**msg)) @@ -425,9 +425,8 @@ class Telegram(RPC): :return: None """ - message = update.message.text.replace('/forcebuy', '').strip().split() - pair = message[0] - price = float(message[1]) if len(message) > 1 else None + pair = context.args[0] + price = float(context.args[1]) if len(context.args) > 1 else None try: self._rpc_forcebuy(pair, price) except RPCException as e: From dc9fda76f351bf145b08445c964a8b4884e1663a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Sep 2019 20:42:39 +0200 Subject: [PATCH 091/227] Fix tests to adapt to new telegram-bot interface --- freqtrade/tests/rpc/test_rpc_telegram.py | 161 ++++++++++++++--------- 1 file changed, 96 insertions(+), 65 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 2f469643f..3820332b8 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -100,7 +100,7 @@ def test_authorized_only(default_conf, mocker, caplog) -> None: bot = FreqtradeBot(default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) - dummy.dummy_handler(bot=MagicMock(), update=update) + dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) assert not log_has('Rejected unauthorized message from: 0', caplog) @@ -117,7 +117,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: bot = FreqtradeBot(default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) - dummy.dummy_handler(bot=MagicMock(), update=update) + dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) assert log_has('Rejected unauthorized message from: 3735928559', caplog) @@ -136,7 +136,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) - dummy.dummy_exception(bot=MagicMock(), update=update) + dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 0', caplog) assert not log_has('Rejected unauthorized message from: 0', caplog) @@ -194,12 +194,13 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: for _ in range(3): freqtradebot.create_trades() - telegram._status(bot=MagicMock(), update=update) + telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 - update.message.text = MagicMock() - update.message.text.replace = MagicMock(return_value='table 2 3') - telegram._status(bot=MagicMock(), update=update) + context = MagicMock() + # /status table 2 3 + context.args = ["table", "2", "3"] + telegram._status(update=update, context=context) assert status_table.call_count == 1 @@ -228,13 +229,13 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No freqtradebot.state = State.STOPPED # Status is also enabled when stopped - telegram._status(bot=MagicMock(), update=update) + telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() freqtradebot.state = State.RUNNING - telegram._status(bot=MagicMock(), update=update) + telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() @@ -242,7 +243,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No # Create some test data freqtradebot.create_trades() # Trigger status while we have a fulfilled order for the open trade - telegram._status(bot=MagicMock(), update=update) + telegram._status(update=update, context=MagicMock()) # close_rate should not be included in the message as the trade is not closed # and no line should be empty @@ -280,13 +281,13 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped - telegram._status_table(bot=MagicMock(), update=update) + telegram._status_table(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() freqtradebot.state = State.RUNNING - telegram._status_table(bot=MagicMock(), update=update) + telegram._status_table(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() @@ -294,7 +295,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) # Create some test data freqtradebot.create_trades() - telegram._status_table(bot=MagicMock(), update=update) + telegram._status_table(update=update, context=MagicMock()) text = re.sub('', '', msg_mock.call_args_list[-1][0][0]) line = text.split("\n") @@ -346,8 +347,10 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, trade.is_open = False # Try valid data - update.message.text = '/daily 2' - telegram._daily(bot=MagicMock(), update=update) + # /daily 2 + context = MagicMock() + context.args = ["2"] + telegram._daily(update=update, context=context) assert msg_mock.call_count == 1 assert 'Daily' in msg_mock.call_args_list[0][0][0] assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] @@ -369,9 +372,10 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, trade.close_date = datetime.utcnow() trade.is_open = False - update.message.text = '/daily 1' - - telegram._daily(bot=MagicMock(), update=update) + # /daily 1 + context = MagicMock() + context.args = ["1"] + telegram._daily(update=update, context=context) assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] @@ -398,16 +402,20 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: # Try invalid data msg_mock.reset_mock() freqtradebot.state = State.RUNNING - update.message.text = '/daily -2' - telegram._daily(bot=MagicMock(), update=update) + # /daily -2 + context = MagicMock() + context.args = ["-2"] + telegram._daily(update=update, context=context) assert msg_mock.call_count == 1 assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0] # Try invalid data msg_mock.reset_mock() freqtradebot.state = State.RUNNING - update.message.text = '/daily today' - telegram._daily(bot=MagicMock(), update=update) + # /daily today + context = MagicMock() + context.args = ["today"] + telegram._daily(update=update, context=context) assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0] @@ -433,7 +441,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - telegram._profit(bot=MagicMock(), update=update) + telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no closed trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() @@ -445,7 +453,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) - telegram._profit(bot=MagicMock(), update=update) + telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'no closed trade' in msg_mock.call_args_list[-1][0][0] msg_mock.reset_mock() @@ -457,7 +465,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, trade.close_date = datetime.utcnow() trade.is_open = False - telegram._profit(bot=MagicMock(), update=update) + telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert '*ROI:* Close trades' in msg_mock.call_args_list[-1][0][0] assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0] @@ -507,7 +515,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance) -> N telegram = Telegram(freqtradebot) - telegram._balance(bot=MagicMock(), update=update) + telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert '*BTC:*' in result @@ -536,7 +544,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) freqtradebot.config['dry_run'] = False - telegram._balance(bot=MagicMock(), update=update) + telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert 'All balances are zero.' in result @@ -557,7 +565,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None telegram = Telegram(freqtradebot) - telegram._balance(bot=MagicMock(), update=update) + telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert "Running in Dry Run, balances are not available." in result @@ -593,7 +601,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None telegram = Telegram(freqtradebot) - telegram._balance(bot=MagicMock(), update=update) + telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 # Test if wrap happens around 4000 - # and each single currency-output is around 120 characters long so we need @@ -615,7 +623,7 @@ def test_start_handle(default_conf, update, mocker) -> None: freqtradebot.state = State.STOPPED assert freqtradebot.state == State.STOPPED - telegram._start(bot=MagicMock(), update=update) + telegram._start(update=update, context=MagicMock()) assert freqtradebot.state == State.RUNNING assert msg_mock.call_count == 1 @@ -633,7 +641,7 @@ def test_start_handle_already_running(default_conf, update, mocker) -> None: freqtradebot.state = State.RUNNING assert freqtradebot.state == State.RUNNING - telegram._start(bot=MagicMock(), update=update) + telegram._start(update=update, context=MagicMock()) assert freqtradebot.state == State.RUNNING assert msg_mock.call_count == 1 assert 'already running' in msg_mock.call_args_list[0][0][0] @@ -652,7 +660,7 @@ def test_stop_handle(default_conf, update, mocker) -> None: freqtradebot.state = State.RUNNING assert freqtradebot.state == State.RUNNING - telegram._stop(bot=MagicMock(), update=update) + telegram._stop(update=update, context=MagicMock()) assert freqtradebot.state == State.STOPPED assert msg_mock.call_count == 1 assert 'stopping trader' in msg_mock.call_args_list[0][0][0] @@ -671,7 +679,7 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None: freqtradebot.state = State.STOPPED assert freqtradebot.state == State.STOPPED - telegram._stop(bot=MagicMock(), update=update) + telegram._stop(update=update, context=MagicMock()) assert freqtradebot.state == State.STOPPED assert msg_mock.call_count == 1 assert 'already stopped' in msg_mock.call_args_list[0][0][0] @@ -689,7 +697,7 @@ def test_stopbuy_handle(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) assert freqtradebot.config['max_open_trades'] != 0 - telegram._stopbuy(bot=MagicMock(), update=update) + telegram._stopbuy(update=update, context=MagicMock()) assert freqtradebot.config['max_open_trades'] == 0 assert msg_mock.call_count == 1 assert 'No more buy will occur from now. Run /reload_conf to reset.' \ @@ -709,7 +717,7 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None: freqtradebot.state = State.RUNNING assert freqtradebot.state == State.RUNNING - telegram._reload_conf(bot=MagicMock(), update=update) + telegram._reload_conf(update=update, context=MagicMock()) assert freqtradebot.state == State.RELOAD_CONF assert msg_mock.call_count == 1 assert 'reloading config' in msg_mock.call_args_list[0][0][0] @@ -742,8 +750,10 @@ def test_forcesell_handle(default_conf, update, ticker, fee, # Increase the price and sell it mocker.patch('freqtrade.exchange.Exchange.get_ticker', ticker_sell_up) - update.message.text = '/forcesell 1' - telegram._forcesell(bot=MagicMock(), update=update) + # /forcesell 1 + context = MagicMock() + context.args = ["1"] + telegram._forcesell(update=update, context=context) assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] @@ -796,8 +806,10 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, trade = Trade.query.first() assert trade - update.message.text = '/forcesell 1' - telegram._forcesell(bot=MagicMock(), update=update) + # /forcesell 1 + context = MagicMock() + context.args = ["1"] + telegram._forcesell(update=update, context=context) assert rpc_mock.call_count == 2 @@ -842,8 +854,10 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker freqtradebot.create_trades() rpc_mock.reset_mock() - update.message.text = '/forcesell all' - telegram._forcesell(bot=MagicMock(), update=update) + # /forcesell all + context = MagicMock() + context.args = ["all"] + telegram._forcesell(update=update, context=context) assert rpc_mock.call_count == 4 msg = rpc_mock.call_args_list[0][0][0] @@ -882,24 +896,29 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: # Trader is not running freqtradebot.state = State.STOPPED - update.message.text = '/forcesell 1' - telegram._forcesell(bot=MagicMock(), update=update) + # /forcesell 1 + context = MagicMock() + context.args = ["1"] + telegram._forcesell(update=update, context=context) assert msg_mock.call_count == 1 assert 'not running' in msg_mock.call_args_list[0][0][0] # No argument msg_mock.reset_mock() freqtradebot.state = State.RUNNING - update.message.text = '/forcesell' - telegram._forcesell(bot=MagicMock(), update=update) + context = MagicMock() + context.args = [] + telegram._forcesell(update=update, context=context) assert msg_mock.call_count == 1 assert 'invalid argument' in msg_mock.call_args_list[0][0][0] # Invalid argument msg_mock.reset_mock() freqtradebot.state = State.RUNNING - update.message.text = '/forcesell 123456' - telegram._forcesell(bot=MagicMock(), update=update) + # /forcesell 123456 + context = MagicMock() + context.args = ["123456"] + telegram._forcesell(update=update, context=context) assert msg_mock.call_count == 1 assert 'invalid argument' in msg_mock.call_args_list[0][0][0] @@ -921,8 +940,10 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - update.message.text = '/forcebuy ETH/BTC' - telegram._forcebuy(bot=MagicMock(), update=update) + # /forcebuy ETH/BTC + context = MagicMock() + context.args = ["ETH/BTC"] + telegram._forcebuy(update=update, context=context) assert fbuy_mock.call_count == 1 assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' @@ -931,8 +952,10 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: # Reset and retry with specified price fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) - update.message.text = '/forcebuy ETH/BTC 0.055' - telegram._forcebuy(bot=MagicMock(), update=update) + # /forcebuy ETH/BTC 0.055 + context = MagicMock() + context.args = ["ETH/BTC", "0.055"] + telegram._forcebuy(update=update, context=context) assert fbuy_mock.call_count == 1 assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' @@ -955,7 +978,7 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non telegram = Telegram(freqtradebot) update.message.text = '/forcebuy ETH/Nonepair' - telegram._forcebuy(bot=MagicMock(), update=update) + telegram._forcebuy(update=update, context=MagicMock()) assert rpc_mock.call_count == 1 assert rpc_mock.call_args_list[0][0][0] == 'Forcebuy not enabled.' @@ -995,7 +1018,7 @@ def test_performance_handle(default_conf, update, ticker, fee, trade.close_date = datetime.utcnow() trade.is_open = False - telegram._performance(bot=MagicMock(), update=update) + telegram._performance(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'Performance' in msg_mock.call_args_list[0][0][0] assert 'ETH/BTC\t6.20% (1)' in msg_mock.call_args_list[0][0][0] @@ -1021,7 +1044,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non telegram = Telegram(freqtradebot) freqtradebot.state = State.STOPPED - telegram._count(bot=MagicMock(), update=update) + telegram._count(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'not running' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() @@ -1030,7 +1053,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non # Create some test data freqtradebot.create_trades() msg_mock.reset_mock() - telegram._count(bot=MagicMock(), update=update) + telegram._count(update=update, context=MagicMock()) msg = '
  current    max    total stake\n---------  -----  -------------\n' \
           '        1      {}          {}
'\ @@ -1052,7 +1075,7 @@ def test_whitelist_static(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) - telegram._whitelist(bot=MagicMock(), update=update) + telegram._whitelist(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert ('Using whitelist `StaticPairList` with 4 pairs\n`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`' in msg_mock.call_args_list[0][0][0]) @@ -1073,7 +1096,7 @@ def test_whitelist_dynamic(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) - telegram._whitelist(bot=MagicMock(), update=update) + telegram._whitelist(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert ('Using whitelist `VolumePairList` with 4 pairs\n`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`' in msg_mock.call_args_list[0][0][0]) @@ -1090,13 +1113,17 @@ def test_blacklist_static(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) - telegram._blacklist(bot=MagicMock(), update=update, args=[]) + telegram._blacklist(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert ("Blacklist contains 2 pairs\n`DOGE/BTC, HOT/BTC`" in msg_mock.call_args_list[0][0][0]) msg_mock.reset_mock() - telegram._blacklist(bot=MagicMock(), update=update, args=["ETH/BTC"]) + + # /blacklist ETH/BTC + context = MagicMock() + context.args = ["ETH/BTC"] + telegram._blacklist(update=update, context=context) assert msg_mock.call_count == 1 assert ("Blacklist contains 3 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC`" in msg_mock.call_args_list[0][0][0]) @@ -1115,7 +1142,7 @@ def test_edge_disabled(default_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) - telegram._edge(bot=MagicMock(), update=update) + telegram._edge(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert "Edge is not enabled." in msg_mock.call_args_list[0][0][0] @@ -1137,7 +1164,7 @@ def test_edge_enabled(edge_conf, update, mocker) -> None: telegram = Telegram(freqtradebot) - telegram._edge(bot=MagicMock(), update=update) + telegram._edge(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'Edge only validated following pairs:\n
' in msg_mock.call_args_list[0][0][0]
     assert 'Pair      Winrate    Expectancy    Stoploss' in msg_mock.call_args_list[0][0][0]
@@ -1154,7 +1181,7 @@ def test_help_handle(default_conf, update, mocker) -> None:
 
     telegram = Telegram(freqtradebot)
 
-    telegram._help(bot=MagicMock(), update=update)
+    telegram._help(update=update, context=MagicMock())
     assert msg_mock.call_count == 1
     assert '*/help:* `This help message`' in msg_mock.call_args_list[0][0][0]
 
@@ -1169,7 +1196,7 @@ def test_version_handle(default_conf, update, mocker) -> None:
     freqtradebot = get_patched_freqtradebot(mocker, default_conf)
     telegram = Telegram(freqtradebot)
 
-    telegram._version(bot=MagicMock(), update=update)
+    telegram._version(update=update, context=MagicMock())
     assert msg_mock.call_count == 1
     assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0]
 
@@ -1395,9 +1422,11 @@ def test__send_msg(default_conf, mocker) -> None:
     bot = MagicMock()
     freqtradebot = get_patched_freqtradebot(mocker, default_conf)
     telegram = Telegram(freqtradebot)
+    telegram._updater = MagicMock()
+    telegram._updater.bot = bot
 
     telegram._config['telegram']['enabled'] = True
-    telegram._send_msg('test', bot)
+    telegram._send_msg('test')
     assert len(bot.method_calls) == 1
 
 
@@ -1407,9 +1436,11 @@ def test__send_msg_network_error(default_conf, mocker, caplog) -> None:
     bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
     freqtradebot = get_patched_freqtradebot(mocker, default_conf)
     telegram = Telegram(freqtradebot)
+    telegram._updater = MagicMock()
+    telegram._updater.bot = bot
 
     telegram._config['telegram']['enabled'] = True
-    telegram._send_msg('test', bot)
+    telegram._send_msg('test')
 
     # Bot should've tried to send it twice
     assert len(bot.method_calls) == 2

From 87ae2430df83e8497cfe0b6f7bd81b337208c053 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 3 Sep 2019 11:32:18 +0300
Subject: [PATCH 092/227] ranges for ROI tables for different ticker_intervals
 in docs

---
 docs/hyperopt.md | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index c1bf56a3d..fa3f3584d 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -389,16 +389,20 @@ minimal_roi = {
     }
 ```
 
-If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps) with the values that can vary in the following ranges:
+If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges:
 
-| # | minutes | ROI percentage |
-|---|---|---|
-| 1 | always 0 | 0.03...0.31 |
-| 2 | 10...40 | 0.02...0.11 |
-| 3 | 20...100 | 0.01...0.04 |
-| 4 | 30...220 | always 0 |
+5m ticker_interval:
 
-This structure of the ROI table is sufficient in most cases. Override the `roi_space()` method defining the ranges desired if you need components of the ROI tables to vary in other ranges.
+| # | minutes / ROI percentage |
+||---|---|---|---|
+|| 1m | 5m | 1h | 1d |
+|---|---|---|---|---|
+| 1 | always 0 / 0.01161...0.11992 | always 0 / 0.03...0.31 | always 0 / 0.06883...0.71124 | always 0 / 0.12178...1.25835 |
+| 2 | 2...8 / 0.00774...0.04255 | 10...40 / 0.02...0.11 | 120...480 / 0.04589...0.25238 | 2880...11520 / 0.08118...0.44651 |
+| 3 | 4...20 / 0.00387...0.01547 | 20...100 / 0.01...0.04 | 240...1200 / 0.02294...0.09177 | 5760...28800 / 0.04059...0.16237 |
+| 4 | 6...44 / always 0.0 | 30...220 / always 0.0 | 360...2640 / always 0.0 | 8640...63360 / always 0.0 |
+
+These ranges should be sufficient in most cases. Override the `roi_space()` method defining the ranges desired if you need components of the ROI tables to vary in other ranges.
 
 Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization in these methods if you need a different structure of the ROI table or other amount of rows (steps) in the ROI tables.
 

From e8614abc5d0fc7278ac0229a8802e9da1c321804 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Tue, 3 Sep 2019 16:52:55 +0300
Subject: [PATCH 093/227] update table md formatting, enhance description

---
 docs/hyperopt.md | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index fa3f3584d..c7544df5b 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -389,22 +389,20 @@ minimal_roi = {
     }
 ```
 
-If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges:
+If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges (for some of the most used ticker intervals, values are rounded to 5 digits after the decimal point):
 
-5m ticker_interval:
+| # step  1m  5m  1h  1d |
+|---|---|---|---|---|---|---|---|---|
+| 1 | 0 | 0.01161...0.11992 | 0 | 0.03...0.31 | 0 | 0.06883...0.71124 | 0 | 0.12178...1.25835 |
+| 2 | 2...8 | 0.00774...0.04255 | 10...40 | 0.02...0.11 | 120...480 | 0.04589...0.25238 | 2880...11520 | 0.08118...0.44651 |
+| 3 | 4...20 | 0.00387...0.01547 | 20...100 | 0.01...0.04 | 240...1200 | 0.02294...0.09177 | 5760...28800 | 0.04059...0.16237 |
+| 4 | 6...44 | 0.0 | 30...220 | 0.0 | 360...2640 | 0.0 | 8640...63360 | 0.0 |
 
-| # | minutes / ROI percentage |
-||---|---|---|---|
-|| 1m | 5m | 1h | 1d |
-|---|---|---|---|---|
-| 1 | always 0 / 0.01161...0.11992 | always 0 / 0.03...0.31 | always 0 / 0.06883...0.71124 | always 0 / 0.12178...1.25835 |
-| 2 | 2...8 / 0.00774...0.04255 | 10...40 / 0.02...0.11 | 120...480 / 0.04589...0.25238 | 2880...11520 / 0.08118...0.44651 |
-| 3 | 4...20 / 0.00387...0.01547 | 20...100 / 0.01...0.04 | 240...1200 / 0.02294...0.09177 | 5760...28800 / 0.04059...0.16237 |
-| 4 | 6...44 / always 0.0 | 30...220 / always 0.0 | 360...2640 / always 0.0 | 8640...63360 / always 0.0 |
+These ranges should be sufficient in most cases. The minutes in the steps (ROI dict keys) are scaled linearly depending on the ticker interval used. The ROI values in the steps (ROI dict values) are scaled logarithmically depending on the ticker interval used.
 
-These ranges should be sufficient in most cases. Override the `roi_space()` method defining the ranges desired if you need components of the ROI tables to vary in other ranges.
+If you use legacy freqtrade HyperOpts class samples where the `generate_roi_table()` and `roi_space()` methods were copied in each custom hyperopt file, simply remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by freqtrade by default.
 
-Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization in these methods if you need a different structure of the ROI table or other amount of rows (steps) in the ROI tables.
+Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps).
 
 ### Understand Hyperopt Stoploss results
 
@@ -426,6 +424,8 @@ Stoploss: -0.37996664668703606
 
 If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.5...-0.02, which is sufficient in most cases.
 
+If you use legacy freqtrade HyperOpts class samples where `stoploss_space()` method was copied in each custom hyperopt file, simply remove it in order to utilize Stoploss hyperoptimization space generated by freqtrade by default.
+
 Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization.
 
 ### Validate backtesting results

From 9a6a89c238629cdb8e8695dc5efff2f305076e49 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 3 Sep 2019 19:54:28 +0300
Subject: [PATCH 094/227] allow simplified hyperopt interface

---
 freqtrade/optimize/hyperopt_interface.py | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py
index f1f123653..2642e9de8 100644
--- a/freqtrade/optimize/hyperopt_interface.py
+++ b/freqtrade/optimize/hyperopt_interface.py
@@ -9,6 +9,15 @@ from typing import Dict, Any, Callable, List
 from pandas import DataFrame
 from skopt.space import Dimension, Integer, Real
 
+from freqtrade import OperationalException
+
+
+def _format_exception_message(method: str, space: str) -> str:
+    return (f"The '{space}' space is included into the hyperoptimization "
+            f"but {method}() method is not found in your "
+            f"custom Hyperopt class. You should either implement this "
+            f"method or remove the '{space}' space from hyperoptimization.")
+
 
 class IHyperOpt(ABC):
     """
@@ -32,32 +41,32 @@ class IHyperOpt(ABC):
         """
 
     @staticmethod
-    @abstractmethod
     def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
         """
         Create a buy strategy generator.
         """
+        raise OperationalException(_format_exception_message('buy_strategy_generator', 'buy'))
 
     @staticmethod
-    @abstractmethod
     def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
         """
         Create a sell strategy generator.
         """
+        raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell'))
 
     @staticmethod
-    @abstractmethod
     def indicator_space() -> List[Dimension]:
         """
         Create an indicator space.
         """
+        raise OperationalException(_format_exception_message('indicator_space', 'buy'))
 
     @staticmethod
-    @abstractmethod
     def sell_indicator_space() -> List[Dimension]:
         """
         Create a sell indicator space.
         """
+        raise OperationalException(_format_exception_message('sell_indicator_space', 'sell'))
 
     @staticmethod
     def generate_roi_table(params: Dict) -> Dict[int, float]:

From 88f823f899dd3d4f5ed1ff05c3f647d3390af004 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 4 Sep 2019 06:56:25 +0200
Subject: [PATCH 095/227] Improvements to documentation

---
 docs/configuration.md          | 13 ++++++-------
 docs/strategy-customization.md |  2 +-
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 1f64da306..a1c0d1a75 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -192,14 +192,13 @@ end up paying more then would probably have been necessary.
 
 ### Understand order_types
 
-The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and stoploss on exchange update interval in seconds.
+The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines stoploss on exchange update interval in seconds.
 
 This allows to buy using limit orders, sell using
-limit-orders, and create stoploss orders using market. It also allows to set the
+limit-orders, and create stoplosses using using market orders. It also allows to set the
 stoploss "on exchange" which means stoploss order would be placed immediately once
-the buy order is fulfilled. In case stoploss on exchange and `trailing_stop` are
-both set, then the bot will use `stoploss_on_exchange_interval` to check the stoploss periodically
-and update it if necessary (e.g. in case of trailing stoploss).
+the buy order is fulfilled.
+If `stoploss_on_exchange` and `trailing_stop` are both set, then the bot will use `stoploss_on_exchange_interval` to check and update the stoploss on exchange periodically.
 `order_types` can be set in the configuration file or in the strategy.
 `order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place.
 
@@ -246,10 +245,10 @@ Configuration:
     refer to [the stoploss documentation](stoploss.md).
 
 !!! Note
-    If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot wil create a new order.
+    If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new order.
 
 !!! Warning stoploss_on_exchange failures
-    If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting `emergencysell` - however this is not advised.
+    If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however this is not advised.
 
 ### Understand order_time_in_force
 
diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md
index e07585800..85cab987a 100644
--- a/docs/strategy-customization.md
+++ b/docs/strategy-customization.md
@@ -224,7 +224,7 @@ This would signify a stoploss of -10%.
 
 For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md).
 
-If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order_types dictionary, so your stoploss is on the exchange and cannot be missed due to network-problems (or other problems).
+If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order_types dictionary, so your stoploss is on the exchange and cannot be missed due to network problems, high load or other reasons.
 
 For more information on order_types please look [here](configuration.md#understand-order_types).
 

From 03f3d0dc8bbff3cbe51da4438badeeed03a44850 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 4 Sep 2019 16:38:33 +0200
Subject: [PATCH 096/227] Remove desc from Arguments header

---
 freqtrade/configuration/arguments.py  |  5 ++-
 freqtrade/main.py                     |  5 +--
 freqtrade/tests/conftest.py           |  2 +-
 freqtrade/tests/test_arguments.py     | 47 +++++++++++++------------
 freqtrade/tests/test_configuration.py | 50 +++++++++++++--------------
 freqtrade/tests/test_main.py          |  4 +--
 6 files changed, 55 insertions(+), 58 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index 2c814b342..a8cd6686b 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -47,11 +47,10 @@ class Arguments(object):
     """
     Arguments Class. Manage the arguments received by the cli
     """
-    def __init__(self, args: Optional[List[str]], description: str,
-                 no_default_config: bool = False) -> None:
+    def __init__(self, args: Optional[List[str]], no_default_config: bool = False) -> None:
         self.args = args
         self._parsed_arg: Optional[argparse.Namespace] = None
-        self.parser = argparse.ArgumentParser(description=description)
+        self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot')
         self._no_default_config = no_default_config
 
     def _load_args(self) -> None:
diff --git a/freqtrade/main.py b/freqtrade/main.py
index a96fd43c5..e65aa5fbb 100755
--- a/freqtrade/main.py
+++ b/freqtrade/main.py
@@ -31,10 +31,7 @@ def main(sysargv: List[str] = None) -> None:
     return_code: Any = 1
     worker = None
     try:
-        arguments = Arguments(
-            sysargv,
-            'Free, open source crypto trading bot'
-        )
+        arguments = Arguments(sysargv)
         args: Namespace = arguments.get_parsed_arg()
 
         # A subcommand has been issued.
diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py
index 654b959ca..79b03b057 100644
--- a/freqtrade/tests/conftest.py
+++ b/freqtrade/tests/conftest.py
@@ -45,7 +45,7 @@ def log_has_re(line, logs):
 
 
 def get_args(args):
-    return Arguments(args, '').get_parsed_arg()
+    return Arguments(args).get_parsed_arg()
 
 
 def patched_configuration_load_config_file(mocker, config) -> None:
diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py
index 558642894..174038eff 100644
--- a/freqtrade/tests/test_arguments.py
+++ b/freqtrade/tests/test_arguments.py
@@ -9,13 +9,15 @@ from freqtrade.configuration.cli_options import check_int_positive
 
 # Parse common command-line-arguments. Used for all tools
 def test_parse_args_none() -> None:
-    arguments = Arguments([], '')
+    arguments = Arguments([])
     assert isinstance(arguments, Arguments)
+    x = arguments.get_parsed_arg()
+    assert isinstance(x, argparse.Namespace)
     assert isinstance(arguments.parser, argparse.ArgumentParser)
 
 
 def test_parse_args_defaults() -> None:
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     assert args.config == ['config.json']
     assert args.strategy_path is None
     assert args.datadir is None
@@ -23,33 +25,32 @@ def test_parse_args_defaults() -> None:
 
 
 def test_parse_args_config() -> None:
-    args = Arguments(['-c', '/dev/null'], '').get_parsed_arg()
+    args = Arguments(['-c', '/dev/null']).get_parsed_arg()
     assert args.config == ['/dev/null']
 
-    args = Arguments(['--config', '/dev/null'], '').get_parsed_arg()
+    args = Arguments(['--config', '/dev/null']).get_parsed_arg()
     assert args.config == ['/dev/null']
 
     args = Arguments(['--config', '/dev/null',
-                      '--config', '/dev/zero'],
-                     '').get_parsed_arg()
+                      '--config', '/dev/zero'],).get_parsed_arg()
     assert args.config == ['/dev/null', '/dev/zero']
 
 
 def test_parse_args_db_url() -> None:
-    args = Arguments(['--db-url', 'sqlite:///test.sqlite'], '').get_parsed_arg()
+    args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg()
     assert args.db_url == 'sqlite:///test.sqlite'
 
 
 def test_parse_args_verbose() -> None:
-    args = Arguments(['-v'], '').get_parsed_arg()
+    args = Arguments(['-v']).get_parsed_arg()
     assert args.verbosity == 1
 
-    args = Arguments(['--verbose'], '').get_parsed_arg()
+    args = Arguments(['--verbose']).get_parsed_arg()
     assert args.verbosity == 1
 
 
 def test_common_scripts_options() -> None:
-    args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC'], '').get_parsed_arg()
+    args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC']).get_parsed_arg()
 
     assert args.pairs == ['ETH/BTC', 'XRP/BTC']
     assert hasattr(args, "func")
@@ -57,40 +58,40 @@ def test_common_scripts_options() -> None:
 
 def test_parse_args_version() -> None:
     with pytest.raises(SystemExit, match=r'0'):
-        Arguments(['--version'], '').get_parsed_arg()
+        Arguments(['--version']).get_parsed_arg()
 
 
 def test_parse_args_invalid() -> None:
     with pytest.raises(SystemExit, match=r'2'):
-        Arguments(['-c'], '').get_parsed_arg()
+        Arguments(['-c']).get_parsed_arg()
 
 
 def test_parse_args_strategy() -> None:
-    args = Arguments(['--strategy', 'SomeStrategy'], '').get_parsed_arg()
+    args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg()
     assert args.strategy == 'SomeStrategy'
 
 
 def test_parse_args_strategy_invalid() -> None:
     with pytest.raises(SystemExit, match=r'2'):
-        Arguments(['--strategy'], '').get_parsed_arg()
+        Arguments(['--strategy']).get_parsed_arg()
 
 
 def test_parse_args_strategy_path() -> None:
-    args = Arguments(['--strategy-path', '/some/path'], '').get_parsed_arg()
+    args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg()
     assert args.strategy_path == '/some/path'
 
 
 def test_parse_args_strategy_path_invalid() -> None:
     with pytest.raises(SystemExit, match=r'2'):
-        Arguments(['--strategy-path'], '').get_parsed_arg()
+        Arguments(['--strategy-path']).get_parsed_arg()
 
 
 def test_parse_args_backtesting_invalid() -> None:
     with pytest.raises(SystemExit, match=r'2'):
-        Arguments(['backtesting --ticker-interval'], '').get_parsed_arg()
+        Arguments(['backtesting --ticker-interval']).get_parsed_arg()
 
     with pytest.raises(SystemExit, match=r'2'):
-        Arguments(['backtesting --ticker-interval', 'abc'], '').get_parsed_arg()
+        Arguments(['backtesting --ticker-interval', 'abc']).get_parsed_arg()
 
 
 def test_parse_args_backtesting_custom() -> None:
@@ -103,7 +104,7 @@ def test_parse_args_backtesting_custom() -> None:
         'DefaultStrategy',
         'SampleStrategy'
         ]
-    call_args = Arguments(args, '').get_parsed_arg()
+    call_args = Arguments(args).get_parsed_arg()
     assert call_args.config == ['test_conf.json']
     assert call_args.verbosity == 0
     assert call_args.subparser == 'backtesting'
@@ -121,7 +122,7 @@ def test_parse_args_hyperopt_custom() -> None:
         '--epochs', '20',
         '--spaces', 'buy'
     ]
-    call_args = Arguments(args, '').get_parsed_arg()
+    call_args = Arguments(args).get_parsed_arg()
     assert call_args.config == ['test_conf.json']
     assert call_args.epochs == 20
     assert call_args.verbosity == 0
@@ -138,7 +139,7 @@ def test_download_data_options() -> None:
         '--days', '30',
         '--exchange', 'binance'
     ]
-    args = Arguments(args, '').get_parsed_arg()
+    args = Arguments(args).get_parsed_arg()
 
     assert args.pairs_file == 'file_with_pairs'
     assert args.datadir == 'datadir/directory'
@@ -155,7 +156,7 @@ def test_plot_dataframe_options() -> None:
         '--plot-limit', '30',
         '-p', 'UNITTEST/BTC',
     ]
-    pargs = Arguments(args, '').get_parsed_arg()
+    pargs = Arguments(args).get_parsed_arg()
 
     assert pargs.indicators1 == ["sma10", "sma100"]
     assert pargs.indicators2 == ["macd", "fastd", "fastk"]
@@ -170,7 +171,7 @@ def test_plot_profit_options() -> None:
         '--trade-source', 'DB',
         "--db-url", "sqlite:///whatever.sqlite",
     ]
-    pargs = Arguments(args, '').get_parsed_arg()
+    pargs = Arguments(args).get_parsed_arg()
 
     assert pargs.trade_source == "DB"
     assert pargs.pairs == ["UNITTEST/BTC"]
diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py
index 5a70715a4..177d365d6 100644
--- a/freqtrade/tests/test_configuration.py
+++ b/freqtrade/tests/test_configuration.py
@@ -66,7 +66,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
 def test__args_to_config(caplog):
 
     arg_list = ['--strategy-path', 'TestTest']
-    args = Arguments(arg_list, '').get_parsed_arg()
+    args = Arguments(arg_list).get_parsed_arg()
     configuration = Configuration(args)
     config = {}
     with warnings.catch_warnings(record=True) as w:
@@ -93,7 +93,7 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
     default_conf['max_open_trades'] = 0
     patched_configuration_load_config_file(mocker, default_conf)
 
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -119,7 +119,7 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
     )
 
     arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ]
-    args = Arguments(arg_list, '').get_parsed_arg()
+    args = Arguments(arg_list).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -167,7 +167,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
     default_conf['max_open_trades'] = -1
     patched_configuration_load_config_file(mocker, default_conf)
 
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -191,7 +191,7 @@ def test_load_config_file_exception(mocker) -> None:
 def test_load_config(default_conf, mocker) -> None:
     patched_configuration_load_config_file(mocker, default_conf)
 
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -208,7 +208,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
         '--strategy-path', '/some/path',
         '--db-url', 'sqlite:///someurl',
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -226,7 +226,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
         '--strategy', 'TestStrategy',
         '--strategy-path', '/some/path'
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
@@ -242,7 +242,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
         '--strategy', 'TestStrategy',
         '--strategy-path', '/some/path'
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
@@ -258,7 +258,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
         '--strategy', 'TestStrategy',
         '--strategy-path', '/some/path'
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
@@ -276,7 +276,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
         '--strategy', 'TestStrategy',
         '--strategy-path', '/some/path'
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
@@ -290,7 +290,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None:
     })
     patched_configuration_load_config_file(mocker, default_conf)
 
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -305,7 +305,7 @@ def test_show_info(default_conf, mocker, caplog) -> None:
         '--strategy', 'TestStrategy',
         '--db-url', 'sqlite:///tmp/testdb',
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     configuration.get_config()
@@ -323,7 +323,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
         'backtesting'
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -373,7 +373,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
         '--export', '/bar/foo'
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -423,7 +423,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
         'TestStrategy'
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args, RunMode.BACKTEST)
     config = configuration.get_config()
@@ -460,7 +460,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
         '--epochs', '10',
         '--spaces', 'all',
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args, RunMode.HYPEROPT)
     config = configuration.get_config()
@@ -536,7 +536,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
     # Prevent setting loggers
     mocker.patch('freqtrade.loggers._set_loggers', MagicMock)
     arglist = ['-vvv']
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
@@ -589,7 +589,7 @@ def test_set_logfile(default_conf, mocker):
     arglist = [
         '--logfile', 'test_file.log',
     ]
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -603,7 +603,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None:
     default_conf['forcebuy_enable'] = True
     patched_configuration_load_config_file(mocker, default_conf)
 
-    args = Arguments([], '').get_parsed_arg()
+    args = Arguments([]).get_parsed_arg()
     configuration = Configuration(args)
     validated_conf = configuration.load_config()
 
@@ -778,7 +778,7 @@ def test_pairlist_resolving():
         '--exchange', 'binance'
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -794,7 +794,7 @@ def test_pairlist_resolving_with_config(mocker, default_conf):
         'download-data',
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -809,7 +809,7 @@ def test_pairlist_resolving_with_config(mocker, default_conf):
         '--pairs', 'ETH/BTC', 'XRP/BTC',
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -831,7 +831,7 @@ def test_pairlist_resolving_with_config_pl(mocker, default_conf):
         '--pairs-file', 'pairs.json',
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
@@ -853,7 +853,7 @@ def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf):
         '--pairs-file', 'pairs.json',
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     with pytest.raises(OperationalException, match=r"No pairs file found with path.*"):
         configuration = Configuration(args)
@@ -870,7 +870,7 @@ def test_pairlist_resolving_fallback(mocker):
         '--exchange', 'binance'
     ]
 
-    args = Arguments(arglist, '').get_parsed_arg()
+    args = Arguments(arglist).get_parsed_arg()
 
     configuration = Configuration(args)
     config = configuration.get_config()
diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py
index db5a438d0..a0fd8218b 100644
--- a/freqtrade/tests/test_main.py
+++ b/freqtrade/tests/test_main.py
@@ -117,7 +117,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
     mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
     mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
 
-    args = Arguments(['-c', 'config.json.example'], '').get_parsed_arg()
+    args = Arguments(['-c', 'config.json.example']).get_parsed_arg()
     worker = Worker(args=args, config=default_conf)
     with pytest.raises(SystemExit):
         main(['-c', 'config.json.example'])
@@ -139,7 +139,7 @@ def test_reconfigure(mocker, default_conf) -> None:
     mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
     mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
 
-    args = Arguments(['-c', 'config.json.example'], '').get_parsed_arg()
+    args = Arguments(['-c', 'config.json.example']).get_parsed_arg()
     worker = Worker(args=args, config=default_conf)
     freqtrade = worker.freqtrade
 

From 5ce63cd54a33098b298da5b8d4c9177828a8e025 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 4 Sep 2019 16:39:23 +0200
Subject: [PATCH 097/227] Remove no_config_ argument from Arguments

---
 freqtrade/configuration/arguments.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index a8cd6686b..addbfcf67 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -47,11 +47,10 @@ class Arguments(object):
     """
     Arguments Class. Manage the arguments received by the cli
     """
-    def __init__(self, args: Optional[List[str]], no_default_config: bool = False) -> None:
+    def __init__(self, args: Optional[List[str]]) -> None:
         self.args = args
         self._parsed_arg: Optional[argparse.Namespace] = None
         self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot')
-        self._no_default_config = no_default_config
 
     def _load_args(self) -> None:
         self._build_args(optionlist=ARGS_MAIN)
@@ -77,7 +76,7 @@ class Arguments(object):
         # Workaround issue in argparse with action='append' and default value
         # (see https://bugs.python.org/issue16399)
         # Allow no-config for certain commands (like downloading / plotting)
-        if (not self._no_default_config and parsed_arg.config is None
+        if (parsed_arg.config is None
                 and not ('subparser' in parsed_arg and parsed_arg.subparser in NO_CONF_REQURIED)):
             parsed_arg.config = [constants.DEFAULT_CONFIG]
 

From 1b66f01ec0af11fa3914f89ca750834c55ae6757 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 3 Sep 2019 06:48:51 +0200
Subject: [PATCH 098/227] Always use config.json if it's available

---
 freqtrade/configuration/arguments.py  | 7 +++++--
 freqtrade/tests/test_configuration.py | 2 ++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index addbfcf67..10350d886 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -3,6 +3,7 @@ This module contains the argument manager class
 """
 import argparse
 from typing import List, Optional
+from pathlib import Path
 
 from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
 from freqtrade import constants
@@ -73,11 +74,13 @@ class Arguments(object):
         """
         parsed_arg = self.parser.parse_args(self.args)
 
+        # When no config is provided, but a config exists, use that configuration!
+
         # Workaround issue in argparse with action='append' and default value
         # (see https://bugs.python.org/issue16399)
         # Allow no-config for certain commands (like downloading / plotting)
-        if (parsed_arg.config is None
-                and not ('subparser' in parsed_arg and parsed_arg.subparser in NO_CONF_REQURIED)):
+        if (parsed_arg.config is None and ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or
+           not ('subparser' in parsed_arg and parsed_arg.subparser in NO_CONF_REQURIED))):
             parsed_arg.config = [constants.DEFAULT_CONFIG]
 
         return parsed_arg
diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py
index 177d365d6..7b489eba7 100644
--- a/freqtrade/tests/test_configuration.py
+++ b/freqtrade/tests/test_configuration.py
@@ -871,6 +871,8 @@ def test_pairlist_resolving_fallback(mocker):
     ]
 
     args = Arguments(arglist).get_parsed_arg()
+    # Fix flaky tests if config.json exists
+    args.config = None
 
     configuration = Configuration(args)
     config = configuration.get_config()

From e107290230fb95e8b9bba1935c4050da1da505a7 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 3 Sep 2019 07:05:48 +0200
Subject: [PATCH 099/227] Validate plot arguments

---
 freqtrade/plot/plot_utils.py     | 12 +++++++++++-
 freqtrade/tests/test_plotting.py | 11 +++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py
index 507e86d9d..d7d8919d5 100644
--- a/freqtrade/plot/plot_utils.py
+++ b/freqtrade/plot/plot_utils.py
@@ -1,15 +1,24 @@
 from argparse import Namespace
-
+from freqtrade import OperationalException
 from freqtrade.state import RunMode
 from freqtrade.utils import setup_utils_configuration
 
 
+def validate_plot_args(args: Namespace):
+    args_tmp = vars(args)
+    if not args_tmp.get('datadir') and not args_tmp.get('config'):
+        raise OperationalException(
+            "You need to specify either `--datadir` or `--config` "
+            "for plot-profit and plot-dataframe.")
+
+
 def start_plot_dataframe(args: Namespace) -> None:
     """
     Entrypoint for dataframe plotting
     """
     # Import here to avoid errors if plot-dependencies are not installed.
     from freqtrade.plot.plotting import analyse_and_plot_pairs
+    validate_plot_args(args)
     config = setup_utils_configuration(args, RunMode.PLOT)
 
     analyse_and_plot_pairs(config)
@@ -21,6 +30,7 @@ def start_plot_profit(args: Namespace) -> None:
     """
     # Import here to avoid errors if plot-dependencies are not installed.
     from freqtrade.plot.plotting import plot_profit
+    validate_plot_args(args)
     config = setup_utils_configuration(args, RunMode.PLOT)
 
     plot_profit(config)
diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py
index df208d4d2..f1785b63f 100644
--- a/freqtrade/tests/test_plotting.py
+++ b/freqtrade/tests/test_plotting.py
@@ -4,8 +4,10 @@ from pathlib import Path
 from unittest.mock import MagicMock
 
 import plotly.graph_objects as go
+import pytest
 from plotly.subplots import make_subplots
 
+from freqtrade import OperationalException
 from freqtrade.configuration import TimeRange
 from freqtrade.data import history
 from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
@@ -335,6 +337,15 @@ def test_start_plot_profit(mocker):
     assert called_config['pairs'] == ["ETH/BTC"]
 
 
+def test_start_plot_profit_error(mocker):
+    args = [
+        "plot-profit",
+        "--pairs", "ETH/BTC"
+    ]
+    with pytest.raises(OperationalException):
+        start_plot_profit(get_args(args))
+
+
 def test_plot_profit(default_conf, mocker, caplog):
     default_conf['trade_source'] = 'file'
     default_conf["datadir"] = history.make_testdata_path(None)

From 3343b34725b6c0cbf3c79fbacf5f60f50606b30a Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 5 Sep 2019 00:38:15 +0300
Subject: [PATCH 100/227] Add tests for simplified hyperopt interface

---
 freqtrade/tests/optimize/test_hyperopt.py | 268 ++++++++++++++++++++++
 1 file changed, 268 insertions(+)

diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py
index 9583de510..fd79a38f2 100644
--- a/freqtrade/tests/optimize/test_hyperopt.py
+++ b/freqtrade/tests/optimize/test_hyperopt.py
@@ -693,3 +693,271 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
     assert dumper.called
     # Should be called twice, once for tickerdata, once to save evaluations
     assert dumper.call_count == 2
+
+
+def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) -> None:
+    dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    parallel = mocker.patch(
+        'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
+        MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
+    )
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'roi stoploss',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.indicator_space
+    del hyperopt.custom_hyperopt.__class__.sell_indicator_space
+
+    hyperopt.start()
+
+    parallel.assert_called_once()
+
+    out, err = capsys.readouterr()
+    assert 'Best result:\n\n*    1/1: foo result Objective: 1.00000\n' in out
+    assert dumper.called
+    # Should be called twice, once for tickerdata, once to save evaluations
+    assert dumper.call_count == 2
+    assert hasattr(hyperopt.backtesting, "advise_sell")
+    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt, "max_open_trades")
+    assert hyperopt.max_open_trades == default_conf['max_open_trades']
+    assert hasattr(hyperopt, "position_stacking")
+
+
+def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) -> None:
+    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'all',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.indicator_space
+    del hyperopt.custom_hyperopt.__class__.sell_indicator_space
+
+    with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
+        hyperopt.start()
+
+
+def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
+    dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    parallel = mocker.patch(
+        'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
+        MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
+    )
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'buy',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    # TODO: sell_strategy_generator() is actually not called because
+    # run_optimizer_parallel() is mocked
+    del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.sell_indicator_space
+
+    hyperopt.start()
+
+    parallel.assert_called_once()
+
+    out, err = capsys.readouterr()
+    assert 'Best result:\n\n*    1/1: foo result Objective: 1.00000\n' in out
+    assert dumper.called
+    # Should be called twice, once for tickerdata, once to save evaluations
+    assert dumper.call_count == 2
+    assert hasattr(hyperopt.backtesting, "advise_sell")
+    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt, "max_open_trades")
+    assert hyperopt.max_open_trades == default_conf['max_open_trades']
+    assert hasattr(hyperopt, "position_stacking")
+
+
+def test_simplified_interface_buy_failed_1(mocker, default_conf, caplog, capsys) -> None:
+    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'buy',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
+
+    with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
+        hyperopt.start()
+
+
+def test_simplified_interface_buy_failed_2(mocker, default_conf, caplog, capsys) -> None:
+    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'buy',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.indicator_space
+
+    with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
+        hyperopt.start()
+
+
+def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None:
+    dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    parallel = mocker.patch(
+        'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
+        MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
+    )
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'sell',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    # TODO: buy_strategy_generator() is actually not called because
+    # run_optimizer_parallel() is mocked
+    del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
+    del hyperopt.custom_hyperopt.__class__.indicator_space
+
+    hyperopt.start()
+
+    parallel.assert_called_once()
+
+    out, err = capsys.readouterr()
+    assert 'Best result:\n\n*    1/1: foo result Objective: 1.00000\n' in out
+    assert dumper.called
+    # Should be called twice, once for tickerdata, once to save evaluations
+    assert dumper.call_count == 2
+    assert hasattr(hyperopt.backtesting, "advise_sell")
+    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt, "max_open_trades")
+    assert hyperopt.max_open_trades == default_conf['max_open_trades']
+    assert hasattr(hyperopt, "position_stacking")
+
+
+def test_simplified_interface_sell_failed_1(mocker, default_conf, caplog, capsys) -> None:
+    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'sell',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
+
+    with pytest.raises(OperationalException, match=r"The 'sell' space is included into *"):
+        hyperopt.start()
+
+
+def test_simplified_interface_sell_failed_2(mocker, default_conf, caplog, capsys) -> None:
+    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
+    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
+    mocker.patch(
+        'freqtrade.optimize.hyperopt.get_timeframe',
+        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
+    )
+
+    patch_exchange(mocker)
+
+    default_conf.update({'config': 'config.json.example',
+                         'epochs': 1,
+                         'timerange': None,
+                         'spaces': 'sell',
+                         'hyperopt_jobs': 1, })
+
+    hyperopt = Hyperopt(default_conf)
+    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
+    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
+
+    del hyperopt.custom_hyperopt.__class__.sell_indicator_space
+
+    with pytest.raises(OperationalException, match=r"The 'sell' space is included into *"):
+        hyperopt.start()

From e2e00151194f43d98dc857cf5d321c2fed86c712 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 5 Sep 2019 20:02:01 +0200
Subject: [PATCH 101/227] Don't rename dict ... we can use it as is

---
 freqtrade/rpc/rpc.py                      |  4 ++--
 freqtrade/rpc/telegram.py                 |  4 ++--
 freqtrade/tests/rpc/test_rpc.py           | 16 ++++++++--------
 freqtrade/tests/rpc/test_rpc_apiserver.py |  4 ++--
 freqtrade/tests/rpc/test_rpc_telegram.py  |  4 ++--
 5 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index 7b811cadc..8112b33bf 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -294,9 +294,9 @@ class RPC(object):
             total = total + est_btc
             output.append({
                 'currency': coin,
-                'available': balance['free'],
+                'free': balance['free'],
                 'balance': balance['total'],
-                'pending': balance['used'],
+                'used': balance['used'],
                 'est_btc': est_btc,
             })
         if total == 0.0:
diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py
index d11fc02ab..80582a0ce 100644
--- a/freqtrade/rpc/telegram.py
+++ b/freqtrade/rpc/telegram.py
@@ -328,9 +328,9 @@ class Telegram(RPC):
             for currency in result['currencies']:
                 if currency['est_btc'] > 0.0001:
                     curr_output = "*{currency}:*\n" \
-                            "\t`Available: {available: .8f}`\n" \
+                            "\t`Available: {free: .8f}`\n" \
                             "\t`Balance: {balance: .8f}`\n" \
-                            "\t`Pending: {pending: .8f}`\n" \
+                            "\t`Pending: {used: .8f}`\n" \
                             "\t`Est. BTC: {est_btc: .8f}`\n".format(**currency)
                 else:
                     curr_output = "*{currency}:* not showing <1$ amount \n".format(**currency)
diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py
index 5d3fb7920..47ed34a58 100644
--- a/freqtrade/tests/rpc/test_rpc.py
+++ b/freqtrade/tests/rpc/test_rpc.py
@@ -363,9 +363,9 @@ def test_rpc_balance_handle_error(default_conf, mocker):
     assert 'USD' == result['symbol']
     assert result['currencies'] == [{
         'currency': 'BTC',
-        'available': 10.0,
+        'free': 10.0,
         'balance': 12.0,
-        'pending': 2.0,
+        'used': 2.0,
         'est_btc': 12.0,
     }]
     assert result['total'] == 12.0
@@ -417,22 +417,22 @@ def test_rpc_balance_handle(default_conf, mocker):
     assert 'USD' == result['symbol']
     assert result['currencies'] == [
         {'currency': 'BTC',
-            'available': 10.0,
+            'free': 10.0,
             'balance': 12.0,
-            'pending': 2.0,
+            'used': 2.0,
             'est_btc': 12.0,
          },
-        {'available': 1.0,
+        {'free': 1.0,
          'balance': 5.0,
          'currency': 'ETH',
          'est_btc': 0.05,
-         'pending': 4.0
+         'used': 4.0
          },
-        {'available': 5.0,
+        {'free': 5.0,
          'balance': 10.0,
          'currency': 'PAX',
          'est_btc': 0.1,
-         'pending': 5.0}
+         'used': 5.0}
     ]
     assert result['total'] == 12.15
 
diff --git a/freqtrade/tests/rpc/test_rpc_apiserver.py b/freqtrade/tests/rpc/test_rpc_apiserver.py
index 794343a98..bcbbb228f 100644
--- a/freqtrade/tests/rpc/test_rpc_apiserver.py
+++ b/freqtrade/tests/rpc/test_rpc_apiserver.py
@@ -251,9 +251,9 @@ def test_api_balance(botclient, mocker, rpc_balance):
     assert len(rc.json["currencies"]) == 5
     assert rc.json['currencies'][0] == {
         'currency': 'BTC',
-        'available': 12.0,
+        'free': 12.0,
         'balance': 12.0,
-        'pending': 0.0,
+        'used': 0.0,
         'est_btc': 12.0,
     }
 
diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py
index 3820332b8..3930b110d 100644
--- a/freqtrade/tests/rpc/test_rpc_telegram.py
+++ b/freqtrade/tests/rpc/test_rpc_telegram.py
@@ -577,8 +577,8 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None
         curr = choice(ascii_uppercase) + choice(ascii_uppercase) + choice(ascii_uppercase)
         balances.append({
             'currency': curr,
-            'available': 1.0,
-            'pending': 0.5,
+            'free': 1.0,
+            'used': 0.5,
             'balance': i,
             'est_btc': 1
         })

From e8f37666ea2223d43c675bb4d002e3a2af40614d Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 5 Sep 2019 20:02:18 +0200
Subject: [PATCH 102/227] Fix Problem when ccxt reports None as values

---
 freqtrade/rpc/rpc.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index 8112b33bf..213812a70 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -294,9 +294,9 @@ class RPC(object):
             total = total + est_btc
             output.append({
                 'currency': coin,
-                'free': balance['free'],
-                'balance': balance['total'],
-                'used': balance['used'],
+                'free': balance['free'] if balance['free'] is not None else 0,
+                'balance': balance['total'] if balance['total'] is not None else 0,
+                'used': balance['used'] if balance['used'] is not None else 0,
                 'est_btc': est_btc,
             })
         if total == 0.0:

From 48ac37a1b83a017aafed7d09e50a10fb78cd2b57 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 5 Sep 2019 20:16:09 +0200
Subject: [PATCH 103/227] BLock kraken trading - it's not working at the moment

---
 freqtrade/exchange/exchange.py        | 3 +++
 freqtrade/tests/test_configuration.py | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
index d48d18ebf..d5c4a6b1c 100644
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -30,6 +30,9 @@ BAD_EXCHANGES = {
     "bitmex": "Various reasons",
     "bitstamp": "Does not provide history. "
                 "Details in https://github.com/freqtrade/freqtrade/issues/1983",
+    "kraken": "TEMPORARY: Balance does not report free balance, so freqtrade will not know "
+              "if enough balance is available."
+              "Details in https://github.com/freqtrade/freqtrade/issues/1687#issuecomment-528509266"
     }
 
 
diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py
index 177d365d6..a93f0c49e 100644
--- a/freqtrade/tests/test_configuration.py
+++ b/freqtrade/tests/test_configuration.py
@@ -494,7 +494,7 @@ def test_check_exchange(default_conf, caplog) -> None:
     caplog.clear()
 
     # Test an available exchange, supported by ccxt
-    default_conf.get('exchange').update({'name': 'kraken'})
+    default_conf.get('exchange').update({'name': 'huobipro'})
     assert check_exchange(default_conf)
     assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported "
                       r"by the Freqtrade development team\. .*", caplog)

From e39d9111770a9bd7b73aa1addc5b7d14649c7d1a Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 5 Sep 2019 23:31:07 +0300
Subject: [PATCH 104/227] Improve wordings in hyperopt.md

---
 docs/hyperopt.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index c7544df5b..95bec8715 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -400,9 +400,9 @@ If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace f
 
 These ranges should be sufficient in most cases. The minutes in the steps (ROI dict keys) are scaled linearly depending on the ticker interval used. The ROI values in the steps (ROI dict values) are scaled logarithmically depending on the ticker interval used.
 
-If you use legacy freqtrade HyperOpts class samples where the `generate_roi_table()` and `roi_space()` methods were copied in each custom hyperopt file, simply remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by freqtrade by default.
+If you have the `generate_roi_table()` and `roi_space()` methods in your custom hyperopt file, remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by Freqtrade by default.
 
-Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps).
+Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). A sample for these methods can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py).
 
 ### Understand Hyperopt Stoploss results
 
@@ -424,9 +424,9 @@ Stoploss: -0.37996664668703606
 
 If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.5...-0.02, which is sufficient in most cases.
 
-If you use legacy freqtrade HyperOpts class samples where `stoploss_space()` method was copied in each custom hyperopt file, simply remove it in order to utilize Stoploss hyperoptimization space generated by freqtrade by default.
+If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default.
 
-Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization.
+Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py).
 
 ### Validate backtesting results
 

From ef8386c06501374dca28f8f3dec8b6d5a3226a2c Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Fri, 6 Sep 2019 11:55:07 +0300
Subject: [PATCH 105/227] Fix table with ROI limits

---
 docs/hyperopt.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index 95bec8715..becf572ee 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -391,7 +391,7 @@ minimal_roi = {
 
 If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges (for some of the most used ticker intervals, values are rounded to 5 digits after the decimal point):
 
-| # step  1m  5m  1h  1d |
+| # step | 1m |  | 5m |  | 1h |  | 1d |  |
 |---|---|---|---|---|---|---|---|---|
 | 1 | 0 | 0.01161...0.11992 | 0 | 0.03...0.31 | 0 | 0.06883...0.71124 | 0 | 0.12178...1.25835 |
 | 2 | 2...8 | 0.00774...0.04255 | 10...40 | 0.02...0.11 | 120...480 | 0.04589...0.25238 | 2880...11520 | 0.08118...0.44651 |

From 7e56704767932fa1ac1ccb4a505ee3cc73568ff1 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Fri, 6 Sep 2019 15:08:44 +0300
Subject: [PATCH 106/227] Parametrize tests for hyperopt simplified failed

---
 freqtrade/tests/optimize/test_hyperopt.py | 92 +++--------------------
 1 file changed, 10 insertions(+), 82 deletions(-)

diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py
index fd79a38f2..6fba59be9 100644
--- a/freqtrade/tests/optimize/test_hyperopt.py
+++ b/freqtrade/tests/optimize/test_hyperopt.py
@@ -814,58 +814,6 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
     assert hasattr(hyperopt, "position_stacking")
 
 
-def test_simplified_interface_buy_failed_1(mocker, default_conf, caplog, capsys) -> None:
-    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
-    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
-    mocker.patch(
-        'freqtrade.optimize.hyperopt.get_timeframe',
-        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
-    )
-
-    patch_exchange(mocker)
-
-    default_conf.update({'config': 'config.json.example',
-                         'epochs': 1,
-                         'timerange': None,
-                         'spaces': 'buy',
-                         'hyperopt_jobs': 1, })
-
-    hyperopt = Hyperopt(default_conf)
-    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
-    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
-
-    del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
-
-    with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
-        hyperopt.start()
-
-
-def test_simplified_interface_buy_failed_2(mocker, default_conf, caplog, capsys) -> None:
-    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
-    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
-    mocker.patch(
-        'freqtrade.optimize.hyperopt.get_timeframe',
-        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
-    )
-
-    patch_exchange(mocker)
-
-    default_conf.update({'config': 'config.json.example',
-                         'epochs': 1,
-                         'timerange': None,
-                         'spaces': 'buy',
-                         'hyperopt_jobs': 1, })
-
-    hyperopt = Hyperopt(default_conf)
-    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
-    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
-
-    del hyperopt.custom_hyperopt.__class__.indicator_space
-
-    with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
-        hyperopt.start()
-
-
 def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None:
     dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
     mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
@@ -911,7 +859,13 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
     assert hasattr(hyperopt, "position_stacking")
 
 
-def test_simplified_interface_sell_failed_1(mocker, default_conf, caplog, capsys) -> None:
+@pytest.mark.parametrize("method,space", [
+    ('buy_strategy_generator', 'buy'),
+    ('indicator_space', 'buy'),
+    ('sell_strategy_generator', 'sell'),
+    ('sell_indicator_space', 'sell'),
+])
+def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, method, space) -> None:
     mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
     mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
     mocker.patch(
@@ -924,40 +878,14 @@ def test_simplified_interface_sell_failed_1(mocker, default_conf, caplog, capsys
     default_conf.update({'config': 'config.json.example',
                          'epochs': 1,
                          'timerange': None,
-                         'spaces': 'sell',
+                         'spaces': space,
                          'hyperopt_jobs': 1, })
 
     hyperopt = Hyperopt(default_conf)
     hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
     hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
 
-    del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
+    delattr(hyperopt.custom_hyperopt.__class__, method)
 
-    with pytest.raises(OperationalException, match=r"The 'sell' space is included into *"):
-        hyperopt.start()
-
-
-def test_simplified_interface_sell_failed_2(mocker, default_conf, caplog, capsys) -> None:
-    mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
-    mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
-    mocker.patch(
-        'freqtrade.optimize.hyperopt.get_timeframe',
-        MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
-    )
-
-    patch_exchange(mocker)
-
-    default_conf.update({'config': 'config.json.example',
-                         'epochs': 1,
-                         'timerange': None,
-                         'spaces': 'sell',
-                         'hyperopt_jobs': 1, })
-
-    hyperopt = Hyperopt(default_conf)
-    hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
-    hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
-
-    del hyperopt.custom_hyperopt.__class__.sell_indicator_space
-
-    with pytest.raises(OperationalException, match=r"The 'sell' space is included into *"):
+    with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"):
         hyperopt.start()

From 4fdf8a75cd6e5f4fd1a6eff0c671a907527b65a4 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Fri, 6 Sep 2019 16:09:03 +0300
Subject: [PATCH 107/227] Adjust hyperopt tests after the merge with develop

---
 freqtrade/tests/optimize/test_hyperopt.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py
index a8b2f2e67..976cb3d53 100644
--- a/freqtrade/tests/optimize/test_hyperopt.py
+++ b/freqtrade/tests/optimize/test_hyperopt.py
@@ -706,7 +706,8 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
 
     parallel = mocker.patch(
         'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
-        MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
+        MagicMock(return_value=[{
+            'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}}])
     )
     patch_exchange(mocker)
 

From bde82e965463e77c3d82d8df47cb9ac3769c2732 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 7 Sep 2019 20:34:25 +0200
Subject: [PATCH 108/227] Move make_testdata_path to conftest

---
 freqtrade/tests/conftest.py             |  5 +++++
 freqtrade/tests/data/test_btanalysis.py |  4 ++--
 freqtrade/tests/data/test_history.py    |  4 ++--
 freqtrade/tests/test_plotting.py        | 24 +++++++++++-------------
 4 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py
index 79b03b057..f09181b0f 100644
--- a/freqtrade/tests/conftest.py
+++ b/freqtrade/tests/conftest.py
@@ -1047,3 +1047,8 @@ def rpc_balance():
             'used': 0.0
         },
     }
+
+
+def make_testdata_path(datadir) -> Path:
+    """Return the path where testdata files are stored"""
+    return (Path(__file__).parent / "testdata").resolve()
diff --git a/freqtrade/tests/data/test_btanalysis.py b/freqtrade/tests/data/test_btanalysis.py
index 90602b4fc..26eff3614 100644
--- a/freqtrade/tests/data/test_btanalysis.py
+++ b/freqtrade/tests/data/test_btanalysis.py
@@ -11,8 +11,8 @@ from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
                                        extract_trades_of_period,
                                        load_backtest_data, load_trades,
                                        load_trades_from_db)
-from freqtrade.data.history import (load_data, load_pair_history,
-                                    make_testdata_path)
+from freqtrade.data.history import load_data, load_pair_history
+from freqtrade.tests.conftest import make_testdata_path
 from freqtrade.tests.test_persistence import create_mock_trades
 
 
diff --git a/freqtrade/tests/data/test_history.py b/freqtrade/tests/data/test_history.py
index ec4a05e63..35b3d949b 100644
--- a/freqtrade/tests/data/test_history.py
+++ b/freqtrade/tests/data/test_history.py
@@ -16,14 +16,14 @@ from freqtrade.configuration 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,
                                     refresh_backtest_ohlcv_data,
                                     trim_tickerlist)
 from freqtrade.exchange import timeframe_to_minutes
 from freqtrade.misc import file_dump_json
 from freqtrade.strategy.default_strategy import DefaultStrategy
 from freqtrade.tests.conftest import (get_patched_exchange, log_has,
-                                      patch_exchange)
+                                      patch_exchange, make_testdata_path)
 
 # Change this if modifying UNITTEST/BTC testdatafile
 _BTC_UNITTEST_LENGTH = 13681
diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py
index f1785b63f..e9079fff4 100644
--- a/freqtrade/tests/test_plotting.py
+++ b/freqtrade/tests/test_plotting.py
@@ -19,7 +19,8 @@ from freqtrade.plot.plotting import (add_indicators, add_profit,
                                      generate_profit_graph, init_plotscript,
                                      plot_profit, plot_trades, store_plot_file)
 from freqtrade.strategy.default_strategy import DefaultStrategy
-from freqtrade.tests.conftest import get_args, log_has, log_has_re
+from freqtrade.tests.conftest import (get_args, log_has, log_has_re,
+                                      make_testdata_path)
 
 
 def fig_generating_mock(fig, *args, **kwargs):
@@ -46,9 +47,8 @@ def test_init_plotscript(default_conf, mocker):
     default_conf['timerange'] = "20180110-20180112"
     default_conf['trade_source'] = "file"
     default_conf['ticker_interval'] = "5m"
-    default_conf["datadir"] = history.make_testdata_path(None)
-    default_conf['exportfilename'] = str(
-        history.make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = make_testdata_path(None)
+    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
     ret = init_plotscript(default_conf)
     assert "tickers" in ret
     assert "trades" in ret
@@ -101,7 +101,7 @@ def test_plot_trades(caplog):
     assert fig == fig1
     assert log_has("No trades found.", caplog)
     pair = "ADA/BTC"
-    filename = history.make_testdata_path(None) / "backtest-result_test.json"
+    filename = make_testdata_path(None) / "backtest-result_test.json"
     trades = load_backtest_data(filename)
     trades = trades.loc[trades['pair'] == pair]
 
@@ -225,7 +225,7 @@ def test_generate_plot_file(mocker, caplog):
 
 
 def test_add_profit():
-    filename = history.make_testdata_path(None) / "backtest-result_test.json"
+    filename = make_testdata_path(None) / "backtest-result_test.json"
     bt_data = load_backtest_data(filename)
     timerange = TimeRange.parse_timerange("20180110-20180112")
 
@@ -245,7 +245,7 @@ def test_add_profit():
 
 
 def test_generate_profit_graph():
-    filename = history.make_testdata_path(None) / "backtest-result_test.json"
+    filename = make_testdata_path(None) / "backtest-result_test.json"
     trades = load_backtest_data(filename)
     timerange = TimeRange.parse_timerange("20180110-20180112")
     pairs = ["POWR/BTC", "XLM/BTC"]
@@ -296,9 +296,8 @@ def test_start_plot_dataframe(mocker):
 
 def test_analyse_and_plot_pairs(default_conf, mocker, caplog):
     default_conf['trade_source'] = 'file'
-    default_conf["datadir"] = history.make_testdata_path(None)
-    default_conf['exportfilename'] = str(
-        history.make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = make_testdata_path(None)
+    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
     default_conf['indicators1'] = ["sma5", "ema10"]
     default_conf['indicators2'] = ["macd"]
     default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
@@ -348,9 +347,8 @@ def test_start_plot_profit_error(mocker):
 
 def test_plot_profit(default_conf, mocker, caplog):
     default_conf['trade_source'] = 'file'
-    default_conf["datadir"] = history.make_testdata_path(None)
-    default_conf['exportfilename'] = str(
-        history.make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = make_testdata_path(None)
+    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
     default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
 
     profit_mock = MagicMock()

From fe631ffd04f3cf42ee8b28135786e7eb3b41c638 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 7 Sep 2019 20:56:03 +0200
Subject: [PATCH 109/227] Use fixture to determine test_data_dir

---
 freqtrade/tests/conftest.py                  |  3 +-
 freqtrade/tests/data/test_btanalysis.py      | 19 ++---
 freqtrade/tests/data/test_converter.py       |  4 +-
 freqtrade/tests/data/test_history.py         | 87 ++++++++++----------
 freqtrade/tests/optimize/test_backtesting.py | 53 ++++++------
 freqtrade/tests/optimize/test_hyperopt.py    |  8 +-
 freqtrade/tests/strategy/test_interface.py   |  4 +-
 freqtrade/tests/test_misc.py                 |  8 +-
 freqtrade/tests/test_plotting.py             | 49 ++++++-----
 9 files changed, 118 insertions(+), 117 deletions(-)

diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py
index f09181b0f..e7b8b386a 100644
--- a/freqtrade/tests/conftest.py
+++ b/freqtrade/tests/conftest.py
@@ -1049,6 +1049,7 @@ def rpc_balance():
     }
 
 
-def make_testdata_path(datadir) -> Path:
+@pytest.fixture
+def testdatadir() -> Path:
     """Return the path where testdata files are stored"""
     return (Path(__file__).parent / "testdata").resolve()
diff --git a/freqtrade/tests/data/test_btanalysis.py b/freqtrade/tests/data/test_btanalysis.py
index 26eff3614..e95757d4e 100644
--- a/freqtrade/tests/data/test_btanalysis.py
+++ b/freqtrade/tests/data/test_btanalysis.py
@@ -12,13 +12,12 @@ from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
                                        load_backtest_data, load_trades,
                                        load_trades_from_db)
 from freqtrade.data.history import load_data, load_pair_history
-from freqtrade.tests.conftest import make_testdata_path
 from freqtrade.tests.test_persistence import create_mock_trades
 
 
-def test_load_backtest_data():
+def test_load_backtest_data(testdatadir):
 
-    filename = make_testdata_path(None) / "backtest-result_test.json"
+    filename = testdatadir / "backtest-result_test.json"
     bt_data = load_backtest_data(filename)
     assert isinstance(bt_data, DataFrame)
     assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profitabs"]
@@ -52,12 +51,12 @@ def test_load_trades_db(default_conf, fee, mocker):
             assert col in trades.columns
 
 
-def test_extract_trades_of_period():
+def test_extract_trades_of_period(testdatadir):
     pair = "UNITTEST/BTC"
     timerange = TimeRange(None, 'line', 0, -1000)
 
     data = load_pair_history(pair=pair, ticker_interval='1m',
-                             datadir=None, timerange=timerange)
+                             datadir=testdatadir, timerange=timerange)
 
     # timerange = 2017-11-14 06:07 - 2017-11-14 22:58:00
     trades = DataFrame(
@@ -108,9 +107,9 @@ def test_load_trades(default_conf, mocker):
     assert bt_mock.call_count == 1
 
 
-def test_combine_tickers_with_mean():
+def test_combine_tickers_with_mean(testdatadir):
     pairs = ["ETH/BTC", "XLM/BTC"]
-    tickers = load_data(datadir=None,
+    tickers = load_data(datadir=testdatadir,
                         pairs=pairs,
                         ticker_interval='5m'
                         )
@@ -121,13 +120,13 @@ def test_combine_tickers_with_mean():
     assert "mean" in df.columns
 
 
-def test_create_cum_profit():
-    filename = make_testdata_path(None) / "backtest-result_test.json"
+def test_create_cum_profit(testdatadir):
+    filename = testdatadir / "backtest-result_test.json"
     bt_data = load_backtest_data(filename)
     timerange = TimeRange.parse_timerange("20180110-20180112")
 
     df = load_pair_history(pair="POWR/BTC", ticker_interval='5m',
-                           datadir=None, timerange=timerange)
+                           datadir=testdatadir, timerange=timerange)
 
     cum_profits = create_cum_profit(df.set_index('date'),
                                     bt_data[bt_data["pair"] == 'POWR/BTC'],
diff --git a/freqtrade/tests/data/test_converter.py b/freqtrade/tests/data/test_converter.py
index 39462bdd8..d47c3ee92 100644
--- a/freqtrade/tests/data/test_converter.py
+++ b/freqtrade/tests/data/test_converter.py
@@ -21,8 +21,8 @@ def test_parse_ticker_dataframe(ticker_history_list, caplog):
     assert log_has('Parsing tickerlist to dataframe', caplog)
 
 
-def test_ohlcv_fill_up_missing_data(caplog):
-    data = load_pair_history(datadir=None,
+def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
+    data = load_pair_history(datadir=testdatadir,
                              ticker_interval='1m',
                              refresh_pairs=False,
                              pair='UNITTEST/BTC',
diff --git a/freqtrade/tests/data/test_history.py b/freqtrade/tests/data/test_history.py
index 35b3d949b..43429031c 100644
--- a/freqtrade/tests/data/test_history.py
+++ b/freqtrade/tests/data/test_history.py
@@ -22,8 +22,7 @@ from freqtrade.data.history import (download_pair_history,
 from freqtrade.exchange import timeframe_to_minutes
 from freqtrade.misc import file_dump_json
 from freqtrade.strategy.default_strategy import DefaultStrategy
-from freqtrade.tests.conftest import (get_patched_exchange, log_has,
-                                      patch_exchange, make_testdata_path)
+from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re, patch_exchange
 
 # Change this if modifying UNITTEST/BTC testdatafile
 _BTC_UNITTEST_LENGTH = 13681
@@ -60,8 +59,8 @@ def _clean_test_file(file: str) -> None:
         os.rename(file_swp, file)
 
 
-def test_load_data_30min_ticker(mocker, caplog, default_conf) -> None:
-    ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='30m', datadir=None)
+def test_load_data_30min_ticker(mocker, caplog, default_conf, testdatadir) -> None:
+    ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='30m', datadir=testdatadir)
     assert isinstance(ld, DataFrame)
     assert not log_has(
         'Download history data for pair: "UNITTEST/BTC", interval: 30m '
@@ -69,8 +68,8 @@ def test_load_data_30min_ticker(mocker, caplog, default_conf) -> None:
     )
 
 
-def test_load_data_7min_ticker(mocker, caplog, default_conf) -> None:
-    ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='7m', datadir=None)
+def test_load_data_7min_ticker(mocker, caplog, default_conf, testdatadir) -> None:
+    ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='7m', datadir=testdatadir)
     assert not isinstance(ld, DataFrame)
     assert ld is None
     assert log_has(
@@ -80,11 +79,11 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf) -> None:
     )
 
 
-def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
+def test_load_data_1min_ticker(ticker_history, mocker, caplog, testdatadir) -> None:
     mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history)
     file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
     _backup_file(file, copy_file=True)
-    history.load_data(datadir=None, ticker_interval='1m', pairs=['UNITTEST/BTC'])
+    history.load_data(datadir=testdatadir, ticker_interval='1m', pairs=['UNITTEST/BTC'])
     assert os.path.isfile(file) is True
     assert not log_has(
         'Download history data for pair: "UNITTEST/BTC", interval: 1m '
@@ -93,7 +92,8 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
     _clean_test_file(file)
 
 
-def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, default_conf) -> None:
+def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
+                                      default_conf, testdatadir) -> None:
     """
     Test load_pair_history() with 1 min ticker
     """
@@ -103,7 +103,7 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau
 
     _backup_file(file)
     # do not download a new pair if refresh_pairs isn't set
-    history.load_pair_history(datadir=None,
+    history.load_pair_history(datadir=testdatadir,
                               ticker_interval='1m',
                               refresh_pairs=False,
                               pair='MEME/BTC')
@@ -115,18 +115,18 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau
     )
 
     # download a new pair if refresh_pairs is set
-    history.load_pair_history(datadir=None,
+    history.load_pair_history(datadir=testdatadir,
                               ticker_interval='1m',
                               refresh_pairs=True,
                               exchange=exchange,
                               pair='MEME/BTC')
     assert os.path.isfile(file) is True
-    assert log_has(
+    assert log_has_re(
         'Download history data for pair: "MEME/BTC", interval: 1m '
-        'and store in None.', caplog
+        'and store in .*', caplog
     )
     with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'):
-        history.load_pair_history(datadir=None,
+        history.load_pair_history(datadir=testdatadir,
                                   ticker_interval='1m',
                                   refresh_pairs=True,
                                   exchange=None,
@@ -159,8 +159,8 @@ def test_load_data_live_noexchange(default_conf, mocker, caplog) -> None:
                           )
 
 
-def test_testdata_path() -> None:
-    assert str(Path('freqtrade') / 'tests' / 'testdata') in str(make_testdata_path(None))
+def test_testdata_path(testdatadir) -> None:
+    assert str(Path('freqtrade') / 'tests' / 'testdata') in str(testdatadir)
 
 
 def test_load_cached_data_for_updating(mocker) -> None:
@@ -248,7 +248,7 @@ def test_load_cached_data_for_updating(mocker) -> None:
     assert start_ts is None
 
 
-def test_download_pair_history(ticker_history_list, mocker, default_conf) -> None:
+def test_download_pair_history(ticker_history_list, mocker, default_conf, testdatadir) -> None:
     mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history_list)
     exchange = get_patched_exchange(mocker, default_conf)
     file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
@@ -264,10 +264,10 @@ def test_download_pair_history(ticker_history_list, mocker, default_conf) -> Non
     assert os.path.isfile(file1_1) is False
     assert os.path.isfile(file2_1) is False
 
-    assert download_pair_history(datadir=None, exchange=exchange,
+    assert download_pair_history(datadir=testdatadir, exchange=exchange,
                                  pair='MEME/BTC',
                                  ticker_interval='1m')
-    assert download_pair_history(datadir=None, exchange=exchange,
+    assert download_pair_history(datadir=testdatadir, exchange=exchange,
                                  pair='CFI/BTC',
                                  ticker_interval='1m')
     assert not exchange._pairs_last_refresh_time
@@ -281,10 +281,10 @@ def test_download_pair_history(ticker_history_list, mocker, default_conf) -> Non
     assert os.path.isfile(file1_5) is False
     assert os.path.isfile(file2_5) is False
 
-    assert download_pair_history(datadir=None, exchange=exchange,
+    assert download_pair_history(datadir=testdatadir, exchange=exchange,
                                  pair='MEME/BTC',
                                  ticker_interval='5m')
-    assert download_pair_history(datadir=None, exchange=exchange,
+    assert download_pair_history(datadir=testdatadir, exchange=exchange,
                                  pair='CFI/BTC',
                                  ticker_interval='5m')
     assert not exchange._pairs_last_refresh_time
@@ -296,7 +296,7 @@ def test_download_pair_history(ticker_history_list, mocker, default_conf) -> Non
     _clean_test_file(file2_5)
 
 
-def test_download_pair_history2(mocker, default_conf) -> None:
+def test_download_pair_history2(mocker, default_conf, testdatadir) -> None:
     tick = [
         [1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
         [1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
@@ -304,12 +304,13 @@ def test_download_pair_history2(mocker, default_conf) -> None:
     json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
     mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=tick)
     exchange = get_patched_exchange(mocker, default_conf)
-    download_pair_history(None, exchange, pair="UNITTEST/BTC", ticker_interval='1m')
-    download_pair_history(None, exchange, pair="UNITTEST/BTC", ticker_interval='3m')
+    download_pair_history(testdatadir, exchange, pair="UNITTEST/BTC", ticker_interval='1m')
+    download_pair_history(testdatadir, exchange, pair="UNITTEST/BTC", ticker_interval='3m')
     assert json_dump_mock.call_count == 2
 
 
-def test_download_backtesting_data_exception(ticker_history, mocker, caplog, default_conf) -> None:
+def test_download_backtesting_data_exception(ticker_history, mocker, caplog,
+                                             default_conf, testdatadir) -> None:
     mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv',
                  side_effect=Exception('File Error'))
 
@@ -320,7 +321,7 @@ def test_download_backtesting_data_exception(ticker_history, mocker, caplog, def
     _backup_file(file1_1)
     _backup_file(file1_5)
 
-    assert not download_pair_history(datadir=None, exchange=exchange,
+    assert not download_pair_history(datadir=testdatadir, exchange=exchange,
                                      pair='MEME/BTC',
                                      ticker_interval='1m')
     # clean files freshly downloaded
@@ -332,22 +333,22 @@ def test_download_backtesting_data_exception(ticker_history, mocker, caplog, def
     )
 
 
-def test_load_tickerdata_file() -> None:
+def test_load_tickerdata_file(testdatadir) -> None:
     # 7 does not exist in either format.
-    assert not load_tickerdata_file(None, 'UNITTEST/BTC', '7m')
+    assert not load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '7m')
     # 1 exists only as a .json
-    tickerdata = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
+    tickerdata = load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '1m')
     assert _BTC_UNITTEST_LENGTH == len(tickerdata)
     # 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json
-    tickerdata = load_tickerdata_file(None, 'UNITTEST/BTC', '8m')
+    tickerdata = load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '8m')
     assert _BTC_UNITTEST_LENGTH == len(tickerdata)
 
 
-def test_load_partial_missing(caplog) -> None:
+def test_load_partial_missing(testdatadir, caplog) -> None:
     # Make sure we start fresh - test missing data at start
     start = arrow.get('2018-01-01T00:00:00')
     end = arrow.get('2018-01-11T00:00:00')
-    tickerdata = history.load_data(None, '5m', ['UNITTEST/BTC'],
+    tickerdata = history.load_data(testdatadir, '5m', ['UNITTEST/BTC'],
                                    refresh_pairs=False,
                                    timerange=TimeRange('date', 'date',
                                                        start.timestamp, end.timestamp))
@@ -362,7 +363,7 @@ def test_load_partial_missing(caplog) -> None:
     caplog.clear()
     start = arrow.get('2018-01-10T00:00:00')
     end = arrow.get('2018-02-20T00:00:00')
-    tickerdata = history.load_data(datadir=None, ticker_interval='5m',
+    tickerdata = history.load_data(datadir=testdatadir, ticker_interval='5m',
                                    pairs=['UNITTEST/BTC'], refresh_pairs=False,
                                    timerange=TimeRange('date', 'date',
                                                        start.timestamp, end.timestamp))
@@ -502,13 +503,13 @@ def test_file_dump_json_tofile() -> None:
     _clean_test_file(file)
 
 
-def test_get_timeframe(default_conf, mocker) -> None:
+def test_get_timeframe(default_conf, mocker, testdatadir) -> None:
     patch_exchange(mocker)
     strategy = DefaultStrategy(default_conf)
 
     data = strategy.tickerdata_to_dataframe(
         history.load_data(
-            datadir=None,
+            datadir=testdatadir,
             ticker_interval='1m',
             pairs=['UNITTEST/BTC']
         )
@@ -518,13 +519,13 @@ def test_get_timeframe(default_conf, mocker) -> None:
     assert max_date.isoformat() == '2017-11-14T22:58:00+00:00'
 
 
-def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None:
+def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) -> None:
     patch_exchange(mocker)
     strategy = DefaultStrategy(default_conf)
 
     data = strategy.tickerdata_to_dataframe(
         history.load_data(
-            datadir=None,
+            datadir=testdatadir,
             ticker_interval='1m',
             pairs=['UNITTEST/BTC'],
             fill_up_missing=False
@@ -540,14 +541,14 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None:
         caplog)
 
 
-def test_validate_backtest_data(default_conf, mocker, caplog) -> None:
+def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> None:
     patch_exchange(mocker)
     strategy = DefaultStrategy(default_conf)
 
     timerange = TimeRange('index', 'index', 200, 250)
     data = strategy.tickerdata_to_dataframe(
         history.load_data(
-            datadir=None,
+            datadir=testdatadir,
             ticker_interval='5m',
             pairs=['UNITTEST/BTC'],
             timerange=timerange
@@ -561,7 +562,7 @@ def test_validate_backtest_data(default_conf, mocker, caplog) -> None:
     assert len(caplog.record_tuples) == 0
 
 
-def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog):
+def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog, testdatadir):
     dl_mock = mocker.patch('freqtrade.data.history.download_pair_history', MagicMock())
     mocker.patch(
         'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)
@@ -572,7 +573,7 @@ def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog):
     ex = get_patched_exchange(mocker, default_conf)
     timerange = TimeRange.parse_timerange("20190101-20190102")
     refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"],
-                                timeframes=["1m", "5m"], dl_path=make_testdata_path(None),
+                                timeframes=["1m", "5m"], dl_path=testdatadir,
                                 timerange=timerange, erase=True
                                 )
 
@@ -582,7 +583,7 @@ def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog):
     assert log_has("Downloading pair ETH/BTC, interval 1m.", caplog)
 
 
-def test_download_data_no_markets(mocker, default_conf, caplog):
+def test_download_data_no_markets(mocker, default_conf, caplog, testdatadir):
     dl_mock = mocker.patch('freqtrade.data.history.download_pair_history', MagicMock())
     mocker.patch(
         'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})
@@ -591,7 +592,7 @@ def test_download_data_no_markets(mocker, default_conf, caplog):
     timerange = TimeRange.parse_timerange("20190101-20190102")
     unav_pairs = refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"],
                                              timeframes=["1m", "5m"],
-                                             dl_path=make_testdata_path(None),
+                                             dl_path=testdatadir,
                                              timerange=timerange, erase=False
                                              )
 
diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py
index 52eae9df0..8e6459d8a 100644
--- a/freqtrade/tests/optimize/test_backtesting.py
+++ b/freqtrade/tests/optimize/test_backtesting.py
@@ -34,9 +34,9 @@ def trim_dictlist(dict_list, num):
     return new
 
 
-def load_data_test(what):
+def load_data_test(what, testdatadir):
     timerange = TimeRange(None, 'line', 0, -101)
-    pair = history.load_tickerdata_file(None, ticker_interval='1m',
+    pair = history.load_tickerdata_file(testdatadir, ticker_interval='1m',
                                         pair='UNITTEST/BTC', timerange=timerange)
     datalen = len(pair)
 
@@ -79,12 +79,12 @@ def load_data_test(what):
                                                    fill_missing=True)}
 
 
-def simple_backtest(config, contour, num_results, mocker) -> None:
+def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None:
     patch_exchange(mocker)
     config['ticker_interval'] = '1m'
     backtesting = Backtesting(config)
 
-    data = load_data_test(contour)
+    data = load_data_test(contour, testdatadir)
     processed = backtesting.strategy.tickerdata_to_dataframe(data)
     min_date, max_date = get_timeframe(processed)
     assert isinstance(processed, dict)
@@ -118,8 +118,8 @@ def _load_pair_as_ticks(pair, tickfreq):
 
 
 # FIX: fixturize this?
-def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
-    data = history.load_data(datadir=None, ticker_interval='1m', pairs=[pair])
+def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC', record=None):
+    data = history.load_data(datadir=datadir, ticker_interval='1m', pairs=[pair])
     data = trim_dictlist(data, -201)
     patch_exchange(mocker)
     backtesting = Backtesting(conf)
@@ -339,10 +339,10 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No
             "or as cli argument `--ticker-interval 5m`", caplog)
 
 
-def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None:
+def test_tickerdata_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
     patch_exchange(mocker)
     timerange = TimeRange(None, 'line', 0, -100)
-    tick = history.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
+    tick = history.load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '1m', timerange=timerange)
     tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', pair="UNITTEST/BTC",
                                                          fill_missing=True)}
 
@@ -456,7 +456,7 @@ def test_generate_text_table_strategyn(default_conf, mocker):
     assert backtesting._generate_text_table_strategy(all_results=results) == result_str
 
 
-def test_backtesting_start(default_conf, mocker, caplog) -> None:
+def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
     def get_timeframe(input1):
         return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
 
@@ -472,7 +472,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
 
     default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
     default_conf['ticker_interval'] = '1m'
-    default_conf['datadir'] = None
+    default_conf['datadir'] = testdatadir
     default_conf['export'] = None
     default_conf['timerange'] = '-100'
 
@@ -516,13 +516,13 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
     assert log_has('No data found. Terminating.', caplog)
 
 
-def test_backtest(default_conf, fee, mocker) -> None:
+def test_backtest(default_conf, fee, mocker, testdatadir) -> None:
     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
     patch_exchange(mocker)
     backtesting = Backtesting(default_conf)
     pair = 'UNITTEST/BTC'
     timerange = TimeRange(None, 'line', 0, -201)
-    data = history.load_data(datadir=None, ticker_interval='5m', pairs=['UNITTEST/BTC'],
+    data = history.load_data(datadir=testdatadir, ticker_interval='5m', pairs=['UNITTEST/BTC'],
                              timerange=timerange)
     data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
     min_date, max_date = get_timeframe(data_processed)
@@ -570,14 +570,14 @@ def test_backtest(default_conf, fee, mocker) -> None:
                 t["close_rate"], 6) < round(ln.iloc[0]["high"], 6))
 
 
-def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
+def test_backtest_1min_ticker_interval(default_conf, fee, mocker, testdatadir) -> None:
     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
     patch_exchange(mocker)
     backtesting = Backtesting(default_conf)
 
     # Run a backtesting for an exiting 1min ticker_interval
     timerange = TimeRange(None, 'line', 0, -200)
-    data = history.load_data(datadir=None, ticker_interval='1m', pairs=['UNITTEST/BTC'],
+    data = history.load_data(datadir=testdatadir, ticker_interval='1m', pairs=['UNITTEST/BTC'],
                              timerange=timerange)
     processed = backtesting.strategy.tickerdata_to_dataframe(data)
     min_date, max_date = get_timeframe(processed)
@@ -595,11 +595,11 @@ def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
     assert len(results) == 1
 
 
-def test_processed(default_conf, mocker) -> None:
+def test_processed(default_conf, mocker, testdatadir) -> None:
     patch_exchange(mocker)
     backtesting = Backtesting(default_conf)
 
-    dict_of_tickerrows = load_data_test('raise')
+    dict_of_tickerrows = load_data_test('raise', testdatadir)
     dataframes = backtesting.strategy.tickerdata_to_dataframe(dict_of_tickerrows)
     dataframe = dataframes['UNITTEST/BTC']
     cols = dataframe.columns
@@ -609,7 +609,7 @@ def test_processed(default_conf, mocker) -> None:
         assert col in cols
 
 
-def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
+def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir) -> None:
     # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic
     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
     tests = [['raise', 19], ['lower', 0], ['sine', 35]]
@@ -617,17 +617,17 @@ def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
     default_conf['experimental'] = {"use_sell_signal": True}
 
     for [contour, numres] in tests:
-        simple_backtest(default_conf, contour, numres, mocker)
+        simple_backtest(default_conf, contour, numres, mocker, testdatadir)
 
 
-def test_backtest_clash_buy_sell(mocker, default_conf):
+def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
     # Override the default buy trend function in our default_strategy
     def fun(dataframe=None, pair=None):
         buy_value = 1
         sell_value = 1
         return _trend(dataframe, buy_value, sell_value)
 
-    backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
+    backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
     backtesting = Backtesting(default_conf)
     backtesting.advise_buy = fun  # Override
     backtesting.advise_sell = fun  # Override
@@ -635,14 +635,14 @@ def test_backtest_clash_buy_sell(mocker, default_conf):
     assert results.empty
 
 
-def test_backtest_only_sell(mocker, default_conf):
+def test_backtest_only_sell(mocker, default_conf, testdatadir):
     # Override the default buy trend function in our default_strategy
     def fun(dataframe=None, pair=None):
         buy_value = 0
         sell_value = 1
         return _trend(dataframe, buy_value, sell_value)
 
-    backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
+    backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
     backtesting = Backtesting(default_conf)
     backtesting.advise_buy = fun  # Override
     backtesting.advise_sell = fun  # Override
@@ -650,10 +650,11 @@ def test_backtest_only_sell(mocker, default_conf):
     assert results.empty
 
 
-def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
+def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
     mocker.patch('freqtrade.optimize.backtesting.file_dump_json', MagicMock())
-    backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
+    backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
+                                        pair='UNITTEST/BTC', datadir=testdatadir)
     # We need to enable sell-signal - otherwise it sells on ROI!!
     default_conf['experimental'] = {"use_sell_signal": True}
     default_conf['ticker_interval'] = '1m'
@@ -672,7 +673,7 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
 
 @pytest.mark.parametrize("pair", ['ADA/BTC', 'LTC/BTC'])
 @pytest.mark.parametrize("tres", [0, 20, 30])
-def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair):
+def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir):
 
     def _trend_alternate_hold(dataframe=None, metadata=None):
         """
@@ -690,7 +691,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair):
     patch_exchange(mocker)
 
     pairs = ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC', 'NXT/BTC']
-    data = history.load_data(datadir=None, ticker_interval='5m', pairs=pairs)
+    data = history.load_data(datadir=testdatadir, ticker_interval='5m', pairs=pairs)
     # Only use 500 lines to increase performance
     data = trim_dictlist(data, -500)
 
diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py
index 976cb3d53..4e26a71f7 100644
--- a/freqtrade/tests/optimize/test_hyperopt.py
+++ b/freqtrade/tests/optimize/test_hyperopt.py
@@ -485,8 +485,8 @@ def test_has_space(hyperopt):
     assert hyperopt.has_space('buy')
 
 
-def test_populate_indicators(hyperopt) -> None:
-    tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
+def test_populate_indicators(hyperopt, testdatadir) -> None:
+    tick = load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '1m')
     tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', pair="UNITTEST/BTC",
                                                          fill_missing=True)}
     dataframes = hyperopt.backtesting.strategy.tickerdata_to_dataframe(tickerlist)
@@ -499,8 +499,8 @@ def test_populate_indicators(hyperopt) -> None:
     assert 'rsi' in dataframe
 
 
-def test_buy_strategy_generator(hyperopt) -> None:
-    tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
+def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
+    tick = load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '1m')
     tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', pair="UNITTEST/BTC",
                                                          fill_missing=True)}
     dataframes = hyperopt.backtesting.strategy.tickerdata_to_dataframe(tickerlist)
diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py
index 36c9ffcd4..1bd571870 100644
--- a/freqtrade/tests/strategy/test_interface.py
+++ b/freqtrade/tests/strategy/test_interface.py
@@ -103,11 +103,11 @@ def test_get_signal_handles_exceptions(mocker, default_conf):
     assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
 
 
-def test_tickerdata_to_dataframe(default_conf) -> None:
+def test_tickerdata_to_dataframe(default_conf, testdatadir) -> None:
     strategy = DefaultStrategy(default_conf)
 
     timerange = TimeRange(None, 'line', 0, -100)
-    tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
+    tick = load_tickerdata_file(testdatadir, 'UNITTEST/BTC', '1m', timerange=timerange)
     tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', pair="UNITTEST/BTC",
                                                          fill_missing=True)}
     data = strategy.tickerdata_to_dataframe(tickerlist)
diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py
index c55083e64..320ed208c 100644
--- a/freqtrade/tests/test_misc.py
+++ b/freqtrade/tests/test_misc.py
@@ -45,16 +45,16 @@ def test_file_dump_json(mocker) -> None:
     assert json_dump.call_count == 1
 
 
-def test_file_load_json(mocker) -> None:
+def test_file_load_json(mocker, testdatadir) -> None:
 
     # 7m .json does not exist
-    ret = file_load_json(pair_data_filename(None, 'UNITTEST/BTC', '7m'))
+    ret = file_load_json(pair_data_filename(testdatadir, 'UNITTEST/BTC', '7m'))
     assert not ret
     # 1m json exists (but no .gz exists)
-    ret = file_load_json(pair_data_filename(None, 'UNITTEST/BTC', '1m'))
+    ret = file_load_json(pair_data_filename(testdatadir, 'UNITTEST/BTC', '1m'))
     assert ret
     # 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json
-    ret = file_load_json(pair_data_filename(None, 'UNITTEST/BTC', '8m'))
+    ret = file_load_json(pair_data_filename(testdatadir, 'UNITTEST/BTC', '8m'))
     assert ret
 
 
diff --git a/freqtrade/tests/test_plotting.py b/freqtrade/tests/test_plotting.py
index e9079fff4..a4abda98b 100644
--- a/freqtrade/tests/test_plotting.py
+++ b/freqtrade/tests/test_plotting.py
@@ -19,8 +19,7 @@ from freqtrade.plot.plotting import (add_indicators, add_profit,
                                      generate_profit_graph, init_plotscript,
                                      plot_profit, plot_trades, store_plot_file)
 from freqtrade.strategy.default_strategy import DefaultStrategy
-from freqtrade.tests.conftest import (get_args, log_has, log_has_re,
-                                      make_testdata_path)
+from freqtrade.tests.conftest import get_args, log_has, log_has_re
 
 
 def fig_generating_mock(fig, *args, **kwargs):
@@ -43,12 +42,12 @@ def generage_empty_figure():
     )
 
 
-def test_init_plotscript(default_conf, mocker):
+def test_init_plotscript(default_conf, mocker, testdatadir):
     default_conf['timerange'] = "20180110-20180112"
     default_conf['trade_source'] = "file"
     default_conf['ticker_interval'] = "5m"
-    default_conf["datadir"] = make_testdata_path(None)
-    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = testdatadir
+    default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
     ret = init_plotscript(default_conf)
     assert "tickers" in ret
     assert "trades" in ret
@@ -61,12 +60,12 @@ def test_init_plotscript(default_conf, mocker):
     assert "XLM/BTC" in ret["tickers"]
 
 
-def test_add_indicators(default_conf, caplog):
+def test_add_indicators(default_conf, testdatadir, caplog):
     pair = "UNITTEST/BTC"
     timerange = TimeRange(None, 'line', 0, -1000)
 
     data = history.load_pair_history(pair=pair, ticker_interval='1m',
-                                     datadir=None, timerange=timerange)
+                                     datadir=testdatadir, timerange=timerange)
     indicators1 = ["ema10"]
     indicators2 = ["macd"]
 
@@ -94,14 +93,14 @@ def test_add_indicators(default_conf, caplog):
     assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
 
 
-def test_plot_trades(caplog):
+def test_plot_trades(testdatadir, caplog):
     fig1 = generage_empty_figure()
     # nothing happens when no trades are available
     fig = plot_trades(fig1, None)
     assert fig == fig1
     assert log_has("No trades found.", caplog)
     pair = "ADA/BTC"
-    filename = make_testdata_path(None) / "backtest-result_test.json"
+    filename = testdatadir / "backtest-result_test.json"
     trades = load_backtest_data(filename)
     trades = trades.loc[trades['pair'] == pair]
 
@@ -122,7 +121,7 @@ def test_plot_trades(caplog):
     assert trade_sell.marker.color == 'red'
 
 
-def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, caplog):
+def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
     row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators',
                             MagicMock(side_effect=fig_generating_mock))
     trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades',
@@ -131,7 +130,7 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, c
     pair = "UNITTEST/BTC"
     timerange = TimeRange(None, 'line', 0, -1000)
     data = history.load_pair_history(pair=pair, ticker_interval='1m',
-                                     datadir=None, timerange=timerange)
+                                     datadir=testdatadir, timerange=timerange)
     data['buy'] = 0
     data['sell'] = 0
 
@@ -158,7 +157,7 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, c
     assert log_has("No sell-signals found.", caplog)
 
 
-def test_generate_candlestick_graph_no_trades(default_conf, mocker):
+def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir):
     row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators',
                             MagicMock(side_effect=fig_generating_mock))
     trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades',
@@ -166,7 +165,7 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker):
     pair = 'UNITTEST/BTC'
     timerange = TimeRange(None, 'line', 0, -1000)
     data = history.load_pair_history(pair=pair, ticker_interval='1m',
-                                     datadir=None, timerange=timerange)
+                                     datadir=testdatadir, timerange=timerange)
 
     # Generate buy/sell signals and indicators
     strat = DefaultStrategy(default_conf)
@@ -224,13 +223,13 @@ def test_generate_plot_file(mocker, caplog):
                    caplog)
 
 
-def test_add_profit():
-    filename = make_testdata_path(None) / "backtest-result_test.json"
+def test_add_profit(testdatadir):
+    filename = testdatadir / "backtest-result_test.json"
     bt_data = load_backtest_data(filename)
     timerange = TimeRange.parse_timerange("20180110-20180112")
 
     df = history.load_pair_history(pair="POWR/BTC", ticker_interval='5m',
-                                   datadir=None, timerange=timerange)
+                                   datadir=testdatadir, timerange=timerange)
     fig = generage_empty_figure()
 
     cum_profits = create_cum_profit(df.set_index('date'),
@@ -244,13 +243,13 @@ def test_add_profit():
     assert profits.yaxis == "y2"
 
 
-def test_generate_profit_graph():
-    filename = make_testdata_path(None) / "backtest-result_test.json"
+def test_generate_profit_graph(testdatadir):
+    filename = testdatadir / "backtest-result_test.json"
     trades = load_backtest_data(filename)
     timerange = TimeRange.parse_timerange("20180110-20180112")
     pairs = ["POWR/BTC", "XLM/BTC"]
 
-    tickers = history.load_data(datadir=None,
+    tickers = history.load_data(datadir=testdatadir,
                                 pairs=pairs,
                                 ticker_interval='5m',
                                 timerange=timerange
@@ -294,10 +293,10 @@ def test_start_plot_dataframe(mocker):
     assert called_config['pairs'] == ["ETH/BTC"]
 
 
-def test_analyse_and_plot_pairs(default_conf, mocker, caplog):
+def test_analyse_and_plot_pairs(default_conf, mocker, caplog, testdatadir):
     default_conf['trade_source'] = 'file'
-    default_conf["datadir"] = make_testdata_path(None)
-    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = testdatadir
+    default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
     default_conf['indicators1'] = ["sma5", "ema10"]
     default_conf['indicators2'] = ["macd"]
     default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
@@ -345,10 +344,10 @@ def test_start_plot_profit_error(mocker):
         start_plot_profit(get_args(args))
 
 
-def test_plot_profit(default_conf, mocker, caplog):
+def test_plot_profit(default_conf, mocker, testdatadir, caplog):
     default_conf['trade_source'] = 'file'
-    default_conf["datadir"] = make_testdata_path(None)
-    default_conf['exportfilename'] = str(make_testdata_path(None) / "backtest-result_test.json")
+    default_conf["datadir"] = testdatadir
+    default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
     default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
 
     profit_mock = MagicMock()

From 972b8a17264770963d48c333a7fd92c69a61d2ee Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 7 Sep 2019 21:06:20 +0200
Subject: [PATCH 110/227] Remove defaulting to test_data folder when no datadir
 is present

---
 freqtrade/data/dataprovider.py               |  3 +--
 freqtrade/data/history.py                    | 22 +++++++-------------
 freqtrade/edge/__init__.py                   |  2 +-
 freqtrade/optimize/backtesting.py            |  2 +-
 freqtrade/optimize/hyperopt.py               |  2 +-
 freqtrade/tests/conftest.py                  |  3 ++-
 freqtrade/tests/data/test_dataprovider.py    |  1 -
 freqtrade/tests/edge/test_edge.py            |  3 ---
 freqtrade/tests/optimize/test_backtesting.py |  4 ++--
 9 files changed, 16 insertions(+), 26 deletions(-)

diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py
index 5b71c21a8..7e2a6b407 100644
--- a/freqtrade/data/dataprovider.py
+++ b/freqtrade/data/dataprovider.py
@@ -66,8 +66,7 @@ class DataProvider():
         return load_pair_history(pair=pair,
                                  ticker_interval=ticker_interval or self._config['ticker_interval'],
                                  refresh_pairs=False,
-                                 datadir=Path(self._config['datadir']) if self._config.get(
-                                     'datadir') else None
+                                 datadir=Path(self._config['datadir'])
                                  )
 
     def get_pair_dataframe(self, pair: str, ticker_interval: str = None) -> DataFrame:
diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py
index aff9f5c74..a71dc373c 100644
--- a/freqtrade/data/history.py
+++ b/freqtrade/data/history.py
@@ -57,7 +57,7 @@ def trim_tickerlist(tickerlist: List[Dict], timerange: TimeRange) -> List[Dict]:
     return tickerlist[start_index:stop_index]
 
 
-def load_tickerdata_file(datadir: Optional[Path], pair: str, ticker_interval: str,
+def load_tickerdata_file(datadir: Path, pair: str, ticker_interval: str,
                          timerange: Optional[TimeRange] = None) -> Optional[list]:
     """
     Load a pair from file, either .json.gz or .json
@@ -73,7 +73,7 @@ def load_tickerdata_file(datadir: Optional[Path], pair: str, ticker_interval: st
     return pairdata
 
 
-def store_tickerdata_file(datadir: Optional[Path], pair: str,
+def store_tickerdata_file(datadir: Path, pair: str,
                           ticker_interval: str, data: list, is_zip: bool = False):
     """
     Stores tickerdata to file
@@ -84,7 +84,7 @@ def store_tickerdata_file(datadir: Optional[Path], pair: str,
 
 def load_pair_history(pair: str,
                       ticker_interval: str,
-                      datadir: Optional[Path],
+                      datadir: Path,
                       timerange: TimeRange = TimeRange(None, None, 0, 0),
                       refresh_pairs: bool = False,
                       exchange: Optional[Exchange] = None,
@@ -135,7 +135,7 @@ def load_pair_history(pair: str,
         return None
 
 
-def load_data(datadir: Optional[Path],
+def load_data(datadir: Path,
               ticker_interval: str,
               pairs: List[str],
               refresh_pairs: bool = False,
@@ -172,19 +172,13 @@ def load_data(datadir: Optional[Path],
     return result
 
 
-def make_testdata_path(datadir: Optional[Path]) -> Path:
-    """Return the path where testdata files are stored"""
-    return datadir or (Path(__file__).parent.parent / "tests" / "testdata").resolve()
-
-
-def pair_data_filename(datadir: Optional[Path], pair: str, ticker_interval: str) -> Path:
-    path = make_testdata_path(datadir)
+def pair_data_filename(datadir: Path, pair: str, ticker_interval: str) -> Path:
     pair_s = pair.replace("/", "_")
-    filename = path.joinpath(f'{pair_s}-{ticker_interval}.json')
+    filename = datadir.joinpath(f'{pair_s}-{ticker_interval}.json')
     return filename
 
 
-def load_cached_data_for_updating(datadir: Optional[Path], pair: str, ticker_interval: str,
+def load_cached_data_for_updating(datadir: Path, pair: str, ticker_interval: str,
                                   timerange: Optional[TimeRange]) -> Tuple[List[Any],
                                                                            Optional[int]]:
     """
@@ -224,7 +218,7 @@ def load_cached_data_for_updating(datadir: Optional[Path], pair: str, ticker_int
     return (data, since_ms)
 
 
-def download_pair_history(datadir: Optional[Path],
+def download_pair_history(datadir: Path,
                           exchange: Optional[Exchange],
                           pair: str,
                           ticker_interval: str = '5m',
diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py
index 2d3097ec4..807fd8825 100644
--- a/freqtrade/edge/__init__.py
+++ b/freqtrade/edge/__init__.py
@@ -93,7 +93,7 @@ class Edge():
         logger.info('Using local backtesting data (using whitelist in given config) ...')
 
         data = history.load_data(
-            datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
+            datadir=Path(self.config['datadir']),
             pairs=pairs,
             ticker_interval=self.strategy.ticker_interval,
             refresh_pairs=self._refresh_pairs,
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 4fba47243..708b60144 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -407,7 +407,7 @@ class Backtesting(object):
         timerange = TimeRange.parse_timerange(None if self.config.get(
             'timerange') is None else str(self.config.get('timerange')))
         data = history.load_data(
-            datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
+            datadir=Path(self.config['datadir']),
             pairs=pairs,
             ticker_interval=self.ticker_interval,
             refresh_pairs=self.config.get('refresh_pairs', False),
diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index eaa9ced7d..bdd0ba258 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -349,7 +349,7 @@ class Hyperopt:
         timerange = TimeRange.parse_timerange(None if self.config.get(
             'timerange') is None else str(self.config.get('timerange')))
         data = load_data(
-            datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
+            datadir=Path(self.config['datadir']),
             pairs=self.config['exchange']['pair_whitelist'],
             ticker_interval=self.backtesting.ticker_interval,
             refresh_pairs=self.config.get('refresh_pairs', False),
diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py
index e7b8b386a..81358e003 100644
--- a/freqtrade/tests/conftest.py
+++ b/freqtrade/tests/conftest.py
@@ -182,7 +182,7 @@ def init_persistence(default_conf):
 
 
 @pytest.fixture(scope="function")
-def default_conf():
+def default_conf(testdatadir):
     """ Returns validated configuration suitable for most tests """
     configuration = {
         "max_open_trades": 1,
@@ -237,6 +237,7 @@ def default_conf():
             "token": "token",
             "chat_id": "0"
         },
+        "datadir": str(testdatadir),
         "initial_state": "running",
         "db_url": "sqlite://",
         "user_data_dir": Path("user_data"),
diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py
index 2272f69a3..441e242f8 100644
--- a/freqtrade/tests/data/test_dataprovider.py
+++ b/freqtrade/tests/data/test_dataprovider.py
@@ -45,7 +45,6 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history):
     data = dp.historic_ohlcv("UNITTEST/BTC", "5m")
     assert isinstance(data, DataFrame)
     assert historymock.call_count == 1
-    assert historymock.call_args_list[0][1]["datadir"] is None
     assert historymock.call_args_list[0][1]["refresh_pairs"] is False
     assert historymock.call_args_list[0][1]["ticker_interval"] == "5m"
 
diff --git a/freqtrade/tests/edge/test_edge.py b/freqtrade/tests/edge/test_edge.py
index 09fa1d93e..d30285a95 100644
--- a/freqtrade/tests/edge/test_edge.py
+++ b/freqtrade/tests/edge/test_edge.py
@@ -291,7 +291,6 @@ def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=Fals
 
 
 def test_edge_process_downloaded_data(mocker, edge_conf):
-    edge_conf['datadir'] = None
     freqtrade = get_patched_freqtradebot(mocker, edge_conf)
     mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
     mocker.patch('freqtrade.data.history.load_data', mocked_load_data)
@@ -303,7 +302,6 @@ def test_edge_process_downloaded_data(mocker, edge_conf):
 
 
 def test_edge_process_no_data(mocker, edge_conf, caplog):
-    edge_conf['datadir'] = None
     freqtrade = get_patched_freqtradebot(mocker, edge_conf)
     mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
     mocker.patch('freqtrade.data.history.load_data', MagicMock(return_value={}))
@@ -316,7 +314,6 @@ def test_edge_process_no_data(mocker, edge_conf, caplog):
 
 
 def test_edge_process_no_trades(mocker, edge_conf, caplog):
-    edge_conf['datadir'] = None
     freqtrade = get_patched_freqtradebot(mocker, edge_conf)
     mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
     mocker.patch('freqtrade.data.history.load_data', mocked_load_data)
diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py
index 8e6459d8a..b1b6dfa06 100644
--- a/freqtrade/tests/optimize/test_backtesting.py
+++ b/freqtrade/tests/optimize/test_backtesting.py
@@ -489,7 +489,7 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
         assert log_has(line, caplog)
 
 
-def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
+def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> None:
     def get_timeframe(input1):
         return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
 
@@ -505,7 +505,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
 
     default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
     default_conf['ticker_interval'] = "1m"
-    default_conf['datadir'] = None
+    default_conf['datadir'] = testdatadir
     default_conf['export'] = None
     default_conf['timerange'] = '20180101-20180102'
 

From bd2ecf8ce3e0f1a46be38a77b11b2ece2e9c9548 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 7 Sep 2019 21:13:05 +0200
Subject: [PATCH 111/227] Add testdatadir to missed test

---
 freqtrade/tests/data/test_history.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/freqtrade/tests/data/test_history.py b/freqtrade/tests/data/test_history.py
index 43429031c..252c73a4e 100644
--- a/freqtrade/tests/data/test_history.py
+++ b/freqtrade/tests/data/test_history.py
@@ -134,12 +134,12 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
     _clean_test_file(file)
 
 
-def test_load_data_live(default_conf, mocker, caplog) -> None:
+def test_load_data_live(default_conf, mocker, caplog, testdatadir) -> None:
     refresh_mock = MagicMock()
     mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock)
     exchange = get_patched_exchange(mocker, default_conf)
 
-    history.load_data(datadir=None, ticker_interval='5m',
+    history.load_data(datadir=testdatadir, ticker_interval='5m',
                       pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'],
                       live=True,
                       exchange=exchange)
@@ -148,11 +148,11 @@ def test_load_data_live(default_conf, mocker, caplog) -> None:
     assert log_has('Live: Downloading data for all defined pairs ...', caplog)
 
 
-def test_load_data_live_noexchange(default_conf, mocker, caplog) -> None:
+def test_load_data_live_noexchange(default_conf, mocker, caplog, testdatadir) -> None:
 
     with pytest.raises(OperationalException,
                        match=r'Exchange needs to be initialized when using live data.'):
-        history.load_data(datadir=None, ticker_interval='5m',
+        history.load_data(datadir=testdatadir, ticker_interval='5m',
                           pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'],
                           exchange=None,
                           live=True,

From 2b00a5d90a168df621731ea30687597ef2f44b12 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Sun, 8 Sep 2019 02:43:02 +0300
Subject: [PATCH 112/227] Get rid of import_strategy()

---
 freqtrade/optimize/__init__.py           | 10 ------
 freqtrade/resolvers/strategy_resolver.py |  8 +----
 freqtrade/strategy/__init__.py           | 42 ------------------------
 3 files changed, 1 insertion(+), 59 deletions(-)

diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py
index 2c7c42c4d..973ea1ff5 100644
--- a/freqtrade/optimize/__init__.py
+++ b/freqtrade/optimize/__init__.py
@@ -25,16 +25,6 @@ def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
             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
 
 
diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py
index 514e9f22b..ca7e1165b 100644
--- a/freqtrade/resolvers/strategy_resolver.py
+++ b/freqtrade/resolvers/strategy_resolver.py
@@ -13,7 +13,6 @@ from typing import Dict, Optional
 
 from freqtrade import constants, OperationalException
 from freqtrade.resolvers import IResolver
-from freqtrade.strategy import import_strategy
 from freqtrade.strategy.interface import IStrategy
 
 logger = logging.getLogger(__name__)
@@ -158,12 +157,7 @@ class StrategyResolver(IResolver):
                                      strategy._sell_fun_len]]):
                 strategy.INTERFACE_VERSION = 1
 
-            try:
-                return import_strategy(strategy, config=config)
-            except TypeError as e:
-                logger.warning(
-                    f"Impossible to load strategy '{strategy_name}'. "
-                    f"Error: {e}")
+            return strategy
 
         raise OperationalException(
             f"Impossible to load Strategy '{strategy_name}'. This class does not exist "
diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py
index c62bfe5dc..3fcc87ff3 100644
--- a/freqtrade/strategy/__init__.py
+++ b/freqtrade/strategy/__init__.py
@@ -1,45 +1,3 @@
-import logging
-import sys
-from copy import deepcopy
-
 from freqtrade.strategy.interface import IStrategy
 # Import Default-Strategy to have hyperopt correctly resolve
 from freqtrade.strategy.default_strategy import DefaultStrategy  # noqa: F401
-
-
-logger = logging.getLogger(__name__)
-
-
-def import_strategy(strategy: IStrategy, config: dict) -> IStrategy:
-    """
-    Imports given Strategy instance to global scope
-    of freqtrade.strategy and returns an instance of it
-    """
-
-    # Copy all attributes from base class and class
-    comb = {**strategy.__class__.__dict__, **strategy.__dict__}
-
-    # Delete '_abc_impl' from dict as deepcopy fails on 3.7 with
-    # `TypeError: can't pickle _abc_data objects``
-    # This will only apply to python 3.7
-    if sys.version_info.major == 3 and sys.version_info.minor == 7 and '_abc_impl' in comb:
-        del comb['_abc_impl']
-
-    attr = deepcopy(comb)
-
-    # Adjust module name
-    attr['__module__'] = 'freqtrade.strategy'
-
-    name = strategy.__class__.__name__
-    clazz = type(name, (IStrategy,), attr)
-
-    logger.debug(
-        'Imported strategy %s.%s as %s.%s',
-        strategy.__module__, strategy.__class__.__name__,
-        clazz.__module__, strategy.__class__.__name__,
-    )
-
-    # Modify global scope to declare class
-    globals()[name] = clazz
-
-    return clazz(config)

From 45cfdbbda7dc05f38cde8d411bde69838fcf1b9e Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Sun, 8 Sep 2019 03:10:01 +0300
Subject: [PATCH 113/227] Make flake happy

---
 freqtrade/strategy/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py
index 3fcc87ff3..8e5dab975 100644
--- a/freqtrade/strategy/__init__.py
+++ b/freqtrade/strategy/__init__.py
@@ -1,3 +1,3 @@
-from freqtrade.strategy.interface import IStrategy
+from freqtrade.strategy.interface import IStrategy  # noqa: F401
 # Import Default-Strategy to have hyperopt correctly resolve
 from freqtrade.strategy.default_strategy import DefaultStrategy  # noqa: F401

From 865e0d3af94ddbfa9195373ed9949bdb31659862 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Sun, 8 Sep 2019 03:19:21 +0300
Subject: [PATCH 114/227] Adjust tests: removed tests for/with
 import_strategy()

---
 freqtrade/tests/optimize/test_hyperopt.py | 20 +------------
 freqtrade/tests/strategy/test_strategy.py | 34 -----------------------
 2 files changed, 1 insertion(+), 53 deletions(-)

diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py
index 976cb3d53..fe7d9ccc4 100644
--- a/freqtrade/tests/optimize/test_hyperopt.py
+++ b/freqtrade/tests/optimize/test_hyperopt.py
@@ -9,7 +9,7 @@ from arrow import Arrow
 from filelock import Timeout
 from pathlib import Path
 
-from freqtrade import DependencyException, OperationalException
+from freqtrade import OperationalException
 from freqtrade.data.converter import parse_ticker_dataframe
 from freqtrade.data.history import load_tickerdata_file
 from freqtrade.optimize import setup_configuration, start_hyperopt
@@ -246,24 +246,6 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
     assert log_has('No data found. Terminating.', caplog)
 
 
-def test_start_failure(mocker, default_conf, caplog) -> None:
-    start_mock = MagicMock()
-    patched_configuration_load_config_file(mocker, default_conf)
-    mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
-    patch_exchange(mocker)
-
-    args = [
-        '--config', 'config.json',
-        '--strategy', 'SampleStrategy',
-        'hyperopt',
-        '--epochs', '5'
-    ]
-    args = get_args(args)
-    with pytest.raises(DependencyException):
-        start_hyperopt(args)
-    assert log_has("Please don't use --strategy for hyperopt.", caplog)
-
-
 def test_start_filelock(mocker, default_conf, caplog) -> None:
     start_mock = MagicMock(side_effect=Timeout(Hyperopt.get_lock_filename(default_conf)))
     patched_configuration_load_config_file(mocker, default_conf)
diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py
index cd1102ead..24ec830a0 100644
--- a/freqtrade/tests/strategy/test_strategy.py
+++ b/freqtrade/tests/strategy/test_strategy.py
@@ -5,40 +5,16 @@ import warnings
 from base64 import urlsafe_b64encode
 from os import path
 from pathlib import Path
-from unittest.mock import Mock
 
 import pytest
 from pandas import DataFrame
 
 from freqtrade import OperationalException
 from freqtrade.resolvers import StrategyResolver
-from freqtrade.strategy import import_strategy
-from freqtrade.strategy.default_strategy import DefaultStrategy
 from freqtrade.strategy.interface import IStrategy
 from freqtrade.tests.conftest import log_has, log_has_re
 
 
-def test_import_strategy(caplog):
-    caplog.set_level(logging.DEBUG)
-    default_config = {}
-
-    strategy = DefaultStrategy(default_config)
-    strategy.some_method = lambda *args, **kwargs: 42
-
-    assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
-    assert strategy.some_method() == 42
-
-    imported_strategy = import_strategy(strategy, default_config)
-
-    assert dir(strategy) == dir(imported_strategy)
-
-    assert imported_strategy.__module__ == 'freqtrade.strategy'
-    assert imported_strategy.some_method() == 42
-
-    assert log_has('Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy '
-                   'as freqtrade.strategy.DefaultStrategy', caplog)
-
-
 def test_search_strategy():
     default_config = {}
     default_location = Path(__file__).parent.parent.parent.joinpath('strategy').resolve()
@@ -96,16 +72,6 @@ def test_load_not_found_strategy(default_conf):
         strategy._load_strategy(strategy_name='NotFoundStrategy', config=default_conf)
 
 
-def test_load_staticmethod_importerror(mocker, caplog, default_conf):
-    mocker.patch("freqtrade.resolvers.strategy_resolver.import_strategy", Mock(
-        side_effect=TypeError("can't pickle staticmethod objects")))
-    with pytest.raises(OperationalException,
-                       match=r"Impossible to load Strategy 'DefaultStrategy'. "
-                             r"This class does not exist or contains Python code errors."):
-        StrategyResolver(default_conf)
-    assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog)
-
-
 def test_strategy(result, default_conf):
     default_conf.update({'strategy': 'DefaultStrategy'})
 

From bb2d8fefd74ee09304dbe22f4ff64ed3e684aa00 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 13 Dec 2018 19:28:20 +0100
Subject: [PATCH 115/227] Enhance setup.py

---
 setup.py | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index b48bddd56..307be079f 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,14 @@ if version_info.major == 3 and version_info.minor < 6 or \
     print('Your Python interpreter must be 3.6 or greater!')
     exit(1)
 
-from freqtrade import __version__
+from pathlib import Path  # noqa: E402
+from freqtrade import __version__  # noqa: E402
+
+
+readme_file = Path(__file__).parent / "README.md"
+readme_long = "Crypto Trading Bot"
+if readme_file.is_file():
+    readme_long = (Path(__file__).parent / "README.md").read_text()
 
 # Requirements used for submodules
 api = ['flask']
@@ -36,6 +43,8 @@ all_extra = api + plot + develop + jupyter
 setup(name='freqtrade',
       version=__version__,
       description='Crypto Trading Bot',
+      long_description=readme_long,
+      long_description_content_type="text/markdown",
       url='https://github.com/freqtrade/freqtrade',
       author='gcarq and contributors',
       author_email='michael.egger@tsn.at',

From 13932f55f576b5f31198f290310c3e07357611e5 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 14:02:32 +0200
Subject: [PATCH 116/227] Fix random test failures

---
 freqtrade/strategy/__init__.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py
index 8e5dab975..40a4a0bea 100644
--- a/freqtrade/strategy/__init__.py
+++ b/freqtrade/strategy/__init__.py
@@ -1,3 +1 @@
 from freqtrade.strategy.interface import IStrategy  # noqa: F401
-# Import Default-Strategy to have hyperopt correctly resolve
-from freqtrade.strategy.default_strategy import DefaultStrategy  # noqa: F401

From 65a516e2298089fd50798dc5445f8c0f0e23609c Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 09:42:28 +0200
Subject: [PATCH 117/227] Move tests out of freqtrade module

---
 {freqtrade/tests => tests}/__init__.py              |   0
 .../tests => tests}/config_test_comments.json       |   0
 {freqtrade/tests => tests}/conftest.py              |   0
 {freqtrade/tests => tests}/data/__init__.py         |   0
 {freqtrade/tests => tests}/data/test_btanalysis.py  |   0
 {freqtrade/tests => tests}/data/test_converter.py   |   0
 .../tests => tests}/data/test_dataprovider.py       |   0
 {freqtrade/tests => tests}/data/test_history.py     |   0
 {freqtrade/tests => tests}/edge/__init__.py         |   0
 {freqtrade/tests => tests}/edge/test_edge.py        |   0
 {freqtrade/tests => tests}/exchange/__init__.py     |   0
 {freqtrade/tests => tests}/exchange/test_binance.py |   0
 .../tests => tests}/exchange/test_exchange.py       |   0
 {freqtrade/tests => tests}/exchange/test_kraken.py  |   0
 {freqtrade/tests => tests}/optimize/__init__.py     |   0
 .../optimize/test_backtest_detail.py                |   0
 .../tests => tests}/optimize/test_backtesting.py    |   0
 .../tests => tests}/optimize/test_edge_cli.py       |   0
 .../tests => tests}/optimize/test_hyperopt.py       |   0
 {freqtrade/tests => tests}/pairlist/__init__.py     |   0
 .../tests => tests}/pairlist/test_pairlist.py       |   0
 {freqtrade/tests => tests}/rpc/__init__.py          |   0
 {freqtrade/tests => tests}/rpc/test_fiat_convert.py |   0
 {freqtrade/tests => tests}/rpc/test_rpc.py          |   0
 .../tests => tests}/rpc/test_rpc_apiserver.py       |   0
 {freqtrade/tests => tests}/rpc/test_rpc_manager.py  |   0
 {freqtrade/tests => tests}/rpc/test_rpc_telegram.py |   0
 {freqtrade/tests => tests}/rpc/test_rpc_webhook.py  |   0
 {freqtrade/tests => tests}/strategy/__init__.py     |   0
 .../tests => tests}/strategy/legacy_strategy.py     |   0
 .../strategy/test_default_strategy.py               |   0
 .../tests => tests}/strategy/test_interface.py      |   0
 .../tests => tests}/strategy/test_strategy.py       |   0
 {freqtrade/tests => tests}/test_arguments.py        |   0
 {freqtrade/tests => tests}/test_configuration.py    |   0
 {freqtrade/tests => tests}/test_freqtradebot.py     |   0
 .../tests => tests}/test_indicator_helpers.py       |   0
 {freqtrade/tests => tests}/test_main.py             |   0
 {freqtrade/tests => tests}/test_misc.py             |   0
 {freqtrade/tests => tests}/test_persistence.py      |   0
 {freqtrade/tests => tests}/test_plotting.py         |   0
 {freqtrade/tests => tests}/test_talib.py            |   0
 {freqtrade/tests => tests}/test_timerange.py        |   0
 {freqtrade/tests => tests}/test_utils.py            |   0
 {freqtrade/tests => tests}/test_wallets.py          |   0
 {freqtrade/tests => tests}/testdata/ADA_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/ADA_BTC-5m.json |   0
 .../tests => tests}/testdata/DASH_BTC-1m.json       |   0
 .../tests => tests}/testdata/DASH_BTC-5m.json       |   0
 {freqtrade/tests => tests}/testdata/ETC_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/ETC_BTC-5m.json |   0
 {freqtrade/tests => tests}/testdata/ETH_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/ETH_BTC-5m.json |   0
 {freqtrade/tests => tests}/testdata/LTC_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/LTC_BTC-5m.json |   0
 {freqtrade/tests => tests}/testdata/NXT_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/NXT_BTC-5m.json |   0
 .../tests => tests}/testdata/POWR_BTC-1m.json       |   0
 .../tests => tests}/testdata/POWR_BTC-5m.json       |   0
 .../tests => tests}/testdata/UNITTEST_BTC-1m.json   |   0
 .../tests => tests}/testdata/UNITTEST_BTC-30m.json  |   0
 .../tests => tests}/testdata/UNITTEST_BTC-5m.json   |   0
 .../tests => tests}/testdata/UNITTEST_BTC-8m.json   |   0
 .../testdata/UNITTEST_BTC-8m.json.gz                | Bin
 {freqtrade/tests => tests}/testdata/XLM_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/XLM_BTC-5m.json |   0
 {freqtrade/tests => tests}/testdata/XMR_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/XMR_BTC-5m.json |   0
 {freqtrade/tests => tests}/testdata/ZEC_BTC-1m.json |   0
 {freqtrade/tests => tests}/testdata/ZEC_BTC-5m.json |   0
 .../testdata/backtest-result_test.json              |   0
 {freqtrade/tests => tests}/testdata/pairs.json      |   0
 72 files changed, 0 insertions(+), 0 deletions(-)
 rename {freqtrade/tests => tests}/__init__.py (100%)
 rename {freqtrade/tests => tests}/config_test_comments.json (100%)
 rename {freqtrade/tests => tests}/conftest.py (100%)
 rename {freqtrade/tests => tests}/data/__init__.py (100%)
 rename {freqtrade/tests => tests}/data/test_btanalysis.py (100%)
 rename {freqtrade/tests => tests}/data/test_converter.py (100%)
 rename {freqtrade/tests => tests}/data/test_dataprovider.py (100%)
 rename {freqtrade/tests => tests}/data/test_history.py (100%)
 rename {freqtrade/tests => tests}/edge/__init__.py (100%)
 rename {freqtrade/tests => tests}/edge/test_edge.py (100%)
 rename {freqtrade/tests => tests}/exchange/__init__.py (100%)
 rename {freqtrade/tests => tests}/exchange/test_binance.py (100%)
 rename {freqtrade/tests => tests}/exchange/test_exchange.py (100%)
 rename {freqtrade/tests => tests}/exchange/test_kraken.py (100%)
 rename {freqtrade/tests => tests}/optimize/__init__.py (100%)
 rename {freqtrade/tests => tests}/optimize/test_backtest_detail.py (100%)
 rename {freqtrade/tests => tests}/optimize/test_backtesting.py (100%)
 rename {freqtrade/tests => tests}/optimize/test_edge_cli.py (100%)
 rename {freqtrade/tests => tests}/optimize/test_hyperopt.py (100%)
 rename {freqtrade/tests => tests}/pairlist/__init__.py (100%)
 rename {freqtrade/tests => tests}/pairlist/test_pairlist.py (100%)
 rename {freqtrade/tests => tests}/rpc/__init__.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_fiat_convert.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_rpc.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_rpc_apiserver.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_rpc_manager.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_rpc_telegram.py (100%)
 rename {freqtrade/tests => tests}/rpc/test_rpc_webhook.py (100%)
 rename {freqtrade/tests => tests}/strategy/__init__.py (100%)
 rename {freqtrade/tests => tests}/strategy/legacy_strategy.py (100%)
 rename {freqtrade/tests => tests}/strategy/test_default_strategy.py (100%)
 rename {freqtrade/tests => tests}/strategy/test_interface.py (100%)
 rename {freqtrade/tests => tests}/strategy/test_strategy.py (100%)
 rename {freqtrade/tests => tests}/test_arguments.py (100%)
 rename {freqtrade/tests => tests}/test_configuration.py (100%)
 rename {freqtrade/tests => tests}/test_freqtradebot.py (100%)
 rename {freqtrade/tests => tests}/test_indicator_helpers.py (100%)
 rename {freqtrade/tests => tests}/test_main.py (100%)
 rename {freqtrade/tests => tests}/test_misc.py (100%)
 rename {freqtrade/tests => tests}/test_persistence.py (100%)
 rename {freqtrade/tests => tests}/test_plotting.py (100%)
 rename {freqtrade/tests => tests}/test_talib.py (100%)
 rename {freqtrade/tests => tests}/test_timerange.py (100%)
 rename {freqtrade/tests => tests}/test_utils.py (100%)
 rename {freqtrade/tests => tests}/test_wallets.py (100%)
 rename {freqtrade/tests => tests}/testdata/ADA_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ADA_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/DASH_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/DASH_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ETC_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ETC_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ETH_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ETH_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/LTC_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/LTC_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/NXT_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/NXT_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/POWR_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/POWR_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/UNITTEST_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/UNITTEST_BTC-30m.json (100%)
 rename {freqtrade/tests => tests}/testdata/UNITTEST_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/UNITTEST_BTC-8m.json (100%)
 rename {freqtrade/tests => tests}/testdata/UNITTEST_BTC-8m.json.gz (100%)
 rename {freqtrade/tests => tests}/testdata/XLM_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/XLM_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/XMR_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/XMR_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ZEC_BTC-1m.json (100%)
 rename {freqtrade/tests => tests}/testdata/ZEC_BTC-5m.json (100%)
 rename {freqtrade/tests => tests}/testdata/backtest-result_test.json (100%)
 rename {freqtrade/tests => tests}/testdata/pairs.json (100%)

diff --git a/freqtrade/tests/__init__.py b/tests/__init__.py
similarity index 100%
rename from freqtrade/tests/__init__.py
rename to tests/__init__.py
diff --git a/freqtrade/tests/config_test_comments.json b/tests/config_test_comments.json
similarity index 100%
rename from freqtrade/tests/config_test_comments.json
rename to tests/config_test_comments.json
diff --git a/freqtrade/tests/conftest.py b/tests/conftest.py
similarity index 100%
rename from freqtrade/tests/conftest.py
rename to tests/conftest.py
diff --git a/freqtrade/tests/data/__init__.py b/tests/data/__init__.py
similarity index 100%
rename from freqtrade/tests/data/__init__.py
rename to tests/data/__init__.py
diff --git a/freqtrade/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py
similarity index 100%
rename from freqtrade/tests/data/test_btanalysis.py
rename to tests/data/test_btanalysis.py
diff --git a/freqtrade/tests/data/test_converter.py b/tests/data/test_converter.py
similarity index 100%
rename from freqtrade/tests/data/test_converter.py
rename to tests/data/test_converter.py
diff --git a/freqtrade/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py
similarity index 100%
rename from freqtrade/tests/data/test_dataprovider.py
rename to tests/data/test_dataprovider.py
diff --git a/freqtrade/tests/data/test_history.py b/tests/data/test_history.py
similarity index 100%
rename from freqtrade/tests/data/test_history.py
rename to tests/data/test_history.py
diff --git a/freqtrade/tests/edge/__init__.py b/tests/edge/__init__.py
similarity index 100%
rename from freqtrade/tests/edge/__init__.py
rename to tests/edge/__init__.py
diff --git a/freqtrade/tests/edge/test_edge.py b/tests/edge/test_edge.py
similarity index 100%
rename from freqtrade/tests/edge/test_edge.py
rename to tests/edge/test_edge.py
diff --git a/freqtrade/tests/exchange/__init__.py b/tests/exchange/__init__.py
similarity index 100%
rename from freqtrade/tests/exchange/__init__.py
rename to tests/exchange/__init__.py
diff --git a/freqtrade/tests/exchange/test_binance.py b/tests/exchange/test_binance.py
similarity index 100%
rename from freqtrade/tests/exchange/test_binance.py
rename to tests/exchange/test_binance.py
diff --git a/freqtrade/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
similarity index 100%
rename from freqtrade/tests/exchange/test_exchange.py
rename to tests/exchange/test_exchange.py
diff --git a/freqtrade/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py
similarity index 100%
rename from freqtrade/tests/exchange/test_kraken.py
rename to tests/exchange/test_kraken.py
diff --git a/freqtrade/tests/optimize/__init__.py b/tests/optimize/__init__.py
similarity index 100%
rename from freqtrade/tests/optimize/__init__.py
rename to tests/optimize/__init__.py
diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py
similarity index 100%
rename from freqtrade/tests/optimize/test_backtest_detail.py
rename to tests/optimize/test_backtest_detail.py
diff --git a/freqtrade/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
similarity index 100%
rename from freqtrade/tests/optimize/test_backtesting.py
rename to tests/optimize/test_backtesting.py
diff --git a/freqtrade/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py
similarity index 100%
rename from freqtrade/tests/optimize/test_edge_cli.py
rename to tests/optimize/test_edge_cli.py
diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
similarity index 100%
rename from freqtrade/tests/optimize/test_hyperopt.py
rename to tests/optimize/test_hyperopt.py
diff --git a/freqtrade/tests/pairlist/__init__.py b/tests/pairlist/__init__.py
similarity index 100%
rename from freqtrade/tests/pairlist/__init__.py
rename to tests/pairlist/__init__.py
diff --git a/freqtrade/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py
similarity index 100%
rename from freqtrade/tests/pairlist/test_pairlist.py
rename to tests/pairlist/test_pairlist.py
diff --git a/freqtrade/tests/rpc/__init__.py b/tests/rpc/__init__.py
similarity index 100%
rename from freqtrade/tests/rpc/__init__.py
rename to tests/rpc/__init__.py
diff --git a/freqtrade/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py
similarity index 100%
rename from freqtrade/tests/rpc/test_fiat_convert.py
rename to tests/rpc/test_fiat_convert.py
diff --git a/freqtrade/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
similarity index 100%
rename from freqtrade/tests/rpc/test_rpc.py
rename to tests/rpc/test_rpc.py
diff --git a/freqtrade/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
similarity index 100%
rename from freqtrade/tests/rpc/test_rpc_apiserver.py
rename to tests/rpc/test_rpc_apiserver.py
diff --git a/freqtrade/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py
similarity index 100%
rename from freqtrade/tests/rpc/test_rpc_manager.py
rename to tests/rpc/test_rpc_manager.py
diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
similarity index 100%
rename from freqtrade/tests/rpc/test_rpc_telegram.py
rename to tests/rpc/test_rpc_telegram.py
diff --git a/freqtrade/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py
similarity index 100%
rename from freqtrade/tests/rpc/test_rpc_webhook.py
rename to tests/rpc/test_rpc_webhook.py
diff --git a/freqtrade/tests/strategy/__init__.py b/tests/strategy/__init__.py
similarity index 100%
rename from freqtrade/tests/strategy/__init__.py
rename to tests/strategy/__init__.py
diff --git a/freqtrade/tests/strategy/legacy_strategy.py b/tests/strategy/legacy_strategy.py
similarity index 100%
rename from freqtrade/tests/strategy/legacy_strategy.py
rename to tests/strategy/legacy_strategy.py
diff --git a/freqtrade/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py
similarity index 100%
rename from freqtrade/tests/strategy/test_default_strategy.py
rename to tests/strategy/test_default_strategy.py
diff --git a/freqtrade/tests/strategy/test_interface.py b/tests/strategy/test_interface.py
similarity index 100%
rename from freqtrade/tests/strategy/test_interface.py
rename to tests/strategy/test_interface.py
diff --git a/freqtrade/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py
similarity index 100%
rename from freqtrade/tests/strategy/test_strategy.py
rename to tests/strategy/test_strategy.py
diff --git a/freqtrade/tests/test_arguments.py b/tests/test_arguments.py
similarity index 100%
rename from freqtrade/tests/test_arguments.py
rename to tests/test_arguments.py
diff --git a/freqtrade/tests/test_configuration.py b/tests/test_configuration.py
similarity index 100%
rename from freqtrade/tests/test_configuration.py
rename to tests/test_configuration.py
diff --git a/freqtrade/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
similarity index 100%
rename from freqtrade/tests/test_freqtradebot.py
rename to tests/test_freqtradebot.py
diff --git a/freqtrade/tests/test_indicator_helpers.py b/tests/test_indicator_helpers.py
similarity index 100%
rename from freqtrade/tests/test_indicator_helpers.py
rename to tests/test_indicator_helpers.py
diff --git a/freqtrade/tests/test_main.py b/tests/test_main.py
similarity index 100%
rename from freqtrade/tests/test_main.py
rename to tests/test_main.py
diff --git a/freqtrade/tests/test_misc.py b/tests/test_misc.py
similarity index 100%
rename from freqtrade/tests/test_misc.py
rename to tests/test_misc.py
diff --git a/freqtrade/tests/test_persistence.py b/tests/test_persistence.py
similarity index 100%
rename from freqtrade/tests/test_persistence.py
rename to tests/test_persistence.py
diff --git a/freqtrade/tests/test_plotting.py b/tests/test_plotting.py
similarity index 100%
rename from freqtrade/tests/test_plotting.py
rename to tests/test_plotting.py
diff --git a/freqtrade/tests/test_talib.py b/tests/test_talib.py
similarity index 100%
rename from freqtrade/tests/test_talib.py
rename to tests/test_talib.py
diff --git a/freqtrade/tests/test_timerange.py b/tests/test_timerange.py
similarity index 100%
rename from freqtrade/tests/test_timerange.py
rename to tests/test_timerange.py
diff --git a/freqtrade/tests/test_utils.py b/tests/test_utils.py
similarity index 100%
rename from freqtrade/tests/test_utils.py
rename to tests/test_utils.py
diff --git a/freqtrade/tests/test_wallets.py b/tests/test_wallets.py
similarity index 100%
rename from freqtrade/tests/test_wallets.py
rename to tests/test_wallets.py
diff --git a/freqtrade/tests/testdata/ADA_BTC-1m.json b/tests/testdata/ADA_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/ADA_BTC-1m.json
rename to tests/testdata/ADA_BTC-1m.json
diff --git a/freqtrade/tests/testdata/ADA_BTC-5m.json b/tests/testdata/ADA_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/ADA_BTC-5m.json
rename to tests/testdata/ADA_BTC-5m.json
diff --git a/freqtrade/tests/testdata/DASH_BTC-1m.json b/tests/testdata/DASH_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/DASH_BTC-1m.json
rename to tests/testdata/DASH_BTC-1m.json
diff --git a/freqtrade/tests/testdata/DASH_BTC-5m.json b/tests/testdata/DASH_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/DASH_BTC-5m.json
rename to tests/testdata/DASH_BTC-5m.json
diff --git a/freqtrade/tests/testdata/ETC_BTC-1m.json b/tests/testdata/ETC_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/ETC_BTC-1m.json
rename to tests/testdata/ETC_BTC-1m.json
diff --git a/freqtrade/tests/testdata/ETC_BTC-5m.json b/tests/testdata/ETC_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/ETC_BTC-5m.json
rename to tests/testdata/ETC_BTC-5m.json
diff --git a/freqtrade/tests/testdata/ETH_BTC-1m.json b/tests/testdata/ETH_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/ETH_BTC-1m.json
rename to tests/testdata/ETH_BTC-1m.json
diff --git a/freqtrade/tests/testdata/ETH_BTC-5m.json b/tests/testdata/ETH_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/ETH_BTC-5m.json
rename to tests/testdata/ETH_BTC-5m.json
diff --git a/freqtrade/tests/testdata/LTC_BTC-1m.json b/tests/testdata/LTC_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/LTC_BTC-1m.json
rename to tests/testdata/LTC_BTC-1m.json
diff --git a/freqtrade/tests/testdata/LTC_BTC-5m.json b/tests/testdata/LTC_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/LTC_BTC-5m.json
rename to tests/testdata/LTC_BTC-5m.json
diff --git a/freqtrade/tests/testdata/NXT_BTC-1m.json b/tests/testdata/NXT_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/NXT_BTC-1m.json
rename to tests/testdata/NXT_BTC-1m.json
diff --git a/freqtrade/tests/testdata/NXT_BTC-5m.json b/tests/testdata/NXT_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/NXT_BTC-5m.json
rename to tests/testdata/NXT_BTC-5m.json
diff --git a/freqtrade/tests/testdata/POWR_BTC-1m.json b/tests/testdata/POWR_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/POWR_BTC-1m.json
rename to tests/testdata/POWR_BTC-1m.json
diff --git a/freqtrade/tests/testdata/POWR_BTC-5m.json b/tests/testdata/POWR_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/POWR_BTC-5m.json
rename to tests/testdata/POWR_BTC-5m.json
diff --git a/freqtrade/tests/testdata/UNITTEST_BTC-1m.json b/tests/testdata/UNITTEST_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/UNITTEST_BTC-1m.json
rename to tests/testdata/UNITTEST_BTC-1m.json
diff --git a/freqtrade/tests/testdata/UNITTEST_BTC-30m.json b/tests/testdata/UNITTEST_BTC-30m.json
similarity index 100%
rename from freqtrade/tests/testdata/UNITTEST_BTC-30m.json
rename to tests/testdata/UNITTEST_BTC-30m.json
diff --git a/freqtrade/tests/testdata/UNITTEST_BTC-5m.json b/tests/testdata/UNITTEST_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/UNITTEST_BTC-5m.json
rename to tests/testdata/UNITTEST_BTC-5m.json
diff --git a/freqtrade/tests/testdata/UNITTEST_BTC-8m.json b/tests/testdata/UNITTEST_BTC-8m.json
similarity index 100%
rename from freqtrade/tests/testdata/UNITTEST_BTC-8m.json
rename to tests/testdata/UNITTEST_BTC-8m.json
diff --git a/freqtrade/tests/testdata/UNITTEST_BTC-8m.json.gz b/tests/testdata/UNITTEST_BTC-8m.json.gz
similarity index 100%
rename from freqtrade/tests/testdata/UNITTEST_BTC-8m.json.gz
rename to tests/testdata/UNITTEST_BTC-8m.json.gz
diff --git a/freqtrade/tests/testdata/XLM_BTC-1m.json b/tests/testdata/XLM_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/XLM_BTC-1m.json
rename to tests/testdata/XLM_BTC-1m.json
diff --git a/freqtrade/tests/testdata/XLM_BTC-5m.json b/tests/testdata/XLM_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/XLM_BTC-5m.json
rename to tests/testdata/XLM_BTC-5m.json
diff --git a/freqtrade/tests/testdata/XMR_BTC-1m.json b/tests/testdata/XMR_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/XMR_BTC-1m.json
rename to tests/testdata/XMR_BTC-1m.json
diff --git a/freqtrade/tests/testdata/XMR_BTC-5m.json b/tests/testdata/XMR_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/XMR_BTC-5m.json
rename to tests/testdata/XMR_BTC-5m.json
diff --git a/freqtrade/tests/testdata/ZEC_BTC-1m.json b/tests/testdata/ZEC_BTC-1m.json
similarity index 100%
rename from freqtrade/tests/testdata/ZEC_BTC-1m.json
rename to tests/testdata/ZEC_BTC-1m.json
diff --git a/freqtrade/tests/testdata/ZEC_BTC-5m.json b/tests/testdata/ZEC_BTC-5m.json
similarity index 100%
rename from freqtrade/tests/testdata/ZEC_BTC-5m.json
rename to tests/testdata/ZEC_BTC-5m.json
diff --git a/freqtrade/tests/testdata/backtest-result_test.json b/tests/testdata/backtest-result_test.json
similarity index 100%
rename from freqtrade/tests/testdata/backtest-result_test.json
rename to tests/testdata/backtest-result_test.json
diff --git a/freqtrade/tests/testdata/pairs.json b/tests/testdata/pairs.json
similarity index 100%
rename from freqtrade/tests/testdata/pairs.json
rename to tests/testdata/pairs.json

From 26d76cdb196ec01e89d487610120c173d89879b2 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 09:54:15 +0200
Subject: [PATCH 118/227] Adjust imports in tests to new path

---
 tests/conftest.py                       |  4 ++--
 tests/data/test_btanalysis.py           |  2 +-
 tests/data/test_converter.py            |  2 +-
 tests/data/test_dataprovider.py         |  2 +-
 tests/data/test_history.py              |  2 +-
 tests/edge/test_edge.py                 |  7 +++----
 tests/exchange/test_binance.py          |  2 +-
 tests/exchange/test_exchange.py         |  2 +-
 tests/exchange/test_kraken.py           |  2 +-
 tests/optimize/test_backtest_detail.py  |  8 +++-----
 tests/optimize/test_backtesting.py      | 17 ++++++++---------
 tests/optimize/test_edge_cli.py         |  5 ++---
 tests/optimize/test_hyperopt.py         | 14 +++++++-------
 tests/pairlist/test_pairlist.py         |  2 +-
 tests/rpc/test_fiat_convert.py          |  2 +-
 tests/rpc/test_rpc.py                   |  2 +-
 tests/rpc/test_rpc_apiserver.py         |  4 +---
 tests/rpc/test_rpc_manager.py           |  2 +-
 tests/rpc/test_rpc_telegram.py          |  4 ++--
 tests/rpc/test_rpc_webhook.py           |  2 +-
 tests/strategy/test_default_strategy.py | 11 -----------
 tests/strategy/test_interface.py        |  2 +-
 tests/strategy/test_strategy.py         |  4 ++--
 tests/test_configuration.py             |  7 ++++---
 tests/test_freqtradebot.py              |  9 ++++-----
 tests/test_main.py                      |  2 +-
 tests/test_persistence.py               |  2 +-
 tests/test_plotting.py                  |  2 +-
 tests/test_utils.py                     |  2 +-
 tests/test_wallets.py                   |  2 +-
 30 files changed, 56 insertions(+), 74 deletions(-)

diff --git a/tests/conftest.py b/tests/conftest.py
index 81358e003..a007aeb7e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -891,8 +891,8 @@ def tickers():
 
 
 @pytest.fixture
-def result():
-    with Path('freqtrade/tests/testdata/UNITTEST_BTC-1m.json').open('r') as data_file:
+def result(testdatadir):
+    with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file:
         return parse_ticker_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
                                       fill_missing=True)
 
diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py
index e95757d4e..18326226c 100644
--- a/tests/data/test_btanalysis.py
+++ b/tests/data/test_btanalysis.py
@@ -12,7 +12,7 @@ from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
                                        load_backtest_data, load_trades,
                                        load_trades_from_db)
 from freqtrade.data.history import load_data, load_pair_history
-from freqtrade.tests.test_persistence import create_mock_trades
+from tests.test_persistence import create_mock_trades
 
 
 def test_load_backtest_data(testdatadir):
diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py
index d47c3ee92..72da47e76 100644
--- a/tests/data/test_converter.py
+++ b/tests/data/test_converter.py
@@ -3,7 +3,7 @@ import logging
 
 from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data
 from freqtrade.data.history import load_pair_history, validate_backtest_data, get_timeframe
-from freqtrade.tests.conftest import log_has
+from tests.conftest import log_has
 
 
 def test_dataframe_correct_columns(result):
diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py
index 441e242f8..ec176d889 100644
--- a/tests/data/test_dataprovider.py
+++ b/tests/data/test_dataprovider.py
@@ -4,7 +4,7 @@ from pandas import DataFrame
 
 from freqtrade.data.dataprovider import DataProvider
 from freqtrade.state import RunMode
-from freqtrade.tests.conftest import get_patched_exchange
+from tests.conftest import get_patched_exchange
 
 
 def test_ohlcv(mocker, default_conf, ticker_history):
diff --git a/tests/data/test_history.py b/tests/data/test_history.py
index 252c73a4e..857967f39 100644
--- a/tests/data/test_history.py
+++ b/tests/data/test_history.py
@@ -22,7 +22,7 @@ from freqtrade.data.history import (download_pair_history,
 from freqtrade.exchange import timeframe_to_minutes
 from freqtrade.misc import file_dump_json
 from freqtrade.strategy.default_strategy import DefaultStrategy
-from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re, patch_exchange
+from tests.conftest import get_patched_exchange, log_has, log_has_re, patch_exchange
 
 # Change this if modifying UNITTEST/BTC testdatafile
 _BTC_UNITTEST_LENGTH = 13681
diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py
index d30285a95..d5f282d7b 100644
--- a/tests/edge/test_edge.py
+++ b/tests/edge/test_edge.py
@@ -14,10 +14,9 @@ from freqtrade import OperationalException
 from freqtrade.data.converter import parse_ticker_dataframe
 from freqtrade.edge import Edge, PairInfo
 from freqtrade.strategy.interface import SellType
-from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
-from freqtrade.tests.optimize import (BTContainer, BTrade,
-                                      _build_backtest_dataframe,
-                                      _get_frame_time_from_offset)
+from tests.conftest import get_patched_freqtradebot, log_has
+from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
+                            _get_frame_time_from_offset)
 
 # Cases to be tested:
 # 1) Open trade should be removed from the end
diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py
index 6518c8523..7720a7d2e 100644
--- a/tests/exchange/test_binance.py
+++ b/tests/exchange/test_binance.py
@@ -6,7 +6,7 @@ import pytest
 
 from freqtrade import (DependencyException, InvalidOrderException,
                        OperationalException, TemporaryError)
-from freqtrade.tests.conftest import get_patched_exchange
+from tests.conftest import get_patched_exchange
 
 
 def test_stoploss_limit_order(default_conf, mocker):
diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index 1fd045f54..e23db42d2 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -20,7 +20,7 @@ from freqtrade.exchange.exchange import (API_RETRY_COUNT, timeframe_to_minutes,
                                          timeframe_to_prev_date,
                                          timeframe_to_seconds)
 from freqtrade.resolvers.exchange_resolver import ExchangeResolver
-from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
+from tests.conftest import get_patched_exchange, log_has, log_has_re
 
 # Make sure to always keep one exchange here which is NOT subclassed!!
 EXCHANGES = ['bittrex', 'binance', 'kraken', ]
diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py
index 8f476affb..ba94f8b45 100644
--- a/tests/exchange/test_kraken.py
+++ b/tests/exchange/test_kraken.py
@@ -3,7 +3,7 @@
 from random import randint
 from unittest.mock import MagicMock
 
-from freqtrade.tests.conftest import get_patched_exchange
+from tests.conftest import get_patched_exchange
 
 
 def test_buy_kraken_trading_agreement(default_conf, mocker):
diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py
index 87f567b4f..8cd88a479 100644
--- a/tests/optimize/test_backtest_detail.py
+++ b/tests/optimize/test_backtest_detail.py
@@ -8,11 +8,9 @@ from pandas import DataFrame
 from freqtrade.data.history import get_timeframe
 from freqtrade.optimize.backtesting import Backtesting
 from freqtrade.strategy.interface import SellType
-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)
+from tests.conftest import patch_exchange
+from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
+                            _get_frame_time_from_offset, tests_ticker_interval)
 
 # Test 0: Sell with signal sell in candle 3
 # Test with Stop-loss at 1%
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index b1b6dfa06..34c0322cb 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -22,9 +22,8 @@ from freqtrade.optimize.backtesting import Backtesting
 from freqtrade.state import RunMode
 from freqtrade.strategy.default_strategy import DefaultStrategy
 from freqtrade.strategy.interface import SellType
-from freqtrade.tests.conftest import (get_args, log_has, log_has_re,
-                                      patch_exchange,
-                                      patched_configuration_load_config_file)
+from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
+                            patched_configuration_load_config_file)
 
 
 def trim_dictlist(dict_list, num):
@@ -807,7 +806,7 @@ def test_backtest_record(default_conf, fee, mocker):
         assert dur > 0
 
 
-def test_backtest_start_timerange(default_conf, mocker, caplog):
+def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
     default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
 
     async def load_pairs(pair, timeframe, since):
@@ -824,7 +823,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog):
     args = [
         '--config', 'config.json',
         '--strategy', 'DefaultStrategy',
-        '--datadir', 'freqtrade/tests/testdata',
+        '--datadir', str(testdatadir),
         'backtesting',
         '--ticker-interval', '1m',
         '--timerange', '-100',
@@ -838,7 +837,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog):
         'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
         'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
         'Parameter --timerange detected: -100 ...',
-        'Using data directory: freqtrade/tests/testdata ...',
+        f'Using data directory: {testdatadir} ...',
         'Using stake_currency: BTC ...',
         'Using stake_amount: 0.001 ...',
         'Backtesting with data from 2017-11-14T21:17:00+00:00 '
@@ -850,7 +849,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog):
         assert log_has(line, caplog)
 
 
-def test_backtest_start_multi_strat(default_conf, mocker, caplog):
+def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
     default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
 
     async def load_pairs(pair, timeframe, since):
@@ -870,7 +869,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
 
     args = [
         '--config', 'config.json',
-        '--datadir', 'freqtrade/tests/testdata',
+        '--datadir', str(testdatadir),
         'backtesting',
         '--ticker-interval', '1m',
         '--timerange', '-100',
@@ -892,7 +891,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
         'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
         'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
         'Parameter --timerange detected: -100 ...',
-        'Using data directory: freqtrade/tests/testdata ...',
+        f'Using data directory: {testdatadir} ...',
         'Using stake_currency: BTC ...',
         'Using stake_amount: 0.001 ...',
         'Backtesting with data from 2017-11-14T21:17:00+00:00 '
diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py
index 25ad48e43..f312bba2c 100644
--- a/tests/optimize/test_edge_cli.py
+++ b/tests/optimize/test_edge_cli.py
@@ -9,9 +9,8 @@ from freqtrade.edge import PairInfo
 from freqtrade.optimize import setup_configuration, start_edge
 from freqtrade.optimize.edge_cli import EdgeCli
 from freqtrade.state import RunMode
-from freqtrade.tests.conftest import (get_args, log_has, log_has_re,
-                                      patch_exchange,
-                                      patched_configuration_load_config_file)
+from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
+                            patched_configuration_load_config_file)
 
 
 def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index a6f0e47e7..eec9e7c19 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -1,13 +1,13 @@
 # pragma pylint: disable=missing-docstring,W0212,C0103
 import os
 from datetime import datetime
+from pathlib import Path
 from unittest.mock import MagicMock, PropertyMock
 
 import pandas as pd
 import pytest
 from arrow import Arrow
 from filelock import Timeout
-from pathlib import Path
 
 from freqtrade import OperationalException
 from freqtrade.data.converter import parse_ticker_dataframe
@@ -16,12 +16,12 @@ from freqtrade.optimize import setup_configuration, start_hyperopt
 from freqtrade.optimize.default_hyperopt import DefaultHyperOpts
 from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
 from freqtrade.optimize.hyperopt import Hyperopt
-from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver, HyperOptLossResolver
+from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver,
+                                                   HyperOptResolver)
 from freqtrade.state import RunMode
 from freqtrade.strategy.interface import SellType
-from freqtrade.tests.conftest import (get_args, log_has, log_has_re,
-                                      patch_exchange,
-                                      patched_configuration_load_config_file)
+from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
+                            patched_configuration_load_config_file)
 
 
 @pytest.fixture(scope='function')
@@ -47,14 +47,14 @@ def hyperopt_results():
 
 
 # Functions for recurrent object patching
-def create_trials(mocker, hyperopt) -> None:
+def create_trials(mocker, hyperopt, testdatadir) -> None:
     """
     When creating trials, mock the hyperopt Trials so that *by default*
       - we don't create any pickle'd files in the filesystem
       - we might have a pickle'd file so make sure that we return
         false when looking for it
     """
-    hyperopt.trials_file = Path('freqtrade/tests/optimize/ut_trials.pickle')
+    hyperopt.trials_file = testdatadir / '/optimize/ut_trials.pickle'
 
     mocker.patch.object(Path, "is_file", MagicMock(return_value=False))
     stat_mock = MagicMock()
diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py
index e7439bb51..411ae60a3 100644
--- a/tests/pairlist/test_pairlist.py
+++ b/tests/pairlist/test_pairlist.py
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock, PropertyMock
 from freqtrade import OperationalException
 from freqtrade.constants import AVAILABLE_PAIRLISTS
 from freqtrade.resolvers import PairListResolver
-from freqtrade.tests.conftest import get_patched_freqtradebot
+from tests.conftest import get_patched_freqtradebot
 import pytest
 
 # whitelist, blacklist
diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py
index 1689ecac6..8f6b2eb93 100644
--- a/tests/rpc/test_fiat_convert.py
+++ b/tests/rpc/test_fiat_convert.py
@@ -8,7 +8,7 @@ import pytest
 from requests.exceptions import RequestException
 
 from freqtrade.rpc.fiat_convert import CryptoFiat, CryptoToFiatConverter
-from freqtrade.tests.conftest import log_has
+from tests.conftest import log_has
 
 
 def test_pair_convertion_object():
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index 47ed34a58..ff5869009 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -14,7 +14,7 @@ from freqtrade.persistence import Trade
 from freqtrade.rpc import RPC, RPCException
 from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
 from freqtrade.state import State
-from freqtrade.tests.conftest import patch_exchange, patch_get_signal
+from tests.conftest import patch_exchange, patch_get_signal
 
 
 # Functions for recurrent object patching
diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
index bcbbb228f..b572a0514 100644
--- a/tests/rpc/test_rpc_apiserver.py
+++ b/tests/rpc/test_rpc_apiserver.py
@@ -13,9 +13,7 @@ from freqtrade.__init__ import __version__
 from freqtrade.persistence import Trade
 from freqtrade.rpc.api_server import BASE_URI, ApiServer
 from freqtrade.state import State
-from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has,
-                                      patch_get_signal)
-
+from tests.conftest import get_patched_freqtradebot, log_has, patch_get_signal
 
 _TEST_USER = "FreqTrader"
 _TEST_PASS = "SuperSecurePassword1!"
diff --git a/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py
index d34d76524..7278f0671 100644
--- a/tests/rpc/test_rpc_manager.py
+++ b/tests/rpc/test_rpc_manager.py
@@ -4,7 +4,7 @@ import logging
 from unittest.mock import MagicMock
 
 from freqtrade.rpc import RPCMessageType, RPCManager
-from freqtrade.tests.conftest import log_has, get_patched_freqtradebot
+from tests.conftest import log_has, get_patched_freqtradebot
 
 
 def test__init__(mocker, default_conf) -> None:
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 3930b110d..cf741a7ea 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -21,8 +21,8 @@ from freqtrade.rpc import RPCMessageType
 from freqtrade.rpc.telegram import Telegram, authorized_only
 from freqtrade.state import State
 from freqtrade.strategy.interface import SellType
-from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has,
-                                      patch_exchange, patch_get_signal)
+from tests.conftest import (get_patched_freqtradebot, log_has, patch_exchange,
+                            patch_get_signal)
 
 
 class DummyCls(Telegram):
diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py
index 1c6c07e16..dbbc4cefb 100644
--- a/tests/rpc/test_rpc_webhook.py
+++ b/tests/rpc/test_rpc_webhook.py
@@ -8,7 +8,7 @@ from requests import RequestException
 from freqtrade.rpc import RPCMessageType
 from freqtrade.rpc.webhook import Webhook
 from freqtrade.strategy.interface import SellType
-from freqtrade.tests.conftest import get_patched_freqtradebot, log_has
+from tests.conftest import get_patched_freqtradebot, log_has
 
 
 def get_webhook_dict() -> dict:
diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py
index 74c81882a..17d6b8ee0 100644
--- a/tests/strategy/test_default_strategy.py
+++ b/tests/strategy/test_default_strategy.py
@@ -1,19 +1,8 @@
-import json
-
-import pytest
 from pandas import DataFrame
 
-from freqtrade.data.converter import parse_ticker_dataframe
 from freqtrade.strategy.default_strategy import DefaultStrategy
 
 
-@pytest.fixture
-def result():
-    with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file:
-        return parse_ticker_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
-                                      fill_missing=True)
-
-
 def test_default_strategy_structure():
     assert hasattr(DefaultStrategy, 'minimal_roi')
     assert hasattr(DefaultStrategy, 'stoploss')
diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py
index 1bd571870..094cd41a1 100644
--- a/tests/strategy/test_interface.py
+++ b/tests/strategy/test_interface.py
@@ -10,7 +10,7 @@ from freqtrade.configuration import TimeRange
 from freqtrade.data.converter import parse_ticker_dataframe
 from freqtrade.data.history import load_tickerdata_file
 from freqtrade.persistence import Trade
-from freqtrade.tests.conftest import get_patched_exchange, log_has
+from tests.conftest import get_patched_exchange, log_has
 from freqtrade.strategy.default_strategy import DefaultStrategy
 
 # Avoid to reinit the same object again and again
diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py
index 24ec830a0..6992d1aa5 100644
--- a/tests/strategy/test_strategy.py
+++ b/tests/strategy/test_strategy.py
@@ -12,12 +12,12 @@ from pandas import DataFrame
 from freqtrade import OperationalException
 from freqtrade.resolvers import StrategyResolver
 from freqtrade.strategy.interface import IStrategy
-from freqtrade.tests.conftest import log_has, log_has_re
+from tests.conftest import log_has, log_has_re
 
 
 def test_search_strategy():
     default_config = {}
-    default_location = Path(__file__).parent.parent.parent.joinpath('strategy').resolve()
+    default_location = Path(__file__).parent.parent.joinpath('strategy').resolve()
 
     s, _ = StrategyResolver._search_object(
         directory=default_location,
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index faf0e4c5f..e6532cecc 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -10,7 +10,8 @@ import pytest
 from jsonschema import Draft4Validator, ValidationError, validate
 
 from freqtrade import OperationalException, constants
-from freqtrade.configuration import Arguments, Configuration, validate_config_consistency
+from freqtrade.configuration import (Arguments, Configuration,
+                                     validate_config_consistency)
 from freqtrade.configuration.check_exchange import check_exchange
 from freqtrade.configuration.config_validation import validate_config_schema
 from freqtrade.configuration.directory_operations import (create_datadir,
@@ -19,8 +20,8 @@ from freqtrade.configuration.load_config import load_config_file
 from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
 from freqtrade.loggers import _set_loggers
 from freqtrade.state import RunMode
-from freqtrade.tests.conftest import (log_has, log_has_re,
-                                      patched_configuration_load_config_file)
+from tests.conftest import (log_has, log_has_re,
+                            patched_configuration_load_config_file)
 
 
 @pytest.fixture(scope="function")
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 83b1d3107..4ffa863e6 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -16,13 +16,12 @@ from freqtrade.data.dataprovider import DataProvider
 from freqtrade.freqtradebot import FreqtradeBot
 from freqtrade.persistence import Trade
 from freqtrade.rpc import RPCMessageType
-from freqtrade.state import State, RunMode
+from freqtrade.state import RunMode, State
 from freqtrade.strategy.interface import SellCheckTuple, SellType
-from freqtrade.tests.conftest import (get_patched_freqtradebot,
-                                      get_patched_worker, log_has, log_has_re,
-                                      patch_edge, patch_exchange,
-                                      patch_get_signal, patch_wallet)
 from freqtrade.worker import Worker
+from tests.conftest import (get_patched_freqtradebot, get_patched_worker,
+                            log_has, log_has_re, patch_edge, patch_exchange,
+                            patch_get_signal, patch_wallet)
 
 
 def patch_RPCManager(mocker) -> MagicMock:
diff --git a/tests/test_main.py b/tests/test_main.py
index a0fd8218b..d6fafe09b 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -10,7 +10,7 @@ from freqtrade.configuration import Arguments
 from freqtrade.freqtradebot import FreqtradeBot
 from freqtrade.main import main
 from freqtrade.state import State
-from freqtrade.tests.conftest import (log_has, patch_exchange,
+from tests.conftest import (log_has, patch_exchange,
                                       patched_configuration_load_config_file)
 from freqtrade.worker import Worker
 
diff --git a/tests/test_persistence.py b/tests/test_persistence.py
index c3ab7c128..85c244f7a 100644
--- a/tests/test_persistence.py
+++ b/tests/test_persistence.py
@@ -8,7 +8,7 @@ from sqlalchemy import create_engine
 
 from freqtrade import OperationalException, constants
 from freqtrade.persistence import Trade, clean_dry_run_db, init
-from freqtrade.tests.conftest import log_has
+from tests.conftest import log_has
 
 
 def create_mock_trades(fee):
diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index a4abda98b..465e3462d 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -19,7 +19,7 @@ from freqtrade.plot.plotting import (add_indicators, add_profit,
                                      generate_profit_graph, init_plotscript,
                                      plot_profit, plot_trades, store_plot_file)
 from freqtrade.strategy.default_strategy import DefaultStrategy
-from freqtrade.tests.conftest import get_args, log_has, log_has_re
+from tests.conftest import get_args, log_has, log_has_re
 
 
 def fig_generating_mock(fig, *args, **kwargs):
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 9e09fd298..386efb5ec 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
 import pytest
 
 from freqtrade.state import RunMode
-from freqtrade.tests.conftest import get_args, log_has, patch_exchange
+from tests.conftest import get_args, log_has, patch_exchange
 from freqtrade.utils import (setup_utils_configuration, start_create_userdir,
                              start_download_data, start_list_exchanges)
 
diff --git a/tests/test_wallets.py b/tests/test_wallets.py
index 2c493cfc3..ae2810a2d 100644
--- a/tests/test_wallets.py
+++ b/tests/test_wallets.py
@@ -1,5 +1,5 @@
 # pragma pylint: disable=missing-docstring
-from freqtrade.tests.conftest import get_patched_freqtradebot
+from tests.conftest import get_patched_freqtradebot
 from unittest.mock import MagicMock
 
 

From f2cbc5fb8ffb3847df0bfbaa9b0e101c06ab9a0e Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 09:55:02 +0200
Subject: [PATCH 119/227] Fix documentation references to tests folder

---
 CONTRIBUTING.md   | 6 +++---
 docs/developer.md | 2 +-
 docs/plotting.md  | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e15059f56..aac25a423 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,19 +28,19 @@ make it pass. It means you have introduced a regression.
 #### Test the whole project
 
 ```bash
-pytest freqtrade
+pytest
 ```
 
 #### Test only one file
 
 ```bash
-pytest freqtrade/tests/test_.py
+pytest tests/test_.py
 ```
 
 #### Test only one method from one file
 
 ```bash
-pytest freqtrade/tests/test_.py::test_
+pytest tests/test_.py::test_
 ```
 
 ### 2. Test if your code is PEP8 compliant
diff --git a/docs/developer.md b/docs/developer.md
index 259bfafd8..b048cf93f 100644
--- a/docs/developer.md
+++ b/docs/developer.md
@@ -30,7 +30,7 @@ These are available from `conftest.py` and can be imported in any test module.
 A sample check looks as follows:
 
 ``` python
-from freqtrade.tests.conftest import log_has, log_has_re
+from tests.conftest import log_has, log_has_re
 
 def test_method_to_test(caplog):
     method_to_test()
diff --git a/docs/plotting.md b/docs/plotting.md
index 61bf7b74a..4deb6db12 100644
--- a/docs/plotting.md
+++ b/docs/plotting.md
@@ -179,5 +179,5 @@ freqtrade plot-profit  -p LTC/BTC --db-url sqlite:///tradesv3.sqlite --trade-sou
 ```
 
 ``` bash
-freqtrade plot-profit --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p LTC/BTC
+freqtrade plot-profit --datadir user_data/data/binance_save/ -p LTC/BTC
 ```

From 9513115ce0aa671e5a021b1b81f1be8a9b2381ca Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 10:07:09 +0200
Subject: [PATCH 120/227] Fix paths in tests

---
 tests/data/test_history.py      |  2 +-
 tests/optimize/test_hyperopt.py | 19 +++++++++----------
 tests/test_configuration.py     |  2 +-
 tests/test_main.py              |  4 ++--
 4 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/tests/data/test_history.py b/tests/data/test_history.py
index 857967f39..e747794e5 100644
--- a/tests/data/test_history.py
+++ b/tests/data/test_history.py
@@ -160,7 +160,7 @@ def test_load_data_live_noexchange(default_conf, mocker, caplog, testdatadir) ->
 
 
 def test_testdata_path(testdatadir) -> None:
-    assert str(Path('freqtrade') / 'tests' / 'testdata') in str(testdatadir)
+    assert str(Path('tests') / 'testdata') in str(testdatadir)
 
 
 def test_load_cached_data_for_updating(mocker) -> None:
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index eec9e7c19..900405f03 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -1,5 +1,4 @@
 # pragma pylint: disable=missing-docstring,W0212,C0103
-import os
 from datetime import datetime
 from pathlib import Path
 from unittest.mock import MagicMock, PropertyMock
@@ -54,7 +53,7 @@ def create_trials(mocker, hyperopt, testdatadir) -> None:
       - we might have a pickle'd file so make sure that we return
         false when looking for it
     """
-    hyperopt.trials_file = testdatadir / '/optimize/ut_trials.pickle'
+    hyperopt.trials_file = testdatadir / 'optimize/ut_trials.pickle'
 
     mocker.patch.object(Path, "is_file", MagicMock(return_value=False))
     stat_mock = MagicMock()
@@ -356,23 +355,23 @@ def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
     assert caplog.record_tuples == []
 
 
-def test_save_trials_saves_trials(mocker, hyperopt, caplog) -> None:
-    trials = create_trials(mocker, hyperopt)
+def test_save_trials_saves_trials(mocker, hyperopt, testdatadir, caplog) -> None:
+    trials = create_trials(mocker, hyperopt, testdatadir)
     mock_dump = mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
     hyperopt.trials = trials
     hyperopt.save_trials()
 
-    trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
-    assert log_has("Saving 1 evaluations to '{}'".format(trials_file), caplog)
+    trials_file = testdatadir / 'optimize' / 'ut_trials.pickle'
+    assert log_has(f"Saving 1 evaluations to '{trials_file}'", caplog)
     mock_dump.assert_called_once()
 
 
-def test_read_trials_returns_trials_file(mocker, hyperopt, caplog) -> None:
-    trials = create_trials(mocker, hyperopt)
+def test_read_trials_returns_trials_file(mocker, hyperopt, testdatadir, caplog) -> None:
+    trials = create_trials(mocker, hyperopt, testdatadir)
     mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials)
     hyperopt_trial = hyperopt.read_trials()
-    trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
-    assert log_has("Reading Trials from '{}'".format(trials_file), caplog)
+    trials_file = testdatadir / 'optimize' / 'ut_trials.pickle'
+    assert log_has(f"Reading Trials from '{trials_file}'", caplog)
     assert hyperopt_trial == trials
     mock_load.assert_called_once()
 
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index e6532cecc..20ebda440 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -26,7 +26,7 @@ from tests.conftest import (log_has, log_has_re,
 
 @pytest.fixture(scope="function")
 def all_conf():
-    config_file = Path(__file__).parents[2] / "config_full.json.example"
+    config_file = Path(__file__).parents[1] / "config_full.json.example"
     print(config_file)
     conf = load_config_file(str(config_file))
     return conf
diff --git a/tests/test_main.py b/tests/test_main.py
index d6fafe09b..eec81ee18 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -10,9 +10,9 @@ from freqtrade.configuration import Arguments
 from freqtrade.freqtradebot import FreqtradeBot
 from freqtrade.main import main
 from freqtrade.state import State
-from tests.conftest import (log_has, patch_exchange,
-                                      patched_configuration_load_config_file)
 from freqtrade.worker import Worker
+from tests.conftest import (log_has, patch_exchange,
+                            patched_configuration_load_config_file)
 
 
 def test_parse_args_backtesting(mocker) -> None:

From 9d2c6c8de27b6ea2b0a8740c52b7c9ee2e434778 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 10:17:44 +0200
Subject: [PATCH 121/227] Fix paths in setup and travis

---
 .coveragerc | 2 +-
 .travis.yml | 8 ++++----
 MANIFEST.in | 1 -
 setup.cfg   | 7 ++++++-
 4 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/.coveragerc b/.coveragerc
index 4bd5b63fa..96ad6b09b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,6 +1,6 @@
 [run]
 omit =
     scripts/*
-    freqtrade/tests/*
     freqtrade/vendor/*
     freqtrade/__main__.py
+    tests/*
diff --git a/.travis.yml b/.travis.yml
index c75049276..405228ab8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,19 +22,19 @@ jobs:
   include:
     - stage: tests
       script:
-      - pytest --random-order --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
+      - pytest --random-order --cov=freqtrade --cov-config=.coveragerc
       # Allow failure for coveralls
       - coveralls || true
       name: pytest
     - script:
       - cp config.json.example config.json
-      - freqtrade --datadir freqtrade/tests/testdata backtesting
+      - freqtrade --datadir tests/testdata backtesting
       name: backtest
     - script:
       - cp config.json.example config.json
-      - freqtrade --datadir freqtrade/tests/testdata hyperopt -e 5
+      - freqtrade --datadir tests/testdata hyperopt -e 5
       name: hyperopt
-    - script: flake8 freqtrade scripts
+    - script: flake8
       name: flake8
     - script:
       # Test Documentation boxes -
diff --git a/MANIFEST.in b/MANIFEST.in
index 63508c05d..7529152a0 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,4 +2,3 @@ include LICENSE
 include README.md
 include config.json.example
 recursive-include freqtrade *.py
-include freqtrade/tests/testdata/*.json
diff --git a/setup.cfg b/setup.cfg
index 473f50639..34f25482b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,9 +2,14 @@
 #ignore =
 max-line-length = 100
 max-complexity = 12
+exclude =
+    .git,
+    __pycache__,
+    .eggs,
+    user_data,
 
 [mypy]
 ignore_missing_imports = True
 
-[mypy-freqtrade.tests.*]
+[mypy-tests.*]
 ignore_errors = True

From 0bb1127cb6dcdb3142a22029bc3048f569add92c Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 10:19:00 +0200
Subject: [PATCH 122/227] update .gitignore

things we no longer use should not be excluded
---
 .gitignore | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1664ad7eb..e0b7a7b8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,7 @@
 # Freqtrade rules
-freqtrade/tests/testdata/*.json
-hyperopt_conf.py
 config*.json
 *.sqlite
-.hyperopt
 logfile.txt
-hyperopt_trials.pickle
 user_data/*
 !user_data/notebooks
 user_data/notebooks/*

From 3e0edc7ee2b88bcab7234fb261a61c67f93fb79f Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 10:28:55 +0200
Subject: [PATCH 123/227] Update backtesting section about correct data used

---
 docs/backtesting.md | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 3da76c0ce..45d5f486f 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -43,12 +43,11 @@ Now you have good Buy and Sell strategies and some historic data, you want to te
 real data. This is what we call
 [backtesting](https://en.wikipedia.org/wiki/Backtesting).
 
-Backtesting will use the crypto-currencies (pair) from your config file
-and load static tickers located in
-[/freqtrade/tests/testdata](https://github.com/freqtrade/freqtrade/tree/develop/freqtrade/tests/testdata).
-If the 5 min and 1 min ticker for the crypto-currencies to test is not
-already in the `testdata` directory, backtesting will download them
-automatically. Testdata files will not be updated until you specify it.
+Backtesting will use the crypto-currencies (pairs) from your config file
+and load ticker data from `user_data/data/` by default.
+If no data is available for the exchange / pair / ticker interval combination, backtesting will
+ask you to download them first using `freqtrade download-data`. 
+For details on downloading, please refer to the [relevant section](#Getting-data-for-backtesting-and-hyperopt) in the documentation.
 
 The result of backtesting will confirm you if your bot has better odds of making a profit than a loss.
 

From 2a236db18f6fc263a2917d44aaa3c9e2551a609d Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 19:27:42 +0200
Subject: [PATCH 124/227] Pass test-data to dockerized backtest

---
 build_helpers/publish_docker.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build_helpers/publish_docker.sh b/build_helpers/publish_docker.sh
index 7a8127c44..839ca0876 100755
--- a/build_helpers/publish_docker.sh
+++ b/build_helpers/publish_docker.sh
@@ -23,7 +23,7 @@ if [ $? -ne 0 ]; then
 fi
 
 # Run backtest
-docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro freqtrade:${TAG} --datadir freqtrade/tests/testdata backtesting
+docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} --datadir /tests/testdata backtesting
 
 if [ $? -ne 0 ]; then
     echo "failed running backtest"

From 867a3273ced30d090cae402aa9f9363144f2a27b Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 19:38:16 +0200
Subject: [PATCH 125/227] Fix random failure if config.json exists

---
 tests/test_plotting.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index 465e3462d..a30d7d04a 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -336,12 +336,17 @@ def test_start_plot_profit(mocker):
 
 
 def test_start_plot_profit_error(mocker):
+
     args = [
         "plot-profit",
         "--pairs", "ETH/BTC"
     ]
+    argsp = get_args(args)
+    # Make sure we use no config. Details: #2241
+    # not resetting config causes random failures if config.json exists
+    argsp.config = []
     with pytest.raises(OperationalException):
-        start_plot_profit(get_args(args))
+        start_plot_profit(argsp)
 
 
 def test_plot_profit(default_conf, mocker, testdatadir, caplog):

From c5726e88e8bf90416d05a69c446f704c92911931 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 19:45:50 +0200
Subject: [PATCH 126/227] Don't gitignore sample_strategy

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index e0b7a7b8f..9ac2c9d5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ config*.json
 *.sqlite
 logfile.txt
 user_data/*
+!user_data/strategy/sample_strategy.py
 !user_data/notebooks
 user_data/notebooks/*
 !user_data/notebooks/*example.ipynb

From 34308504214e9edae4bc3e13bc326f5f521a0c28 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sun, 8 Sep 2019 19:47:16 +0200
Subject: [PATCH 127/227] don't print in tests

---
 tests/edge/test_edge.py                | 2 --
 tests/exchange/test_exchange.py        | 2 --
 tests/optimize/test_backtest_detail.py | 1 -
 tests/optimize/test_backtesting.py     | 1 -
 tests/optimize/test_hyperopt.py        | 6 ------
 tests/test_configuration.py            | 2 --
 tests/test_freqtradebot.py             | 1 -
 tests/test_persistence.py              | 1 -
 8 files changed, 16 deletions(-)

diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py
index d5f282d7b..4fab68591 100644
--- a/tests/edge/test_edge.py
+++ b/tests/edge/test_edge.py
@@ -155,8 +155,6 @@ def test_edge_results(edge_conf, mocker, caplog, data) -> None:
     trades = edge._find_trades_for_stoploss_range(frame, 'TEST/BTC', [data.stop_loss])
     results = edge._fill_calculable_fields(DataFrame(trades)) if trades else DataFrame()
 
-    print(results)
-
     assert len(trades) == len(data.trades)
 
     if not results.empty:
diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index e23db42d2..5ab7cdb47 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -1035,7 +1035,6 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name):
     exchange._async_get_candle_history = Mock(wraps=mock_candle_hist)
     # one_call calculation * 1.8 should do 2 calls
     since = 5 * 60 * 500 * 1.8
-    print(f"since = {since}")
     ret = exchange.get_historic_ohlcv(pair, "5m", int((arrow.utcnow().timestamp - since) * 1000))
 
     assert exchange._async_get_candle_history.call_count == 2
@@ -1340,7 +1339,6 @@ def test_get_order(default_conf, mocker, exchange_name):
     order.myid = 123
     exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     exchange._dry_run_open_orders['X'] = order
-    print(exchange.get_order('X', 'TKN/BTC'))
     assert exchange.get_order('X', 'TKN/BTC').myid == 123
 
     with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py
index 8cd88a479..1bcd9b08e 100644
--- a/tests/optimize/test_backtest_detail.py
+++ b/tests/optimize/test_backtest_detail.py
@@ -308,7 +308,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
             'end_date': max_date,
         }
     )
-    print(results.T)
 
     assert len(results) == len(data.trades)
     assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index 34c0322cb..770f6c4ba 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -451,7 +451,6 @@ def test_generate_text_table_strategyn(default_conf, mocker):
         '| LTC/BTC    |           3 |          30.00 |          90.00 '
         '|       1.30000000 |          45.00 | 0:20:00        |        3 |      0 |'
     )
-    print(backtesting._generate_text_table_strategy(all_results=results))
     assert backtesting._generate_text_table_strategy(all_results=results) == result_str
 
 
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 900405f03..f5c8bf0ae 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -214,9 +214,6 @@ def test_start(mocker, default_conf, caplog) -> None:
     args = get_args(args)
     start_hyperopt(args)
 
-    import pprint
-    pprint.pprint(caplog.record_tuples)
-
     assert log_has('Starting freqtrade in Hyperopt mode', caplog)
     assert start_mock.call_count == 1
 
@@ -239,9 +236,6 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
     args = get_args(args)
     start_hyperopt(args)
 
-    import pprint
-    pprint.pprint(caplog.record_tuples)
-
     assert log_has('No data found. Terminating.', caplog)
 
 
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 20ebda440..3717f4d1b 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -27,7 +27,6 @@ from tests.conftest import (log_has, log_has_re,
 @pytest.fixture(scope="function")
 def all_conf():
     config_file = Path(__file__).parents[1] / "config_full.json.example"
-    print(config_file)
     conf = load_config_file(str(config_file))
     return conf
 
@@ -713,7 +712,6 @@ def test_load_config_test_comments() -> None:
     Load config with comments
     """
     config_file = Path(__file__).parents[0] / "config_test_comments.json"
-    print(config_file)
     conf = load_config_file(str(config_file))
 
     assert conf
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 4ffa863e6..a67659d8c 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -970,7 +970,6 @@ def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> Non
         markets=PropertyMock(return_value=markets)
     )
     pair = 'ETH/BTC'
-    print(buy_mm.call_args_list)
 
     assert freqtrade.execute_buy(pair, stake_amount)
     assert get_bid.call_count == 1
diff --git a/tests/test_persistence.py b/tests/test_persistence.py
index 85c244f7a..6bd223a9b 100644
--- a/tests/test_persistence.py
+++ b/tests/test_persistence.py
@@ -729,7 +729,6 @@ def test_to_json(default_conf, fee):
     )
     result = trade.to_json()
     assert isinstance(result, dict)
-    print(result)
 
     assert result == {'trade_id': None,
                       'pair': 'ETH/BTC',

From a5510d14e93ebd801fc093259ac47a362da7a3e8 Mon Sep 17 00:00:00 2001
From: Jonathan Raviotta 
Date: Thu, 5 Sep 2019 16:00:16 -0400
Subject: [PATCH 128/227] de-mangling

---
 freqtrade/data/history.py    | 2 +-
 freqtrade/plot/plot_utils.py | 4 ++--
 freqtrade/plot/plotting.py   | 5 +----
 tests/test_plotting.py       | 6 +++---
 4 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py
index a71dc373c..b4776fab0 100644
--- a/freqtrade/data/history.py
+++ b/freqtrade/data/history.py
@@ -61,7 +61,7 @@ def load_tickerdata_file(datadir: Path, pair: str, ticker_interval: str,
                          timerange: Optional[TimeRange] = None) -> Optional[list]:
     """
     Load a pair from file, either .json.gz or .json
-    :return: tickerlist or None if unsuccesful
+    :return: tickerlist or None if unsuccessful
     """
     filename = pair_data_filename(datadir, pair, ticker_interval)
     pairdata = misc.file_load_json(filename)
diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py
index d7d8919d5..d7fb326d1 100644
--- a/freqtrade/plot/plot_utils.py
+++ b/freqtrade/plot/plot_utils.py
@@ -17,11 +17,11 @@ def start_plot_dataframe(args: Namespace) -> None:
     Entrypoint for dataframe plotting
     """
     # Import here to avoid errors if plot-dependencies are not installed.
-    from freqtrade.plot.plotting import analyse_and_plot_pairs
+    from freqtrade.plot.plotting import load_and_plot_trades
     validate_plot_args(args)
     config = setup_utils_configuration(args, RunMode.PLOT)
 
-    analyse_and_plot_pairs(config)
+    load_and_plot_trades(config)
 
 
 def start_plot_profit(args: Namespace) -> None:
diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py
index b0b8e3df9..1627959f9 100644
--- a/freqtrade/plot/plotting.py
+++ b/freqtrade/plot/plotting.py
@@ -3,7 +3,6 @@ from pathlib import Path
 from typing import Any, Dict, List
 
 import pandas as pd
-
 from freqtrade.configuration import TimeRange
 from freqtrade.data import history
 from freqtrade.data.btanalysis import (combine_tickers_with_mean,
@@ -324,7 +323,7 @@ def store_plot_file(fig, filename: str, directory: Path, auto_open: bool = False
     logger.info(f"Stored plot as {_filename}")
 
 
-def analyse_and_plot_pairs(config: Dict[str, Any]):
+def load_and_plot_trades(config: Dict[str, Any]):
     """
     From configuration provided
     - Initializes plot-script
@@ -339,7 +338,6 @@ def analyse_and_plot_pairs(config: Dict[str, Any]):
 
     plot_elements = init_plotscript(config)
     trades = plot_elements['trades']
-
     pair_counter = 0
     for pair, data in plot_elements["tickers"].items():
         pair_counter += 1
@@ -348,7 +346,6 @@ def analyse_and_plot_pairs(config: Dict[str, Any]):
         tickers[pair] = data
 
         dataframe = strategy.analyze_ticker(tickers[pair], {'pair': pair})
-
         trades_pair = trades.loc[trades['pair'] == pair]
         trades_pair = extract_trades_of_period(dataframe, trades_pair)
 
diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index a30d7d04a..7debff071 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -32,7 +32,7 @@ def find_trace_in_fig_data(data, search_string: str):
     return next(matches)
 
 
-def generage_empty_figure():
+def generate_empty_figure():
     return make_subplots(
         rows=3,
         cols=1,
@@ -72,7 +72,7 @@ def test_add_indicators(default_conf, testdatadir, caplog):
     # Generate buy/sell signals and indicators
     strat = DefaultStrategy(default_conf)
     data = strat.analyze_ticker(data, {'pair': pair})
-    fig = generage_empty_figure()
+    fig = generate_empty_figure()
 
     # Row 1
     fig1 = add_indicators(fig=deepcopy(fig), row=1, indicators=indicators1, data=data)
@@ -210,7 +210,7 @@ def test_generate_Plot_filename():
 
 
 def test_generate_plot_file(mocker, caplog):
-    fig = generage_empty_figure()
+    fig = generate_empty_figure()
     plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock())
     store_plot_file(fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html",
                     directory=Path("user_data/plots"))

From adbc0159aec0f5d82cda1e16bb138c3cc57349c8 Mon Sep 17 00:00:00 2001
From: Jonathan Raviotta 
Date: Fri, 6 Sep 2019 18:59:42 -0400
Subject: [PATCH 129/227] changed more occuranes of function

---
 tests/test_plotting.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index 7debff071..2c3b8a339 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -13,7 +13,7 @@ from freqtrade.data import history
 from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
 from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
 from freqtrade.plot.plotting import (add_indicators, add_profit,
-                                     analyse_and_plot_pairs,
+                                     load_and_plot_trades,
                                      generate_candlestick_graph,
                                      generate_plot_filename,
                                      generate_profit_graph, init_plotscript,
@@ -94,7 +94,7 @@ def test_add_indicators(default_conf, testdatadir, caplog):
 
 
 def test_plot_trades(testdatadir, caplog):
-    fig1 = generage_empty_figure()
+    fig1 = generate_empty_figure()
     # nothing happens when no trades are available
     fig = plot_trades(fig1, None)
     assert fig == fig1
@@ -230,7 +230,7 @@ def test_add_profit(testdatadir):
 
     df = history.load_pair_history(pair="POWR/BTC", ticker_interval='5m',
                                    datadir=testdatadir, timerange=timerange)
-    fig = generage_empty_figure()
+    fig = generate_empty_figure()
 
     cum_profits = create_cum_profit(df.set_index('date'),
                                     bt_data[bt_data["pair"] == 'POWR/BTC'],
@@ -279,7 +279,7 @@ def test_generate_profit_graph(testdatadir):
 
 
 def test_start_plot_dataframe(mocker):
-    aup = mocker.patch("freqtrade.plot.plotting.analyse_and_plot_pairs", MagicMock())
+    aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
     args = [
         "--config", "config.json.example",
         "plot-dataframe",
@@ -293,7 +293,7 @@ def test_start_plot_dataframe(mocker):
     assert called_config['pairs'] == ["ETH/BTC"]
 
 
-def test_analyse_and_plot_pairs(default_conf, mocker, caplog, testdatadir):
+def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
     default_conf['trade_source'] = 'file'
     default_conf["datadir"] = testdatadir
     default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
@@ -308,7 +308,7 @@ def test_analyse_and_plot_pairs(default_conf, mocker, caplog, testdatadir):
         generate_candlestick_graph=candle_mock,
         store_plot_file=store_mock
         )
-    analyse_and_plot_pairs(default_conf)
+    load_and_plot_trades(default_conf)
 
     # Both mocks should be called once per pair
     assert candle_mock.call_count == 2

From edba5a001415b0c95adb79b089e4cb8d96210a5f Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2019 08:21:22 +0000
Subject: [PATCH 130/227] Bump numpy from 1.17.1 to 1.17.2

Bumps [numpy](https://github.com/numpy/numpy) from 1.17.1 to 1.17.2.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/master/doc/HOWTO_RELEASE.rst.txt)
- [Commits](https://github.com/numpy/numpy/compare/v1.17.1...v1.17.2)

Signed-off-by: dependabot-preview[bot] 
---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index cf264ee81..9a723fee4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
 # Load common requirements
 -r requirements-common.txt
 
-numpy==1.17.1
+numpy==1.17.2
 pandas==0.25.1
 scipy==1.3.1

From 3c869a8032f1c99cf1c5e8e5501d7e7b37a86a88 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2019 08:21:33 +0000
Subject: [PATCH 131/227] Bump arrow from 0.14.6 to 0.15.0

Bumps [arrow](https://github.com/crsmithdev/arrow) from 0.14.6 to 0.15.0.
- [Release notes](https://github.com/crsmithdev/arrow/releases)
- [Changelog](https://github.com/crsmithdev/arrow/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crsmithdev/arrow/compare/0.14.6...0.15.0)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 7b6befaf6..a8d6c9eb4 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -3,7 +3,7 @@
 ccxt==1.18.1115
 SQLAlchemy==1.3.8
 python-telegram-bot==12.0.0
-arrow==0.14.6
+arrow==0.15.0
 cachetools==3.1.1
 requests==2.22.0
 urllib3==1.25.3

From 7dc3e67bbae48edbea465f806f481d2129218dfe Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2019 08:21:55 +0000
Subject: [PATCH 132/227] Bump plotly from 4.1.0 to 4.1.1

Bumps [plotly](https://github.com/plotly/plotly.py) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/plotly/plotly.py/releases)
- [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.py/compare/v4.1.0...v4.1.1)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-plot.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-plot.txt b/requirements-plot.txt
index f10bfac3f..1f1df4ecc 100644
--- a/requirements-plot.txt
+++ b/requirements-plot.txt
@@ -1,5 +1,5 @@
 # Include all requirements to run the bot.
 -r requirements.txt
 
-plotly==4.1.0
+plotly==4.1.1
 

From 3398f31b871badd1c5c9f2abf0bf7b51e17c1d08 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2019 17:40:31 +0000
Subject: [PATCH 133/227] Bump ccxt from 1.18.1115 to 1.18.1124

Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1115 to 1.18.1124.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/1.18.1115...1.18.1124)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index a8d6c9eb4..30c93950a 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -1,6 +1,6 @@
 # requirements without requirements installable via conda
 # mainly used for Raspberry pi installs
-ccxt==1.18.1115
+ccxt==1.18.1124
 SQLAlchemy==1.3.8
 python-telegram-bot==12.0.0
 arrow==0.15.0

From 9aa7db103d3197ce5a190ece2d0190db2990ae51 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Mon, 9 Sep 2019 19:59:41 +0200
Subject: [PATCH 134/227] Add test for failing case

---
 tests/rpc/test_fiat_convert.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py
index 8f6b2eb93..05760ce25 100644
--- a/tests/rpc/test_fiat_convert.py
+++ b/tests/rpc/test_fiat_convert.py
@@ -210,3 +210,10 @@ def test_convert_amount(mocker):
         fiat_symbol="BTC"
     )
     assert result == 1.23
+
+    result = fiat_convert.convert_amount(
+        crypto_amount="1.23",
+        crypto_symbol="BTC",
+        fiat_symbol="BTC"
+    )
+    assert result == 1.23

From 94d2790ab5df93b703b8da45a9e16caea2c4abc9 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Mon, 9 Sep 2019 20:00:13 +0200
Subject: [PATCH 135/227] Fix #2239 -

return float even if fiat/crypto are identical
---
 freqtrade/rpc/fiat_convert.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py
index 6812bf77f..74ccc62ed 100644
--- a/freqtrade/rpc/fiat_convert.py
+++ b/freqtrade/rpc/fiat_convert.py
@@ -104,7 +104,7 @@ class CryptoToFiatConverter(object):
         :return: float, value in fiat of the crypto-currency amount
         """
         if crypto_symbol == fiat_symbol:
-            return crypto_amount
+            return float(crypto_amount)
         price = self.get_price(crypto_symbol=crypto_symbol, fiat_symbol=fiat_symbol)
         return float(crypto_amount) * float(price)
 

From 85f1291597b507a997b62e750c66444afad2ece5 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Mon, 9 Sep 2019 20:18:47 +0200
Subject: [PATCH 136/227] use git log to print version

---
 freqtrade/__init__.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py
index 175b689f9..83fee0b0d 100644
--- a/freqtrade/__init__.py
+++ b/freqtrade/__init__.py
@@ -5,8 +5,9 @@ if __version__ == 'develop':
 
     try:
         import subprocess
-        __version__ = str(subprocess.check_output(
-            ["git", "describe"], stderr=subprocess.DEVNULL).rstrip())
+        __version__ = 'develop-' + subprocess.check_output(
+            ['git', 'log', '--format="%h"', '-n 1'],
+            stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
     except Exception:
         # git not available, ignore
         pass

From 35580b135a1ab2fa04bc046b93ba6aa28edf0bb2 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 10 Sep 2019 10:42:45 +0300
Subject: [PATCH 137/227] Improve backtesting logs

---
 freqtrade/optimize/backtesting.py | 44 +++++++++++++++-----------
 freqtrade/persistence.py          | 52 +++++++++++++------------------
 freqtrade/strategy/interface.py   | 39 +++++++++++++----------
 3 files changed, 69 insertions(+), 66 deletions(-)

diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 708b60144..752a83d2d 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -239,14 +239,16 @@ class Backtesting(object):
             stake_amount: float, max_open_trades: int) -> Optional[BacktestResult]:
 
         trade = Trade(
+            pair=pair,
             open_rate=buy_row.open,
             open_date=buy_row.date,
             stake_amount=stake_amount,
             amount=stake_amount / buy_row.open,
             fee_open=self.fee,
-            fee_close=self.fee
+            fee_close=self.fee,
+            is_open=True,
         )
-
+        logger.debug(f"'{pair}' - Backtesting emulates creation of new trade: {trade}.")
         # calculate win/lose forwards from buy point
         for sell_row in partial_ticker:
             if max_open_trades > 0:
@@ -289,23 +291,25 @@ class Backtesting(object):
         if partial_ticker:
             # no sell condition found - trade stil open at end of backtest period
             sell_row = partial_ticker[-1]
-            btr = BacktestResult(pair=pair,
-                                 profit_percent=trade.calc_profit_percent(rate=sell_row.open),
-                                 profit_abs=trade.calc_profit(rate=sell_row.open),
-                                 open_time=buy_row.date,
-                                 close_time=sell_row.date,
-                                 trade_duration=int((
-                                     sell_row.date - buy_row.date).total_seconds() // 60),
-                                 open_index=buy_row.Index,
-                                 close_index=sell_row.Index,
-                                 open_at_end=True,
-                                 open_rate=buy_row.open,
-                                 close_rate=sell_row.open,
-                                 sell_reason=SellType.FORCE_SELL
-                                 )
-            logger.debug('Force_selling still open trade %s with %s perc - %s', btr.pair,
-                         btr.profit_percent, btr.profit_abs)
-            return btr
+            bt_res = BacktestResult(pair=pair,
+                                    profit_percent=trade.calc_profit_percent(rate=sell_row.open),
+                                    profit_abs=trade.calc_profit(rate=sell_row.open),
+                                    open_time=buy_row.date,
+                                    close_time=sell_row.date,
+                                    trade_duration=int((
+                                        sell_row.date - buy_row.date).total_seconds() // 60),
+                                    open_index=buy_row.Index,
+                                    close_index=sell_row.Index,
+                                    open_at_end=True,
+                                    open_rate=buy_row.open,
+                                    close_rate=sell_row.open,
+                                    sell_reason=SellType.FORCE_SELL
+                                    )
+            logger.debug(f"'{pair}' - Force selling still open trade, "
+                         f"profit percent: {bt_res.profit_percent}, "
+                         f"profit abs: {bt_res.profit_abs}")
+
+            return bt_res
         return None
 
     def backtest(self, args: Dict) -> DataFrame:
@@ -384,6 +388,8 @@ class Backtesting(object):
                                                          max_open_trades)
 
                 if trade_entry:
+                    logger.debug(f"'{pair}' - Locking pair till "
+                                 f"close_time={trade_entry.close_time}")
                     lock_pair_until[pair] = trade_entry.close_time
                     trades.append(trade_entry)
                 else:
diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index dff7e4ff6..608243d54 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -1,7 +1,6 @@
 """
 This module contains the class to persist trades into SQLite
 """
-
 import logging
 from datetime import datetime
 from decimal import Decimal
@@ -19,8 +18,10 @@ from sqlalchemy.pool import StaticPool
 
 from freqtrade import OperationalException
 
+
 logger = logging.getLogger(__name__)
 
+
 _DECL_BASE: Any = declarative_base()
 _SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
 
@@ -209,7 +210,8 @@ class Trade(_DECL_BASE):
     ticker_interval = Column(Integer, nullable=True)
 
     def __repr__(self):
-        open_since = arrow.get(self.open_date).humanize() if self.is_open else 'closed'
+        open_since = (f"{arrow.get(self.open_date).strftime('%Y-%m-%d %H:%M:%S')} "
+                     f"({arrow.get(self.open_date).humanize()})" ) if self.is_open else 'closed'
 
         return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
                 f'open_rate={self.open_rate:.8f}, open_since={open_since})')
@@ -250,7 +252,6 @@ class Trade(_DECL_BASE):
         :param initial: Called to initiate stop_loss.
             Skips everything if self.stop_loss is already set.
         """
-
         if initial and not (self.stop_loss is None or self.stop_loss == 0):
             # Don't modify if called with initial and nothing to do
             return
@@ -259,7 +260,7 @@ class Trade(_DECL_BASE):
 
         # no stop loss assigned yet
         if not self.stop_loss:
-            logger.debug("assigning new stop loss")
+            logger.debug(f"'{self.pair}' - Assigning new stoploss...")
             self.stop_loss = new_loss
             self.stop_loss_pct = -1 * abs(stoploss)
             self.initial_stop_loss = new_loss
@@ -269,21 +270,21 @@ class Trade(_DECL_BASE):
         # evaluate if the stop loss needs to be updated
         else:
             if new_loss > self.stop_loss:  # stop losses only walk up, never down!
+                logger.debug(f"'{self.pair}' - Adjusting stoploss...")
                 self.stop_loss = new_loss
                 self.stop_loss_pct = -1 * abs(stoploss)
                 self.stoploss_last_update = datetime.utcnow()
-                logger.debug("adjusted stop loss")
             else:
-                logger.debug("keeping current stop loss")
+                logger.debug(f"'{self.pair}' - Keeping current stoploss...")
 
         logger.debug(
-            f"{self.pair} - current price {current_price:.8f}, "
+            f"'{self.pair}' - Stoploss adjusted. Current price {current_price:.8f}, "
             f"bought at {self.open_rate:.8f} and calculated "
-            f"stop loss is at: {self.initial_stop_loss:.8f} initial "
-            f"stop at {self.stop_loss:.8f}. "
-            f"trailing stop loss saved us: "
+            f"stoploss is at: {self.initial_stop_loss:.8f}, "
+            f"initial stop at {self.stop_loss:.8f}. "
+            f"Trailing stoploss saved us: "
             f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f} "
-            f"and max observed rate was {self.max_rate:.8f}")
+            f"and max observed rate was {self.max_rate:.8f}.")
 
     def update(self, order: Dict) -> None:
         """
@@ -331,24 +332,19 @@ class Trade(_DECL_BASE):
             self
         )
 
-    def calc_open_trade_price(
-            self,
-            fee: Optional[float] = None) -> float:
+    def calc_open_trade_price(self, fee: Optional[float] = None) -> float:
         """
         Calculate the open_rate including fee.
         :param fee: fee to use on the open rate (optional).
         If rate is not set self.fee will be used
         :return: Price in of the open trade incl. Fees
         """
-
         buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
         fees = buy_trade * Decimal(fee or self.fee_open)
         return float(buy_trade + fees)
 
-    def calc_close_trade_price(
-            self,
-            rate: Optional[float] = None,
-            fee: Optional[float] = None) -> float:
+    def calc_close_trade_price(self, rate: Optional[float] = None,
+                               fee: Optional[float] = None) -> float:
         """
         Calculate the close_rate including fee
         :param fee: fee to use on the close rate (optional).
@@ -357,7 +353,6 @@ class Trade(_DECL_BASE):
         If rate is not set self.close_rate will be used
         :return: Price in BTC of the open trade
         """
-
         if rate is None and not self.close_rate:
             return 0.0
 
@@ -365,10 +360,8 @@ class Trade(_DECL_BASE):
         fees = sell_trade * Decimal(fee or self.fee_close)
         return float(sell_trade - fees)
 
-    def calc_profit(
-            self,
-            rate: Optional[float] = None,
-            fee: Optional[float] = None) -> float:
+    def calc_profit(self, rate: Optional[float] = None,
+                    fee: Optional[float] = None) -> float:
         """
         Calculate the absolute profit in stake currency between Close and Open trade
         :param fee: fee to use on the close rate (optional).
@@ -385,10 +378,8 @@ class Trade(_DECL_BASE):
         profit = close_trade_price - open_trade_price
         return float(f"{profit:.8f}")
 
-    def calc_profit_percent(
-            self,
-            rate: Optional[float] = None,
-            fee: Optional[float] = None) -> float:
+    def calc_profit_percent(self, rate: Optional[float] = None,
+                            fee: Optional[float] = None) -> float:
         """
         Calculates the profit in percentage (including fee).
         :param rate: rate to compare with (optional).
@@ -396,7 +387,6 @@ class Trade(_DECL_BASE):
         :param fee: fee to use on the close rate (optional).
         :return: profit in percentage as float
         """
-
         open_trade_price = self.calc_open_trade_price()
         close_trade_price = self.calc_close_trade_price(
             rate=(rate or self.close_rate),
@@ -436,8 +426,8 @@ class Trade(_DECL_BASE):
                and trade.initial_stop_loss_pct != desired_stoploss):
                 # Stoploss value got changed
 
-                logger.info(f"Stoploss for {trade} needs adjustment.")
+                logger.info(f"Stoploss for {trade} needs adjustment...")
                 # Force reset of stoploss
                 trade.stop_loss = None
                 trade.adjust_stop_loss(trade.open_rate, desired_stoploss)
-                logger.info(f"new stoploss: {trade.stop_loss}, ")
+                logger.info(f"New stoploss: {trade.stop_loss}.")
diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py
index 6c6593a53..d4b89a3ae 100644
--- a/freqtrade/strategy/interface.py
+++ b/freqtrade/strategy/interface.py
@@ -202,7 +202,6 @@ class IStrategy(ABC):
         :param metadata: Metadata dictionary with additional data (e.g. 'pair')
         :return: DataFrame with ticker data and indicator data
         """
-
         pair = str(metadata.get('pair'))
 
         # Test if seen this pair and last candle before.
@@ -292,7 +291,6 @@ class IStrategy(ABC):
         :param force_stoploss: Externally provided stoploss
         :return: True if trade should be sold, False otherwise
         """
-
         # Set current rate to low for backtesting sell
         current_rate = low or rate
         current_profit = trade.calc_profit_percent(current_rate)
@@ -304,6 +302,8 @@ class IStrategy(ABC):
                                               force_stoploss=force_stoploss, high=high)
 
         if stoplossflag.sell_flag:
+            logger.debug(f"'{trade.pair}' - Stoploss hit. Selling "
+                         f"(sell_type={stoplossflag.sell_type})...")
             return stoplossflag
 
         # Set current rate to high for backtesting sell
@@ -312,22 +312,30 @@ class IStrategy(ABC):
         experimental = self.config.get('experimental', {})
 
         if buy and experimental.get('ignore_roi_if_buy_signal', False):
-            logger.debug('Buy signal still active - not selling.')
+            logger.debug(f"'{trade.pair}' - Buy signal still active. Not selling "
+                         f"(sell_type=SellType.NONE)...")
             return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
         if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
-            logger.debug('Required profit reached. Selling..')
+            logger.debug(f"'{trade.pair}' - Required profit reached. Selling "
+                         f"(sell_type=SellType.ROI)...")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
 
         if experimental.get('sell_profit_only', False):
-            logger.debug('Checking if trade is profitable..')
+            logger.debug(f"'{trade.pair}' - Checking if trade is profitable...")
             if trade.calc_profit(rate=rate) <= 0:
+                logger.debug(f"'{trade.pair}' - Trade is not profitable. Not selling "
+                             f"(sell_type=SellType.NONE)...")
                 return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
+
         if sell and not buy and experimental.get('use_sell_signal', False):
-            logger.debug('Sell signal received. Selling..')
+            logger.debug(f"'{trade.pair}' - Sell signal received. Selling "
+                         f"(sell_type=SellType.SELL_SIGNAL)...")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
 
+        # This one is noisy, commented out...
+#        logger.debug(f"'{trade.pair}' - Not selling (sell_type=SellType.NONE)...")
         return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
     def stop_loss_reached(self, current_rate: float, trade: Trade,
@@ -338,7 +346,6 @@ class IStrategy(ABC):
         decides to sell or not
         :param current_profit: current profit in percent
         """
-
         trailing_stop = self.config.get('trailing_stop', False)
         stop_loss_value = force_stoploss if force_stoploss else self.stoploss
 
@@ -359,7 +366,7 @@ class IStrategy(ABC):
                 if 'trailing_stop_positive' in self.config and high_profit > sl_offset:
                     # Ignore mypy error check in configuration that this is a float
                     stop_loss_value = self.config.get('trailing_stop_positive')  # type: ignore
-                    logger.debug(f"using positive stop loss: {stop_loss_value} "
+                    logger.debug(f"'{trade.pair}' - Using positive stoploss: {stop_loss_value} "
                                  f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%")
 
                 trade.adjust_stop_loss(high or current_rate, stop_loss_value)
@@ -369,20 +376,20 @@ class IStrategy(ABC):
             (trade.stop_loss >= current_rate) and
                 (not self.order_types.get('stoploss_on_exchange'))):
 
-            selltype = SellType.STOP_LOSS
+            sell_type = SellType.STOP_LOSS
 
             # If initial stoploss is not the same as current one then it is trailing.
             if trade.initial_stop_loss != trade.stop_loss:
-                selltype = SellType.TRAILING_STOP_LOSS
+                sell_type = SellType.TRAILING_STOP_LOSS
                 logger.debug(
-                    f"HIT STOP: current price at {current_rate:.6f}, "
-                    f"stop loss is {trade.stop_loss:.6f}, "
-                    f"initial stop loss was at {trade.initial_stop_loss:.6f}, "
+                    f"'{trade.pair}' - HIT STOP: current price at {current_rate:.6f}, "
+                    f"stoploss is {trade.stop_loss:.6f}, "
+                    f"initial stoploss was at {trade.initial_stop_loss:.6f}, "
                     f"trade opened at {trade.open_rate:.6f}")
-                logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
+                logger.debug(f"'{trade.pair}' - Trailing stop saved "
+                             f"{trade.stop_loss - trade.initial_stop_loss:.6f}")
 
-            logger.debug('Stop loss hit.')
-            return SellCheckTuple(sell_flag=True, sell_type=selltype)
+            return SellCheckTuple(sell_flag=True, sell_type=sell_type)
 
         return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 

From e298e773198150a0ec9fdd4d36b4f16001b87f65 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 10 Sep 2019 10:43:15 +0300
Subject: [PATCH 138/227] Adjust tests

---
 tests/test_freqtradebot.py | 44 ++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index a67659d8c..388553dc5 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -1823,7 +1823,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
     # if ROI is reached we must sell
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has('Required profit reached. Selling..', caplog)
+    assert log_has("'ETH/BTC' - Required profit reached. Selling (sell_type=SellType.ROI)...", caplog)
 
 
 def test_handle_trade_experimental(
@@ -1853,7 +1853,7 @@ def test_handle_trade_experimental(
 
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has('Sell signal received. Selling..', caplog)
+    assert log_has("'ETH/BTC' - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...", caplog)
 
 
 def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
@@ -2132,6 +2132,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
     )
     freqtrade = FreqtradeBot(default_conf)
 
+    open_date = arrow.utcnow().shift(minutes=-601)
     trade_buy = Trade(
         pair='ETH/BTC',
         open_rate=0.00001099,
@@ -2141,16 +2142,16 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
         fee_open=0.0,
         fee_close=0.0,
         stake_amount=1,
-        open_date=arrow.utcnow().shift(minutes=-601).datetime,
+        open_date=open_date.datetime,
         is_open=True
     )
 
     Trade.session.add(trade_buy)
 
     freqtrade.check_handle_timedout()
-    assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, '
-                      r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most '
-                      r'recent call last\):\n.*', caplog)
+    assert log_has_re(f"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
+                      f"open_rate=0.00001099, open_since={open_date.strftime('%Y-%m-%d %H:%M:%S')} "
+                      f"\(10 hours ago\)\) due to Traceback \(most recent call last\):\n*", caplog)
 
 
 def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
@@ -2793,8 +2794,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
     # Sell as trailing-stop is reached
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f'HIT STOP: current price at 0.000012, stop loss is 0.000015, '
-        f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
+        f"'ETH/BTC' - HIT STOP: current price at 0.000012, "
+        f"stoploss is 0.000015, "
+        f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
 
 
@@ -2836,8 +2838,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
                  }))
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', caplog)
-    assert log_has(f'adjusted stop loss', caplog)
+    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
+    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000138501
 
     mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@@ -2849,9 +2851,9 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
     # Lower price again (but still positive)
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
-        f'stop loss is {trade.stop_loss:.6f}, '
-        f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
+        f"'ETH/BTC' - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
+        f"stoploss is {trade.stop_loss:.6f}, "
+        f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
 
 
 def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
@@ -2894,8 +2896,8 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
                  }))
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', caplog)
-    assert log_has(f'adjusted stop loss', caplog)
+    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog)
+    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000138501
 
     mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@@ -2907,9 +2909,9 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
     # Lower price again (but still positive)
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
-        f'stop loss is {trade.stop_loss:.6f}, '
-        f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
+        f"'ETH/BTC' - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
+        f"stoploss is {trade.stop_loss:.6f}, "
+        f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
 
 
@@ -2960,7 +2962,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
     # stop-loss should not be adjusted as offset is not reached yet
     assert freqtrade.handle_trade(trade) is False
 
-    assert not log_has(f'adjusted stop loss', caplog)
+    assert not log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000098910
 
     # price rises above the offset (rises 12% when the offset is 5.5%)
@@ -2972,8 +2974,8 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
                  }))
 
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', caplog)
-    assert log_has(f'adjusted stop loss', caplog)
+    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog)
+    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000117705
 
 

From 2081d7552f9b92dd2ec2f8d996d6a8c645965381 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 10 Sep 2019 12:37:15 +0300
Subject: [PATCH 139/227] Make flake happy

---
 freqtrade/persistence.py   |  2 +-
 tests/test_freqtradebot.py | 20 +++++++++++++-------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index 608243d54..be9374ebe 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -211,7 +211,7 @@ class Trade(_DECL_BASE):
 
     def __repr__(self):
         open_since = (f"{arrow.get(self.open_date).strftime('%Y-%m-%d %H:%M:%S')} "
-                     f"({arrow.get(self.open_date).humanize()})" ) if self.is_open else 'closed'
+                      f"({arrow.get(self.open_date).humanize()})") if self.is_open else 'closed'
 
         return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
                 f'open_rate={self.open_rate:.8f}, open_since={open_since})')
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 388553dc5..cb6259c22 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -1823,7 +1823,8 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
     # if ROI is reached we must sell
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("'ETH/BTC' - Required profit reached. Selling (sell_type=SellType.ROI)...", caplog)
+    assert log_has("'ETH/BTC' - Required profit reached. Selling (sell_type=SellType.ROI)...",
+                   caplog)
 
 
 def test_handle_trade_experimental(
@@ -1853,7 +1854,8 @@ def test_handle_trade_experimental(
 
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("'ETH/BTC' - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...", caplog)
+    assert log_has("'ETH/BTC' - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...",
+                   caplog)
 
 
 def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
@@ -2149,9 +2151,11 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
     Trade.session.add(trade_buy)
 
     freqtrade.check_handle_timedout()
-    assert log_has_re(f"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
-                      f"open_rate=0.00001099, open_since={open_date.strftime('%Y-%m-%d %H:%M:%S')} "
-                      f"\(10 hours ago\)\) due to Traceback \(most recent call last\):\n*", caplog)
+    assert log_has_re(f"Cannot query order for Trade\\(id=1, pair=ETH/BTC, amount=90.99181073, "
+                      f"open_rate=0.00001099, open_since="
+                      f"{open_date.strftime('%Y-%m-%d %H:%M:%S')} "
+                      f"\\(10 hours ago\\)\\) due to Traceback \\(most recent call last\\):\n*",
+                      caplog)
 
 
 def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
@@ -2896,7 +2900,8 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
                  }))
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog)
+    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%",
+                   caplog)
     assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000138501
 
@@ -2974,7 +2979,8 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
                  }))
 
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog)
+    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%",
+                   caplog)
     assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000117705
 

From 869a5b4901a27283c11b0f12be45f91915538fda Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Tue, 10 Sep 2019 13:45:30 +0300
Subject: [PATCH 140/227] Eliminate asyncio warnings in tests

---
 tests/exchange/test_exchange.py |  6 +++--
 tests/rpc/test_rpc.py           |  3 ++-
 tests/rpc/test_rpc_telegram.py  | 16 +++++++++-----
 tests/test_freqtradebot.py      | 39 ++++++++++++++++++++-------------
 4 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index 5ab7cdb47..9279e0026 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -69,7 +69,8 @@ def test_init(default_conf, mocker, caplog):
     assert log_has('Instance is running with dry_run enabled', caplog)
 
 
-def test_init_ccxt_kwargs(default_conf, mocker, caplog):
+@pytest.mark.asyncio
+async def test_init_ccxt_kwargs(default_conf, mocker, caplog):
     mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
     caplog.set_level(logging.INFO)
     conf = copy.deepcopy(default_conf)
@@ -1362,7 +1363,8 @@ def test_get_order(default_conf, mocker, exchange_name):
 
 
 @pytest.mark.parametrize("exchange_name", EXCHANGES)
-def test_name(default_conf, mocker, exchange_name):
+@pytest.mark.asyncio
+async def test_name(default_conf, mocker, exchange_name):
     mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
     default_conf['exchange']['name'] = exchange_name
     exchange = Exchange(default_conf)
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index ff5869009..265f24ab9 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -26,7 +26,8 @@ def prec_satoshi(a, b) -> float:
 
 
 # Unit tests
-def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index cf741a7ea..4f1409362 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -723,8 +723,9 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None:
     assert 'reloading config' in msg_mock.call_args_list[0][0][0]
 
 
-def test_forcesell_handle(default_conf, update, ticker, fee,
-                          ticker_sell_up, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_forcesell_handle(default_conf, update, ticker, fee,
+                                ticker_sell_up, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
@@ -775,8 +776,9 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
     } == last_msg
 
 
-def test_forcesell_down_handle(default_conf, update, ticker, fee,
-                               ticker_sell_down, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_forcesell_down_handle(default_conf, update, ticker, fee,
+                                     ticker_sell_down, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
                  return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
@@ -923,7 +925,8 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
     assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
 
 
-def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
@@ -963,7 +966,8 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
     assert fbuy_mock.call_args_list[0][0][1] == 0.055
 
 
-def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index a67659d8c..4f68e6397 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -2198,7 +2198,8 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
     assert cancel_order_mock.call_count == 1
 
 
-def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -2244,7 +2245,9 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
     } == last_msg
 
 
-def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down,
+                                 markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -2291,9 +2294,10 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
     } == last_msg
 
 
-def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
-                                                        ticker_sell_down,
-                                                        markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
+                                                              ticker_sell_down,
+                                                              markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -2377,9 +2381,10 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee,
     assert log_has('Could not cancel stoploss order abcd', caplog)
 
 
-def test_execute_sell_with_stoploss_on_exchange(default_conf,
-                                                ticker, fee, ticker_sell_up,
-                                                markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_execute_sell_with_stoploss_on_exchange(default_conf,
+                                                      ticker, fee, ticker_sell_up,
+                                                      markets, mocker) -> None:
 
     default_conf['exchange']['name'] = 'binance'
     rpc_mock = patch_RPCManager(mocker)
@@ -2432,10 +2437,11 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf,
     assert rpc_mock.call_count == 2
 
 
-def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
-                                                         ticker, fee,
-                                                         limit_buy_order,
-                                                         markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
+                                                               ticker, fee,
+                                                               limit_buy_order,
+                                                               markets, mocker) -> None:
     default_conf['exchange']['name'] = 'binance'
     rpc_mock = patch_RPCManager(mocker)
     mocker.patch.multiple(
@@ -2499,8 +2505,9 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
     assert rpc_mock.call_count == 2
 
 
-def test_execute_sell_market_order(default_conf, ticker, fee,
-                                   ticker_sell_up, markets, mocker) -> None:
+@pytest.mark.asyncio
+async def test_execute_sell_market_order(default_conf, ticker, fee,
+                                         ticker_sell_up, markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
@@ -2676,7 +2683,9 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
     assert trade.sell_reason == SellType.SELL_SIGNAL.value
 
 
-def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, markets, mocker, caplog) -> None:
+@pytest.mark.asyncio
+async def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down,
+                            markets, mocker, caplog) -> None:
     patch_RPCManager(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',

From a9ecdc7764b81cccd401b98c47b695b4edeb7e73 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 11 Sep 2019 00:18:07 +0300
Subject: [PATCH 141/227] Use patched exchange instead

---
 tests/exchange/test_exchange.py | 10 ++----
 tests/rpc/test_rpc.py           |  5 ++-
 tests/rpc/test_rpc_telegram.py  | 30 ++++++------------
 tests/test_freqtradebot.py      | 55 ++++++++++++++-------------------
 4 files changed, 38 insertions(+), 62 deletions(-)

diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index 9279e0026..694a2228e 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -69,8 +69,7 @@ def test_init(default_conf, mocker, caplog):
     assert log_has('Instance is running with dry_run enabled', caplog)
 
 
-@pytest.mark.asyncio
-async def test_init_ccxt_kwargs(default_conf, mocker, caplog):
+def test_init_ccxt_kwargs(default_conf, mocker, caplog):
     mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
     caplog.set_level(logging.INFO)
     conf = copy.deepcopy(default_conf)
@@ -1363,11 +1362,8 @@ def test_get_order(default_conf, mocker, exchange_name):
 
 
 @pytest.mark.parametrize("exchange_name", EXCHANGES)
-@pytest.mark.asyncio
-async def test_name(default_conf, mocker, exchange_name):
-    mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
-    default_conf['exchange']['name'] = exchange_name
-    exchange = Exchange(default_conf)
+def test_name(default_conf, mocker, exchange_name):
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
 
     assert exchange.name == exchange_name.title()
     assert exchange.id == exchange_name
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index 265f24ab9..66468927f 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -26,12 +26,11 @@ def prec_satoshi(a, b) -> float:
 
 
 # Unit tests
-@pytest.mark.asyncio
-async def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
+def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 4f1409362..1e392703b 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -723,19 +723,17 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None:
     assert 'reloading config' in msg_mock.call_args_list[0][0][0]
 
 
-@pytest.mark.asyncio
-async def test_forcesell_handle(default_conf, update, ticker, fee,
-                                ticker_sell_up, markets, mocker) -> None:
+def test_forcesell_handle(default_conf, update, ticker, fee,
+                          ticker_sell_up, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets),
-        validate_pairs=MagicMock(return_value={})
     )
 
     freqtradebot = FreqtradeBot(default_conf)
@@ -776,20 +774,18 @@ async def test_forcesell_handle(default_conf, update, ticker, fee,
     } == last_msg
 
 
-@pytest.mark.asyncio
-async def test_forcesell_down_handle(default_conf, update, ticker, fee,
-                                     ticker_sell_down, markets, mocker) -> None:
+def test_forcesell_down_handle(default_conf, update, ticker, fee,
+                               ticker_sell_down, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
                  return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets),
-        validate_pairs=MagicMock(return_value={})
     )
 
     freqtradebot = FreqtradeBot(default_conf)
@@ -845,7 +841,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets),
-        validate_pairs=MagicMock(return_value={})
     )
     default_conf['max_open_trades'] = 4
     freqtradebot = FreqtradeBot(default_conf)
@@ -925,16 +920,14 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
     assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
 
 
-@pytest.mark.asyncio
-async def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
+def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         markets=PropertyMock(markets),
-        validate_pairs=MagicMock(return_value={})
         )
     fbuy_mock = MagicMock(return_value=None)
     mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
@@ -966,16 +959,14 @@ async def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
     assert fbuy_mock.call_args_list[0][0][1] == 0.055
 
 
-@pytest.mark.asyncio
-async def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> None:
+def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> None:
     mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         markets=PropertyMock(markets),
-        validate_pairs=MagicMock(return_value={})
     )
     freqtradebot = FreqtradeBot(default_conf)
     patch_get_signal(freqtradebot, (True, False))
@@ -1002,7 +993,6 @@ def test_performance_handle(default_conf, update, ticker, fee,
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(markets),
-        validate_pairs=MagicMock(return_value={})
     )
     mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
     freqtradebot = FreqtradeBot(default_conf)
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 4f68e6397..2e5014e0c 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -2198,12 +2198,11 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
     assert cancel_order_mock.call_count == 1
 
 
-@pytest.mark.asyncio
-async def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None:
+def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2245,13 +2244,11 @@ async def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, market
     } == last_msg
 
 
-@pytest.mark.asyncio
-async def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down,
-                                 markets, mocker) -> None:
+def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2294,14 +2291,13 @@ async def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down,
     } == last_msg
 
 
-@pytest.mark.asyncio
-async def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
-                                                              ticker_sell_down,
-                                                              markets, mocker) -> None:
+def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
+                                                        ticker_sell_down,
+                                                        markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2356,9 +2352,9 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee,
     freqtrade = get_patched_freqtradebot(mocker, default_conf)
     mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
     sellmock = MagicMock()
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets),
@@ -2381,16 +2377,15 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee,
     assert log_has('Could not cancel stoploss order abcd', caplog)
 
 
-@pytest.mark.asyncio
-async def test_execute_sell_with_stoploss_on_exchange(default_conf,
-                                                      ticker, fee, ticker_sell_up,
-                                                      markets, mocker) -> None:
+def test_execute_sell_with_stoploss_on_exchange(default_conf,
+                                                ticker, fee, ticker_sell_up,
+                                                markets, mocker) -> None:
 
     default_conf['exchange']['name'] = 'binance'
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2437,16 +2432,15 @@ async def test_execute_sell_with_stoploss_on_exchange(default_conf,
     assert rpc_mock.call_count == 2
 
 
-@pytest.mark.asyncio
-async def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
-                                                               ticker, fee,
-                                                               limit_buy_order,
-                                                               markets, mocker) -> None:
+def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
+                                                         ticker, fee,
+                                                         limit_buy_order,
+                                                         markets, mocker) -> None:
     default_conf['exchange']['name'] = 'binance'
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2505,13 +2499,12 @@ async def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
     assert rpc_mock.call_count == 2
 
 
-@pytest.mark.asyncio
-async def test_execute_sell_market_order(default_conf, ticker, fee,
-                                         ticker_sell_up, markets, mocker) -> None:
+def test_execute_sell_market_order(default_conf, ticker, fee,
+                                   ticker_sell_up, markets, mocker) -> None:
     rpc_mock = patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)
@@ -2683,13 +2676,11 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
     assert trade.sell_reason == SellType.SELL_SIGNAL.value
 
 
-@pytest.mark.asyncio
-async def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down,
-                            markets, mocker, caplog) -> None:
+def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, markets, mocker, caplog) -> None:
     patch_RPCManager(mocker)
+    patch_exchange(mocker)
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
-        _load_markets=MagicMock(return_value={}),
         get_ticker=ticker,
         get_fee=fee,
         markets=PropertyMock(return_value=markets)

From c01953daf2a68f9e51fade461f87151c65255c81 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 11 Sep 2019 06:57:58 +0200
Subject: [PATCH 142/227] Remove kraken block

---
 freqtrade/exchange/exchange.py | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
index d5c4a6b1c..d48d18ebf 100644
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -30,9 +30,6 @@ BAD_EXCHANGES = {
     "bitmex": "Various reasons",
     "bitstamp": "Does not provide history. "
                 "Details in https://github.com/freqtrade/freqtrade/issues/1983",
-    "kraken": "TEMPORARY: Balance does not report free balance, so freqtrade will not know "
-              "if enough balance is available."
-              "Details in https://github.com/freqtrade/freqtrade/issues/1687#issuecomment-528509266"
     }
 
 

From ac413c65dcc7839fdb8458cd324ce1794b11d352 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 11 Sep 2019 09:52:09 +0300
Subject: [PATCH 143/227] Clean up the use of patch_exchange

---
 tests/conftest.py              | 2 +-
 tests/rpc/test_rpc_telegram.py | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/conftest.py b/tests/conftest.py
index a007aeb7e..8a5ba6683 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -117,7 +117,7 @@ def patch_freqtradebot(mocker, config) -> None:
     """
     mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
     persistence.init(config['db_url'])
-    patch_exchange(mocker, None)
+    patch_exchange(mocker)
     mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
     mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
 
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 1e392703b..a776ad5df 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -90,7 +90,7 @@ def test_cleanup(default_conf, mocker) -> None:
 
 
 def test_authorized_only(default_conf, mocker, caplog) -> None:
-    patch_exchange(mocker, None)
+    patch_exchange(mocker)
 
     chat = Chat(0, 0)
     update = Update(randint(1, 100))
@@ -108,7 +108,7 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
 
 
 def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
-    patch_exchange(mocker, None)
+    patch_exchange(mocker)
     chat = Chat(0xdeadbeef, 0)
     update = Update(randint(1, 100))
     update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat)

From 2bd59de00243bb30ebbf24692963c002f5b566eb Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 11 Sep 2019 10:56:02 +0300
Subject: [PATCH 144/227] Cleanup log_has_re regexp string

---
 tests/test_freqtradebot.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index cb6259c22..ac2014cd5 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -2151,10 +2151,10 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
     Trade.session.add(trade_buy)
 
     freqtrade.check_handle_timedout()
-    assert log_has_re(f"Cannot query order for Trade\\(id=1, pair=ETH/BTC, amount=90.99181073, "
-                      f"open_rate=0.00001099, open_since="
+    assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
+                      r"open_rate=0.00001099, open_since="
                       f"{open_date.strftime('%Y-%m-%d %H:%M:%S')} "
-                      f"\\(10 hours ago\\)\\) due to Traceback \\(most recent call last\\):\n*",
+                      r"\(10 hours ago\)\) due to Traceback \(most recent call last\):\n*",
                       caplog)
 
 

From 3b4bbe7a18718fa00f4aa7161ad7261da97d2677 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 11 Sep 2019 06:58:10 +0200
Subject: [PATCH 145/227] Implement get_balances which uses open_orders

---
 freqtrade/exchange/kraken.py | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py
index 91b41a159..df74a762d 100644
--- a/freqtrade/exchange/kraken.py
+++ b/freqtrade/exchange/kraken.py
@@ -2,7 +2,11 @@
 import logging
 from typing import Dict
 
+import ccxt
+
+from freqtrade import OperationalException, TemporaryError
 from freqtrade.exchange import Exchange
+from freqtrade.exchange.exchange import retrier
 
 logger = logging.getLogger(__name__)
 
@@ -10,3 +14,30 @@ logger = logging.getLogger(__name__)
 class Kraken(Exchange):
 
     _params: Dict = {"trading_agreement": "agree"}
+
+    @retrier
+    def get_balances(self) -> dict:
+        if self._config['dry_run']:
+            return {}
+
+        try:
+            balances = self._api.fetch_balance()
+            # Remove additional info from ccxt results
+            balances.pop("info", None)
+            balances.pop("free", None)
+            balances.pop("total", None)
+            balances.pop("used", None)
+
+            orders = self._api.fetch_open_orders()
+            order_list = [[x["symbol"].split("/")[0 if x["side"] == "sell" else 1],
+                           x["remaining"], x["side"], x["amount"], ] for x in orders]
+            for bal in balances:
+                balances[bal]['used'] = sum(order[1] for order in order_list if order[0] == bal)
+                balances[bal]['free'] = balances[bal]['total'] - balances[bal]['used']
+
+            return balances
+        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
+            raise TemporaryError(
+                f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e
+        except ccxt.BaseError as e:
+            raise OperationalException(e) from e

From f8eb1cd58a47ac6ca6fa21a5ebe8502013cb8796 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 11 Sep 2019 19:43:29 +0200
Subject: [PATCH 146/227] Add tests for kraken balance implementation

---
 tests/exchange/test_exchange.py |  3 +-
 tests/exchange/test_kraken.py   | 82 +++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index 5ab7cdb47..72b3af206 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -867,7 +867,7 @@ def test_get_balance_dry_run(default_conf, mocker):
 @pytest.mark.parametrize("exchange_name", EXCHANGES)
 def test_get_balance_prod(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
-    api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
+    api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4, 'total': 123.4}})
     default_conf['dry_run'] = False
 
     exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
@@ -883,6 +883,7 @@ def test_get_balance_prod(default_conf, mocker, exchange_name):
     with pytest.raises(TemporaryError, match=r'.*balance due to malformed exchange response:.*'):
         exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
+        mocker.patch('freqtrade.exchange.Kraken.get_balances', MagicMock(return_value={}))
         exchange.get_balance(currency='BTC')
 
 
diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py
index ba94f8b45..3ad62d85a 100644
--- a/tests/exchange/test_kraken.py
+++ b/tests/exchange/test_kraken.py
@@ -4,6 +4,7 @@ from random import randint
 from unittest.mock import MagicMock
 
 from tests.conftest import get_patched_exchange
+from tests.exchange.test_exchange import ccxt_exceptionhandlers
 
 
 def test_buy_kraken_trading_agreement(default_conf, mocker):
@@ -67,3 +68,84 @@ def test_sell_kraken_trading_agreement(default_conf, mocker):
     assert api_mock.create_order.call_args[0][3] == 1
     assert api_mock.create_order.call_args[0][4] is None
     assert api_mock.create_order.call_args[0][5] == {'trading_agreement': 'agree'}
+
+
+def test_get_balances_prod(default_conf, mocker):
+    balance_item = {
+        'free': None,
+        'total': 10.0,
+        'used': 0.0
+    }
+
+    api_mock = MagicMock()
+    api_mock.fetch_balance = MagicMock(return_value={
+        '1ST': balance_item.copy(),
+        '2ST': balance_item.copy(),
+        '3ST': balance_item.copy(),
+        '4ST': balance_item.copy(),
+    })
+    kraken_open_orders = [{'symbol': '1ST/EUR',
+                           'type': 'limit',
+                           'side': 'sell',
+                           'price': 20,
+                           'cost': 0.0,
+                           'amount': 1.0,
+                           'filled': 0.0,
+                           'average': 0.0,
+                           'remaining': 1.0,
+                           },
+                          {'status': 'open',
+                           'symbol': '2ST/EUR',
+                           'type': 'limit',
+                           'side': 'sell',
+                           'price': 20.0,
+                           'cost': 0.0,
+                           'amount': 2.0,
+                           'filled': 0.0,
+                           'average': 0.0,
+                           'remaining': 2.0,
+                           },
+                          {'status': 'open',
+                           'symbol': '2ST/USD',
+                           'type': 'limit',
+                           'side': 'sell',
+                           'price': 20.0,
+                           'cost': 0.0,
+                           'amount': 2.0,
+                           'filled': 0.0,
+                           'average': 0.0,
+                           'remaining': 2.0,
+                           },
+                          {'status': 'open',
+                           'symbol': 'BTC/3ST',
+                           'type': 'limit',
+                           'side': 'buy',
+                           'price': 20,
+                           'cost': 0.0,
+                           'amount': 3.0,
+                           'filled': 0.0,
+                           'average': 0.0,
+                           'remaining': 3.0,
+                           }]
+    api_mock.fetch_open_orders = MagicMock(return_value=kraken_open_orders)
+    default_conf['dry_run'] = False
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
+    balances = exchange.get_balances()
+    assert len(balances) == 4
+    assert balances['1ST']['free'] == 9.0
+    assert balances['1ST']['total'] == 10.0
+    assert balances['1ST']['used'] == 1.0
+
+    assert balances['2ST']['free'] == 6.0
+    assert balances['2ST']['total'] == 10.0
+    assert balances['2ST']['used'] == 4.0
+
+    assert balances['3ST']['free'] == 7.0
+    assert balances['3ST']['total'] == 10.0
+    assert balances['3ST']['used'] == 3.0
+
+    assert balances['4ST']['free'] == 10.0
+    assert balances['4ST']['total'] == 10.0
+    assert balances['4ST']['used'] == 0.0
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken",
+                           "get_balances", "fetch_balance")

From 9bdfaf3803f17588bcb1546770da3bbac0cafd79 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 11 Sep 2019 23:32:08 +0300
Subject: [PATCH 147/227] Remove quotes around the pairs

---
 freqtrade/optimize/backtesting.py |  6 +++---
 freqtrade/persistence.py          |  8 ++++----
 freqtrade/strategy/interface.py   | 20 ++++++++++----------
 tests/test_freqtradebot.py        | 28 ++++++++++++++--------------
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 752a83d2d..acfe8392a 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -248,7 +248,7 @@ class Backtesting(object):
             fee_close=self.fee,
             is_open=True,
         )
-        logger.debug(f"'{pair}' - Backtesting emulates creation of new trade: {trade}.")
+        logger.debug(f"{pair} - Backtesting emulates creation of new trade: {trade}.")
         # calculate win/lose forwards from buy point
         for sell_row in partial_ticker:
             if max_open_trades > 0:
@@ -305,7 +305,7 @@ class Backtesting(object):
                                     close_rate=sell_row.open,
                                     sell_reason=SellType.FORCE_SELL
                                     )
-            logger.debug(f"'{pair}' - Force selling still open trade, "
+            logger.debug(f"{pair} - Force selling still open trade, "
                          f"profit percent: {bt_res.profit_percent}, "
                          f"profit abs: {bt_res.profit_abs}")
 
@@ -388,7 +388,7 @@ class Backtesting(object):
                                                          max_open_trades)
 
                 if trade_entry:
-                    logger.debug(f"'{pair}' - Locking pair till "
+                    logger.debug(f"{pair} - Locking pair till "
                                  f"close_time={trade_entry.close_time}")
                     lock_pair_until[pair] = trade_entry.close_time
                     trades.append(trade_entry)
diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index be9374ebe..ea5188e0c 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -260,7 +260,7 @@ class Trade(_DECL_BASE):
 
         # no stop loss assigned yet
         if not self.stop_loss:
-            logger.debug(f"'{self.pair}' - Assigning new stoploss...")
+            logger.debug(f"{self.pair} - Assigning new stoploss...")
             self.stop_loss = new_loss
             self.stop_loss_pct = -1 * abs(stoploss)
             self.initial_stop_loss = new_loss
@@ -270,15 +270,15 @@ class Trade(_DECL_BASE):
         # evaluate if the stop loss needs to be updated
         else:
             if new_loss > self.stop_loss:  # stop losses only walk up, never down!
-                logger.debug(f"'{self.pair}' - Adjusting stoploss...")
+                logger.debug(f"{self.pair} - Adjusting stoploss...")
                 self.stop_loss = new_loss
                 self.stop_loss_pct = -1 * abs(stoploss)
                 self.stoploss_last_update = datetime.utcnow()
             else:
-                logger.debug(f"'{self.pair}' - Keeping current stoploss...")
+                logger.debug(f"{self.pair} - Keeping current stoploss...")
 
         logger.debug(
-            f"'{self.pair}' - Stoploss adjusted. Current price {current_price:.8f}, "
+            f"{self.pair} - Stoploss adjusted. Current price {current_price:.8f}, "
             f"bought at {self.open_rate:.8f} and calculated "
             f"stoploss is at: {self.initial_stop_loss:.8f}, "
             f"initial stop at {self.stop_loss:.8f}. "
diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py
index d4b89a3ae..1c4b8e669 100644
--- a/freqtrade/strategy/interface.py
+++ b/freqtrade/strategy/interface.py
@@ -302,7 +302,7 @@ class IStrategy(ABC):
                                               force_stoploss=force_stoploss, high=high)
 
         if stoplossflag.sell_flag:
-            logger.debug(f"'{trade.pair}' - Stoploss hit. Selling "
+            logger.debug(f"{trade.pair} - Stoploss hit. Selling "
                          f"(sell_type={stoplossflag.sell_type})...")
             return stoplossflag
 
@@ -312,30 +312,30 @@ class IStrategy(ABC):
         experimental = self.config.get('experimental', {})
 
         if buy and experimental.get('ignore_roi_if_buy_signal', False):
-            logger.debug(f"'{trade.pair}' - Buy signal still active. Not selling "
+            logger.debug(f"{trade.pair} - Buy signal still active. Not selling "
                          f"(sell_type=SellType.NONE)...")
             return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
         if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
-            logger.debug(f"'{trade.pair}' - Required profit reached. Selling "
+            logger.debug(f"{trade.pair} - Required profit reached. Selling "
                          f"(sell_type=SellType.ROI)...")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
 
         if experimental.get('sell_profit_only', False):
-            logger.debug(f"'{trade.pair}' - Checking if trade is profitable...")
+            logger.debug(f"{trade.pair} - Checking if trade is profitable...")
             if trade.calc_profit(rate=rate) <= 0:
-                logger.debug(f"'{trade.pair}' - Trade is not profitable. Not selling "
+                logger.debug(f"{trade.pair} - Trade is not profitable. Not selling "
                              f"(sell_type=SellType.NONE)...")
                 return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         if sell and not buy and experimental.get('use_sell_signal', False):
-            logger.debug(f"'{trade.pair}' - Sell signal received. Selling "
+            logger.debug(f"{trade.pair} - Sell signal received. Selling "
                          f"(sell_type=SellType.SELL_SIGNAL)...")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
 
         # This one is noisy, commented out...
-#        logger.debug(f"'{trade.pair}' - Not selling (sell_type=SellType.NONE)...")
+#        logger.debug(f"{trade.pair} - Not selling (sell_type=SellType.NONE)...")
         return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
     def stop_loss_reached(self, current_rate: float, trade: Trade,
@@ -366,7 +366,7 @@ class IStrategy(ABC):
                 if 'trailing_stop_positive' in self.config and high_profit > sl_offset:
                     # Ignore mypy error check in configuration that this is a float
                     stop_loss_value = self.config.get('trailing_stop_positive')  # type: ignore
-                    logger.debug(f"'{trade.pair}' - Using positive stoploss: {stop_loss_value} "
+                    logger.debug(f"{trade.pair} - Using positive stoploss: {stop_loss_value} "
                                  f"offset: {sl_offset:.4g} profit: {current_profit:.4f}%")
 
                 trade.adjust_stop_loss(high or current_rate, stop_loss_value)
@@ -382,11 +382,11 @@ class IStrategy(ABC):
             if trade.initial_stop_loss != trade.stop_loss:
                 sell_type = SellType.TRAILING_STOP_LOSS
                 logger.debug(
-                    f"'{trade.pair}' - HIT STOP: current price at {current_rate:.6f}, "
+                    f"{trade.pair} - HIT STOP: current price at {current_rate:.6f}, "
                     f"stoploss is {trade.stop_loss:.6f}, "
                     f"initial stoploss was at {trade.initial_stop_loss:.6f}, "
                     f"trade opened at {trade.open_rate:.6f}")
-                logger.debug(f"'{trade.pair}' - Trailing stop saved "
+                logger.debug(f"{trade.pair} - Trailing stop saved "
                              f"{trade.stop_loss - trade.initial_stop_loss:.6f}")
 
             return SellCheckTuple(sell_flag=True, sell_type=sell_type)
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index ac2014cd5..7a74fc333 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -1823,7 +1823,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
     # if ROI is reached we must sell
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("'ETH/BTC' - Required profit reached. Selling (sell_type=SellType.ROI)...",
+    assert log_has("ETH/BTC - Required profit reached. Selling (sell_type=SellType.ROI)...",
                    caplog)
 
 
@@ -1854,7 +1854,7 @@ def test_handle_trade_experimental(
 
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("'ETH/BTC' - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...",
+    assert log_has("ETH/BTC - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...",
                    caplog)
 
 
@@ -2798,7 +2798,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
     # Sell as trailing-stop is reached
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f"'ETH/BTC' - HIT STOP: current price at 0.000012, "
+        f"ETH/BTC - HIT STOP: current price at 0.000012, "
         f"stoploss is 0.000015, "
         f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@@ -2842,8 +2842,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
                  }))
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
-    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
+    assert log_has(f"ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
+    assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000138501
 
     mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@@ -2855,7 +2855,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
     # Lower price again (but still positive)
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f"'ETH/BTC' - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
+        f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
         f"stoploss is {trade.stop_loss:.6f}, "
         f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
 
@@ -2900,9 +2900,9 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
                  }))
     # stop-loss not reached, adjusted stoploss
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%",
+    assert log_has(f"ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%",
                    caplog)
-    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
+    assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000138501
 
     mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@@ -2914,7 +2914,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
     # Lower price again (but still positive)
     assert freqtrade.handle_trade(trade) is True
     assert log_has(
-        f"'ETH/BTC' - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
+        f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
         f"stoploss is {trade.stop_loss:.6f}, "
         f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
     assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@@ -2967,7 +2967,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
     # stop-loss should not be adjusted as offset is not reached yet
     assert freqtrade.handle_trade(trade) is False
 
-    assert not log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
+    assert not log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000098910
 
     # price rises above the offset (rises 12% when the offset is 5.5%)
@@ -2979,9 +2979,9 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
                  }))
 
     assert freqtrade.handle_trade(trade) is False
-    assert log_has(f"'ETH/BTC' - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%",
+    assert log_has(f"ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%",
                    caplog)
-    assert log_has(f"'ETH/BTC' - Adjusting stoploss...", caplog)
+    assert log_has(f"ETH/BTC - Adjusting stoploss...", caplog)
     assert trade.stop_loss == 0.0000117705
 
 
@@ -3350,8 +3350,8 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets)
     default_conf['telegram']['enabled'] = False
 
     freqtrade = FreqtradeBot(default_conf)
-    # ordrebook shall be used even if tickers would be lower.
-    assert freqtrade.get_target_bid('ETH/BTC', ) != 0.042
+    # orderbook shall be used even if tickers would be lower.
+    assert freqtrade.get_target_bid('ETH/BTC') != 0.042
     assert ticker_mock.call_count == 0
 
 

From acf3b751f01e6d76722c31ed3d94c5d1140a5b24 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 01:21:14 +0300
Subject: [PATCH 148/227] Log sell_flag, do not log sell_type=SellType.NONE

---
 freqtrade/strategy/interface.py | 20 +++++++++-----------
 tests/test_freqtradebot.py      |  4 ++--
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py
index 1c4b8e669..3c1df276c 100644
--- a/freqtrade/strategy/interface.py
+++ b/freqtrade/strategy/interface.py
@@ -302,8 +302,8 @@ class IStrategy(ABC):
                                               force_stoploss=force_stoploss, high=high)
 
         if stoplossflag.sell_flag:
-            logger.debug(f"{trade.pair} - Stoploss hit. Selling "
-                         f"(sell_type={stoplossflag.sell_type})...")
+            logger.debug(f"{trade.pair} - Stoploss hit. sell_flag=True, "
+                         f"sell_type={stoplossflag.sell_type}")
             return stoplossflag
 
         # Set current rate to high for backtesting sell
@@ -312,30 +312,28 @@ class IStrategy(ABC):
         experimental = self.config.get('experimental', {})
 
         if buy and experimental.get('ignore_roi_if_buy_signal', False):
-            logger.debug(f"{trade.pair} - Buy signal still active. Not selling "
-                         f"(sell_type=SellType.NONE)...")
+            logger.debug(f"{trade.pair} - Buy signal still active. sell_flag=False")
             return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
         if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
-            logger.debug(f"{trade.pair} - Required profit reached. Selling "
-                         f"(sell_type=SellType.ROI)...")
+            logger.debug(f"{trade.pair} - Required profit reached. sell_flag=True, "
+                         f"sell_type=SellType.ROI")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
 
         if experimental.get('sell_profit_only', False):
             logger.debug(f"{trade.pair} - Checking if trade is profitable...")
             if trade.calc_profit(rate=rate) <= 0:
-                logger.debug(f"{trade.pair} - Trade is not profitable. Not selling "
-                             f"(sell_type=SellType.NONE)...")
+                logger.debug(f"{trade.pair} - Trade is not profitable. sell_flag=False")
                 return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         if sell and not buy and experimental.get('use_sell_signal', False):
-            logger.debug(f"{trade.pair} - Sell signal received. Selling "
-                         f"(sell_type=SellType.SELL_SIGNAL)...")
+            logger.debug(f"{trade.pair} - Sell signal received. sell_flag=True, "
+                         f"sell_type=SellType.SELL_SIGNAL")
             return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
 
         # This one is noisy, commented out...
-#        logger.debug(f"{trade.pair} - Not selling (sell_type=SellType.NONE)...")
+#        logger.debug(f"{trade.pair} - No sell signal. sell_flag=False")
         return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
     def stop_loss_reached(self, current_rate: float, trade: Trade,
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 7a74fc333..d34197519 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -1823,7 +1823,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
     # if ROI is reached we must sell
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("ETH/BTC - Required profit reached. Selling (sell_type=SellType.ROI)...",
+    assert log_has("ETH/BTC - Required profit reached. sell_flag=True, sell_type=SellType.ROI",
                    caplog)
 
 
@@ -1854,7 +1854,7 @@ def test_handle_trade_experimental(
 
     patch_get_signal(freqtrade, value=(False, True))
     assert freqtrade.handle_trade(trade)
-    assert log_has("ETH/BTC - Sell signal received. Selling (sell_type=SellType.SELL_SIGNAL)...",
+    assert log_has("ETH/BTC - Sell signal received. sell_flag=True, sell_type=SellType.SELL_SIGNAL",
                    caplog)
 
 

From 1d781ea9e0591f3589e11c42612380681119bb77 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 02:29:47 +0300
Subject: [PATCH 149/227] Refine 'stoploss adjusted' log message

---
 freqtrade/persistence.py | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index ea5188e0c..e497a16c7 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -278,13 +278,12 @@ class Trade(_DECL_BASE):
                 logger.debug(f"{self.pair} - Keeping current stoploss...")
 
         logger.debug(
-            f"{self.pair} - Stoploss adjusted. Current price {current_price:.8f}, "
-            f"bought at {self.open_rate:.8f} and calculated "
-            f"stoploss is at: {self.initial_stop_loss:.8f}, "
-            f"initial stop at {self.stop_loss:.8f}. "
+            f"{self.pair} - Stoploss adjusted. current_price={current_price:.8f}, "
+            f"open_rate={self.open_rate:.8f}, max_rate={self.max_rate:.8f}, "
+            f"initial_stop_loss={self.initial_stop_loss:.8f}, "
+            f"stop_loss={self.stop_loss:.8f}. "
             f"Trailing stoploss saved us: "
-            f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f} "
-            f"and max observed rate was {self.max_rate:.8f}.")
+            f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.")
 
     def update(self, order: Dict) -> None:
         """

From 849d694c27f0a996ce128d36b4bf9a26a8c54cdd Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 04:39:52 +0300
Subject: [PATCH 150/227] Don't inherit from object

---
 freqtrade/configuration/arguments.py     | 2 +-
 freqtrade/configuration/configuration.py | 2 +-
 freqtrade/exchange/exchange.py           | 2 +-
 freqtrade/freqtradebot.py                | 2 +-
 freqtrade/optimize/backtesting.py        | 2 +-
 freqtrade/optimize/edge_cli.py           | 2 +-
 freqtrade/resolvers/iresolver.py         | 2 +-
 freqtrade/rpc/fiat_convert.py            | 4 ++--
 freqtrade/rpc/rpc.py                     | 2 +-
 freqtrade/rpc/rpc_manager.py             | 2 +-
 freqtrade/wallets.py                     | 2 +-
 freqtrade/worker.py                      | 2 +-
 12 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index 10350d886..befa42cd1 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -44,7 +44,7 @@ ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
 NO_CONF_REQURIED = ["download-data", "plot-dataframe", "plot-profit"]
 
 
-class Arguments(object):
+class Arguments:
     """
     Arguments Class. Manage the arguments received by the cli
     """
diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index b1bd3ef1c..8871d1113 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -22,7 +22,7 @@ from freqtrade.state import RunMode
 logger = logging.getLogger(__name__)
 
 
-class Configuration(object):
+class Configuration:
     """
     Class to read and init the bot configuration
     Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
index d5c4a6b1c..63039e69a 100644
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -72,7 +72,7 @@ def retrier(f):
     return wrapper
 
 
-class Exchange(object):
+class Exchange:
 
     _config: Dict = {}
     _params: Dict = {}
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index e889402f0..f56b1f2ea 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -29,7 +29,7 @@ from freqtrade.wallets import Wallets
 logger = logging.getLogger(__name__)
 
 
-class FreqtradeBot(object):
+class FreqtradeBot:
     """
     Freqtrade is the main class of the bot.
     This is from here the bot start its logic.
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 708b60144..d8dc971c7 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -44,7 +44,7 @@ class BacktestResult(NamedTuple):
     sell_reason: SellType
 
 
-class Backtesting(object):
+class Backtesting:
     """
     Backtesting class, this class contains all the logic to run a backtest
 
diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py
index 7e0d60843..f22bcb642 100644
--- a/freqtrade/optimize/edge_cli.py
+++ b/freqtrade/optimize/edge_cli.py
@@ -16,7 +16,7 @@ from freqtrade.resolvers import StrategyResolver
 logger = logging.getLogger(__name__)
 
 
-class EdgeCli(object):
+class EdgeCli:
     """
     EdgeCli class, this class contains all the logic to run edge backtesting
 
diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py
index 8d93af568..6303d4801 100644
--- a/freqtrade/resolvers/iresolver.py
+++ b/freqtrade/resolvers/iresolver.py
@@ -12,7 +12,7 @@ from typing import Any, List, Optional, Tuple, Type, Union
 logger = logging.getLogger(__name__)
 
 
-class IResolver(object):
+class IResolver:
     """
     This class contains all the logic to load custom classes
     """
diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py
index 74ccc62ed..d40f9221e 100644
--- a/freqtrade/rpc/fiat_convert.py
+++ b/freqtrade/rpc/fiat_convert.py
@@ -15,7 +15,7 @@ from freqtrade.constants import SUPPORTED_FIAT
 logger = logging.getLogger(__name__)
 
 
-class CryptoFiat(object):
+class CryptoFiat:
     """
     Object to describe what is the price of Crypto-currency in a FIAT
     """
@@ -60,7 +60,7 @@ class CryptoFiat(object):
         return self._expiration - time.time() <= 0
 
 
-class CryptoToFiatConverter(object):
+class CryptoToFiatConverter:
     """
     Main class to initiate Crypto to FIAT.
     This object contains a list of pair Crypto, FIAT
diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index 213812a70..f994ac006 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -54,7 +54,7 @@ class RPCException(Exception):
         }
 
 
-class RPC(object):
+class RPC:
     """
     RPC class can be used to have extra feature, like bot data, and access to DB data
     """
diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py
index d6e7b174d..802550b94 100644
--- a/freqtrade/rpc/rpc_manager.py
+++ b/freqtrade/rpc/rpc_manager.py
@@ -9,7 +9,7 @@ from freqtrade.rpc import RPC, RPCMessageType
 logger = logging.getLogger(__name__)
 
 
-class RPCManager(object):
+class RPCManager:
     """
     Class to manage RPC objects (Telegram, Slack, ...)
     """
diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py
index c8ab90276..90b68c49d 100644
--- a/freqtrade/wallets.py
+++ b/freqtrade/wallets.py
@@ -17,7 +17,7 @@ class Wallet(NamedTuple):
     total: float = 0
 
 
-class Wallets(object):
+class Wallets:
 
     def __init__(self, config: dict, exchange: Exchange) -> None:
         self._config = config
diff --git a/freqtrade/worker.py b/freqtrade/worker.py
index df792e35e..7a5360824 100755
--- a/freqtrade/worker.py
+++ b/freqtrade/worker.py
@@ -19,7 +19,7 @@ from freqtrade.rpc import RPCMessageType
 logger = logging.getLogger(__name__)
 
 
-class Worker(object):
+class Worker:
     """
     Freqtradebot worker class
     """

From 6c5eff4a7c68d6bfeb9f6a0ed3cfdba9dc7bd833 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 12 Sep 2019 07:03:52 +0200
Subject: [PATCH 151/227] Use List of Tuples, remove unused columns

---
 freqtrade/exchange/kraken.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py
index df74a762d..6d3e82eca 100644
--- a/freqtrade/exchange/kraken.py
+++ b/freqtrade/exchange/kraken.py
@@ -29,8 +29,11 @@ class Kraken(Exchange):
             balances.pop("used", None)
 
             orders = self._api.fetch_open_orders()
-            order_list = [[x["symbol"].split("/")[0 if x["side"] == "sell" else 1],
-                           x["remaining"], x["side"], x["amount"], ] for x in orders]
+            order_list = [(x["symbol"].split("/")[0 if x["side"] == "sell" else 1],
+                           x["remaining"],
+                           # Don't remove the below comment, this can be important for debuggung
+                           # x["side"], x["amount"],
+                           ) for x in orders]
             for bal in balances:
                 balances[bal]['used'] = sum(order[1] for order in order_list if order[0] == bal)
                 balances[bal]['free'] = balances[bal]['total'] - balances[bal]['used']

From dda513c9237628a87294acaecf48368ae4637cd5 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 12:13:20 +0300
Subject: [PATCH 152/227] Minor class cosmetics

---
 freqtrade/configuration/timerange.py | 2 +-
 freqtrade/data/dataprovider.py       | 2 +-
 freqtrade/edge/__init__.py           | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/freqtrade/configuration/timerange.py b/freqtrade/configuration/timerange.py
index f980b71ea..ec6eb75e6 100644
--- a/freqtrade/configuration/timerange.py
+++ b/freqtrade/configuration/timerange.py
@@ -7,7 +7,7 @@ from typing import Optional
 import arrow
 
 
-class TimeRange():
+class TimeRange:
     """
     object defining timerange inputs.
     [start/stop]type defines if [start/stop]ts shall be used.
diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py
index 7e2a6b407..eb6ec0f2a 100644
--- a/freqtrade/data/dataprovider.py
+++ b/freqtrade/data/dataprovider.py
@@ -17,7 +17,7 @@ from freqtrade.state import RunMode
 logger = logging.getLogger(__name__)
 
 
-class DataProvider():
+class DataProvider:
 
     def __init__(self, config: dict, exchange: Exchange) -> None:
         self._config = config
diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py
index 807fd8825..66a777ce5 100644
--- a/freqtrade/edge/__init__.py
+++ b/freqtrade/edge/__init__.py
@@ -28,7 +28,7 @@ class PairInfo(NamedTuple):
     avg_trade_duration: float
 
 
-class Edge():
+class Edge:
     """
     Calculates Win Rate, Risk Reward Ratio, Expectancy
     against historical data for a give set of markets and a strategy

From 045ca8739dc5c35101e06906632d54865f79c7a3 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 20:56:00 +0300
Subject: [PATCH 153/227] Do not print humanized datetime in the log message

---
 freqtrade/persistence.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index e497a16c7..1850aafd9 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -210,8 +210,7 @@ class Trade(_DECL_BASE):
     ticker_interval = Column(Integer, nullable=True)
 
     def __repr__(self):
-        open_since = (f"{arrow.get(self.open_date).strftime('%Y-%m-%d %H:%M:%S')} "
-                      f"({arrow.get(self.open_date).humanize()})") if self.is_open else 'closed'
+        open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.is_open else 'closed'
 
         return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
                 f'open_rate={self.open_rate:.8f}, open_since={open_since})')

From 67ff48ce3e9dbca1fac36109d5cbb2ab66c03f7f Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 20:58:10 +0300
Subject: [PATCH 154/227] Comment out noisy log messages

---
 freqtrade/strategy/interface.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py
index 3c1df276c..17246ecf7 100644
--- a/freqtrade/strategy/interface.py
+++ b/freqtrade/strategy/interface.py
@@ -312,7 +312,8 @@ class IStrategy(ABC):
         experimental = self.config.get('experimental', {})
 
         if buy and experimental.get('ignore_roi_if_buy_signal', False):
-            logger.debug(f"{trade.pair} - Buy signal still active. sell_flag=False")
+            # This one is noisy, commented out
+            # logger.debug(f"{trade.pair} - Buy signal still active. sell_flag=False")
             return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
@@ -322,9 +323,11 @@ class IStrategy(ABC):
             return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
 
         if experimental.get('sell_profit_only', False):
-            logger.debug(f"{trade.pair} - Checking if trade is profitable...")
+            # This one is noisy, commented out
+            # logger.debug(f"{trade.pair} - Checking if trade is profitable...")
             if trade.calc_profit(rate=rate) <= 0:
-                logger.debug(f"{trade.pair} - Trade is not profitable. sell_flag=False")
+                # This one is noisy, commented out
+                # logger.debug(f"{trade.pair} - Trade is not profitable. sell_flag=False")
                 return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
         if sell and not buy and experimental.get('use_sell_signal', False):
@@ -333,7 +336,7 @@ class IStrategy(ABC):
             return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
 
         # This one is noisy, commented out...
-#        logger.debug(f"{trade.pair} - No sell signal. sell_flag=False")
+        # logger.debug(f"{trade.pair} - No sell signal. sell_flag=False")
         return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
 
     def stop_loss_reached(self, current_rate: float, trade: Trade,

From 52b186eabe5b37cf70642d324b37b9c7fe192d5e Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 12 Sep 2019 20:14:58 +0200
Subject: [PATCH 155/227] Create-userdir does not need a configuration

---
 freqtrade/configuration/arguments.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index befa42cd1..bb961173b 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -2,11 +2,11 @@
 This module contains the argument manager class
 """
 import argparse
-from typing import List, Optional
 from pathlib import Path
+from typing import Any, Dict, List, Optional
 
-from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
 from freqtrade import constants
+from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
 
 ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"]
 
@@ -41,7 +41,7 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_
 ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
                     "trade_source", "ticker_interval"]
 
-NO_CONF_REQURIED = ["download-data", "plot-dataframe", "plot-profit"]
+NO_CONF_REQURIED = ["create-userdir", "download-data", "plot-dataframe", "plot-profit"]
 
 
 class Arguments:
@@ -57,7 +57,7 @@ class Arguments:
         self._build_args(optionlist=ARGS_MAIN)
         self._build_subcommands()
 
-    def get_parsed_arg(self) -> argparse.Namespace:
+    def get_parsed_arg(self) -> Dict[str, Any]:
         """
         Return the list of arguments
         :return: List[str] List of arguments
@@ -66,7 +66,7 @@ class Arguments:
             self._load_args()
             self._parsed_arg = self._parse_args()
 
-        return self._parsed_arg
+        return vars(self._parsed_arg)
 
     def _parse_args(self) -> argparse.Namespace:
         """

From e6ccc1427cac52eda2af9ef92ba420633e75c898 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 12 Sep 2019 20:16:39 +0200
Subject: [PATCH 156/227] have Arguments return a dict instead of Namespace

---
 freqtrade/configuration/configuration.py | 61 ++++++++++++------------
 freqtrade/main.py                        |  6 +--
 freqtrade/optimize/__init__.py           |  9 ++--
 freqtrade/plot/plot_utils.py             | 12 ++---
 freqtrade/utils.py                       | 15 +++---
 freqtrade/worker.py                      | 13 +++--
 6 files changed, 56 insertions(+), 60 deletions(-)

diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index 8871d1113..1811cc6ed 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -3,7 +3,6 @@ This module contains the configuration class
 """
 import logging
 import warnings
-from argparse import Namespace
 from copy import deepcopy
 from pathlib import Path
 from typing import Any, Callable, Dict, List, Optional
@@ -28,7 +27,7 @@ class Configuration:
     Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
     """
 
-    def __init__(self, args: Namespace, runmode: RunMode = None) -> None:
+    def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
         self.args = args
         self.config: Optional[Dict[str, Any]] = None
         self.runmode = runmode
@@ -82,7 +81,7 @@ class Configuration:
         :return: Configuration dictionary
         """
         # Load all configs
-        config: Dict[str, Any] = Configuration.from_files(self.args.config)
+        config: Dict[str, Any] = Configuration.from_files(self.args["config"])
 
         self._process_common_options(config)
 
@@ -107,13 +106,13 @@ class Configuration:
         the -v/--verbose, --logfile options
         """
         # Log level
-        if 'verbosity' in self.args and self.args.verbosity:
-            config.update({'verbosity': self.args.verbosity})
+        if 'verbosity' in self.args and self.args["verbosity"]:
+            config.update({'verbosity': self.args["verbosity"]})
         else:
             config.update({'verbosity': 0})
 
-        if 'logfile' in self.args and self.args.logfile:
-            config.update({'logfile': self.args.logfile})
+        if 'logfile' in self.args and self.args["logfile"]:
+            config.update({'logfile': self.args["logfile"]})
 
         setup_logging(config)
 
@@ -122,15 +121,15 @@ class Configuration:
         self._process_logging_options(config)
 
         # Set strategy if not specified in config and or if it's non default
-        if self.args.strategy != constants.DEFAULT_STRATEGY or not config.get('strategy'):
-            config.update({'strategy': self.args.strategy})
+        if self.args["strategy"] != constants.DEFAULT_STRATEGY or not config.get('strategy'):
+            config.update({'strategy': self.args["strategy"]})
 
         self._args_to_config(config, argname='strategy_path',
                              logstring='Using additional Strategy lookup path: {}')
 
-        if ('db_url' in self.args and self.args.db_url and
-                self.args.db_url != constants.DEFAULT_DB_PROD_URL):
-            config.update({'db_url': self.args.db_url})
+        if ('db_url' in self.args and self.args["db_url"] and
+                self.args["db_url"] != constants.DEFAULT_DB_PROD_URL):
+            config.update({'db_url': self.args["db_url"]})
             logger.info('Parameter --db-url detected ...')
 
         if config.get('dry_run', False):
@@ -153,7 +152,7 @@ class Configuration:
             config['max_open_trades'] = float('inf')
 
         # Support for sd_notify
-        if 'sd_notify' in self.args and self.args.sd_notify:
+        if 'sd_notify' in self.args and self.args["sd_notify"]:
             config['internals'].update({'sd_notify': True})
 
     def _process_datadir_options(self, config: Dict[str, Any]) -> None:
@@ -162,12 +161,12 @@ class Configuration:
         --user-data, --datadir
         """
         # Check exchange parameter here - otherwise `datadir` might be wrong.
-        if "exchange" in self.args and self.args.exchange:
-            config['exchange']['name'] = self.args.exchange
+        if "exchange" in self.args and self.args["exchange"]:
+            config['exchange']['name'] = self.args["exchange"]
             logger.info(f"Using exchange {config['exchange']['name']}")
 
-        if 'user_data_dir' in self.args and self.args.user_data_dir:
-            config.update({'user_data_dir': self.args.user_data_dir})
+        if 'user_data_dir' in self.args and self.args["user_data_dir"]:
+            config.update({'user_data_dir': self.args["user_data_dir"]})
         elif 'user_data_dir' not in config:
             # Default to cwd/user_data (legacy option ...)
             config.update({'user_data_dir': str(Path.cwd() / "user_data")})
@@ -176,8 +175,8 @@ class Configuration:
         config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
         logger.info('Using user-data directory: %s ...', config['user_data_dir'])
 
-        if 'datadir' in self.args and self.args.datadir:
-            config.update({'datadir': create_datadir(config, self.args.datadir)})
+        if 'datadir' in self.args and self.args["datadir"]:
+            config.update({'datadir': create_datadir(config, self.args["datadir"])})
         else:
             config.update({'datadir': create_datadir(config, None)})
         logger.info('Using data directory: %s ...', config.get('datadir'))
@@ -192,12 +191,12 @@ class Configuration:
         self._args_to_config(config, argname='position_stacking',
                              logstring='Parameter --enable-position-stacking detected ...')
 
-        if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions:
+        if 'use_max_market_positions' in self.args and not self.args["use_max_market_positions"]:
             config.update({'use_max_market_positions': False})
             logger.info('Parameter --disable-max-market-positions detected ...')
             logger.info('max_open_trades set to unlimited ...')
-        elif 'max_open_trades' in self.args and self.args.max_open_trades:
-            config.update({'max_open_trades': self.args.max_open_trades})
+        elif 'max_open_trades' in self.args and self.args["max_open_trades"]:
+            config.update({'max_open_trades': self.args["max_open_trades"]})
             logger.info('Parameter --max_open_trades detected, '
                         'overriding max_open_trades to: %s ...', config.get('max_open_trades'))
         else:
@@ -229,12 +228,12 @@ class Configuration:
                              logstring='Storing backtest results to {} ...')
 
         # Edge section:
-        if 'stoploss_range' in self.args and self.args.stoploss_range:
-            txt_range = eval(self.args.stoploss_range)
+        if 'stoploss_range' in self.args and self.args["stoploss_range"]:
+            txt_range = eval(self.args["stoploss_range"])
             config['edge'].update({'stoploss_range_min': txt_range[0]})
             config['edge'].update({'stoploss_range_max': txt_range[1]})
             config['edge'].update({'stoploss_range_step': txt_range[2]})
-            logger.info('Parameter --stoplosses detected: %s ...', self.args.stoploss_range)
+            logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
 
         # Hyperopt section
         self._args_to_config(config, argname='hyperopt',
@@ -254,7 +253,7 @@ class Configuration:
         self._args_to_config(config, argname='print_all',
                              logstring='Parameter --print-all detected ...')
 
-        if 'print_colorized' in self.args and not self.args.print_colorized:
+        if 'print_colorized' in self.args and not self.args["print_colorized"]:
             logger.info('Parameter --no-color detected ...')
             config.update({'print_colorized': False})
         else:
@@ -324,9 +323,9 @@ class Configuration:
                         sample: logfun=len (prints the length of the found
                         configuration instead of the content)
         """
-        if argname in self.args and getattr(self.args, argname):
+        if argname in self.args and self.args[argname]:
 
-            config.update({argname: getattr(self.args, argname)})
+            config.update({argname: self.args[argname]})
             if logfun:
                 logger.info(logstring.format(logfun(config[argname])))
             else:
@@ -346,8 +345,8 @@ class Configuration:
         if "pairs" in config:
             return
 
-        if "pairs_file" in self.args and self.args.pairs_file:
-            pairs_file = Path(self.args.pairs_file)
+        if "pairs_file" in self.args and self.args["pairs_file"]:
+            pairs_file = Path(self.args["pairs_file"])
             logger.info(f'Reading pairs file "{pairs_file}".')
             # Download pairs from the pairs file if no config is specified
             # or if pairs file is specified explicitely
@@ -358,7 +357,7 @@ class Configuration:
                 config['pairs'].sort()
             return
 
-        if "config" in self.args and self.args.config:
+        if "config" in self.args and self.args["config"]:
             logger.info("Using pairlist from configuration.")
             config['pairs'] = config.get('exchange', {}).get('pair_whitelist')
         else:
diff --git a/freqtrade/main.py b/freqtrade/main.py
index e65aa5fbb..b8a22490c 100755
--- a/freqtrade/main.py
+++ b/freqtrade/main.py
@@ -32,12 +32,12 @@ def main(sysargv: List[str] = None) -> None:
     worker = None
     try:
         arguments = Arguments(sysargv)
-        args: Namespace = arguments.get_parsed_arg()
+        args = arguments.get_parsed_arg()
 
         # A subcommand has been issued.
         # Means if Backtesting or Hyperopt have been called we exit the bot
-        if hasattr(args, 'func'):
-            args.func(args)
+        if 'func' in args:
+            args['func'](args)
             # TODO: fetch return_code as returned by the command function here
             return_code = 0
         else:
diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py
index 973ea1ff5..7a3c290bf 100644
--- a/freqtrade/optimize/__init__.py
+++ b/freqtrade/optimize/__init__.py
@@ -1,5 +1,4 @@
 import logging
-from argparse import Namespace
 from typing import Any, Dict
 
 from filelock import FileLock, Timeout
@@ -12,7 +11,7 @@ from freqtrade.utils import setup_utils_configuration
 logger = logging.getLogger(__name__)
 
 
-def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
+def setup_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
     """
     Prepare the configuration for the Hyperopt module
     :param args: Cli args from Arguments()
@@ -28,7 +27,7 @@ def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
     return config
 
 
-def start_backtesting(args: Namespace) -> None:
+def start_backtesting(args: Dict[str, Any]) -> None:
     """
     Start Backtesting script
     :param args: Cli args from Arguments()
@@ -47,7 +46,7 @@ def start_backtesting(args: Namespace) -> None:
     backtesting.start()
 
 
-def start_hyperopt(args: Namespace) -> None:
+def start_hyperopt(args: Dict[str, Any]) -> None:
     """
     Start hyperopt script
     :param args: Cli args from Arguments()
@@ -85,7 +84,7 @@ def start_hyperopt(args: Namespace) -> None:
         # Same in Edge and Backtesting start() functions.
 
 
-def start_edge(args: Namespace) -> None:
+def start_edge(args: Dict[str, Any]) -> None:
     """
     Start Edge script
     :param args: Cli args from Arguments()
diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py
index d7fb326d1..8de0eb9e7 100644
--- a/freqtrade/plot/plot_utils.py
+++ b/freqtrade/plot/plot_utils.py
@@ -1,18 +1,18 @@
-from argparse import Namespace
+from typing import Any, Dict
+
 from freqtrade import OperationalException
 from freqtrade.state import RunMode
 from freqtrade.utils import setup_utils_configuration
 
 
-def validate_plot_args(args: Namespace):
-    args_tmp = vars(args)
-    if not args_tmp.get('datadir') and not args_tmp.get('config'):
+def validate_plot_args(args: Dict[str, Any]):
+    if not args.get('datadir') and not args.get('config'):
         raise OperationalException(
             "You need to specify either `--datadir` or `--config` "
             "for plot-profit and plot-dataframe.")
 
 
-def start_plot_dataframe(args: Namespace) -> None:
+def start_plot_dataframe(args: Dict[str, Any]) -> None:
     """
     Entrypoint for dataframe plotting
     """
@@ -24,7 +24,7 @@ def start_plot_dataframe(args: Namespace) -> None:
     load_and_plot_trades(config)
 
 
-def start_plot_profit(args: Namespace) -> None:
+def start_plot_profit(args: Dict[str, Any]) -> None:
     """
     Entrypoint for plot_profit
     """
diff --git a/freqtrade/utils.py b/freqtrade/utils.py
index e32c8f12e..5b2b08357 100644
--- a/freqtrade/utils.py
+++ b/freqtrade/utils.py
@@ -1,6 +1,5 @@
 import logging
 import sys
-from argparse import Namespace
 from pathlib import Path
 from typing import Any, Dict, List
 
@@ -16,7 +15,7 @@ from freqtrade.state import RunMode
 logger = logging.getLogger(__name__)
 
 
-def setup_utils_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
+def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
     """
     Prepare the configuration for utils subcommands
     :param args: Cli args from Arguments()
@@ -33,34 +32,34 @@ def setup_utils_configuration(args: Namespace, method: RunMode) -> Dict[str, Any
     return config
 
 
-def start_list_exchanges(args: Namespace) -> None:
+def start_list_exchanges(args: Dict[str, Any]) -> None:
     """
     Print available exchanges
     :param args: Cli args from Arguments()
     :return: None
     """
 
-    if args.print_one_column:
+    if args['print_one_column']:
         print('\n'.join(available_exchanges()))
     else:
         print(f"Exchanges supported by ccxt and available for Freqtrade: "
               f"{', '.join(available_exchanges())}")
 
 
-def start_create_userdir(args: Namespace) -> None:
+def start_create_userdir(args: Dict[str, Any]) -> None:
     """
     Create "user_data" directory to contain user data strategies, hyperopts, ...)
     :param args: Cli args from Arguments()
     :return: None
     """
-    if "user_data_dir" in args and args.user_data_dir:
-        create_userdata_dir(args.user_data_dir, create_dir=True)
+    if "user_data_dir" in args and args["user_data_dir"]:
+        create_userdata_dir(args["user_data_dir"], create_dir=True)
     else:
         logger.warning("`create-userdir` requires --userdir to be set.")
         sys.exit(1)
 
 
-def start_download_data(args: Namespace) -> None:
+def start_download_data(args: Dict[str, Any]) -> None:
     """
     Download data (former download_backtest_data.py script)
     """
diff --git a/freqtrade/worker.py b/freqtrade/worker.py
index 7a5360824..8e4be9d43 100755
--- a/freqtrade/worker.py
+++ b/freqtrade/worker.py
@@ -4,17 +4,16 @@ Main Freqtrade worker class.
 import logging
 import time
 import traceback
-from argparse import Namespace
-from typing import Any, Callable, Optional
+from typing import Any, Callable, Dict, Optional
+
 import sdnotify
 
-from freqtrade import (constants, OperationalException, TemporaryError,
-                       __version__)
+from freqtrade import (OperationalException, TemporaryError, __version__,
+                       constants)
 from freqtrade.configuration import Configuration
 from freqtrade.freqtradebot import FreqtradeBot
-from freqtrade.state import State
 from freqtrade.rpc import RPCMessageType
-
+from freqtrade.state import State
 
 logger = logging.getLogger(__name__)
 
@@ -24,7 +23,7 @@ class Worker:
     Freqtradebot worker class
     """
 
-    def __init__(self, args: Namespace, config=None) -> None:
+    def __init__(self, args: Dict[str, Any], config=None) -> None:
         """
         Init all variables and objects the bot needs to work
         """

From 4d566e8badf264d0608d12566ec230aa2f34a3d6 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 12 Sep 2019 20:25:27 +0200
Subject: [PATCH 157/227] Update tests to not use Namespace

---
 freqtrade/main.py           |  1 -
 tests/test_arguments.py     | 83 +++++++++++++++++++------------------
 tests/test_configuration.py |  2 +-
 tests/test_main.py          | 20 +++++----
 tests/test_plotting.py      |  2 +-
 5 files changed, 55 insertions(+), 53 deletions(-)

diff --git a/freqtrade/main.py b/freqtrade/main.py
index b8a22490c..4d6f0dce7 100755
--- a/freqtrade/main.py
+++ b/freqtrade/main.py
@@ -11,7 +11,6 @@ if sys.version_info < (3, 6):
 
 # flake8: noqa E402
 import logging
-from argparse import Namespace
 from typing import Any, List
 
 from freqtrade import OperationalException
diff --git a/tests/test_arguments.py b/tests/test_arguments.py
index 174038eff..7b18aa356 100644
--- a/tests/test_arguments.py
+++ b/tests/test_arguments.py
@@ -12,48 +12,48 @@ def test_parse_args_none() -> None:
     arguments = Arguments([])
     assert isinstance(arguments, Arguments)
     x = arguments.get_parsed_arg()
-    assert isinstance(x, argparse.Namespace)
+    assert isinstance(x, dict)
     assert isinstance(arguments.parser, argparse.ArgumentParser)
 
 
 def test_parse_args_defaults() -> None:
     args = Arguments([]).get_parsed_arg()
-    assert args.config == ['config.json']
-    assert args.strategy_path is None
-    assert args.datadir is None
-    assert args.verbosity == 0
+    assert args["config"] == ['config.json']
+    assert args["strategy_path"] is None
+    assert args["datadir"] is None
+    assert args["verbosity"] == 0
 
 
 def test_parse_args_config() -> None:
     args = Arguments(['-c', '/dev/null']).get_parsed_arg()
-    assert args.config == ['/dev/null']
+    assert args["config"] == ['/dev/null']
 
     args = Arguments(['--config', '/dev/null']).get_parsed_arg()
-    assert args.config == ['/dev/null']
+    assert args["config"] == ['/dev/null']
 
     args = Arguments(['--config', '/dev/null',
                       '--config', '/dev/zero'],).get_parsed_arg()
-    assert args.config == ['/dev/null', '/dev/zero']
+    assert args["config"] == ['/dev/null', '/dev/zero']
 
 
 def test_parse_args_db_url() -> None:
     args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg()
-    assert args.db_url == 'sqlite:///test.sqlite'
+    assert args["db_url"] == 'sqlite:///test.sqlite'
 
 
 def test_parse_args_verbose() -> None:
     args = Arguments(['-v']).get_parsed_arg()
-    assert args.verbosity == 1
+    assert args["verbosity"] == 1
 
     args = Arguments(['--verbose']).get_parsed_arg()
-    assert args.verbosity == 1
+    assert args["verbosity"] == 1
 
 
 def test_common_scripts_options() -> None:
     args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC']).get_parsed_arg()
 
-    assert args.pairs == ['ETH/BTC', 'XRP/BTC']
-    assert hasattr(args, "func")
+    assert args["pairs"] == ['ETH/BTC', 'XRP/BTC']
+    assert "func" in args
 
 
 def test_parse_args_version() -> None:
@@ -68,7 +68,7 @@ def test_parse_args_invalid() -> None:
 
 def test_parse_args_strategy() -> None:
     args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg()
-    assert args.strategy == 'SomeStrategy'
+    assert args["strategy"] == 'SomeStrategy'
 
 
 def test_parse_args_strategy_invalid() -> None:
@@ -78,7 +78,7 @@ def test_parse_args_strategy_invalid() -> None:
 
 def test_parse_args_strategy_path() -> None:
     args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg()
-    assert args.strategy_path == '/some/path'
+    assert args["strategy_path"] == '/some/path'
 
 
 def test_parse_args_strategy_path_invalid() -> None:
@@ -105,14 +105,14 @@ def test_parse_args_backtesting_custom() -> None:
         'SampleStrategy'
         ]
     call_args = Arguments(args).get_parsed_arg()
-    assert call_args.config == ['test_conf.json']
-    assert call_args.verbosity == 0
-    assert call_args.subparser == 'backtesting'
-    assert call_args.func is not None
-    assert call_args.ticker_interval == '1m'
-    assert call_args.refresh_pairs is True
-    assert type(call_args.strategy_list) is list
-    assert len(call_args.strategy_list) == 2
+    assert call_args["config"] == ['test_conf.json']
+    assert call_args["verbosity"] == 0
+    assert call_args["subparser"] == 'backtesting'
+    assert call_args["func"] is not None
+    assert call_args["ticker_interval"] == '1m'
+    assert call_args["refresh_pairs"] is True
+    assert type(call_args["strategy_list"]) is list
+    assert len(call_args["strategy_list"]) == 2
 
 
 def test_parse_args_hyperopt_custom() -> None:
@@ -123,12 +123,13 @@ def test_parse_args_hyperopt_custom() -> None:
         '--spaces', 'buy'
     ]
     call_args = Arguments(args).get_parsed_arg()
-    assert call_args.config == ['test_conf.json']
-    assert call_args.epochs == 20
-    assert call_args.verbosity == 0
-    assert call_args.subparser == 'hyperopt'
-    assert call_args.spaces == ['buy']
-    assert call_args.func is not None
+    assert call_args["config"] == ['test_conf.json']
+    assert call_args["epochs"] == 20
+    assert call_args["verbosity"] == 0
+    assert call_args["subparser"] == 'hyperopt'
+    assert call_args["spaces"] == ['buy']
+    assert call_args["func"] is not None
+    assert callable(call_args["func"])
 
 
 def test_download_data_options() -> None:
@@ -139,12 +140,12 @@ def test_download_data_options() -> None:
         '--days', '30',
         '--exchange', 'binance'
     ]
-    args = Arguments(args).get_parsed_arg()
+    pargs = Arguments(args).get_parsed_arg()
 
-    assert args.pairs_file == 'file_with_pairs'
-    assert args.datadir == 'datadir/directory'
-    assert args.days == 30
-    assert args.exchange == 'binance'
+    assert pargs["pairs_file"] == 'file_with_pairs'
+    assert pargs["datadir"] == 'datadir/directory'
+    assert pargs["days"] == 30
+    assert pargs["exchange"] == 'binance'
 
 
 def test_plot_dataframe_options() -> None:
@@ -158,10 +159,10 @@ def test_plot_dataframe_options() -> None:
     ]
     pargs = Arguments(args).get_parsed_arg()
 
-    assert pargs.indicators1 == ["sma10", "sma100"]
-    assert pargs.indicators2 == ["macd", "fastd", "fastk"]
-    assert pargs.plot_limit == 30
-    assert pargs.pairs == ["UNITTEST/BTC"]
+    assert pargs["indicators1"] == ["sma10", "sma100"]
+    assert pargs["indicators2"] == ["macd", "fastd", "fastk"]
+    assert pargs["plot_limit"] == 30
+    assert pargs["pairs"] == ["UNITTEST/BTC"]
 
 
 def test_plot_profit_options() -> None:
@@ -173,9 +174,9 @@ def test_plot_profit_options() -> None:
     ]
     pargs = Arguments(args).get_parsed_arg()
 
-    assert pargs.trade_source == "DB"
-    assert pargs.pairs == ["UNITTEST/BTC"]
-    assert pargs.db_url == "sqlite:///whatever.sqlite"
+    assert pargs["trade_source"] == "DB"
+    assert pargs["pairs"] == ["UNITTEST/BTC"]
+    assert pargs["db_url"] == "sqlite:///whatever.sqlite"
 
 
 def test_check_int_positive() -> None:
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 3717f4d1b..67bde50fa 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -871,7 +871,7 @@ def test_pairlist_resolving_fallback(mocker):
 
     args = Arguments(arglist).get_parsed_arg()
     # Fix flaky tests if config.json exists
-    args.config = None
+    args["config"] = None
 
     configuration = Configuration(args)
     config = configuration.get_config()
diff --git a/tests/test_main.py b/tests/test_main.py
index eec81ee18..d73edc0da 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -27,11 +27,12 @@ def test_parse_args_backtesting(mocker) -> None:
         main(['backtesting'])
     assert backtesting_mock.call_count == 1
     call_args = backtesting_mock.call_args[0][0]
-    assert call_args.config == ['config.json']
-    assert call_args.verbosity == 0
-    assert call_args.subparser == 'backtesting'
-    assert call_args.func is not None
-    assert call_args.ticker_interval is None
+    assert call_args["config"] == ['config.json']
+    assert call_args["verbosity"] == 0
+    assert call_args["subparser"] == 'backtesting'
+    assert call_args["func"] is not None
+    assert callable(call_args["func"])
+    assert call_args["ticker_interval"] is None
 
 
 def test_main_start_hyperopt(mocker) -> None:
@@ -42,10 +43,11 @@ def test_main_start_hyperopt(mocker) -> None:
         main(['hyperopt'])
     assert hyperopt_mock.call_count == 1
     call_args = hyperopt_mock.call_args[0][0]
-    assert call_args.config == ['config.json']
-    assert call_args.verbosity == 0
-    assert call_args.subparser == 'hyperopt'
-    assert call_args.func is not None
+    assert call_args["config"] == ['config.json']
+    assert call_args["verbosity"] == 0
+    assert call_args["subparser"] == 'hyperopt'
+    assert call_args["func"] is not None
+    assert callable(call_args["func"])
 
 
 def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index 2c3b8a339..9028ab961 100644
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -344,7 +344,7 @@ def test_start_plot_profit_error(mocker):
     argsp = get_args(args)
     # Make sure we use no config. Details: #2241
     # not resetting config causes random failures if config.json exists
-    argsp.config = []
+    argsp["config"] = []
     with pytest.raises(OperationalException):
         start_plot_profit(argsp)
 

From e6ec8f9f30138e5eb63666dd0cad5d73c48a6324 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 21:28:51 +0300
Subject: [PATCH 158/227] Fix tests: Change condition for printing 'close'

---
 freqtrade/persistence.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index 1850aafd9..bccbce931 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -210,7 +210,7 @@ class Trade(_DECL_BASE):
     ticker_interval = Column(Integer, nullable=True)
 
     def __repr__(self):
-        open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.is_open else 'closed'
+        open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.open_date else 'closed'
 
         return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
                 f'open_rate={self.open_rate:.8f}, open_since={open_since})')

From c8d191a5c9ca63661276e1c6585e2b29be3bf71f Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 12 Sep 2019 22:53:54 +0300
Subject: [PATCH 159/227] Adjust test

---
 tests/test_freqtradebot.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index d34197519..d39c9aaef 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -2153,8 +2153,8 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
     freqtrade.check_handle_timedout()
     assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, "
                       r"open_rate=0.00001099, open_since="
-                      f"{open_date.strftime('%Y-%m-%d %H:%M:%S')} "
-                      r"\(10 hours ago\)\) due to Traceback \(most recent call last\):\n*",
+                      f"{open_date.strftime('%Y-%m-%d %H:%M:%S')}"
+                      r"\) due to Traceback \(most recent call last\):\n*",
                       caplog)
 
 

From f16324071054f35f3e04543810558699e13eba2a Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 13 Sep 2019 07:02:36 +0200
Subject: [PATCH 160/227] Simplify configuration init  where possible

---
 freqtrade/configuration/configuration.py | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index 1811cc6ed..547bfc135 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -106,10 +106,7 @@ class Configuration:
         the -v/--verbose, --logfile options
         """
         # Log level
-        if 'verbosity' in self.args and self.args["verbosity"]:
-            config.update({'verbosity': self.args["verbosity"]})
-        else:
-            config.update({'verbosity': 0})
+        config.update({'verbosity': self.args.get("verbosity", 0)})
 
         if 'logfile' in self.args and self.args["logfile"]:
             config.update({'logfile': self.args["logfile"]})
@@ -121,8 +118,8 @@ class Configuration:
         self._process_logging_options(config)
 
         # Set strategy if not specified in config and or if it's non default
-        if self.args["strategy"] != constants.DEFAULT_STRATEGY or not config.get('strategy'):
-            config.update({'strategy': self.args["strategy"]})
+        if self.args.get("strategy") != constants.DEFAULT_STRATEGY or not config.get('strategy'):
+            config.update({'strategy': self.args.get("strategy")})
 
         self._args_to_config(config, argname='strategy_path',
                              logstring='Using additional Strategy lookup path: {}')
@@ -175,10 +172,7 @@ class Configuration:
         config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
         logger.info('Using user-data directory: %s ...', config['user_data_dir'])
 
-        if 'datadir' in self.args and self.args["datadir"]:
-            config.update({'datadir': create_datadir(config, self.args["datadir"])})
-        else:
-            config.update({'datadir': create_datadir(config, None)})
+        config.update({'datadir': create_datadir(config, self.args.get("datadir", None))})
         logger.info('Using data directory: %s ...', config.get('datadir'))
 
     def _process_optimize_options(self, config: Dict[str, Any]) -> None:

From a5f3b68bff86e45e6ac1bae1472e3b026ad94bb4 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 13 Sep 2019 07:08:22 +0200
Subject: [PATCH 161/227] Allow loading of fully initialized config from
 jupyter notbooks

---
 freqtrade/configuration/configuration.py | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index 547bfc135..dba94abc8 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -49,9 +49,16 @@ class Configuration:
         and merging their contents.
         Files are loaded in sequence, parameters in later configuration files
         override the same parameter from an earlier file (last definition wins).
+        Runs through the whole Configuration initialization, so all expected config entries
+        are available to interactive environments.
         :param files: List of file paths
         :return: configuration dictionary
         """
+        c = Configuration({"config": files}, RunMode.OTHER)
+        return c.get_config()
+
+    def load_from_files(self, files: List[str]) -> Dict[str, Any]:
+
         # Keep this method as staticmethod, so it can be used from interactive environments
         config: Dict[str, Any] = {}
 
@@ -81,7 +88,7 @@ class Configuration:
         :return: Configuration dictionary
         """
         # Load all configs
-        config: Dict[str, Any] = Configuration.from_files(self.args["config"])
+        config: Dict[str, Any] = self.load_from_files(self.args["config"])
 
         self._process_common_options(config)
 

From 16b4ae8396903752838aa8c17c3ff1dd7effa29f Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 13 Sep 2019 07:08:42 +0200
Subject: [PATCH 162/227] Document this new behaviour

---
 docs/data-analysis.md | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/docs/data-analysis.md b/docs/data-analysis.md
index f6277cac2..d02a00ad9 100644
--- a/docs/data-analysis.md
+++ b/docs/data-analysis.md
@@ -91,7 +91,8 @@ df.groupby("pair")["sell_reason"].value_counts()
 
 ### Load multiple configuration files
 
-This option can be useful to inspect the results of passing in multiple configs
+This option can be useful to inspect the results of passing in multiple configs.
+This will also run through the whole Configuration initialization, so the configuration is completely initialized to be passed to other methods.
 
 ``` python
 import json
@@ -101,7 +102,16 @@ from freqtrade.configuration import Configuration
 config = Configuration.from_files(["config1.json", "config2.json"])
 
 # Show the config in memory
-print(json.dumps(config, indent=1))
+print(json.dumps(config, indent=2))
+```
+
+For Interactive environments, have an additional configuration specifying `user_data_dir` and pass this in last, so you don't have to change directories while running the bot.
+Best avoid relative paths, since this starts at the storage location of the jupyter notebook, unless the folder is changed.
+
+``` json
+{
+    "user_data_dir": "~/.freqtrade/"
+}
 ```
 
 ### Load exchange data to a pandas dataframe

From 5e654620b791b3f1b218a40ec1b699a492f41ae5 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 13 Sep 2019 19:49:13 +0200
Subject: [PATCH 163/227] Use available indicators in tests where possible

---
 tests/optimize/test_backtesting.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index 770f6c4ba..99fb30cbd 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -603,7 +603,7 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
     cols = dataframe.columns
     # assert the dataframe got some of the indicator columns
     for col in ['close', 'high', 'low', 'open', 'date',
-                'ema50', 'ao', 'macd', 'plus_dm']:
+                'ema10', 'rsi', 'fastd', 'plus_di']:
         assert col in cols
 
 

From 01357845891ba0629f8352115a8725b07e965d5a Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 13 Sep 2019 19:49:34 +0200
Subject: [PATCH 164/227] remove unused indicators from default_strategy

---
 freqtrade/strategy/default_strategy.py | 38 ++++++++++++++------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py
index 4907f20ed..caf1bb82d 100644
--- a/freqtrade/strategy/default_strategy.py
+++ b/freqtrade/strategy/default_strategy.py
@@ -4,7 +4,6 @@ import talib.abstract as ta
 from pandas import DataFrame
 
 import freqtrade.vendor.qtpylib.indicators as qtpylib
-from freqtrade.indicator_helpers import fishers_inverse
 from freqtrade.strategy.interface import IStrategy
 
 
@@ -75,7 +74,8 @@ class DefaultStrategy(IStrategy):
         dataframe['adx'] = ta.ADX(dataframe)
 
         # Awesome oscillator
-        dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
+        # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
+
         """
         # Commodity Channel Index: values Oversold:<-100, Overbought:>100
         dataframe['cci'] = ta.CCI(dataframe)
@@ -87,16 +87,15 @@ class DefaultStrategy(IStrategy):
         dataframe['macdhist'] = macd['macdhist']
 
         # MFI
-        dataframe['mfi'] = ta.MFI(dataframe)
+        # dataframe['mfi'] = ta.MFI(dataframe)
 
         # Minus Directional Indicator / Movement
-        dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
+        # dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
         dataframe['minus_di'] = ta.MINUS_DI(dataframe)
 
         # Plus Directional Indicator / Movement
-        dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
+        # dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
         dataframe['plus_di'] = ta.PLUS_DI(dataframe)
-        dataframe['minus_di'] = ta.MINUS_DI(dataframe)
 
         """
         # ROC
@@ -106,15 +105,15 @@ class DefaultStrategy(IStrategy):
         dataframe['rsi'] = ta.RSI(dataframe)
 
         # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
-        dataframe['fisher_rsi'] = fishers_inverse(dataframe['rsi'])
+        # dataframe['fisher_rsi'] = fishers_inverse(dataframe['rsi'])
 
         # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
-        dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
+        # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
 
         # Stoch
-        stoch = ta.STOCH(dataframe)
-        dataframe['slowd'] = stoch['slowd']
-        dataframe['slowk'] = stoch['slowk']
+        # stoch = ta.STOCH(dataframe)
+        # dataframe['slowd'] = stoch['slowd']
+        # dataframe['slowk'] = stoch['slowk']
 
         # Stoch fast
         stoch_fast = ta.STOCHF(dataframe)
@@ -134,37 +133,39 @@ class DefaultStrategy(IStrategy):
         # Because ta.BBANDS implementation is broken with small numbers, it actually
         # returns middle band for all the three bands. Switch to qtpylib.bollinger_bands
         # and use middle band instead.
-        dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
+        # dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
 
         # Bollinger bands
         bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
         dataframe['bb_lowerband'] = bollinger['lower']
         dataframe['bb_middleband'] = bollinger['mid']
         dataframe['bb_upperband'] = bollinger['upper']
-
+        """
         # EMA - Exponential Moving Average
         dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
         dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
-        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
         dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
         dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
+        """
+        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
 
         # SAR Parabol
-        dataframe['sar'] = ta.SAR(dataframe)
+        # dataframe['sar'] = ta.SAR(dataframe)
 
         # SMA - Simple Moving Average
         dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
 
         # TEMA - Triple Exponential Moving Average
-        dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
+        # dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
 
+        """
         # Cycle Indicator
         # ------------------------------------
         # Hilbert Transform Indicator - SineWave
         hilbert = ta.HT_SINE(dataframe)
         dataframe['htsine'] = hilbert['sine']
         dataframe['htleadsine'] = hilbert['leadsine']
-
+        """
         # Pattern Recognition - Bullish candlestick patterns
         # ------------------------------------
         """
@@ -216,6 +217,7 @@ class DefaultStrategy(IStrategy):
         dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
         """
 
+        """
         # Chart type
         # ------------------------------------
         # Heikinashi stategy
@@ -224,7 +226,7 @@ class DefaultStrategy(IStrategy):
         dataframe['ha_close'] = heikinashi['close']
         dataframe['ha_high'] = heikinashi['high']
         dataframe['ha_low'] = heikinashi['low']
-
+        """
         return dataframe
 
     def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

From eda1ec652f99f8210916c19f678df441db01921b Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Fri, 13 Sep 2019 23:00:09 +0300
Subject: [PATCH 165/227] Revert back condition for open_since in
 Trade.__repr__

---
 freqtrade/persistence.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index bccbce931..1850aafd9 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -210,7 +210,7 @@ class Trade(_DECL_BASE):
     ticker_interval = Column(Integer, nullable=True)
 
     def __repr__(self):
-        open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.open_date else 'closed'
+        open_since = self.open_date.strftime('%Y-%m-%d %H:%M:%S') if self.is_open else 'closed'
 
         return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
                 f'open_rate={self.open_rate:.8f}, open_since={open_since})')

From e2a100c9257f81f235666f41c99b4f5303ee6276 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 14 Sep 2019 09:54:40 +0200
Subject: [PATCH 166/227] Directory / folder

---
 docs/data-analysis.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/data-analysis.md b/docs/data-analysis.md
index d02a00ad9..1bf781c4d 100644
--- a/docs/data-analysis.md
+++ b/docs/data-analysis.md
@@ -106,7 +106,7 @@ print(json.dumps(config, indent=2))
 ```
 
 For Interactive environments, have an additional configuration specifying `user_data_dir` and pass this in last, so you don't have to change directories while running the bot.
-Best avoid relative paths, since this starts at the storage location of the jupyter notebook, unless the folder is changed.
+Best avoid relative paths, since this starts at the storage location of the jupyter notebook, unless the directory is changed.
 
 ``` json
 {

From 2cf045c53ea9cec1ee8216730d87a3c2eab7d7d9 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 14 Sep 2019 10:00:32 +0200
Subject: [PATCH 167/227] Remove commented indicators from DefaultStrategy

---
 freqtrade/strategy/default_strategy.py | 131 ++-----------------------
 1 file changed, 6 insertions(+), 125 deletions(-)

diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py
index caf1bb82d..b839a9618 100644
--- a/freqtrade/strategy/default_strategy.py
+++ b/freqtrade/strategy/default_strategy.py
@@ -10,7 +10,10 @@ from freqtrade.strategy.interface import IStrategy
 class DefaultStrategy(IStrategy):
     """
     Default Strategy provided by freqtrade bot.
-    You can override it with your own strategy
+    Please do not modify this strategy, it's  intended for internal use only.
+    Please look at the SampleStrategy in the user_data/strategy directory
+    or strategy repository https://github.com/freqtrade/freqtrade-strategies
+    for samples and inspiration.
     """
     INTERFACE_VERSION = 2
 
@@ -73,160 +76,38 @@ class DefaultStrategy(IStrategy):
         # ADX
         dataframe['adx'] = ta.ADX(dataframe)
 
-        # Awesome oscillator
-        # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
-
-        """
-        # Commodity Channel Index: values Oversold:<-100, Overbought:>100
-        dataframe['cci'] = ta.CCI(dataframe)
-        """
         # MACD
         macd = ta.MACD(dataframe)
         dataframe['macd'] = macd['macd']
         dataframe['macdsignal'] = macd['macdsignal']
         dataframe['macdhist'] = macd['macdhist']
 
-        # MFI
-        # dataframe['mfi'] = ta.MFI(dataframe)
-
         # Minus Directional Indicator / Movement
-        # dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
         dataframe['minus_di'] = ta.MINUS_DI(dataframe)
 
         # Plus Directional Indicator / Movement
-        # dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
         dataframe['plus_di'] = ta.PLUS_DI(dataframe)
 
-        """
-        # ROC
-        dataframe['roc'] = ta.ROC(dataframe)
-        """
         # RSI
         dataframe['rsi'] = ta.RSI(dataframe)
 
-        # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
-        # dataframe['fisher_rsi'] = fishers_inverse(dataframe['rsi'])
-
-        # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
-        # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
-
-        # Stoch
-        # stoch = ta.STOCH(dataframe)
-        # dataframe['slowd'] = stoch['slowd']
-        # dataframe['slowk'] = stoch['slowk']
-
         # Stoch fast
         stoch_fast = ta.STOCHF(dataframe)
         dataframe['fastd'] = stoch_fast['fastd']
         dataframe['fastk'] = stoch_fast['fastk']
-        """
-        # Stoch RSI
-        stoch_rsi = ta.STOCHRSI(dataframe)
-        dataframe['fastd_rsi'] = stoch_rsi['fastd']
-        dataframe['fastk_rsi'] = stoch_rsi['fastk']
-        """
-
-        # Overlap Studies
-        # ------------------------------------
-
-        # Previous Bollinger bands
-        # Because ta.BBANDS implementation is broken with small numbers, it actually
-        # returns middle band for all the three bands. Switch to qtpylib.bollinger_bands
-        # and use middle band instead.
-        # dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
 
         # Bollinger bands
         bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
         dataframe['bb_lowerband'] = bollinger['lower']
         dataframe['bb_middleband'] = bollinger['mid']
         dataframe['bb_upperband'] = bollinger['upper']
-        """
-        # EMA - Exponential Moving Average
-        dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
-        dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
-        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
-        dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
-        """
-        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
 
-        # SAR Parabol
-        # dataframe['sar'] = ta.SAR(dataframe)
+        # EMA - Exponential Moving Average
+        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
 
         # SMA - Simple Moving Average
         dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
 
-        # TEMA - Triple Exponential Moving Average
-        # dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
-
-        """
-        # Cycle Indicator
-        # ------------------------------------
-        # Hilbert Transform Indicator - SineWave
-        hilbert = ta.HT_SINE(dataframe)
-        dataframe['htsine'] = hilbert['sine']
-        dataframe['htleadsine'] = hilbert['leadsine']
-        """
-        # Pattern Recognition - Bullish candlestick patterns
-        # ------------------------------------
-        """
-        # Hammer: values [0, 100]
-        dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
-        # Inverted Hammer: values [0, 100]
-        dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
-        # Dragonfly Doji: values [0, 100]
-        dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
-        # Piercing Line: values [0, 100]
-        dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
-        # Morningstar: values [0, 100]
-        dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
-        # Three White Soldiers: values [0, 100]
-        dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
-        """
-
-        # Pattern Recognition - Bearish candlestick patterns
-        # ------------------------------------
-        """
-        # Hanging Man: values [0, 100]
-        dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
-        # Shooting Star: values [0, 100]
-        dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
-        # Gravestone Doji: values [0, 100]
-        dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
-        # Dark Cloud Cover: values [0, 100]
-        dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
-        # Evening Doji Star: values [0, 100]
-        dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
-        # Evening Star: values [0, 100]
-        dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
-        """
-
-        # Pattern Recognition - Bullish/Bearish candlestick patterns
-        # ------------------------------------
-        """
-        # Three Line Strike: values [0, -100, 100]
-        dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
-        # Spinning Top: values [0, -100, 100]
-        dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
-        # Engulfing: values [0, -100, 100]
-        dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
-        # Harami: values [0, -100, 100]
-        dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
-        # Three Outside Up/Down: values [0, -100, 100]
-        dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
-        # Three Inside Up/Down: values [0, -100, 100]
-        dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
-        """
-
-        """
-        # Chart type
-        # ------------------------------------
-        # Heikinashi stategy
-        heikinashi = qtpylib.heikinashi(dataframe)
-        dataframe['ha_open'] = heikinashi['open']
-        dataframe['ha_close'] = heikinashi['close']
-        dataframe['ha_high'] = heikinashi['high']
-        dataframe['ha_low'] = heikinashi['low']
-        """
         return dataframe
 
     def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

From b00467c8efaa61c2556d6e88c446c3dcd3230dd6 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 14 Sep 2019 10:07:23 +0200
Subject: [PATCH 168/227] Fix test failure

---
 tests/test_freqtradebot.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index d39c9aaef..a595e0ce6 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -1590,6 +1590,8 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
     Trade.session = MagicMock()
     trade.open_order_id = '123'
     trade.open_fee = 0.001
+    # Add datetime explicitly since sqlalchemy defaults apply only once written to database
+    trade.open_date = arrow.utcnow().datetime
     freqtrade.update_trade_state(trade)
     # Test amount not modified by fee-logic
     assert not log_has_re(r'Applying fee to .*', caplog)

From c2462ee87b87814918bbd6788ce25a98dce67555 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 16 Sep 2019 08:49:41 +0000
Subject: [PATCH 169/227] Bump arrow from 0.15.0 to 0.15.2

Bumps [arrow](https://github.com/crsmithdev/arrow) from 0.15.0 to 0.15.2.
- [Release notes](https://github.com/crsmithdev/arrow/releases)
- [Changelog](https://github.com/crsmithdev/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/crsmithdev/arrow/compare/0.15.0...0.15.2)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 30c93950a..62a886637 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -3,7 +3,7 @@
 ccxt==1.18.1124
 SQLAlchemy==1.3.8
 python-telegram-bot==12.0.0
-arrow==0.15.0
+arrow==0.15.2
 cachetools==3.1.1
 requests==2.22.0
 urllib3==1.25.3

From cab394a0584e31cc9c7fd9ade087902546583f69 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 16 Sep 2019 09:31:10 +0000
Subject: [PATCH 170/227] Bump python-telegram-bot from 12.0.0 to 12.1.0

Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 12.0.0 to 12.1.0.
- [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases)
- [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v12.0.0...v12.1.0)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 62a886637..e6d8e8977 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -2,7 +2,7 @@
 # mainly used for Raspberry pi installs
 ccxt==1.18.1124
 SQLAlchemy==1.3.8
-python-telegram-bot==12.0.0
+python-telegram-bot==12.1.0
 arrow==0.15.2
 cachetools==3.1.1
 requests==2.22.0

From 9c1cce6fe2e112af1f84cefe28f162d18f6323fa Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 16 Sep 2019 09:31:14 +0000
Subject: [PATCH 171/227] Bump ccxt from 1.18.1124 to 1.18.1149

Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1124 to 1.18.1149.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/1.18.1124...1.18.1149)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 62a886637..eb0e9f3c1 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -1,6 +1,6 @@
 # requirements without requirements installable via conda
 # mainly used for Raspberry pi installs
-ccxt==1.18.1124
+ccxt==1.18.1149
 SQLAlchemy==1.3.8
 python-telegram-bot==12.0.0
 arrow==0.15.2

From f3e3a8fcbe962ac5da0491fcbf970defd3a72fc6 Mon Sep 17 00:00:00 2001
From: Pialat 
Date: Mon, 16 Sep 2019 14:04:10 +0200
Subject: [PATCH 172/227] unused in tests

---
 tests/optimize/test_hyperopt.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index f5c8bf0ae..90b7953d0 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -38,8 +38,6 @@ def hyperopt_results():
             'profit_percent': [0.1, 0.2, 0.3],
             'profit_abs': [0.2, 0.4, 0.5],
             'trade_duration': [10, 30, 10],
-            'profit': [2, 0, 0],
-            'loss': [0, 0, 1],
             'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
         }
     )

From b7da02aab4adb6908b8abb3747515ed3a43d4ba6 Mon Sep 17 00:00:00 2001
From: Pialat 
Date: Mon, 16 Sep 2019 14:05:39 +0200
Subject: [PATCH 173/227] realistic fixture datas

---
 tests/optimize/test_hyperopt.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 90b7953d0..e2f953485 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -35,10 +35,10 @@ def hyperopt_results():
     return pd.DataFrame(
         {
             'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
-            'profit_percent': [0.1, 0.2, 0.3],
-            'profit_abs': [0.2, 0.4, 0.5],
+            'profit_percent': [-0.1, 0.2, 0.3],
+            'profit_abs': [-0.2, 0.4, 0.6],
             'trade_duration': [10, 30, 10],
-            'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
+            'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI]
         }
     )
 

From 5cbc073dd1adcec6bd8f9fd24b6570e67adc37e8 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Mon, 16 Sep 2019 21:22:07 +0300
Subject: [PATCH 174/227] minor: Cleanup hyperopt

---
 freqtrade/optimize/hyperopt.py | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index bdd0ba258..6dbcf8765 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -73,9 +73,11 @@ class Hyperopt:
         self.trials: List = []
 
         # Populate functions here (hasattr is slow so should not be run during "regular" operations)
+        if hasattr(self.custom_hyperopt, 'populate_indicators'):
+            self.backtesting.strategy.advise_indicators = \
+                    self.custom_hyperopt.populate_indicators  # type: ignore
         if hasattr(self.custom_hyperopt, 'populate_buy_trend'):
             self.backtesting.advise_buy = self.custom_hyperopt.populate_buy_trend  # type: ignore
-
         if hasattr(self.custom_hyperopt, 'populate_sell_trend'):
             self.backtesting.advise_sell = self.custom_hyperopt.populate_sell_trend  # type: ignore
 
@@ -109,7 +111,9 @@ class Hyperopt:
                 p.unlink()
 
     def get_args(self, params):
-        dimensions = self.hyperopt_space()
+
+        dimensions = self.dimensions
+
         # Ensure the number of dimensions match
         # the number of parameters in the list x.
         if len(params) != len(dimensions):
@@ -322,9 +326,9 @@ class Hyperopt:
                 f'Total profit {total_profit: 11.8f} {stake_cur} '
                 f'({profit: 7.2f}Σ%). Avg duration {duration:5.1f} mins.')
 
-    def get_optimizer(self, cpu_count) -> Optimizer:
+    def get_optimizer(self, dimensions, cpu_count) -> Optimizer:
         return Optimizer(
-            self.hyperopt_space(),
+            dimensions,
             base_estimator="ET",
             acq_optimizer="auto",
             n_initial_points=INITIAL_POINTS,
@@ -370,9 +374,6 @@ class Hyperopt:
             (max_date - min_date).days
         )
 
-        self.backtesting.strategy.advise_indicators = \
-            self.custom_hyperopt.populate_indicators  # type: ignore
-
         preprocessed = self.backtesting.strategy.tickerdata_to_dataframe(data)
 
         dump(preprocessed, self.tickerdata_pickle)
@@ -387,7 +388,8 @@ class Hyperopt:
         config_jobs = self.config.get('hyperopt_jobs', -1)
         logger.info(f'Number of parallel jobs set as: {config_jobs}')
 
-        opt = self.get_optimizer(config_jobs)
+        self.dimensions = self.hyperopt_space()
+        self.opt = self.get_optimizer(self.dimensions, config_jobs)
 
         if self.config.get('print_colorized', False):
             colorama_init(autoreset=True)
@@ -398,9 +400,9 @@ class Hyperopt:
                 logger.info(f'Effective number of parallel workers used: {jobs}')
                 EVALS = max(self.total_epochs // jobs, 1)
                 for i in range(EVALS):
-                    asked = opt.ask(n_points=jobs)
+                    asked = self.opt.ask(n_points=jobs)
                     f_val = self.run_optimizer_parallel(parallel, asked)
-                    opt.tell(asked, [v['loss'] for v in f_val])
+                    self.opt.tell(asked, [v['loss'] for v in f_val])
                     for j in range(jobs):
                         current = i * jobs + j
                         val = f_val[j]

From e9a75e57b8140029d23d79160bb4c7daac3d10af Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Mon, 16 Sep 2019 21:53:19 +0300
Subject: [PATCH 175/227] test adjusted

---
 tests/optimize/test_hyperopt.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index f5c8bf0ae..d388a38ae 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -560,6 +560,7 @@ def test_generate_optimizer(mocker, default_conf) -> None:
     }
 
     hyperopt = Hyperopt(default_conf)
+    hyperopt.dimensions = hyperopt.hyperopt_space()
     generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
     assert generate_optimizer_value == response_expected
 

From a42000e1ddca8e4e1f8341cb27fe15582fae7e12 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 18 Sep 2019 11:36:16 +0200
Subject: [PATCH 176/227] Change package author to "freqtrade team"

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 307be079f..ca94dd338 100644
--- a/setup.py
+++ b/setup.py
@@ -46,7 +46,7 @@ setup(name='freqtrade',
       long_description=readme_long,
       long_description_content_type="text/markdown",
       url='https://github.com/freqtrade/freqtrade',
-      author='gcarq and contributors',
+      author='Freqtrade Team',
       author_email='michael.egger@tsn.at',
       license='GPLv3',
       packages=['freqtrade'],

From ee6ad51a44d530926172ca514c2eefa850d791c2 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Wed, 18 Sep 2019 22:41:25 +0300
Subject: [PATCH 177/227] Manual bump to ccxt 1.18.1159

(support for binance.us)
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 53f98af3f..363890a4d 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -1,6 +1,6 @@
 # requirements without requirements installable via conda
 # mainly used for Raspberry pi installs
-ccxt==1.18.1149
+ccxt==1.18.1159
 SQLAlchemy==1.3.8
 python-telegram-bot==12.1.0
 arrow==0.15.2

From 69f29e89070c0164bd43f5af8206cc178a5d77ab Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 18 Sep 2019 22:57:17 +0300
Subject: [PATCH 178/227] minor: Cleanup for backtesting

---
 freqtrade/configuration/configuration.py |  6 +++---
 freqtrade/optimize/backtesting.py        |  6 ++----
 freqtrade/optimize/hyperopt.py           | 18 ++++++++++++------
 freqtrade/resolvers/hyperopt_resolver.py |  8 ++++----
 4 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index dba94abc8..1aed32e50 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -217,7 +217,7 @@ class Configuration:
                              deprecated_msg='-r/--refresh-pairs-cached will be removed soon.')
 
         self._args_to_config(config, argname='strategy_list',
-                             logstring='Using strategy list of {} Strategies', logfun=len)
+                             logstring='Using strategy list of {} strategies', logfun=len)
 
         self._args_to_config(config, argname='ticker_interval',
                              logstring='Overriding ticker interval with Command line argument')
@@ -238,7 +238,7 @@ class Configuration:
 
         # Hyperopt section
         self._args_to_config(config, argname='hyperopt',
-                             logstring='Using Hyperopt file {}')
+                             logstring='Using Hyperopt class name: {}')
 
         self._args_to_config(config, argname='hyperopt_path',
                              logstring='Using additional Hyperopt lookup path: {}')
@@ -276,7 +276,7 @@ class Configuration:
                              logstring='Hyperopt continue: {}')
 
         self._args_to_config(config, argname='hyperopt_loss',
-                             logstring='Using loss function: {}')
+                             logstring='Using Hyperopt loss class name: {}')
 
     def _process_plot_options(self, config: Dict[str, Any]) -> None:
 
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 300cef82f..4956907fb 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -95,8 +95,6 @@ class Backtesting:
         Load strategy into backtesting
         """
         self.strategy = strategy
-        self.advise_buy = strategy.advise_buy
-        self.advise_sell = strategy.advise_sell
         # Set stoploss_on_exchange to false for backtesting,
         # since a "perfect" stoploss-sell is assumed anyway
         # And the regular "stoploss" function would not apply to that case
@@ -219,8 +217,8 @@ class Backtesting:
         for pair, pair_data in processed.items():
             pair_data['buy'], pair_data['sell'] = 0, 0  # cleanup from previous run
 
-            ticker_data = self.advise_sell(
-                self.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
+            ticker_data = self.strategy.advise_sell(
+                self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
 
             # to avoid using data from future, we buy/sell with signal from previous candle
             ticker_data.loc[:, 'buy'] = ticker_data['buy'].shift(1)
diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index 6dbcf8765..c511aa5ac 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -49,10 +49,11 @@ class Hyperopt:
     """
     def __init__(self, config: Dict[str, Any]) -> None:
         self.config = config
-        self.backtesting = Backtesting(self.config)
 
         self.custom_hyperopt = HyperOptResolver(self.config).hyperopt
 
+        self.backtesting = Backtesting(self.config)
+
         self.custom_hyperoptloss = HyperOptLossResolver(self.config).hyperoptloss
         self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function
 
@@ -77,9 +78,11 @@ class Hyperopt:
             self.backtesting.strategy.advise_indicators = \
                     self.custom_hyperopt.populate_indicators  # type: ignore
         if hasattr(self.custom_hyperopt, 'populate_buy_trend'):
-            self.backtesting.advise_buy = self.custom_hyperopt.populate_buy_trend  # type: ignore
+            self.backtesting.strategy.advise_buy = \
+                    self.custom_hyperopt.populate_buy_trend  # type: ignore
         if hasattr(self.custom_hyperopt, 'populate_sell_trend'):
-            self.backtesting.advise_sell = self.custom_hyperopt.populate_sell_trend  # type: ignore
+            self.backtesting.strategy.advise_sell = \
+                    self.custom_hyperopt.populate_sell_trend  # type: ignore
 
         # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
         if self.config.get('use_max_market_positions', True):
@@ -259,13 +262,16 @@ class Hyperopt:
         """
         params = self.get_args(_params)
         if self.has_space('roi'):
-            self.backtesting.strategy.minimal_roi = self.custom_hyperopt.generate_roi_table(params)
+            self.backtesting.strategy.minimal_roi = \
+                    self.custom_hyperopt.generate_roi_table(params)
 
         if self.has_space('buy'):
-            self.backtesting.advise_buy = self.custom_hyperopt.buy_strategy_generator(params)
+            self.backtesting.strategy.advise_buy = \
+                    self.custom_hyperopt.buy_strategy_generator(params)
 
         if self.has_space('sell'):
-            self.backtesting.advise_sell = self.custom_hyperopt.sell_strategy_generator(params)
+            self.backtesting.strategy.advise_sell = \
+                    self.custom_hyperopt.sell_strategy_generator(params)
 
         if self.has_space('stoploss'):
             self.backtesting.strategy.stoploss = params['stoploss']
diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py
index f808ca0d9..3ffdc5e1f 100644
--- a/freqtrade/resolvers/hyperopt_resolver.py
+++ b/freqtrade/resolvers/hyperopt_resolver.py
@@ -38,11 +38,11 @@ class HyperOptResolver(IResolver):
         IHyperOpt.ticker_interval = str(config['ticker_interval'])
 
         if not hasattr(self.hyperopt, 'populate_buy_trend'):
-            logger.warning("Custom Hyperopt does not provide populate_buy_trend. "
-                           "Using populate_buy_trend from DefaultStrategy.")
+            logger.warning("Hyperopt class does not provide populate_buy_trend() method. "
+                           "Using populate_buy_trend from the strategy.")
         if not hasattr(self.hyperopt, 'populate_sell_trend'):
-            logger.warning("Custom Hyperopt does not provide populate_sell_trend. "
-                           "Using populate_sell_trend from DefaultStrategy.")
+            logger.warning("Hyperopt class does not provide populate_sell_trend() method. "
+                           "Using populate_sell_trend from the strategy.")
 
     def _load_hyperopt(
             self, hyperopt_name: str, config: Dict, extra_dir: Optional[str] = None) -> IHyperOpt:

From 50b45639124ae418a4383164ce6d08b16cb506f6 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 18 Sep 2019 22:57:37 +0300
Subject: [PATCH 179/227] Tests adjusted

---
 tests/optimize/test_backtest_detail.py |  4 ++--
 tests/optimize/test_backtesting.py     | 20 ++++++++++----------
 tests/optimize/test_hyperopt.py        | 24 ++++++++++++------------
 tests/test_configuration.py            |  2 +-
 4 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py
index 1bcd9b08e..d8a4190e2 100644
--- a/tests/optimize/test_backtest_detail.py
+++ b/tests/optimize/test_backtest_detail.py
@@ -291,8 +291,8 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
     patch_exchange(mocker)
     frame = _build_backtest_dataframe(data.data)
     backtesting = Backtesting(default_conf)
-    backtesting.advise_buy = lambda a, m: frame
-    backtesting.advise_sell = lambda a, m: frame
+    backtesting.strategy.advise_buy = lambda a, m: frame
+    backtesting.strategy.advise_sell = lambda a, m: frame
     caplog.set_level(logging.DEBUG)
 
     pair = "UNITTEST/BTC"
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index 99fb30cbd..7b50f2b18 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -313,8 +313,8 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
     assert backtesting.config == default_conf
     assert backtesting.ticker_interval == '5m'
     assert callable(backtesting.strategy.tickerdata_to_dataframe)
-    assert callable(backtesting.advise_buy)
-    assert callable(backtesting.advise_sell)
+    assert callable(backtesting.strategy.advise_buy)
+    assert callable(backtesting.strategy.advise_sell)
     assert isinstance(backtesting.strategy.dp, DataProvider)
     get_fee.assert_called()
     assert backtesting.fee == 0.5
@@ -627,8 +627,8 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
 
     backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
     backtesting = Backtesting(default_conf)
-    backtesting.advise_buy = fun  # Override
-    backtesting.advise_sell = fun  # Override
+    backtesting.strategy.advise_buy = fun  # Override
+    backtesting.strategy.advise_sell = fun  # Override
     results = backtesting.backtest(backtest_conf)
     assert results.empty
 
@@ -642,8 +642,8 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
 
     backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
     backtesting = Backtesting(default_conf)
-    backtesting.advise_buy = fun  # Override
-    backtesting.advise_sell = fun  # Override
+    backtesting.strategy.advise_buy = fun  # Override
+    backtesting.strategy.advise_sell = fun  # Override
     results = backtesting.backtest(backtest_conf)
     assert results.empty
 
@@ -657,8 +657,8 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
     default_conf['experimental'] = {"use_sell_signal": True}
     default_conf['ticker_interval'] = '1m'
     backtesting = Backtesting(default_conf)
-    backtesting.advise_buy = _trend_alternate  # Override
-    backtesting.advise_sell = _trend_alternate  # Override
+    backtesting.strategy.advise_buy = _trend_alternate  # Override
+    backtesting.strategy.advise_sell = _trend_alternate  # Override
     results = backtesting.backtest(backtest_conf)
     backtesting._store_backtest_result("test_.json", results)
     # 200 candles in backtest data
@@ -700,8 +700,8 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
     default_conf['ticker_interval'] = '5m'
 
     backtesting = Backtesting(default_conf)
-    backtesting.advise_buy = _trend_alternate_hold  # Override
-    backtesting.advise_sell = _trend_alternate_hold  # Override
+    backtesting.strategy.advise_buy = _trend_alternate_hold  # Override
+    backtesting.strategy.advise_sell = _trend_alternate_hold  # Override
 
     data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
     min_date, max_date = get_timeframe(data_processed)
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 15ecb92d8..2e383c839 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -166,10 +166,10 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
     x = HyperOptResolver(default_conf, ).hyperopt
     assert not hasattr(x, 'populate_buy_trend')
     assert not hasattr(x, 'populate_sell_trend')
-    assert log_has("Custom Hyperopt does not provide populate_sell_trend. "
-                   "Using populate_sell_trend from DefaultStrategy.", caplog)
-    assert log_has("Custom Hyperopt does not provide populate_buy_trend. "
-                   "Using populate_buy_trend from DefaultStrategy.", caplog)
+    assert log_has("Hyperopt class does not provide populate_sell_trend() method. "
+                   "Using populate_sell_trend from the strategy.", caplog)
+    assert log_has("Hyperopt class does not provide populate_buy_trend() method. "
+                   "Using populate_buy_trend from the strategy.", caplog)
     assert hasattr(x, "ticker_interval")
 
 
@@ -415,8 +415,8 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
     assert dumper.called
     # Should be called twice, once for tickerdata, once to save evaluations
     assert dumper.call_count == 2
-    assert hasattr(hyperopt.backtesting, "advise_sell")
-    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
     assert hasattr(hyperopt, "max_open_trades")
     assert hyperopt.max_open_trades == default_conf['max_open_trades']
     assert hasattr(hyperopt, "position_stacking")
@@ -709,8 +709,8 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
     assert dumper.called
     # Should be called twice, once for tickerdata, once to save evaluations
     assert dumper.call_count == 2
-    assert hasattr(hyperopt.backtesting, "advise_sell")
-    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
     assert hasattr(hyperopt, "max_open_trades")
     assert hyperopt.max_open_trades == default_conf['max_open_trades']
     assert hasattr(hyperopt, "position_stacking")
@@ -783,8 +783,8 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
     assert dumper.called
     # Should be called twice, once for tickerdata, once to save evaluations
     assert dumper.call_count == 2
-    assert hasattr(hyperopt.backtesting, "advise_sell")
-    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
     assert hasattr(hyperopt, "max_open_trades")
     assert hyperopt.max_open_trades == default_conf['max_open_trades']
     assert hasattr(hyperopt, "position_stacking")
@@ -828,8 +828,8 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
     assert dumper.called
     # Should be called twice, once for tickerdata, once to save evaluations
     assert dumper.call_count == 2
-    assert hasattr(hyperopt.backtesting, "advise_sell")
-    assert hasattr(hyperopt.backtesting, "advise_buy")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
+    assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
     assert hasattr(hyperopt, "max_open_trades")
     assert hyperopt.max_open_trades == default_conf['max_open_trades']
     assert hasattr(hyperopt, "position_stacking")
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 67bde50fa..53f46fe2e 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -440,7 +440,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
                    caplog)
 
     assert 'strategy_list' in config
-    assert log_has('Using strategy list of 2 Strategies', caplog)
+    assert log_has('Using strategy list of 2 strategies', caplog)
 
     assert 'position_stacking' not in config
 

From f0cf8d6a8133cc5a16a39523e1dccc0054d7a64d Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 07:02:54 +0200
Subject: [PATCH 180/227] Allow easy printing of loaded configuration

(beforechanging types and applying defaults)
---
 docs/data-analysis.md                    |  2 +-
 freqtrade/configuration/configuration.py |  3 +++
 tests/test_configuration.py              | 21 +++++++++++++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/docs/data-analysis.md b/docs/data-analysis.md
index 1bf781c4d..cf292cacd 100644
--- a/docs/data-analysis.md
+++ b/docs/data-analysis.md
@@ -102,7 +102,7 @@ from freqtrade.configuration import Configuration
 config = Configuration.from_files(["config1.json", "config2.json"])
 
 # Show the config in memory
-print(json.dumps(config, indent=2))
+print(json.dumps(config['original_config'], indent=2))
 ```
 
 For Interactive environments, have an additional configuration specifying `user_data_dir` and pass this in last, so you don't have to change directories while running the bot.
diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index dba94abc8..424fdb67e 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -90,6 +90,9 @@ class Configuration:
         # Load all configs
         config: Dict[str, Any] = self.load_from_files(self.args["config"])
 
+        # Keep a copy of the original configuration file
+        config['original_config'] = deepcopy(config)
+
         self._process_common_options(config)
 
         self._process_optimize_options(config)
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 67bde50fa..87f021df4 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -161,6 +161,27 @@ def test_from_config(default_conf, mocker, caplog) -> None:
     assert validated_conf['fiat_display_currency'] == "EUR"
     assert 'internals' in validated_conf
     assert log_has('Validating configuration ...', caplog)
+    assert isinstance(validated_conf['user_data_dir'], Path)
+
+
+def test_print_config(default_conf, mocker, caplog) -> None:
+    conf1 = deepcopy(default_conf)
+    # Delete non-json elements from default_conf
+    del conf1['user_data_dir']
+    config_files = [conf1]
+
+    configsmock = MagicMock(side_effect=config_files)
+    mocker.patch(
+        'freqtrade.configuration.configuration.load_config_file',
+        configsmock
+    )
+
+    validated_conf = Configuration.from_files(['test_conf.json'])
+
+    assert isinstance(validated_conf['user_data_dir'], Path)
+    assert "user_data_dir" in validated_conf
+    assert "original_config" in validated_conf
+    assert isinstance(json.dumps(validated_conf['original_config']), str)
 
 
 def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None:

From 15a4df4c49f1518e42151755374cdd6f73792c32 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 08:34:18 +0200
Subject: [PATCH 181/227] Mock create_datadir to make sure no folders are left
 behind

---
 tests/test_configuration.py | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 87f021df4..0cf8590cb 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -143,12 +143,10 @@ def test_from_config(default_conf, mocker, caplog) -> None:
     conf2['exchange']['pair_whitelist'] += ['NANO/BTC']
     conf2['fiat_display_currency'] = "EUR"
     config_files = [conf1, conf2]
+    mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x)
 
     configsmock = MagicMock(side_effect=config_files)
-    mocker.patch(
-        'freqtrade.configuration.configuration.load_config_file',
-        configsmock
-    )
+    mocker.patch('freqtrade.configuration.configuration.load_config_file',configsmock)
 
     validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json'])
 
@@ -171,10 +169,8 @@ def test_print_config(default_conf, mocker, caplog) -> None:
     config_files = [conf1]
 
     configsmock = MagicMock(side_effect=config_files)
-    mocker.patch(
-        'freqtrade.configuration.configuration.load_config_file',
-        configsmock
-    )
+    mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x)
+    mocker.patch('freqtrade.configuration.configuration.load_config_file', configsmock)
 
     validated_conf = Configuration.from_files(['test_conf.json'])
 

From dc825c249c2fb1e34ec21f0a4eeafbb9eabf9e43 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Fri, 20 Sep 2019 20:51:31 +0300
Subject: [PATCH 182/227] Make flake happy

---
 tests/test_configuration.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 0cf8590cb..7cb7f12da 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -146,7 +146,7 @@ def test_from_config(default_conf, mocker, caplog) -> None:
     mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x)
 
     configsmock = MagicMock(side_effect=config_files)
-    mocker.patch('freqtrade.configuration.configuration.load_config_file',configsmock)
+    mocker.patch('freqtrade.configuration.configuration.load_config_file', configsmock)
 
     validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json'])
 

From 1cd8ed0c1afec3236e1f0e0bc0a198c33ca32357 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:02:07 +0200
Subject: [PATCH 183/227] Remove --refresh-pairs

---
 freqtrade/configuration/arguments.py     | 2 +-
 freqtrade/configuration/cli_options.py   | 7 -------
 freqtrade/configuration/configuration.py | 4 ----
 freqtrade/data/dataprovider.py           | 1 -
 freqtrade/optimize/backtesting.py        | 2 --
 freqtrade/optimize/edge_cli.py           | 2 +-
 freqtrade/optimize/hyperopt.py           | 2 --
 7 files changed, 2 insertions(+), 18 deletions(-)

diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index bb961173b..6e2ecea2e 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -15,7 +15,7 @@ ARGS_STRATEGY = ["strategy", "strategy_path"]
 ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"]
 
 ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
-                        "max_open_trades", "stake_amount", "refresh_pairs"]
+                        "max_open_trades", "stake_amount"]
 
 ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
                                         "strategy_list", "export", "exportfilename"]
diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py
index bf1ec3620..cb07dbdba 100644
--- a/freqtrade/configuration/cli_options.py
+++ b/freqtrade/configuration/cli_options.py
@@ -107,13 +107,6 @@ AVAILABLE_CLI_OPTIONS = {
         help='Specify stake_amount.',
         type=float,
     ),
-    "refresh_pairs": Arg(
-        '-r', '--refresh-pairs-cached',
-        help='Refresh the pairs files in tests/testdata with the latest data from the '
-        'exchange. Use it if you want to run your optimization commands with '
-        'up-to-date data.',
-        action='store_true',
-    ),
     # Backtesting
     "position_stacking": Arg(
         '--eps', '--enable-position-stacking',
diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index 1aed32e50..af9e420f8 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -212,10 +212,6 @@ class Configuration:
 
         self._process_datadir_options(config)
 
-        self._args_to_config(config, argname='refresh_pairs',
-                             logstring='Parameter -r/--refresh-pairs-cached detected ...',
-                             deprecated_msg='-r/--refresh-pairs-cached will be removed soon.')
-
         self._args_to_config(config, argname='strategy_list',
                              logstring='Using strategy list of {} strategies', logfun=len)
 
diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py
index eb6ec0f2a..7d5e4540b 100644
--- a/freqtrade/data/dataprovider.py
+++ b/freqtrade/data/dataprovider.py
@@ -65,7 +65,6 @@ class DataProvider:
         """
         return load_pair_history(pair=pair,
                                  ticker_interval=ticker_interval or self._config['ticker_interval'],
-                                 refresh_pairs=False,
                                  datadir=Path(self._config['datadir'])
                                  )
 
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 4956907fb..6074b281b 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -414,8 +414,6 @@ class Backtesting:
             datadir=Path(self.config['datadir']),
             pairs=pairs,
             ticker_interval=self.ticker_interval,
-            refresh_pairs=self.config.get('refresh_pairs', False),
-            exchange=self.exchange,
             timerange=timerange,
         )
 
diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py
index f22bcb642..6484a5328 100644
--- a/freqtrade/optimize/edge_cli.py
+++ b/freqtrade/optimize/edge_cli.py
@@ -39,7 +39,7 @@ class EdgeCli:
         self.strategy = StrategyResolver(self.config).strategy
 
         self.edge = Edge(config, self.exchange, self.strategy)
-        self.edge._refresh_pairs = self.config.get('refresh_pairs', False)
+        self.edge._refresh_pairs = False
 
         self.timerange = TimeRange.parse_timerange(None if self.config.get(
             'timerange') is None else str(self.config.get('timerange')))
diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index c511aa5ac..61623d3df 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -362,8 +362,6 @@ class Hyperopt:
             datadir=Path(self.config['datadir']),
             pairs=self.config['exchange']['pair_whitelist'],
             ticker_interval=self.backtesting.ticker_interval,
-            refresh_pairs=self.config.get('refresh_pairs', False),
-            exchange=self.backtesting.exchange,
             timerange=timerange
         )
 

From e66fa1cec69732670b93b1e8edbee4b34537cbe1 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:12:35 +0200
Subject: [PATCH 184/227] Adjust tests to not use --refresh-pairs

---
 tests/data/test_dataprovider.py    | 1 -
 tests/data/test_history.py         | 7 ++-----
 tests/optimize/test_backtesting.py | 5 -----
 tests/optimize/test_edge_cli.py    | 9 ---------
 tests/optimize/test_hyperopt.py    | 5 -----
 tests/test_arguments.py            | 2 --
 tests/test_configuration.py        | 7 -------
 7 files changed, 2 insertions(+), 34 deletions(-)

diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py
index ec176d889..39e2f7d2e 100644
--- a/tests/data/test_dataprovider.py
+++ b/tests/data/test_dataprovider.py
@@ -45,7 +45,6 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history):
     data = dp.historic_ohlcv("UNITTEST/BTC", "5m")
     assert isinstance(data, DataFrame)
     assert historymock.call_count == 1
-    assert historymock.call_args_list[0][1]["refresh_pairs"] is False
     assert historymock.call_args_list[0][1]["ticker_interval"] == "5m"
 
 
diff --git a/tests/data/test_history.py b/tests/data/test_history.py
index e747794e5..abe3f0886 100644
--- a/tests/data/test_history.py
+++ b/tests/data/test_history.py
@@ -74,8 +74,7 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf, testdatadir) -> Non
     assert ld is None
     assert log_has(
         'No history data for pair: "UNITTEST/BTC", interval: 7m. '
-        'Use --refresh-pairs-cached option or `freqtrade download-data` '
-        'script to download the data', caplog
+        'Use `freqtrade download-data` to download the data', caplog
     )
 
 
@@ -105,13 +104,11 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
     # do not download a new pair if refresh_pairs isn't set
     history.load_pair_history(datadir=testdatadir,
                               ticker_interval='1m',
-                              refresh_pairs=False,
                               pair='MEME/BTC')
     assert os.path.isfile(file) is False
     assert log_has(
         'No history data for pair: "MEME/BTC", interval: 1m. '
-        'Use --refresh-pairs-cached option or `freqtrade download-data` '
-        'script to download the data', caplog
+        'Use `freqtrade download-data` to download the data', caplog
     )
 
     # download a new pair if refresh_pairs is set
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index 7b50f2b18..d06116755 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -197,7 +197,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
     assert config['runmode'] == RunMode.BACKTEST
 
 
-@pytest.mark.filterwarnings("ignore:DEPRECATED")
 def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None:
     patched_configuration_load_config_file(mocker, default_conf)
     mocker.patch(
@@ -213,7 +212,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
         '--ticker-interval', '1m',
         '--enable-position-stacking',
         '--disable-max-market-positions',
-        '--refresh-pairs-cached',
         '--timerange', ':100',
         '--export', '/bar/foo',
         '--export-filename', 'foo_bar.json'
@@ -240,9 +238,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
     assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
     assert log_has('max_open_trades set to unlimited ...', caplog)
 
-    assert 'refresh_pairs' in config
-    assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' in config
     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
 
diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py
index f312bba2c..97103da55 100644
--- a/tests/optimize/test_edge_cli.py
+++ b/tests/optimize/test_edge_cli.py
@@ -3,8 +3,6 @@
 
 from unittest.mock import MagicMock
 
-import pytest
-
 from freqtrade.edge import PairInfo
 from freqtrade.optimize import setup_configuration, start_edge
 from freqtrade.optimize.edge_cli import EdgeCli
@@ -35,14 +33,10 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
     assert 'ticker_interval' in config
     assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
 
-    assert 'refresh_pairs' not in config
-    assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' not in config
     assert 'stoploss_range' not in config
 
 
-@pytest.mark.filterwarnings("ignore:DEPRECATED")
 def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> None:
     patched_configuration_load_config_file(mocker, edge_conf)
     mocker.patch(
@@ -56,7 +50,6 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
         '--datadir', '/foo/bar',
         'edge',
         '--ticker-interval', '1m',
-        '--refresh-pairs-cached',
         '--timerange', ':100',
         '--stoplosses=-0.01,-0.10,-0.001'
     ]
@@ -74,8 +67,6 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
     assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
                    caplog)
 
-    assert 'refresh_pairs' in config
-    assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
     assert 'timerange' in config
     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
 
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 2e383c839..543acbde6 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -94,7 +94,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
     assert config['runmode'] == RunMode.HYPEROPT
 
 
-@pytest.mark.filterwarnings("ignore:DEPRECATED")
 def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None:
     patched_configuration_load_config_file(mocker, default_conf)
     mocker.patch(
@@ -108,7 +107,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
         'hyperopt',
         '--ticker-interval', '1m',
         '--timerange', ':100',
-        '--refresh-pairs-cached',
         '--enable-position-stacking',
         '--disable-max-market-positions',
         '--epochs', '1000',
@@ -137,9 +135,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
     assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
     assert log_has('max_open_trades set to unlimited ...', caplog)
 
-    assert 'refresh_pairs' in config
-    assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' in config
     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
 
diff --git a/tests/test_arguments.py b/tests/test_arguments.py
index 7b18aa356..cf0104c01 100644
--- a/tests/test_arguments.py
+++ b/tests/test_arguments.py
@@ -99,7 +99,6 @@ def test_parse_args_backtesting_custom() -> None:
         '-c', 'test_conf.json',
         'backtesting',
         '--ticker-interval', '1m',
-        '--refresh-pairs-cached',
         '--strategy-list',
         'DefaultStrategy',
         'SampleStrategy'
@@ -110,7 +109,6 @@ def test_parse_args_backtesting_custom() -> None:
     assert call_args["subparser"] == 'backtesting'
     assert call_args["func"] is not None
     assert call_args["ticker_interval"] == '1m'
-    assert call_args["refresh_pairs"] is True
     assert type(call_args["strategy_list"]) is list
     assert len(call_args["strategy_list"]) == 2
 
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 53f46fe2e..6cd38bb6d 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -341,14 +341,10 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
     assert 'position_stacking' not in config
     assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
 
-    assert 'refresh_pairs' not in config
-    assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' not in config
     assert 'export' not in config
 
 
-@pytest.mark.filterwarnings("ignore:DEPRECATED")
 def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None:
     patched_configuration_load_config_file(mocker, default_conf)
     mocker.patch(
@@ -368,7 +364,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
         '--ticker-interval', '1m',
         '--enable-position-stacking',
         '--disable-max-market-positions',
-        '--refresh-pairs-cached',
         '--timerange', ':100',
         '--export', '/bar/foo'
     ]
@@ -398,8 +393,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
     assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
     assert log_has('max_open_trades set to unlimited ...', caplog)
 
-    assert 'refresh_pairs'in config
-    assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
     assert 'timerange' in config
     assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
 

From 9cedbc13455df7b8c763dd0357dd3155d04a2b12 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:16:49 +0200
Subject: [PATCH 185/227] Cleanup history.py and update documentation

---
 docs/bot-usage.md          | 12 ------------
 docs/deprecated.md         |  6 ++----
 freqtrade/data/history.py  | 31 +++++++++----------------------
 tests/data/test_history.py | 25 -------------------------
 4 files changed, 11 insertions(+), 63 deletions(-)

diff --git a/docs/bot-usage.md b/docs/bot-usage.md
index 0b0561d3d..f44400e32 100644
--- a/docs/bot-usage.md
+++ b/docs/bot-usage.md
@@ -184,10 +184,6 @@ optional arguments:
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
-  -r, --refresh-pairs-cached
-                        Refresh the pairs files in tests/testdata with the
-                        latest data from the exchange. Use it if you want to
-                        run your optimization commands with up-to-date data.
   --eps, --enable-position-stacking
                         Allow buying the same pair multiple times (position
                         stacking).
@@ -245,10 +241,6 @@ optional arguments:
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
-  -r, --refresh-pairs-cached
-                        Refresh the pairs files in tests/testdata with the
-                        latest data from the exchange. Use it if you want to
-                        run your optimization commands with up-to-date data.
   --customhyperopt NAME
                         Specify hyperopt class name (default:
                         `DefaultHyperOpts`).
@@ -310,10 +302,6 @@ optional arguments:
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
-  -r, --refresh-pairs-cached
-                        Refresh the pairs files in tests/testdata with the
-                        latest data from the exchange. Use it if you want to
-                        run your optimization commands with up-to-date data.
   --stoplosses STOPLOSS_RANGE
                         Defines a range of stoploss against which edge will
                         assess the strategy the format is "min,max,step"
diff --git a/docs/deprecated.md b/docs/deprecated.md
index ed70b1936..24c2bb1e3 100644
--- a/docs/deprecated.md
+++ b/docs/deprecated.md
@@ -4,7 +4,7 @@ This page contains description of the command line arguments, configuration para
 and the bot features that were declared as DEPRECATED by the bot development team
 and are no longer supported. Please avoid their usage in your configuration.
 
-## Deprecated
+## Removed features
 
 ### the `--refresh-pairs-cached` command line option
 
@@ -12,9 +12,7 @@ and are no longer supported. Please avoid their usage in your configuration.
 Since this leads to much confusion, and slows down backtesting (while not being part of backtesting) this has been singled out 
 as a seperate freqtrade subcommand `freqtrade download-data`.
 
-This command line option was deprecated in `2019.7-dev` and will be removed after the next release.
-
-## Removed features
+This command line option was deprecated in `2019.7-dev` and removed in `2019-9`
 
 ### The **--dynamic-whitelist** command line option
 
diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py
index b4776fab0..981a398da 100644
--- a/freqtrade/data/history.py
+++ b/freqtrade/data/history.py
@@ -129,8 +129,7 @@ def load_pair_history(pair: str,
     else:
         logger.warning(
             f'No history data for pair: "{pair}", interval: {ticker_interval}. '
-            'Use --refresh-pairs-cached option or `freqtrade download-data` '
-            'script to download the data'
+            'Use `freqtrade download-data` to download the data'
         )
         return None
 
@@ -142,33 +141,21 @@ def load_data(datadir: Path,
               exchange: Optional[Exchange] = None,
               timerange: TimeRange = TimeRange(None, None, 0, 0),
               fill_up_missing: bool = True,
-              live: bool = False
               ) -> Dict[str, DataFrame]:
     """
     Loads ticker history data for a list of pairs the given parameters
     :return: dict(:)
     """
     result: Dict[str, DataFrame] = {}
-    if live:
-        if exchange:
-            logger.info('Live: Downloading data for all defined pairs ...')
-            exchange.refresh_latest_ohlcv([(pair, ticker_interval) for pair in pairs])
-            result = {key[0]: value for key, value in exchange._klines.items() if value is not None}
-        else:
-            raise OperationalException(
-                "Exchange needs to be initialized when using live data."
-            )
-    else:
-        logger.info('Using local backtesting data ...')
 
-        for pair in pairs:
-            hist = load_pair_history(pair=pair, ticker_interval=ticker_interval,
-                                     datadir=datadir, timerange=timerange,
-                                     refresh_pairs=refresh_pairs,
-                                     exchange=exchange,
-                                     fill_up_missing=fill_up_missing)
-            if hist is not None:
-                result[pair] = hist
+    for pair in pairs:
+        hist = load_pair_history(pair=pair, ticker_interval=ticker_interval,
+                                 datadir=datadir, timerange=timerange,
+                                 refresh_pairs=refresh_pairs,
+                                 exchange=exchange,
+                                 fill_up_missing=fill_up_missing)
+        if hist is not None:
+            result[pair] = hist
     return result
 
 
diff --git a/tests/data/test_history.py b/tests/data/test_history.py
index abe3f0886..1983c2a9f 100644
--- a/tests/data/test_history.py
+++ b/tests/data/test_history.py
@@ -131,31 +131,6 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
     _clean_test_file(file)
 
 
-def test_load_data_live(default_conf, mocker, caplog, testdatadir) -> None:
-    refresh_mock = MagicMock()
-    mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock)
-    exchange = get_patched_exchange(mocker, default_conf)
-
-    history.load_data(datadir=testdatadir, ticker_interval='5m',
-                      pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'],
-                      live=True,
-                      exchange=exchange)
-    assert refresh_mock.call_count == 1
-    assert len(refresh_mock.call_args_list[0][0][0]) == 2
-    assert log_has('Live: Downloading data for all defined pairs ...', caplog)
-
-
-def test_load_data_live_noexchange(default_conf, mocker, caplog, testdatadir) -> None:
-
-    with pytest.raises(OperationalException,
-                       match=r'Exchange needs to be initialized when using live data.'):
-        history.load_data(datadir=testdatadir, ticker_interval='5m',
-                          pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'],
-                          exchange=None,
-                          live=True,
-                          )
-
-
 def test_testdata_path(testdatadir) -> None:
     assert str(Path('tests') / 'testdata') in str(testdatadir)
 

From 508a35fc2067ef6123fe3aadfe58c5954ece79a4 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:20:16 +0200
Subject: [PATCH 186/227] Update comment as to why certain points have not been
 removed

---
 freqtrade/data/history.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py
index 981a398da..865289f38 100644
--- a/freqtrade/data/history.py
+++ b/freqtrade/data/history.py
@@ -143,8 +143,12 @@ def load_data(datadir: Path,
               fill_up_missing: bool = True,
               ) -> Dict[str, DataFrame]:
     """
-    Loads ticker history data for a list of pairs the given parameters
+    Loads ticker history data for a list of pairs
     :return: dict(:)
+    TODO: refresh_pairs is still used by edge to keep the data uptodate.
+        This should be replaced in the future. Instead, writing the current candles to disk
+        from dataprovider should be implemented, as this would avoid loading ohlcv data twice.
+        exchange and refresh_pairs are then not needed here nor in load_pair_history.
     """
     result: Dict[str, DataFrame] = {}
 

From 313091eb1caf6a2ce2d4838fcccd863cbbc9a582 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:22:51 +0200
Subject: [PATCH 187/227] some more refresh_pairs cleanups

---
 freqtrade/optimize/edge_cli.py     | 1 +
 tests/data/test_converter.py       | 1 -
 tests/data/test_history.py         | 3 +--
 tests/optimize/test_backtesting.py | 3 ---
 tests/optimize/test_hyperopt.py    | 3 ---
 5 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py
index 6484a5328..0cf5a009b 100644
--- a/freqtrade/optimize/edge_cli.py
+++ b/freqtrade/optimize/edge_cli.py
@@ -39,6 +39,7 @@ class EdgeCli:
         self.strategy = StrategyResolver(self.config).strategy
 
         self.edge = Edge(config, self.exchange, self.strategy)
+        # Set refresh_pairs to false for edge-cli (it must be true for edge)
         self.edge._refresh_pairs = False
 
         self.timerange = TimeRange.parse_timerange(None if self.config.get(
diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py
index 72da47e76..e773a970e 100644
--- a/tests/data/test_converter.py
+++ b/tests/data/test_converter.py
@@ -24,7 +24,6 @@ def test_parse_ticker_dataframe(ticker_history_list, caplog):
 def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
     data = load_pair_history(datadir=testdatadir,
                              ticker_interval='1m',
-                             refresh_pairs=False,
                              pair='UNITTEST/BTC',
                              fill_up_missing=False)
     caplog.set_level(logging.DEBUG)
diff --git a/tests/data/test_history.py b/tests/data/test_history.py
index 1983c2a9f..e386c3506 100644
--- a/tests/data/test_history.py
+++ b/tests/data/test_history.py
@@ -321,7 +321,6 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
     start = arrow.get('2018-01-01T00:00:00')
     end = arrow.get('2018-01-11T00:00:00')
     tickerdata = history.load_data(testdatadir, '5m', ['UNITTEST/BTC'],
-                                   refresh_pairs=False,
                                    timerange=TimeRange('date', 'date',
                                                        start.timestamp, end.timestamp))
     # timedifference in 5 minutes
@@ -336,7 +335,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
     start = arrow.get('2018-01-10T00:00:00')
     end = arrow.get('2018-02-20T00:00:00')
     tickerdata = history.load_data(datadir=testdatadir, ticker_interval='5m',
-                                   pairs=['UNITTEST/BTC'], refresh_pairs=False,
+                                   pairs=['UNITTEST/BTC'],
                                    timerange=TimeRange('date', 'date',
                                                        start.timestamp, end.timestamp))
     # timedifference in 5 minutes
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index d06116755..fa40809d8 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -188,9 +188,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
     assert 'position_stacking' not in config
     assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
 
-    assert 'refresh_pairs' not in config
-    assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' not in config
     assert 'export' not in config
     assert 'runmode' in config
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 543acbde6..0888c79d4 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -86,9 +86,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
     assert 'position_stacking' not in config
     assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
 
-    assert 'refresh_pairs' not in config
-    assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
-
     assert 'timerange' not in config
     assert 'runmode' in config
     assert config['runmode'] == RunMode.HYPEROPT

From 2fcddfc8666f982627b55188a6b502c98dcf1577 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Fri, 20 Sep 2019 20:29:26 +0200
Subject: [PATCH 188/227] Clarify updating existing data

---
 docs/backtesting.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 45d5f486f..879cf78a3 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -7,7 +7,7 @@ Backtesting.
 
 To download data (candles / OHLCV) needed for backtesting and hyperoptimization use the `freqtrade download-data` command.
 
-If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes.
+If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes for 30 days.
 Exchange and pairs will come from `config.json` (if specified using `-c/--config`). Otherwise `--exchange` becomes mandatory.
 
 Alternatively, a `pairs.json` file can be used.
@@ -37,6 +37,10 @@ This will download ticker data for all the currency pairs you defined in `pairs.
 - Use `--timeframes` to specify which tickers to download. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute tickers.
 - To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
 
+!!! Tip Updating existing data
+    If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will load the available data and only download the missing data.
+    Be carefull though: If the number is too small (which would result in a few missing days), the whole dataset will be removed and only xx days will be downloaded.
+
 ## Test your strategy with Backtesting
 
 Now you have good Buy and Sell strategies and some historic data, you want to test it against

From b1a3e213ae9eab1479ba852589593591f425a485 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 21 Sep 2019 10:09:14 +0200
Subject: [PATCH 189/227] Improve backtesting docs

---
 docs/backtesting.md   | 117 +++++++++++++++++-------------------------
 docs/data-download.md |  39 ++++++++++++++
 docs/hyperopt.md      |   3 ++
 mkdocs.yml            |   1 +
 4 files changed, 89 insertions(+), 71 deletions(-)
 create mode 100644 docs/data-download.md

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 45d5f486f..b3f22c664 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -1,41 +1,9 @@
 # Backtesting
 
-This page explains how to validate your strategy performance by using
-Backtesting.
+This page explains how to validate your strategy performance by using Backtesting.
 
-## Getting data for backtesting and hyperopt
-
-To download data (candles / OHLCV) needed for backtesting and hyperoptimization use the `freqtrade download-data` command.
-
-If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes.
-Exchange and pairs will come from `config.json` (if specified using `-c/--config`). Otherwise `--exchange` becomes mandatory.
-
-Alternatively, a `pairs.json` file can be used.
-
-If you are using Binance for example:
-
-- create a directory `user_data/data/binance` and copy `pairs.json` in that directory.
-- update the `pairs.json` to contain the currency pairs you are interested in.
-
-```bash
-mkdir -p user_data/data/binance
-cp freqtrade/tests/testdata/pairs.json user_data/data/binance
-```
-
-Then run:
-
-```bash
-freqtrade download-data --exchange binance
-```
-
-This will download ticker data for all the currency pairs you defined in `pairs.json`.
-
-- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`.
-- To change the exchange used to download the tickers, please use a different configuration file (you'll probably need to adjust ratelimits etc.)
-- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`.
-- To download ticker data for only 10 days, use `--days 10` (defaults to 30 days).
-- Use `--timeframes` to specify which tickers to download. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute tickers.
-- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
+Backtesting requires historic data to be available.
+To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data downloading](data-download.md) section of the documentation.
 
 ## Test your strategy with Backtesting
 
@@ -46,14 +14,13 @@ real data. This is what we call
 Backtesting will use the crypto-currencies (pairs) from your config file
 and load ticker data from `user_data/data/` by default.
 If no data is available for the exchange / pair / ticker interval combination, backtesting will
-ask you to download them first using `freqtrade download-data`. 
-For details on downloading, please refer to the [relevant section](#Getting-data-for-backtesting-and-hyperopt) in the documentation.
+ask you to download them first using `freqtrade download-data`.
+For details on downloading, please refer to the [Downloading data](data-download.md) section in the documentation.
 
-The result of backtesting will confirm you if your bot has better odds of making a profit than a loss.
-
-The backtesting is very easy with freqtrade.
+The result of backtesting will confirm if your bot has better odds of making a profit than a loss.
 
 ### Run a backtesting against the currencies listed in your config file
+
 #### With 5 min tickers (Per default)
 
 ```bash
@@ -107,31 +74,33 @@ freqtrade backtesting --export trades --export-filename=backtest_samplestrategy.
 
 #### Running backtest with smaller testset
 
-Use the `--timerange` argument to change how much of the testset
-you want to use. The last N ticks/timeframes will be used.
+Use the `--timerange` argument to change how much of the testset you want to use.
 
 Example:
 
 ```bash
-freqtrade backtesting --timerange=-200
+freqtrade backtesting --timerange=20190501-
 ```
 
 #### Advanced use of timerange
 
-Doing `--timerange=-200` will get the last 200 timeframes
-from your inputdata. You can also specify specific dates,
-or a range span indexed by start and stop.
+Doing `--timerange=20190101-` will all available data starting with January 1st, 2019 from your inputdata.
+You can also specify specific dates, or a range span indexed by start and stop.
 
 The full timerange specification:
 
-- Use last 123 tickframes of data: `--timerange=-123`
-- Use first 123 tickframes of data: `--timerange=123-`
-- Use tickframes from line 123 through 456: `--timerange=123-456`
+
 - Use tickframes till 2018/01/31: `--timerange=-20180131`
 - Use tickframes since 2018/01/31: `--timerange=20180131-`
 - Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301`
 - Use tickframes between POSIX timestamps 1527595200 1527618600:
                                                 `--timerange=1527595200-1527618600`
+- Use last 123 tickframes of data: `--timerange=-123`
+- Use first 123 tickframes of data: `--timerange=123-`
+- Use tickframes from line 123 through 456: `--timerange=123-456`
+
+!!! warning
+    Be carefull when using non-date functions - these do not allow you to specify precise dates, so if you updated the test-data it will probably use a different dataset.
 
 ## Understand the backtesting result
 
@@ -177,11 +146,12 @@ A backtesting result will look like that:
 | TOTAL    |           2 |           0.78 |           1.57 |       0.00007855 |           0.78 | 4:00:00        |        2 |      0 |
 ```
 
-The 1st table will contain all trades the bot made.
+The 1st table contains all trades the bot made, including "left open trades".
 
-The 2nd table will contain a recap of sell reasons.
+The 2nd table contains a recap of sell reasons.
 
-The 3rd table will contain all trades the bot had to `forcesell` at the end of the backtest period to present a full picture.
+The 3rd table contains all trades the bot had to `forcesell` at the end of the backtest period to present a full picture.
+This is necessary to simulate realistic behaviour, since the backtest period has to end at some point, while realistically, you could leave the bot running forever.
 These trades are also included in the first table, but are extracted separately for clarity.
 
 The last line will give you the overall performance of your strategy,
@@ -191,22 +161,16 @@ here:
 | TOTAL    |         429 |           0.36 |         152.41 |       0.00762792 |          76.20 | 4:12:00        |      186 |    243 |
 ```
 
-We understand the bot has made `429` trades for an average duration of
-`4:12:00`, with a performance of `76.20%` (profit), that means it has
+The bot has made `429` trades for an average duration of `4:12:00`, with a performance of `76.20%` (profit), that means it has
 earned a total of `0.00762792 BTC` starting with a capital of 0.01 BTC.
 
-The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums all the profits/losses. 
-The column `tot profit %` shows instead the total profit % in relation to allocated capital 
-(`max_open_trades * stake_amount`). In the above results we have `max_open_trades=2 stake_amount=0.005` in config 
-so `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`.
+The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums all the profits/losses.
+The column `tot profit %` shows instead the total profit % in relation to allocated capital (`max_open_trades * stake_amount`).
+In the above results we have `max_open_trades=2 stake_amount=0.005` in config  so `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`.
 
-As you will see your strategy performance will be influenced by your buy
-strategy, your sell strategy, and also by the `minimal_roi` and
-`stop_loss` you have set.
+Your strategy performance is influenced by your buy strategy, your sell strategy, and also by the `minimal_roi` and `stop_loss` you have set.
 
-As for an example if your minimal_roi is only `"0":  0.01`. You cannot
-expect the bot to make more profit than 1% (because it will sell every
-time a trade will reach 1%).
+For example, if your minimal_roi is only `"0":  0.01`. You cannot expect the bot to make more profit than 1% (because it will sell every time a trade will reach 1%).
 
 ```json
 "minimal_roi": {
@@ -215,22 +179,33 @@ time a trade will reach 1%).
 ```
 
 On the other hand, if you set a too high `minimal_roi` like `"0":  0.55`
-(55%), there is a lot of chance that the bot will never reach this
-profit. Hence, keep in mind that your performance is a mix of your
-strategies, your configuration, and the crypto-currency you have set up.
+(55%), there is almost no chance that the bot will never reach this profit.
+Hence, keep in mind that your performance is a mix of the different elements of the strategy, your configuration, and the crypto-currency pairs you have set up.
+
+### Assumptions made by backtesting
+
+Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions:
+
+- Buys happen at open-price
+- Low happens before high for stoploss, protecting capital first.
+- ROI sells are compared to high - but the ROI value is used (e.g. ROI = 2%, high=5% - so the sell will be at 2%)
+- Stoploss sells happen exactly at stoploss price, even if low was lower
+- Trailing stoploss
+  - High happens first - adjusting stoploss
+  - Low uses the adjusted stoploss (so sells with large high-low difference are backtested correctly)
+- Sell-reason does not explain if a trade was positive or negative, just what triggered the sell (this can look odd if negative ROI values are used)
 
 ### Further backtest-result analysis
 
 To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
 You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section.
 
-
 ## Backtesting multiple strategies
 
-To backtest multiple strategies, a list of Strategies can be provided.
+To compare multiple strategies, a list of Strategies can be provided to backtesting.
 
 This is limited to 1 ticker-interval per run, however, data is only loaded once from disk so if you have multiple
-strategies you'd like to compare, this should give a nice runtime boost.
+strategies you'd like to compare, this will give a nice runtime boost.
 
 All listed Strategies need to be in the same directory.
 
@@ -240,7 +215,7 @@ freqtrade backtesting --timerange 20180401-20180410 --ticker-interval 5m --strat
 
 This will save the results to `user_data/backtest_results/backtest-result-.json`, injecting the strategy-name into the target filename.
 There will be an additional table comparing win/losses of the different strategies (identical to the "Total" row in the first table).
-Detailed output for all strategies one after the other will be available, so make sure to scroll up.
+Detailed output for all strategies one after the other will be available, so make sure to scroll up to see the details per strategy.
 
 ```
 =========================================================== Strategy Summary ===========================================================
diff --git a/docs/data-download.md b/docs/data-download.md
new file mode 100644
index 000000000..f6b1e9904
--- /dev/null
+++ b/docs/data-download.md
@@ -0,0 +1,39 @@
+# Data download
+
+## Getting data for backtesting and hyperopt
+
+To download data (candles / OHLCV) needed for backtesting and hyperoptimization use the `freqtrade download-data` command.
+
+If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes.
+Exchange and pairs will come from `config.json` (if specified using `-c/--config`). Otherwise `--exchange` becomes mandatory.
+
+Alternatively, a `pairs.json` file can be used.
+
+If you are using Binance for example:
+
+- create a directory `user_data/data/binance` and copy `pairs.json` in that directory.
+- update the `pairs.json` to contain the currency pairs you are interested in.
+
+```bash
+mkdir -p user_data/data/binance
+cp freqtrade/tests/testdata/pairs.json user_data/data/binance
+```
+
+Then run:
+
+```bash
+freqtrade download-data --exchange binance
+```
+
+This will download ticker data for all the currency pairs you defined in `pairs.json`.
+
+- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`.
+- To change the exchange used to download the tickers, please use a different configuration file (you'll probably need to adjust ratelimits etc.)
+- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`.
+- To download ticker data for only 10 days, use `--days 10` (defaults to 30 days).
+- Use `--timeframes` to specify which tickers to download. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute tickers.
+- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
+
+## Next step
+
+Great, you now have backtest data downloaded, so you can now start [backtesting](backtesting.md) your strategy.
diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index becf572ee..00aec458c 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -6,6 +6,9 @@ algorithms included in the `scikit-optimize` package to accomplish this. The
 search will burn all your CPU cores, make your laptop sound like a fighter jet
 and still take a long time.
 
+Hyperopt requires historic data to be available, just as backtesting does.
+To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data downloading](data-download.md) section of the documentation.
+
 !!! Bug
     Hyperopt will crash when used with only 1 CPU Core as found out in [Issue #1133](https://github.com/freqtrade/freqtrade/issues/1133)
 
diff --git a/mkdocs.yml b/mkdocs.yml
index 869c6565c..069f060db 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -11,6 +11,7 @@ nav:
         - Telegram: telegram-usage.md
         - Web Hook: webhook-config.md
         - REST API: rest-api.md
+    - Data Download: data-download.md
     - Backtesting: backtesting.md
     - Hyperopt: hyperopt.md
     - Edge positioning: edge.md

From 3245ebccd473082de40de8901eb1705752b1889d Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 21 Sep 2019 11:24:51 +0200
Subject: [PATCH 190/227] Fix problme when no exchange is given to
 download-data

---
 freqtrade/configuration/check_exchange.py |  8 ++++++++
 tests/test_configuration.py               |  7 +++++++
 tests/test_utils.py                       | 18 +++++++++++++++++-
 3 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py
index 61e862a9c..019081def 100644
--- a/freqtrade/configuration/check_exchange.py
+++ b/freqtrade/configuration/check_exchange.py
@@ -27,6 +27,14 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
     logger.info("Checking exchange...")
 
     exchange = config.get('exchange', {}).get('name').lower()
+    if not exchange:
+        raise OperationalException(
+            f'This command requires a configured exchange. You can use either '
+            f'`--exchange  None:
     default_conf['runmode'] = RunMode.PLOT
     assert check_exchange(default_conf)
 
+    # Test no exchange...
+    default_conf.get('exchange').update({'name': ''})
+    default_conf['runmode'] = RunMode.OTHER
+    with pytest.raises(OperationalException,
+                       match=r'This command requires a configured exchange.*'):
+        check_exchange(default_conf)
+
 
 def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
     patched_configuration_load_config_file(mocker, default_conf)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 386efb5ec..8126e5055 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3,10 +3,11 @@ from unittest.mock import MagicMock, PropertyMock
 
 import pytest
 
+from freqtrade import OperationalException
 from freqtrade.state import RunMode
-from tests.conftest import get_args, log_has, patch_exchange
 from freqtrade.utils import (setup_utils_configuration, start_create_userdir,
                              start_download_data, start_list_exchanges)
+from tests.conftest import get_args, log_has, log_has_re, patch_exchange
 
 
 def test_setup_utils_configuration():
@@ -103,3 +104,18 @@ def test_download_data_no_markets(mocker, caplog):
     start_download_data(get_args(args))
     assert dl_mock.call_args[1]['timerange'].starttype == "date"
     assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange binance.", caplog)
+
+
+def test_download_data_no_exchange(mocker, caplog):
+    mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data',
+                 MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
+    patch_exchange(mocker)
+    mocker.patch(
+        'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})
+    )
+    args = [
+        "download-data"
+        ]
+    with pytest.raises(OperationalException,
+                       match=r"This command requires a configured exchange.*"):
+        start_download_data(get_args(args))

From 7aa42f8868a0afaa81b3d1a6d632c4c01a246bb6 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 21 Sep 2019 12:53:15 +0200
Subject: [PATCH 191/227] Fail download-data gracefully if no pairs-file exists

---
 freqtrade/utils.py  |  6 ++++++
 tests/test_utils.py | 24 ++++++++++++++++++++++--
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/freqtrade/utils.py b/freqtrade/utils.py
index 5b2b08357..276c7267b 100644
--- a/freqtrade/utils.py
+++ b/freqtrade/utils.py
@@ -5,6 +5,7 @@ from typing import Any, Dict, List
 
 import arrow
 
+from freqtrade import OperationalException
 from freqtrade.configuration import Configuration, TimeRange
 from freqtrade.configuration.directory_operations import create_userdata_dir
 from freqtrade.data.history import refresh_backtest_ohlcv_data
@@ -70,6 +71,11 @@ def start_download_data(args: Dict[str, Any]) -> None:
         time_since = arrow.utcnow().shift(days=-config['days']).strftime("%Y%m%d")
         timerange = TimeRange.parse_timerange(f'{time_since}-')
 
+    if 'pairs' not in config:
+        raise OperationalException(
+            "Downloading data requires a list of pairs."
+            "Please check the documentation on how to configure this.")
+
     dl_path = Path(config['datadir'])
     logger.info(f'About to download pairs: {config["pairs"]}, '
                 f'intervals: {config["timeframes"]} to {dl_path}')
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8126e5055..dc0badd01 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,4 +1,5 @@
 import re
+from pathlib import Path
 from unittest.mock import MagicMock, PropertyMock
 
 import pytest
@@ -7,7 +8,7 @@ from freqtrade import OperationalException
 from freqtrade.state import RunMode
 from freqtrade.utils import (setup_utils_configuration, start_create_userdir,
                              start_download_data, start_list_exchanges)
-from tests.conftest import get_args, log_has, log_has_re, patch_exchange
+from tests.conftest import get_args, log_has, patch_exchange
 
 
 def test_setup_utils_configuration():
@@ -114,8 +115,27 @@ def test_download_data_no_exchange(mocker, caplog):
         'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})
     )
     args = [
-        "download-data"
+        "download-data",
         ]
     with pytest.raises(OperationalException,
                        match=r"This command requires a configured exchange.*"):
         start_download_data(get_args(args))
+
+
+def test_download_data_no_pairs(mocker, caplog):
+    mocker.patch.object(Path, "exists", MagicMock(return_value=False))
+
+    mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data',
+                 MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
+    patch_exchange(mocker)
+    mocker.patch(
+        'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})
+    )
+    args = [
+        "download-data",
+        "--exchange",
+        "binance",
+    ]
+    with pytest.raises(OperationalException,
+                       match=r"Downloading data requires a list of pairs\..*"):
+        start_download_data(get_args(args))

From ba4db0da493e501ea56cffe20767cf81c67fce15 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 21 Sep 2019 13:16:53 +0200
Subject: [PATCH 192/227] Improve configuration documentation

---
 docs/configuration.md | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index a1c0d1a75..08137fe79 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,14 +1,15 @@
 # Configure the bot
 
-This page explains how to configure the bot.
+Freqtrade has many configurable options and possibilities.
+By default, these options are configured via the configuration file (see below).
 
 ## The Freqtrade configuration file
 
 The bot uses a set of configuration parameters during its operation that all together conform the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file).
 
-Per default, the bot loads configuration from the `config.json` file located in the current working directory.
+Per default, the bot loads the configuration from the file `config.json`, located in the current working directory.
 
-You can change the name of the configuration file used by the bot with the `-c/--config` command line option.
+You can use a different configuration file used by the bot with the `-c/--config` command line option.
 
 In some advanced use cases, multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream.
 
@@ -22,19 +23,26 @@ The Freqtrade configuration file is to be written in the JSON format.
 
 Additionally to the standard JSON syntax, you may use one-line `// ...` and multi-line `/* ... */` comments in your configuration files and trailing commas in the lists of parameters.
 
-Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates syntax of the configuration file at startup and will warn you if you made any errors editing it.
+Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines.
 
 ## Configuration parameters
 
 The table below will list all configuration parameters available.
 
-Mandatory parameters are marked as **Required**.
+Freqtrade can also load many options via command line arguments (check out the commands `--help` output for details).
+The prevelance for all Options is as follows:
+
+- CLI arguments override any other option
+- Configuration files are used in sequence (last file wins), and override Strategy configurations.
+- Strategy configurations are only used if they are not set via configuration or via command line arguments. These options are market with [Strategy Override](#parameters-in-the-strategy) in the below table.
+
+Mandatory parameters are marked as **Required**, which means that they are required to be set in one of the possible ways.
 
 |  Command | Default | Description |
 |----------|---------|-------------|
 | `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades)
-| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. [Strategy Override](#parameters-in-the-strategy).
-| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. [Strategy Override](#parameters-in-the-strategy).
+| `stake_currency` | BTC | **Required.** Crypto-currency used for trading.
+| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance.
 | `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals.
 | `ticker_interval` | [1m, 5m, 15m, 30m, 1h, 1d, ...] | The ticker interval to use (1min, 5 min, 15 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-the-strategy).
 | `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below.
@@ -99,8 +107,6 @@ Mandatory parameters are marked as **Required**.
 The following parameters can be set in either configuration file or strategy.
 Values set in the configuration file always overwrite values set in the strategy.
 
-* `stake_currency`
-* `stake_amount`
 * `ticker_interval`
 * `minimal_roi`
 * `stoploss`

From ab0adabd39ff057a85c97ecf3a2426b6ef68f9fc Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 23 Sep 2019 08:54:11 +0000
Subject: [PATCH 193/227] Bump urllib3 from 1.25.3 to 1.25.5

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.25.3 to 1.25.5.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/master/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.25.3...1.25.5)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 363890a4d..fe995836c 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -6,7 +6,7 @@ python-telegram-bot==12.1.0
 arrow==0.15.2
 cachetools==3.1.1
 requests==2.22.0
-urllib3==1.25.3
+urllib3==1.25.5
 wrapt==1.11.2
 scikit-learn==0.21.3
 joblib==0.13.2

From 242ff26e215c29933bd647d2c713775270d4988d Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 23 Sep 2019 08:54:57 +0000
Subject: [PATCH 194/227] Bump pytest from 5.1.2 to 5.1.3

Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.1.2 to 5.1.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.1.2...5.1.3)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-dev.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-dev.txt b/requirements-dev.txt
index 1b9bf7570..2678130f3 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -7,7 +7,7 @@ flake8==3.7.8
 flake8-type-annotations==0.1.0
 flake8-tidy-imports==2.0.0
 mypy==0.720
-pytest==5.1.2
+pytest==5.1.3
 pytest-asyncio==0.10.0
 pytest-cov==2.7.1
 pytest-mock==1.10.4

From d8bc350445ba404bdbb0217184d7cf26aa2fd475 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 23 Sep 2019 08:55:31 +0000
Subject: [PATCH 195/227] Bump python-telegram-bot from 12.1.0 to 12.1.1

Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 12.1.0 to 12.1.1.
- [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases)
- [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v12.1.0...v12.1.1)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index 363890a4d..c4b5383ae 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -2,7 +2,7 @@
 # mainly used for Raspberry pi installs
 ccxt==1.18.1159
 SQLAlchemy==1.3.8
-python-telegram-bot==12.1.0
+python-telegram-bot==12.1.1
 arrow==0.15.2
 cachetools==3.1.1
 requests==2.22.0

From 0c6164df7eaa59c438279e44e03ba34614b1b36a Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Mon, 23 Sep 2019 11:59:34 +0300
Subject: [PATCH 196/227] Fix memory exhaustion in skopt models list

---
 freqtrade/optimize/hyperopt.py | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index c511aa5ac..04a05dc8f 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -36,6 +36,11 @@ logger = logging.getLogger(__name__)
 
 
 INITIAL_POINTS = 30
+
+# Keep no more than 2*SKOPT_MODELS_MAX_NUM models
+# in the skopt models list
+SKOPT_MODELS_MAX_NUM = 10
+
 MAX_LOSS = 100000  # just a big enough number to be bad result in loss optimization
 
 
@@ -255,12 +260,13 @@ class Hyperopt:
             spaces += self.custom_hyperopt.stoploss_space()
         return spaces
 
-    def generate_optimizer(self, _params: Dict) -> Dict:
+    def generate_optimizer(self, _params: Dict, iteration=None) -> Dict:
         """
         Used Optimize function. Called once per epoch to optimize whatever is configured.
         Keep this function as optimized as possible!
         """
         params = self.get_args(_params)
+
         if self.has_space('roi'):
             self.backtesting.strategy.minimal_roi = \
                     self.custom_hyperopt.generate_roi_table(params)
@@ -342,9 +348,25 @@ class Hyperopt:
             random_state=self.config.get('hyperopt_random_state', None)
         )
 
-    def run_optimizer_parallel(self, parallel, asked) -> List:
+    def fix_optimizer_models_list(self):
+        """
+        WORKAROUND: Since skopt is not actively supported, this resolves problems with skopt
+        memory usage, see also: https://github.com/scikit-optimize/scikit-optimize/pull/746
+
+        This may cease working when skopt updates if implementation of this intrinsic
+        part changes.
+        """
+        n = len(self.opt.models) - SKOPT_MODELS_MAX_NUM
+        # Keep no more than 2*SKOPT_MODELS_MAX_NUM models in the skopt models list,
+        # remove the old ones. These are no really needed, the current model
+        # from the estimator is only used.
+        if n >= SKOPT_MODELS_MAX_NUM:
+            logger.debug(f"Fixing skopt models list, removing {n} old items...")
+            del self.opt.models[0:n]
+
+    def run_optimizer_parallel(self, parallel, asked, i) -> List:
         return parallel(delayed(
-                        wrap_non_picklable_objects(self.generate_optimizer))(v) for v in asked)
+                        wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked)
 
     def load_previous_results(self):
         """ read trials file if we have one """
@@ -407,8 +429,9 @@ class Hyperopt:
                 EVALS = max(self.total_epochs // jobs, 1)
                 for i in range(EVALS):
                     asked = self.opt.ask(n_points=jobs)
-                    f_val = self.run_optimizer_parallel(parallel, asked)
+                    f_val = self.run_optimizer_parallel(parallel, asked, i)
                     self.opt.tell(asked, [v['loss'] for v in f_val])
+                    self.fix_optimizer_models_list()
                     for j in range(jobs):
                         current = i * jobs + j
                         val = f_val[j]

From 95e725c2b62c7752b92d61a270b85acfec87860a Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Mon, 23 Sep 2019 10:15:28 +0000
Subject: [PATCH 197/227] Bump ccxt from 1.18.1159 to 1.18.1180

Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1159 to 1.18.1180.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/1.18.1159...1.18.1180)

Signed-off-by: dependabot-preview[bot] 
---
 requirements-common.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements-common.txt b/requirements-common.txt
index c4b5383ae..cdaafd818 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -1,6 +1,6 @@
 # requirements without requirements installable via conda
 # mainly used for Raspberry pi installs
-ccxt==1.18.1159
+ccxt==1.18.1180
 SQLAlchemy==1.3.8
 python-telegram-bot==12.1.1
 arrow==0.15.2

From 6ffb8b7a7017b834420a8de2f75a753a54a8f9c1 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Mon, 23 Sep 2019 13:25:31 +0300
Subject: [PATCH 198/227] Fix wordings in comment

---
 freqtrade/optimize/hyperopt.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index 04a05dc8f..09d821962 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -358,8 +358,9 @@ class Hyperopt:
         """
         n = len(self.opt.models) - SKOPT_MODELS_MAX_NUM
         # Keep no more than 2*SKOPT_MODELS_MAX_NUM models in the skopt models list,
-        # remove the old ones. These are no really needed, the current model
-        # from the estimator is only used.
+        # remove the old ones. These are actually of no use, the current model
+        # from the estimator is the only one used in the skopt optimizer.
+        # Freqtrade code also does not inspect details of the models.
         if n >= SKOPT_MODELS_MAX_NUM:
             logger.debug(f"Fixing skopt models list, removing {n} old items...")
             del self.opt.models[0:n]

From 0f97a999fb7f49bb6de05fa4ba6c4a733cc45cc1 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 06:35:41 +0200
Subject: [PATCH 199/227] Improve wording

---
 freqtrade/configuration/check_exchange.py | 4 ++--
 freqtrade/utils.py                        | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py
index 019081def..19c377732 100644
--- a/freqtrade/configuration/check_exchange.py
+++ b/freqtrade/configuration/check_exchange.py
@@ -29,8 +29,8 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
     exchange = config.get('exchange', {}).get('name').lower()
     if not exchange:
         raise OperationalException(
-            f'This command requires a configured exchange. You can use either '
-            f'`--exchange ` or specify a configuration file via `--config`.\n'
             f'The following exchanges are supported by ccxt: '
             f'{", ".join(available_exchanges())}'
         )
diff --git a/freqtrade/utils.py b/freqtrade/utils.py
index 276c7267b..6ce5e888c 100644
--- a/freqtrade/utils.py
+++ b/freqtrade/utils.py
@@ -73,7 +73,7 @@ def start_download_data(args: Dict[str, Any]) -> None:
 
     if 'pairs' not in config:
         raise OperationalException(
-            "Downloading data requires a list of pairs."
+            "Downloading data requires a list of pairs. "
             "Please check the documentation on how to configure this.")
 
     dl_path = Path(config['datadir'])

From 577b1fd965b7b8167b332ee3f30b5062eb556830 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 06:39:00 +0200
Subject: [PATCH 200/227] Improve documentation wording

---
 docs/backtesting.md | 2 +-
 docs/deprecated.md  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 879cf78a3..004425323 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -38,7 +38,7 @@ This will download ticker data for all the currency pairs you defined in `pairs.
 - To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
 
 !!! Tip Updating existing data
-    If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will load the available data and only download the missing data.
+    If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will keep the available data and only download the missing data.
     Be carefull though: If the number is too small (which would result in a few missing days), the whole dataset will be removed and only xx days will be downloaded.
 
 ## Test your strategy with Backtesting
diff --git a/docs/deprecated.md b/docs/deprecated.md
index 24c2bb1e3..349d41a09 100644
--- a/docs/deprecated.md
+++ b/docs/deprecated.md
@@ -12,7 +12,7 @@ and are no longer supported. Please avoid their usage in your configuration.
 Since this leads to much confusion, and slows down backtesting (while not being part of backtesting) this has been singled out 
 as a seperate freqtrade subcommand `freqtrade download-data`.
 
-This command line option was deprecated in `2019.7-dev` and removed in `2019-9`
+This command line option was deprecated in 2019.7-dev (develop branch) and removed in 2019.9 (master branch).
 
 ### The **--dynamic-whitelist** command line option
 

From fe40636ae10717a4e35ba8e104bd5f4f46333da5 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 06:42:44 +0200
Subject: [PATCH 201/227] Improve wordings

---
 docs/configuration.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 08137fe79..0d902766a 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,15 +1,15 @@
 # Configure the bot
 
-Freqtrade has many configurable options and possibilities.
-By default, these options are configured via the configuration file (see below).
+Freqtrade has many configurable features and possibilities.
+By default, these settings are configured via the configuration file (see below).
 
 ## The Freqtrade configuration file
 
 The bot uses a set of configuration parameters during its operation that all together conform the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file).
 
-Per default, the bot loads the configuration from the file `config.json`, located in the current working directory.
+Per default, the bot loads the configuration from the `config.json` file, located in the current working directory.
 
-You can use a different configuration file used by the bot with the `-c/--config` command line option.
+You can specify a different configuration file used by the bot with the `-c/--config` command line option.
 
 In some advanced use cases, multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream.
 
@@ -29,7 +29,7 @@ Do not worry if you are not familiar with JSON format -- simply open the configu
 
 The table below will list all configuration parameters available.
 
-Freqtrade can also load many options via command line arguments (check out the commands `--help` output for details).
+Freqtrade can also load many options via command line (CLI) arguments (check out the commands `--help` output for details).
 The prevelance for all Options is as follows:
 
 - CLI arguments override any other option

From cc9fc413188105f1af6e87df38406bf031481c8a Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 06:56:19 +0200
Subject: [PATCH 202/227] Rename section to data-downloading, implement some
 feedback

---
 docs/backtesting.md | 12 ++++++------
 docs/hyperopt.md    |  2 +-
 mkdocs.yml          |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index b3f22c664..9ddc6bbb3 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -3,7 +3,7 @@
 This page explains how to validate your strategy performance by using Backtesting.
 
 Backtesting requires historic data to be available.
-To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data downloading](data-download.md) section of the documentation.
+To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data Downloading](data-download.md) section of the documentation.
 
 ## Test your strategy with Backtesting
 
@@ -164,13 +164,13 @@ here:
 The bot has made `429` trades for an average duration of `4:12:00`, with a performance of `76.20%` (profit), that means it has
 earned a total of `0.00762792 BTC` starting with a capital of 0.01 BTC.
 
-The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums all the profits/losses.
+The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums up all the profits/losses.
 The column `tot profit %` shows instead the total profit % in relation to allocated capital (`max_open_trades * stake_amount`).
-In the above results we have `max_open_trades=2 stake_amount=0.005` in config  so `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`.
+In the above results we have `max_open_trades=2` and `stake_amount=0.005` in config  so `tot_profit %` will be `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`.
 
 Your strategy performance is influenced by your buy strategy, your sell strategy, and also by the `minimal_roi` and `stop_loss` you have set.
 
-For example, if your minimal_roi is only `"0":  0.01`. You cannot expect the bot to make more profit than 1% (because it will sell every time a trade will reach 1%).
+For example, if your `minimal_roi` is only `"0":  0.01` you cannot expect the bot to make more profit than 1% (because it will sell every time a trade reaches 1%).
 
 ```json
 "minimal_roi": {
@@ -179,8 +179,8 @@ For example, if your minimal_roi is only `"0":  0.01`. You cannot expect the bot
 ```
 
 On the other hand, if you set a too high `minimal_roi` like `"0":  0.55`
-(55%), there is almost no chance that the bot will never reach this profit.
-Hence, keep in mind that your performance is a mix of the different elements of the strategy, your configuration, and the crypto-currency pairs you have set up.
+(55%), there is almost no chance that the bot will ever reach this profit.
+Hence, keep in mind that your performance is an integral mix of all different elements of the strategy, your configuration, and the crypto-currency pairs you have set up.
 
 ### Assumptions made by backtesting
 
diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index 00aec458c..9542d5725 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -7,7 +7,7 @@ search will burn all your CPU cores, make your laptop sound like a fighter jet
 and still take a long time.
 
 Hyperopt requires historic data to be available, just as backtesting does.
-To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data downloading](data-download.md) section of the documentation.
+To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data Downloading](data-download.md) section of the documentation.
 
 !!! Bug
     Hyperopt will crash when used with only 1 CPU Core as found out in [Issue #1133](https://github.com/freqtrade/freqtrade/issues/1133)
diff --git a/mkdocs.yml b/mkdocs.yml
index 069f060db..7aedb4bba 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -11,7 +11,7 @@ nav:
         - Telegram: telegram-usage.md
         - Web Hook: webhook-config.md
         - REST API: rest-api.md
-    - Data Download: data-download.md
+    - Data Downloading: data-download.md
     - Backtesting: backtesting.md
     - Hyperopt: hyperopt.md
     - Edge positioning: edge.md

From 6aa1ec2a4ce054f174c668f00b624257913be1ac Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 07:05:30 +0200
Subject: [PATCH 203/227] Some small restructuring

---
 docs/backtesting.md   |  8 ++++----
 docs/data-download.md | 17 ++++++++++++-----
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 9ddc6bbb3..24df545fd 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -3,7 +3,7 @@
 This page explains how to validate your strategy performance by using Backtesting.
 
 Backtesting requires historic data to be available.
-To learn how to get data for the pairs and exchange you're interrested in, head over to the [Data Downloading](data-download.md) section of the documentation.
+To learn how to get data for the pairs and exchange you're interested in, head over to the [Data Downloading](data-download.md) section of the documentation.
 
 ## Test your strategy with Backtesting
 
@@ -15,7 +15,7 @@ Backtesting will use the crypto-currencies (pairs) from your config file
 and load ticker data from `user_data/data/` by default.
 If no data is available for the exchange / pair / ticker interval combination, backtesting will
 ask you to download them first using `freqtrade download-data`.
-For details on downloading, please refer to the [Downloading data](data-download.md) section in the documentation.
+For details on downloading, please refer to the [Data Downloading](data-download.md) section in the documentation.
 
 The result of backtesting will confirm if your bot has better odds of making a profit than a loss.
 
@@ -84,8 +84,8 @@ freqtrade backtesting --timerange=20190501-
 
 #### Advanced use of timerange
 
-Doing `--timerange=20190101-` will all available data starting with January 1st, 2019 from your inputdata.
-You can also specify specific dates, or a range span indexed by start and stop.
+Using `--timerange=20190101-` will use all available data starting with January 1st, 2019 from your inputdata.
+You can also specify particular dates or a range span indexed by start and stop.
 
 The full timerange specification:
 
diff --git a/docs/data-download.md b/docs/data-download.md
index f6b1e9904..b576e5c01 100644
--- a/docs/data-download.md
+++ b/docs/data-download.md
@@ -4,21 +4,26 @@
 
 To download data (candles / OHLCV) needed for backtesting and hyperoptimization use the `freqtrade download-data` command.
 
-If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes.
-Exchange and pairs will come from `config.json` (if specified using `-c/--config`). Otherwise `--exchange` becomes mandatory.
+If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes for the last 30 days.
+Exchange and pairs will come from `config.json` (if specified using `-c/--config`).
+Otherwise `--exchange` becomes mandatory.
 
-Alternatively, a `pairs.json` file can be used.
+### Pairs file
+
+In alternative to the whitelist from `config.json`, a `pairs.json` file can be used.
 
 If you are using Binance for example:
 
-- create a directory `user_data/data/binance` and copy `pairs.json` in that directory.
-- update the `pairs.json` to contain the currency pairs you are interested in.
+- create a directory `user_data/data/binance` and copy or create the `pairs.json` file in that directory.
+- update the `pairs.json` file to contain the currency pairs you are interested in.
 
 ```bash
 mkdir -p user_data/data/binance
 cp freqtrade/tests/testdata/pairs.json user_data/data/binance
 ```
 
+### start download
+
 Then run:
 
 ```bash
@@ -27,6 +32,8 @@ freqtrade download-data --exchange binance
 
 This will download ticker data for all the currency pairs you defined in `pairs.json`.
 
+### Other Notes
+
 - To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`.
 - To change the exchange used to download the tickers, please use a different configuration file (you'll probably need to adjust ratelimits etc.)
 - To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`.

From 93b262165161089583d2ab02a5bb37bab464d995 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 07:07:06 +0200
Subject: [PATCH 204/227] Add format description for pairs.json file

---
 docs/data-download.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/docs/data-download.md b/docs/data-download.md
index b576e5c01..17565bb98 100644
--- a/docs/data-download.md
+++ b/docs/data-download.md
@@ -22,6 +22,18 @@ mkdir -p user_data/data/binance
 cp freqtrade/tests/testdata/pairs.json user_data/data/binance
 ```
 
+The format of the `pairs.json` file is a simple json list.
+Mixing different stake-currencies is allowed for this file, since it's only used for downloading.
+
+``` json
+[
+    "ETH/BTC",
+    "ETH/USDT",
+    "BTC/USDT",
+    "XRP/ETH"
+]
+```
+
 ### start download
 
 Then run:

From 6c0a1fc42cd279c0d7f4293b9e7fbe5d8919ae5b Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 11:07:12 +0200
Subject: [PATCH 205/227] Fix tests that fail when config.json is present

---
 tests/test_utils.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/tests/test_utils.py b/tests/test_utils.py
index dc0badd01..c99044610 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -117,12 +117,15 @@ def test_download_data_no_exchange(mocker, caplog):
     args = [
         "download-data",
         ]
+    pargs = get_args(args)
+    pargs['config'] = None
     with pytest.raises(OperationalException,
                        match=r"This command requires a configured exchange.*"):
-        start_download_data(get_args(args))
+        start_download_data(pargs)
 
 
 def test_download_data_no_pairs(mocker, caplog):
+
     mocker.patch.object(Path, "exists", MagicMock(return_value=False))
 
     mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data',
@@ -136,6 +139,8 @@ def test_download_data_no_pairs(mocker, caplog):
         "--exchange",
         "binance",
     ]
+    pargs = get_args(args)
+    pargs['config'] = None
     with pytest.raises(OperationalException,
                        match=r"Downloading data requires a list of pairs\..*"):
-        start_download_data(get_args(args))
+        start_download_data(pargs)

From 665e0570ae9d2977e9cd67514213591cab3e50cc Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Wed, 25 Sep 2019 03:41:22 +0300
Subject: [PATCH 206/227] Fix hyperopt position stacking

---
 freqtrade/optimize/hyperopt.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py
index 61623d3df..f2984c111 100644
--- a/freqtrade/optimize/hyperopt.py
+++ b/freqtrade/optimize/hyperopt.py
@@ -90,7 +90,7 @@ class Hyperopt:
         else:
             logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
             self.max_open_trades = 0
-        self.position_stacking = self.config.get('position_stacking', False),
+        self.position_stacking = self.config.get('position_stacking', False)
 
         if self.has_space('sell'):
             # Make sure experimental is enabled

From cc91ccad3ee39ec0a501878132219492df332a25 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 25 Sep 2019 06:26:28 +0200
Subject: [PATCH 207/227] Improve documentation wording

---
 docs/backtesting.md   | 2 +-
 docs/data-download.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 24df545fd..041a47ba2 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -84,7 +84,7 @@ freqtrade backtesting --timerange=20190501-
 
 #### Advanced use of timerange
 
-Using `--timerange=20190101-` will use all available data starting with January 1st, 2019 from your inputdata.
+For Example, running backtesting with the `--timerange=20190101-` option will use all available data starting with January 1st, 2019 from your inputdata.
 You can also specify particular dates or a range span indexed by start and stop.
 
 The full timerange specification:
diff --git a/docs/data-download.md b/docs/data-download.md
index 17565bb98..c370b3919 100644
--- a/docs/data-download.md
+++ b/docs/data-download.md
@@ -1,4 +1,4 @@
-# Data download
+# Data Downloading
 
 ## Getting data for backtesting and hyperopt
 

From 27cc73f47ed2936f3f3e2a98eee3095af302b3f2 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 14:39:28 +0200
Subject: [PATCH 208/227] Dynamically import hyperopt modules

---
 freqtrade/optimize/__init__.py  | 12 +++++++-----
 tests/optimize/test_hyperopt.py | 31 +++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py
index 7a3c290bf..3adf5eb43 100644
--- a/freqtrade/optimize/__init__.py
+++ b/freqtrade/optimize/__init__.py
@@ -1,9 +1,7 @@
 import logging
 from typing import Any, Dict
 
-from filelock import FileLock, Timeout
-
-from freqtrade import DependencyException, constants
+from freqtrade import DependencyException, constants, OperationalException
 from freqtrade.state import RunMode
 from freqtrade.utils import setup_utils_configuration
 
@@ -53,8 +51,12 @@ def start_hyperopt(args: Dict[str, Any]) -> None:
     :return: None
     """
     # Import here to avoid loading hyperopt module when it's not used
-    from freqtrade.optimize.hyperopt import Hyperopt
-
+    try:
+        from filelock import FileLock, Timeout
+        from freqtrade.optimize.hyperopt import Hyperopt
+    except ImportError as e:
+        raise OperationalException(
+            f"{e}. Please ensure that the hyperopt dependencies are installed.") from e
     # Initialize configuration
     config = setup_configuration(args, RunMode.HYPEROPT)
 
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 0888c79d4..8ceea6caa 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -190,6 +190,37 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None:
         HyperOptLossResolver(default_conf, ).hyperopt
 
 
+def test_start_not_installed(mocker, default_conf, caplog) -> None:
+    start_mock = MagicMock()
+    patched_configuration_load_config_file(mocker, default_conf)
+    # Source of this test-method: https://stackoverflow.com/questions/2481511/mocking-importerror-in-python
+    import builtins
+    realimport = builtins.__import__
+
+    def mockedimport(name, *args, **kwargs):
+        if name == "filelock":
+            raise ImportError("No module named 'filelock'")
+        return realimport(name, *args, **kwargs)
+
+    builtins.__import__ = mockedimport
+
+    mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
+    patch_exchange(mocker)
+
+    args = [
+        '--config', 'config.json',
+        'hyperopt',
+        '--epochs', '5'
+    ]
+    args = get_args(args)
+
+    with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"):
+        start_hyperopt(args)
+
+    # restore previous importfunction
+    builtins.__import__ = realimport
+
+
 def test_start(mocker, default_conf, caplog) -> None:
     start_mock = MagicMock()
     patched_configuration_load_config_file(mocker, default_conf)

From 47b6b56566bd90752b44df044eaa66c5ca9c601f Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 14:53:36 +0200
Subject: [PATCH 209/227] Reorg dependencies to have hyperopt seperated

---
 environment.yml           | 11 ++++++-----
 requirements-common.txt   |  8 +-------
 requirements-dev.txt      |  1 +
 requirements-hyperopt.txt |  9 +++++++++
 requirements.txt          |  1 -
 5 files changed, 17 insertions(+), 13 deletions(-)
 create mode 100644 requirements-hyperopt.txt

diff --git a/environment.yml b/environment.yml
index cd3350fd5..4e8c1efcc 100644
--- a/environment.yml
+++ b/environment.yml
@@ -9,25 +9,26 @@ dependencies:
   - wheel
   - numpy
   - pandas
-  - scipy
   - SQLAlchemy
-  - scikit-learn
   - arrow
   - requests
   - urllib3
   - wrapt
-  - joblib
   - jsonschema
   - tabulate
   - python-rapidjson
-  - filelock
   - flask
   - python-dotenv
   - cachetools
-  - scikit-optimize
   - python-telegram-bot
   # Optional for plotting
   - plotly
+  # Optional for hyperopt
+  - scipy
+  - scikit-optimize
+  - scikit-learn
+  - filelock
+  - joblib
   # Optional for development
   - flake8
   - pytest
diff --git a/requirements-common.txt b/requirements-common.txt
index 70a37e695..f10134203 100644
--- a/requirements-common.txt
+++ b/requirements-common.txt
@@ -8,21 +8,15 @@ cachetools==3.1.1
 requests==2.22.0
 urllib3==1.25.5
 wrapt==1.11.2
-scikit-learn==0.21.3
-joblib==0.13.2
 jsonschema==3.0.2
 TA-Lib==0.4.17
 tabulate==0.8.3
 coinmarketcap==5.0.3
 
-# Required for hyperopt
-scikit-optimize==0.5.2
-filelock==3.0.12
-
 # find first, C search in arrays
 py_find_1st==1.1.4
 
-#Load ticker files 30% faster
+# Load ticker files 30% faster
 python-rapidjson==0.8.0
 
 # Notify systemd
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 2678130f3..dcf2c7217 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,7 @@
 # Include all requirements to run the bot.
 -r requirements.txt
 -r requirements-plot.txt
+-r requirements-hyperopt.txt
 
 coveralls==1.8.2
 flake8==3.7.8
diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt
new file mode 100644
index 000000000..bb0ad60f0
--- /dev/null
+++ b/requirements-hyperopt.txt
@@ -0,0 +1,9 @@
+# Include all requirements to run the bot.
+# -r requirements.txt
+
+# Required for hyperopt
+scipy==1.3.1
+scikit-learn==0.21.3
+scikit-optimize==0.5.2
+filelock==3.0.12
+joblib==0.13.2
diff --git a/requirements.txt b/requirements.txt
index 9a723fee4..2767180ac 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,4 +3,3 @@
 
 numpy==1.17.2
 pandas==0.25.1
-scipy==1.3.1

From d2f247307092e5df9c67107f491655e991163d04 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Tue, 24 Sep 2019 15:03:40 +0200
Subject: [PATCH 210/227] install hyperopt seperately ([hyperopt])

---
 setup.py | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/setup.py b/setup.py
index ca94dd338..2eb09e589 100644
--- a/setup.py
+++ b/setup.py
@@ -18,6 +18,13 @@ if readme_file.is_file():
 # Requirements used for submodules
 api = ['flask']
 plot = ['plotly>=4.0']
+hyperopt = [
+    'scipy',
+    'scikit-learn',
+    'scikit-optimize',
+    'filelock',
+    'joblib',
+    ]
 
 develop = [
     'coveralls',
@@ -38,7 +45,7 @@ jupyter = [
     'ipykernel',
     ]
 
-all_extra = api + plot + develop + jupyter
+all_extra = api + plot + develop + jupyter + hyperopt
 
 setup(name='freqtrade',
       version=__version__,
@@ -62,14 +69,10 @@ setup(name='freqtrade',
           'requests',
           'urllib3',
           'wrapt',
-          'scikit-learn',
-          'joblib',
           'jsonschema',
           'TA-Lib',
           'tabulate',
           'coinmarketcap',
-          'scikit-optimize',
-          'filelock',
           'py_find_1st',
           'python-rapidjson',
           'sdnotify',
@@ -77,15 +80,14 @@ setup(name='freqtrade',
           # from requirements.txt
           'numpy',
           'pandas',
-          'scipy',
       ],
       extras_require={
           'api': api,
           'dev': all_extra,
           'plot': plot,
-          'all': all_extra,
           'jupyter': jupyter,
-
+          'hyperopt': hyperopt,
+          'all': all_extra,
       },
       include_package_data=True,
       zip_safe=False,

From d05db077e29666e4539bd929d67edb252be3b2dc Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 25 Sep 2019 08:56:06 +0200
Subject: [PATCH 211/227] Update PI install documentation  and dockerfile

---
 Dockerfile.pi        |  4 ++--
 docs/installation.md | 11 +++++++----
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/Dockerfile.pi b/Dockerfile.pi
index 1b9c4c579..85ba5892f 100644
--- a/Dockerfile.pi
+++ b/Dockerfile.pi
@@ -22,13 +22,13 @@ RUN tar -xzf /freqtrade/ta-lib-0.4.0-src.tar.gz \
 ENV LD_LIBRARY_PATH /usr/local/lib
 
 # Install berryconda
-RUN wget https://github.com/jjhelmus/berryconda/releases/download/v2.0.0/Berryconda3-2.0.0-Linux-armv7l.sh \
+RUN wget -q https://github.com/jjhelmus/berryconda/releases/download/v2.0.0/Berryconda3-2.0.0-Linux-armv7l.sh \
  && bash ./Berryconda3-2.0.0-Linux-armv7l.sh -b \
  && rm Berryconda3-2.0.0-Linux-armv7l.sh
 
 # Install dependencies
 COPY requirements-common.txt /freqtrade/
-RUN ~/berryconda3/bin/conda install -y numpy pandas scipy \
+RUN ~/berryconda3/bin/conda install -y numpy pandas \
  && ~/berryconda3/bin/pip install -r requirements-common.txt --no-cache-dir
 
 # Install and execute
diff --git a/docs/installation.md b/docs/installation.md
index f15cc356c..641879ff1 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -99,8 +99,8 @@ sudo apt-get install build-essential git
 
 Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/).
 
-The following assumes that miniconda3 is installed and available in your environment. Last miniconda3 installation file use python 3.4, we will update to python 3.6 on this installation.
-It's recommended to use (mini)conda for this as installation/compilation of `numpy`, `scipy` and `pandas` takes a long time.
+The following assumes that miniconda3 is installed and available in your environment. Since the last miniconda3 installation file uses python 3.4, we will update to python 3.6 on this installation.
+It's recommended to use (mini)conda for this as installation/compilation of `numpy` and `pandas` takes a long time.
 
 Additional package to install on your Raspbian, `libffi-dev` required by cryptography (from python-telegram-bot).
 
@@ -109,13 +109,17 @@ conda config --add channels rpi
 conda install python=3.6
 conda create -n freqtrade python=3.6
 conda activate freqtrade
-conda install scipy pandas numpy
+conda install pandas numpy
 
 sudo apt install libffi-dev
 python3 -m pip install -r requirements-common.txt
 python3 -m pip install -e .
 ```
 
+!!! Note
+    This does not install hyperopt dependencies. To install these, please use `python3 -m pip install -e .[hyperopt]`.
+    We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerfull machine.
+
 ### Common
 
 #### 1. Install TA-Lib
@@ -175,7 +179,6 @@ cp config.json.example config.json
 
 ``` bash
 python3 -m pip install --upgrade pip
-python3 -m pip install -r requirements.txt
 python3 -m pip install -e .
 ```
 

From e9de088209736e1f1f255c4339575ff7b34c38f2 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Wed, 25 Sep 2019 11:54:57 +0200
Subject: [PATCH 212/227] Add import-fails code as a fixture

---
 tests/conftest.py               | 21 +++++++++++++++++++++
 tests/optimize/test_hyperopt.py | 15 +--------------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/tests/conftest.py b/tests/conftest.py
index 8a5ba6683..6a0a74b5b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1054,3 +1054,24 @@ def rpc_balance():
 def testdatadir() -> Path:
     """Return the path where testdata files are stored"""
     return (Path(__file__).parent / "testdata").resolve()
+
+
+@pytest.fixture(scope="function")
+def import_fails() -> None:
+    # Source of this test-method:
+    # https://stackoverflow.com/questions/2481511/mocking-importerror-in-python
+    import builtins
+    realimport = builtins.__import__
+
+    def mockedimport(name, *args, **kwargs):
+        if name in ["filelock"]:
+            raise ImportError(f"No module named '{name}'")
+        return realimport(name, *args, **kwargs)
+
+    builtins.__import__ = mockedimport
+
+    # Run test - then cleanup
+    yield
+
+    # restore previous importfunction
+    builtins.__import__ = realimport
diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 8ceea6caa..55f94f572 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -190,19 +190,9 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None:
         HyperOptLossResolver(default_conf, ).hyperopt
 
 
-def test_start_not_installed(mocker, default_conf, caplog) -> None:
+def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None:
     start_mock = MagicMock()
     patched_configuration_load_config_file(mocker, default_conf)
-    # Source of this test-method: https://stackoverflow.com/questions/2481511/mocking-importerror-in-python
-    import builtins
-    realimport = builtins.__import__
-
-    def mockedimport(name, *args, **kwargs):
-        if name == "filelock":
-            raise ImportError("No module named 'filelock'")
-        return realimport(name, *args, **kwargs)
-
-    builtins.__import__ = mockedimport
 
     mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
     patch_exchange(mocker)
@@ -217,9 +207,6 @@ def test_start_not_installed(mocker, default_conf, caplog) -> None:
     with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"):
         start_hyperopt(args)
 
-    # restore previous importfunction
-    builtins.__import__ = realimport
-
 
 def test_start(mocker, default_conf, caplog) -> None:
     start_mock = MagicMock()

From 0268bfdbd4ac5aba98fde16a89256e11fdb558b7 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Thu, 26 Sep 2019 02:04:48 +0300
Subject: [PATCH 213/227] Minor: fix typo in comment

Minor cosmetics. typo caught.
---
 freqtrade/data/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/data/__init__.py b/freqtrade/data/__init__.py
index 0a31d095c..0e7eea0d0 100644
--- a/freqtrade/data/__init__.py
+++ b/freqtrade/data/__init__.py
@@ -2,7 +2,7 @@
 Module to handle data operations for freqtrade
 """
 
-# limit what's imported when using `from freqtrad.data import *``
+# limit what's imported when using `from freqtrade.data import *`
 __all__ = [
     'converter'
 ]

From 5978b7bb93ca46c8d8e62f0168c7f43e7efc690d Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 06:26:25 +0200
Subject: [PATCH 214/227] Add explicit test for halfbought fee adjustment

---
 tests/test_freqtradebot.py | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index c26186e9d..45a2960e1 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -3205,6 +3205,30 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
     assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
 
 
+def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_fee, mocker):
+    limit_buy_order = deepcopy(buy_order_fee)
+    limit_buy_order['fee'] = {'cost': 0.004}
+    limit_buy_order['amount'] = limit_buy_order['amount'] + 0.1
+
+    patch_RPCManager(mocker)
+    patch_exchange(mocker)
+    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
+    amount = float(sum(x['amount'] for x in trades_for_order))
+    trade = Trade(
+        pair='LTC/ETH',
+        amount=amount,
+        exchange='binance',
+        open_rate=0.245441,
+        open_order_id="123456"
+    )
+    freqtrade = FreqtradeBot(default_conf)
+    patch_get_signal(freqtrade)
+
+    # Amount does not change
+    with pytest.raises(OperationalException, match=r"Half bought\? Amounts don't match"):
+        freqtrade.get_real_amount(trade, limit_buy_order)
+
+
 def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):
     # Remove "Currency" from fee dict
     trades_for_order[0]['fee'] = {'cost': 0.008}

From 49f0a721213cbdf1a6bca8c27b61e17432dbc4a8 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 06:48:46 +0200
Subject: [PATCH 215/227] Add test for rounding error on fload aggregation

---
 freqtrade/constants.py     |  1 +
 tests/test_freqtradebot.py | 55 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index d7ace131c..abf43b24d 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -22,6 +22,7 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market']
 ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
 AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList']
 DRY_RUN_WALLET = 999.9
+MATH_CLOSE_PREC = 1e-14  # Precision used for float comparisons
 
 TICKER_INTERVALS = [
     '1m', '3m', '5m', '15m', '30m',
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 45a2960e1..dd535a0b0 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -4,6 +4,7 @@
 import logging
 import time
 from copy import deepcopy
+from math import isclose
 from unittest.mock import MagicMock, PropertyMock
 
 import arrow
@@ -12,6 +13,7 @@ import requests
 
 from freqtrade import (DependencyException, InvalidOrderException,
                        OperationalException, TemporaryError, constants)
+from freqtrade.constants import MATH_CLOSE_PREC
 from freqtrade.data.dataprovider import DataProvider
 from freqtrade.freqtradebot import FreqtradeBot
 from freqtrade.persistence import Trade
@@ -1635,6 +1637,31 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_
     assert trade.amount == limit_buy_order['amount']
 
 
+def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_order,
+                                                       limit_buy_order, mocker, caplog):
+    trades_for_order[0]['amount'] = limit_buy_order['amount'] + 1e-14
+    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
+    # get_order should not be called!!
+    mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
+    patch_exchange(mocker)
+    Trade.session = MagicMock()
+    amount = sum(x['amount'] for x in trades_for_order)
+    freqtrade = get_patched_freqtradebot(mocker, default_conf)
+    trade = Trade(
+        pair='LTC/ETH',
+        amount=amount,
+        exchange='binance',
+        open_rate=0.245441,
+        open_order_id="123456",
+        is_open=True,
+        open_date=arrow.utcnow().datetime,
+    )
+    freqtrade.update_trade_state(trade, limit_buy_order)
+    assert trade.amount != amount
+    assert trade.amount == limit_buy_order['amount']
+    assert log_has_re(r'Applying fee on amount for .*', caplog)
+
+
 def test_update_trade_state_exception(mocker, default_conf,
                                       limit_buy_order, caplog) -> None:
     freqtrade = get_patched_freqtradebot(mocker, default_conf)
@@ -3207,8 +3234,7 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
 
 def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_fee, mocker):
     limit_buy_order = deepcopy(buy_order_fee)
-    limit_buy_order['fee'] = {'cost': 0.004}
-    limit_buy_order['amount'] = limit_buy_order['amount'] + 0.1
+    limit_buy_order['amount'] = limit_buy_order['amount'] - 0.001
 
     patch_RPCManager(mocker)
     patch_exchange(mocker)
@@ -3229,6 +3255,31 @@ def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_
         freqtrade.get_real_amount(trade, limit_buy_order)
 
 
+def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, buy_order_fee,
+                                               mocker):
+    # Floats should not be compared directly.
+    limit_buy_order = deepcopy(buy_order_fee)
+    trades_for_order[0]['amount'] = trades_for_order[0]['amount'] + 1e-15
+
+    patch_RPCManager(mocker)
+    patch_exchange(mocker)
+    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
+    amount = float(sum(x['amount'] for x in trades_for_order))
+    trade = Trade(
+        pair='LTC/ETH',
+        amount=amount,
+        exchange='binance',
+        open_rate=0.245441,
+        open_order_id="123456"
+    )
+    freqtrade = FreqtradeBot(default_conf)
+    patch_get_signal(freqtrade)
+
+    # Amount changes by fee amount.
+    assert isclose(freqtrade.get_real_amount(trade, limit_buy_order), amount - (amount * 0.001),
+                   rel_tol=MATH_CLOSE_PREC,)
+
+
 def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):
     # Remove "Currency" from fee dict
     trades_for_order[0]['fee'] = {'cost': 0.008}

From 8d92f8b3624555bf50f8463ebe961653a1db2435 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 07:04:56 +0200
Subject: [PATCH 216/227] Compare floats via isclose instead of ==

---
 freqtrade/freqtradebot.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index f56b1f2ea..c6d3d0f44 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -6,6 +6,7 @@ import copy
 import logging
 import traceback
 from datetime import datetime
+from math import isclose
 from typing import Any, Dict, List, Optional, Tuple
 
 import arrow
@@ -510,7 +511,7 @@ class FreqtradeBot:
                         trade.pair.startswith(exectrade['fee']['currency'])):
                     fee_abs += exectrade['fee']['cost']
 
-        if amount != order_amount:
+        if not isclose(amount, order_amount, rel_tol=constants.MATH_CLOSE_PREC):
             logger.warning(f"Amount {amount} does not match amount {trade.amount}")
             raise OperationalException("Half bought? Amounts don't match")
         real_amount = amount - fee_abs
@@ -535,7 +536,7 @@ class FreqtradeBot:
             # Try update amount (binance-fix)
             try:
                 new_amount = self.get_real_amount(trade, order)
-                if order['amount'] != new_amount:
+                if not isclose(order['amount'], new_amount, rel_tol=constants.MATH_CLOSE_PREC):
                     order['amount'] = new_amount
                     # Fee was applied, so set to 0
                     trade.fee_open = 0

From eb07f1fee9b5763a0e635c586b55e5d4d5243325 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 09:31:31 +0200
Subject: [PATCH 217/227] Fix typo

---
 docs/installation.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/installation.md b/docs/installation.md
index 641879ff1..081d7e0cf 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -118,7 +118,7 @@ python3 -m pip install -e .
 
 !!! Note
     This does not install hyperopt dependencies. To install these, please use `python3 -m pip install -e .[hyperopt]`.
-    We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerfull machine.
+    We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerful machine.
 
 ### Common
 

From 9db915853ab4b9d454f618eda68ccb3a4ad02e2d Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Thu, 26 Sep 2019 11:59:21 +0300
Subject: [PATCH 218/227] Allow use of config in custom hyperopt methods

---
 freqtrade/optimize/hyperopt_interface.py | 6 ++++++
 freqtrade/resolvers/hyperopt_resolver.py | 5 +----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py
index 0e2e75d2e..9973739e8 100644
--- a/freqtrade/optimize/hyperopt_interface.py
+++ b/freqtrade/optimize/hyperopt_interface.py
@@ -36,6 +36,12 @@ class IHyperOpt(ABC):
     """
     ticker_interval: str
 
+    def __init__(self, config: dict) -> None:
+        self.config = config
+
+        # Assign ticker_interval to be used in hyperopt
+        IHyperOpt.ticker_interval = str(config['ticker_interval'])
+
     @staticmethod
     @abstractmethod
     def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py
index 3ffdc5e1f..e96394d69 100644
--- a/freqtrade/resolvers/hyperopt_resolver.py
+++ b/freqtrade/resolvers/hyperopt_resolver.py
@@ -34,9 +34,6 @@ class HyperOptResolver(IResolver):
         self.hyperopt = self._load_hyperopt(hyperopt_name, config,
                                             extra_dir=config.get('hyperopt_path'))
 
-        # Assign ticker_interval to be used in hyperopt
-        IHyperOpt.ticker_interval = str(config['ticker_interval'])
-
         if not hasattr(self.hyperopt, 'populate_buy_trend'):
             logger.warning("Hyperopt class does not provide populate_buy_trend() method. "
                            "Using populate_buy_trend from the strategy.")
@@ -65,7 +62,7 @@ class HyperOptResolver(IResolver):
             abs_paths.insert(0, Path(extra_dir).resolve())
 
         hyperopt = self._load_object(paths=abs_paths, object_type=IHyperOpt,
-                                     object_name=hyperopt_name)
+                                     object_name=hyperopt_name, kwargs={'config': config})
         if hyperopt:
             return hyperopt
         raise OperationalException(

From 60e3e626e4744172469a576dceec497086f7820a Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 11:00:26 +0200
Subject: [PATCH 219/227] Improve timerange section of the docs

---
 docs/backtesting.md | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/docs/backtesting.md b/docs/backtesting.md
index 041a47ba2..75aba6c73 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -72,24 +72,21 @@ The exported trades can be used for [further analysis](#further-backtest-result-
 freqtrade backtesting --export trades --export-filename=backtest_samplestrategy.json
 ```
 
-#### Running backtest with smaller testset
+#### Running backtest with smaller testset by using timerange
 
 Use the `--timerange` argument to change how much of the testset you want to use.
 
-Example:
+
+For example, running backtesting with the `--timerange=20190501-` option will use all available data starting with May 1st, 2019 from your inputdata.
 
 ```bash
 freqtrade backtesting --timerange=20190501-
 ```
 
-#### Advanced use of timerange
-
-For Example, running backtesting with the `--timerange=20190101-` option will use all available data starting with January 1st, 2019 from your inputdata.
 You can also specify particular dates or a range span indexed by start and stop.
 
 The full timerange specification:
 
-
 - Use tickframes till 2018/01/31: `--timerange=-20180131`
 - Use tickframes since 2018/01/31: `--timerange=20180131-`
 - Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301`

From 637ec60644c2a31722c03ba3722c4153d66b899b Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Thu, 26 Sep 2019 19:36:09 +0200
Subject: [PATCH 220/227] Update slack link

---
 CONTRIBUTING.md                | 2 +-
 README.md                      | 4 ++--
 docs/developer.md              | 2 +-
 docs/index.md                  | 2 +-
 docs/strategy-customization.md | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aac25a423..72c04e151 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,7 +11,7 @@ Few pointers for contributions:
 - Create your PR against the `develop` branch, not `master`.
 - New features need to contain unit tests and must be PEP8 conformant (max-line-length = 100).
 
-If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg)
+If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE)
 or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR.
 
 ## Getting started
diff --git a/README.md b/README.md
index 240b4f917..6d57dcd89 100644
--- a/README.md
+++ b/README.md
@@ -141,7 +141,7 @@ Accounts having BNB accounts use this to pay for fees - if your first trade happ
 For any questions not covered by the documentation or for further
 information about the bot, we encourage you to join our slack channel.
 
-- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg).
+- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE).
 
 ### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
 
@@ -172,7 +172,7 @@ to understand the requirements before sending your pull-requests.
 Coding is not a neccessity to contribute - maybe start with improving our documentation?
 Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase.
 
-**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.
+**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.
 
 **Important:** Always create your PR against the `develop` branch, not `master`.
 
diff --git a/docs/developer.md b/docs/developer.md
index b048cf93f..627627b07 100644
--- a/docs/developer.md
+++ b/docs/developer.md
@@ -2,7 +2,7 @@
 
 This page is intended for developers of FreqTrade, people who want to contribute to the FreqTrade codebase or documentation, or people who want to understand the source code of the application they're running.
 
-All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg) where you can ask questions.
+All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) where you can ask questions.
 
 ## Documentation
 
diff --git a/docs/index.md b/docs/index.md
index 63d6be75e..206d635c6 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -64,7 +64,7 @@ To run this bot we recommend you a cloud instance with a minimum of:
 Help / Slack
 For any questions not covered by the documentation or for further information about the bot, we encourage you to join our Slack channel.
 
-Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg) to join Slack channel.
+Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) to join Slack channel.
 
 ## Ready to try?
 
diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md
index 85cab987a..b927e5aad 100644
--- a/docs/strategy-customization.md
+++ b/docs/strategy-customization.md
@@ -411,7 +411,7 @@ To get additional Ideas for strategies, head over to our [strategy repository](h
 Feel free to use any of them as inspiration for your own strategies.
 We're happy to accept Pull Requests containing new Strategies to that repo.
 
-We also got a *strategy-sharing* channel in our [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LWEyODBiNzkzNzcyNzU0MWYyYzE5NjIyOTQxMzBmMGUxOTIzM2YyN2Y4NWY1YTEwZDgwYTRmMzE2NmM5ZmY2MTg) which is a great place to get and/or share ideas.
+We also got a *strategy-sharing* channel in our [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) which is a great place to get and/or share ideas.
 
 ## Next step
 

From 4ac53f154908557959cbdea2eeed1c43be063db8 Mon Sep 17 00:00:00 2001
From: hroff-1902 
Date: Sat, 28 Sep 2019 04:13:53 +0300
Subject: [PATCH 221/227] Shorten the default hyperopt stoploss space

---
 freqtrade/optimize/hyperopt_interface.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py
index 0e2e75d2e..7b0e956b9 100644
--- a/freqtrade/optimize/hyperopt_interface.py
+++ b/freqtrade/optimize/hyperopt_interface.py
@@ -175,7 +175,7 @@ class IHyperOpt(ABC):
         You may override it in your custom Hyperopt class.
         """
         return [
-            Real(-0.5, -0.02, name='stoploss'),
+            Real(-0.35, -0.02, name='stoploss'),
         ]
 
     # This is needed for proper unpickling the class attribute ticker_interval

From 42b5a0977e2b7b03917bbcbb12c1961c7c4c77bc Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 28 Sep 2019 10:14:38 +0200
Subject: [PATCH 222/227] fix failing test

---
 tests/optimize/test_hyperopt.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py
index 0888c79d4..1a22d116d 100644
--- a/tests/optimize/test_hyperopt.py
+++ b/tests/optimize/test_hyperopt.py
@@ -153,7 +153,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
     delattr(hyperopts, 'populate_sell_trend')
     mocker.patch(
         'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt',
-        MagicMock(return_value=hyperopts)
+        MagicMock(return_value=hyperopts(default_conf))
     )
     x = HyperOptResolver(default_conf, ).hyperopt
     assert not hasattr(x, 'populate_buy_trend')

From 43f2ef226c969894d6a9a3487a52eeef95f7f5a7 Mon Sep 17 00:00:00 2001
From: Matthias 
Date: Sat, 28 Sep 2019 10:30:12 +0200
Subject: [PATCH 223/227] Change rel_tol to abs_tol to avoid surprises with
 high priced pairs

---
 freqtrade/freqtradebot.py  | 4 ++--
 tests/test_freqtradebot.py | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index c6d3d0f44..3f7eab27a 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -511,7 +511,7 @@ class FreqtradeBot:
                         trade.pair.startswith(exectrade['fee']['currency'])):
                     fee_abs += exectrade['fee']['cost']
 
-        if not isclose(amount, order_amount, rel_tol=constants.MATH_CLOSE_PREC):
+        if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC):
             logger.warning(f"Amount {amount} does not match amount {trade.amount}")
             raise OperationalException("Half bought? Amounts don't match")
         real_amount = amount - fee_abs
@@ -536,7 +536,7 @@ class FreqtradeBot:
             # Try update amount (binance-fix)
             try:
                 new_amount = self.get_real_amount(trade, order)
-                if not isclose(order['amount'], new_amount, rel_tol=constants.MATH_CLOSE_PREC):
+                if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
                     order['amount'] = new_amount
                     # Fee was applied, so set to 0
                     trade.fee_open = 0
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index dd535a0b0..ee28f2e58 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -3277,7 +3277,7 @@ def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, b
 
     # Amount changes by fee amount.
     assert isclose(freqtrade.get_real_amount(trade, limit_buy_order), amount - (amount * 0.001),
-                   rel_tol=MATH_CLOSE_PREC,)
+                   abs_tol=MATH_CLOSE_PREC,)
 
 
 def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, mocker):

From 7e214d8e4c14fb640a1b4c417c78b07381aaa4b7 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Sat, 28 Sep 2019 11:50:15 +0300
Subject: [PATCH 224/227] minor: change default stoploss space

---
 docs/hyperopt.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index 9542d5725..b31e515ab 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -425,7 +425,7 @@ Buy hyperspace params:
 Stoploss: -0.37996664668703606
 ```
 
-If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.5...-0.02, which is sufficient in most cases.
+If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.35...-0.02, which is sufficient in most cases.
 
 If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default.
 

From 45f5394d799190edf023bbe493bc3964cd0aebb2 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Sat, 28 Sep 2019 11:54:26 +0300
Subject: [PATCH 225/227] Align example in the docs

---
 docs/hyperopt.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index b31e515ab..45528edab 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -422,7 +422,7 @@ Buy hyperspace params:
     'adx-enabled': False,
     'rsi-enabled': True,
     'trigger': 'bb_lower'}
-Stoploss: -0.37996664668703606
+Stoploss: -0.27996
 ```
 
 If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.35...-0.02, which is sufficient in most cases.

From 2f005d6be96dcc8d98abcca813a9c10eb5a99eb9 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Sat, 28 Sep 2019 11:56:19 +0300
Subject: [PATCH 226/227] Align example of ROI in the docs

---
 docs/hyperopt.md | 17 +++--------------
 1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index 45528edab..beef414bf 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -375,23 +375,12 @@ Buy hyperspace params:
     'rsi-enabled': True,
     'trigger': 'bb_lower'}
 ROI table:
-{   0: 0.10674752302642071,
-    21: 0.09158372701087236,
-    78: 0.03634636907306948,
+{   0: 0.10674,
+    21: 0.09158,
+    78: 0.03634,
     118: 0}
 ```
 
-This would translate to the following ROI table:
-
-``` python
-minimal_roi = {
-        "118": 0,
-        "78": 0.0363,
-        "21": 0.0915,
-        "0": 0.106
-    }
-```
-
 If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges (for some of the most used ticker intervals, values are rounded to 5 digits after the decimal point):
 
 | # step | 1m |  | 5m |  | 1h |  | 1d |  |

From 6a397f579e449e40c18584d45ee318b1603ac7b5 Mon Sep 17 00:00:00 2001
From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com>
Date: Sun, 29 Sep 2019 00:43:27 +0300
Subject: [PATCH 227/227] Add description of usage

---
 docs/hyperopt.md | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index beef414bf..1ca371e3d 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -381,6 +381,22 @@ ROI table:
     118: 0}
 ```
 
+In order to use this best ROI table found by Hyperopt in backtesting and for live trades/dry-run, copy-paste it as the value of the `minimal_roi` attribute of your custom strategy:
+
+```
+    # Minimal ROI designed for the strategy.
+    # This attribute will be overridden if the config file contains "minimal_roi"
+    minimal_roi = {
+        0: 0.10674,
+        21: 0.09158,
+        78: 0.03634,
+        118: 0
+    }
+```
+As stated in the comment, you can also use it as the value of the `minimal_roi` setting in the configuration file.
+
+#### Default ROI Search Space
+
 If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace for you -- it's the hyperspace of components for the ROI tables. By default, each ROI table generated by the Freqtrade consists of 4 rows (steps). Hyperopt implements adaptive ranges for ROI tables with ranges for values in the ROI steps that depend on the ticker_interval used. By default the values can vary in the following ranges (for some of the most used ticker intervals, values are rounded to 5 digits after the decimal point):
 
 | # step | 1m |  | 5m |  | 1h |  | 1d |  |
@@ -414,6 +430,17 @@ Buy hyperspace params:
 Stoploss: -0.27996
 ```
 
+In order to use this best stoploss value found by Hyperopt in backtesting and for live trades/dry-run, copy-paste it as the value of the `stoploss` attribute of your custom strategy:
+
+```
+    # Optimal stoploss designed for the strategy
+    # This attribute will be overridden if the config file contains "stoploss"
+    stoploss = -0.27996
+```
+As stated in the comment, you can also use it as the value of the `stoploss` setting in the configuration file.
+
+#### Default Stoploss Search Space
+
 If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimization hyperspace for you. By default, the stoploss values in that hyperspace can vary in the range -0.35...-0.02, which is sufficient in most cases.
 
 If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default.