From 989ab646a9ec1367b3eef17d8740c6bc7c84a4f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 9 Jan 2020 06:46:39 +0100 Subject: [PATCH 1/3] Add profit % to sell_reason table --- freqtrade/optimize/backtest_reports.py | 10 ++++++---- tests/optimize/test_backtest_reports.py | 12 ++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/freqtrade/optimize/backtest_reports.py b/freqtrade/optimize/backtest_reports.py index 5778747cf..555250760 100644 --- a/freqtrade/optimize/backtest_reports.py +++ b/freqtrade/optimize/backtest_reports.py @@ -66,11 +66,13 @@ def generate_text_table_sell_reason(data: Dict[str, Dict], results: DataFrame) - :return: pretty printed table with tabulate as string """ tabular_data = [] - headers = ['Sell Reason', 'Count', 'Profit', 'Loss'] + headers = ['Sell Reason', 'Count', 'Profit', 'Loss', 'Profit %'] for reason, count in results['sell_reason'].value_counts().iteritems(): - profit = len(results[(results['sell_reason'] == reason) & (results['profit_abs'] >= 0)]) - loss = len(results[(results['sell_reason'] == reason) & (results['profit_abs'] < 0)]) - tabular_data.append([reason.value, count, profit, loss]) + result = results.loc[results['sell_reason'] == reason] + profit = len(result[result['profit_abs'] >= 0]) + loss = len(result[results['profit_abs'] < 0]) + profit_mean = round(result['profit_percent'].mean() * 100.0, 2) + tabular_data.append([reason.value, count, profit, loss, profit_mean]) return tabulate(tabular_data, headers=headers, tablefmt="pipe") diff --git a/tests/optimize/test_backtest_reports.py b/tests/optimize/test_backtest_reports.py index 726202517..107389a42 100644 --- a/tests/optimize/test_backtest_reports.py +++ b/tests/optimize/test_backtest_reports.py @@ -39,8 +39,8 @@ def test_generate_text_table_sell_reason(default_conf, mocker): results = pd.DataFrame( { 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], - 'profit_percent': [0.1, 0.2, -0.3], - 'profit_abs': [0.2, 0.4, -0.5], + 'profit_percent': [0.1, 0.2, -0.1], + 'profit_abs': [0.2, 0.4, -0.2], 'trade_duration': [10, 30, 10], 'profit': [2, 0, 0], 'loss': [0, 0, 1], @@ -49,10 +49,10 @@ def test_generate_text_table_sell_reason(default_conf, mocker): ) result_str = ( - '| Sell Reason | Count | Profit | Loss |\n' - '|:--------------|--------:|---------:|-------:|\n' - '| roi | 2 | 2 | 0 |\n' - '| stop_loss | 1 | 0 | 1 |' + '| Sell Reason | Count | Profit | Loss | Profit % |\n' + '|:--------------|--------:|---------:|-------:|-----------:|\n' + '| roi | 2 | 2 | 0 | 15 |\n' + '| stop_loss | 1 | 0 | 1 | -10 |' ) assert generate_text_table_sell_reason( data={'ETH/BTC': {}}, results=results) == result_str From c475729c13c22b404c469dec6dbdb9256a312fb8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 9 Jan 2020 06:52:34 +0100 Subject: [PATCH 2/3] Extract edge reporting to optimize_reports --- freqtrade/optimize/backtesting.py | 2 +- freqtrade/optimize/edge_cli.py | 32 ++----------------- ...acktest_reports.py => optimize_reports.py} | 26 +++++++++++++++ tests/optimize/test_backtest_reports.py | 16 ++++++++-- tests/optimize/test_edge_cli.py | 14 -------- 5 files changed, 44 insertions(+), 46 deletions(-) rename freqtrade/optimize/{backtest_reports.py => optimize_reports.py} (83%) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index ae3fbed0a..18cc27ff4 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -18,7 +18,7 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.misc import file_dump_json -from freqtrade.optimize.backtest_reports import ( +from freqtrade.optimize.optimize_reports import ( generate_text_table, generate_text_table_sell_reason, generate_text_table_strategy) from freqtrade.persistence import Trade diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index 4944f1dbb..be19688d8 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -6,13 +6,12 @@ This module contains the edge backtesting interface import logging from typing import Any, Dict -from tabulate import tabulate - from freqtrade import constants from freqtrade.configuration import (TimeRange, remove_credentials, validate_config_consistency) from freqtrade.edge import Edge -from freqtrade.resolvers import StrategyResolver, ExchangeResolver +from freqtrade.optimize.optimize_reports import generate_edge_table +from freqtrade.resolvers import ExchangeResolver, StrategyResolver logger = logging.getLogger(__name__) @@ -44,33 +43,8 @@ class EdgeCli: self.edge._timerange = TimeRange.parse_timerange(None if self.config.get( 'timerange') is None else str(self.config.get('timerange'))) - def _generate_edge_table(self, results: dict) -> str: - - floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', '.d') - tabular_data = [] - headers = ['pair', 'stoploss', 'win rate', 'risk reward ratio', - 'required risk reward', 'expectancy', 'total number of trades', - 'average duration (min)'] - - for result in results.items(): - if result[1].nb_trades > 0: - tabular_data.append([ - result[0], - result[1].stoploss, - result[1].winrate, - result[1].risk_reward_ratio, - result[1].required_risk_reward, - result[1].expectancy, - result[1].nb_trades, - round(result[1].avg_trade_duration) - ]) - - # Ignore type as floatfmt does allow tuples but mypy does not know that - return tabulate(tabular_data, headers=headers, - floatfmt=floatfmt, tablefmt="pipe") # type: ignore - def start(self) -> None: result = self.edge.calculate() if result: print('') # blank line for readability - print(self._generate_edge_table(self.edge._cached_pairs)) + print(generate_edge_table(self.edge._cached_pairs)) diff --git a/freqtrade/optimize/backtest_reports.py b/freqtrade/optimize/optimize_reports.py similarity index 83% rename from freqtrade/optimize/backtest_reports.py rename to freqtrade/optimize/optimize_reports.py index 555250760..ffc8c53d4 100644 --- a/freqtrade/optimize/backtest_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -107,3 +107,29 @@ def generate_text_table_strategy(stake_currency: str, max_open_trades: str, # Ignore type as floatfmt does allow tuples but mypy does not know that return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe") # type: ignore + + +def generate_edge_table(results: dict) -> str: + + floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', '.d') + tabular_data = [] + headers = ['pair', 'stoploss', 'win rate', 'risk reward ratio', + 'required risk reward', 'expectancy', 'total number of trades', + 'average duration (min)'] + + for result in results.items(): + if result[1].nb_trades > 0: + tabular_data.append([ + result[0], + result[1].stoploss, + result[1].winrate, + result[1].risk_reward_ratio, + result[1].required_risk_reward, + result[1].expectancy, + result[1].nb_trades, + round(result[1].avg_trade_duration) + ]) + + # Ignore type as floatfmt does allow tuples but mypy does not know that + return tabulate(tabular_data, headers=headers, + floatfmt=floatfmt, tablefmt="pipe") # type: ignore diff --git a/tests/optimize/test_backtest_reports.py b/tests/optimize/test_backtest_reports.py index 107389a42..518b50d0f 100644 --- a/tests/optimize/test_backtest_reports.py +++ b/tests/optimize/test_backtest_reports.py @@ -1,7 +1,8 @@ import pandas as pd -from freqtrade.optimize.backtest_reports import ( - generate_text_table, generate_text_table_sell_reason, +from freqtrade.edge import PairInfo +from freqtrade.optimize.optimize_reports import ( + generate_edge_table, generate_text_table, generate_text_table_sell_reason, generate_text_table_strategy) from freqtrade.strategy.interface import SellType @@ -94,3 +95,14 @@ def test_generate_text_table_strategy(default_conf, mocker): '| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |' ) assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str + + +def test_generate_edge_table(edge_conf, mocker): + + results = {} + results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60) + + assert generate_edge_table(results).count(':|') == 7 + assert generate_edge_table(results).count('| ETH/BTC |') == 1 + assert generate_edge_table(results).count( + '| risk reward ratio | required risk reward | expectancy |') == 1 diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index ddfa7156e..acc0d2d16 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -3,7 +3,6 @@ from unittest.mock import MagicMock -from freqtrade.edge import PairInfo from freqtrade.optimize import setup_configuration, start_edge from freqtrade.optimize.edge_cli import EdgeCli from freqtrade.state import RunMode @@ -106,16 +105,3 @@ def test_edge_init_fee(mocker, edge_conf) -> None: edge_cli = EdgeCli(edge_conf) assert edge_cli.edge.fee == 0.1234 assert fee_mock.call_count == 0 - - -def test_generate_edge_table(edge_conf, mocker): - patch_exchange(mocker) - edge_cli = EdgeCli(edge_conf) - - results = {} - results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60) - - assert edge_cli._generate_edge_table(results).count(':|') == 7 - assert edge_cli._generate_edge_table(results).count('| ETH/BTC |') == 1 - assert edge_cli._generate_edge_table(results).count( - '| risk reward ratio | required risk reward | expectancy |') == 1 From 785cd2a640bda12ef39a1391e9f07f2b89dc229d Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 9 Jan 2020 06:52:56 +0100 Subject: [PATCH 3/3] Rename test module --- freqtrade/optimize/optimize_reports.py | 4 ++-- .../{test_backtest_reports.py => test_optimize_reports.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/optimize/{test_backtest_reports.py => test_optimize_reports.py} (100%) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index ffc8c53d4..3801546b1 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -114,8 +114,8 @@ def generate_edge_table(results: dict) -> str: floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', '.d') tabular_data = [] headers = ['pair', 'stoploss', 'win rate', 'risk reward ratio', - 'required risk reward', 'expectancy', 'total number of trades', - 'average duration (min)'] + 'required risk reward', 'expectancy', 'total number of trades', + 'average duration (min)'] for result in results.items(): if result[1].nb_trades > 0: diff --git a/tests/optimize/test_backtest_reports.py b/tests/optimize/test_optimize_reports.py similarity index 100% rename from tests/optimize/test_backtest_reports.py rename to tests/optimize/test_optimize_reports.py