Merge pull request #2755 from freqtrade/backtest_mean

Add average profit to sell_reason stats
This commit is contained in:
hroff-1902 2020-01-09 20:35:35 +03:00 committed by GitHub
commit 7c7f7b9ece
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 56 deletions

View File

@ -18,7 +18,7 @@ from freqtrade.data.dataprovider import DataProvider
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.misc import file_dump_json 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, generate_text_table_sell_reason,
generate_text_table_strategy) generate_text_table_strategy)
from freqtrade.persistence import Trade from freqtrade.persistence import Trade

View File

@ -6,13 +6,12 @@ This module contains the edge backtesting interface
import logging import logging
from typing import Any, Dict from typing import Any, Dict
from tabulate import tabulate
from freqtrade import constants from freqtrade import constants
from freqtrade.configuration import (TimeRange, remove_credentials, from freqtrade.configuration import (TimeRange, remove_credentials,
validate_config_consistency) validate_config_consistency)
from freqtrade.edge import Edge 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__) logger = logging.getLogger(__name__)
@ -44,33 +43,8 @@ class EdgeCli:
self.edge._timerange = TimeRange.parse_timerange(None if self.config.get( self.edge._timerange = TimeRange.parse_timerange(None if self.config.get(
'timerange') is None else str(self.config.get('timerange'))) '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: def start(self) -> None:
result = self.edge.calculate() result = self.edge.calculate()
if result: if result:
print('') # blank line for readability print('') # blank line for readability
print(self._generate_edge_table(self.edge._cached_pairs)) print(generate_edge_table(self.edge._cached_pairs))

View File

@ -66,11 +66,13 @@ def generate_text_table_sell_reason(data: Dict[str, Dict], results: DataFrame) -
:return: pretty printed table with tabulate as string :return: pretty printed table with tabulate as string
""" """
tabular_data = [] 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(): for reason, count in results['sell_reason'].value_counts().iteritems():
profit = len(results[(results['sell_reason'] == reason) & (results['profit_abs'] >= 0)]) result = results.loc[results['sell_reason'] == reason]
loss = len(results[(results['sell_reason'] == reason) & (results['profit_abs'] < 0)]) profit = len(result[result['profit_abs'] >= 0])
tabular_data.append([reason.value, count, profit, loss]) 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") return tabulate(tabular_data, headers=headers, tablefmt="pipe")
@ -105,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 # Ignore type as floatfmt does allow tuples but mypy does not know that
return tabulate(tabular_data, headers=headers, return tabulate(tabular_data, headers=headers,
floatfmt=floatfmt, tablefmt="pipe") # type: ignore 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

View File

@ -3,7 +3,6 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freqtrade.edge import PairInfo
from freqtrade.optimize import setup_configuration, start_edge from freqtrade.optimize import setup_configuration, start_edge
from freqtrade.optimize.edge_cli import EdgeCli from freqtrade.optimize.edge_cli import EdgeCli
from freqtrade.state import RunMode from freqtrade.state import RunMode
@ -106,16 +105,3 @@ def test_edge_init_fee(mocker, edge_conf) -> None:
edge_cli = EdgeCli(edge_conf) edge_cli = EdgeCli(edge_conf)
assert edge_cli.edge.fee == 0.1234 assert edge_cli.edge.fee == 0.1234
assert fee_mock.call_count == 0 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

View File

@ -1,7 +1,8 @@
import pandas as pd import pandas as pd
from freqtrade.optimize.backtest_reports import ( from freqtrade.edge import PairInfo
generate_text_table, generate_text_table_sell_reason, from freqtrade.optimize.optimize_reports import (
generate_edge_table, generate_text_table, generate_text_table_sell_reason,
generate_text_table_strategy) generate_text_table_strategy)
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
@ -39,8 +40,8 @@ def test_generate_text_table_sell_reason(default_conf, mocker):
results = pd.DataFrame( results = pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, -0.3], 'profit_percent': [0.1, 0.2, -0.1],
'profit_abs': [0.2, 0.4, -0.5], 'profit_abs': [0.2, 0.4, -0.2],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'profit': [2, 0, 0], 'profit': [2, 0, 0],
'loss': [0, 0, 1], 'loss': [0, 0, 1],
@ -49,10 +50,10 @@ def test_generate_text_table_sell_reason(default_conf, mocker):
) )
result_str = ( result_str = (
'| Sell Reason | Count | Profit | Loss |\n' '| Sell Reason | Count | Profit | Loss | Profit % |\n'
'|:--------------|--------:|---------:|-------:|\n' '|:--------------|--------:|---------:|-------:|-----------:|\n'
'| roi | 2 | 2 | 0 |\n' '| roi | 2 | 2 | 0 | 15 |\n'
'| stop_loss | 1 | 0 | 1 |' '| stop_loss | 1 | 0 | 1 | -10 |'
) )
assert generate_text_table_sell_reason( assert generate_text_table_sell_reason(
data={'ETH/BTC': {}}, results=results) == result_str data={'ETH/BTC': {}}, results=results) == result_str
@ -94,3 +95,14 @@ def test_generate_text_table_strategy(default_conf, mocker):
'| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |' '| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |'
) )
assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str 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