feat: calculate sharpe-ratio from historic balance snapshots
This commit is contained in:
@@ -390,10 +390,55 @@ def calculate_sharpe(
|
|||||||
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
|
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
|
||||||
sharp_ratio = -100
|
sharp_ratio = -100
|
||||||
|
|
||||||
# print(expected_returns_mean, up_stdev, sharp_ratio)
|
|
||||||
return sharp_ratio
|
return sharp_ratio
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_sharpe_from_balance(
|
||||||
|
balance_history: pd.DataFrame,
|
||||||
|
date_col: str = "date",
|
||||||
|
balance_col: str = "total_quote",
|
||||||
|
) -> float:
|
||||||
|
"""
|
||||||
|
Calculate sharpe ratio from historical balance snapshots.
|
||||||
|
|
||||||
|
:param balance_history: DataFrame containing at least date and balance columns
|
||||||
|
:param date_col: Column containing timestamps
|
||||||
|
:param balance_col: Column containing historical balance values
|
||||||
|
:return: sharpe
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
len(balance_history) == 0
|
||||||
|
or date_col not in balance_history
|
||||||
|
or balance_col not in balance_history
|
||||||
|
):
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
wallet = balance_history.loc[:, [date_col, balance_col]].copy()
|
||||||
|
wallet[date_col] = pd.to_datetime(wallet[date_col], utc=True, errors="coerce")
|
||||||
|
wallet = wallet.dropna(subset=[date_col, balance_col]).sort_values(date_col)
|
||||||
|
|
||||||
|
if len(wallet) < 2:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Sample balance to daily end-of-day values to normalize variable snapshot frequency.
|
||||||
|
daily_balance = wallet.set_index(date_col)[balance_col].resample("1D").last().dropna()
|
||||||
|
daily_returns = daily_balance.pct_change().dropna()
|
||||||
|
|
||||||
|
if len(daily_returns) == 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
expected_returns_mean = daily_returns.mean()
|
||||||
|
up_stdev = daily_returns.std(ddof=0)
|
||||||
|
|
||||||
|
if up_stdev != 0 and not np.isnan(up_stdev):
|
||||||
|
sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365)
|
||||||
|
else:
|
||||||
|
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
|
||||||
|
sharp_ratio = -100
|
||||||
|
|
||||||
|
return float(sharp_ratio)
|
||||||
|
|
||||||
|
|
||||||
def calculate_calmar(
|
def calculate_calmar(
|
||||||
trades: pd.DataFrame,
|
trades: pd.DataFrame,
|
||||||
min_date: datetime | None,
|
min_date: datetime | None,
|
||||||
|
|||||||
@@ -308,6 +308,13 @@ def text_table_add_metrics(strat_results: dict) -> None:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
if "sharpe" in wallet_stats:
|
||||||
|
wallet_metrics.append(
|
||||||
|
(
|
||||||
|
"Sharpe ratio balance",
|
||||||
|
f"{wallet_stats['sharpe']:.2f}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# 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
|
||||||
# command stores these results and newer version of freqtrade must be able to handle old
|
# command stores these results and newer version of freqtrade must be able to handle old
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from freqtrade.data.metrics import (
|
|||||||
calculate_market_change,
|
calculate_market_change,
|
||||||
calculate_max_drawdown,
|
calculate_max_drawdown,
|
||||||
calculate_sharpe,
|
calculate_sharpe,
|
||||||
|
calculate_sharpe_from_balance,
|
||||||
calculate_sortino,
|
calculate_sortino,
|
||||||
calculate_sqn,
|
calculate_sqn,
|
||||||
)
|
)
|
||||||
@@ -59,11 +60,13 @@ def generate_wallet_stats(wallet_df: DataFrame, stake_currency: str) -> dict[str
|
|||||||
low_balance = total_quote.loc[low_idx]
|
low_balance = total_quote.loc[low_idx]
|
||||||
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)
|
||||||
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,
|
||||||
"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