From 5a8824171c9239d40777efce6f387a1fbe3d2072 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 18 Nov 2021 20:41:37 +0100 Subject: [PATCH] Add short/long metrics to backtest result --- docs/backtesting.md | 81 ++++++++++++++------------ freqtrade/optimize/optimize_reports.py | 38 ++++++++---- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 49a94b05e..981d4cf5e 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -371,42 +371,48 @@ The last element of the backtest report is the summary metrics table. It contains some useful key metrics about performance of your strategy on backtesting data. ``` -=============== SUMMARY METRICS =============== -| Metric | Value | -|-----------------------+---------------------| -| Backtesting from | 2019-01-01 00:00:00 | -| Backtesting to | 2019-05-01 00:00:00 | -| Max open trades | 3 | -| | | -| Total/Daily Avg Trades| 429 / 3.575 | -| Starting balance | 0.01000000 BTC | -| Final balance | 0.01762792 BTC | -| Absolute profit | 0.00762792 BTC | -| Total profit % | 76.2% | -| Avg. stake amount | 0.001 BTC | -| Total trade volume | 0.429 BTC | -| | | -| Best Pair | LSK/BTC 26.26% | -| Worst Pair | ZEC/BTC -10.18% | -| Best Trade | LSK/BTC 4.25% | -| Worst Trade | ZEC/BTC -10.25% | -| Best day | 0.00076 BTC | -| Worst day | -0.00036 BTC | -| Days win/draw/lose | 12 / 82 / 25 | -| Avg. Duration Winners | 4:23:00 | -| Avg. Duration Loser | 6:55:00 | -| Rejected Buy signals | 3089 | -| | | -| Min balance | 0.00945123 BTC | -| Max balance | 0.01846651 BTC | -| Drawdown | 50.63% | -| Drawdown | 0.0015 BTC | -| Drawdown high | 0.0013 BTC | -| Drawdown low | -0.0002 BTC | -| Drawdown Start | 2019-02-15 14:10:00 | -| Drawdown End | 2019-04-11 18:15:00 | -| Market change | -5.88% | -=============================================== +================ SUMMARY METRICS =============== +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2019-01-01 00:00:00 | +| Backtesting to | 2019-05-01 00:00:00 | +| Max open trades | 3 | +| | | +| Total/Daily Avg Trades | 429 / 3.575 | +| Starting balance | 0.01000000 BTC | +| Final balance | 0.01762792 BTC | +| Absolute profit | 0.00762792 BTC | +| Total profit % | 76.2% | +| Avg. stake amount | 0.001 BTC | +| Total trade volume | 0.429 BTC | +| | | +| Long / Short | 352 / 77 | +| Total profit Long % | 1250.58% | +| Total profit Short % | -15.02% | +| Absolute profit Long | 0.00838792 BTC | +| Absolute profit Short | -0.00076 BTC | +| | | +| Best Pair | LSK/BTC 26.26% | +| Worst Pair | ZEC/BTC -10.18% | +| Best Trade | LSK/BTC 4.25% | +| Worst Trade | ZEC/BTC -10.25% | +| Best day | 0.00076 BTC | +| Worst day | -0.00036 BTC | +| Days win/draw/lose | 12 / 82 / 25 | +| Avg. Duration Winners | 4:23:00 | +| Avg. Duration Loser | 6:55:00 | +| Rejected Buy signals | 3089 | +| | | +| Min balance | 0.00945123 BTC | +| Max balance | 0.01846651 BTC | +| Drawdown | 50.63% | +| Drawdown | 0.0015 BTC | +| Drawdown high | 0.0013 BTC | +| Drawdown low | -0.0002 BTC | +| Drawdown Start | 2019-02-15 14:10:00 | +| Drawdown End | 2019-04-11 18:15:00 | +| Market change | -5.88% | +================================================ ``` @@ -430,6 +436,9 @@ It contains some useful key metrics about performance of your strategy on backte - `Drawdown high` / `Drawdown low`: Profit at the beginning and end of the largest drawdown period. A negative low value means initial capital lost. - `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). - `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column. +- `Long / Short`: Split long/short values (Only shown when short trades were made). +- `Total profit Long %` / `Absolute profit Long`: Profit long trades only (Only shown when short trades were made). +- `Total profit Short %` / `Absolute profit Short`: Profit short trades only (Only shown when short trades were made). ### Daily / Weekly / Monthly breakdown diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 07ff3e993..30feeb5ac 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -415,20 +415,20 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], return {} config = content['config'] max_open_trades = min(config['max_open_trades'], len(btdata.keys())) - starting_balance = config['dry_run_wallet'] + start_balance = config['dry_run_wallet'] stake_currency = config['stake_currency'] pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency, - starting_balance=starting_balance, + starting_balance=start_balance, results=results, skip_nan=False) - buy_tag_results = generate_tag_metrics("buy_tag", starting_balance=starting_balance, + buy_tag_results = generate_tag_metrics("buy_tag", starting_balance=start_balance, results=results, skip_nan=False) sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades, results=results) left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency, - starting_balance=starting_balance, + starting_balance=start_balance, results=results.loc[results['is_open']], skip_nan=True) daily_stats = generate_daily_stats(results) @@ -460,8 +460,12 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], '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_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() / start_balance, + 'profit_total_long': results.loc[~results['is_short'], 'profit_abs'].sum() / start_balance, + 'profit_total_short': results.loc[results['is_short'], 'profit_abs'].sum() / start_balance, 'profit_total_abs': results['profit_abs'].sum(), + 'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(), + 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), 'backtest_start': min_date.strftime(DATETIME_PRINT_FORMAT), 'backtest_start_ts': int(min_date.timestamp() * 1000), 'backtest_end': max_date.strftime(DATETIME_PRINT_FORMAT), @@ -477,8 +481,8 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'stake_amount': config['stake_amount'], 'stake_currency': config['stake_currency'], 'stake_currency_decimals': decimals_per_coin(config['stake_currency']), - 'starting_balance': starting_balance, - 'dry_run_wallet': starting_balance, + 'starting_balance': start_balance, + 'dry_run_wallet': start_balance, 'final_balance': content['final_balance'], 'rejected_signals': content['rejected_signals'], 'max_open_trades': max_open_trades, @@ -522,7 +526,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'max_drawdown_high': high_val, }) - csum_min, csum_max = calculate_csum(results, starting_balance) + csum_min, csum_max = calculate_csum(results, start_balance) strat_stats.update({ 'csum_min': csum_min, 'csum_max': csum_max @@ -711,6 +715,19 @@ def text_table_add_metrics(strat_results: Dict) -> str: best_trade = max(strat_results['trades'], key=lambda x: x['profit_ratio']) worst_trade = min(strat_results['trades'], key=lambda x: x['profit_ratio']) + short_metrics = [ + ('', ''), # Empty line to improve readability + ('Long / Short', + f"{strat_results.get('trade_count_long', 'total_trades')} / " + f"{strat_results.get('trade_count_short', 0)}"), + ('Total profit Long %', f"{strat_results['profit_total_long']:.2%}"), + ('Total profit Short %', f"{strat_results['profit_total_short']:.2%}"), + ('Absolute profit Long', round_coin_value(strat_results['profit_total_long_abs'], + strat_results['stake_currency'])), + ('Absolute profit Short', round_coin_value(strat_results['profit_total_short_abs'], + strat_results['stake_currency'])), + ] if strat_results.get('trade_count_short', 0) > 0 else [] + # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # command stores these results and newer version of freqtrade must be able to handle old # results with missing new fields. @@ -721,9 +738,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('', ''), # Empty line to improve readability ('Total/Daily Avg Trades', f"{strat_results['total_trades']} / {strat_results['trades_per_day']}"), - ('Long / Short', - f"{strat_results.get('trade_count_long', 'total_trades')} / " - f"{strat_results.get('trade_count_short', 0)}"), + ('Starting balance', round_coin_value(strat_results['starting_balance'], strat_results['stake_currency'])), ('Final balance', round_coin_value(strat_results['final_balance'], @@ -738,6 +753,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Total trade volume', round_coin_value(strat_results['total_volume'], strat_results['stake_currency'])), + *short_metrics, ('', ''), # Empty line to improve readability ('Best Pair', f"{strat_results['best_pair']['key']} " f"{strat_results['best_pair']['profit_sum']:.2%}"),