Refactor optimize_report

we should not calculate non-daily statistics in the daily stats method
This commit is contained in:
Matthias 2021-04-25 19:28:32 +02:00
parent 9994fce577
commit 545cba7fd8
2 changed files with 51 additions and 12 deletions

View File

@ -194,7 +194,37 @@ def generate_edge_table(results: dict) -> str:
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
""" Generate overall trade statistics """
if len(results) == 0:
return {
'wins': 0,
'losses': 0,
'draws': 0,
'holding_avg': timedelta(),
'winner_holding_avg': timedelta(),
'loser_holding_avg': timedelta(),
}
winning_trades = results.loc[results['profit_ratio'] > 0]
draw_trades = results.loc[results['profit_ratio'] == 0]
losing_trades = results.loc[results['profit_ratio'] < 0]
return {
'wins': len(winning_trades),
'losses': len(losing_trades),
'draws': len(draw_trades),
'holding_avg': (timedelta(minutes=round(results['trade_duration'].mean()))
if not results.empty else timedelta()),
'winner_holding_avg': (timedelta(minutes=round(winning_trades['trade_duration'].mean()))
if not winning_trades.empty else timedelta()),
'loser_holding_avg': (timedelta(minutes=round(losing_trades['trade_duration'].mean()))
if not losing_trades.empty else timedelta()),
}
def generate_daily_stats(results: DataFrame) -> Dict[str, Any]: def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
""" Generate daily statistics """
if len(results) == 0: if len(results) == 0:
return { return {
'backtest_best_day': 0, 'backtest_best_day': 0,
@ -204,8 +234,6 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
'winning_days': 0, 'winning_days': 0,
'draw_days': 0, 'draw_days': 0,
'losing_days': 0, 'losing_days': 0,
'winner_holding_avg': timedelta(),
'loser_holding_avg': timedelta(),
} }
daily_profit_rel = results.resample('1d', on='close_date')['profit_ratio'].sum() daily_profit_rel = results.resample('1d', on='close_date')['profit_ratio'].sum()
daily_profit = results.resample('1d', on='close_date')['profit_abs'].sum().round(10) daily_profit = results.resample('1d', on='close_date')['profit_abs'].sum().round(10)
@ -217,9 +245,6 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
draw_days = sum(daily_profit == 0) draw_days = sum(daily_profit == 0)
losing_days = sum(daily_profit < 0) losing_days = sum(daily_profit < 0)
winning_trades = results.loc[results['profit_ratio'] > 0]
losing_trades = results.loc[results['profit_ratio'] < 0]
return { return {
'backtest_best_day': best_rel, 'backtest_best_day': best_rel,
'backtest_worst_day': worst_rel, 'backtest_worst_day': worst_rel,
@ -228,10 +253,6 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
'winning_days': winning_days, 'winning_days': winning_days,
'draw_days': draw_days, 'draw_days': draw_days,
'losing_days': losing_days, 'losing_days': losing_days,
'winner_holding_avg': (timedelta(minutes=round(winning_trades['trade_duration'].mean()))
if not winning_trades.empty else timedelta()),
'loser_holding_avg': (timedelta(minutes=round(losing_trades['trade_duration'].mean()))
if not losing_trades.empty else timedelta()),
} }
@ -269,6 +290,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
results=results.loc[results['is_open']], results=results.loc[results['is_open']],
skip_nan=True) skip_nan=True)
daily_stats = generate_daily_stats(results) daily_stats = generate_daily_stats(results)
trade_stats = generate_trading_stats(results)
best_pair = max([pair for pair in pair_results if pair['key'] != 'TOTAL'], best_pair = max([pair for pair in pair_results if pair['key'] != 'TOTAL'],
key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None
worst_pair = min([pair for pair in pair_results if pair['key'] != 'TOTAL'], worst_pair = min([pair for pair in pair_results if pair['key'] != 'TOTAL'],
@ -289,6 +311,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
'total_volume': float(results['stake_amount'].sum()), 'total_volume': float(results['stake_amount'].sum()),
'avg_stake_amount': results['stake_amount'].mean() if len(results) > 0 else 0, 'avg_stake_amount': results['stake_amount'].mean() if len(results) > 0 else 0,
'profit_mean': results['profit_ratio'].mean() if len(results) > 0 else 0, 'profit_mean': results['profit_ratio'].mean() if len(results) > 0 else 0,
'profit_median': results['profit_ratio'].median() if len(results) > 0 else 0,
'profit_total': results['profit_abs'].sum() / starting_balance, 'profit_total': results['profit_abs'].sum() / starting_balance,
'profit_total_abs': results['profit_abs'].sum(), 'profit_total_abs': results['profit_abs'].sum(),
'backtest_start': min_date.datetime, 'backtest_start': min_date.datetime,
@ -329,6 +352,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
'sell_profit_offset': config['ask_strategy']['sell_profit_offset'], 'sell_profit_offset': config['ask_strategy']['sell_profit_offset'],
'ignore_roi_if_buy_signal': config['ask_strategy']['ignore_roi_if_buy_signal'], 'ignore_roi_if_buy_signal': config['ask_strategy']['ignore_roi_if_buy_signal'],
**daily_stats, **daily_stats,
**trade_stats
} }
try: try:

View File

@ -14,7 +14,7 @@ from freqtrade.edge import PairInfo
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats, from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats,
generate_edge_table, generate_pair_metrics, generate_edge_table, generate_pair_metrics,
generate_sell_reason_stats, generate_sell_reason_stats,
generate_strategy_comparison, store_backtest_stats, generate_strategy_comparison, generate_trading_stats, store_backtest_stats,
text_table_bt_results, text_table_sell_reason, text_table_bt_results, text_table_sell_reason,
text_table_strategy) text_table_strategy)
from freqtrade.resolvers.strategy_resolver import StrategyResolver from freqtrade.resolvers.strategy_resolver import StrategyResolver
@ -226,8 +226,6 @@ def test_generate_daily_stats(testdatadir):
assert res['winning_days'] == 14 assert res['winning_days'] == 14
assert res['draw_days'] == 4 assert res['draw_days'] == 4
assert res['losing_days'] == 3 assert res['losing_days'] == 3
assert res['winner_holding_avg'] == timedelta(seconds=1440)
assert res['loser_holding_avg'] == timedelta(days=1, seconds=21420)
# Select empty dataframe! # Select empty dataframe!
res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :]) res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
@ -238,6 +236,23 @@ def test_generate_daily_stats(testdatadir):
assert res['losing_days'] == 0 assert res['losing_days'] == 0
def test_generate_trading_stats(testdatadir):
filename = testdatadir / "backtest-result_new.json"
bt_data = load_backtest_data(filename)
res = generate_trading_stats(bt_data)
assert isinstance(res, dict)
assert res['winner_holding_avg'] == timedelta(seconds=1440)
assert res['loser_holding_avg'] == timedelta(days=1, seconds=21420)
assert 'wins' in res
assert 'losses' in res
assert 'draws' in res
# Select empty dataframe!
res = generate_trading_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
assert res['wins'] == 0
assert res['losses'] == 0
def test_text_table_sell_reason(): def test_text_table_sell_reason():
results = pd.DataFrame( results = pd.DataFrame(