Exemplo n.º 1
0
def test_generate_profit_graph():
    filename = history.make_testdata_path(None) / "backtest-result_test.json"
    trades = load_backtest_data(filename)
    timerange = Arguments.parse_timerange("20180110-20180112")
    pairs = ["POWR/BTC", "XLM/BTC"]

    tickers = history.load_data(datadir=None,
                                pairs=pairs,
                                ticker_interval='5m',
                                timerange=timerange)
    trades = trades[trades['pair'].isin(pairs)]

    fig = generate_profit_graph(pairs, tickers, trades)
    assert isinstance(fig, go.Figure)

    assert fig.layout.title.text == "Profit plot"
    figure = fig.layout.figure
    assert len(figure.data) == 4

    avgclose = find_trace_in_fig_data(figure.data, "Avg close price")
    assert isinstance(avgclose, go.Scattergl)

    profit = find_trace_in_fig_data(figure.data, "Profit")
    assert isinstance(profit, go.Scattergl)

    for pair in pairs:
        profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}")
        assert isinstance(profit_pair, go.Scattergl)
Exemplo n.º 2
0
def test_create_cum_profit():
    filename = make_testdata_path(None) / "backtest-result_test.json"
    bt_data = load_backtest_data(filename)
    timerange = Arguments.parse_timerange("20180110-20180112")

    df = load_pair_history(pair="POWR/BTC",
                           ticker_interval='5m',
                           datadir=None,
                           timerange=timerange)

    cum_profits = create_cum_profit(df.set_index('date'),
                                    bt_data[bt_data["pair"] == 'POWR/BTC'],
                                    "cum_profits")
    assert "cum_profits" in cum_profits.columns
    assert cum_profits.iloc[0]['cum_profits'] == 0
    assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005
Exemplo n.º 3
0
    def __init__(self, config: Dict[str, Any]) -> None:
        self.config = config

        # Reset keys for edge
        self.config['exchange']['key'] = ''
        self.config['exchange']['secret'] = ''
        self.config['exchange']['password'] = ''
        self.config['exchange']['uid'] = ''
        self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
        self.config['dry_run'] = True
        self.exchange = Exchange(self.config)
        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.timerange = Arguments.parse_timerange(None if self.config.get(
            'timerange') is None else str(self.config.get('timerange')))

        self.edge._timerange = self.timerange
Exemplo n.º 4
0
    def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:

        self.config = config
        self.exchange = exchange
        self.strategy = strategy

        self.edge_config = self.config.get('edge', {})
        self._cached_pairs: Dict[str, Any] = {}  # Keeps a list of pairs
        self._final_pairs: list = []

        # checking max_open_trades. it should be -1 as with Edge
        # the number of trades is determined by position size
        if self.config['max_open_trades'] != float('inf'):
            logger.critical('max_open_trades should be -1 in config !')

        if self.config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT:
            raise OperationalException('Edge works only with unlimited stake amount')

        self._capital_percentage: float = self.edge_config.get('capital_available_percentage')
        self._allowed_risk: float = self.edge_config.get('allowed_risk')
        self._since_number_of_days: int = self.edge_config.get('calculate_since_number_of_days', 14)
        self._last_updated: int = 0  # Timestamp of pairs last updated time
        self._refresh_pairs = True

        self._stoploss_range_min = float(self.edge_config.get('stoploss_range_min', -0.01))
        self._stoploss_range_max = float(self.edge_config.get('stoploss_range_max', -0.05))
        self._stoploss_range_step = float(self.edge_config.get('stoploss_range_step', -0.001))

        # calculating stoploss range
        self._stoploss_range = np.arange(
            self._stoploss_range_min,
            self._stoploss_range_max,
            self._stoploss_range_step
        )

        self._timerange: TimeRange = Arguments.parse_timerange("%s-" % arrow.now().shift(
            days=-1 * self._since_number_of_days).format('YYYYMMDD'))

        self.fee = self.exchange.get_fee()
Exemplo n.º 5
0
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("live", False) or config.get("refresh_pairs", False):
        exchange = ExchangeResolver(config.get('exchange', {}).get('name'),
                                    config).exchange

    strategy = StrategyResolver(config).strategy
    if "pairs" in config:
        pairs = config["pairs"].split(',')
    else:
        pairs = config["exchange"]["pair_whitelist"]

    # Set timerange to use
    timerange = Arguments.parse_timerange(config.get("timerange"))

    tickers = history.load_data(
        datadir=Path(str(config.get("datadir"))),
        pairs=pairs,
        ticker_interval=config['ticker_interval'],
        refresh_pairs=config.get('refresh_pairs', False),
        timerange=timerange,
        exchange=exchange,
        live=config.get("live", False),
    )

    trades = load_trades(config)
    return {"tickers": tickers,
            "trades": trades,
            "pairs": pairs,
            "strategy": strategy,
            }
Exemplo n.º 6
0
def test_add_profit():
    filename = history.make_testdata_path(None) / "backtest-result_test.json"
    bt_data = load_backtest_data(filename)
    timerange = Arguments.parse_timerange("20180110-20180112")

    df = history.load_pair_history(pair="POWR/BTC",
                                   ticker_interval='5m',
                                   datadir=None,
                                   timerange=timerange)
    fig = generage_empty_figure()

    cum_profits = create_cum_profit(df.set_index('date'),
                                    bt_data[bt_data["pair"] == 'POWR/BTC'],
                                    "cum_profits")

    fig1 = add_profit(fig,
                      row=2,
                      data=cum_profits,
                      column='cum_profits',
                      name='Profits')
    figure = fig1.layout.figure
    profits = find_trace_in_fig_data(figure.data, "Profits")
    assert isinstance(profits, go.Scattergl)
    assert profits.yaxis == "y2"
Exemplo n.º 7
0
def test_parse_timerange_incorrect() -> None:
    assert TimeRange(None, 'line', 0,
                     -200) == Arguments.parse_timerange('-200')
    assert TimeRange('line', None, 200, 0) == Arguments.parse_timerange('200-')
    assert TimeRange('index', 'index', 200,
                     500) == Arguments.parse_timerange('200-500')

    assert TimeRange('date', None, 1274486400,
                     0) == Arguments.parse_timerange('20100522-')
    assert TimeRange(None, 'date', 0,
                     1274486400) == Arguments.parse_timerange('-20100522')
    timerange = Arguments.parse_timerange('20100522-20150730')
    assert timerange == TimeRange('date', 'date', 1274486400, 1438214400)

    # Added test for unix timestamp - BTC genesis date
    assert TimeRange('date', None, 1231006505,
                     0) == Arguments.parse_timerange('1231006505-')
    assert TimeRange(None, 'date', 0,
                     1233360000) == Arguments.parse_timerange('-1233360000')
    timerange = Arguments.parse_timerange('1231006505-1233360000')
    assert TimeRange('date', 'date', 1231006505, 1233360000) == timerange

    # TODO: Find solution for the following case (passing timestamp in ms)
    timerange = Arguments.parse_timerange('1231006505000-1233360000000')
    assert TimeRange('date', 'date', 1231006505, 1233360000) != timerange

    with pytest.raises(Exception, match=r'Incorrect syntax.*'):
        Arguments.parse_timerange('-')
Exemplo n.º 8
0
    def start(self) -> None:
        """
        Run a backtesting end-to-end
        :return: None
        """
        data: Dict[str, Any] = {}
        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(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,
                                 pairs=pairs,
                                 ticker_interval=self.ticker_interval,
                                 refresh_pairs=self.config.get(
                                     'refresh_pairs', False),
                                 exchange=self.exchange,
                                 timerange=timerange,
                                 live=self.config.get('live', False))

        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 = {}

        min_date, max_date = history.get_timeframe(data)

        logger.info('Backtesting with data from %s up to %s (%s days)..',
                    min_date.isoformat(), max_date.isoformat(),
                    (max_date - min_date).days)

        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.strategy.tickerdata_to_dataframe(data)

            # 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),
                'start_date':
                min_date,
                'end_date':
                max_date,
            })

        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(133, '='))
            print(self._generate_text_table(data, results))

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

            print(' LEFT OPEN TRADES REPORT '.center(133, '='))
            print(
                self._generate_text_table(data,
                                          results.loc[results.open_at_end],
                                          True))
            print()
        if len(all_results) > 1:
            # Print Strategy summary table
            print(' Strategy Summary '.center(133, '='))
            print(self._generate_text_table_strategy(all_results))
            print('\nFor more details, please look at the detail tables above')
Exemplo n.º 9
0
if not pairs or 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
    if not pairs_file.exists():
        sys.exit(f'No pairs file found with path "{pairs_file}".')

    with pairs_file.open() as file:
        pairs = list(set(json.load(file)))

    pairs.sort()

timerange = TimeRange()
if args.days:
    time_since = arrow.utcnow().shift(days=-args.days).strftime("%Y%m%d")
    timerange = arguments.parse_timerange(f'{time_since}-')

logger.info(f'About to download pairs: {pairs}, intervals: {timeframes} to {dl_path}')

pairs_not_available = []

try:
    # Init exchange
    exchange = Exchange(config)

    for pair in pairs:
        if pair not in exchange._api.markets:
            pairs_not_available.append(pair)
            logger.info(f"Skipping pair {pair}...")
            continue
        for ticker_interval in timeframes:
Exemplo n.º 10
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=Path(self.config['datadir'])
                         if self.config.get('datadir') else None,
                         pairs=self.config['exchange']['pair_whitelist'],
                         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

        min_date, max_date = get_timeframe(data)

        logger.info('Hyperopting with data from %s up to %s (%s days)..',
                    min_date.isoformat(), max_date.isoformat(),
                    (max_date - min_date).days)

        self.strategy.advise_indicators = \
            self.custom_hyperopt.populate_indicators  # type: ignore

        preprocessed = self.strategy.tickerdata_to_dataframe(data)

        dump(preprocessed, TICKERDATA_PICKLE)

        # We don't need exchange instance anymore while running hyperopt
        self.exchange = None  # type: ignore

        self.load_previous_results()

        cpus = cpu_count()
        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}')

        opt = self.get_optimizer(config_jobs)
        try:
            with Parallel(n_jobs=config_jobs) as parallel:
                jobs = parallel._effective_n_jobs()
                logger.info(
                    f'Effective number of parallel workers used: {jobs}')
                EVALS = max(self.total_tries // jobs, 1)
                for i in range(EVALS):
                    asked = opt.ask(n_points=jobs)
                    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(jobs):
                        current = i * jobs + j
                        self.log_results({
                            'loss': f_val[j]['loss'],
                            'current_tries': current,
                            'initial_point': current < INITIAL_POINTS,
                            'total_tries': self.total_tries,
                            'result': f_val[j]['result'],
                        })
                        logger.debug(f"Optimizer params: {f_val[j]['params']}")
                    for j in range(jobs):
                        logger.debug(
                            f"Optimizer state: Xi: {opt.Xi[-j-1]}, yi: {opt.yi[-j-1]}"
                        )
        except KeyboardInterrupt:
            print('User interrupted..')

        self.save_trials()
        self.log_trials_result()