Skip to content

Commit 5df0530

Browse files
authored
Merge pull request freqtrade#12389 from mrpabloyeah/add-day-of-week-to-backtest-breakdowns
Add day of week to backtest breakdowns
2 parents db11844 + 522fa09 commit 5df0530

File tree

8 files changed

+82
-36
lines changed

8 files changed

+82
-36
lines changed

build_helpers/schema.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@
268268
"day",
269269
"week",
270270
"month",
271-
"year"
271+
"year",
272+
"weekday"
272273
]
273274
}
274275
},

docs/commands/backtesting-show.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ usage: freqtrade backtesting-show [-h] [-v] [--no-color] [--logfile FILE] [-V]
44
[--backtest-filename PATH]
55
[--backtest-directory PATH]
66
[--show-pair-list]
7-
[--breakdown {day,week,month,year} [{day,week,month,year} ...]]
7+
[--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]]
88
99
options:
1010
-h, --help show this help message and exit
@@ -18,9 +18,9 @@ options:
1818
Directory to use for backtest results. Example:
1919
`--export-directory=user_data/backtest_results/`.
2020
--show-pair-list Show backtesting pairlist sorted by profit.
21-
--breakdown {day,week,month,year} [{day,week,month,year} ...]
21+
--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]
2222
Show backtesting breakdown per [day, week, month,
23-
year].
23+
year, weekday].
2424
2525
Common arguments:
2626
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).

docs/commands/backtesting.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ usage: freqtrade backtesting [-h] [-v] [--no-color] [--logfile FILE] [-V]
1717
[--export {none,trades,signals}]
1818
[--backtest-filename PATH]
1919
[--backtest-directory PATH]
20-
[--breakdown {day,week,month,year} [{day,week,month,year} ...]]
20+
[--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]]
2121
[--cache {none,day,week,month}]
2222
[--freqai-backtest-live-models] [--notes TEXT]
2323
@@ -77,9 +77,9 @@ options:
7777
--backtest-directory PATH, --export-directory PATH
7878
Directory to use for backtest results. Example:
7979
`--export-directory=user_data/backtest_results/`.
80-
--breakdown {day,week,month,year} [{day,week,month,year} ...]
80+
--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]
8181
Show backtesting breakdown per [day, week, month,
82-
year].
82+
year, weekday].
8383
--cache {none,day,week,month}
8484
Load a cached backtest result no older than specified
8585
age (default: day).

docs/commands/hyperopt-show.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ usage: freqtrade hyperopt-show [-h] [-v] [--no-color] [--logfile FILE] [-V]
44
[--profitable] [-n INT] [--print-json]
55
[--hyperopt-filename FILENAME] [--no-header]
66
[--disable-param-export]
7-
[--breakdown {day,week,month,year} [{day,week,month,year} ...]]
7+
[--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]]
88
99
options:
1010
-h, --help show this help message and exit
@@ -18,9 +18,9 @@ options:
1818
--no-header Do not print epoch details header.
1919
--disable-param-export
2020
Disable automatic hyperopt parameter export.
21-
--breakdown {day,week,month,year} [{day,week,month,year} ...]
21+
--breakdown {day,week,month,year,weekday} [{day,week,month,year,weekday} ...]
2222
Show backtesting breakdown per [day, week, month,
23-
year].
23+
year, weekday].
2424
2525
Common arguments:
2626
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).

freqtrade/commands/cli_options.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def __init__(self, *args, **kwargs):
245245
),
246246
"backtest_breakdown": Arg(
247247
"--breakdown",
248-
help="Show backtesting breakdown per [day, week, month, year].",
248+
help="Show backtesting breakdown per [day, week, month, year, weekday].",
249249
nargs="+",
250250
choices=constants.BACKTEST_BREAKDOWNS,
251251
),

freqtrade/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"VolatilityFilter",
6262
]
6363
AVAILABLE_DATAHANDLERS = ["json", "jsongz", "feather", "parquet"]
64-
BACKTEST_BREAKDOWNS = ["day", "week", "month", "year"]
64+
BACKTEST_BREAKDOWNS = ["day", "week", "month", "year", "weekday"]
6565
BACKTEST_CACHE_AGE = ["none", "day", "week", "month"]
6666
BACKTEST_CACHE_DEFAULT = "day"
6767
DRY_RUN_WALLET = 1000

freqtrade/optimize/optimize_reports/optimize_reports.py

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -256,40 +256,66 @@ def _get_resample_from_period(period: str) -> str:
256256
return "1ME"
257257
if period == "year":
258258
return "1YE"
259+
if period == "weekday":
260+
# Required to pass the test
261+
return "weekday"
259262
raise ValueError(f"Period {period} is not supported.")
260263

261264

265+
def _calculate_stats_for_period(data: DataFrame) -> dict[str, Any]:
266+
profit_abs = data["profit_abs"].sum().round(10)
267+
wins = sum(data["profit_abs"] > 0)
268+
draws = sum(data["profit_abs"] == 0)
269+
losses = sum(data["profit_abs"] < 0)
270+
trades = wins + draws + losses
271+
winning_profit = data.loc[data["profit_abs"] > 0, "profit_abs"].sum()
272+
losing_profit = data.loc[data["profit_abs"] < 0, "profit_abs"].sum()
273+
profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0
274+
275+
return {
276+
"profit_abs": profit_abs,
277+
"wins": wins,
278+
"draws": draws,
279+
"losses": losses,
280+
"trades": trades,
281+
"profit_factor": round(profit_factor, 8),
282+
}
283+
284+
262285
def generate_periodic_breakdown_stats(
263286
trade_list: list | DataFrame, period: str
264287
) -> list[dict[str, Any]]:
265288
results = trade_list if not isinstance(trade_list, list) else DataFrame.from_records(trade_list)
266289
if len(results) == 0:
267290
return []
291+
268292
results["close_date"] = to_datetime(results["close_date"], utc=True)
269-
resample_period = _get_resample_from_period(period)
270-
resampled = results.resample(resample_period, on="close_date")
271-
stats = []
272-
for name, day in resampled:
273-
profit_abs = day["profit_abs"].sum().round(10)
274-
wins = sum(day["profit_abs"] > 0)
275-
draws = sum(day["profit_abs"] == 0)
276-
losses = sum(day["profit_abs"] < 0)
277-
trades = wins + draws + losses
278-
winning_profit = day.loc[day["profit_abs"] > 0, "profit_abs"].sum()
279-
losing_profit = day.loc[day["profit_abs"] < 0, "profit_abs"].sum()
280-
profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0
281-
stats.append(
282-
{
283-
"date": name.strftime("%d/%m/%Y"),
284-
"date_ts": int(name.to_pydatetime().timestamp() * 1000),
285-
"profit_abs": profit_abs,
286-
"wins": wins,
287-
"draws": draws,
288-
"losses": losses,
289-
"trades": trades,
290-
"profit_factor": round(profit_factor, 8),
291-
}
292-
)
293+
294+
if period == "weekday":
295+
day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
296+
results["weekday"] = results["close_date"].dt.dayofweek
297+
298+
stats = []
299+
for day_num in range(7):
300+
day_data = results[results["weekday"] == day_num]
301+
if len(day_data) > 0:
302+
period_stats = _calculate_stats_for_period(day_data)
303+
stats.append({"date": day_names[day_num], "date_ts": day_num, **period_stats})
304+
else:
305+
resample_period = _get_resample_from_period(period)
306+
resampled = results.resample(resample_period, on="close_date")
307+
308+
stats = []
309+
for name, period_data in resampled:
310+
period_stats = _calculate_stats_for_period(period_data)
311+
stats.append(
312+
{
313+
"date": name.strftime("%d/%m/%Y"),
314+
"date_ts": int(name.to_pydatetime().timestamp() * 1000),
315+
**period_stats,
316+
}
317+
)
318+
293319
return stats
294320

295321

tests/optimize/test_optimize_reports.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,30 @@ def test_generate_periodic_breakdown_stats(testdatadir):
634634
res = generate_periodic_breakdown_stats([], "day")
635635
assert res == []
636636

637+
# Test weekday
638+
reswd = generate_periodic_breakdown_stats(bt_data, "weekday")
639+
assert isinstance(reswd, list)
640+
assert len(reswd) == 7
641+
assert reswd[0]["date"] == "Monday"
642+
assert reswd[0]["date_ts"] == 0
643+
assert reswd[1]["date"] == "Tuesday"
644+
assert reswd[2]["date"] == "Wednesday"
645+
assert reswd[3]["date"] == "Thursday"
646+
assert reswd[4]["date"] == "Friday"
647+
assert reswd[5]["date"] == "Saturday"
648+
assert reswd[6]["date"] == "Sunday"
649+
monday = reswd[0]
650+
assert "draws" in monday
651+
assert "losses" in monday
652+
assert "wins" in monday
653+
assert "profit_abs" in monday
654+
637655

638656
def test__get_resample_from_period():
639657
assert _get_resample_from_period("day") == "1d"
640658
assert _get_resample_from_period("week") == "1W-MON"
641659
assert _get_resample_from_period("month") == "1ME"
660+
assert _get_resample_from_period("weekday") == "weekday"
642661
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
643662
_get_resample_from_period("noooo")
644663

0 commit comments

Comments
 (0)