From 7f0e5dd3350167a838f62c5459f62070eead3fcb Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Mon, 5 Aug 2024 23:19:38 +0530 Subject: [PATCH] Refactor and add documentation --- docs/advanced-backtesting.md | 18 ++++++++--- freqtrade/data/entryexitanalysis.py | 22 +++++++++---- freqtrade/optimize/backtesting.py | 11 +++---- .../optimize/optimize_reports/__init__.py | 3 +- .../optimize_reports/optimize_reports.py | 31 +++---------------- tests/data/test_entryexitanalysis.py | 2 +- 6 files changed, 40 insertions(+), 47 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 563e5df08..c01d763f8 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -18,15 +18,15 @@ freqtrade backtesting -c --timeframe --strategy --rejected-signals ``` +### Printing analysis on exit signals + +Use the `--exit-signals` option to print out analysis on exited signals. + +```bash +freqtrade backtesting-analysis -c --exit-signals +``` + ### Writing tables to CSV Some of the tabular outputs can become large, so printing them out to the terminal is not preferable. diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index d274eabe0..f2440a787 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -51,7 +51,9 @@ def _load_exit_signal_candles(backtest_dir: Path): return _load_backtest_analysis_data(backtest_dir, "exited") -def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles): +def _process_candles_and_indicators( + pairlist, strategy_name, trades, signal_candles, analyse_on="open_date" +): analysed_trades_dict = {strategy_name: {}} try: @@ -60,7 +62,7 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand for pair in pairlist: if pair in signal_candles[strategy_name]: analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators( - pair, trades, signal_candles[strategy_name][pair] + pair, trades, signal_candles[strategy_name][pair], analyse_on ) except Exception as e: print(f"Cannot process entry/exit reasons for {strategy_name}: ", e) @@ -68,7 +70,9 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand return analysed_trades_dict -def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: pd.DataFrame): +def _analyze_candles_and_indicators( + pair, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date" +): buyf = signal_candles if len(buyf) > 0: @@ -78,8 +82,8 @@ def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: trades_inds = pd.DataFrame() if trades_red.shape[0] > 0 and buyf.shape[0] > 0: - for t, v in trades_red.open_date.items(): - allinds = buyf.loc[(buyf["date"] < v)] + for t, v in trades_red.iterrows(): + allinds = buyf.loc[(buyf["date"] < v[analyse_on])] if allinds.shape[0] > 0: tmp_inds = allinds.iloc[[-1]] @@ -339,8 +343,10 @@ def process_entry_exit_reasons(config: Config): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: + analyse_on = "open_date" if do_exited is True: signal_candles = _load_exit_signal_candles(config["exportfilename"]) + analyse_on = "close_date" else: signal_candles = _load_signal_candles(config["exportfilename"]) @@ -356,7 +362,11 @@ def process_entry_exit_reasons(config: Config): ) analysed_trades_dict = _process_candles_and_indicators( - config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles + config["exchange"]["pair_whitelist"], + strategy_name, + trades, + signal_candles, + analyse_on, ) res_df = prepare_results( diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8beb05ff4..bb088d064 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -42,8 +42,7 @@ from freqtrade.optimize.bt_progress import BTProgress from freqtrade.optimize.optimize_reports import ( generate_backtest_stats, generate_rejected_signals, - generate_trade_entry_signal_candles, - generate_trade_exit_signal_candles, + generate_trade_signal_candles, show_backtest_results, store_backtest_analysis_results, store_backtest_stats, @@ -1560,14 +1559,14 @@ class Backtesting: self.config.get("export", "none") == "signals" and self.dataprovider.runmode == RunMode.BACKTEST ): - self.processed_dfs[strategy_name] = generate_trade_entry_signal_candles( - preprocessed_tmp, results + self.processed_dfs[strategy_name] = generate_trade_signal_candles( + preprocessed_tmp, results, "open_date" ) self.rejected_df[strategy_name] = generate_rejected_signals( preprocessed_tmp, self.rejected_dict ) - self.exited_dfs[strategy_name] = generate_trade_exit_signal_candles( - preprocessed_tmp, results + self.exited_dfs[strategy_name] = generate_trade_signal_candles( + preprocessed_tmp, results, "close_date" ) return min_date, max_date diff --git a/freqtrade/optimize/optimize_reports/__init__.py b/freqtrade/optimize/optimize_reports/__init__.py index 1dcc1e885..6f3278a1c 100644 --- a/freqtrade/optimize/optimize_reports/__init__.py +++ b/freqtrade/optimize/optimize_reports/__init__.py @@ -25,7 +25,6 @@ from freqtrade.optimize.optimize_reports.optimize_reports import ( generate_strategy_comparison, generate_strategy_stats, generate_tag_metrics, - generate_trade_entry_signal_candles, - generate_trade_exit_signal_candles, + generate_trade_signal_candles, generate_trading_stats, ) diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index e82184516..2b0fd54bf 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -24,8 +24,8 @@ from freqtrade.util import decimals_per_coin, fmt_coin logger = logging.getLogger(__name__) -def generate_trade_entry_signal_candles( - preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] +def generate_trade_signal_candles( + preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], analysis_on="open_date" ) -> Dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): @@ -36,31 +36,8 @@ def generate_trade_entry_signal_candles( pairresults = resdf.loc[(resdf["pair"] == pair)] if pairdf.shape[0] > 0: - for t, v in pairresults.open_date.items(): - allinds = pairdf.loc[(pairdf["date"] < v)] - signal_inds = allinds.iloc[[-1]] - signal_candles_only_df = concat( - [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] - ) - - signal_candles_only[pair] = signal_candles_only_df - return signal_candles_only - - -def generate_trade_exit_signal_candles( - preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] -) -> Dict[str, DataFrame]: - signal_candles_only = {} - for pair in preprocessed_df.keys(): - signal_candles_only_df = DataFrame() - - pairdf = preprocessed_df[pair] - resdf = bt_results["results"] - pairresults = resdf.loc[(resdf["pair"] == pair)] - - if pairdf.shape[0] > 0: - for t, v in pairresults.close_date.items(): - allinds = pairdf.loc[(pairdf["date"] < v)] + for t, v in pairresults.iterrows(): + allinds = pairdf.loc[(pairdf["date"] < v[analysis_on])] signal_inds = allinds.iloc[[-1]] signal_candles_only_df = concat( [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 1a00e22dd..66e0179f8 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -393,8 +393,8 @@ def test_backtest_analysis_on_exit_signals_nomock( assert "0.5" in captured.out assert "-4" in captured.out assert "-2" in captured.out - assert "nan" in captured.out assert "57.654" in captured.out + assert "44.428" in captured.out assert "0" in captured.out assert "0.104" in captured.out assert "0.016" in captured.out