Merge pull request #1510 from gianlup/add_totprofit_to_bt

Added total profit column to backtest result
This commit is contained in:
Matthias 2019-01-25 06:38:39 +01:00 committed by GitHub
commit 3afe54790e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 58 deletions

View File

@ -166,53 +166,65 @@ The most important in the backtesting is to understand the result.
A backtesting result will look like that: A backtesting result will look like that:
``` ```
======================================== BACKTESTING REPORT ========================================= ========================================================= BACKTESTING REPORT ========================================================
| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss | | pair | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss |
|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:| |:---------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:|
| ETH/BTC | 44 | 0.18 | 0.00159118 | 50.9 | 44 | 0 | | ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 | 21 |
| LTC/BTC | 27 | 0.10 | 0.00051931 | 103.1 | 26 | 1 | | ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 | 8 |
| ETC/BTC | 24 | 0.05 | 0.00022434 | 166.0 | 22 | 2 | | BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 | 14 |
| DASH/BTC | 29 | 0.18 | 0.00103223 | 192.2 | 29 | 0 | | DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 | 7 |
| ZEC/BTC | 65 | -0.02 | -0.00020621 | 202.7 | 62 | 3 | | ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 | 10 |
| XLM/BTC | 35 | 0.02 | 0.00012877 | 242.4 | 32 | 3 | | EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 | 20 |
| BCH/BTC | 12 | 0.62 | 0.00149284 | 50.0 | 12 | 0 | | ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 | 15 |
| POWR/BTC | 21 | 0.26 | 0.00108215 | 134.8 | 21 | 0 | | ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 | 17 |
| ADA/BTC | 54 | -0.19 | -0.00205202 | 191.3 | 47 | 7 | | IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 | 18 |
| XMR/BTC | 24 | -0.43 | -0.00206013 | 120.6 | 20 | 4 | | LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 | 9 |
| TOTAL | 335 | 0.03 | 0.00175246 | 157.9 | 315 | 20 | | LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 | 21 |
2018-06-13 06:57:27,347 - freqtrade.optimize.backtesting - INFO - | NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 | 7 |
====================================== LEFT OPEN TRADES REPORT ====================================== | NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 | 13 |
| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss | | REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 | 5 |
|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:| | XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 | 9 |
| ETH/BTC | 3 | 0.16 | 0.00009619 | 25.0 | 3 | 0 | | XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 | 11 |
| LTC/BTC | 1 | -1.00 | -0.00020118 | 1085.0 | 0 | 1 | | XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 | 23 |
| ETC/BTC | 2 | -1.80 | -0.00071933 | 1092.5 | 0 | 2 | | ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 | 15 |
| DASH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | | TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 |
| ZEC/BTC | 3 | -4.27 | -0.00256826 | 1301.7 | 0 | 3 | ========================================================= SELL REASON STATS =========================================================
| XLM/BTC | 3 | -1.11 | -0.00066744 | 965.0 | 0 | 3 | | Sell Reason | Count |
| BCH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | |:-------------------|--------:|
| POWR/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | | trailing_stop_loss | 205 |
| ADA/BTC | 7 | -3.58 | -0.00503604 | 850.0 | 0 | 7 | | stop_loss | 166 |
| XMR/BTC | 4 | -3.79 | -0.00303456 | 291.2 | 0 | 4 | | sell_signal | 56 |
| TOTAL | 23 | -2.63 | -0.01213062 | 750.4 | 3 | 20 | | force_sell | 2 |
====================================================== LEFT OPEN TRADES REPORT ======================================================
| pair | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss |
|:---------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:|
| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 | 0 |
| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 | 0 |
| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 | 0 |
``` ```
The 1st table will contain all trades the bot made. The 1st table will contain all trades the bot made.
The 2nd table will contain all trades the bot had to `forcesell` at the end of the backtest period to present a full picture. The 2nd table will contain a recap of sell reasons.
The 3rd table will contain all trades the bot had to `forcesell` at the end of the backtest period to present a full picture.
These trades are also included in the first table, but are extracted separately for clarity. These trades are also included in the first table, but are extracted separately for clarity.
The last line will give you the overall performance of your strategy, The last line will give you the overall performance of your strategy,
here: here:
``` ```
TOTAL 419 -0.41 -0.00348593 52.9 | TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 |
``` ```
We understand the bot has made `419` trades for an average duration of We understand the bot has made `429` trades for an average duration of
`52.9` min, with a performance of `-0.41%` (loss), that means it has `4:12:00`, with a performance of `76.20%` (profit), that means it has
lost a total of `-0.00348593 BTC`. earned a total of `0.00762792 BTC` starting with a capital of 0.01 BTC.
The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums all the profits/losses.
The column `tot profit %` shows instead the total profit % in relation to allocated capital
(`max_open_trades * stake_amount`). In the above results we have `max_open_trades=2 stake_amount=0.005` in config
so `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`.
As you will see your strategy performance will be influenced by your buy As you will see your strategy performance will be influenced by your buy
strategy, your sell strategy, and also by the `minimal_roi` and strategy, your sell strategy, and also by the `minimal_roi` and
@ -251,11 +263,11 @@ There will be an additional table comparing win/losses of the different strategi
Detailed output for all strategies one after the other will be available, so make sure to scroll up. Detailed output for all strategies one after the other will be available, so make sure to scroll up.
``` ```
=================================================== Strategy Summary ==================================================== =========================================================== Strategy Summary ===========================================================
| Strategy | buy count | avg profit % | cum profit % | total profit ETH | avg duration | profit | loss | | Strategy | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss |
|:-----------|------------:|---------------:|---------------:|-------------------:|:----------------|---------:|-------:| |:------------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:|
| Strategy1 | 19 | -0.76 | -14.39 | -0.01440287 | 15:48:00 | 15 | 4 | | Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 |
| Strategy2 | 6 | -2.73 | -16.40 | -0.01641299 | 1 day, 14:12:00 | 3 | 3 | | Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 825 |
``` ```
## Next step ## Next step

View File

@ -100,11 +100,13 @@ class Backtesting(object):
:return: pretty printed table with tabulate as str :return: pretty printed table with tabulate as str
""" """
stake_currency = str(self.config.get('stake_currency')) stake_currency = str(self.config.get('stake_currency'))
max_open_trades = self.config.get('max_open_trades')
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f') floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f')
tabular_data = [] tabular_data = []
headers = ['pair', 'buy count', 'avg profit %', 'cum profit %', headers = ['pair', 'buy count', 'avg profit %', 'cum profit %',
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss'] 'tot profit ' + stake_currency, 'tot profit %', 'avg duration',
'profit', 'loss']
for pair in data: for pair in data:
result = results[results.pair == pair] result = results[results.pair == pair]
if skip_nan and result.profit_abs.isnull().all(): if skip_nan and result.profit_abs.isnull().all():
@ -116,6 +118,7 @@ class Backtesting(object):
result.profit_percent.mean() * 100.0, result.profit_percent.mean() * 100.0,
result.profit_percent.sum() * 100.0, result.profit_percent.sum() * 100.0,
result.profit_abs.sum(), result.profit_abs.sum(),
result.profit_percent.sum() * 100.0 / max_open_trades,
str(timedelta( str(timedelta(
minutes=round(result.trade_duration.mean()))) if not result.empty else '0:00', minutes=round(result.trade_duration.mean()))) if not result.empty else '0:00',
len(result[result.profit_abs > 0]), len(result[result.profit_abs > 0]),
@ -129,6 +132,7 @@ class Backtesting(object):
results.profit_percent.mean() * 100.0, results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0, results.profit_percent.sum() * 100.0,
results.profit_abs.sum(), results.profit_abs.sum(),
results.profit_percent.sum() * 100.0 / max_open_trades,
str(timedelta( str(timedelta(
minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00', minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00',
len(results[results.profit_abs > 0]), len(results[results.profit_abs > 0]),
@ -153,11 +157,13 @@ class Backtesting(object):
Generate summary table per strategy Generate summary table per strategy
""" """
stake_currency = str(self.config.get('stake_currency')) stake_currency = str(self.config.get('stake_currency'))
max_open_trades = self.config.get('max_open_trades')
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f') floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f')
tabular_data = [] tabular_data = []
headers = ['Strategy', 'buy count', 'avg profit %', 'cum profit %', headers = ['Strategy', 'buy count', 'avg profit %', 'cum profit %',
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss'] 'tot profit ' + stake_currency, 'tot profit %', 'avg duration',
'profit', 'loss']
for strategy, results in all_results.items(): for strategy, results in all_results.items():
tabular_data.append([ tabular_data.append([
strategy, strategy,
@ -165,6 +171,7 @@ class Backtesting(object):
results.profit_percent.mean() * 100.0, results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0, results.profit_percent.sum() * 100.0,
results.profit_abs.sum(), results.profit_abs.sum(),
results.profit_percent.sum() * 100.0 / max_open_trades,
str(timedelta( str(timedelta(
minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00', minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00',
len(results[results.profit_abs > 0]), len(results[results.profit_abs > 0]),
@ -430,18 +437,18 @@ class Backtesting(object):
strategy if len(self.strategylist) > 1 else None) strategy if len(self.strategylist) > 1 else None)
print(f"Result for strategy {strategy}") print(f"Result for strategy {strategy}")
print(' BACKTESTING REPORT '.center(119, '=')) print(' BACKTESTING REPORT '.center(133, '='))
print(self._generate_text_table(data, results)) print(self._generate_text_table(data, results))
print(' SELL REASON STATS '.center(119, '=')) print(' SELL REASON STATS '.center(133, '='))
print(self._generate_text_table_sell_reason(data, results)) print(self._generate_text_table_sell_reason(data, results))
print(' LEFT OPEN TRADES REPORT '.center(119, '=')) print(' LEFT OPEN TRADES REPORT '.center(133, '='))
print(self._generate_text_table(data, results.loc[results.open_at_end], True)) print(self._generate_text_table(data, results.loc[results.open_at_end], True))
print() print()
if len(all_results) > 1: if len(all_results) > 1:
# Print Strategy summary table # Print Strategy summary table
print(' Strategy Summary '.center(119, '=')) print(' Strategy Summary '.center(133, '='))
print(self._generate_text_table_strategy(all_results)) print(self._generate_text_table_strategy(all_results))
print('\nFor more details, please look at the detail tables above') print('\nFor more details, please look at the detail tables above')

View File

@ -341,6 +341,7 @@ def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None:
def test_generate_text_table(default_conf, mocker): def test_generate_text_table(default_conf, mocker):
patch_exchange(mocker) patch_exchange(mocker)
default_conf['max_open_trades'] = 2
backtesting = Backtesting(default_conf) backtesting = Backtesting(default_conf)
results = pd.DataFrame( results = pd.DataFrame(
@ -356,13 +357,13 @@ def test_generate_text_table(default_conf, mocker):
result_str = ( result_str = (
'| pair | buy count | avg profit % | cum profit % | ' '| pair | buy count | avg profit % | cum profit % | '
'total profit BTC | avg duration | profit | loss |\n' 'tot profit BTC | tot profit % | avg duration | profit | loss |\n'
'|:--------|------------:|---------------:|---------------:|' '|:--------|------------:|---------------:|---------------:|'
'-------------------:|:---------------|---------:|-------:|\n' '-----------------:|---------------:|:---------------|---------:|-------:|\n'
'| ETH/BTC | 2 | 15.00 | 30.00 | ' '| ETH/BTC | 2 | 15.00 | 30.00 | '
'0.60000000 | 0:20:00 | 2 | 0 |\n' '0.60000000 | 15.00 | 0:20:00 | 2 | 0 |\n'
'| TOTAL | 2 | 15.00 | 30.00 | ' '| TOTAL | 2 | 15.00 | 30.00 | '
'0.60000000 | 0:20:00 | 2 | 0 |' '0.60000000 | 15.00 | 0:20:00 | 2 | 0 |'
) )
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
@ -398,6 +399,7 @@ def test_generate_text_table_strategyn(default_conf, mocker):
Test Backtesting.generate_text_table_sell_reason() method Test Backtesting.generate_text_table_sell_reason() method
""" """
patch_exchange(mocker) patch_exchange(mocker)
default_conf['max_open_trades'] = 2
backtesting = Backtesting(default_conf) backtesting = Backtesting(default_conf)
results = {} results = {}
results['ETH/BTC'] = pd.DataFrame( results['ETH/BTC'] = pd.DataFrame(
@ -425,13 +427,13 @@ def test_generate_text_table_strategyn(default_conf, mocker):
result_str = ( result_str = (
'| Strategy | buy count | avg profit % | cum profit % ' '| Strategy | buy count | avg profit % | cum profit % '
'| total profit BTC | avg duration | profit | loss |\n' '| tot profit BTC | tot profit % | avg duration | profit | loss |\n'
'|:-----------|------------:|---------------:|---------------:' '|:-----------|------------:|---------------:|---------------:'
'|-------------------:|:---------------|---------:|-------:|\n' '|-----------------:|---------------:|:---------------|---------:|-------:|\n'
'| ETH/BTC | 3 | 20.00 | 60.00 ' '| ETH/BTC | 3 | 20.00 | 60.00 '
'| 1.10000000 | 0:17:00 | 3 | 0 |\n' '| 1.10000000 | 30.00 | 0:17:00 | 3 | 0 |\n'
'| LTC/BTC | 3 | 30.00 | 90.00 ' '| LTC/BTC | 3 | 30.00 | 90.00 '
'| 1.30000000 | 0:20:00 | 3 | 0 |' '| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |'
) )
print(backtesting._generate_text_table_strategy(all_results=results)) print(backtesting._generate_text_table_strategy(all_results=results))
assert backtesting._generate_text_table_strategy(all_results=results) == result_str assert backtesting._generate_text_table_strategy(all_results=results) == result_str