Merge pull request #3452 from freqtrade/bt_report_sorting

Optimize sorting, rename column when loading backtest data
This commit is contained in:
hroff-1902 2020-06-08 05:09:03 +03:00 committed by GitHub
commit 143197c5d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 140 additions and 131 deletions

View File

@ -63,8 +63,8 @@ class SuperDuperHyperOptLoss(IHyperOptLoss):
* 0.25: Avoiding trade loss * 0.25: Avoiding trade loss
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above * 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
""" """
total_profit = results.profit_percent.sum() total_profit = results['profit_percent'].sum()
trade_duration = results.trade_duration.mean() trade_duration = results['trade_duration'].mean()
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8) trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT) profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)

View File

@ -16,7 +16,7 @@ from freqtrade.persistence import Trade
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# must align with columns in backtest.py # must align with columns in backtest.py
BT_DATA_COLUMNS = ["pair", "profitperc", "open_time", "close_time", "index", "duration", BT_DATA_COLUMNS = ["pair", "profit_percent", "open_time", "close_time", "index", "duration",
"open_rate", "close_rate", "open_at_end", "sell_reason"] "open_rate", "close_rate", "open_at_end", "sell_reason"]
@ -99,7 +99,7 @@ def load_trades_from_db(db_url: str) -> pd.DataFrame:
trades: pd.DataFrame = pd.DataFrame([], columns=BT_DATA_COLUMNS) trades: pd.DataFrame = pd.DataFrame([], columns=BT_DATA_COLUMNS)
persistence.init(db_url, clean_open_orders=False) persistence.init(db_url, clean_open_orders=False)
columns = ["pair", "open_time", "close_time", "profit", "profitperc", columns = ["pair", "open_time", "close_time", "profit", "profit_percent",
"open_rate", "close_rate", "amount", "duration", "sell_reason", "open_rate", "close_rate", "amount", "duration", "sell_reason",
"fee_open", "fee_close", "open_rate_requested", "close_rate_requested", "fee_open", "fee_close", "open_rate_requested", "close_rate_requested",
"stake_amount", "max_rate", "min_rate", "id", "exchange", "stake_amount", "max_rate", "min_rate", "id", "exchange",
@ -190,7 +190,7 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
""" """
Adds a column `col_name` with the cumulative profit for the given trades array. Adds a column `col_name` with the cumulative profit for the given trades array.
:param df: DataFrame with date index :param df: DataFrame with date index
:param trades: DataFrame containing trades (requires columns close_time and profitperc) :param trades: DataFrame containing trades (requires columns close_time and profit_percent)
:param col_name: Column name that will be assigned the results :param col_name: Column name that will be assigned the results
:param timeframe: Timeframe used during the operations :param timeframe: Timeframe used during the operations
:return: Returns df with one additional column, col_name, containing the cumulative profit. :return: Returns df with one additional column, col_name, containing the cumulative profit.
@ -201,7 +201,8 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
timeframe_minutes = timeframe_to_minutes(timeframe) timeframe_minutes = timeframe_to_minutes(timeframe)
# Resample to timeframe to make sure trades match candles # Resample to timeframe to make sure trades match candles
_trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_time')[['profitperc']].sum() _trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_time'
)[['profit_percent']].sum()
df.loc[:, col_name] = _trades_sum.cumsum() df.loc[:, col_name] = _trades_sum.cumsum()
# Set first value to 0 # Set first value to 0
df.loc[df.iloc[0].name, col_name] = 0 df.loc[df.iloc[0].name, col_name] = 0
@ -211,13 +212,13 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_time', def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_time',
value_col: str = 'profitperc' value_col: str = 'profit_percent'
) -> Tuple[float, pd.Timestamp, pd.Timestamp]: ) -> Tuple[float, pd.Timestamp, pd.Timestamp]:
""" """
Calculate max drawdown and the corresponding close dates Calculate max drawdown and the corresponding close dates
:param trades: DataFrame containing trades (requires columns close_time and profitperc) :param trades: DataFrame containing trades (requires columns close_time and profit_percent)
:param date_col: Column in DataFrame to use for dates (defaults to 'close_time') :param date_col: Column in DataFrame to use for dates (defaults to 'close_time')
:param value_col: Column in DataFrame to use for values (defaults to 'profitperc') :param value_col: Column in DataFrame to use for values (defaults to 'profit_percent')
:return: Tuple (float, highdate, lowdate) with absolute max drawdown, high and low time :return: Tuple (float, highdate, lowdate) with absolute max drawdown, high and low time
:raise: ValueError if trade-dataframe was found empty. :raise: ValueError if trade-dataframe was found empty.
""" """

View File

@ -42,8 +42,8 @@ class DefaultHyperOptLoss(IHyperOptLoss):
* 0.25: Avoiding trade loss * 0.25: Avoiding trade loss
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above * 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
""" """
total_profit = results.profit_percent.sum() total_profit = results['profit_percent'].sum()
trade_duration = results.trade_duration.mean() trade_duration = results['trade_duration'].mean()
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8) trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT) profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)

View File

@ -34,5 +34,5 @@ class OnlyProfitHyperOptLoss(IHyperOptLoss):
""" """
Objective function, returns smaller number for better results. Objective function, returns smaller number for better results.
""" """
total_profit = results.profit_percent.sum() total_profit = results['profit_percent'].sum()
return 1 - total_profit / EXPECTED_MAX_PROFIT return 1 - total_profit / EXPECTED_MAX_PROFIT

View File

@ -65,25 +65,25 @@ def _generate_result_line(result: DataFrame, max_open_trades: int, first_column:
""" """
return { return {
'key': first_column, 'key': first_column,
'trades': len(result.index), 'trades': len(result),
'profit_mean': result.profit_percent.mean(), 'profit_mean': result['profit_percent'].mean(),
'profit_mean_pct': result.profit_percent.mean() * 100.0, 'profit_mean_pct': result['profit_percent'].mean() * 100.0,
'profit_sum': result.profit_percent.sum(), 'profit_sum': result['profit_percent'].sum(),
'profit_sum_pct': result.profit_percent.sum() * 100.0, 'profit_sum_pct': result['profit_percent'].sum() * 100.0,
'profit_total_abs': result.profit_abs.sum(), 'profit_total_abs': result['profit_abs'].sum(),
'profit_total_pct': result.profit_percent.sum() * 100.0 / max_open_trades, 'profit_total_pct': result['profit_percent'].sum() * 100.0 / max_open_trades,
'duration_avg': str(timedelta( 'duration_avg': str(timedelta(
minutes=round(result.trade_duration.mean())) minutes=round(result['trade_duration'].mean()))
) if not result.empty else '0:00', ) if not result.empty else '0:00',
# 'duration_max': str(timedelta( # 'duration_max': str(timedelta(
# minutes=round(result.trade_duration.max())) # minutes=round(result['trade_duration'].max()))
# ) if not result.empty else '0:00', # ) if not result.empty else '0:00',
# 'duration_min': str(timedelta( # 'duration_min': str(timedelta(
# minutes=round(result.trade_duration.min())) # minutes=round(result['trade_duration'].min()))
# ) if not result.empty else '0:00', # ) if not result.empty else '0:00',
'wins': len(result[result.profit_abs > 0]), 'wins': len(result[result['profit_abs'] > 0]),
'draws': len(result[result.profit_abs == 0]), 'draws': len(result[result['profit_abs'] == 0]),
'losses': len(result[result.profit_abs < 0]), 'losses': len(result[result['profit_abs'] < 0]),
} }
@ -102,8 +102,8 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, max_open_t
tabular_data = [] tabular_data = []
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(_generate_result_line(result, max_open_trades, pair)) tabular_data.append(_generate_result_line(result, max_open_trades, pair))
@ -113,25 +113,6 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, max_open_t
return tabular_data return tabular_data
def generate_text_table(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
"""
Generates and returns a text table for the given backtest data and the results dataframe
:param pair_results: List of Dictionaries - one entry per pair + final TOTAL row
:param stake_currency: stake-currency - used to correctly name headers
:return: pretty printed table with tabulate as string
"""
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
return tabulate(output, headers=headers,
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]: def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
""" """
Generate small table outlining Backtest results Generate small table outlining Backtest results
@ -166,33 +147,6 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
return tabular_data return tabular_data
def generate_text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]],
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, def generate_strategy_metrics(stake_currency: str, max_open_trades: int,
all_results: Dict) -> List[Dict]: all_results: Dict) -> List[Dict]:
""" """
@ -209,26 +163,6 @@ def generate_strategy_metrics(stake_currency: str, max_open_trades: int,
return tabular_data return tabular_data
def generate_text_table_strategy(strategy_results, stake_currency: str) -> str:
"""
Generate summary table 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: pretty printed table with tabulate as string
"""
floatfmt = _get_line_floatfmt()
headers = _get_line_header('Strategy', stake_currency)
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 strategy_results]
# Ignore type as floatfmt does allow tuples but mypy does not know that
return tabulate(output, headers=headers,
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', 'd') floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', 'd', 'd')
@ -256,7 +190,14 @@ def generate_edge_table(results: dict) -> str:
def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame], def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame],
all_results: Dict[str, DataFrame]): all_results: Dict[str, DataFrame]) -> Dict[str, Any]:
"""
:param config: Configuration object used for backtest
:param btdata: Backtest data
:param all_results: backtest result - dictionary with { Strategy: results}.
:return:
Dictionary containing results per strategy and a stratgy summary.
"""
stake_currency = config['stake_currency'] stake_currency = config['stake_currency']
max_open_trades = config['max_open_trades'] max_open_trades = config['max_open_trades']
result: Dict[str, Any] = {'strategy': {}} result: Dict[str, Any] = {'strategy': {}}
@ -288,6 +229,75 @@ def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame],
return result return result
###
# Start output section
###
def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
"""
Generates and returns a text table for the given backtest data and the results dataframe
:param pair_results: List of Dictionaries - one entry per pair + final TOTAL row
:param stake_currency: stake-currency - used to correctly name headers
:return: pretty printed table with tabulate as string
"""
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
return tabulate(output, headers=headers,
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], 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 text_table_strategy(strategy_results, stake_currency: str) -> str:
"""
Generate summary table 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: pretty printed table with tabulate as string
"""
floatfmt = _get_line_floatfmt()
headers = _get_line_header('Strategy', stake_currency)
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 strategy_results]
# Ignore type as floatfmt does allow tuples but mypy does not know that
return tabulate(output, headers=headers,
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
def show_backtest_results(config: Dict, backtest_stats: Dict): def show_backtest_results(config: Dict, backtest_stats: Dict):
stake_currency = config['stake_currency'] stake_currency = config['stake_currency']
@ -295,19 +305,18 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
# Print results # Print results
print(f"Result for strategy {strategy}") print(f"Result for strategy {strategy}")
table = generate_text_table(results['results_per_pair'], stake_currency=stake_currency) table = text_table_bt_results(results['results_per_pair'], 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(sell_reason_stats=results['sell_reason_summary'], table = text_table_sell_reason(sell_reason_stats=results['sell_reason_summary'],
stake_currency=stake_currency, stake_currency=stake_currency)
)
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(results['left_open_trades'], stake_currency=stake_currency) table = text_table_bt_results(results['left_open_trades'], stake_currency=stake_currency)
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)
@ -318,7 +327,7 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
if len(backtest_stats['strategy']) > 1: if len(backtest_stats['strategy']) > 1:
# Print Strategy summary table # Print Strategy summary table
table = generate_text_table_strategy(backtest_stats['strategy_comparison'], stake_currency) table = text_table_strategy(backtest_stats['strategy_comparison'], 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

@ -162,7 +162,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
# Trades can be empty # Trades can be empty
if trades is not None and len(trades) > 0: if trades is not None and len(trades) > 0:
# Create description for sell summarizing the trade # Create description for sell summarizing the trade
trades['desc'] = trades.apply(lambda row: f"{round(row['profitperc'] * 100, 1)}%, " trades['desc'] = trades.apply(lambda row: f"{round(row['profit_percent'] * 100, 1)}%, "
f"{row['sell_reason']}, {row['duration']} min", f"{row['sell_reason']}, {row['duration']} min",
axis=1) axis=1)
trade_buys = go.Scatter( trade_buys = go.Scatter(
@ -181,9 +181,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
) )
trade_sells = go.Scatter( trade_sells = go.Scatter(
x=trades.loc[trades['profitperc'] > 0, "close_time"], x=trades.loc[trades['profit_percent'] > 0, "close_time"],
y=trades.loc[trades['profitperc'] > 0, "close_rate"], y=trades.loc[trades['profit_percent'] > 0, "close_rate"],
text=trades.loc[trades['profitperc'] > 0, "desc"], text=trades.loc[trades['profit_percent'] > 0, "desc"],
mode='markers', mode='markers',
name='Sell - Profit', name='Sell - Profit',
marker=dict( marker=dict(
@ -194,9 +194,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
) )
) )
trade_sells_loss = go.Scatter( trade_sells_loss = go.Scatter(
x=trades.loc[trades['profitperc'] <= 0, "close_time"], x=trades.loc[trades['profit_percent'] <= 0, "close_time"],
y=trades.loc[trades['profitperc'] <= 0, "close_rate"], y=trades.loc[trades['profit_percent'] <= 0, "close_rate"],
text=trades.loc[trades['profitperc'] <= 0, "desc"], text=trades.loc[trades['profit_percent'] <= 0, "desc"],
mode='markers', mode='markers',
name='Sell - Loss', name='Sell - Loss',
marker=dict( marker=dict(

View File

@ -47,7 +47,7 @@ def test_load_trades_from_db(default_conf, fee, mocker):
assert isinstance(trades, DataFrame) assert isinstance(trades, DataFrame)
assert "pair" in trades.columns assert "pair" in trades.columns
assert "open_time" in trades.columns assert "open_time" in trades.columns
assert "profitperc" in trades.columns assert "profit_percent" in trades.columns
for col in BT_DATA_COLUMNS: for col in BT_DATA_COLUMNS:
if col not in ['index', 'open_at_end']: if col not in ['index', 'open_at_end']:

View File

@ -659,17 +659,17 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist', mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist',
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() text_table_mock = MagicMock()
sell_reason_mock = MagicMock() sell_reason_mock = MagicMock()
gen_strattable_mock = MagicMock() strattable_mock = MagicMock()
gen_strat_summary = MagicMock() strat_summary = MagicMock()
mocker.patch.multiple('freqtrade.optimize.optimize_reports', mocker.patch.multiple('freqtrade.optimize.optimize_reports',
generate_text_table=gen_table_mock, text_table_bt_results=text_table_mock,
generate_text_table_strategy=gen_strattable_mock, text_table_strategy=strattable_mock,
generate_pair_metrics=MagicMock(), generate_pair_metrics=MagicMock(),
generate_sell_reason_stats=sell_reason_mock, generate_sell_reason_stats=sell_reason_mock,
generate_strategy_metrics=gen_strat_summary, generate_strategy_metrics=strat_summary,
) )
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
@ -690,10 +690,10 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
start_backtesting(args) start_backtesting(args)
# 2 backtests, 4 tables # 2 backtests, 4 tables
assert backtestmock.call_count == 2 assert backtestmock.call_count == 2
assert gen_table_mock.call_count == 4 assert text_table_mock.call_count == 4
assert gen_strattable_mock.call_count == 1 assert strattable_mock.call_count == 1
assert sell_reason_mock.call_count == 2 assert sell_reason_mock.call_count == 2
assert gen_strat_summary.call_count == 1 assert 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 = [

View File

@ -7,13 +7,13 @@ 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_pair_metrics, generate_edge_table, generate_sell_reason_stats, generate_pair_metrics, generate_edge_table, generate_sell_reason_stats,
generate_text_table, generate_text_table_sell_reason, generate_strategy_metrics, text_table_bt_results, text_table_sell_reason, generate_strategy_metrics,
generate_text_table_strategy, store_backtest_result) 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
def test_generate_text_table(default_conf, mocker): def test_text_table_bt_results(default_conf, mocker):
results = pd.DataFrame( results = pd.DataFrame(
{ {
@ -40,8 +40,7 @@ def test_generate_text_table(default_conf, mocker):
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC', pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
max_open_trades=2, results=results) max_open_trades=2, results=results)
assert generate_text_table(pair_results, assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
stake_currency='BTC') == result_str
def test_generate_pair_metrics(default_conf, mocker): def test_generate_pair_metrics(default_conf, mocker):
@ -69,7 +68,7 @@ def test_generate_pair_metrics(default_conf, mocker):
pytest.approx(pair_results[-1]['profit_sum_pct']) == pair_results[-1]['profit_sum'] * 100) pytest.approx(pair_results[-1]['profit_sum_pct']) == pair_results[-1]['profit_sum'] * 100)
def test_generate_text_table_sell_reason(default_conf): def test_text_table_sell_reason(default_conf):
results = pd.DataFrame( results = pd.DataFrame(
{ {
@ -97,7 +96,7 @@ def test_generate_text_table_sell_reason(default_conf):
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2, sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
results=results) results=results)
assert generate_text_table_sell_reason(sell_reason_stats=sell_reason_stats, assert text_table_sell_reason(sell_reason_stats=sell_reason_stats,
stake_currency='BTC') == result_str stake_currency='BTC') == result_str
@ -136,7 +135,7 @@ def test_generate_sell_reason_stats(default_conf):
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2) assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
def test_generate_text_table_strategy(default_conf, mocker): def test_text_table_strategy(default_conf, mocker):
results = {} results = {}
results['TestStrategy1'] = pd.DataFrame( results['TestStrategy1'] = pd.DataFrame(
{ {
@ -178,7 +177,7 @@ def test_generate_text_table_strategy(default_conf, mocker):
max_open_trades=2, max_open_trades=2,
all_results=results) all_results=results)
assert generate_text_table_strategy(strategy_results, 'BTC') == result_str assert text_table_strategy(strategy_results, 'BTC') == result_str
def test_generate_edge_table(edge_conf, mocker): def test_generate_edge_table(edge_conf, mocker):

View File

@ -298,7 +298,7 @@ def test_calc_profit(limit_buy_order, limit_sell_order, fee):
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' trade.open_order_id = 'something'
trade.update(limit_buy_order) # Buy @ 0.00001099 trade.update(limit_buy_order) # Buy @ 0.00001099
# Custom closing rate and regular fee rate # Custom closing rate and regular fee rate
@ -332,7 +332,7 @@ def test_calc_profit_ratio(limit_buy_order, limit_sell_order, fee):
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' trade.open_order_id = 'something'
trade.update(limit_buy_order) # Buy @ 0.00001099 trade.update(limit_buy_order) # Buy @ 0.00001099
# Get percent of profit with a custom rate (Higher than open rate) # Get percent of profit with a custom rate (Higher than open rate)

View File

@ -124,7 +124,7 @@ def test_plot_trades(testdatadir, caplog):
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit') trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
assert isinstance(trade_sell, go.Scatter) assert isinstance(trade_sell, go.Scatter)
assert trade_sell.yaxis == 'y' assert trade_sell.yaxis == 'y'
assert len(trades.loc[trades['profitperc'] > 0]) == len(trade_sell.x) assert len(trades.loc[trades['profit_percent'] > 0]) == len(trade_sell.x)
assert trade_sell.marker.color == 'green' assert trade_sell.marker.color == 'green'
assert trade_sell.marker.symbol == 'square-open' assert trade_sell.marker.symbol == 'square-open'
assert trade_sell.text[0] == '4.0%, roi, 15 min' assert trade_sell.text[0] == '4.0%, roi, 15 min'
@ -132,7 +132,7 @@ def test_plot_trades(testdatadir, caplog):
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss') trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
assert isinstance(trade_sell_loss, go.Scatter) assert isinstance(trade_sell_loss, go.Scatter)
assert trade_sell_loss.yaxis == 'y' assert trade_sell_loss.yaxis == 'y'
assert len(trades.loc[trades['profitperc'] <= 0]) == len(trade_sell_loss.x) assert len(trades.loc[trades['profit_percent'] <= 0]) == len(trade_sell_loss.x)
assert trade_sell_loss.marker.color == 'red' assert trade_sell_loss.marker.color == 'red'
assert trade_sell_loss.marker.symbol == 'square-open' assert trade_sell_loss.marker.symbol == 'square-open'
assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min' assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min'