diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index 09410667b..83c30bb68 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -171,7 +171,7 @@ class DrawDownResult: relative_account_drawdown: float = 0.0 -def calc_max_drawdown( +def calculate_max_drawdown( trades: pd.DataFrame, *, 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 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) + :return: DrawDownResult object 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. @@ -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]: """ 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 try: - drawdown = calc_max_drawdown( + drawdown = calculate_max_drawdown( trades, value_col="profit_abs", starting_balance=starting_balance ) max_drawdown = drawdown.relative_account_drawdown diff --git a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py index 1e065d8ad..ce1b29cf5 100644 --- a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py @@ -9,7 +9,7 @@ from datetime import datetime 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 @@ -38,7 +38,7 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): """ total_profit = results["profit_abs"].sum() try: - max_drawdown = calc_max_drawdown(results, value_col="profit_abs") + max_drawdown = calculate_max_drawdown(results, value_col="profit_abs") except ValueError: # No losing trade, therefore no drawdown. return -total_profit diff --git a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py index 886979382..ac3f82700 100644 --- a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py @@ -10,7 +10,7 @@ individual needs. 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 @@ -24,7 +24,7 @@ class ProfitDrawDownHyperOptLoss(IHyperOptLoss): total_profit = results["profit_abs"].sum() 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 except ValueError: relative_account_drawdown = 0 diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 4f818c21b..d40f3e73b 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -8,12 +8,12 @@ from pandas import DataFrame, Series, concat, to_datetime from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT from freqtrade.data.metrics import ( - calc_max_drawdown, calculate_cagr, calculate_calmar, calculate_csum, calculate_expectancy, calculate_market_change, + calculate_max_drawdown, calculate_sharpe, calculate_sortino, ) @@ -497,12 +497,12 @@ def generate_strategy_stats( } try: - max_drawdown_legacy = calc_max_drawdown(results, value_col="profit_ratio") - drawdown = calc_max_drawdown( + max_drawdown_legacy = calculate_max_drawdown(results, value_col="profit_ratio") + drawdown = calculate_max_drawdown( results, value_col="profit_abs", starting_balance=start_balance ) # max_relative_drawdown = Underwater - underwater = calc_max_drawdown( + underwater = calculate_max_drawdown( results, value_col="profit_abs", starting_balance=start_balance, relative=True ) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 0f0e9d527..de0910732 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -16,7 +16,7 @@ from freqtrade.data.converter import trim_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange, load_data from freqtrade.data.metrics import ( - calc_max_drawdown, + calculate_max_drawdown, calculate_underwater, combine_dataframes_with_mean, create_cum_profit, @@ -179,7 +179,7 @@ def add_max_drawdown( Add scatter points indicating max drawdown """ try: - drawdown = calc_max_drawdown(trades, starting_balance=starting_balance) + drawdown = calculate_max_drawdown(trades, starting_balance=starting_balance) drawdown = go.Scatter( x=[drawdown.high_date, drawdown.low_date], diff --git a/freqtrade/plugins/protections/max_drawdown_protection.py b/freqtrade/plugins/protections/max_drawdown_protection.py index 852e2dca5..a1ba166fa 100644 --- a/freqtrade/plugins/protections/max_drawdown_protection.py +++ b/freqtrade/plugins/protections/max_drawdown_protection.py @@ -5,7 +5,7 @@ from typing import Any, Dict, Optional import pandas as pd 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.plugins.protections import IProtection, ProtectionReturn @@ -59,7 +59,7 @@ class MaxDrawdown(IProtection): # Drawdown is always positive try: # 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 except ValueError: return None diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 4f60e3049..06c6c571b 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -21,8 +21,8 @@ from freqtrade.constants import CANCEL_REASON, DEFAULT_DATAFRAME_COLUMNS, Config from freqtrade.data.history import load_data from freqtrade.data.metrics import ( DrawDownResult, - calc_max_drawdown, calculate_expectancy, + calculate_max_drawdown, ) from freqtrade.enums import ( CandleType, @@ -599,7 +599,7 @@ class RPC: drawdown = DrawDownResult() if len(trades_df) > 0: try: - drawdown = calc_max_drawdown( + drawdown = calculate_max_drawdown( trades_df, value_col="profit_abs", date_col="close_date_dt", diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index c78e85421..d5dc8fc1e 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -20,7 +20,6 @@ from freqtrade.data.btanalysis import ( ) from freqtrade.data.history import load_data, load_pair_history from freqtrade.data.metrics import ( - calc_max_drawdown, calculate_cagr, calculate_calmar, calculate_csum, @@ -344,7 +343,7 @@ def test_create_cum_profit1(testdatadir): def test_calculate_max_drawdown(testdatadir): filename = testdatadir / "backtest_results/backtest-result.json" 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 pytest.approx(drawdown.relative_account_drawdown) == 0.29753914 assert isinstance(drawdown.high_date, Timestamp) @@ -358,7 +357,7 @@ def test_calculate_max_drawdown(testdatadir): assert isinstance(underwater, DataFrame) with pytest.raises(ValueError, match="Trade dataframe empty."): - calc_max_drawdown(DataFrame()) + calculate_max_drawdown(DataFrame()) with pytest.raises(ValueError, match="Trade dataframe empty."): calculate_underwater(DataFrame()) @@ -508,7 +507,7 @@ def test_calculate_max_drawdown2(): # sort by profit and reset index df = df.sort_values("profit").reset_index(drop=True) 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. assert df.equals(df1) @@ -522,12 +521,12 @@ def test_calculate_max_drawdown2(): df = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"]) 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.loc[:, "profit"] = df1["profit"] * -1 # 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 @@ -550,7 +549,9 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r # sort by profit and reset index df = df.sort_values("profit_abs").reset_index(drop=True) 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. assert df.equals(df1)