Merge pull request #12512 from mrpabloyeah/fix-high_value-calculation-in-calculate_max_drawdown

Fix high_value calculation in calculate_max_drawdown()
This commit is contained in:
Matthias
2025-11-15 16:03:35 +01:00
committed by GitHub
2 changed files with 27 additions and 6 deletions

View File

@@ -143,6 +143,20 @@ def _calc_drawdown_series(
max_drawdown_df["drawdown_relative"] = ( max_drawdown_df["drawdown_relative"] = (
max_drawdown_df["high_value"] - max_drawdown_df["cumulative"] max_drawdown_df["high_value"] - max_drawdown_df["cumulative"]
) / max_drawdown_df["high_value"] ) / max_drawdown_df["high_value"]
# Add zero row at start to account for edge-cases with no winning / losing trades - so high/low
# will be 0.0 in such cases.
zero_row = pd.DataFrame(
{
"cumulative": [0.0],
"high_value": [0.0],
"drawdown": [0.0],
"drawdown_relative": [0.0],
"date": [profit_results.loc[0, date_col]],
}
)
max_drawdown_df = pd.concat([zero_row, max_drawdown_df], ignore_index=True)
return max_drawdown_df return max_drawdown_df
@@ -215,6 +229,7 @@ def calculate_max_drawdown(
max_drawdown_df = _calc_drawdown_series( max_drawdown_df = _calc_drawdown_series(
profit_results, date_col=date_col, value_col=value_col, starting_balance=starting_balance profit_results, date_col=date_col, value_col=value_col, starting_balance=starting_balance
) )
# max_drawdown_df has an extra zero row at the start
# Calculate maximum drawdown # Calculate maximum drawdown
idxmin = ( idxmin = (
@@ -223,15 +238,15 @@ def calculate_max_drawdown(
else max_drawdown_df["drawdown"].idxmin() else max_drawdown_df["drawdown"].idxmin()
) )
high_idx = max_drawdown_df.iloc[: idxmin + 1]["high_value"].idxmax() high_idx = max_drawdown_df.iloc[: idxmin + 1]["high_value"].idxmax()
high_date = profit_results.loc[high_idx, date_col] high_date = profit_results.at[max(high_idx - 1, 0), date_col]
low_date = profit_results.loc[idxmin, date_col] low_date = profit_results.at[max(idxmin - 1, 0), date_col]
high_val = max_drawdown_df.loc[high_idx, "cumulative"] high_val = max_drawdown_df.at[high_idx, "cumulative"]
low_val = max_drawdown_df.loc[idxmin, "cumulative"] low_val = max_drawdown_df.at[idxmin, "cumulative"]
max_drawdown_rel = max_drawdown_df.loc[idxmin, "drawdown_relative"] max_drawdown_rel = max_drawdown_df.at[idxmin, "drawdown_relative"]
# Calculate current drawdown # Calculate current drawdown
current_high_idx = max_drawdown_df["high_value"].iloc[:-1].idxmax() current_high_idx = max_drawdown_df["high_value"].iloc[:-1].idxmax()
current_high_date = profit_results.loc[current_high_idx, date_col] current_high_date = profit_results.at[max(current_high_idx - 1, 0), date_col]
current_high_value = max_drawdown_df.iloc[-1]["high_value"] current_high_value = max_drawdown_df.iloc[-1]["high_value"]
current_cumulative = max_drawdown_df.iloc[-1]["cumulative"] current_cumulative = max_drawdown_df.iloc[-1]["cumulative"]
current_drawdown_abs = current_high_value - current_cumulative current_drawdown_abs = current_high_value - current_cumulative

View File

@@ -575,12 +575,18 @@ def test_calculate_max_drawdown2():
# No losing trade ... # No losing trade ...
drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit") drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit")
assert drawdown.drawdown_abs == 0.0 assert drawdown.drawdown_abs == 0.0
assert drawdown.low_value == 0.0
assert drawdown.current_high_value >= 0.0
assert drawdown.current_drawdown_abs == 0.0
df1 = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"]) df1 = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"])
df1.loc[:, "profit"] = df1["profit"] * -1 df1.loc[:, "profit"] = df1["profit"] * -1
# No winning trade ... # No winning trade ...
drawdown = calculate_max_drawdown(df1, date_col="open_date", value_col="profit") drawdown = calculate_max_drawdown(df1, date_col="open_date", value_col="profit")
assert drawdown.drawdown_abs == 0.055545 assert drawdown.drawdown_abs == 0.055545
assert drawdown.high_value == 0.0
assert drawdown.current_high_value == 0.0
assert drawdown.current_drawdown_abs == 0.055545
@pytest.mark.parametrize( @pytest.mark.parametrize(