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)
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
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
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()
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, }
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"
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('-')
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')
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:
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()