def backtest(backtest_conf, backdata, mocker): trades = [] exchange._API = Bittrex({'key': '', 'secret': ''}) mocked_history = mocker.patch('freqtrade.analyze.get_ticker_history') mocker.patch.dict('freqtrade.main._CONF', backtest_conf) mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')) for pair, pair_data in backdata.items(): mocked_history.return_value = pair_data ticker = analyze_ticker(pair)[['close', 'date', 'buy']].copy() # for each buy point for row in ticker[ticker.buy == 1].itertuples(index=True): trade = Trade(open_rate=row.close, open_date=row.date, amount=1, fee=exchange.get_fee() * 2) # calculate win/lose forwards from buy point for row2 in ticker[row.Index:].itertuples(index=True): if should_sell(trade, row2.close, row2.date): current_profit = trade.calc_profit(row2.close) trades.append( (pair, current_profit, row2.Index - row.Index)) break labels = ['currency', 'profit', 'duration'] results = DataFrame.from_records(trades, columns=labels) return results
def backtest(backtest_conf, processed, mocker): trades = [] exchange._API = Bittrex({'key': '', 'secret': ''}) mocker.patch.dict('freqtrade.main._CONF', backtest_conf) for pair, pair_data in processed.items(): pair_data['buy'] = 0 pair_data['sell'] = 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point for row in ticker[ticker.buy == 1].itertuples(index=True): trade = Trade(open_rate=row.close, open_date=row.date, amount=backtest_conf['stake_amount'], fee=exchange.get_fee() * 2) # calculate win/lose forwards from buy point for row2 in ticker[row.Index:].itertuples(index=True): if min_roi_reached(trade, row2.close, row2.date) or row2.sell == 1: current_profit = trade.calc_profit(row2.close) trades.append( (pair, current_profit, row2.Index - row.Index)) break labels = ['currency', 'profit', 'duration'] return DataFrame.from_records(trades, columns=labels)
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_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 _init(self) -> None: """ Init objects required for backtesting :return: None """ self.analyze = Analyze(self.config) self.ticker_interval = self.analyze.strategy.ticker_interval self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe self.populate_buy_trend = self.analyze.populate_buy_trend self.populate_sell_trend = self.analyze.populate_sell_trend exchange._API = Bittrex({'key': '', 'secret': ''})
def test_backtest(backtest_conf, mocker): print('') exchange._API = Bittrex({'key': '', 'secret': ''}) # Load configuration file based on env variable conf_path = os.environ.get('BACKTEST_CONFIG') if conf_path: print('Using config: {} ...'.format(conf_path)) config = load_config(conf_path) else: config = backtest_conf # Parse ticker interval ticker_interval = int(os.environ.get('BACKTEST_TICKER_INTERVAL') or 5) print('Using ticker_interval: {} ...'.format(ticker_interval)) data = {} if os.environ.get('BACKTEST_LIVE'): print('Downloading data for all pairs in whitelist ...') for pair in config['exchange']['pair_whitelist']: data[pair] = exchange.get_ticker_history(pair, ticker_interval) else: print( 'Using local backtesting data (ignoring whitelist in given config)...' ) data = load_backtesting_data(ticker_interval) print('Using stake_currency: {} ...\nUsing stake_amount: {} ...'.format( config['stake_currency'], config['stake_amount'])) # Print timeframe min_date, max_date = get_timeframe(data) print('Measuring data from {} up to {} ...'.format(min_date.isoformat(), max_date.isoformat())) # Execute backtest and print results results = backtest(config, preprocess(data), mocker) print( '====================== BACKTESTING REPORT ======================================\n\n' 'NOTE: This Report doesn\'t respect the limits of max_open_trades, \n' ' so the projected values should be taken with a grain of salt.\n' ) print(generate_text_table(data, results, config['stake_currency']))
ax1.plot(dataframe.index.values, dataframe['blower'], '-.', label='BB low') ax1.plot(dataframe.index.values, dataframe['buy_price'], 'bo', label='buy') ax1.legend() ax2.plot(dataframe.index.values, dataframe['adx'], label='ADX') ax2.plot(dataframe.index.values, dataframe['mfi'], label='MFI') # ax2.plot(dataframe.index.values, [25] * len(dataframe.index.values)) ax2.legend() ax3.plot(dataframe.index.values, dataframe['fastk'], label='k') ax3.plot(dataframe.index.values, dataframe['fastd'], label='d') ax3.plot(dataframe.index.values, [20] * len(dataframe.index.values)) ax3.legend() # Fine-tune figure; make subplots close to each other and hide x ticks for # all but bottom plot. fig.subplots_adjust(hspace=0) plt.setp([a.get_xticklabels() for a in fig.axes[:-1]], visible=False) plt.show() if __name__ == '__main__': # Install PYQT5==5.9 manually if you want to test this helper function while True: exchange.EXCHANGE = Bittrex({'key': '', 'secret': ''}) test_pair = 'BTC_ETH' # for pair in ['BTC_ANT', 'BTC_ETH', 'BTC_GNT', 'BTC_ETC']: # get_buy_signal(pair) plot_dataframe(analyze_ticker(test_pair), test_pair) time.sleep(60)
def test_hyperopt(backtest_conf, mocker): mocked_buy_trend = mocker.patch('freqtrade.tests.test_backtesting.populate_buy_trend') backdata = load_backtesting_data() processed = preprocess(backdata) exchange._API = Bittrex({'key': '', 'secret': ''}) def optimizer(params): mocked_buy_trend.side_effect = buy_strategy_generator(params) results = backtest(backtest_conf, processed, mocker) result = format_results(results) total_profit = results.profit.sum() * 1000 trade_count = len(results.index) trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2) profit_loss = max(0, 1 - total_profit / 10000) # max profit 10000 # pylint: disable=W0603 global current_tries current_tries += 1 print('{:5d}/{}: {}'.format(current_tries, TOTAL_TRIES, result)) return { 'loss': trade_loss + profit_loss, 'status': STATUS_OK, 'result': result } space = { 'mfi': hp.choice('mfi', [ {'enabled': False}, {'enabled': True, 'value': hp.quniform('mfi-value', 5, 25, 1)} ]), 'fastd': hp.choice('fastd', [ {'enabled': False}, {'enabled': True, 'value': hp.quniform('fastd-value', 10, 50, 1)} ]), 'adx': hp.choice('adx', [ {'enabled': False}, {'enabled': True, 'value': hp.quniform('adx-value', 15, 50, 1)} ]), 'rsi': hp.choice('rsi', [ {'enabled': False}, {'enabled': True, 'value': hp.quniform('rsi-value', 20, 40, 1)} ]), 'uptrend_long_ema': hp.choice('uptrend_long_ema', [ {'enabled': False}, {'enabled': True} ]), 'uptrend_short_ema': hp.choice('uptrend_short_ema', [ {'enabled': False}, {'enabled': True} ]), 'over_sar': hp.choice('over_sar', [ {'enabled': False}, {'enabled': True} ]), 'green_candle': hp.choice('green_candle', [ {'enabled': False}, {'enabled': True} ]), 'uptrend_sma': hp.choice('uptrend_sma', [ {'enabled': False}, {'enabled': True} ]), 'trigger': hp.choice('trigger', [ {'type': 'lower_bb'}, {'type': 'faststoch10'}, {'type': 'ao_cross_zero'}, {'type': 'ema5_cross_ema10'}, {'type': 'macd_cross_signal'}, {'type': 'sar_reversal'}, {'type': 'stochf_cross'}, {'type': 'ht_sine'}, ]), } trials = Trials() best = fmin(fn=optimizer, space=space, algo=tpe.suggest, max_evals=TOTAL_TRIES, trials=trials) print('\n\n\n\n==================== HYPEROPT BACKTESTING REPORT ==============================') print('Best parameters {}'.format(best)) newlist = sorted(trials.results, key=itemgetter('loss')) print('Result: {}'.format(newlist[0]['result']))
#!/usr/bin/env python3 """This script generate json data from bittrex""" import json from os import path from freqtrade import exchange from freqtrade.exchange import Bittrex PAIRS = [ 'BTC_BCC', 'BTC_ETH', 'BTC_MER', 'BTC_POWR', 'BTC_ETC', 'BTC_OK', 'BTC_NEO', 'BTC_EMC2', 'BTC_DASH', 'BTC_LSK', 'BTC_LTC', 'BTC_XZC', 'BTC_OMG', 'BTC_STRAT', 'BTC_XRP', 'BTC_QTUM', 'BTC_WAVES', 'BTC_VTC', 'BTC_XLM', 'BTC_MCO' ] TICKER_INTERVAL = 5 # ticker interval in minutes (currently implemented: 1 and 5) OUTPUT_DIR = path.dirname(path.realpath(__file__)) # Init Bittrex exchange exchange._API = Bittrex({'key': '', 'secret': ''}) for pair in PAIRS: data = exchange.get_ticker_history(pair, TICKER_INTERVAL) filename = path.join(OUTPUT_DIR, '{}-{}.json'.format( pair, TICKER_INTERVAL, )) with open(filename, 'w') as fp: json.dump(data, fp)
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 backtest(stake_amount: float, processed: Dict[str, DataFrame], max_open_trades: int = 0, realistic: bool = True, sell_profit_only: bool = False, stoploss: int = -1.00, use_sell_signal: bool = False) -> DataFrame: """ Implements backtesting functionality :param stake_amount: btc amount to use for each trade :param processed: a processed dictionary with format {pair, data} :param max_open_trades: maximum number of concurrent trades (default: 0, disabled) :param realistic: do we try to simulate realistic trades? (default: True) :return: DataFrame """ trades = [] trade_count_lock: dict = {} exchange._API = Bittrex({'key': '', 'secret': ''}) for pair, pair_data in processed.items(): pair_data['buy'], pair_data['sell'] = 0, 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point lock_pair_until = None buy_subset = ticker[ticker.buy == 1][[ 'buy', 'open', 'close', 'date', 'sell' ]] for row in buy_subset.itertuples(index=True): if realistic: if lock_pair_until is not None and row.Index <= lock_pair_until: continue if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: continue if max_open_trades > 0: # Increase lock trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 trade = Trade(open_rate=row.close, open_date=row.date, stake_amount=stake_amount, amount=stake_amount / row.open, fee=exchange.get_fee()) # calculate win/lose forwards from buy point sell_subset = ticker[row.Index + 1:][['close', 'date', 'sell']] for row2 in sell_subset.itertuples(index=True): if max_open_trades > 0: # Increase trade_count_lock for every iteration trade_count_lock[row2.date] = trade_count_lock.get( row2.date, 0) + 1 current_profit_percent = trade.calc_profit_percent( rate=row2.close) if (sell_profit_only and current_profit_percent < 0): continue if min_roi_reached(trade, row2.close, row2.date) or \ (row2.sell == 1 and use_sell_signal) or \ current_profit_percent <= stoploss: current_profit_btc = trade.calc_profit(rate=row2.close) lock_pair_until = row2.Index trades.append( (pair, current_profit_percent, current_profit_btc, row2.Index - row.Index, current_profit_btc > 0, current_profit_btc < 0)) break labels = [ 'currency', 'profit_percent', 'profit_BTC', 'duration', 'profit', 'loss' ] return DataFrame.from_records(trades, columns=labels)
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))