Some improvements to backtest summary metrics

This commit is contained in:
mrpabloyeah
2025-08-28 02:04:39 +02:00
parent 22ddd9a7d4
commit 8e623ee64c
2 changed files with 41 additions and 34 deletions
@@ -194,6 +194,7 @@ def text_table_strategy(strategy_results, stake_currency: str, title: str):
def text_table_add_metrics(strat_results: dict) -> None: def text_table_add_metrics(strat_results: dict) -> None:
stake = strat_results["stake_currency"]
if len(strat_results["trades"]) > 0: if len(strat_results["trades"]) > 0:
best_trade = max(strat_results["trades"], key=lambda x: x["profit_ratio"]) best_trade = max(strat_results["trades"], key=lambda x: x["profit_ratio"])
worst_trade = min(strat_results["trades"], key=lambda x: x["profit_ratio"]) 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:
[ [
("", ""), # Empty line to improve readability ("", ""), # Empty line to improve readability
( (
"Long / Short", "Long / Short trades",
f"{strat_results.get('trade_count_long', 'total_trades')} / " f"{strat_results.get('trade_count_long', 'total_trades')} / "
f"{strat_results.get('trade_count_short', 0)}", 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", "Long / Short profit %",
fmt_coin( f"{strat_results['profit_total_long']:.2%} / "
strat_results["profit_total_long_abs"], strat_results["stake_currency"] f"{strat_results['profit_total_short']:.2%}",
),
), ),
( (
"Absolute profit Short", f"Long / Short profit {stake}",
fmt_coin( f"{strat_results['profit_total_long_abs']:.{decimals_per_coin(stake)}f} / "
strat_results["profit_total_short_abs"], strat_results["stake_currency"] f"{strat_results['profit_total_short_abs']:.{decimals_per_coin(stake)}f}",
),
), ),
] ]
if strat_results.get("trade_count_short", 0) > 0 if strat_results.get("trade_count_short", 0) > 0
@@ -231,27 +228,34 @@ def text_table_add_metrics(strat_results: dict) -> None:
drawdown_metrics.append( drawdown_metrics.append(
("Max % of account underwater", f"{strat_results['max_relative_drawdown']:.2%}") ("Max % of account underwater", f"{strat_results['max_relative_drawdown']:.2%}")
) )
drawdown_account = (
strat_results["max_drawdown_account"]
if "max_drawdown_account" in strat_results
else strat_results["max_drawdown"]
)
drawdown_metrics.extend( drawdown_metrics.extend(
[ [
( (
("Absolute Drawdown (Account)", f"{strat_results['max_drawdown_account']:.2%}") "Absolute drawdown",
if "max_drawdown_account" in strat_results f"{fmt_coin(strat_results['max_drawdown_abs'], stake)} "
else ("Drawdown", f"{strat_results['max_drawdown']:.2%}") f"({drawdown_account:.2%})",
), ),
( (
"Absolute Drawdown", "Drawdown duration",
fmt_coin(strat_results["max_drawdown_abs"], strat_results["stake_currency"]), strat_results["drawdown_duration"]
if "drawdown_duration" in strat_results
else "N/A",
), ),
( (
"Drawdown high", "Profit at drawdown start",
fmt_coin(strat_results["max_drawdown_high"], strat_results["stake_currency"]), fmt_coin(strat_results["max_drawdown_high"], stake),
), ),
( (
"Drawdown low", "Profit at drawdown end",
fmt_coin(strat_results["max_drawdown_low"], strat_results["stake_currency"]), fmt_coin(strat_results["max_drawdown_low"], stake),
), ),
("Drawdown Start", strat_results["drawdown_start"]), ("Drawdown start", strat_results["drawdown_start"]),
("Drawdown End", strat_results["drawdown_end"]), ("Drawdown end", strat_results["drawdown_end"]),
] ]
) )
@@ -299,15 +303,15 @@ def text_table_add_metrics(strat_results: dict) -> None:
), ),
( (
"Starting balance", "Starting balance",
fmt_coin(strat_results["starting_balance"], strat_results["stake_currency"]), fmt_coin(strat_results["starting_balance"], stake),
), ),
( (
"Final balance", "Final balance",
fmt_coin(strat_results["final_balance"], strat_results["stake_currency"]), fmt_coin(strat_results["final_balance"], stake),
), ),
( (
"Absolute profit ", "Absolute profit ",
fmt_coin(strat_results["profit_total_abs"], strat_results["stake_currency"]), fmt_coin(strat_results["profit_total_abs"], stake),
), ),
("Total profit %", f"{strat_results['profit_total']:.2%}"), ("Total profit %", f"{strat_results['profit_total']:.2%}"),
("CAGR %", f"{strat_results['cagr']:.2%}" if "cagr" in strat_results else "N/A"), ("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:
"Avg. daily profit", "Avg. daily profit",
fmt_coin( fmt_coin(
(strat_results["profit_total_abs"] / strat_results["backtest_days"]), (strat_results["profit_total_abs"] / strat_results["backtest_days"]),
strat_results["stake_currency"], stake,
), ),
), ),
( (
"Avg. stake amount", "Avg. stake amount",
fmt_coin(strat_results["avg_stake_amount"], strat_results["stake_currency"]), fmt_coin(strat_results["avg_stake_amount"], stake),
), ),
( (
"Total trade volume", "Total trade volume",
fmt_coin(strat_results["total_volume"], strat_results["stake_currency"]), fmt_coin(strat_results["total_volume"], stake),
), ),
*short_metrics, *short_metrics,
("", ""), # Empty line to improve readability ("", ""), # Empty line to improve readability
@@ -362,11 +366,11 @@ def text_table_add_metrics(strat_results: dict) -> None:
("Worst trade", f"{worst_trade['pair']} {worst_trade['profit_ratio']:.2%}"), ("Worst trade", f"{worst_trade['pair']} {worst_trade['profit_ratio']:.2%}"),
( (
"Best day", "Best day",
fmt_coin(strat_results["backtest_best_day_abs"], strat_results["stake_currency"]), fmt_coin(strat_results["backtest_best_day_abs"], stake),
), ),
( (
"Worst day", "Worst day",
fmt_coin(strat_results["backtest_worst_day_abs"], strat_results["stake_currency"]), fmt_coin(strat_results["backtest_worst_day_abs"], stake),
), ),
( (
"Days win/draw/lose", "Days win/draw/lose",
@@ -404,17 +408,17 @@ def text_table_add_metrics(strat_results: dict) -> None:
), ),
*entry_adjustment_metrics, *entry_adjustment_metrics,
("", ""), # Empty line to improve readability ("", ""), # Empty line to improve readability
("Min balance", fmt_coin(strat_results["csum_min"], strat_results["stake_currency"])), ("Min balance", fmt_coin(strat_results["csum_min"], stake)),
("Max balance", fmt_coin(strat_results["csum_max"], strat_results["stake_currency"])), ("Max balance", fmt_coin(strat_results["csum_max"], stake)),
*drawdown_metrics, *drawdown_metrics,
("Market change", f"{strat_results['market_change']:.2%}"), ("Market change", f"{strat_results['market_change']:.2%}"),
] ]
print_rich_table(metrics, ["Metric", "Value"], summary="SUMMARY METRICS", justify="left") print_rich_table(metrics, ["Metric", "Value"], summary="SUMMARY METRICS", justify="left")
else: else:
start_balance = fmt_coin(strat_results["starting_balance"], strat_results["stake_currency"]) start_balance = fmt_coin(strat_results["starting_balance"], stake)
stake_amount = ( stake_amount = (
fmt_coin(strat_results["stake_amount"], strat_results["stake_currency"]) fmt_coin(strat_results["stake_amount"], stake)
if strat_results["stake_amount"] != UNLIMITED_STAKE_AMOUNT if strat_results["stake_amount"] != UNLIMITED_STAKE_AMOUNT
else "unlimited" else "unlimited"
) )
@@ -627,6 +627,7 @@ def generate_strategy_stats(
underwater = calculate_max_drawdown( underwater = calculate_max_drawdown(
results, value_col="profit_abs", starting_balance=start_balance, relative=True results, value_col="profit_abs", starting_balance=start_balance, relative=True
) )
drawdown_duration = drawdown.low_date - drawdown.high_date
strat_stats.update( strat_stats.update(
{ {
@@ -637,6 +638,8 @@ def generate_strategy_stats(
"drawdown_start_ts": drawdown.high_date.timestamp() * 1000, "drawdown_start_ts": drawdown.high_date.timestamp() * 1000,
"drawdown_end": drawdown.low_date.strftime(DATETIME_PRINT_FORMAT), "drawdown_end": drawdown.low_date.strftime(DATETIME_PRINT_FORMAT),
"drawdown_end_ts": drawdown.low_date.timestamp() * 1000, "drawdown_end_ts": drawdown.low_date.timestamp() * 1000,
"drawdown_duration": drawdown_duration,
"drawdown_duration_s": drawdown_duration.total_seconds(),
"max_drawdown_low": drawdown.low_value, "max_drawdown_low": drawdown.low_value,
"max_drawdown_high": drawdown.high_value, "max_drawdown_high": drawdown.high_value,
} }