feat: add wallet based metrics to backtest output
This commit is contained in:
@@ -289,7 +289,7 @@ def text_table_add_metrics(strat_results: dict) -> None:
|
|||||||
)
|
)
|
||||||
wallet_metrics: list[tuple[str, str]] = [
|
wallet_metrics: list[tuple[str, str]] = [
|
||||||
(
|
(
|
||||||
"Min/Max balance realized",
|
"Min/Max balance (realized)",
|
||||||
f"{fmt_coin(strat_results['csum_min'], stake)} / "
|
f"{fmt_coin(strat_results['csum_min'], stake)} / "
|
||||||
f"{fmt_coin(strat_results['csum_max'], stake)}",
|
f"{fmt_coin(strat_results['csum_max'], stake)}",
|
||||||
),
|
),
|
||||||
@@ -298,22 +298,38 @@ def text_table_add_metrics(strat_results: dict) -> None:
|
|||||||
wallet_metrics.extend(
|
wallet_metrics.extend(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"Min/Max balance unrealized",
|
"Min/Max balance (unrealized)",
|
||||||
f"{fmt_coin(wallet_stats['low_balance'], stake)} / "
|
f"{fmt_coin(wallet_stats['low_balance'], stake)} / "
|
||||||
f"{fmt_coin(wallet_stats['high_balance'], stake)}",
|
f"{fmt_coin(wallet_stats['high_balance'], stake)}",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Min/Max balance dates",
|
"Min/Max balance dates (unrealized)",
|
||||||
f"{wallet_stats['low_date']} / {wallet_stats['high_date']}",
|
f"{wallet_stats['low_date']} / {wallet_stats['high_date']}",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if "sharpe" in wallet_stats:
|
if "sharpe" in wallet_stats:
|
||||||
wallet_metrics.append(
|
# Assume that if sharpe is there, all others are there as well.
|
||||||
(
|
wallet_metrics.extend(
|
||||||
"Sharpe ratio balance",
|
[
|
||||||
f"{wallet_stats['sharpe']:.2f}",
|
(
|
||||||
)
|
"Sharpe (unrealized)",
|
||||||
|
f"{wallet_stats['sharpe']:.2f}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Sortino (unrealized)",
|
||||||
|
f"{wallet_stats['sortino']:.2f}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Calmar (unrealized)",
|
||||||
|
f"{wallet_stats['calmar']:.2f}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Max drawdown (unrealized)",
|
||||||
|
f"{fmt_coin(wallet_stats['max_drawdown_abs'], stake)} "
|
||||||
|
f"({wallet_stats['max_drawdown_account']:.2%})",
|
||||||
|
),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Newly added fields should be ignored if they are missing in strat_results. hyperopt-show
|
# Newly added fields should be ignored if they are missing in strat_results. hyperopt-show
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT
|
|||||||
from freqtrade.data.metrics import (
|
from freqtrade.data.metrics import (
|
||||||
calculate_cagr,
|
calculate_cagr,
|
||||||
calculate_calmar,
|
calculate_calmar,
|
||||||
|
calculate_calmar_from_balance,
|
||||||
calculate_csum,
|
calculate_csum,
|
||||||
calculate_expectancy,
|
calculate_expectancy,
|
||||||
calculate_market_change,
|
calculate_market_change,
|
||||||
calculate_max_drawdown,
|
calculate_max_drawdown,
|
||||||
|
calculate_max_drawdown_from_balance,
|
||||||
calculate_sharpe,
|
calculate_sharpe,
|
||||||
calculate_sharpe_from_balance,
|
calculate_sharpe_from_balance,
|
||||||
calculate_sortino,
|
calculate_sortino,
|
||||||
|
calculate_sortino_from_balance,
|
||||||
calculate_sqn,
|
calculate_sqn,
|
||||||
)
|
)
|
||||||
from freqtrade.ft_types import (
|
from freqtrade.ft_types import (
|
||||||
@@ -61,12 +64,43 @@ def generate_wallet_stats(wallet_df: DataFrame, stake_currency: str) -> dict[str
|
|||||||
low_date = wallet.loc[low_idx, "date"]
|
low_date = wallet.loc[low_idx, "date"]
|
||||||
high_date = wallet.loc[high_idx, "date"]
|
high_date = wallet.loc[high_idx, "date"]
|
||||||
sharpe = calculate_sharpe_from_balance(wallet)
|
sharpe = calculate_sharpe_from_balance(wallet)
|
||||||
|
sortino = calculate_sortino_from_balance(wallet)
|
||||||
|
calmar = calculate_calmar_from_balance(wallet)
|
||||||
|
try:
|
||||||
|
drawdown = calculate_max_drawdown_from_balance(wallet)
|
||||||
|
except ValueError:
|
||||||
|
drawdown = None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"start_balance": start_balance,
|
"start_balance": start_balance,
|
||||||
"end_balance": end_balance,
|
"end_balance": end_balance,
|
||||||
"high_balance": high_balance,
|
"high_balance": high_balance,
|
||||||
"low_balance": low_balance,
|
"low_balance": low_balance,
|
||||||
"sharpe": sharpe,
|
"sharpe": sharpe,
|
||||||
|
"sortino": sortino,
|
||||||
|
"calmar": calmar,
|
||||||
|
"max_drawdown_account": drawdown.relative_account_drawdown if drawdown else 0.0,
|
||||||
|
"max_drawdown_abs": drawdown.drawdown_abs if drawdown else 0.0,
|
||||||
|
"drawdown_start": (
|
||||||
|
drawdown.high_date.strftime(DATETIME_PRINT_FORMAT)
|
||||||
|
if drawdown and drawdown.high_date is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"drawdown_start_ts": (
|
||||||
|
int(drawdown.high_date.timestamp() * 1000)
|
||||||
|
if drawdown and drawdown.high_date is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"drawdown_end": (
|
||||||
|
drawdown.low_date.strftime(DATETIME_PRINT_FORMAT)
|
||||||
|
if drawdown and drawdown.low_date is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"drawdown_end_ts": (
|
||||||
|
int(drawdown.low_date.timestamp() * 1000)
|
||||||
|
if drawdown and drawdown.low_date is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
"low_date": low_date.strftime(DATETIME_PRINT_FORMAT),
|
"low_date": low_date.strftime(DATETIME_PRINT_FORMAT),
|
||||||
"low_ts": int(low_date.timestamp() * 1000),
|
"low_ts": int(low_date.timestamp() * 1000),
|
||||||
"high_date": high_date.strftime(DATETIME_PRINT_FORMAT),
|
"high_date": high_date.strftime(DATETIME_PRINT_FORMAT),
|
||||||
|
|||||||
Reference in New Issue
Block a user