From f43ba44b159c6ed846492d5f5842a4946ebadbb7 Mon Sep 17 00:00:00 2001 From: Samuel Husso Date: Tue, 24 Oct 2017 07:58:42 +0300 Subject: [PATCH 1/2] refactor backtesting to its own method as we use it also in hyperopt --- freqtrade/tests/test_backtesting.py | 19 +++++++++++-------- freqtrade/tests/test_hyperopt.py | 25 ++++++++++++++----------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/freqtrade/tests/test_backtesting.py b/freqtrade/tests/test_backtesting.py index d008c4ae6..3af459ee4 100644 --- a/freqtrade/tests/test_backtesting.py +++ b/freqtrade/tests/test_backtesting.py @@ -21,6 +21,10 @@ def print_results(results): results.duration.mean() * 5 )) +def print_pair_results(pair, results): + print('For currency {}:'.format(pair)) + print_results(results[results.currency == pair]) + @pytest.fixture def pairs(): return ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay', @@ -38,9 +42,7 @@ def conf(): "stoploss": -0.40 } - -@pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") -def test_backtest(conf, pairs, mocker): +def backtest(conf, pairs, mocker): trades = [] mocker.patch.dict('freqtrade.main._CONF', conf) for pair in pairs: @@ -64,14 +66,15 @@ def test_backtest(conf, pairs, mocker): trades.append((pair, current_profit, index2 - index)) break - labels = ['currency', 'profit', 'duration'] results = DataFrame.from_records(trades, columns=labels) + return results + +@pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") +def test_backtest(conf, pairs, mocker, report=True): + results = backtest(conf, pairs, mocker) print('====================== BACKTESTING REPORT ================================') - - for pair in pairs: - print('For currency {}:'.format(pair)) - print_results(results[results.currency == pair]) + [print_pair_results(pair, results) for pair in pairs] print('TOTAL OVER ALL TRADES:') print_results(results) diff --git a/freqtrade/tests/test_hyperopt.py b/freqtrade/tests/test_hyperopt.py index 167e0d9ab..c09901f88 100644 --- a/freqtrade/tests/test_hyperopt.py +++ b/freqtrade/tests/test_hyperopt.py @@ -14,15 +14,9 @@ from freqtrade.analyze import analyze_ticker from freqtrade.main import should_sell from freqtrade.persistence import Trade -logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot +from freqtrade.tests.test_backtesting import backtest, print_results -def print_results(results): - print('Made {} buys. Average profit {:.2f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format( - len(results.index), - results.profit.mean() * 100.0, - results.profit.sum(), - results.duration.mean() * 5 - )) +logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot @pytest.fixture def pairs(): @@ -42,7 +36,7 @@ def conf(): } -def backtest(conf, pairs, mocker, buy_strategy): +def backtest2(conf, pairs, mocker, buy_strategy): trades = [] mocker.patch.dict('freqtrade.main._CONF', conf) for pair in pairs: @@ -121,8 +115,18 @@ def buy_strategy_generator(params): @pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") def test_hyperopt(conf, pairs, mocker): + # def optimizer(params): + # return backtest2(conf, pairs, mocker, buy_strategy_generator(params)) + def optimizer(params): - return backtest(conf, pairs, mocker, buy_strategy_generator(params)) + buy_strategy = buy_strategy_generator(params) + mocker.patch('freqtrade.analyze.populate_buy_trend', side_effect=buy_strategy) + results = backtest(conf, pairs, mocker) + + print_results(results) + if len(results.index) < 800: # require at least 800 trades + return 100000 # return large number to "ignore" this result + return results.duration.mean() ** 3 / results.profit.sum() / results.profit.mean() # the smaller the better space = { 'mfi': hp.choice('mfi', [ @@ -162,5 +166,4 @@ def test_hyperopt(conf, pairs, mocker): {'type': 'faststoch10'} ]), } - print('Best parameters {}'.format(fmin(fn=optimizer, space=space, algo=tpe.suggest, max_evals=40))) From 041e201713f23b9190e4c9dc2091b245f7cd80ac Mon Sep 17 00:00:00 2001 From: Samuel Husso Date: Wed, 25 Oct 2017 08:17:17 +0300 Subject: [PATCH 2/2] remove duplicated backtesting from hyperopt --- freqtrade/tests/test_hyperopt.py | 51 ++++---------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/freqtrade/tests/test_hyperopt.py b/freqtrade/tests/test_hyperopt.py index c09901f88..9a9df78e9 100644 --- a/freqtrade/tests/test_hyperopt.py +++ b/freqtrade/tests/test_hyperopt.py @@ -35,44 +35,6 @@ def conf(): "stoploss": -0.05 } - -def backtest2(conf, pairs, mocker, buy_strategy): - trades = [] - mocker.patch.dict('freqtrade.main._CONF', conf) - for pair in pairs: - with open('freqtrade/tests/testdata/'+pair+'.json') as data_file: - data = json.load(data_file) - - mocker.patch('freqtrade.analyze.get_ticker_history', return_value=data) - mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')) - mocker.patch('freqtrade.analyze.populate_buy_trend', side_effect=buy_strategy) - ticker = analyze_ticker(pair) - # for each buy point - for index, row in ticker[ticker.buy == 1].iterrows(): - trade = Trade( - open_rate=row['close'], - open_date=arrow.get(row['date']).datetime, - amount=1, - ) - # calculate win/lose forwards from buy point - for index2, row2 in ticker[index:].iterrows(): - if should_sell(trade, row2['close'], arrow.get(row2['date']).datetime): - current_profit = (row2['close'] - trade.open_rate) / trade.open_rate - - trades.append((pair, current_profit, index2 - index)) - break - - labels = ['currency', 'profit', 'duration'] - results = DataFrame.from_records(trades, columns=labels) - - print_results(results) - - # set the value below to suit your number concurrent trades so its realistic to 20days of data - TARGET_TRADES = 1200 - if results.profit.sum() == 0 or results.profit.mean() == 0: - return 49999999999 # avoid division by zero, return huge value to discard result - return abs(len(results.index) - 1200.1) / (results.profit.sum() ** 2) * results.duration.mean() # the smaller the better - def buy_strategy_generator(params): print(params) def populate_buy_trend(dataframe: DataFrame) -> DataFrame: @@ -114,19 +76,18 @@ def buy_strategy_generator(params): @pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set") def test_hyperopt(conf, pairs, mocker): - - # def optimizer(params): - # return backtest2(conf, pairs, mocker, buy_strategy_generator(params)) - def optimizer(params): buy_strategy = buy_strategy_generator(params) mocker.patch('freqtrade.analyze.populate_buy_trend', side_effect=buy_strategy) results = backtest(conf, pairs, mocker) print_results(results) - if len(results.index) < 800: # require at least 800 trades - return 100000 # return large number to "ignore" this result - return results.duration.mean() ** 3 / results.profit.sum() / results.profit.mean() # the smaller the better + + # set the value below to suit your number concurrent trades so its realistic to 20days of data + TARGET_TRADES = 1200 + if results.profit.sum() == 0 or results.profit.mean() == 0: + return 49999999999 # avoid division by zero, return huge value to discard result + return abs(len(results.index) - 1200.1) / (results.profit.sum() ** 2) * results.duration.mean() # the smaller the better space = { 'mfi': hp.choice('mfi', [