diff --git a/build_helpers/schema.json b/build_helpers/schema.json index 974ae0431..42beb45ca 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -268,7 +268,8 @@ "day", "week", "month", - "year" + "year", + "weekday" ] } }, diff --git a/docs/commands/backtesting-show.md b/docs/commands/backtesting-show.md index eb9c7137b..31d1d44fd 100644 --- a/docs/commands/backtesting-show.md +++ b/docs/commands/backtesting-show.md @@ -4,7 +4,7 @@ usage: freqtrade backtesting-show [-h] [-v] [--no-color] [--logfile FILE] [-V] [--backtest-filename PATH] [--backtest-directory PATH] [--show-pair-list] - [--breakdown {day,week,month,year} [{day,week,month,year} ...]] + [--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]] options: -h, --help show this help message and exit @@ -18,9 +18,9 @@ options: Directory to use for backtest results. Example: `--export-directory=user_data/backtest_results/`. --show-pair-list Show backtesting pairlist sorted by profit. - --breakdown {day,week,month,year} [{day,week,month,year} ...] + --breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...] Show backtesting breakdown per [day, week, month, - year]. + year, weekday]. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/docs/commands/backtesting.md b/docs/commands/backtesting.md index 925b29da6..2383c3614 100644 --- a/docs/commands/backtesting.md +++ b/docs/commands/backtesting.md @@ -17,7 +17,7 @@ usage: freqtrade backtesting [-h] [-v] [--no-color] [--logfile FILE] [-V] [--export {none,trades,signals}] [--backtest-filename PATH] [--backtest-directory PATH] - [--breakdown {day,week,month,year} [{day,week,month,year} ...]] + [--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]] [--cache {none,day,week,month}] [--freqai-backtest-live-models] [--notes TEXT] @@ -77,9 +77,9 @@ options: --backtest-directory PATH, --export-directory PATH Directory to use for backtest results. Example: `--export-directory=user_data/backtest_results/`. - --breakdown {day,week,month,year} [{day,week,month,year} ...] + --breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...] Show backtesting breakdown per [day, week, month, - year]. + year, weekday]. --cache {none,day,week,month} Load a cached backtest result no older than specified age (default: day). diff --git a/docs/commands/hyperopt-show.md b/docs/commands/hyperopt-show.md index 5cd114c17..354a3afcf 100644 --- a/docs/commands/hyperopt-show.md +++ b/docs/commands/hyperopt-show.md @@ -4,7 +4,7 @@ usage: freqtrade hyperopt-show [-h] [-v] [--no-color] [--logfile FILE] [-V] [--profitable] [-n INT] [--print-json] [--hyperopt-filename FILENAME] [--no-header] [--disable-param-export] - [--breakdown {day,week,month,year} [{day,week,month,year} ...]] + [--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]] options: -h, --help show this help message and exit @@ -18,9 +18,9 @@ options: --no-header Do not print epoch details header. --disable-param-export Disable automatic hyperopt parameter export. - --breakdown {day,week,month,year} [{day,week,month,year} ...] + --breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...] Show backtesting breakdown per [day, week, month, - year]. + year, weekday]. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 9df2c3128..485f85dbd 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -245,7 +245,7 @@ AVAILABLE_CLI_OPTIONS = { ), "backtest_breakdown": Arg( "--breakdown", - help="Show backtesting breakdown per [day, week, month, year].", + help="Show backtesting breakdown per [day, week, month, year, weekday].", nargs="+", choices=constants.BACKTEST_BREAKDOWNS, ), diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 6ce87793a..75d267130 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -61,7 +61,7 @@ AVAILABLE_PAIRLISTS = [ "VolatilityFilter", ] AVAILABLE_DATAHANDLERS = ["json", "jsongz", "feather", "parquet"] -BACKTEST_BREAKDOWNS = ["day", "week", "month", "year"] +BACKTEST_BREAKDOWNS = ["day", "week", "month", "year", "weekday"] BACKTEST_CACHE_AGE = ["none", "day", "week", "month"] BACKTEST_CACHE_DEFAULT = "day" DRY_RUN_WALLET = 1000 diff --git a/freqtrade/optimize/optimize_reports/bt_output.py b/freqtrade/optimize/optimize_reports/bt_output.py index 338fe5ca5..dfae84f90 100644 --- a/freqtrade/optimize/optimize_reports/bt_output.py +++ b/freqtrade/optimize/optimize_reports/bt_output.py @@ -132,8 +132,13 @@ def text_table_periodic_breakdown( :param days_breakdown_stats: Days breakdown metrics :param stake_currency: Stakecurrency used """ + if period == "weekday": + first_column = "Week day" + else: + first_column = period.capitalize() + headers = [ - period.capitalize(), + first_column, "Trades", f"Tot Profit {stake_currency}", "Profit Factor", diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index a53980fb3..57b7740d8 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -256,40 +256,66 @@ def _get_resample_from_period(period: str) -> str: return "1ME" if period == "year": return "1YE" + if period == "weekday": + # Required to pass the test + return "weekday" raise ValueError(f"Period {period} is not supported.") +def _calculate_stats_for_period(data: DataFrame) -> dict[str, Any]: + profit_abs = data["profit_abs"].sum().round(10) + wins = sum(data["profit_abs"] > 0) + draws = sum(data["profit_abs"] == 0) + losses = sum(data["profit_abs"] < 0) + trades = wins + draws + losses + winning_profit = data.loc[data["profit_abs"] > 0, "profit_abs"].sum() + losing_profit = data.loc[data["profit_abs"] < 0, "profit_abs"].sum() + profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0 + + return { + "profit_abs": profit_abs, + "wins": wins, + "draws": draws, + "losses": losses, + "trades": trades, + "profit_factor": round(profit_factor, 8), + } + + def generate_periodic_breakdown_stats( trade_list: list | DataFrame, period: str ) -> list[dict[str, Any]]: results = trade_list if not isinstance(trade_list, list) else DataFrame.from_records(trade_list) if len(results) == 0: return [] + results["close_date"] = to_datetime(results["close_date"], utc=True) - resample_period = _get_resample_from_period(period) - resampled = results.resample(resample_period, on="close_date") - stats = [] - for name, day in resampled: - profit_abs = day["profit_abs"].sum().round(10) - wins = sum(day["profit_abs"] > 0) - draws = sum(day["profit_abs"] == 0) - losses = sum(day["profit_abs"] < 0) - trades = wins + draws + losses - winning_profit = day.loc[day["profit_abs"] > 0, "profit_abs"].sum() - losing_profit = day.loc[day["profit_abs"] < 0, "profit_abs"].sum() - profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0 - stats.append( - { - "date": name.strftime("%d/%m/%Y"), - "date_ts": int(name.to_pydatetime().timestamp() * 1000), - "profit_abs": profit_abs, - "wins": wins, - "draws": draws, - "losses": losses, - "trades": trades, - "profit_factor": round(profit_factor, 8), - } - ) + + if period == "weekday": + day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + results["weekday"] = results["close_date"].dt.dayofweek + + stats = [] + for day_num in range(7): + day_data = results[results["weekday"] == day_num] + if len(day_data) > 0: + period_stats = _calculate_stats_for_period(day_data) + stats.append({"date": day_names[day_num], "date_ts": day_num, **period_stats}) + else: + resample_period = _get_resample_from_period(period) + resampled = results.resample(resample_period, on="close_date") + + stats = [] + for name, period_data in resampled: + period_stats = _calculate_stats_for_period(period_data) + stats.append( + { + "date": name.strftime("%d/%m/%Y"), + "date_ts": int(name.to_pydatetime().timestamp() * 1000), + **period_stats, + } + ) + return stats