Merge pull request #11899 from mrpabloyeah/add-current-drawdown-in-telegram-profit-command

Add current drawdown in telegram profit command
This commit is contained in:
Matthias
2025-07-09 06:40:28 +02:00
committed by GitHub
5 changed files with 45 additions and 4 deletions

View File

@@ -174,12 +174,18 @@ def calculate_underwater(
@dataclass() @dataclass()
class DrawDownResult: class DrawDownResult:
# Max drawdown fields
drawdown_abs: float = 0.0 drawdown_abs: float = 0.0
high_date: pd.Timestamp = None high_date: pd.Timestamp = None
low_date: pd.Timestamp = None low_date: pd.Timestamp = None
high_value: float = 0.0 high_value: float = 0.0
low_value: float = 0.0 low_value: float = 0.0
relative_account_drawdown: float = 0.0 relative_account_drawdown: float = 0.0
# Current drawdown fields
current_high_date: pd.Timestamp = None
current_high_value: float = 0.0
current_drawdown_abs: float = 0.0
current_relative_account_drawdown: float = 0.0
def calculate_max_drawdown( def calculate_max_drawdown(
@@ -191,29 +197,31 @@ def calculate_max_drawdown(
relative: bool = False, relative: bool = False,
) -> DrawDownResult: ) -> DrawDownResult:
""" """
Calculate max drawdown and the corresponding close dates Calculate max drawdown and current drawdown with corresponding dates
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio) :param trades: DataFrame containing trades (requires columns close_date and profit_abs)
: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.
:param relative: If True, use relative drawdown for max calculation instead of absolute
:return: DrawDownResult object :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 relative account drawdown, and current drawdown information.
:raise: ValueError if trade-dataframe was found empty. :raise: ValueError if trade-dataframe was found empty.
""" """
if len(trades) == 0: if len(trades) == 0:
raise ValueError("Trade dataframe empty.") raise ValueError("Trade dataframe empty.")
profit_results = trades.sort_values(date_col).reset_index(drop=True) profit_results = trades.sort_values(date_col).reset_index(drop=True)
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
) )
# Calculate maximum drawdown
idxmin = ( idxmin = (
max_drawdown_df["drawdown_relative"].idxmax() max_drawdown_df["drawdown_relative"].idxmax()
if relative if relative
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.loc[high_idx, date_col]
low_date = profit_results.loc[idxmin, date_col] low_date = profit_results.loc[idxmin, date_col]
@@ -221,13 +229,27 @@ def calculate_max_drawdown(
low_val = max_drawdown_df.loc[idxmin, "cumulative"] low_val = max_drawdown_df.loc[idxmin, "cumulative"]
max_drawdown_rel = max_drawdown_df.loc[idxmin, "drawdown_relative"] max_drawdown_rel = max_drawdown_df.loc[idxmin, "drawdown_relative"]
# Calculate current drawdown
current_high_idx = max_drawdown_df["high_value"].iloc[:-1].idxmax()
current_high_date = profit_results.loc[current_high_idx, date_col]
current_high_value = max_drawdown_df.iloc[-1]["high_value"]
current_cumulative = max_drawdown_df.iloc[-1]["cumulative"]
current_drawdown_abs = current_high_value - current_cumulative
current_drawdown_relative = max_drawdown_df.iloc[-1]["drawdown_relative"]
return DrawDownResult( return DrawDownResult(
# Max drawdown
drawdown_abs=abs(max_drawdown_df.loc[idxmin, "drawdown"]), drawdown_abs=abs(max_drawdown_df.loc[idxmin, "drawdown"]),
high_date=high_date, high_date=high_date,
low_date=low_date, low_date=low_date,
high_value=high_val, high_value=high_val,
low_value=low_val, low_value=low_val,
relative_account_drawdown=max_drawdown_rel, relative_account_drawdown=max_drawdown_rel,
# Current drawdown
current_high_date=current_high_date,
current_high_value=current_high_value,
current_drawdown_abs=current_drawdown_abs,
current_relative_account_drawdown=current_drawdown_relative,
) )

View File

@@ -163,6 +163,11 @@ class Profit(BaseModel):
max_drawdown_start_timestamp: int max_drawdown_start_timestamp: int
max_drawdown_end: str max_drawdown_end: str
max_drawdown_end_timestamp: int max_drawdown_end_timestamp: int
current_drawdown: float
current_drawdown_abs: float
current_drawdown_high: float
current_drawdown_start: str
current_drawdown_start_timestamp: int
trading_volume: float | None = None trading_volume: float | None = None
bot_start_timestamp: int bot_start_timestamp: int
bot_start_date: str bot_start_date: str

View File

@@ -681,6 +681,11 @@ class RPC:
"max_drawdown_end_timestamp": dt_ts_def(drawdown.low_date), "max_drawdown_end_timestamp": dt_ts_def(drawdown.low_date),
"drawdown_high": drawdown.high_value, "drawdown_high": drawdown.high_value,
"drawdown_low": drawdown.low_value, "drawdown_low": drawdown.low_value,
"current_drawdown": drawdown.current_relative_account_drawdown,
"current_drawdown_abs": drawdown.current_drawdown_abs,
"current_drawdown_high": drawdown.current_high_value,
"current_drawdown_start": format_date(drawdown.current_high_date),
"current_drawdown_start_timestamp": dt_ts_def(drawdown.current_high_date),
"trading_volume": trading_volume, "trading_volume": trading_volume,
"bot_start_timestamp": dt_ts_def(bot_start, 0), "bot_start_timestamp": dt_ts_def(bot_start, 0),
"bot_start_date": format_date(bot_start), "bot_start_date": format_date(bot_start),

View File

@@ -1085,6 +1085,10 @@ class Telegram(RPCHandler):
f"({fmt_coin(stats['drawdown_high'], stake_cur)})`\n" f"({fmt_coin(stats['drawdown_high'], stake_cur)})`\n"
f" to `{stats['max_drawdown_end']} " f" to `{stats['max_drawdown_end']} "
f"({fmt_coin(stats['drawdown_low'], stake_cur)})`\n" f"({fmt_coin(stats['drawdown_low'], stake_cur)})`\n"
f"*Current Drawdown:* `{stats['current_drawdown']:.2%} "
f"({fmt_coin(stats['current_drawdown_abs'], stake_cur)})`\n"
f" from `{stats['current_drawdown_start']} "
f"({fmt_coin(stats['current_drawdown_high'], stake_cur)})`\n"
) )
await self._send_msg( await self._send_msg(
markdown_msg, markdown_msg,

View File

@@ -1332,6 +1332,11 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
"max_drawdown_start_timestamp": ANY, "max_drawdown_start_timestamp": ANY,
"max_drawdown_end": ANY, "max_drawdown_end": ANY,
"max_drawdown_end_timestamp": ANY, "max_drawdown_end_timestamp": ANY,
"current_drawdown": ANY,
"current_drawdown_abs": ANY,
"current_drawdown_high": ANY,
"current_drawdown_start": ANY,
"current_drawdown_start_timestamp": ANY,
"trading_volume": expected["trading_volume"], "trading_volume": expected["trading_volume"],
"bot_start_timestamp": 0, "bot_start_timestamp": 0,
"bot_start_date": "", "bot_start_date": "",