Remove compatibility layer for calculate_max_drawdown

This commit is contained in:
Matthias 2024-05-15 06:54:17 +02:00
parent c79b75ff9a
commit a6b07ec96f
8 changed files with 25 additions and 62 deletions

View File

@ -171,7 +171,7 @@ class DrawDownResult:
relative_account_drawdown: float = 0.0 relative_account_drawdown: float = 0.0
def calc_max_drawdown( def calculate_max_drawdown(
trades: pd.DataFrame, trades: pd.DataFrame,
*, *,
date_col: str = "close_date", date_col: str = "close_date",
@ -185,7 +185,7 @@ def calc_max_drawdown(
:param date_col: Column in DataFrame to use for dates (defaults to 'close_date') :param date_col: Column in DataFrame to use for dates (defaults to 'close_date')
:param value_col: Column in DataFrame to use for values (defaults to 'profit_abs') :param value_col: Column in DataFrame to use for values (defaults to 'profit_abs')
:param starting_balance: Portfolio starting balance - properly calculate relative drawdown. :param starting_balance: Portfolio starting balance - properly calculate relative drawdown.
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown) :return: DrawDownResult object
with absolute max drawdown, high and low time and high and low value, with absolute max drawdown, high and low time and high and low value,
and the relative account drawdown and the relative account drawdown
:raise: ValueError if trade-dataframe was found empty. :raise: ValueError if trade-dataframe was found empty.
@ -222,44 +222,6 @@ def calc_max_drawdown(
) )
def calculate_max_drawdown(
trades: pd.DataFrame,
*,
date_col: str = "close_date",
value_col: str = "profit_abs",
starting_balance: float = 0,
relative: bool = False,
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]:
"""
Calculate max drawdown and the corresponding close dates
Deprecated, favor calc_max_drawdown instead!
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:param date_col: Column in DataFrame to use for dates (defaults to 'close_date')
:param value_col: Column in DataFrame to use for values (defaults to 'profit_abs')
:param starting_balance: Portfolio starting balance - properly calculate relative drawdown.
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown)
with absolute max drawdown, high and low time and high and low value,
and the relative account drawdown
:raise: ValueError if trade-dataframe was found empty.
"""
# TODO: add deprecation warning
res = calc_max_drawdown(
trades,
date_col=date_col,
value_col=value_col,
starting_balance=starting_balance,
relative=relative,
)
return (
res.drawdown_abs,
res.high_date,
res.low_date,
res.high_value,
res.low_value,
res.relative_account_drawdown,
)
def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]: def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]:
""" """
Calculate min/max cumsum of trades, to show if the wallet/stake amount ratio is sane Calculate min/max cumsum of trades, to show if the wallet/stake amount ratio is sane
@ -399,7 +361,7 @@ def calculate_calmar(
# calculate max drawdown # calculate max drawdown
try: try:
drawdown = calc_max_drawdown( drawdown = calculate_max_drawdown(
trades, value_col="profit_abs", starting_balance=starting_balance trades, value_col="profit_abs", starting_balance=starting_balance
) )
max_drawdown = drawdown.relative_account_drawdown max_drawdown = drawdown.relative_account_drawdown

View File

@ -9,7 +9,7 @@ from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
from freqtrade.data.metrics import calc_max_drawdown from freqtrade.data.metrics import calculate_max_drawdown
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@ -38,7 +38,7 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss):
""" """
total_profit = results["profit_abs"].sum() total_profit = results["profit_abs"].sum()
try: try:
max_drawdown = calc_max_drawdown(results, value_col="profit_abs") max_drawdown = calculate_max_drawdown(results, value_col="profit_abs")
except ValueError: except ValueError:
# No losing trade, therefore no drawdown. # No losing trade, therefore no drawdown.
return -total_profit return -total_profit

View File

@ -10,7 +10,7 @@ individual needs.
from pandas import DataFrame from pandas import DataFrame
from freqtrade.data.metrics import calc_max_drawdown from freqtrade.data.metrics import calculate_max_drawdown
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@ -24,7 +24,7 @@ class ProfitDrawDownHyperOptLoss(IHyperOptLoss):
total_profit = results["profit_abs"].sum() total_profit = results["profit_abs"].sum()
try: try:
drawdown = calc_max_drawdown(results, value_col="profit_abs") drawdown = calculate_max_drawdown(results, value_col="profit_abs")
relative_account_drawdown = drawdown.relative_account_drawdown relative_account_drawdown = drawdown.relative_account_drawdown
except ValueError: except ValueError:
relative_account_drawdown = 0 relative_account_drawdown = 0

View File

@ -8,12 +8,12 @@ from pandas import DataFrame, Series, concat, to_datetime
from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT
from freqtrade.data.metrics import ( from freqtrade.data.metrics import (
calc_max_drawdown,
calculate_cagr, calculate_cagr,
calculate_calmar, calculate_calmar,
calculate_csum, calculate_csum,
calculate_expectancy, calculate_expectancy,
calculate_market_change, calculate_market_change,
calculate_max_drawdown,
calculate_sharpe, calculate_sharpe,
calculate_sortino, calculate_sortino,
) )
@ -497,12 +497,12 @@ def generate_strategy_stats(
} }
try: try:
max_drawdown_legacy = calc_max_drawdown(results, value_col="profit_ratio") max_drawdown_legacy = calculate_max_drawdown(results, value_col="profit_ratio")
drawdown = calc_max_drawdown( drawdown = calculate_max_drawdown(
results, value_col="profit_abs", starting_balance=start_balance results, value_col="profit_abs", starting_balance=start_balance
) )
# max_relative_drawdown = Underwater # max_relative_drawdown = Underwater
underwater = calc_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
) )

View File

@ -16,7 +16,7 @@ from freqtrade.data.converter import trim_dataframe
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.data.history import get_timerange, load_data from freqtrade.data.history import get_timerange, load_data
from freqtrade.data.metrics import ( from freqtrade.data.metrics import (
calc_max_drawdown, calculate_max_drawdown,
calculate_underwater, calculate_underwater,
combine_dataframes_with_mean, combine_dataframes_with_mean,
create_cum_profit, create_cum_profit,
@ -179,7 +179,7 @@ def add_max_drawdown(
Add scatter points indicating max drawdown Add scatter points indicating max drawdown
""" """
try: try:
drawdown = calc_max_drawdown(trades, starting_balance=starting_balance) drawdown = calculate_max_drawdown(trades, starting_balance=starting_balance)
drawdown = go.Scatter( drawdown = go.Scatter(
x=[drawdown.high_date, drawdown.low_date], x=[drawdown.high_date, drawdown.low_date],

View File

@ -5,7 +5,7 @@ from typing import Any, Dict, Optional
import pandas as pd import pandas as pd
from freqtrade.constants import Config, LongShort from freqtrade.constants import Config, LongShort
from freqtrade.data.metrics import calc_max_drawdown from freqtrade.data.metrics import calculate_max_drawdown
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.plugins.protections import IProtection, ProtectionReturn from freqtrade.plugins.protections import IProtection, ProtectionReturn
@ -59,7 +59,7 @@ class MaxDrawdown(IProtection):
# Drawdown is always positive # Drawdown is always positive
try: try:
# TODO: This should use absolute profit calculation, considering account balance. # TODO: This should use absolute profit calculation, considering account balance.
drawdown_obj = calc_max_drawdown(trades_df, value_col="close_profit") drawdown_obj = calculate_max_drawdown(trades_df, value_col="close_profit")
drawdown = drawdown_obj.drawdown_abs drawdown = drawdown_obj.drawdown_abs
except ValueError: except ValueError:
return None return None

View File

@ -21,8 +21,8 @@ from freqtrade.constants import CANCEL_REASON, DEFAULT_DATAFRAME_COLUMNS, Config
from freqtrade.data.history import load_data from freqtrade.data.history import load_data
from freqtrade.data.metrics import ( from freqtrade.data.metrics import (
DrawDownResult, DrawDownResult,
calc_max_drawdown,
calculate_expectancy, calculate_expectancy,
calculate_max_drawdown,
) )
from freqtrade.enums import ( from freqtrade.enums import (
CandleType, CandleType,
@ -599,7 +599,7 @@ class RPC:
drawdown = DrawDownResult() drawdown = DrawDownResult()
if len(trades_df) > 0: if len(trades_df) > 0:
try: try:
drawdown = calc_max_drawdown( drawdown = calculate_max_drawdown(
trades_df, trades_df,
value_col="profit_abs", value_col="profit_abs",
date_col="close_date_dt", date_col="close_date_dt",

View File

@ -20,7 +20,6 @@ from freqtrade.data.btanalysis import (
) )
from freqtrade.data.history import load_data, load_pair_history from freqtrade.data.history import load_data, load_pair_history
from freqtrade.data.metrics import ( from freqtrade.data.metrics import (
calc_max_drawdown,
calculate_cagr, calculate_cagr,
calculate_calmar, calculate_calmar,
calculate_csum, calculate_csum,
@ -344,7 +343,7 @@ def test_create_cum_profit1(testdatadir):
def test_calculate_max_drawdown(testdatadir): def test_calculate_max_drawdown(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json" filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename) bt_data = load_backtest_data(filename)
drawdown = calc_max_drawdown(bt_data, value_col="profit_abs") drawdown = calculate_max_drawdown(bt_data, value_col="profit_abs")
assert isinstance(drawdown.relative_account_drawdown, float) assert isinstance(drawdown.relative_account_drawdown, float)
assert pytest.approx(drawdown.relative_account_drawdown) == 0.29753914 assert pytest.approx(drawdown.relative_account_drawdown) == 0.29753914
assert isinstance(drawdown.high_date, Timestamp) assert isinstance(drawdown.high_date, Timestamp)
@ -358,7 +357,7 @@ def test_calculate_max_drawdown(testdatadir):
assert isinstance(underwater, DataFrame) assert isinstance(underwater, DataFrame)
with pytest.raises(ValueError, match="Trade dataframe empty."): with pytest.raises(ValueError, match="Trade dataframe empty."):
calc_max_drawdown(DataFrame()) calculate_max_drawdown(DataFrame())
with pytest.raises(ValueError, match="Trade dataframe empty."): with pytest.raises(ValueError, match="Trade dataframe empty."):
calculate_underwater(DataFrame()) calculate_underwater(DataFrame())
@ -508,7 +507,7 @@ def test_calculate_max_drawdown2():
# sort by profit and reset index # sort by profit and reset index
df = df.sort_values("profit").reset_index(drop=True) df = df.sort_values("profit").reset_index(drop=True)
df1 = df.copy() df1 = df.copy()
drawdown = calc_max_drawdown(df, date_col="open_date", value_col="profit") drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit")
# Ensure df has not been altered. # Ensure df has not been altered.
assert df.equals(df1) assert df.equals(df1)
@ -522,12 +521,12 @@ def test_calculate_max_drawdown2():
df = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"]) df = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"])
with pytest.raises(ValueError, match="No losing trade, therefore no drawdown."): with pytest.raises(ValueError, match="No losing trade, therefore no drawdown."):
calc_max_drawdown(df, date_col="open_date", value_col="profit") calculate_max_drawdown(df, date_col="open_date", value_col="profit")
df1 = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"]) df1 = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"])
df1.loc[:, "profit"] = df1["profit"] * -1 df1.loc[:, "profit"] = df1["profit"] * -1
# No winning trade ... # No winning trade ...
drawdown = calc_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.043965 assert drawdown.drawdown_abs == 0.043965
@ -550,7 +549,9 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r
# sort by profit and reset index # sort by profit and reset index
df = df.sort_values("profit_abs").reset_index(drop=True) df = df.sort_values("profit_abs").reset_index(drop=True)
df1 = df.copy() df1 = df.copy()
drawdown = calc_max_drawdown(df, date_col="open_date", starting_balance=1000, relative=relative) drawdown = calculate_max_drawdown(
df, date_col="open_date", starting_balance=1000, relative=relative
)
# Ensure df has not been altered. # Ensure df has not been altered.
assert df.equals(df1) assert df.equals(df1)