diff --git a/freqtrade/data/btanalysis/__init__.py b/freqtrade/data/btanalysis/__init__.py index 5ff8a746b..31e8c8a16 100644 --- a/freqtrade/data/btanalysis/__init__.py +++ b/freqtrade/data/btanalysis/__init__.py @@ -1,9 +1,7 @@ # flake8: noqa: F401 from .btanalysis import ( BT_DATA_COLUMNS, - analyze_trade_parallelism, delete_backtest_result, - evaluate_result_multi, extract_trades_of_period, find_existing_backtest_stats, get_backtest_market_change, @@ -27,3 +25,7 @@ from .btanalysis import ( trade_list_to_dataframe, update_backtest_metadata, ) +from .trade_parallelism import ( + analyze_trade_parallelism, + evaluate_result_multi, +) diff --git a/freqtrade/data/btanalysis/btanalysis.py b/freqtrade/data/btanalysis/btanalysis.py index b79c12959..3835e831a 100644 --- a/freqtrade/data/btanalysis/btanalysis.py +++ b/freqtrade/data/btanalysis/btanalysis.py @@ -13,7 +13,7 @@ from typing import Any, Literal import numpy as np import pandas as pd -from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf +from freqtrade.constants import LAST_BT_RESULT_FN from freqtrade.exceptions import ConfigurationError, OperationalException from freqtrade.ft_types import BacktestHistoryEntryType, BacktestResultType from freqtrade.misc import file_dump_json, json_load @@ -491,57 +491,6 @@ def load_exit_signal_candles(backtest_dir: Path) -> dict[str, dict[str, pd.DataF return load_backtest_analysis_data(backtest_dir, "exited") -def analyze_trade_parallelism(trades: pd.DataFrame, timeframe: str) -> pd.DataFrame: - """ - Find overlapping trades by expanding each trade once per period it was open - and then counting overlaps. - :param trades: Trades Dataframe - can be loaded from backtest, or created - via trade_list_to_dataframe - :param timeframe: Timeframe used for backtest - :return: dataframe with open-counts per time-period in timeframe - """ - from freqtrade.exchange import timeframe_to_resample_freq - - timeframe_freq = timeframe_to_resample_freq(timeframe) - dates = [ - pd.Series( - pd.date_range( - row[1]["open_date"], - row[1]["close_date"], - freq=timeframe_freq, - # Exclude right boundary - the date is the candle open date. - inclusive="left", - ) - ) - for row in trades[["open_date", "close_date"]].iterrows() - ] - deltas = [len(x) for x in dates] - dates = pd.Series(pd.concat(dates).values, name="date") - df2 = pd.DataFrame(np.repeat(trades.values, deltas, axis=0), columns=trades.columns) - - df2 = pd.concat([dates, df2], axis=1) - df2 = df2.set_index("date") - df_final = df2.resample(timeframe_freq)[["pair"]].count() - df_final = df_final.rename({"pair": "open_trades"}, axis=1) - return df_final - - -def evaluate_result_multi( - trades: pd.DataFrame, timeframe: str, max_open_trades: IntOrInf -) -> pd.DataFrame: - """ - Find overlapping trades by expanding each trade once per period it was open - and then counting overlaps - :param trades: Trades Dataframe - can be loaded from backtest, or created - via trade_list_to_dataframe - :param timeframe: Frequency used for the backtest - :param max_open_trades: parameter max_open_trades used during backtest run - :return: dataframe with open-counts per time-period in freq - """ - df_final = analyze_trade_parallelism(trades, timeframe) - return df_final[df_final["open_trades"] > max_open_trades] - - def trade_list_to_dataframe(trades: list[Trade] | list[LocalTrade]) -> pd.DataFrame: """ Convert list of Trade objects to pandas Dataframe diff --git a/freqtrade/data/btanalysis/trade_parallelism.py b/freqtrade/data/btanalysis/trade_parallelism.py new file mode 100644 index 000000000..eabdcf08a --- /dev/null +++ b/freqtrade/data/btanalysis/trade_parallelism.py @@ -0,0 +1,60 @@ +import logging + +import numpy as np +import pandas as pd + +from freqtrade.constants import IntOrInf + + +logger = logging.getLogger(__name__) + + +def analyze_trade_parallelism(trades: pd.DataFrame, timeframe: str) -> pd.DataFrame: + """ + Find overlapping trades by expanding each trade once per period it was open + and then counting overlaps. + :param trades: Trades Dataframe - can be loaded from backtest, or created + via trade_list_to_dataframe + :param timeframe: Timeframe used for backtest + :return: dataframe with open-counts per time-period in timeframe + """ + from freqtrade.exchange import timeframe_to_resample_freq + + timeframe_freq = timeframe_to_resample_freq(timeframe) + dates = [ + pd.Series( + pd.date_range( + row[1]["open_date"], + row[1]["close_date"], + freq=timeframe_freq, + # Exclude right boundary - the date is the candle open date. + inclusive="left", + ) + ) + for row in trades[["open_date", "close_date"]].iterrows() + ] + deltas = [len(x) for x in dates] + dates = pd.Series(pd.concat(dates).values, name="date") + df2 = pd.DataFrame(np.repeat(trades.values, deltas, axis=0), columns=trades.columns) + + df2 = pd.concat([dates, df2], axis=1) + df2 = df2.set_index("date") + df_final = df2.resample(timeframe_freq)[["pair"]].count() + df_final = df_final.rename({"pair": "open_trades"}, axis=1) + return df_final + + +def evaluate_result_multi( + trades: pd.DataFrame, timeframe: str, max_open_trades: IntOrInf +) -> pd.DataFrame: + """ + Find overlapping trades by expanding each trade once per period it was open + and then counting overlaps + :param trades: Trades Dataframe - can be loaded from backtest, or created + via trade_list_to_dataframe + :param timeframe: Frequency used for the backtest + :param max_open_trades: parameter max_open_trades used during backtest run + :return: dataframe with open-counts per time-period in freq + """ + df_final = analyze_trade_parallelism(trades, timeframe) + return df_final[df_final["open_trades"] > max_open_trades]