Merge pull request #3377 from freqtrade/btreport_refactor

Refactor BTReport
This commit is contained in:
Matthias 2020-05-27 19:33:08 +02:00 committed by GitHub
commit 04eb11bb5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 349 additions and 127 deletions

View File

@ -1,7 +1,7 @@
import logging import logging
from datetime import timedelta from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Any, Dict, List
from pandas import DataFrame from pandas import DataFrame
from tabulate import tabulate from tabulate import tabulate
@ -34,118 +34,173 @@ def store_backtest_result(recordfilename: Path, all_results: Dict[str, DataFrame
file_dump_json(filename, records) file_dump_json(filename, records)
def generate_text_table(data: Dict[str, Dict], stake_currency: str, max_open_trades: int, def _get_line_floatfmt() -> List[str]:
results: DataFrame, skip_nan: bool = False) -> str:
""" """
Generates and returns a text table for the given backtest data and the results dataframe Generate floatformat (goes in line with _generate_result_line())
"""
return ['s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', 'd', 'd', 'd']
def _get_line_header(first_column: str, stake_currency: str) -> List[str]:
"""
Generate header lines (goes in line with _generate_result_line())
"""
return [first_column, 'Buys', 'Avg Profit %', 'Cum Profit %',
f'Tot Profit {stake_currency}', 'Tot Profit %', 'Avg Duration',
'Wins', 'Draws', 'Losses']
def _generate_result_line(result: DataFrame, max_open_trades: int, first_column: str) -> Dict:
"""
Generate one result dict, with "first_column" as key.
"""
return {
'key': first_column,
'trades': len(result.index),
'profit_mean': result.profit_percent.mean(),
'profit_mean_pct': result.profit_percent.mean() * 100.0,
'profit_sum': result.profit_percent.sum(),
'profit_sum_pct': result.profit_percent.sum() * 100.0,
'profit_total_abs': result.profit_abs.sum(),
'profit_total_pct': result.profit_percent.sum() * 100.0 / max_open_trades,
'duration_avg': str(timedelta(
minutes=round(result.trade_duration.mean()))
) if not result.empty else '0:00',
# 'duration_max': str(timedelta(
# minutes=round(result.trade_duration.max()))
# ) if not result.empty else '0:00',
# 'duration_min': str(timedelta(
# minutes=round(result.trade_duration.min()))
# ) if not result.empty else '0:00',
'wins': len(result[result.profit_abs > 0]),
'draws': len(result[result.profit_abs == 0]),
'losses': len(result[result.profit_abs < 0]),
}
def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, max_open_trades: int,
results: DataFrame, skip_nan: bool = False) -> List[Dict]:
"""
Generates and returns a list for the given backtest data and the results dataframe
:param data: Dict of <pair: dataframe> containing data that was used during backtesting. :param data: Dict of <pair: dataframe> containing data that was used during backtesting.
:param stake_currency: stake-currency - used to correctly name headers :param stake_currency: stake-currency - used to correctly name headers
:param max_open_trades: Maximum allowed open trades :param max_open_trades: Maximum allowed open trades
:param results: Dataframe containing the backtest results :param results: Dataframe containing the backtest results
:param skip_nan: Print "left open" open trades :param skip_nan: Print "left open" open trades
:return: pretty printed table with tabulate as string :return: List of Dicts containing the metrics per pair
""" """
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f')
tabular_data = [] tabular_data = []
headers = [
'Pair',
'Buys',
'Avg Profit %',
'Cum Profit %',
f'Tot Profit {stake_currency}',
'Tot Profit %',
'Avg Duration',
'Wins',
'Draws',
'Losses'
]
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():
continue continue
tabular_data.append([ tabular_data.append(_generate_result_line(result, max_open_trades, pair))
pair,
len(result.index),
result.profit_percent.mean() * 100.0,
result.profit_percent.sum() * 100.0,
result.profit_abs.sum(),
result.profit_percent.sum() * 100.0 / max_open_trades,
str(timedelta(
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]),
len(result[result.profit_abs < 0])
])
# Append Total # Append Total
tabular_data.append([ tabular_data.append(_generate_result_line(results, max_open_trades, 'TOTAL'))
'TOTAL', return tabular_data
len(results.index),
results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0, def generate_text_table(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
results.profit_abs.sum(), """
results.profit_percent.sum() * 100.0 / max_open_trades, Generates and returns a text table for the given backtest data and the results dataframe
str(timedelta( :param pair_results: List of Dictionaries - one entry per pair + final TOTAL row
minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00', :param stake_currency: stake-currency - used to correctly name headers
len(results[results.profit_abs > 0]), :return: pretty printed table with tabulate as string
len(results[results.profit_abs == 0]), """
len(results[results.profit_abs < 0])
]) headers = _get_line_header('Pair', stake_currency)
floatfmt = _get_line_floatfmt()
output = [[
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
] for t in pair_results]
# 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(output, headers=headers,
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
def generate_text_table_sell_reason(stake_currency: str, max_open_trades: int, def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
results: DataFrame) -> str:
""" """
Generate small table outlining Backtest results Generate small table outlining Backtest results
:param stake_currency: Stakecurrency used
:param max_open_trades: Max_open_trades parameter :param max_open_trades: Max_open_trades parameter
:param results: Dataframe containing the backtest results :param results: Dataframe containing the backtest result for one strategy
:return: pretty printed table with tabulate as string :return: List of Dicts containing the metrics per Sell reason
""" """
tabular_data = [] tabular_data = []
headers = [
"Sell Reason",
"Sells",
"Wins",
"Draws",
"Losses",
"Avg Profit %",
"Cum Profit %",
f"Tot Profit {stake_currency}",
"Tot Profit %",
]
for reason, count in results['sell_reason'].value_counts().iteritems(): for reason, count in results['sell_reason'].value_counts().iteritems():
result = results.loc[results['sell_reason'] == reason] result = results.loc[results['sell_reason'] == reason]
wins = len(result[result['profit_abs'] > 0])
draws = len(result[result['profit_abs'] == 0]) profit_mean = result['profit_percent'].mean()
loss = len(result[result['profit_abs'] < 0]) profit_sum = result["profit_percent"].sum()
profit_mean = round(result['profit_percent'].mean() * 100.0, 2)
profit_sum = round(result["profit_percent"].sum() * 100.0, 2)
profit_tot = result['profit_abs'].sum()
profit_percent_tot = round(result['profit_percent'].sum() * 100.0 / max_open_trades, 2) profit_percent_tot = round(result['profit_percent'].sum() * 100.0 / max_open_trades, 2)
tabular_data.append( tabular_data.append(
[ {
reason.value, 'sell_reason': reason.value,
count, 'trades': count,
wins, 'wins': len(result[result['profit_abs'] > 0]),
draws, 'draws': len(result[result['profit_abs'] == 0]),
loss, 'losses': len(result[result['profit_abs'] < 0]),
profit_mean, 'profit_mean': profit_mean,
profit_sum, 'profit_mean_pct': round(profit_mean * 100, 2),
profit_tot, 'profit_sum': profit_sum,
profit_percent_tot, 'profit_sum_pct': round(profit_sum * 100, 2),
] 'profit_total_abs': result['profit_abs'].sum(),
'profit_pct_total': profit_percent_tot,
}
) )
return tabulate(tabular_data, headers=headers, tablefmt="orgtbl", stralign="right") return tabular_data
def generate_text_table_strategy(stake_currency: str, max_open_trades: str, def generate_text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]],
all_results: Dict) -> str: stake_currency: str) -> str:
"""
Generate small table outlining Backtest results
:param sell_reason_stats: Sell reason metrics
:param stake_currency: Stakecurrency used
:return: pretty printed table with tabulate as string
"""
headers = [
'Sell Reason',
'Sells',
'Wins',
'Draws',
'Losses',
'Avg Profit %',
'Cum Profit %',
f'Tot Profit {stake_currency}',
'Tot Profit %',
]
output = [[
t['sell_reason'], t['trades'], t['wins'], t['draws'], t['losses'],
t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'], t['profit_pct_total'],
] for t in sell_reason_stats]
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
def generate_strategy_metrics(stake_currency: str, max_open_trades: int,
all_results: Dict) -> List[Dict]:
"""
Generate summary per strategy
:param stake_currency: stake-currency - used to correctly name headers
:param max_open_trades: Maximum allowed open trades used for backtest
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies
:return: List of Dicts containing the metrics per Strategy
"""
tabular_data = []
for strategy, results in all_results.items():
tabular_data.append(_generate_result_line(results, max_open_trades, strategy))
return tabular_data
def generate_text_table_strategy(strategy_results, stake_currency: str) -> str:
""" """
Generate summary table per strategy Generate summary table per strategy
:param stake_currency: stake-currency - used to correctly name headers :param stake_currency: stake-currency - used to correctly name headers
@ -153,34 +208,21 @@ def generate_text_table_strategy(stake_currency: str, max_open_trades: str,
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies :param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies
:return: pretty printed table with tabulate as string :return: pretty printed table with tabulate as string
""" """
floatfmt = _get_line_floatfmt()
headers = _get_line_header('Strategy', stake_currency)
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f') output = [[
tabular_data = [] t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
headers = ['Strategy', 'Buys', 'Avg Profit %', 'Cum Profit %', t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
f'Tot Profit {stake_currency}', 'Tot Profit %', 'Avg Duration', ] for t in strategy_results]
'Wins', 'Draws', 'Losses']
for strategy, results in all_results.items():
tabular_data.append([
strategy,
len(results.index),
results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0,
results.profit_abs.sum(),
results.profit_percent.sum() * 100.0 / max_open_trades,
str(timedelta(
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]),
len(results[results.profit_abs < 0])
])
# 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(output, headers=headers,
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
def generate_edge_table(results: dict) -> str: def generate_edge_table(results: dict) -> str:
floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', '.d') floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', 'd', 'd')
tabular_data = [] tabular_data = []
headers = ['Pair', 'Stoploss', 'Win Rate', 'Risk Reward Ratio', headers = ['Pair', 'Stoploss', 'Win Rate', 'Risk Reward Ratio',
'Required Risk Reward', 'Expectancy', 'Total Number of Trades', 'Required Risk Reward', 'Expectancy', 'Total Number of Trades',
@ -206,38 +248,48 @@ def generate_edge_table(results: dict) -> str:
def show_backtest_results(config: Dict, btdata: Dict[str, DataFrame], def show_backtest_results(config: Dict, btdata: Dict[str, DataFrame],
all_results: Dict[str, DataFrame]): all_results: Dict[str, DataFrame]):
for strategy, results in all_results.items(): stake_currency = config['stake_currency']
max_open_trades = config['max_open_trades']
print(f"Result for strategy {strategy}") for strategy, results in all_results.items():
table = generate_text_table(btdata, stake_currency=config['stake_currency'], pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
max_open_trades=config['max_open_trades'], max_open_trades=max_open_trades,
results=results, skip_nan=False)
sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
results=results) results=results)
left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
max_open_trades=max_open_trades,
results=results.loc[results['open_at_end']],
skip_nan=True)
# Print results
print(f"Result for strategy {strategy}")
table = generate_text_table(pair_results, stake_currency=stake_currency)
if isinstance(table, str): if isinstance(table, str):
print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '=')) print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '='))
print(table) print(table)
table = generate_text_table_sell_reason(stake_currency=config['stake_currency'], table = generate_text_table_sell_reason(sell_reason_stats=sell_reason_stats,
max_open_trades=config['max_open_trades'], stake_currency=stake_currency,
results=results) )
if isinstance(table, str): if isinstance(table, str):
print(' SELL REASON STATS '.center(len(table.splitlines()[0]), '=')) print(' SELL REASON STATS '.center(len(table.splitlines()[0]), '='))
print(table) print(table)
table = generate_text_table(btdata, table = generate_text_table(left_open_results, stake_currency=stake_currency)
stake_currency=config['stake_currency'],
max_open_trades=config['max_open_trades'],
results=results.loc[results.open_at_end], skip_nan=True)
if isinstance(table, str): if isinstance(table, str):
print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '=')) print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '='))
print(table) print(table)
if isinstance(table, str): if isinstance(table, str):
print('=' * len(table.splitlines()[0])) print('=' * len(table.splitlines()[0]))
print() print()
if len(all_results) > 1: if len(all_results) > 1:
# Print Strategy summary table # Print Strategy summary table
table = generate_text_table_strategy(config['stake_currency'], strategy_results = generate_strategy_metrics(stake_currency=stake_currency,
config['max_open_trades'], max_open_trades=max_open_trades,
all_results=all_results) all_results=all_results)
table = generate_text_table_strategy(strategy_results, stake_currency)
print(' STRATEGY SUMMARY '.center(len(table.splitlines()[0]), '=')) print(' STRATEGY SUMMARY '.center(len(table.splitlines()[0]), '='))
print(table) print(table)
print('=' * len(table.splitlines()[0])) print('=' * len(table.splitlines()[0]))

View File

@ -658,10 +658,17 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
PropertyMock(return_value=['UNITTEST/BTC'])) PropertyMock(return_value=['UNITTEST/BTC']))
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
gen_table_mock = MagicMock() gen_table_mock = MagicMock()
mocker.patch('freqtrade.optimize.optimize_reports.generate_text_table', gen_table_mock) sell_reason_mock = MagicMock()
gen_strattable_mock = MagicMock() gen_strattable_mock = MagicMock()
mocker.patch('freqtrade.optimize.optimize_reports.generate_text_table_strategy', gen_strat_summary = MagicMock()
gen_strattable_mock)
mocker.patch.multiple('freqtrade.optimize.optimize_reports',
generate_text_table=gen_table_mock,
generate_text_table_strategy=gen_strattable_mock,
generate_pair_metrics=MagicMock(),
generate_sell_reason_stats=sell_reason_mock,
generate_strategy_metrics=gen_strat_summary,
)
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
@ -683,6 +690,8 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
assert backtestmock.call_count == 2 assert backtestmock.call_count == 2
assert gen_table_mock.call_count == 4 assert gen_table_mock.call_count == 4
assert gen_strattable_mock.call_count == 1 assert gen_strattable_mock.call_count == 1
assert sell_reason_mock.call_count == 2
assert gen_strat_summary.call_count == 1
# check the logs, that will contain the backtest result # check the logs, that will contain the backtest result
exists = [ exists = [
@ -703,3 +712,92 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
for line in exists: for line in exists:
assert log_has(line, caplog) assert log_has(line, caplog)
@pytest.mark.filterwarnings("ignore:deprecated")
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
patch_exchange(mocker)
backtestmock = MagicMock(side_effect=[
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
'profit_percent': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'open_time': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', ], utc=True
),
'close_time': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00', ], utc=True),
'open_index': [78, 184],
'close_index': [125, 192],
'trade_duration': [235, 40],
'open_at_end': [False, False],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [SellType.ROI, SellType.ROI]
}),
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_percent': [0.03, 0.01, 0.1],
'profit_abs': [0.01, 0.02, 0.2],
'open_time': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00',
'2018-01-30 05:30:00'], utc=True
),
'close_time': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00',
'2018-01-30 08:30:00'], utc=True),
'open_index': [78, 184, 185],
'close_index': [125, 224, 205],
'trade_duration': [47, 40, 20],
'open_at_end': [False, False, False],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}),
])
mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist',
PropertyMock(return_value=['UNITTEST/BTC']))
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
patched_configuration_load_config_file(mocker, default_conf)
args = [
'backtesting',
'--config', 'config.json',
'--datadir', str(testdatadir),
'--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'),
'--ticker-interval', '1m',
'--timerange', '1510694220-1510700340',
'--enable-position-stacking',
'--disable-max-market-positions',
'--strategy-list',
'DefaultStrategy',
'TestStrategyLegacy',
]
args = get_args(args)
start_backtesting(args)
# check the logs, that will contain the backtest result
exists = [
'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
'Parameter --timerange detected: 1510694220-1510700340 ...',
f'Using data directory: {testdatadir} ...',
'Using stake_currency: BTC ...',
'Using stake_amount: 0.001 ...',
'Loading data from 2017-11-14T20:57:00+00:00 '
'up to 2017-11-14T22:58:00+00:00 (0 days)..',
'Backtesting with data from 2017-11-14T21:17:00+00:00 '
'up to 2017-11-14T22:58:00+00:00 (0 days)..',
'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy',
'Running backtesting for Strategy TestStrategyLegacy',
]
for line in exists:
assert log_has(line, caplog)
captured = capsys.readouterr()
assert 'BACKTESTING REPORT' in captured.out
assert 'SELL REASON STATS' in captured.out
assert 'LEFT OPEN TRADES REPORT' in captured.out
assert 'STRATEGY SUMMARY' in captured.out

View File

@ -1,11 +1,13 @@
from pathlib import Path from pathlib import Path
import pandas as pd import pandas as pd
import pytest
from arrow import Arrow from arrow import Arrow
from freqtrade.edge import PairInfo from freqtrade.edge import PairInfo
from freqtrade.optimize.optimize_reports import ( from freqtrade.optimize.optimize_reports import (
generate_edge_table, generate_text_table, generate_text_table_sell_reason, generate_pair_metrics, generate_edge_table, generate_sell_reason_stats,
generate_text_table, generate_text_table_sell_reason, generate_strategy_metrics,
generate_text_table_strategy, store_backtest_result) generate_text_table_strategy, store_backtest_result)
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
from tests.conftest import patch_exchange from tests.conftest import patch_exchange
@ -35,12 +37,39 @@ def test_generate_text_table(default_conf, mocker):
'| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |' '| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |'
' 15.00 | 0:20:00 | 2 | 0 | 0 |' ' 15.00 | 0:20:00 | 2 | 0 | 0 |'
) )
assert generate_text_table(data={'ETH/BTC': {}},
stake_currency='BTC', max_open_trades=2, pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
results=results) == result_str max_open_trades=2, results=results)
assert generate_text_table(pair_results,
stake_currency='BTC') == result_str
def test_generate_text_table_sell_reason(default_conf, mocker): def test_generate_pair_metrics(default_conf, mocker):
results = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2],
'profit_abs': [0.2, 0.4],
'trade_duration': [10, 30],
'wins': [2, 0],
'draws': [0, 0],
'losses': [0, 0]
}
)
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
max_open_trades=2, results=results)
assert isinstance(pair_results, list)
assert len(pair_results) == 2
assert pair_results[-1]['key'] == 'TOTAL'
assert (
pytest.approx(pair_results[-1]['profit_mean_pct']) == pair_results[-1]['profit_mean'] * 100)
assert (
pytest.approx(pair_results[-1]['profit_sum_pct']) == pair_results[-1]['profit_sum'] * 100)
def test_generate_text_table_sell_reason(default_conf):
results = pd.DataFrame( results = pd.DataFrame(
{ {
@ -65,8 +94,46 @@ def test_generate_text_table_sell_reason(default_conf, mocker):
'| stop_loss | 1 | 0 | 0 | 1 |' '| stop_loss | 1 | 0 | 0 | 1 |'
' -10 | -10 | -0.2 | -5 |' ' -10 | -10 | -0.2 | -5 |'
) )
assert generate_text_table_sell_reason(stake_currency='BTC', max_open_trades=2,
results=results) == result_str sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
results=results)
assert generate_text_table_sell_reason(sell_reason_stats=sell_reason_stats,
stake_currency='BTC') == result_str
def test_generate_sell_reason_stats(default_conf):
results = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, -0.1],
'profit_abs': [0.2, 0.4, -0.2],
'trade_duration': [10, 30, 10],
'wins': [2, 0, 0],
'draws': [0, 0, 0],
'losses': [0, 0, 1],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}
)
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
results=results)
roi_result = sell_reason_stats[0]
assert roi_result['sell_reason'] == 'roi'
assert roi_result['trades'] == 2
assert pytest.approx(roi_result['profit_mean']) == 0.15
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
assert pytest.approx(roi_result['profit_mean']) == 0.15
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
stop_result = sell_reason_stats[1]
assert stop_result['sell_reason'] == 'stop_loss'
assert stop_result['trades'] == 1
assert pytest.approx(stop_result['profit_mean']) == -0.1
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
assert pytest.approx(stop_result['profit_mean']) == -0.1
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
def test_generate_text_table_strategy(default_conf, mocker): def test_generate_text_table_strategy(default_conf, mocker):
@ -106,7 +173,12 @@ def test_generate_text_table_strategy(default_conf, mocker):
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |' '| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
' 45.00 | 0:20:00 | 3 | 0 | 0 |' ' 45.00 | 0:20:00 | 3 | 0 | 0 |'
) )
assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str
strategy_results = generate_strategy_metrics(stake_currency='BTC',
max_open_trades=2,
all_results=results)
assert generate_text_table_strategy(strategy_results, 'BTC') == result_str
def test_generate_edge_table(edge_conf, mocker): def test_generate_edge_table(edge_conf, mocker):