def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog,
                                      default_conf) -> None:
    """
    Test load_data() with 1 min ticker
    """
    mocker.patch('freqtrade.exchange.Exchange.get_history',
                 return_value=ticker_history)
    exchange = get_patched_exchange(mocker, default_conf)
    file = os.path.join(os.path.dirname(__file__), '..', 'testdata',
                        'MEME_BTC-1m.json')

    _backup_file(file)
    # do not download a new pair if refresh_pairs isn't set
    optimize.load_data(None,
                       ticker_interval='1m',
                       refresh_pairs=False,
                       pairs=['MEME/BTC'])
    assert os.path.isfile(file) is False
    assert log_has(
        'No data for pair: "MEME/BTC", Interval: 1m. '
        'Use --refresh-pairs-cached to download the data',
        caplog.record_tuples)

    # download a new pair if refresh_pairs is set
    optimize.load_data(None,
                       ticker_interval='1m',
                       refresh_pairs=True,
                       exchange=exchange,
                       pairs=['MEME/BTC'])
    assert os.path.isfile(file) is True
    assert log_has('Download the pair: "MEME/BTC", Interval: 1m',
                   caplog.record_tuples)
    _clean_test_file(file)
def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
    mocker.patch('freqtrade.exchange.Exchange.get_history',
                 return_value=ticker_history)
    file = os.path.join(os.path.dirname(__file__), '..', 'testdata',
                        'UNITTEST_BTC-1m.json')
    _backup_file(file, copy_file=True)
    optimize.load_data(None, ticker_interval='1m', pairs=['UNITTEST/BTC'])
    assert os.path.isfile(file) is True
    assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 1m',
                       caplog.record_tuples)
    _clean_test_file(file)
Beispiel #3
0
def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
    """
    Test load_data() with 1 min ticker
    """
    mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)

    file = 'freqtrade/tests/testdata/BTC_MEME-1.json'
    _backup_file(file)
    optimize.load_data(None, ticker_interval=1, pairs=['BTC_MEME'])
    assert os.path.isfile(file) is True
    assert log_has('Download the pair: "BTC_MEME", Interval: 1 min', caplog.record_tuples)
    _clean_test_file(file)
Beispiel #4
0
def test_load_data_5min_ticker(ticker_history, mocker, caplog) -> None:
    """
    Test load_data() with 5 min ticker
    """
    mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)

    file = 'freqtrade/tests/testdata/BTC_ETH-5.json'
    _backup_file(file, copy_file=True)
    optimize.load_data(None, pairs=['BTC_ETH'], ticker_interval=5)
    assert os.path.isfile(file) is True
    assert not log_has('Download the pair: "BTC_ETH", Interval: 5 min', caplog.record_tuples)
    _clean_test_file(file)
Beispiel #5
0
def test_load_data_with_new_pair_1min(default_conf, ticker_history, mocker, caplog):
    mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
    mocker.patch.dict('freqtrade.main._CONF', default_conf)

    exchange._API = Bittrex({'key': '', 'secret': ''})

    file = 'freqtrade/tests/testdata/BTC_MEME-1.json'
    _backup_file(file)
    optimize.load_data(None, ticker_interval=1, pairs=['BTC_MEME'])
    assert os.path.isfile(file) is True
    assert ('freqtrade.optimize',
            logging.INFO,
            'Download the pair: "BTC_MEME", Interval: 1 min'
            ) in caplog.record_tuples
    _clean_test_file(file)
Beispiel #6
0
def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
    """
    Test Backtesting.backtest() method with 1 min ticker
    """
    mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
    patch_exchange(mocker)
    backtesting = Backtesting(default_conf)

    # Run a backtesting for an exiting 5min ticker_interval
    data = optimize.load_data(None,
                              ticker_interval='1m',
                              pairs=['UNITTEST/BTC'])
    data = trim_dictlist(data, -200)
    results = backtesting.backtest({
        'stake_amount':
        default_conf['stake_amount'],
        'processed':
        backtesting.tickerdata_to_dataframe(data),
        'max_open_trades':
        1,
        'realistic':
        True
    })
    assert not results.empty
    assert len(results) == 1
def load_data_test(what):
    timerange = TimeRange(None, 'line', 0, -101)
    data = optimize.load_data(None,
                              ticker_interval='1m',
                              pairs=['UNITTEST/BTC'],
                              timerange=timerange)
    pair = data['UNITTEST/BTC']
    datalen = len(pair)
    # Depending on the what parameter we now adjust the
    # loaded data looks:
    # pair :: [[    1509836520000,   unix timestamp in ms
    #               0.00162008,      open
    #               0.00162008,      high
    #               0.00162008,      low
    #               0.00162008,      close
    #               108.14853839     base volume
    #           ]]
    base = 0.001
    if what == 'raise':
        return {
            'UNITTEST/BTC': [
                [
                    pair[x][0],  # Keep old dates
                    x * base,  # But replace O,H,L,C
                    x * base + 0.0001,
                    x * base - 0.0001,
                    x * base,
                    pair[x][5],  # Keep old volume
                ] for x in range(0, datalen)
            ]
        }
    if what == 'lower':
        return {
            'UNITTEST/BTC': [
                [
                    pair[x][0],  # Keep old dates
                    1 - x * base,  # But replace O,H,L,C
                    1 - x * base + 0.0001,
                    1 - x * base - 0.0001,
                    1 - x * base,
                    pair[x][5]  # Keep old volume
                ] for x in range(0, datalen)
            ]
        }
    if what == 'sine':
        hz = 0.1  # frequency
        return {
            'UNITTEST/BTC': [
                [
                    pair[x][0],  # Keep old dates
                    math.sin(x * hz) / 1000 + base,  # But replace O,H,L,C
                    math.sin(x * hz) / 1000 + base + 0.0001,
                    math.sin(x * hz) / 1000 + base - 0.0001,
                    math.sin(x * hz) / 1000 + base,
                    pair[x][5]  # Keep old volume
                ] for x in range(0, datalen)
            ]
        }
    return data
Beispiel #8
0
def test_backtest(default_conf, mocker):
    mocker.patch.dict('freqtrade.main._CONF', default_conf)
    exchange._API = Bittrex({'key': '', 'secret': ''})

    data = optimize.load_data(None, ticker_interval=5, pairs=['BTC_ETH'])
    results = backtest(default_conf['stake_amount'], optimize.preprocess(data),
                       10, True)
    assert not results.empty
def test_init(default_conf, mocker) -> None:
    exchange = get_patched_exchange(mocker, default_conf)
    assert {} == optimize.load_data(
        '',
        exchange=exchange,
        pairs=[],
        refresh_pairs=True,
        ticker_interval=default_conf['ticker_interval'])
Beispiel #10
0
def test_backtest(default_conf, mocker):
    mocker.patch.dict('freqtrade.main._CONF', default_conf)
    exchange._API = Bittrex({'key': '', 'secret': ''})

    data = optimize.load_data(None, ticker_interval=5, pairs=['BTC_ETH'])
    results = backtest(default_conf['stake_amount'],
                       optimize.preprocess(data), 10, True)
    assert not results.empty
Beispiel #11
0
def load_dataframe_pair(pairs, strategy):
    ld = load_data(None, ticker_interval='5m', pairs=pairs)
    assert isinstance(ld, dict)
    assert isinstance(pairs[0], str)
    dataframe = ld[pairs[0]]

    dataframe = strategy.analyze_ticker(dataframe, {'pair': pairs[0]})
    return dataframe
Beispiel #12
0
def load_dataframe_pair(pairs):
    ld = load_data(None, ticker_interval=5, pairs=pairs)
    assert isinstance(ld, dict)
    assert isinstance(pairs[0], str)
    dataframe = ld[pairs[0]]

    analyze = Analyze({'strategy': 'DefaultStrategy'})
    dataframe = analyze.analyze_ticker(dataframe)
    return dataframe
Beispiel #13
0
def test_init(default_conf, mocker) -> None:
    conf = {'exchange': {'pair_whitelist': []}}
    mocker.patch('freqtrade.optimize.hyperopt_optimize_conf', return_value=conf)
    assert {} == optimize.load_data(
        '',
        pairs=[],
        refresh_pairs=True,
        ticker_interval=int(default_conf['ticker_interval'])
    )
def test_get_timeframe(default_conf, mocker) -> None:
    patch_exchange(mocker)
    backtesting = Backtesting(default_conf)

    data = backtesting.tickerdata_to_dataframe(
        optimize.load_data(None, ticker_interval='1m', pairs=['UNITTEST/BTC']))
    min_date, max_date = backtesting.get_timeframe(data)
    assert min_date.isoformat() == '2017-11-04T23:02:00+00:00'
    assert max_date.isoformat() == '2017-11-14T22:58:00+00:00'
Beispiel #15
0
def test_backtest_1min_ticker_interval(default_conf, mocker):
    mocker.patch.dict('freqtrade.main._CONF', default_conf)
    exchange._API = Bittrex({'key': '', 'secret': ''})

    # Run a backtesting for an exiting 5min ticker_interval
    data = optimize.load_data(None, ticker_interval=1, pairs=['BTC_UNITEST'])
    results = backtest(default_conf['stake_amount'], optimize.preprocess(data),
                       1, True)
    assert not results.empty
Beispiel #16
0
def test_backtest_1min_ticker_interval(default_conf, mocker):
    mocker.patch.dict('freqtrade.main._CONF', default_conf)
    exchange._API = Bittrex({'key': '', 'secret': ''})

    # Run a backtesting for an exiting 5min ticker_interval
    data = optimize.load_data(None, ticker_interval=1, pairs=['BTC_UNITEST'])
    results = backtest(default_conf['stake_amount'],
                       optimize.preprocess(data), 1, True)
    assert not results.empty
Beispiel #17
0
def load_data_test(what):
    data = optimize.load_data(None, ticker_interval=1, pairs=['BTC_UNITEST'])
    data = trim_dictlist(data, -100)
    pair = data['BTC_UNITEST']
    datalen = len(pair)
    # Depending on the what parameter we now adjust the
    # loaded data looks:
    # pair :: [{'O': 0.123, 'H': 0.123, 'L': 0.123,
    #           'C': 0.123, 'V': 123.123,
    #           'T': '2017-11-04T23:02:00', 'BV': 0.123}]
    base = 0.001
    if what == 'raise':
        return {
            'BTC_UNITEST': [
                {
                    'T': pair[x]['T'],  # Keep old dates
                    'V': pair[x]['V'],  # Keep old volume
                    'BV': pair[x]['BV'],  # keep too
                    'O': x * base,  # But replace O,H,L,C
                    'H': x * base + 0.0001,
                    'L': x * base - 0.0001,
                    'C': x * base
                } for x in range(0, datalen)
            ]
        }
    if what == 'lower':
        return {
            'BTC_UNITEST': [
                {
                    'T': pair[x]['T'],  # Keep old dates
                    'V': pair[x]['V'],  # Keep old volume
                    'BV': pair[x]['BV'],  # keep too
                    'O': 1 - x * base,  # But replace O,H,L,C
                    'H': 1 - x * base + 0.0001,
                    'L': 1 - x * base - 0.0001,
                    'C': 1 - x * base
                } for x in range(0, datalen)
            ]
        }
    if what == 'sine':
        hz = 0.1  # frequency
        return {
            'BTC_UNITEST': [
                {
                    'T': pair[x]['T'],  # Keep old dates
                    'V': pair[x]['V'],  # Keep old volume
                    'BV': pair[x]['BV'],  # keep too
                    # But replace O,H,L,C
                    'O': math.sin(x * hz) / 1000 + base,
                    'H': math.sin(x * hz) / 1000 + base + 0.0001,
                    'L': math.sin(x * hz) / 1000 + base - 0.0001,
                    'C': math.sin(x * hz) / 1000 + base
                } for x in range(0, datalen)
            ]
        }
    return data
Beispiel #18
0
def start(args):
    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    )

    exchange._API = Bittrex({'key': '', 'secret': ''})

    logger.info('Using config: %s ...', args.config)
    config = misc.load_config(args.config)

    logger.info('Using ticker_interval: %s ...', args.ticker_interval)

    data = {}
    pairs = config['exchange']['pair_whitelist']
    if args.live:
        logger.info('Downloading data for all pairs in whitelist ...')
        for pair in pairs:
            data[pair] = exchange.get_ticker_history(pair, args.ticker_interval)
    else:
        logger.info('Using local backtesting data (using whitelist in given config) ...')
        data = optimize.load_data(args.datadir, pairs=pairs, ticker_interval=args.ticker_interval,
                                  refresh_pairs=args.refresh_pairs)

        logger.info('Using stake_currency: %s ...', config['stake_currency'])
        logger.info('Using stake_amount: %s ...', config['stake_amount'])

    max_open_trades = 0
    if args.realistic_simulation:
        logger.info('Using max_open_trades: %s ...', config['max_open_trades'])
        max_open_trades = config['max_open_trades']

    # Monkey patch config
    from freqtrade import main
    main._CONF = config

    preprocessed = preprocess(data)
    # Print timeframe
    min_date, max_date = get_timeframe(preprocessed)
    logger.info('Measuring data from %s up to %s ...', min_date.isoformat(), max_date.isoformat())

    # Execute backtest and print results
    results = backtest(
        stake_amount=config['stake_amount'],
        processed=preprocessed,
        max_open_trades=max_open_trades,
        realistic=args.realistic_simulation,
        sell_profit_only=config.get('experimental', {}).get('sell_profit_only', False),
        stoploss=config.get('stoploss'),
        use_sell_signal=config.get('experimental', {}).get('use_sell_signal', False)
    )
    logger.info(
        '\n==================================== BACKTESTING REPORT ====================================\n%s',  # noqa
        generate_text_table(data, results, config['stake_currency'], args.ticker_interval)
    )
Beispiel #19
0
    def start(self) -> None:
        timerange = Arguments.parse_timerange(None if self.config.get(
            'timerange') is None else str(self.config.get('timerange')))
        data = load_data(datadir=str(self.config.get('datadir')),
                         pairs=self.config['exchange']['pair_whitelist'],
                         ticker_interval=self.ticker_interval,
                         timerange=timerange)

        if self.has_space('buy'):
            self.analyze.populate_indicators = Hyperopt.populate_indicators  # type: ignore
        self.processed = self.tickerdata_to_dataframe(data)

        logger.info('Preparing Trials..')
        signal.signal(signal.SIGINT, self.signal_handler)
        # read trials file if we have one
        if os.path.exists(
                self.trials_file) and os.path.getsize(self.trials_file) > 0:
            self.trials = self.read_trials()

            self.current_tries = len(self.trials.results)
            self.total_tries += self.current_tries
            logger.info('Continuing with trials. Current: %d, Total: %d',
                        self.current_tries, self.total_tries)

        try:
            best_parameters = fmin(fn=self.generate_optimizer,
                                   space=self.hyperopt_space(),
                                   algo=tpe.suggest,
                                   max_evals=self.total_tries,
                                   trials=self.trials)

            results = sorted(self.trials.results, key=itemgetter('loss'))
            best_result = results[0]['result']

        except ValueError:
            best_parameters = {}
            best_result = 'Sorry, Hyperopt was not able to find good parameters. Please ' \
                          'try with more epochs (param: -e).'

        # Improve best parameter logging display
        if best_parameters:
            best_parameters = space_eval(self.hyperopt_space(),
                                         best_parameters)

        logger.info('Best parameters:\n%s',
                    json.dumps(best_parameters, indent=4))
        if 'roi_t1' in best_parameters:
            logger.info('ROI table:\n%s',
                        self.generate_roi_table(best_parameters))

        logger.info('Best Result:\n%s', best_result)

        # Store trials result to file to resume next time
        self.save_trials()
def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
    data = optimize.load_data(None, ticker_interval='8m', pairs=[pair])
    data = trim_dictlist(data, -201)
    patch_exchange(mocker)
    backtesting = Backtesting(conf)
    return {
        'stake_amount': conf['stake_amount'],
        'processed': backtesting.tickerdata_to_dataframe(data),
        'max_open_trades': 10,
        'position_stacking': False,
        'record': record
    }
def test_backtest(default_conf, fee, mocker) -> None:
    mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
    patch_exchange(mocker)
    backtesting = Backtesting(default_conf)
    pair = 'UNITTEST/BTC'
    data = optimize.load_data(None,
                              ticker_interval='5m',
                              pairs=['UNITTEST/BTC'])
    data = trim_dictlist(data, -200)
    data_processed = backtesting.tickerdata_to_dataframe(data)
    results = backtesting.backtest({
        'stake_amount': default_conf['stake_amount'],
        'processed': data_processed,
        'max_open_trades': 10,
        'position_stacking': False
    })
    assert not results.empty
    assert len(results) == 2

    expected = pd.DataFrame({
        'pair': [pair, pair],
        'profit_percent': [0.00029975, 0.00056708],
        'profit_abs': [1.49e-06, 7.6e-07],
        'open_time': [
            Arrow(2018, 1, 29, 18, 40, 0).datetime,
            Arrow(2018, 1, 30, 3, 30, 0).datetime
        ],
        'close_time': [
            Arrow(2018, 1, 29, 22, 40, 0).datetime,
            Arrow(2018, 1, 30, 4, 20, 0).datetime
        ],
        'open_index': [77, 183],
        'close_index': [125, 193],
        'trade_duration': [240, 50],
        'open_at_end': [False, False],
        'open_rate': [0.104445, 0.10302485],
        'close_rate': [0.105, 0.10359999],
        'sell_reason': [SellType.ROI, SellType.ROI]
    })
    pd.testing.assert_frame_equal(results, expected)
    data_pair = data_processed[pair]
    for _, t in results.iterrows():
        ln = data_pair.loc[data_pair["date"] == t["open_time"]]
        # Check open trade rate alignes to open rate
        assert ln is not None
        assert round(ln.iloc[0]["open"], 6) == round(t["open_rate"], 6)
        # check close trade rate alignes to close rate
        ln = data_pair.loc[data_pair["date"] == t["close_time"]]
        assert round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6)
Beispiel #22
0
def load_data_test(what):
    data = optimize.load_data(None, ticker_interval=1, pairs=['BTC_UNITEST'])
    data = trim_dictlist(data, -100)
    pair = data['BTC_UNITEST']
    datalen = len(pair)
    # Depending on the what parameter we now adjust the
    # loaded data looks:
    # pair :: [{'O': 0.123, 'H': 0.123, 'L': 0.123,
    #           'C': 0.123, 'V': 123.123,
    #           'T': '2017-11-04T23:02:00', 'BV': 0.123}]
    base = 0.001
    if what == 'raise':
        return {'BTC_UNITEST':
                [{'T': pair[x]['T'],  # Keep old dates
                  'V': pair[x]['V'],  # Keep old volume
                  'BV': pair[x]['BV'],  # keep too
                  'O': x * base,        # But replace O,H,L,C
                  'H': x * base + 0.0001,
                  'L': x * base - 0.0001,
                  'C': x * base} for x in range(0, datalen)]}
    if what == 'lower':
        return {'BTC_UNITEST':
                [{'T': pair[x]['T'],  # Keep old dates
                  'V': pair[x]['V'],  # Keep old volume
                  'BV': pair[x]['BV'],  # keep too
                  'O': 1 - x * base,        # But replace O,H,L,C
                  'H': 1 - x * base + 0.0001,
                  'L': 1 - x * base - 0.0001,
                  'C': 1 - x * base} for x in range(0, datalen)]}
    if what == 'sine':
        hz = 0.1  # frequency
        return {'BTC_UNITEST':
                [{'T': pair[x]['T'],  # Keep old dates
                  'V': pair[x]['V'],  # Keep old volume
                  'BV': pair[x]['BV'],  # keep too
                  # But replace O,H,L,C
                  'O': math.sin(x * hz) / 1000 + base,
                  'H': math.sin(x * hz) / 1000 + base + 0.0001,
                  'L': math.sin(x * hz) / 1000 + base - 0.0001,
                  'C': math.sin(x * hz) / 1000 + base} for x in range(0, datalen)]}
    return data
Beispiel #23
0
    def start(self) -> None:
        """
        Run a backtesting end-to-end
        :return: None
        """
        data = {}
        pairs = self.config['exchange']['pair_whitelist']
        logger.info('Using stake_currency: %s ...',
                    self.config['stake_currency'])
        logger.info('Using stake_amount: %s ...', self.config['stake_amount'])

        timerange = Arguments.parse_timerange(self.config.get('timerange'))

        data = optimize.load_data(self.config['datadir'],
                                  pairs=pairs,
                                  ticker_interval=self.ticker_interval,
                                  refresh_pairs=False,
                                  timerange=timerange)

        preprocessed = self.ML_tickerdata_to_dataframe(data)

        out_coin = 'BTC_XMR'
        test_ratio = 0.2

        data = ml_utils.run_pipeline(preprocessed[out_coin], out_coin,
                                     test_ratio)

        initial_cash = 100  # in USD
        transaction_cost = 0.01  # as ratio for fee
        alpha = 0.05  # ratio of portfolio value that we trade each transaction
        (trading_portfolio,
         trading_stats) = self.run_buy_sell_strategy(data, initial_cash,
                                                     transaction_cost, alpha)
        print(trading_portfolio)
        #print(trading_portfolio.head(),"Head of trading series.")
        print("Stats from trading: ", trading_stats)
        # plots trading portfolio value in time next to out_coin price
        trading_portfolio.plot(subplots=True, figsize=(6, 6))
        plt.legend(loc='best')
        plt.show()
Beispiel #24
0
    def start(self) -> None:
        timerange = Arguments.parse_timerange(None if self.config.get(
            'timerange') is None else str(self.config.get('timerange')))
        data = load_data(datadir=str(self.config.get('datadir')),
                         pairs=self.config['exchange']['pair_whitelist'],
                         ticker_interval=self.ticker_interval,
                         timerange=timerange)

        if self.has_space('buy'):
            self.strategy.advise_indicators = Hyperopt.populate_indicators  # type: ignore
        dump(self.tickerdata_to_dataframe(data), TICKERDATA_PICKLE)
        self.exchange = None  # type: ignore
        self.load_previous_results()

        cpus = multiprocessing.cpu_count()
        logger.info(f'Found {cpus} CPU cores. Let\'s make them scream!')

        opt = self.get_optimizer(cpus)
        EVALS = max(self.total_tries // cpus, 1)
        try:
            with Parallel(n_jobs=cpus) as parallel:
                for i in range(EVALS):
                    asked = opt.ask(n_points=cpus)
                    f_val = self.run_optimizer_parallel(parallel, asked)
                    opt.tell(asked, [i['loss'] for i in f_val])

                    self.trials += f_val
                    for j in range(cpus):
                        self.log_results({
                            'loss': f_val[j]['loss'],
                            'current_tries': i * cpus + j,
                            'total_tries': self.total_tries,
                            'result': f_val[j]['result'],
                        })
        except KeyboardInterrupt:
            print('User interrupted..')

        self.save_trials()
        self.log_trials_result()
Beispiel #25
0
def plot_analyzed_dataframe(args: Namespace) -> None:
    """
    Calls analyze() and plots the returned dataframe
    :return: None
    """
    global _CONF

    # Load the configuration
    _CONF.update(setup_configuration(args))

    # Set the pair to audit
    pair = args.pair

    if pair is None:
        logger.critical('Parameter --pair mandatory;. E.g --pair ETH/BTC')
        exit()

    if '/' not in pair:
        logger.critical('--pair format must be XXX/YYY')
        exit()

    # Set timerange to use
    timerange = Arguments.parse_timerange(args.timerange)

    # Load the strategy
    try:
        analyze = Analyze(_CONF)
        exchange = Exchange(_CONF)
    except AttributeError:
        logger.critical(
            'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
            args.strategy)
        exit()

    # Set the ticker to use
    tick_interval = analyze.get_ticker_interval()

    # Load pair tickers
    tickers = {}
    if args.live:
        logger.info('Downloading pair.')
        tickers[pair] = exchange.get_ticker_history(pair, tick_interval)
    else:
        tickers = optimize.load_data(datadir=_CONF.get("datadir"),
                                     pairs=[pair],
                                     ticker_interval=tick_interval,
                                     refresh_pairs=_CONF.get(
                                         'refresh_pairs', False),
                                     timerange=timerange)

        # No ticker found, or impossible to download
        if tickers == {}:
            exit()

    # Get trades already made from the DB
    trades: List[Trade] = []
    if args.db_url:
        persistence.init(_CONF)
        trades = Trade.query.filter(Trade.pair.is_(pair)).all()

    dataframes = analyze.tickerdata_to_dataframe(tickers)
    dataframe = dataframes[pair]
    dataframe = analyze.populate_buy_trend(dataframe)
    dataframe = analyze.populate_sell_trend(dataframe)

    if len(dataframe.index) > 750:
        logger.warning('Ticker contained more than 750 candles, clipping.')

    fig = generate_graph(pair=pair,
                         trades=trades,
                         data=dataframe.tail(750),
                         args=args)

    plot(fig, filename=os.path.join('user_data', 'freqtrade-plot.html'))
Beispiel #26
0
    def start(self) -> None:
        """
        Run a backtesting end-to-end
        :return: None
        """
        data = {}
        pairs = self.config['exchange']['pair_whitelist']
        logger.info('Using stake_currency: %s ...', self.config['stake_currency'])
        logger.info('Using stake_amount: %s ...', self.config['stake_amount'])

        if self.config.get('live'):
            logger.info('Downloading data for all pairs in whitelist ...')
            for pair in pairs:
                data[pair] = exchange.get_ticker_history(pair, self.ticker_interval)
        else:
            logger.info('Using local backtesting data (using whitelist in given config) ...')

            timerange = Arguments.parse_timerange(self.config.get('timerange'))
            data = optimize.load_data(
                self.config['datadir'],
                pairs=pairs,
                ticker_interval=self.ticker_interval,
                refresh_pairs=self.config.get('refresh_pairs', False),
                timerange=timerange
            )

        # Ignore max_open_trades in backtesting, except realistic flag was passed
        if self.config.get('realistic_simulation', False):
            max_open_trades = self.config['max_open_trades']
        else:
            logger.info('Ignoring max_open_trades (realistic_simulation not set) ...')
            max_open_trades = 0

        preprocessed = self.tickerdata_to_dataframe(data)
        print(preprocessed)
        # Print timeframe
        min_date, max_date = self.get_timeframe(preprocessed)
        logger.info(
            'Measuring data from %s up to %s (%s days)..',
            min_date.isoformat(),
            max_date.isoformat(),
            (max_date - min_date).days
        )

        # Execute backtest and print results
        sell_profit_only = self.config.get('experimental', {}).get('sell_profit_only', False)
        use_sell_signal = self.config.get('experimental', {}).get('use_sell_signal', False)
        results = self.backtest(
            {
                'stake_amount': self.config.get('stake_amount'),
                'processed': preprocessed,
                'max_open_trades': max_open_trades,
                'realistic': self.config.get('realistic_simulation', False),
                'sell_profit_only': sell_profit_only,
                'use_sell_signal': use_sell_signal,
                'record': self.config.get('export')
            }
        )
        logger.info(
            '\n==================================== '
            'BACKTESTING REPORT'
            ' ====================================\n'
            '%s',
            self._generate_text_table(
                data,
                results
            )
        )
Beispiel #27
0
    def start(self) -> None:
        """
        Run a backtesting end-to-end
        :return: None
        """
        data = {}
        pairs = self.config['exchange']['pair_whitelist']
        logger.info('Using stake_currency: %s ...',
                    self.config['stake_currency'])
        logger.info('Using stake_amount: %s ...', self.config['stake_amount'])

        if self.config.get('live'):
            logger.info('Downloading data for all pairs in whitelist ...')
            for pair in pairs:
                data[pair] = self.exchange.get_ticker_history(
                    pair, self.ticker_interval)
        else:
            logger.info(
                'Using local backtesting data (using whitelist in given config) ...'
            )

            timerange = Arguments.parse_timerange(None if self.config.get(
                'timerange') is None else str(self.config.get('timerange')))
            data = optimize.load_data(self.config['datadir'],
                                      pairs=pairs,
                                      ticker_interval=self.ticker_interval,
                                      refresh_pairs=self.config.get(
                                          'refresh_pairs', False),
                                      exchange=self.exchange,
                                      timerange=timerange)

        if not data:
            logger.critical("No data found. Terminating.")
            return
        # Ignore max_open_trades in backtesting, except realistic flag was passed
        if self.config.get('realistic_simulation', False):
            max_open_trades = self.config['max_open_trades']
        else:
            logger.info(
                'Ignoring max_open_trades (realistic_simulation not set) ...')
            max_open_trades = 0

        preprocessed = self.tickerdata_to_dataframe(data)

        # Print timeframe
        min_date, max_date = self.get_timeframe(preprocessed)
        logger.info('Measuring data from %s up to %s (%s days)..',
                    min_date.isoformat(), max_date.isoformat(),
                    (max_date - min_date).days)

        # Execute backtest and print results
        results = self.backtest({
            'stake_amount':
            self.config.get('stake_amount'),
            'processed':
            preprocessed,
            'max_open_trades':
            max_open_trades,
            'realistic':
            self.config.get('realistic_simulation', False),
        })

        if self.config.get('export', False):
            self._store_backtest_result(self.config.get('exportfilename'),
                                        results)

        logger.info(
            '\n======================================== '
            'BACKTESTING REPORT'
            ' =========================================\n'
            '%s', self._generate_text_table(data, results))

        logger.info(
            '\n====================================== '
            'LEFT OPEN TRADES REPORT'
            ' ======================================\n'
            '%s',
            self._generate_text_table(data, results.loc[results.open_at_end]))
def _load_pair_as_ticks(pair, tickfreq):
    ticks = optimize.load_data(None, ticker_interval=tickfreq, pairs=[pair])
    ticks = trim_dictlist(ticks, -201)
    return ticks[pair]
Beispiel #29
0
    def start(self) -> None:
        """
        Run a backtesting end-to-end
        :return: None
        """
        data = {}
        pairs = self.config['exchange']['pair_whitelist']
        logger.info('Using stake_currency: %s ...', self.config['stake_currency'])
        logger.info('Using stake_amount: %s ...', self.config['stake_amount'])

        if self.config.get('live'):
            logger.info('Downloading data for all pairs in whitelist ...')
            for pair in pairs:
                data[pair] = self.exchange.get_candle_history(pair, self.ticker_interval)
        else:
            logger.info('Using local backtesting data (using whitelist in given config) ...')

            timerange = Arguments.parse_timerange(None if self.config.get(
                'timerange') is None else str(self.config.get('timerange')))
            data = optimize.load_data(
                self.config['datadir'],
                pairs=pairs,
                ticker_interval=self.ticker_interval,
                refresh_pairs=self.config.get('refresh_pairs', False),
                exchange=self.exchange,
                timerange=timerange
            )

        if not data:
            logger.critical("No data found. Terminating.")
            return
        # Use max_open_trades in backtesting, except --disable-max-market-positions is set
        if self.config.get('use_max_market_positions', True):
            max_open_trades = self.config['max_open_trades']
        else:
            logger.info('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
            max_open_trades = 0
        all_results = {}

        for strat in self.strategylist:
            logger.info("Running backtesting for Strategy %s", strat.get_strategy_name())
            self._set_strategy(strat)

            # need to reprocess data every time to populate signals
            preprocessed = self.tickerdata_to_dataframe(data)

            # Print timeframe
            min_date, max_date = self.get_timeframe(preprocessed)
            logger.info(
                'Measuring data from %s up to %s (%s days)..',
                min_date.isoformat(),
                max_date.isoformat(),
                (max_date - min_date).days
            )

            # Execute backtest and print results
            all_results[self.strategy.get_strategy_name()] = self.backtest(
                {
                    'stake_amount': self.config.get('stake_amount'),
                    'processed': preprocessed,
                    'max_open_trades': max_open_trades,
                    'position_stacking': self.config.get('position_stacking', False),
                }
            )

        for strategy, results in all_results.items():

            if self.config.get('export', False):
                self._store_backtest_result(self.config['exportfilename'], results,
                                            strategy if len(self.strategylist) > 1 else None)

            print(f"Result for strategy {strategy}")
            print(' BACKTESTING REPORT '.center(119, '='))
            print(self._generate_text_table(data, results))

            print(' SELL REASON STATS '.center(119, '='))
            print(self._generate_text_table_sell_reason(data, results))

            print(' LEFT OPEN TRADES REPORT '.center(119, '='))
            print(self._generate_text_table(data, results.loc[results.open_at_end]))
            print()
        if len(all_results) > 1:
            # Print Strategy summary table
            print(' Strategy Summary '.center(119, '='))
            print(self._generate_text_table_strategy(all_results))
            print('\nFor more details, please look at the detail tables above')
Beispiel #30
0
def test_get_timeframe():
    data = preprocess(
        optimize.load_data(None, ticker_interval=1, pairs=['BTC_UNITEST']))
    min_date, max_date = get_timeframe(data)
    assert min_date.isoformat() == '2017-11-04T23:02:00+00:00'
    assert max_date.isoformat() == '2017-11-14T22:59:00+00:00'
Beispiel #31
0
def start(args):
    global TOTAL_TRIES, PROCESSED, SPACE, TRIALS, _CURRENT_TRIES

    TOTAL_TRIES = args.epochs

    exchange._API = Bittrex({'key': '', 'secret': ''})

    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='\n%(message)s',
    )

    logger.info('Using config: %s ...', args.config)
    config = load_config(args.config)
    pairs = config['exchange']['pair_whitelist']
    PROCESSED = optimize.preprocess(
        optimize.load_data(args.datadir,
                           pairs=pairs,
                           ticker_interval=args.ticker_interval))

    if args.mongodb:
        logger.info('Using mongodb ...')
        logger.info(
            'Start scripts/start-mongodb.sh and start-hyperopt-worker.sh manually!'
        )

        db_name = 'freqtrade_hyperopt'
        TRIALS = MongoTrials('mongo://127.0.0.1:1234/{}/jobs'.format(db_name),
                             exp_key='exp1')
    else:
        logger.info('Preparing Trials..')
        signal.signal(signal.SIGINT, signal_handler)
        # read trials file if we have one
        if os.path.exists(TRIALS_FILE):
            TRIALS = read_trials()

            _CURRENT_TRIES = len(TRIALS.results)
            TOTAL_TRIES = TOTAL_TRIES + _CURRENT_TRIES
            logger.info(
                'Continuing with trials. Current: {}, Total: {}'.format(
                    _CURRENT_TRIES, TOTAL_TRIES))

    try:
        best_parameters = fmin(fn=optimizer,
                               space=SPACE,
                               algo=tpe.suggest,
                               max_evals=TOTAL_TRIES,
                               trials=TRIALS)

        results = sorted(TRIALS.results, key=itemgetter('loss'))
        best_result = results[0]['result']

    except ValueError:
        best_parameters = {}
        best_result = 'Sorry, Hyperopt was not able to find good parameters. Please ' \
                      'try with more epochs (param: -e).'

    # Improve best parameter logging display
    if best_parameters:
        best_parameters = space_eval(SPACE, best_parameters)

    logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4))
    logger.info('Best Result:\n%s', best_result)

    # Store trials result to file to resume next time
    save_trials(TRIALS)
Beispiel #32
0
def plot_profit(args: Namespace) -> 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.
    """

    # We need to use the same pairs, same tick_interval
    # and same timeperiod as used in backtesting
    # to match the tickerdata against the profits-results
    timerange = Arguments.parse_timerange(args.timerange)

    config = Configuration(args).get_config()

    # Init strategy
    try:
        analyze = Analyze({'strategy': config.get('strategy')})
    except AttributeError:
        logger.critical(
            'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
            config.get('strategy'))
        exit(1)

    # Load the profits results
    try:
        filename = args.exportfilename
        with open(filename) as file:
            data = json.load(file)
    except FileNotFoundError:
        logger.critical(
            'File "backtest-result.json" not found. This script require backtesting '
            'results to run.\nPlease run a backtesting with the parameter --export.'
        )
        exit(1)

    # Take pairs from the cli otherwise switch to the pair in the config file
    if args.pair:
        filter_pairs = args.pair
        filter_pairs = filter_pairs.split(',')
    else:
        filter_pairs = config['exchange']['pair_whitelist']

    tick_interval = analyze.strategy.ticker_interval
    pairs = config['exchange']['pair_whitelist']

    if filter_pairs:
        pairs = list(set(pairs) & set(filter_pairs))
        logger.info('Filter, keep pairs %s' % pairs)

    tickers = optimize.load_data(datadir=config.get('datadir'),
                                 pairs=pairs,
                                 ticker_interval=tick_interval,
                                 refresh_pairs=False,
                                 timerange=timerange)
    dataframes = analyze.tickerdata_to_dataframe(tickers)

    # NOTE: the dataframes are of unequal length,
    # 'dates' is an merged date array of them all.

    dates = misc.common_datearray(dataframes)
    min_date = int(min(dates).timestamp())
    max_date = int(max(dates).timestamp())
    num_iterations = define_index(min_date, max_date, tick_interval) + 1

    # Make an average close price of all the pairs that was involved.
    # this could be useful to gauge the overall market trend
    # We are essentially saying:
    #  array <- sum dataframes[*]['close'] / num_items dataframes
    #  FIX: there should be some onliner numpy/panda for this
    avgclose = np.zeros(num_iterations)
    num = 0
    for pair, pair_data in dataframes.items():
        close = pair_data['close']
        maxprice = max(close)  # Normalize price to [0,1]
        logger.info('Pair %s has length %s' % (pair, len(close)))
        for x in range(0, len(close)):
            avgclose[x] += close[x] / maxprice
        # avgclose += close
        num += 1
    avgclose /= num

    # make an profits-growth array
    pg = make_profit_array(data, num_iterations, min_date, tick_interval,
                           filter_pairs)

    #
    # Plot the pairs average close prices, and total profit growth
    #

    avgclose = go.Scattergl(
        x=dates,
        y=avgclose,
        name='Avg close price',
    )

    profit = go.Scattergl(
        x=dates,
        y=pg,
        name='Profit',
    )

    fig = tools.make_subplots(rows=3,
                              cols=1,
                              shared_xaxes=True,
                              row_width=[1, 1, 1])

    fig.append_trace(avgclose, 1, 1)
    fig.append_trace(profit, 2, 1)

    for pair in pairs:
        pg = make_profit_array(data, num_iterations, min_date, tick_interval,
                               pair)
        pair_profit = go.Scattergl(
            x=dates,
            y=pg,
            name=pair,
        )
        fig.append_trace(pair_profit, 3, 1)

    plot(fig, filename=os.path.join('user_data', 'freqtrade-profit-plot.html'))
Beispiel #33
0
def start(args):
    global TOTAL_TRIES, PROCESSED, SPACE, TRIALS, _CURRENT_TRIES

    TOTAL_TRIES = args.epochs

    exchange._API = Bittrex({'key': '', 'secret': ''})

    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='\n%(message)s',
    )

    logger.info('Using config: %s ...', args.config)
    config = load_config(args.config)
    pairs = config['exchange']['pair_whitelist']
    PROCESSED = optimize.preprocess(optimize.load_data(
        args.datadir, pairs=pairs, ticker_interval=args.ticker_interval))

    if args.mongodb:
        logger.info('Using mongodb ...')
        logger.info('Start scripts/start-mongodb.sh and start-hyperopt-worker.sh manually!')

        db_name = 'freqtrade_hyperopt'
        TRIALS = MongoTrials('mongo://127.0.0.1:1234/{}/jobs'.format(db_name), exp_key='exp1')
    else:
        logger.info('Preparing Trials..')
        signal.signal(signal.SIGINT, signal_handler)
        # read trials file if we have one
        if os.path.exists(TRIALS_FILE):
            TRIALS = read_trials()

            _CURRENT_TRIES = len(TRIALS.results)
            TOTAL_TRIES = TOTAL_TRIES + _CURRENT_TRIES
            logger.info(
                'Continuing with trials. Current: {}, Total: {}'
                .format(_CURRENT_TRIES, TOTAL_TRIES))

    try:
        best_parameters = fmin(
            fn=optimizer,
            space=SPACE,
            algo=tpe.suggest,
            max_evals=TOTAL_TRIES,
            trials=TRIALS
        )

        results = sorted(TRIALS.results, key=itemgetter('loss'))
        best_result = results[0]['result']

    except ValueError:
        best_parameters = {}
        best_result = 'Sorry, Hyperopt was not able to find good parameters. Please ' \
                      'try with more epochs (param: -e).'

    # Improve best parameter logging display
    if best_parameters:
        best_parameters = space_eval(SPACE, best_parameters)

    logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4))
    logger.info('Best Result:\n%s', best_result)

    # Store trials result to file to resume next time
    save_trials(TRIALS)
Beispiel #34
0
def start(args):
    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    )

    exchange._API = Bittrex({'key': '', 'secret': ''})

    logger.info('Using config: %s ...', args.config)
    config = misc.load_config(args.config)

    logger.info('Using ticker_interval: %s ...', args.ticker_interval)

    data = {}
    pairs = config['exchange']['pair_whitelist']
    if args.live:
        logger.info('Downloading data for all pairs in whitelist ...')
        for pair in pairs:
            data[pair] = exchange.get_ticker_history(pair,
                                                     args.ticker_interval)
    else:
        logger.info(
            'Using local backtesting data (using whitelist in given config) ...'
        )
        data = optimize.load_data(args.datadir,
                                  pairs=pairs,
                                  ticker_interval=args.ticker_interval,
                                  refresh_pairs=args.refresh_pairs)

        logger.info('Using stake_currency: %s ...', config['stake_currency'])
        logger.info('Using stake_amount: %s ...', config['stake_amount'])

    max_open_trades = 0
    if args.realistic_simulation:
        logger.info('Using max_open_trades: %s ...', config['max_open_trades'])
        max_open_trades = config['max_open_trades']

    # Monkey patch config
    from freqtrade import main
    main._CONF = config

    preprocessed = preprocess(data)
    # Print timeframe
    min_date, max_date = get_timeframe(preprocessed)
    logger.info('Measuring data from %s up to %s ...', min_date.isoformat(),
                max_date.isoformat())

    # Execute backtest and print results
    results = backtest(
        stake_amount=config['stake_amount'],
        processed=preprocessed,
        max_open_trades=max_open_trades,
        realistic=args.realistic_simulation,
        sell_profit_only=config.get('experimental',
                                    {}).get('sell_profit_only', False),
        stoploss=config.get('stoploss'),
        use_sell_signal=config.get('experimental',
                                   {}).get('use_sell_signal', False))
    logger.info(
        '\n==================================== BACKTESTING REPORT ====================================\n%s',  # noqa
        generate_text_table(data, results, config['stake_currency'],
                            args.ticker_interval))
Beispiel #35
0
def test_get_timeframe():
    data = preprocess(optimize.load_data(
        None, ticker_interval=1, pairs=['BTC_UNITEST']))
    min_date, max_date = get_timeframe(data)
    assert min_date.isoformat() == '2017-11-04T23:02:00+00:00'
    assert max_date.isoformat() == '2017-11-14T22:59:00+00:00'
def plot_analyzed_dataframe(args: Namespace) -> None:
    """
    Calls analyze() and plots the returned dataframe
    :return: None
    """
    global _CONF

    # Load the configuration
    _CONF.update(setup_configuration(args))

    print(_CONF)
    # Set the pair to audit
    pair = args.pair

    if pair is None:
        logger.critical('Parameter --pair mandatory;. E.g --pair ETH/BTC')
        exit()

    if '/' not in pair:
        logger.critical('--pair format must be XXX/YYY')
        exit()

    # Set timerange to use
    timerange = Arguments.parse_timerange(args.timerange)

    # Load the strategy
    try:
        strategy = StrategyResolver(_CONF).strategy
        exchange = Exchange(_CONF)
    except AttributeError:
        logger.critical(
            'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
            args.strategy
        )
        exit()

    # Set the ticker to use
    tick_interval = strategy.ticker_interval

    # Load pair tickers
    tickers = {}
    if args.live:
        logger.info('Downloading pair.')
        tickers[pair] = exchange.get_candle_history(pair, tick_interval)
    else:
        tickers = optimize.load_data(
            datadir=_CONF.get("datadir"),
            pairs=[pair],
            ticker_interval=tick_interval,
            refresh_pairs=_CONF.get('refresh_pairs', False),
            timerange=timerange,
            exchange=Exchange(_CONF)
        )

        # No ticker found, or impossible to download
        if tickers == {}:
            exit()

    # Get trades already made from the DB
    trades = load_trades(args, pair, timerange)

    dataframes = strategy.tickerdata_to_dataframe(tickers)

    dataframe = dataframes[pair]
    dataframe = strategy.advise_buy(dataframe, {'pair': pair})
    dataframe = strategy.advise_sell(dataframe, {'pair': pair})

    if len(dataframe.index) > args.plot_limit:
        logger.warning('Ticker contained more than %s candles as defined '
                       'with --plot-limit, clipping.', args.plot_limit)
    dataframe = dataframe.tail(args.plot_limit)

    trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']]
    fig = generate_graph(
        pair=pair,
        trades=trades,
        data=dataframe,
        args=args
    )

    plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')))
Beispiel #37
0
def plot_analyzed_dataframe(args: Namespace) -> None:
    """
    Calls analyze() and plots the returned dataframe
    :return: None
    """
    pair = args.pair.replace('-', '_')
    timerange = Arguments.parse_timerange(args.timerange)

    # Init strategy
    try:
        analyze = Analyze({'strategy': args.strategy})
    except AttributeError:
        logger.critical(
            'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
            args.strategy)
        exit()

    tick_interval = analyze.strategy.ticker_interval

    tickers = {}
    if args.live:
        logger.info('Downloading pair.')
        # Init Bittrex to use public API
        exchange._API = exchange.Bittrex({'key': '', 'secret': ''})
        tickers[pair] = exchange.get_ticker_history(pair, tick_interval)
    else:
        tickers = optimize.load_data(datadir=args.datadir,
                                     pairs=[pair],
                                     ticker_interval=tick_interval,
                                     refresh_pairs=False,
                                     timerange=timerange)
    dataframes = analyze.tickerdata_to_dataframe(tickers)
    dataframe = dataframes[pair]
    dataframe = analyze.populate_buy_trend(dataframe)
    dataframe = analyze.populate_sell_trend(dataframe)

    if len(dataframe.index) > 750:
        logger.warning('Ticker contained more than 750 candles, clipping.')
    data = dataframe.tail(750)

    candles = go.Candlestick(x=data.date,
                             open=data.open,
                             high=data.high,
                             low=data.low,
                             close=data.close,
                             name='Price')

    df_buy = data[data['buy'] == 1]
    buys = go.Scattergl(x=df_buy.date,
                        y=df_buy.close,
                        mode='markers',
                        name='buy',
                        marker=dict(
                            symbol='triangle-up-dot',
                            size=9,
                            line=dict(width=1),
                            color='green',
                        ))
    df_sell = data[data['sell'] == 1]
    sells = go.Scattergl(x=df_sell.date,
                         y=df_sell.close,
                         mode='markers',
                         name='sell',
                         marker=dict(
                             symbol='triangle-down-dot',
                             size=9,
                             line=dict(width=1),
                             color='red',
                         ))

    bb_lower = go.Scatter(
        x=data.date,
        y=data.bb_lowerband,
        name='BB lower',
        line={'color': "transparent"},
    )
    bb_upper = go.Scatter(
        x=data.date,
        y=data.bb_upperband,
        name='BB upper',
        fill="tonexty",
        fillcolor="rgba(0,176,246,0.2)",
        line={'color': "transparent"},
    )
    macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD')
    macdsignal = go.Scattergl(x=data['date'],
                              y=data['macdsignal'],
                              name='MACD signal')
    volume = go.Bar(x=data['date'], y=data['volume'], name='Volume')

    fig = tools.make_subplots(
        rows=3,
        cols=1,
        shared_xaxes=True,
        row_width=[1, 1, 4],
        vertical_spacing=0.0001,
    )

    fig.append_trace(candles, 1, 1)
    fig.append_trace(bb_lower, 1, 1)
    fig.append_trace(bb_upper, 1, 1)
    fig.append_trace(buys, 1, 1)
    fig.append_trace(sells, 1, 1)
    fig.append_trace(volume, 2, 1)
    fig.append_trace(macd, 3, 1)
    fig.append_trace(macdsignal, 3, 1)

    fig['layout'].update(title=args.pair)
    fig['layout']['yaxis1'].update(title='Price')
    fig['layout']['yaxis2'].update(title='Volume')
    fig['layout']['yaxis3'].update(title='MACD')

    plot(fig, filename='freqtrade-plot.html')