From e0fda7a5ddac9624276256343975a5ba17a9d0d4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Jul 2018 21:38:49 +0200 Subject: [PATCH 01/24] Add tests validating backtest details --- .../tests/optimize/test_backtest_detail.py | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 freqtrade/tests/optimize/test_backtest_detail.py diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py new file mode 100644 index 000000000..fb88fe2a7 --- /dev/null +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -0,0 +1,77 @@ +# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument +import logging +from unittest.mock import MagicMock + +import pandas as pd +import pytest +from arrow import get as getdate + +from freqtrade.optimize.backtesting import Backtesting +from freqtrade.tests.conftest import patch_exchange, log_has + + +columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] +data_profit = pd.DataFrame([[getdate('2018-07-08 18:00:00').datetime, + 0.0009910, 0.001011, 0.00098618, 0.001000, 47027.0, 1, 0], + [getdate('2018-07-08 19:00:00').datetime, + 0.001000, 0.001010, 0.0009900, 0.0009900, 87116.0, 0, 0], + [getdate('2018-07-08 20:00:00').datetime, + 0.0009900, 0.001011, 0.00091618, 0.0009900, 58539.0, 0, 0], + [getdate('2018-07-08 21:00:00').datetime, + 0.001000, 0.001011, 0.00098618, 0.001100, 37498.0, 0, 1], + [getdate('2018-07-08 22:00:00').datetime, + 0.001000, 0.001011, 0.00098618, 0.0009900, 59792.0, 0, 0]], + columns=columns) + +data_loss = pd.DataFrame([[getdate('2018-07-08 18:00:00').datetime, + 0.0009910, 0.001011, 0.00098618, 0.001000, 47027.0, 1, 0], + [getdate('2018-07-08 19:00:00').datetime, + 0.001000, 0.001010, 0.0009900, 0.001000, 87116.0, 0, 0], + [getdate('2018-07-08 20:00:00').datetime, + 0.001000, 0.001011, 0.0010618, 0.00091618, 58539.0, 0, 0], + [getdate('2018-07-08 21:00:00').datetime, + 0.001000, 0.001011, 0.00098618, 0.00091618, 37498.0, 0, 0], + [getdate('2018-07-08 22:00:00').datetime, + 0.001000, 0.001011, 0.00098618, 0.00091618, 59792.0, 0, 0]], + columns=columns) + + +@pytest.mark.parametrize("data, stoploss, tradecount, profit_perc, sl", [ + (data_profit, -0.01, 1, 0.10557, False), # should be stoploss - drops 8% + # (data_profit, -0.10, 1, 0.10557, True), # win + (data_loss, -0.05, 1, -0.08839, True), # Stoploss ... + ]) +def test_backtest_results(default_conf, fee, mocker, caplog, + data, stoploss, tradecount, profit_perc, sl) -> None: + """ + run functional tests + """ + default_conf["stoploss"] = stoploss + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + mocker.patch('freqtrade.analyze.Analyze.populate_sell_trend', MagicMock(return_value=data)) + mocker.patch('freqtrade.analyze.Analyze.populate_buy_trend', MagicMock(return_value=data)) + patch_exchange(mocker) + + backtesting = Backtesting(default_conf) + caplog.set_level(logging.DEBUG) + + pair = 'UNITTEST/BTC' + # Dummy data as we mock the analyze functions + data_processed = {pair: pd.DataFrame()} + results = backtesting.backtest( + { + 'stake_amount': default_conf['stake_amount'], + 'processed': data_processed, + 'max_open_trades': 10, + 'realistic': True + } + ) + print(results.T) + + assert len(results) == tradecount + assert round(results["profit_percent"].sum(), 5) == profit_perc + if sl: + assert log_has("Stop loss hit.", caplog.record_tuples) + else: + + assert not log_has("Stop loss hit.", caplog.record_tuples) From b8f78cb1878ef4fff90353d63305a0dc56be19dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Jul 2018 21:08:44 +0200 Subject: [PATCH 02/24] Refactor tests, implement @creslinux's data --- .../tests/optimize/test_backtest_detail.py | 196 ++++++++++++++---- 1 file changed, 159 insertions(+), 37 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index fb88fe2a7..5023f4b24 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -1,55 +1,177 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import logging from unittest.mock import MagicMock +from typing import NamedTuple -import pandas as pd +from pandas import DataFrame import pytest from arrow import get as getdate + from freqtrade.optimize.backtesting import Backtesting from freqtrade.tests.conftest import patch_exchange, log_has +class BTContainer(NamedTuple): + """ + NamedTuple Defining BacktestResults inputs. + """ + data: DataFrame + stop_loss: float + roi: float + trades: int + profit_perc: float + sl: float + + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] -data_profit = pd.DataFrame([[getdate('2018-07-08 18:00:00').datetime, - 0.0009910, 0.001011, 0.00098618, 0.001000, 47027.0, 1, 0], - [getdate('2018-07-08 19:00:00').datetime, - 0.001000, 0.001010, 0.0009900, 0.0009900, 87116.0, 0, 0], - [getdate('2018-07-08 20:00:00').datetime, - 0.0009900, 0.001011, 0.00091618, 0.0009900, 58539.0, 0, 0], - [getdate('2018-07-08 21:00:00').datetime, - 0.001000, 0.001011, 0.00098618, 0.001100, 37498.0, 0, 1], - [getdate('2018-07-08 22:00:00').datetime, - 0.001000, 0.001011, 0.00098618, 0.0009900, 59792.0, 0, 0]], - columns=columns) +data_profit = DataFrame([ + [getdate('2018-07-08 18:00:00').datetime, 0.0009910, + 0.001011, 0.00098618, 0.001000, 12345, 1, 0], + [getdate('2018-07-08 19:00:00').datetime, 0.001000, + 0.001010, 0.0009900, 0.0009900, 12345, 0, 0], + [getdate('2018-07-08 20:00:00').datetime, 0.0009900, + 0.001011, 0.00091618, 0.0009900, 12345, 0, 0], + [getdate('2018-07-08 21:00:00').datetime, 0.001000, + 0.001011, 0.00098618, 0.001100, 12345, 0, 1], + [getdate('2018-07-08 22:00:00').datetime, 0.001000, + 0.001011, 0.00098618, 0.0009900, 12345, 0, 0] +], columns=columns) -data_loss = pd.DataFrame([[getdate('2018-07-08 18:00:00').datetime, - 0.0009910, 0.001011, 0.00098618, 0.001000, 47027.0, 1, 0], - [getdate('2018-07-08 19:00:00').datetime, - 0.001000, 0.001010, 0.0009900, 0.001000, 87116.0, 0, 0], - [getdate('2018-07-08 20:00:00').datetime, - 0.001000, 0.001011, 0.0010618, 0.00091618, 58539.0, 0, 0], - [getdate('2018-07-08 21:00:00').datetime, - 0.001000, 0.001011, 0.00098618, 0.00091618, 37498.0, 0, 0], - [getdate('2018-07-08 22:00:00').datetime, - 0.001000, 0.001011, 0.00098618, 0.00091618, 59792.0, 0, 0]], - columns=columns) +tc_profit1 = BTContainer(data=data_profit, stop_loss=-0.01, roi=1, trades=1, + profit_perc=0.10557, sl=False) # should be stoploss - drops 8% +tc_profit2 = BTContainer(data=data_profit, stop_loss=-0.10, roi=1, + trades=1, profit_perc=0.10557, sl=True) -@pytest.mark.parametrize("data, stoploss, tradecount, profit_perc, sl", [ - (data_profit, -0.01, 1, 0.10557, False), # should be stoploss - drops 8% - # (data_profit, -0.10, 1, 0.10557, True), # win - (data_loss, -0.05, 1, -0.08839, True), # Stoploss ... - ]) -def test_backtest_results(default_conf, fee, mocker, caplog, - data, stoploss, tradecount, profit_perc, sl) -> None: +tc_loss0 = BTContainer(data=DataFrame([ + [getdate('2018-07-08 18:00:00').datetime, 0.0009910, + 0.001011, 0.00098618, 0.001000, 12345, 1, 0], + [getdate('2018-07-08 19:00:00').datetime, 0.001000, + 0.001010, 0.0009900, 0.001000, 12345, 0, 0], + [getdate('2018-07-08 20:00:00').datetime, 0.001000, + 0.001011, 0.0010618, 0.00091618, 12345, 0, 0], + [getdate('2018-07-08 21:00:00').datetime, 0.001000, + 0.001011, 0.00098618, 0.00091618, 12345, 0, 0], + [getdate('2018-07-08 22:00:00').datetime, 0.001000, + 0.001011, 0.00098618, 0.00091618, 12345, 0, 0] +], columns=columns), + stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sl=True) + + +# Test 1 Minus 8% Close +# Candle Data for test 1 – close at -8% (9200) +# Test with Stop-loss at 1% +tc1 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9960, 9955, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9200, 9200, 12345, 0, 0] +], columns=columns), + stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.07999, sl=True) + +# Test 2 Minus 4% Low, minus 1% close +# Candle Data for test 2 +# Test with Stop-Loss at 3% +tc2 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9600, 9925, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] +], columns=columns), stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.00999, sl=False) # + + +# Test 3 Candle drops 4%, Recovers 1%. +# Entry Criteria Met +# Candle drops 20% +# Candle Data for test 3 +# Test with Stop-Loss at 2% +tc3 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9600, 9950, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 1, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] +], columns=columns), stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.19999, sl=True) # + + +# Test 4 Minus 3% / recovery +15% +# Candle Data for test 4 – Candle drops 3% Closed 15% up +# Test with Stop-loss at 2% ROI 6% + +tc4 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 11500, 9700, 11500, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] +], columns=columns), + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.141, sl=True) + +# Test 5 / Drops 0.5% Closes +20% +# Candle Data for test 5 +# Set stop-loss at 1% ROI 3% +tc5 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 12000, 9950, 12000, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] +], columns=columns), + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.177, sl=True) + +# Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve +# Candle Data for test 6 +# Set stop-loss at 2% ROI at 5% +tc6 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9700, 10100, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] +], columns=columns), stop_loss=-0.02, roi=0.05, + trades=1, profit_perc=-0.025, sl=False) + +# Test 7 - 6% Positive / 1% Negative / Close 1% Positve +# Candle Data for test 7 +# Set stop-loss at 2% ROI at 3% + +tc7 = BTContainer(data=DataFrame([ + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9900, 10100, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] +], columns=columns), stop_loss=-0.02, roi=0.03, + trades=1, profit_perc=-0.025, sl=False) + +TESTS = [ + # tc_profit1, + # tc_profit2, + tc_loss0, + tc1, + tc2, + tc3, + tc4, + tc5, + tc6, + tc7, +] + + +@pytest.mark.parametrize("data", TESTS) +def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ run functional tests """ - default_conf["stoploss"] = stoploss + default_conf["stoploss"] = data.stop_loss + default_conf["minimal_roi"] = {"0": data.roi} mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch('freqtrade.analyze.Analyze.populate_sell_trend', MagicMock(return_value=data)) - mocker.patch('freqtrade.analyze.Analyze.populate_buy_trend', MagicMock(return_value=data)) + mocker.patch.multiple('freqtrade.analyze.Analyze', + populate_sell_trend=MagicMock(return_value=data.data), + populate_buy_trend=MagicMock(return_value=data.data)) patch_exchange(mocker) backtesting = Backtesting(default_conf) @@ -57,7 +179,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, pair = 'UNITTEST/BTC' # Dummy data as we mock the analyze functions - data_processed = {pair: pd.DataFrame()} + data_processed = {pair: DataFrame()} results = backtesting.backtest( { 'stake_amount': default_conf['stake_amount'], @@ -68,9 +190,9 @@ def test_backtest_results(default_conf, fee, mocker, caplog, ) print(results.T) - assert len(results) == tradecount - assert round(results["profit_percent"].sum(), 5) == profit_perc - if sl: + assert len(results) == data.trades + assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3) + if data.sl: assert log_has("Stop loss hit.", caplog.record_tuples) else: From 30a6e684a66956c5e715076244d9d15daab223a0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 11 Jul 2018 07:18:52 +0200 Subject: [PATCH 03/24] update with new comments and new data for tc5 --- .../tests/optimize/test_backtest_detail.py | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 5023f4b24..f8fe2cd16 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -21,7 +21,8 @@ class BTContainer(NamedTuple): roi: float trades: int profit_perc: float - sl: float + sl: bool + remains: bool columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] @@ -39,9 +40,9 @@ data_profit = DataFrame([ ], columns=columns) tc_profit1 = BTContainer(data=data_profit, stop_loss=-0.01, roi=1, trades=1, - profit_perc=0.10557, sl=False) # should be stoploss - drops 8% + profit_perc=0.10557, sl=False, remains=False) # should be stoploss - drops 8% tc_profit2 = BTContainer(data=data_profit, stop_loss=-0.10, roi=1, - trades=1, profit_perc=0.10557, sl=True) + trades=1, profit_perc=0.10557, sl=True, remains=False) tc_loss0 = BTContainer(data=DataFrame([ @@ -56,31 +57,37 @@ tc_loss0 = BTContainer(data=DataFrame([ [getdate('2018-07-08 22:00:00').datetime, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0] ], columns=columns), - stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sl=True) + stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sl=True, remains=False) # Test 1 Minus 8% Close # Candle Data for test 1 – close at -8% (9200) # Test with Stop-loss at 1% +# TC1: Stop-Loss Triggered 1% loss tc1 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9200, 9200, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9960, 9955, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9200, 9200, 12345, 0, 0] + [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9990, 9900, 12345, 0, 0] ], columns=columns), - stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.07999, sl=True) + # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sl=True, remains=False) # should be + stop_loss=-0.01, roi=1, trades=1, profit_perc=0.071, sl=False, remains=True) # + # Test 2 Minus 4% Low, minus 1% close # Candle Data for test 2 # Test with Stop-Loss at 3% +# TC2: Stop-Loss Triggered 3% Loss tc2 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9600, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.00999, sl=False) # +], columns=columns), + # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sl=True, remains=False) #should be + stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.00999, sl=False, remains=True) # # Test 3 Candle drops 4%, Recovers 1%. @@ -88,19 +95,23 @@ tc2 = BTContainer(data=DataFrame([ # Candle drops 20% # Candle Data for test 3 # Test with Stop-Loss at 2% +# TC3: Trade-A: Stop-Loss Triggered 2% Loss +# Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9600, 9950, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 1, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] -], columns=columns), stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.19999, sl=True) # +], columns=columns), + # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sl=True, remains=False) #should be + stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.19999, sl=True, remains=False) # # Test 4 Minus 3% / recovery +15% # Candle Data for test 4 – Candle drops 3% Closed 15% up # Test with Stop-loss at 2% ROI 6% - +# TC4: Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 11500, 9700, 11500, 12345, 0, 0], @@ -108,49 +119,55 @@ tc4 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.141, sl=True) + # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sl=False, remains=False) #should be + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.141, sl=True, remains=False) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 # Set stop-loss at 1% ROI 3% +# TC5: ROI triggers 3% Gain tc5 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 12000, 9950, 12000, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 1, 0], + [getdate('2018-06-10 09:00:00').datetime, 9975, 10050, 9950, 9975, 12345, 0, 0], + [getdate('2018-06-10 10:00:00').datetime, 9950, 12000, 9950, 12000, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.177, sl=True) + # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sl=False, remains=False) #should be + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.197, sl=False, remains=False) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 # Set stop-loss at 2% ROI at 5% +# TC6: Stop-Loss triggers 2% Loss tc6 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9700, 10100, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), stop_loss=-0.02, roi=0.05, - trades=1, profit_perc=-0.025, sl=False) +], columns=columns), + # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sl=False, remains=False) #should be + stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.025, sl=False, remains=True) # # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 # Set stop-loss at 2% ROI at 3% - +# TC7: ROI Triggers 3% Gain tc7 = BTContainer(data=DataFrame([ [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9900, 10100, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), stop_loss=-0.02, roi=0.03, - trades=1, profit_perc=-0.025, sl=False) +], columns=columns), + # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.03, sl=False, remains=False) #should be + stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.025, sl=False, remains=True) # TESTS = [ # tc_profit1, # tc_profit2, - tc_loss0, + # tc_loss0, tc1, tc2, tc3, @@ -195,5 +212,12 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: if data.sl: assert log_has("Stop loss hit.", caplog.record_tuples) else: - assert not log_has("Stop loss hit.", caplog.record_tuples) + log_test = (f'Force_selling still open trade UNITTEST/BTC with ' + f'{results.iloc[-1].profit_percent} perc - {results.iloc[-1].profit_abs}') + if data.remains: + assert log_has(log_test, + caplog.record_tuples) + else: + assert not log_has(log_test, + caplog.record_tuples) From 409465ac8e7580a3bacd04b61cd0539a80d8c9d5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Jul 2018 21:32:54 +0200 Subject: [PATCH 04/24] adapt functional tests for new version after rebase --- .../tests/optimize/test_backtest_detail.py | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index f8fe2cd16..779687f10 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -9,6 +9,7 @@ from arrow import get as getdate from freqtrade.optimize.backtesting import Backtesting +from freqtrade.strategy.interface import SellType from freqtrade.tests.conftest import patch_exchange, log_has @@ -21,8 +22,7 @@ class BTContainer(NamedTuple): roi: float trades: int profit_perc: float - sl: bool - remains: bool + sell_r: SellType columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] @@ -40,9 +40,9 @@ data_profit = DataFrame([ ], columns=columns) tc_profit1 = BTContainer(data=data_profit, stop_loss=-0.01, roi=1, trades=1, - profit_perc=0.10557, sl=False, remains=False) # should be stoploss - drops 8% + profit_perc=0.10557, sell_r=SellType.STOP_LOSS) # should be stoploss - drops 8% tc_profit2 = BTContainer(data=data_profit, stop_loss=-0.10, roi=1, - trades=1, profit_perc=0.10557, sl=True, remains=False) + trades=1, profit_perc=0.10557, sell_r=SellType.STOP_LOSS) tc_loss0 = BTContainer(data=DataFrame([ @@ -57,7 +57,7 @@ tc_loss0 = BTContainer(data=DataFrame([ [getdate('2018-07-08 22:00:00').datetime, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0] ], columns=columns), - stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sl=True, remains=False) + stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sell_r=SellType.STOP_LOSS) # Test 1 Minus 8% Close @@ -71,8 +71,8 @@ tc1 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9990, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sl=True, remains=False) # should be - stop_loss=-0.01, roi=1, trades=1, profit_perc=0.071, sl=False, remains=True) # + # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be + stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # # Test 2 Minus 4% Low, minus 1% close @@ -86,8 +86,8 @@ tc2 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sl=True, remains=False) #should be - stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.00999, sl=False, remains=True) # + # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be + stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 3 Candle drops 4%, Recovers 1%. @@ -104,8 +104,8 @@ tc3 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sl=True, remains=False) #should be - stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.19999, sl=True, remains=False) # + # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sell_r=SellType.STOP_LOSS) #should be + stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 4 Minus 3% / recovery +15% @@ -119,8 +119,8 @@ tc4 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sl=False, remains=False) #should be - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.141, sl=True, remains=False) + # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 @@ -133,8 +133,8 @@ tc5 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sl=False, remains=False) #should be - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.197, sl=False, remains=False) + # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 @@ -147,8 +147,8 @@ tc6 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sl=False, remains=False) #should be - stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.025, sl=False, remains=True) # + # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 @@ -161,8 +161,8 @@ tc7 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.03, sl=False, remains=False) #should be - stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.025, sl=False, remains=True) # + # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # TESTS = [ # tc_profit1, @@ -186,12 +186,11 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = {"0": data.roi} mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch.multiple('freqtrade.analyze.Analyze', - populate_sell_trend=MagicMock(return_value=data.data), - populate_buy_trend=MagicMock(return_value=data.data)) patch_exchange(mocker) backtesting = Backtesting(default_conf) + backtesting.advise_buy = lambda a, m: data.data + backtesting.advise_sell = lambda a, m: data.data caplog.set_level(logging.DEBUG) pair = 'UNITTEST/BTC' @@ -202,20 +201,19 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: 'stake_amount': default_conf['stake_amount'], 'processed': data_processed, 'max_open_trades': 10, - 'realistic': True } ) print(results.T) assert len(results) == data.trades assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3) - if data.sl: + if data.sell_r == SellType.STOP_LOSS: assert log_has("Stop loss hit.", caplog.record_tuples) else: assert not log_has("Stop loss hit.", caplog.record_tuples) log_test = (f'Force_selling still open trade UNITTEST/BTC with ' f'{results.iloc[-1].profit_percent} perc - {results.iloc[-1].profit_abs}') - if data.remains: + if data.sell_r == SellType.FORCE_SELL: assert log_has(log_test, caplog.record_tuples) else: From a0e8bfbd77daa21d2af1dd83f865e7099f5869b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 16 Aug 2018 11:28:25 +0200 Subject: [PATCH 05/24] shift buy-signal to one earlier (backtest shifts it forward to avoid lookahead) --- .../tests/optimize/test_backtest_detail.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 779687f10..bb24f1602 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -65,7 +65,8 @@ tc_loss0 = BTContainer(data=DataFrame([ # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss tc1 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9200, 9200, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9960, 9955, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], @@ -80,7 +81,8 @@ tc1 = BTContainer(data=DataFrame([ # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss tc2 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9600, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], @@ -98,11 +100,13 @@ tc2 = BTContainer(data=DataFrame([ # TC3: Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9600, 9950, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 1, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] + [getdate('2018-06-10 11:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], + [getdate('2018-06-10 12:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], + [getdate('2018-06-10 13:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] ], columns=columns), # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sell_r=SellType.STOP_LOSS) #should be stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # @@ -113,7 +117,8 @@ tc3 = BTContainer(data=DataFrame([ # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 11500, 9700, 11500, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], @@ -127,7 +132,8 @@ tc4 = BTContainer(data=DataFrame([ # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain tc5 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 12000, 9950, 12000, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], @@ -141,7 +147,8 @@ tc5 = BTContainer(data=DataFrame([ # Set stop-loss at 2% ROI at 5% # TC6: Stop-Loss triggers 2% Loss tc6 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9700, 10100, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], @@ -155,7 +162,8 @@ tc6 = BTContainer(data=DataFrame([ # Set stop-loss at 2% ROI at 3% # TC7: ROI Triggers 3% Gain tc7 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], + [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9900, 10100, 12345, 0, 0], [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], From 233c442af97631bd525ef55559a4df9356258e29 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 16 Aug 2018 11:31:41 +0200 Subject: [PATCH 06/24] Adjust backtest so sell uses stop-loss or roi value as closerate --- freqtrade/optimize/backtesting.py | 18 +++++++--- freqtrade/strategy/interface.py | 12 ++++--- .../tests/optimize/test_backtest_detail.py | 33 ++++++++++--------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 961cfb092..a203fef2e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -206,12 +206,20 @@ class Backtesting(object): buy_signal = sell_row.buy sell = self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal, - sell_row.sell) + sell_row.sell, low=sell_row.low, high=sell_row.high) if sell.sell_flag: + if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): + # Set close_rate to stoploss + closerate = trade.stop_loss + elif sell.sell_type == (SellType.ROI): + # set close-rate to min-roi + closerate = trade.open_rate + trade.open_rate * self.strategy.minimal_roi[0] + else: + closerate = sell_row.open return BacktestResult(pair=pair, - profit_percent=trade.calc_profit_percent(rate=sell_row.open), - profit_abs=trade.calc_profit(rate=sell_row.open), + profit_percent=trade.calc_profit_percent(rate=closerate), + profit_abs=trade.calc_profit(rate=closerate), open_time=buy_row.date, close_time=sell_row.date, trade_duration=int(( @@ -220,7 +228,7 @@ class Backtesting(object): close_index=sell_row.Index, open_at_end=False, open_rate=buy_row.open, - close_rate=sell_row.open, + close_rate=closerate, sell_reason=sell.sell_type ) if partial_ticker: @@ -260,7 +268,7 @@ class Backtesting(object): position_stacking: do we allow position stacking? (default: False) :return: DataFrame """ - headers = ['date', 'buy', 'open', 'close', 'sell'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] processed = args['processed'] max_open_trades = args.get('max_open_trades', 0) position_stacking = args.get('position_stacking', False) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6afa4161b..21efa09d6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -203,18 +203,22 @@ class IStrategy(ABC): return buy, sell def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, - sell: bool) -> SellCheckTuple: + sell: bool, low: float=None, high: float=None) -> SellCheckTuple: """ This function evaluate if on the condition required to trigger a sell has been reached if the threshold is reached and updates the trade record. :return: True if trade should be sold, False otherwise """ - current_profit = trade.calc_profit_percent(rate) - stoplossflag = self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date, + # Set current rate to low for backtesting sell + current_rate = rate if not low else low + current_profit = trade.calc_profit_percent(current_rate) + stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, current_time=date, current_profit=current_profit) if stoplossflag.sell_flag: return stoplossflag - + # Set current rate to low for backtesting sell + current_rate = rate if not high else high + current_profit = trade.calc_profit_percent(current_rate) experimental = self.config.get('experimental', {}) if buy and experimental.get('ignore_roi_if_buy_signal', False): diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index bb24f1602..6430f6c1e 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -72,8 +72,8 @@ tc1 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9990, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be - stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be + # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # # Test 2 Minus 4% Low, minus 1% close @@ -88,8 +88,8 @@ tc2 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.007, sell_r=SellType.FORCE_SELL) # # Test 3 Candle drops 4%, Recovers 1%. @@ -108,8 +108,9 @@ tc3 = BTContainer(data=DataFrame([ [getdate('2018-06-10 12:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], [getdate('2018-06-10 13:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 4 Minus 3% / recovery +15% @@ -124,8 +125,8 @@ tc4 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 @@ -139,8 +140,8 @@ tc5 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 @@ -154,8 +155,8 @@ tc6 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 @@ -169,8 +170,8 @@ tc7 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # TESTS = [ # tc_profit1, @@ -193,7 +194,9 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = {"0": data.roi} - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + # mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + # TODO: don't Mock fee to for now + mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) backtesting = Backtesting(default_conf) From 98050ff594d36914488c6285faed8d3719785029 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 17 Aug 2018 07:07:50 +0200 Subject: [PATCH 07/24] use all min_roi entries --- freqtrade/optimize/backtesting.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index a203fef2e..9678b6d09 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -208,12 +208,17 @@ class Backtesting(object): sell = self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal, sell_row.sell, low=sell_row.low, high=sell_row.high) if sell.sell_flag: + trade_dur = int((sell_row.date - buy_row.date).total_seconds() // 60) if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): # Set close_rate to stoploss closerate = trade.stop_loss elif sell.sell_type == (SellType.ROI): + # get entry in min_roi >= to trade duration + roi_entry = max(list(filter(lambda x: trade_dur >= x, + list(self.strategy.minimal_roi.keys())))) # set close-rate to min-roi - closerate = trade.open_rate + trade.open_rate * self.strategy.minimal_roi[0] + closerate = trade.open_rate + trade.open_rate * \ + self.strategy.minimal_roi[roi_entry] else: closerate = sell_row.open @@ -222,8 +227,7 @@ class Backtesting(object): profit_abs=trade.calc_profit(rate=closerate), open_time=buy_row.date, close_time=sell_row.date, - trade_duration=int(( - sell_row.date - buy_row.date).total_seconds() // 60), + trade_duration=trade_dur, open_index=buy_row.Index, close_index=sell_row.Index, open_at_end=False, From 6096f3ca47c74e5aa609720273f004c5f13a3da4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 29 Oct 2018 20:17:15 +0100 Subject: [PATCH 08/24] Simplify functional tests --- .../tests/optimize/test_backtest_detail.py | 175 +++++++++--------- 1 file changed, 85 insertions(+), 90 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 6430f6c1e..f2b97c744 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -1,11 +1,11 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import logging from unittest.mock import MagicMock -from typing import NamedTuple +from typing import NamedTuple, List from pandas import DataFrame import pytest -from arrow import get as getdate +import arrow from freqtrade.optimize.backtesting import Backtesting @@ -13,11 +13,15 @@ from freqtrade.strategy.interface import SellType from freqtrade.tests.conftest import patch_exchange, log_has +ticker_start_time = arrow.get(2018, 10, 3) +ticker_interval_in_minute = 60 + + class BTContainer(NamedTuple): """ NamedTuple Defining BacktestResults inputs. """ - data: DataFrame + data: List[float] stop_loss: float roi: float trades: int @@ -25,19 +29,24 @@ class BTContainer(NamedTuple): sell_r: SellType -columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] -data_profit = DataFrame([ - [getdate('2018-07-08 18:00:00').datetime, 0.0009910, - 0.001011, 0.00098618, 0.001000, 12345, 1, 0], - [getdate('2018-07-08 19:00:00').datetime, 0.001000, - 0.001010, 0.0009900, 0.0009900, 12345, 0, 0], - [getdate('2018-07-08 20:00:00').datetime, 0.0009900, - 0.001011, 0.00091618, 0.0009900, 12345, 0, 0], - [getdate('2018-07-08 21:00:00').datetime, 0.001000, - 0.001011, 0.00098618, 0.001100, 12345, 0, 1], - [getdate('2018-07-08 22:00:00').datetime, 0.001000, - 0.001011, 0.00098618, 0.0009900, 12345, 0, 0] -], columns=columns) +def _build_dataframe(ticker_with_signals): + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + + frame = DataFrame.from_records(ticker_with_signals, columns=columns) + frame['date'] = frame['date'].apply(lambda x: ticker_start_time.shift( + minutes=(x * ticker_interval_in_minute)).datetime) + # Ensure floats are in place + for column in ['open', 'high', 'low', 'close', 'volume']: + frame[column] = frame[column].astype('float64') + return frame + + +data_profit = [ + [0, 0.0009910, 0.001011, 0.00098618, 0.001000, 12345, 1, 0], + [1, 0.001000, 0.001010, 0.0009900, 0.0009900, 12345, 0, 0], + [2, 0.0009900, 0.001011, 0.00091618, 0.0009900, 12345, 0, 0], + [3, 0.001000, 0.001011, 0.00098618, 0.001100, 12345, 0, 1], + [4, 0.001000, 0.001011, 0.00098618, 0.0009900, 12345, 0, 0]] tc_profit1 = BTContainer(data=data_profit, stop_loss=-0.01, roi=1, trades=1, profit_perc=0.10557, sell_r=SellType.STOP_LOSS) # should be stoploss - drops 8% @@ -45,18 +54,12 @@ tc_profit2 = BTContainer(data=data_profit, stop_loss=-0.10, roi=1, trades=1, profit_perc=0.10557, sell_r=SellType.STOP_LOSS) -tc_loss0 = BTContainer(data=DataFrame([ - [getdate('2018-07-08 18:00:00').datetime, 0.0009910, - 0.001011, 0.00098618, 0.001000, 12345, 1, 0], - [getdate('2018-07-08 19:00:00').datetime, 0.001000, - 0.001010, 0.0009900, 0.001000, 12345, 0, 0], - [getdate('2018-07-08 20:00:00').datetime, 0.001000, - 0.001011, 0.0010618, 0.00091618, 12345, 0, 0], - [getdate('2018-07-08 21:00:00').datetime, 0.001000, - 0.001011, 0.00098618, 0.00091618, 12345, 0, 0], - [getdate('2018-07-08 22:00:00').datetime, 0.001000, - 0.001011, 0.00098618, 0.00091618, 12345, 0, 0] -], columns=columns), +tc_loss0 = BTContainer(data=[ + [0, 0.0009910, 0.001011, 0.00098618, 0.001000, 12345, 1, 0], + [1, 0.001000, 0.001010, 0.0009900, 0.001000, 12345, 0, 0], + [2, 0.001000, 0.001011, 0.0010618, 0.00091618, 12345, 0, 0], + [3, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0], + [4, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0]], stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sell_r=SellType.STOP_LOSS) @@ -64,30 +67,27 @@ tc_loss0 = BTContainer(data=DataFrame([ # Candle Data for test 1 – close at -8% (9200) # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss -tc1 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9200, 9200, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9960, 9955, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9990, 9900, 12345, 0, 0] -], columns=columns), - stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be - # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # +tc1 = BTContainer(data=[ + [0, 10000.0, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 10025, 9200, 9200, 12345, 0, 0], + [3, 9950, 10000, 9960, 9955, 12345, 0, 0], + [4, 9955, 9975, 9955, 9990, 12345, 0, 0], + [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], + stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # Test 2 Minus 4% Low, minus 1% close # Candle Data for test 2 # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss -tc2 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9925, 9950, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9600, 9925, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), +tc2 = BTContainer(data=[ + [0, 10000, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 10025, 9925, 9950, 12345, 0, 0], + [3, 9950, 10000, 9600, 9925, 12345, 0, 0], + [4, 9925, 9975, 9875, 9900, 12345, 0, 0], + [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.007, sell_r=SellType.FORCE_SELL) # @@ -99,15 +99,14 @@ tc2 = BTContainer(data=DataFrame([ # Test with Stop-Loss at 2% # TC3: Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss -tc3 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10025, 9600, 9950, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 1, 0], - [getdate('2018-06-10 11:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], - [getdate('2018-06-10 13:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] -], columns=columns), +tc3 = BTContainer(data=[ + [0, 10000, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 10025, 9600, 9950, 12345, 0, 0], + [3, 9950, 10000, 9900, 9925, 12345, 1, 0], + [4, 9950, 10000, 9900, 9925, 12345, 0, 0], + [5, 9925, 9975, 8000, 8000, 12345, 0, 0], + [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) #should be # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # @@ -117,14 +116,13 @@ tc3 = BTContainer(data=DataFrame([ # Candle Data for test 4 – Candle drops 3% Closed 15% up # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss -tc4 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 11500, 9700, 11500, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), +tc4 = BTContainer(data=[ + [0, 10000, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 11500, 9700, 11500, 12345, 0, 0], + [3, 9950, 10000, 9900, 9925, 12345, 0, 0], + [4, 9925, 9975, 9875, 9900, 12345, 0, 0], + [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) @@ -132,14 +130,13 @@ tc4 = BTContainer(data=DataFrame([ # Candle Data for test 5 # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain -tc5 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9960, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 12000, 9950, 12000, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), +tc5 = BTContainer(data=[ + [0, 10000, 10050, 9960, 9975, 12345, 1, 0], + [1, 10000, 10050, 9960, 9975, 12345, 0, 0], + [2, 9975, 10050, 9950, 9975, 12345, 0, 0], + [3, 9950, 12000, 9950, 12000, 12345, 0, 0], + [4, 9925, 9975, 9945, 9900, 12345, 0, 0], + [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) @@ -147,14 +144,13 @@ tc5 = BTContainer(data=DataFrame([ # Candle Data for test 6 # Set stop-loss at 2% ROI at 5% # TC6: Stop-Loss triggers 2% Loss -tc6 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9700, 10100, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), +tc6 = BTContainer(data=[ + [0, 10000, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 10600, 9700, 10100, 12345, 0, 0], + [3, 9950, 10000, 9900, 9925, 12345, 0, 0], + [4, 9925, 9975, 9945, 9900, 12345, 0, 0], + [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # @@ -162,14 +158,13 @@ tc6 = BTContainer(data=DataFrame([ # Candle Data for test 7 # Set stop-loss at 2% ROI at 3% # TC7: ROI Triggers 3% Gain -tc7 = BTContainer(data=DataFrame([ - [getdate('2018-06-10 07:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 1, 0], - [getdate('2018-06-10 08:00:00').datetime, 10000, 10050, 9950, 9975, 12345, 0, 0], - [getdate('2018-06-10 09:00:00').datetime, 9975, 10600, 9900, 10100, 12345, 0, 0], - [getdate('2018-06-10 10:00:00').datetime, 9950, 10000, 9900, 9925, 12345, 0, 0], - [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], - [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] -], columns=columns), +tc7 = BTContainer(data=[ + [0, 10000, 10050, 9950, 9975, 12345, 1, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [2, 9975, 10600, 9900, 10100, 12345, 0, 0], + [3, 9950, 10000, 9900, 9925, 12345, 0, 0], + [4, 9925, 9975, 9945, 9900, 12345, 0, 0], + [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # @@ -198,10 +193,10 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: # TODO: don't Mock fee to for now mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) - + frame = _build_dataframe(data.data) backtesting = Backtesting(default_conf) - backtesting.advise_buy = lambda a, m: data.data - backtesting.advise_sell = lambda a, m: data.data + backtesting.advise_buy = lambda a, m: frame + backtesting.advise_sell = lambda a, m: frame caplog.set_level(logging.DEBUG) pair = 'UNITTEST/BTC' From 9065e79f531d0a6b7338b5f6afbc6927e455ec12 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 29 Oct 2018 20:33:27 +0100 Subject: [PATCH 09/24] Cleanup and add some comments on what's happening in the sample snippets --- .../tests/optimize/test_backtest_detail.py | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index f2b97c744..dda3be93f 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -69,8 +69,8 @@ tc_loss0 = BTContainer(data=[ # TC1: Stop-Loss Triggered 1% loss tc1 = BTContainer(data=[ [0, 10000.0, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], - [2, 9975, 10025, 9200, 9200, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) + [2, 9975, 10025, 9200, 9200, 12345, 0, 0], # Exit with stoploss hit [3, 9950, 10000, 9960, 9955, 12345, 0, 0], [4, 9955, 9975, 9955, 9990, 12345, 0, 0], [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], @@ -83,13 +83,12 @@ tc1 = BTContainer(data=[ # TC2: Stop-Loss Triggered 3% Loss tc2 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9925, 9950, 12345, 0, 0], - [3, 9950, 10000, 9600, 9925, 12345, 0, 0], + [3, 9950, 10000, 9600, 9925, 12345, 0, 0], # Exit with stoploss hit [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be - # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.007, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) # Test 3 Candle drops 4%, Recovers 1%. @@ -101,16 +100,13 @@ tc2 = BTContainer(data=[ # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9600, 9950, 12345, 0, 0], - [3, 9950, 10000, 9900, 9925, 12345, 1, 0], + [3, 9950, 10000, 9900, 9925, 12345, 1, 0], # enter trade 2 (signal on last candle) [4, 9950, 10000, 9900, 9925, 12345, 0, 0], [5, 9925, 9975, 8000, 8000, 12345, 0, 0], [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) #should be - # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # - + stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) # Test 4 Minus 3% / recovery +15% # Candle Data for test 4 – Candle drops 3% Closed 15% up @@ -118,13 +114,12 @@ tc3 = BTContainer(data=[ # TC4: Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 11500, 9700, 11500, 12345, 0, 0], [3, 9950, 10000, 9900, 9925, 12345, 0, 0], - [4, 9925, 9975, 9875, 9900, 12345, 0, 0], + [4, 9925, 9975, 9875, 9900, 12345, 0, 0], # Exit with stoploss hit [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 @@ -132,13 +127,12 @@ tc4 = BTContainer(data=[ # TC5: ROI triggers 3% Gain tc5 = BTContainer(data=[ [0, 10000, 10050, 9960, 9975, 12345, 1, 0], - [1, 10000, 10050, 9960, 9975, 12345, 0, 0], + [1, 10000, 10050, 9960, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10050, 9950, 9975, 12345, 0, 0], - [3, 9950, 12000, 9950, 12000, 12345, 0, 0], + [3, 9950, 12000, 9950, 12000, 12345, 0, 0], # ROI [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 @@ -146,13 +140,12 @@ tc5 = BTContainer(data=[ # TC6: Stop-Loss triggers 2% Loss tc6 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], - [2, 9975, 10600, 9700, 10100, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) + [2, 9975, 10600, 9700, 10100, 12345, 0, 0], # Exit with stoploss [3, 9950, 10000, 9900, 9925, 12345, 0, 0], [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 @@ -160,13 +153,12 @@ tc6 = BTContainer(data=[ # TC7: ROI Triggers 3% Gain tc7 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], - [2, 9975, 10600, 9900, 10100, 12345, 0, 0], + [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) + [2, 9975, 10600, 9900, 10100, 12345, 0, 0], # ROI [3, 9950, 10000, 9900, 9925, 12345, 0, 0], [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) TESTS = [ # tc_profit1, @@ -189,7 +181,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = {"0": data.roi} - # mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) # TODO: don't Mock fee to for now mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) From b383113d6cfbce19db1188a15e8af921bc201b63 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 19:33:32 +0100 Subject: [PATCH 10/24] Test open / close time - small refactorings --- .../tests/optimize/test_backtest_detail.py | 119 +++++++++--------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index dda3be93f..705a803cc 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -17,6 +17,12 @@ ticker_start_time = arrow.get(2018, 10, 3) ticker_interval_in_minute = 60 +class BTrade(NamedTuple): + sell_r: SellType + open_tick: int + close_tick: int + + class BTContainer(NamedTuple): """ NamedTuple Defining BacktestResults inputs. @@ -24,71 +30,56 @@ class BTContainer(NamedTuple): data: List[float] stop_loss: float roi: float - trades: int + trades: List[BTrade] profit_perc: float - sell_r: SellType + + +def _get_frame_time(offset): + return ticker_start_time.shift( + minutes=(offset * ticker_interval_in_minute)).datetime def _build_dataframe(ticker_with_signals): columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] frame = DataFrame.from_records(ticker_with_signals, columns=columns) - frame['date'] = frame['date'].apply(lambda x: ticker_start_time.shift( - minutes=(x * ticker_interval_in_minute)).datetime) + frame['date'] = frame['date'].apply(_get_frame_time) # Ensure floats are in place for column in ['open', 'high', 'low', 'close', 'volume']: frame[column] = frame[column].astype('float64') return frame -data_profit = [ - [0, 0.0009910, 0.001011, 0.00098618, 0.001000, 12345, 1, 0], - [1, 0.001000, 0.001010, 0.0009900, 0.0009900, 12345, 0, 0], - [2, 0.0009900, 0.001011, 0.00091618, 0.0009900, 12345, 0, 0], - [3, 0.001000, 0.001011, 0.00098618, 0.001100, 12345, 0, 1], - [4, 0.001000, 0.001011, 0.00098618, 0.0009900, 12345, 0, 0]] - -tc_profit1 = BTContainer(data=data_profit, stop_loss=-0.01, roi=1, trades=1, - profit_perc=0.10557, sell_r=SellType.STOP_LOSS) # should be stoploss - drops 8% -tc_profit2 = BTContainer(data=data_profit, stop_loss=-0.10, roi=1, - trades=1, profit_perc=0.10557, sell_r=SellType.STOP_LOSS) - - -tc_loss0 = BTContainer(data=[ - [0, 0.0009910, 0.001011, 0.00098618, 0.001000, 12345, 1, 0], - [1, 0.001000, 0.001010, 0.0009900, 0.001000, 12345, 0, 0], - [2, 0.001000, 0.001011, 0.0010618, 0.00091618, 12345, 0, 0], - [3, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0], - [4, 0.001000, 0.001011, 0.00098618, 0.00091618, 12345, 0, 0]], - stop_loss=-0.05, roi=1, trades=1, profit_perc=-0.08839, sell_r=SellType.STOP_LOSS) - - # Test 1 Minus 8% Close # Candle Data for test 1 – close at -8% (9200) # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss -tc1 = BTContainer(data=[ +tc0 = BTContainer(data=[ [0, 10000.0, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9200, 9200, 12345, 0, 0], # Exit with stoploss hit [3, 9950, 10000, 9960, 9955, 12345, 0, 0], [4, 9955, 9975, 9955, 9990, 12345, 0, 0], [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], - stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) + stop_loss=-0.01, roi=1, profit_perc=-0.01, + trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] +) # Test 2 Minus 4% Low, minus 1% close # Candle Data for test 2 # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss -tc2 = BTContainer(data=[ +tc1 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9925, 9950, 12345, 0, 0], [3, 9950, 10000, 9600, 9925, 12345, 0, 0], # Exit with stoploss hit [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) + stop_loss=-0.03, roi=1, profit_perc=-0.03, + trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=3)] + ) # Test 3 Candle drops 4%, Recovers 1%. @@ -98,79 +89,90 @@ tc2 = BTContainer(data=[ # Test with Stop-Loss at 2% # TC3: Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss -tc3 = BTContainer(data=[ +tc2 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9600, 9950, 12345, 0, 0], - [3, 9950, 10000, 9900, 9925, 12345, 1, 0], # enter trade 2 (signal on last candle) - [4, 9950, 10000, 9900, 9925, 12345, 0, 0], + [3, 9950, 10000, 9900, 9925, 12345, 1, 0], + [4, 9950, 10000, 9900, 9925, 12345, 0, 0], # enter trade 2 (signal on last candle) [5, 9925, 9975, 8000, 8000, 12345, 0, 0], [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) + stop_loss=-0.02, roi=1, profit_perc=-0.04, + trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2), + BTrade(sell_r=SellType.STOP_LOSS, open_tick=4, close_tick=5)] +) # Test 4 Minus 3% / recovery +15% # Candle Data for test 4 – Candle drops 3% Closed 15% up # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss -tc4 = BTContainer(data=[ +tc3 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 11500, 9700, 11500, 12345, 0, 0], [3, 9950, 10000, 9900, 9925, 12345, 0, 0], [4, 9925, 9975, 9875, 9900, 12345, 0, 0], # Exit with stoploss hit [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) + stop_loss=-0.02, roi=0.06, profit_perc=-0.02, + trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] +) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain -tc5 = BTContainer(data=[ +tc4 = BTContainer(data=[ [0, 10000, 10050, 9960, 9975, 12345, 1, 0], [1, 10000, 10050, 9960, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10050, 9950, 9975, 12345, 0, 0], [3, 9950, 12000, 9950, 12000, 12345, 0, 0], # ROI [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) + stop_loss=-0.01, roi=0.03, profit_perc=0.03, + trades=[BTrade(sell_r=SellType.ROI, open_tick=1, close_tick=3)] +) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 # Set stop-loss at 2% ROI at 5% # TC6: Stop-Loss triggers 2% Loss -tc6 = BTContainer(data=[ +tc5 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10600, 9700, 10100, 12345, 0, 0], # Exit with stoploss [3, 9950, 10000, 9900, 9925, 12345, 0, 0], [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) + stop_loss=-0.02, roi=0.05, profit_perc=-0.02, + trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] +) # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 # Set stop-loss at 2% ROI at 3% # TC7: ROI Triggers 3% Gain -tc7 = BTContainer(data=[ +tc6 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10600, 9900, 10100, 12345, 0, 0], # ROI [3, 9950, 10000, 9900, 9925, 12345, 0, 0], [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], - stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) + stop_loss=-0.02, roi=0.03, profit_perc=0.03, + trades=[BTrade(sell_r=SellType.ROI, open_tick=1, close_tick=2)] + ) TESTS = [ # tc_profit1, # tc_profit2, # tc_loss0, + tc0, tc1, tc2, tc3, tc4, tc5, tc6, - tc7, ] @@ -203,17 +205,22 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: ) print(results.T) - assert len(results) == data.trades + assert len(results) == len(data.trades) assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3) - if data.sell_r == SellType.STOP_LOSS: - assert log_has("Stop loss hit.", caplog.record_tuples) - else: - assert not log_has("Stop loss hit.", caplog.record_tuples) - log_test = (f'Force_selling still open trade UNITTEST/BTC with ' - f'{results.iloc[-1].profit_percent} perc - {results.iloc[-1].profit_abs}') - if data.sell_r == SellType.FORCE_SELL: - assert log_has(log_test, - caplog.record_tuples) - else: - assert not log_has(log_test, - caplog.record_tuples) + # if data.sell_r == SellType.STOP_LOSS: + # assert log_has("Stop loss hit.", caplog.record_tuples) + # else: + # assert not log_has("Stop loss hit.", caplog.record_tuples) + # log_test = (f'Force_selling still open trade UNITTEST/BTC with ' + # f'{results.iloc[-1].profit_percent} perc - {results.iloc[-1].profit_abs}') + # if data.sell_r == SellType.FORCE_SELL: + # assert log_has(log_test, + # caplog.record_tuples) + # else: + # assert not log_has(log_test, + # caplog.record_tuples) + for c, trade in enumerate(data.trades): + res = results.iloc[c] + assert res.sell_reason == trade.sell_r + assert res.open_time == _get_frame_time(trade.open_tick) + assert res.close_time == _get_frame_time(trade.close_tick) From fc3f8b436d1f1ba5da3332fd3be8ea72860e2655 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 19:36:19 +0100 Subject: [PATCH 11/24] some more cleanup --- .../tests/optimize/test_backtest_detail.py | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 705a803cc..e8b33d5f2 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -18,6 +18,9 @@ ticker_interval_in_minute = 60 class BTrade(NamedTuple): + """ + Minimalistic Trade result used for functional backtesting + """ sell_r: SellType open_tick: int close_tick: int @@ -25,7 +28,7 @@ class BTrade(NamedTuple): class BTContainer(NamedTuple): """ - NamedTuple Defining BacktestResults inputs. + Minimal BacktestContainer defining Backtest inputs and results. """ data: List[float] stop_loss: float @@ -50,14 +53,13 @@ def _build_dataframe(ticker_with_signals): return frame -# Test 1 Minus 8% Close -# Candle Data for test 1 – close at -8% (9200) +# Test 0 Minus 8% Close # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss tc0 = BTContainer(data=[ [0, 10000.0, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10025, 9200, 9200, 12345, 0, 0], # Exit with stoploss hit + [2, 9975, 10025, 9200, 9200, 12345, 0, 0], # exit with stoploss hit [3, 9950, 10000, 9960, 9955, 12345, 0, 0], [4, 9955, 9975, 9955, 9990, 12345, 0, 0], [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], @@ -66,15 +68,14 @@ tc0 = BTContainer(data=[ ) -# Test 2 Minus 4% Low, minus 1% close -# Candle Data for test 2 +# Test 1 Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss tc1 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) [2, 9975, 10025, 9925, 9950, 12345, 0, 0], - [3, 9950, 10000, 9600, 9925, 12345, 0, 0], # Exit with stoploss hit + [3, 9950, 10000, 9600, 9925, 12345, 0, 0], # exit with stoploss hit [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.03, roi=1, profit_perc=-0.03, @@ -92,10 +93,10 @@ tc1 = BTContainer(data=[ tc2 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10025, 9600, 9950, 12345, 0, 0], + [2, 9975, 10025, 9600, 9950, 12345, 0, 0], # exit with stoploss hit [3, 9950, 10000, 9900, 9925, 12345, 1, 0], [4, 9950, 10000, 9900, 9925, 12345, 0, 0], # enter trade 2 (signal on last candle) - [5, 9925, 9975, 8000, 8000, 12345, 0, 0], + [5, 9925, 9975, 8000, 8000, 12345, 0, 0], # exit with stoploss hit [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=1, profit_perc=-0.04, trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2), @@ -103,22 +104,21 @@ tc2 = BTContainer(data=[ ) # Test 4 Minus 3% / recovery +15% -# Candle Data for test 4 – Candle drops 3% Closed 15% up +# Candle Data for test 3 – Candle drops 3% Closed 15% up # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ [0, 10000, 10050, 9950, 9975, 12345, 1, 0], [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 11500, 9700, 11500, 12345, 0, 0], + [2, 9975, 11500, 9700, 11500, 12345, 0, 0], # Exit with stoploss hit [3, 9950, 10000, 9900, 9925, 12345, 0, 0], - [4, 9925, 9975, 9875, 9900, 12345, 0, 0], # Exit with stoploss hit + [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.06, profit_perc=-0.02, trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) -# Test 5 / Drops 0.5% Closes +20% -# Candle Data for test 5 +# Test 4 / Drops 0.5% Closes +20% # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain tc4 = BTContainer(data=[ @@ -163,9 +163,6 @@ tc6 = BTContainer(data=[ ) TESTS = [ - # tc_profit1, - # tc_profit2, - # tc_loss0, tc0, tc1, tc2, From 3679b0948a775a600dcad533acfaa03bbe7199a4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 19:37:45 +0100 Subject: [PATCH 12/24] cleanup interface --- freqtrade/strategy/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 21efa09d6..710a4e8f1 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -203,7 +203,7 @@ class IStrategy(ABC): return buy, sell def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, - sell: bool, low: float=None, high: float=None) -> SellCheckTuple: + sell: bool, low: float = None, high: float = None) -> SellCheckTuple: """ This function evaluate if on the condition required to trigger a sell has been reached if the threshold is reached and updates the trade record. @@ -212,8 +212,8 @@ class IStrategy(ABC): # Set current rate to low for backtesting sell current_rate = rate if not low else low current_profit = trade.calc_profit_percent(current_rate) - stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, current_time=date, - current_profit=current_profit) + stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, + current_time=date, current_profit=current_profit) if stoplossflag.sell_flag: return stoplossflag # Set current rate to low for backtesting sell From 9798e881cbf1db8f2921a0f9a44ce6c1c77cacbd Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 19:44:31 +0100 Subject: [PATCH 13/24] refactor sell_r to sell_reason --- .../tests/optimize/test_backtest_detail.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index e8b33d5f2..dc767210f 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -21,7 +21,7 @@ class BTrade(NamedTuple): """ Minimalistic Trade result used for functional backtesting """ - sell_r: SellType + sell_reason: SellType open_tick: int close_tick: int @@ -64,7 +64,7 @@ tc0 = BTContainer(data=[ [4, 9955, 9975, 9955, 9990, 12345, 0, 0], [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], stop_loss=-0.01, roi=1, profit_perc=-0.01, - trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] + trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -79,7 +79,7 @@ tc1 = BTContainer(data=[ [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.03, roi=1, profit_perc=-0.03, - trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=3)] + trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -99,8 +99,8 @@ tc2 = BTContainer(data=[ [5, 9925, 9975, 8000, 8000, 12345, 0, 0], # exit with stoploss hit [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=1, profit_perc=-0.04, - trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2), - BTrade(sell_r=SellType.STOP_LOSS, open_tick=4, close_tick=5)] + trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), + BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] ) # Test 4 Minus 3% / recovery +15% @@ -115,7 +115,7 @@ tc3 = BTContainer(data=[ [4, 9925, 9975, 9875, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.06, profit_perc=-0.02, - trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] + trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) # Test 4 / Drops 0.5% Closes +20% @@ -129,7 +129,7 @@ tc4 = BTContainer(data=[ [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.01, roi=0.03, profit_perc=0.03, - trades=[BTrade(sell_r=SellType.ROI, open_tick=1, close_tick=3)] + trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve @@ -144,7 +144,7 @@ tc5 = BTContainer(data=[ [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.05, profit_perc=-0.02, - trades=[BTrade(sell_r=SellType.STOP_LOSS, open_tick=1, close_tick=2)] + trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) # Test 7 - 6% Positive / 1% Negative / Close 1% Positve @@ -159,7 +159,7 @@ tc6 = BTContainer(data=[ [4, 9925, 9975, 9945, 9900, 12345, 0, 0], [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], stop_loss=-0.02, roi=0.03, profit_perc=0.03, - trades=[BTrade(sell_r=SellType.ROI, open_tick=1, close_tick=2)] + trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) TESTS = [ @@ -218,6 +218,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: # caplog.record_tuples) for c, trade in enumerate(data.trades): res = results.iloc[c] - assert res.sell_reason == trade.sell_r + assert res.sell_reason == trade.sell_reason assert res.open_time == _get_frame_time(trade.open_tick) assert res.close_time == _get_frame_time(trade.close_tick) From e442e22a20bdad7833c5ba6c402397cd3f2f5bbc Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 19:58:06 +0100 Subject: [PATCH 14/24] refactorign --- freqtrade/tests/optimize/test_backtest_detail.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index dc767210f..4b6bcd4ce 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -37,16 +37,16 @@ class BTContainer(NamedTuple): profit_perc: float -def _get_frame_time(offset): +def _get_frame_time_from_offset(offset): return ticker_start_time.shift( minutes=(offset * ticker_interval_in_minute)).datetime -def _build_dataframe(ticker_with_signals): +def _build_backtest_dataframe(ticker_with_signals): columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] frame = DataFrame.from_records(ticker_with_signals, columns=columns) - frame['date'] = frame['date'].apply(_get_frame_time) + frame['date'] = frame['date'].apply(_get_frame_time_from_offset) # Ensure floats are in place for column in ['open', 'high', 'low', 'close', 'volume']: frame[column] = frame[column].astype('float64') @@ -184,7 +184,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: # TODO: don't Mock fee to for now mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) - frame = _build_dataframe(data.data) + frame = _build_backtest_dataframe(data.data) backtesting = Backtesting(default_conf) backtesting.advise_buy = lambda a, m: frame backtesting.advise_sell = lambda a, m: frame @@ -219,5 +219,5 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: for c, trade in enumerate(data.trades): res = results.iloc[c] assert res.sell_reason == trade.sell_reason - assert res.open_time == _get_frame_time(trade.open_tick) - assert res.close_time == _get_frame_time(trade.close_tick) + assert res.open_time == _get_frame_time_from_offset(trade.open_tick) + assert res.close_time == _get_frame_time_from_offset(trade.close_tick) From 9e921d441011958ed06ae5953f09c36e71f763a9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:02:01 +0100 Subject: [PATCH 15/24] refactor General bt-utils out of detailed backtest file --- freqtrade/tests/optimize/__init__.py | 45 +++++++++++++++++ .../tests/optimize/test_backtest_detail.py | 48 ++----------------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/freqtrade/tests/optimize/__init__.py b/freqtrade/tests/optimize/__init__.py index e69de29bb..2b7222e88 100644 --- a/freqtrade/tests/optimize/__init__.py +++ b/freqtrade/tests/optimize/__init__.py @@ -0,0 +1,45 @@ +from typing import NamedTuple, List + +import arrow +from pandas import DataFrame + +from freqtrade.strategy.interface import SellType + +ticker_start_time = arrow.get(2018, 10, 3) +ticker_interval_in_minute = 60 + + +class BTrade(NamedTuple): + """ + Minimalistic Trade result used for functional backtesting + """ + sell_reason: SellType + open_tick: int + close_tick: int + + +class BTContainer(NamedTuple): + """ + Minimal BacktestContainer defining Backtest inputs and results. + """ + data: List[float] + stop_loss: float + roi: float + trades: List[BTrade] + profit_perc: float + + +def _get_frame_time_from_offset(offset): + return ticker_start_time.shift( + minutes=(offset * ticker_interval_in_minute)).datetime + + +def _build_backtest_dataframe(ticker_with_signals): + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + + frame = DataFrame.from_records(ticker_with_signals, columns=columns) + frame['date'] = frame['date'].apply(_get_frame_time_from_offset) + # Ensure floats are in place + for column in ['open', 'high', 'low', 'close', 'volume']: + frame[column] = frame[column].astype('float64') + return frame diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 4b6bcd4ce..323323683 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -1,56 +1,16 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import logging from unittest.mock import MagicMock -from typing import NamedTuple, List from pandas import DataFrame import pytest -import arrow from freqtrade.optimize.backtesting import Backtesting from freqtrade.strategy.interface import SellType -from freqtrade.tests.conftest import patch_exchange, log_has - - -ticker_start_time = arrow.get(2018, 10, 3) -ticker_interval_in_minute = 60 - - -class BTrade(NamedTuple): - """ - Minimalistic Trade result used for functional backtesting - """ - sell_reason: SellType - open_tick: int - close_tick: int - - -class BTContainer(NamedTuple): - """ - Minimal BacktestContainer defining Backtest inputs and results. - """ - data: List[float] - stop_loss: float - roi: float - trades: List[BTrade] - profit_perc: float - - -def _get_frame_time_from_offset(offset): - return ticker_start_time.shift( - minutes=(offset * ticker_interval_in_minute)).datetime - - -def _build_backtest_dataframe(ticker_with_signals): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] - - frame = DataFrame.from_records(ticker_with_signals, columns=columns) - frame['date'] = frame['date'].apply(_get_frame_time_from_offset) - # Ensure floats are in place - for column in ['open', 'high', 'low', 'close', 'volume']: - frame[column] = frame[column].astype('float64') - return frame +from freqtrade.tests.optimize import (BTrade, BTContainer, _build_backtest_dataframe, + _get_frame_time_from_offset) +from freqtrade.tests.conftest import patch_exchange # Test 0 Minus 8% Close @@ -180,8 +140,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = {"0": data.roi} - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - # TODO: don't Mock fee to for now mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) frame = _build_backtest_dataframe(data.data) From f96f0cdea7b665435240c4bef07a9f01ba31f425 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:02:31 +0100 Subject: [PATCH 16/24] Add additional comment --- freqtrade/optimize/backtesting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 9678b6d09..b5c883d4f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -208,7 +208,9 @@ class Backtesting(object): sell = self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal, sell_row.sell, low=sell_row.low, high=sell_row.high) if sell.sell_flag: + trade_dur = int((sell_row.date - buy_row.date).total_seconds() // 60) + # Special handling if high or low hit STOP_LOSS or ROI if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): # Set close_rate to stoploss closerate = trade.stop_loss From fe2c158e59668eb31f54ecc8fee485a0b805bc3c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:13:56 +0100 Subject: [PATCH 17/24] Adjust sell-rate to new backtesting (respects roi/stoploss) --- freqtrade/tests/optimize/test_backtesting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index fc08eba89..2d3d49ab0 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -580,7 +580,7 @@ def test_processed(default_conf, mocker) -> None: def test_backtest_pricecontours(default_conf, fee, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - tests = [['raise', 18], ['lower', 0], ['sine', 16]] + tests = [['raise', 18], ['lower', 0], ['sine', 19]] for [contour, numres] in tests: simple_backtest(default_conf, contour, numres, mocker) From 8c93760a6dda1eecdb4ec4276dea5cc04106eec2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:23:31 +0100 Subject: [PATCH 18/24] simplify some code --- freqtrade/optimize/backtesting.py | 2 +- freqtrade/strategy/interface.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index b5c883d4f..1f02b9793 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -217,7 +217,7 @@ class Backtesting(object): elif sell.sell_type == (SellType.ROI): # get entry in min_roi >= to trade duration roi_entry = max(list(filter(lambda x: trade_dur >= x, - list(self.strategy.minimal_roi.keys())))) + self.strategy.minimal_roi.keys()))) # set close-rate to min-roi closerate = trade.open_rate + trade.open_rate * \ self.strategy.minimal_roi[roi_entry] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 710a4e8f1..27da6147c 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -210,14 +210,14 @@ class IStrategy(ABC): :return: True if trade should be sold, False otherwise """ # Set current rate to low for backtesting sell - current_rate = rate if not low else low + current_rate = low or rate current_profit = trade.calc_profit_percent(current_rate) stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, current_time=date, current_profit=current_profit) if stoplossflag.sell_flag: return stoplossflag # Set current rate to low for backtesting sell - current_rate = rate if not high else high + current_rate = high or rate current_profit = trade.calc_profit_percent(current_rate) experimental = self.config.get('experimental', {}) From 79d1d63e6fd5a06cd77ee6682ce10315e73886bc Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:42:34 +0100 Subject: [PATCH 19/24] Align data (by halfing all data) --- .../tests/optimize/test_backtest_detail.py | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 323323683..5ec9c02c2 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -17,12 +17,12 @@ from freqtrade.tests.conftest import patch_exchange # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss tc0 = BTContainer(data=[ - [0, 10000.0, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10025, 9200, 9200, 12345, 0, 0], # exit with stoploss hit - [3, 9950, 10000, 9960, 9955, 12345, 0, 0], - [4, 9955, 9975, 9955, 9990, 12345, 0, 0], - [5, 9990, 9990, 9990, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4980, 4977, 6172, 0, 0], + [4, 4977, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi=1, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -32,12 +32,12 @@ tc0 = BTContainer(data=[ # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss tc1 = BTContainer(data=[ - [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10025, 9925, 9950, 12345, 0, 0], - [3, 9950, 10000, 9600, 9925, 12345, 0, 0], # exit with stoploss hit - [4, 9925, 9975, 9875, 9900, 12345, 0, 0], - [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.03, roi=1, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -51,13 +51,13 @@ tc1 = BTContainer(data=[ # TC3: Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc2 = BTContainer(data=[ - [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10025, 9600, 9950, 12345, 0, 0], # exit with stoploss hit - [3, 9950, 10000, 9900, 9925, 12345, 1, 0], - [4, 9950, 10000, 9900, 9925, 12345, 0, 0], # enter trade 2 (signal on last candle) - [5, 9925, 9975, 8000, 8000, 12345, 0, 0], # exit with stoploss hit - [6, 9900, 9950, 9950, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit + [6, 4950, 4975, 4975, 4950, 6172, 0, 0]], stop_loss=-0.02, roi=1, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] @@ -68,12 +68,12 @@ tc2 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ - [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 11500, 9700, 11500, 12345, 0, 0], # Exit with stoploss hit - [3, 9950, 10000, 9900, 9925, 12345, 0, 0], - [4, 9925, 9975, 9875, 9900, 12345, 0, 0], - [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi=0.06, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -82,12 +82,12 @@ tc3 = BTContainer(data=[ # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain tc4 = BTContainer(data=[ - [0, 10000, 10050, 9960, 9975, 12345, 1, 0], - [1, 10000, 10050, 9960, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10050, 9950, 9975, 12345, 0, 0], - [3, 9950, 12000, 9950, 12000, 12345, 0, 0], # ROI - [4, 9925, 9975, 9945, 9900, 12345, 0, 0], - [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4980, 4987, 6172, 1, 0], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.01, roi=0.03, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -97,12 +97,12 @@ tc4 = BTContainer(data=[ # Set stop-loss at 2% ROI at 5% # TC6: Stop-Loss triggers 2% Loss tc5 = BTContainer(data=[ - [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10600, 9700, 10100, 12345, 0, 0], # Exit with stoploss - [3, 9950, 10000, 9900, 9925, 12345, 0, 0], - [4, 9925, 9975, 9945, 9900, 12345, 0, 0], - [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi=0.05, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -112,12 +112,12 @@ tc5 = BTContainer(data=[ # Set stop-loss at 2% ROI at 3% # TC7: ROI Triggers 3% Gain tc6 = BTContainer(data=[ - [0, 10000, 10050, 9950, 9975, 12345, 1, 0], - [1, 10000, 10050, 9950, 9975, 12345, 0, 0], # enter trade (signal on last candle) - [2, 9975, 10600, 9900, 10100, 12345, 0, 0], # ROI - [3, 9950, 10000, 9900, 9925, 12345, 0, 0], - [4, 9925, 9975, 9945, 9900, 12345, 0, 0], - [5, 9900, 9950, 9850, 9900, 12345, 0, 0]], + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi=0.03, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) From daa9863d0ba82c22f9fa41de8cc49434396868d0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:45:32 +0100 Subject: [PATCH 20/24] Try adding headers --- freqtrade/tests/optimize/test_backtest_detail.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 5ec9c02c2..049a761b8 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -17,6 +17,7 @@ from freqtrade.tests.conftest import patch_exchange # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss tc0 = BTContainer(data=[ + # date, open, high, low, close, volume, buy, sell [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit From a321d0a8203478f2eaebb1889490641463864d91 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 30 Oct 2018 20:49:12 +0100 Subject: [PATCH 21/24] Short descriptors --- freqtrade/tests/optimize/test_backtest_detail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 049a761b8..9efe2d573 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -17,7 +17,7 @@ from freqtrade.tests.conftest import patch_exchange # Test with Stop-loss at 1% # TC1: Stop-Loss Triggered 1% loss tc0 = BTContainer(data=[ - # date, open, high, low, close, volume, buy, sell + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit From c21b45647d4d06d4307385be0a49c69da5318f58 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Nov 2018 13:05:57 +0100 Subject: [PATCH 22/24] Fix smoe comments in persistence --- freqtrade/persistence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 02267ac21..51a8129fb 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -272,10 +272,10 @@ class Trade(_DECL_BASE): self, fee: Optional[float] = None) -> float: """ - Calculate the open_rate in BTC + Calculate the open_rate including fee. :param fee: fee to use on the open rate (optional). If rate is not set self.fee will be used - :return: Price in BTC of the open trade + :return: Price in of the open trade incl. Fees """ buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) @@ -287,7 +287,7 @@ class Trade(_DECL_BASE): rate: Optional[float] = None, fee: Optional[float] = None) -> float: """ - Calculate the close_rate in BTC + Calculate the close_rate including fee :param fee: fee to use on the close rate (optional). If rate is not set self.fee will be used :param rate: rate to compare with (optional). @@ -307,12 +307,12 @@ class Trade(_DECL_BASE): rate: Optional[float] = None, fee: Optional[float] = None) -> float: """ - Calculate the profit in BTC between Close and Open trade + Calculate the absolute profit in stake currency between Close and Open trade :param fee: fee to use on the close rate (optional). If rate is not set self.fee will be used :param rate: close rate to compare with (optional). If rate is not set self.close_rate will be used - :return: profit in BTC as float + :return: profit in stake currency as float """ open_trade_price = self.calc_open_trade_price() close_trade_price = self.calc_close_trade_price( From 95d271ca5d73be67ab8fc557a12b8e4a879a54a1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Nov 2018 13:14:59 +0100 Subject: [PATCH 23/24] Fix ROI close-rate calculation to work with fees - adjust tests --- freqtrade/optimize/backtesting.py | 8 +++++--- freqtrade/tests/optimize/test_backtesting.py | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 1f02b9793..6fcde64fa 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -218,9 +218,11 @@ class Backtesting(object): # get entry in min_roi >= to trade duration roi_entry = max(list(filter(lambda x: trade_dur >= x, self.strategy.minimal_roi.keys()))) - # set close-rate to min-roi - closerate = trade.open_rate + trade.open_rate * \ - self.strategy.minimal_roi[roi_entry] + roi = self.strategy.minimal_roi[roi_entry] + + # - (Expected abs profit + open_rate + open_fee) / (fee_close -1) + closerate = - (trade.open_rate * roi + trade.open_rate * + (1 + trade.fee_open)) / (trade.fee_close - 1) else: closerate = sell_row.open diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 2d3d49ab0..20f2a6582 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -518,18 +518,18 @@ def test_backtest(default_conf, fee, mocker) -> None: expected = pd.DataFrame( {'pair': [pair, pair], - 'profit_percent': [0.00029977, 0.00056716], - 'profit_abs': [1.49e-06, 7.6e-07], + 'profit_percent': [0.0, 0.0], + 'profit_abs': [0.0, 0.0], '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], + 'close_time': [Arrow(2018, 1, 29, 22, 35, 0).datetime, + Arrow(2018, 1, 30, 4, 15, 0).datetime], 'open_index': [77, 183], - 'close_index': [125, 193], - 'trade_duration': [240, 50], + 'close_index': [124, 192], + 'trade_duration': [235, 45], 'open_at_end': [False, False], 'open_rate': [0.104445, 0.10302485], - 'close_rate': [0.105, 0.10359999], + 'close_rate': [0.104969, 0.103541], 'sell_reason': [SellType.ROI, SellType.ROI] }) pd.testing.assert_frame_equal(results, expected) @@ -539,9 +539,11 @@ def test_backtest(default_conf, fee, mocker) -> None: # 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 + # check close trade rate alignes to close rate or is between high and low ln = data_pair.loc[data_pair["date"] == t["close_time"]] - assert round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) + assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or + round(ln.iloc[0]["low"], 6) < round( + t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None: From 8316acfa780c1e988f3aec46bc9503fdfeb6b8fb Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Nov 2018 13:16:10 +0100 Subject: [PATCH 24/24] Add column description to test-cases --- freqtrade/tests/optimize/test_backtest_detail.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index 9efe2d573..806c136bc 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -33,6 +33,7 @@ tc0 = BTContainer(data=[ # Test with Stop-Loss at 3% # TC2: Stop-Loss Triggered 3% Loss tc1 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5012, 4962, 4975, 6172, 0, 0], @@ -52,6 +53,7 @@ tc1 = BTContainer(data=[ # TC3: Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc2 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit @@ -69,6 +71,7 @@ tc2 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # TC4: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit @@ -83,6 +86,7 @@ tc3 = BTContainer(data=[ # Set stop-loss at 1% ROI 3% # TC5: ROI triggers 3% Gain tc4 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4980, 4987, 6172, 1, 0], [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5025, 4975, 4987, 6172, 0, 0], @@ -98,6 +102,7 @@ tc4 = BTContainer(data=[ # Set stop-loss at 2% ROI at 5% # TC6: Stop-Loss triggers 2% Loss tc5 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss @@ -113,6 +118,7 @@ tc5 = BTContainer(data=[ # Set stop-loss at 2% ROI at 3% # TC7: ROI Triggers 3% Gain tc6 = BTContainer(data=[ + # D O H L C V B S [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], [2, 4987, 5300, 4950, 5050, 6172, 0, 0],