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)
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)
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)
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)
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
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'])
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
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
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'
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
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
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) )
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)
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
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()
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()
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'))
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 ) )
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]
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')
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 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)
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'))
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)
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))
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')))
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')