Skip to content

Commit 2fd703f

Browse files
authored
Merge pull request freqtrade#12170 from mrpabloyeah/some-improvements-to-backtest-summary-metrics
Some improvements to backtest summary metrics
2 parents 539937c + 2a84b00 commit 2fd703f

File tree

6 files changed

+367
-237
lines changed

6 files changed

+367
-237
lines changed

docs/backtesting.md

Lines changed: 232 additions & 203 deletions
Large diffs are not rendered by default.

docs/developer.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,22 @@ jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace freqtrade/tem
408408
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to markdown freqtrade/templates/strategy_analysis_example.ipynb --stdout > docs/strategy_analysis_example.md
409409
```
410410

411+
## Backtest documentation results
412+
413+
To generate backtest outputs, please use the following commands:
414+
415+
``` bash
416+
# Assume a dedicated user directory for this output
417+
freqtrade create-userdir --userdir user_data_bttest/
418+
# set can_short = True
419+
sed -i "s/can_short: bool = False/can_short: bool = True/" user_data_bttest/strategies/sample_strategy.py
420+
421+
freqtrade download-data --timerange 20250625-20250801 --config tests/testdata/config.tests.usdt.json --userdir user_data_bttest/ -t 5m
422+
423+
freqtrade backtesting --config tests/testdata/config.tests.usdt.json -s SampleStrategy --userdir user_data_bttest/ --cache none --timerange 20250701-20250801
424+
```
425+
426+
411427
## Continuous integration
412428

413429
This documents some decisions taken for the CI Pipeline.

freqtrade/optimize/optimize_reports/bt_output.py

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ def text_table_strategy(strategy_results, stake_currency: str, title: str):
194194

195195

196196
def text_table_add_metrics(strat_results: dict) -> None:
197+
stake = strat_results["stake_currency"]
197198
if len(strat_results["trades"]) > 0:
198199
best_trade = max(strat_results["trades"], key=lambda x: x["profit_ratio"])
199200
worst_trade = min(strat_results["trades"], key=lambda x: x["profit_ratio"])
@@ -202,23 +203,19 @@ def text_table_add_metrics(strat_results: dict) -> None:
202203
[
203204
("", ""), # Empty line to improve readability
204205
(
205-
"Long / Short",
206+
"Long / Short trades",
206207
f"{strat_results.get('trade_count_long', 'total_trades')} / "
207208
f"{strat_results.get('trade_count_short', 0)}",
208209
),
209-
("Total profit Long %", f"{strat_results['profit_total_long']:.2%}"),
210-
("Total profit Short %", f"{strat_results['profit_total_short']:.2%}"),
211210
(
212-
"Absolute profit Long",
213-
fmt_coin(
214-
strat_results["profit_total_long_abs"], strat_results["stake_currency"]
215-
),
211+
"Long / Short profit %",
212+
f"{strat_results['profit_total_long']:.2%} / "
213+
f"{strat_results['profit_total_short']:.2%}",
216214
),
217215
(
218-
"Absolute profit Short",
219-
fmt_coin(
220-
strat_results["profit_total_short_abs"], strat_results["stake_currency"]
221-
),
216+
f"Long / Short profit {stake}",
217+
f"{strat_results['profit_total_long_abs']:.{decimals_per_coin(stake)}f} / "
218+
f"{strat_results['profit_total_short_abs']:.{decimals_per_coin(stake)}f}",
222219
),
223220
]
224221
if strat_results.get("trade_count_short", 0) > 0
@@ -231,27 +228,34 @@ def text_table_add_metrics(strat_results: dict) -> None:
231228
drawdown_metrics.append(
232229
("Max % of account underwater", f"{strat_results['max_relative_drawdown']:.2%}")
233230
)
231+
drawdown_account = (
232+
strat_results["max_drawdown_account"]
233+
if "max_drawdown_account" in strat_results
234+
else strat_results["max_drawdown"]
235+
)
234236
drawdown_metrics.extend(
235237
[
236238
(
237-
("Absolute Drawdown (Account)", f"{strat_results['max_drawdown_account']:.2%}")
238-
if "max_drawdown_account" in strat_results
239-
else ("Drawdown", f"{strat_results['max_drawdown']:.2%}")
239+
"Absolute drawdown",
240+
f"{fmt_coin(strat_results['max_drawdown_abs'], stake)} "
241+
f"({drawdown_account:.2%})",
240242
),
241243
(
242-
"Absolute Drawdown",
243-
fmt_coin(strat_results["max_drawdown_abs"], strat_results["stake_currency"]),
244+
"Drawdown duration",
245+
strat_results["drawdown_duration"]
246+
if "drawdown_duration" in strat_results
247+
else "N/A",
244248
),
245249
(
246-
"Drawdown high",
247-
fmt_coin(strat_results["max_drawdown_high"], strat_results["stake_currency"]),
250+
"Profit at drawdown start",
251+
fmt_coin(strat_results["max_drawdown_high"], stake),
248252
),
249253
(
250-
"Drawdown low",
251-
fmt_coin(strat_results["max_drawdown_low"], strat_results["stake_currency"]),
254+
"Profit at drawdown end",
255+
fmt_coin(strat_results["max_drawdown_low"], stake),
252256
),
253-
("Drawdown Start", strat_results["drawdown_start"]),
254-
("Drawdown End", strat_results["drawdown_end"]),
257+
("Drawdown start", strat_results["drawdown_start"]),
258+
("Drawdown end", strat_results["drawdown_end"]),
255259
]
256260
)
257261

@@ -299,15 +303,15 @@ def text_table_add_metrics(strat_results: dict) -> None:
299303
),
300304
(
301305
"Starting balance",
302-
fmt_coin(strat_results["starting_balance"], strat_results["stake_currency"]),
306+
fmt_coin(strat_results["starting_balance"], stake),
303307
),
304308
(
305309
"Final balance",
306-
fmt_coin(strat_results["final_balance"], strat_results["stake_currency"]),
310+
fmt_coin(strat_results["final_balance"], stake),
307311
),
308312
(
309313
"Absolute profit ",
310-
fmt_coin(strat_results["profit_total_abs"], strat_results["stake_currency"]),
314+
fmt_coin(strat_results["profit_total_abs"], stake),
311315
),
312316
("Total profit %", f"{strat_results['profit_total']:.2%}"),
313317
("CAGR %", f"{strat_results['cagr']:.2%}" if "cagr" in strat_results else "N/A"),
@@ -335,16 +339,16 @@ def text_table_add_metrics(strat_results: dict) -> None:
335339
"Avg. daily profit",
336340
fmt_coin(
337341
(strat_results["profit_total_abs"] / strat_results["backtest_days"]),
338-
strat_results["stake_currency"],
342+
stake,
339343
),
340344
),
341345
(
342346
"Avg. stake amount",
343-
fmt_coin(strat_results["avg_stake_amount"], strat_results["stake_currency"]),
347+
fmt_coin(strat_results["avg_stake_amount"], stake),
344348
),
345349
(
346350
"Total trade volume",
347-
fmt_coin(strat_results["total_volume"], strat_results["stake_currency"]),
351+
fmt_coin(strat_results["total_volume"], stake),
348352
),
349353
*short_metrics,
350354
("", ""), # Empty line to improve readability
@@ -362,11 +366,11 @@ def text_table_add_metrics(strat_results: dict) -> None:
362366
("Worst trade", f"{worst_trade['pair']} {worst_trade['profit_ratio']:.2%}"),
363367
(
364368
"Best day",
365-
fmt_coin(strat_results["backtest_best_day_abs"], strat_results["stake_currency"]),
369+
fmt_coin(strat_results["backtest_best_day_abs"], stake),
366370
),
367371
(
368372
"Worst day",
369-
fmt_coin(strat_results["backtest_worst_day_abs"], strat_results["stake_currency"]),
373+
fmt_coin(strat_results["backtest_worst_day_abs"], stake),
370374
),
371375
(
372376
"Days win/draw/lose",
@@ -404,17 +408,17 @@ def text_table_add_metrics(strat_results: dict) -> None:
404408
),
405409
*entry_adjustment_metrics,
406410
("", ""), # Empty line to improve readability
407-
("Min balance", fmt_coin(strat_results["csum_min"], strat_results["stake_currency"])),
408-
("Max balance", fmt_coin(strat_results["csum_max"], strat_results["stake_currency"])),
411+
("Min balance", fmt_coin(strat_results["csum_min"], stake)),
412+
("Max balance", fmt_coin(strat_results["csum_max"], stake)),
409413
*drawdown_metrics,
410414
("Market change", f"{strat_results['market_change']:.2%}"),
411415
]
412416
print_rich_table(metrics, ["Metric", "Value"], summary="SUMMARY METRICS", justify="left")
413417

414418
else:
415-
start_balance = fmt_coin(strat_results["starting_balance"], strat_results["stake_currency"])
419+
start_balance = fmt_coin(strat_results["starting_balance"], stake)
416420
stake_amount = (
417-
fmt_coin(strat_results["stake_amount"], strat_results["stake_currency"])
421+
fmt_coin(strat_results["stake_amount"], stake)
418422
if strat_results["stake_amount"] != UNLIMITED_STAKE_AMOUNT
419423
else "unlimited"
420424
)

freqtrade/optimize/optimize_reports/optimize_reports.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ def generate_strategy_stats(
627627
underwater = calculate_max_drawdown(
628628
results, value_col="profit_abs", starting_balance=start_balance, relative=True
629629
)
630+
drawdown_duration = drawdown.low_date - drawdown.high_date
630631

631632
strat_stats.update(
632633
{
@@ -637,6 +638,8 @@ def generate_strategy_stats(
637638
"drawdown_start_ts": drawdown.high_date.timestamp() * 1000,
638639
"drawdown_end": drawdown.low_date.strftime(DATETIME_PRINT_FORMAT),
639640
"drawdown_end_ts": drawdown.low_date.timestamp() * 1000,
641+
"drawdown_duration": drawdown_duration,
642+
"drawdown_duration_s": drawdown_duration.total_seconds(),
640643
"max_drawdown_low": drawdown.low_value,
641644
"max_drawdown_high": drawdown.high_value,
642645
}

tests/testdata/config.tests.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "https://schema.freqtrade.io/schema.json",
23
"max_open_trades": 3,
34
"stake_currency": "BTC",
45
"stake_amount": 0.05,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"$schema": "https://schema.freqtrade.io/schema.json",
3+
"max_open_trades": 3,
4+
"stake_currency": "USDT",
5+
"stake_amount": "unlimited",
6+
"tradable_balance_ratio": 0.99,
7+
"fiat_display_currency": "USD",
8+
"trading_mode": "futures",
9+
"margin_mode": "isolated",
10+
"timeframe": "5m",
11+
"dry_run": true,
12+
"cancel_open_orders_on_exit": false,
13+
"unfilledtimeout": {
14+
"entry": 5,
15+
"exit": 5,
16+
"exit_timeout_count": 0,
17+
"unit": "minutes"
18+
},
19+
"entry_pricing": {
20+
"price_side": "same",
21+
"use_order_book": true,
22+
"order_book_top": 1,
23+
"price_last_balance": 0.0,
24+
"check_depth_of_market": {
25+
"enabled": false,
26+
"bids_to_ask_delta": 1
27+
}
28+
},
29+
"exit_pricing":{
30+
"price_side": "same",
31+
"use_order_book": true,
32+
"order_book_top": 1
33+
},
34+
"exchange": {
35+
"name": "bybit",
36+
"key": "your_exchange_key",
37+
"secret": "your_exchange_secret",
38+
"ccxt_config": {},
39+
"ccxt_async_config": {},
40+
"pair_whitelist": [
41+
"BTC/USDT:USDT",
42+
"ETH/USDT:USDT",
43+
"LTC/USDT:USDT",
44+
"ETC/USDT:USDT",
45+
"XLM/USDT:USDT",
46+
"XRP/USDT:USDT",
47+
"ADA/USDT:USDT",
48+
"DOT/USDT:USDT"
49+
],
50+
"pair_blacklist": [
51+
]
52+
},
53+
"pairlists": [
54+
{"method": "StaticPairList"}
55+
],
56+
"telegram": {
57+
"enabled": false,
58+
"token": "your_telegram_token",
59+
"chat_id": "your_telegram_chat_id"
60+
},
61+
"api_server": {
62+
"enabled": false,
63+
"listen_ip_address": "127.0.0.1",
64+
"listen_port": 8080,
65+
"verbosity": "error",
66+
"jwt_secret_key": "somethingrandom",
67+
"CORS_origins": [],
68+
"username": "freqtrader",
69+
"password": "SuperSecurePassword"
70+
},
71+
"bot_name": "freqtrade",
72+
"initial_state": "running",
73+
"force_entry_enable": false,
74+
"internals": {
75+
"process_throttle_secs": 5
76+
}
77+
}

0 commit comments

Comments
 (0)