From 82e30c8519042187a1c7953ec8387e9b946b48d7 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:03:17 -0400 Subject: [PATCH] feat: if a biased_indicator starting with & appears in a lookahead-analysis, caption the table with a note that freqai targets appearing here can be ignored --- .../optimize/analysis/lookahead_helpers.py | 24 +++- tests/optimize/test_lookahead_analysis.py | 114 ++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/analysis/lookahead_helpers.py b/freqtrade/optimize/analysis/lookahead_helpers.py index a8fb1cd35..aff2083bf 100644 --- a/freqtrade/optimize/analysis/lookahead_helpers.py +++ b/freqtrade/optimize/analysis/lookahead_helpers.py @@ -19,7 +19,9 @@ logger = logging.getLogger(__name__) class LookaheadAnalysisSubFunctions: @staticmethod def text_table_lookahead_analysis_instances( - config: Dict[str, Any], lookahead_instances: List[LookaheadAnalysis] + config: Dict[str, Any], + lookahead_instances: List[LookaheadAnalysis], + caption: str | None = None, ): headers = [ "filename", @@ -65,7 +67,12 @@ class LookaheadAnalysisSubFunctions: ] ) - print_rich_table(data, headers, summary="Lookahead Analysis") + print_rich_table( + data, + headers, + summary="Lookahead Analysis", + table_kwargs={"caption": caption} + ) return data @staticmethod @@ -239,8 +246,19 @@ class LookaheadAnalysisSubFunctions: # report the results if lookaheadAnalysis_instances: + caption: str | None = None + if any([ + any([ + indicator.startswith("&") + for indicator in inst.current_analysis.false_indicators + ]) for inst in lookaheadAnalysis_instances + ]): + caption = ( + "Any indicators in 'biased_indicators' which are used within " + "set_freqai_targets() can be ignored." + ) LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( - config, lookaheadAnalysis_instances + config, lookaheadAnalysis_instances, caption=caption ) if config.get("lookahead_analysis_exportfilename") is not None: LookaheadAnalysisSubFunctions.export_to_csv(config, lookaheadAnalysis_instances) diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index f7d38b24b..387587afb 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -133,6 +133,72 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: text_table_mock.reset_mock() +@pytest.mark.parametrize( + "indicators, expected_caption_text", + [ + ( + ["&indicator1", "indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["indicator1", "&indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["&indicator1", "&indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["indicator1", "indicator2"], + None + ), + ( + [], + None + ) + ], + ids=( + "First of two biased indicators starts with '&'", + "Second of two biased indicators starts with '&'", + "Both biased indicators start with '&'", + "No biased indicators start with '&'", + "Empty biased indicators list", + ) +) +def test_lookahead_helper_start__caption_based_on_indicators( + indicators, + expected_caption_text, + lookahead_conf, + mocker +): + """Test that the table caption is only populated if a biased_indicator starts with '&'.""" + + single_mock = MagicMock() + lookahead_analysis = LookaheadAnalysis( + lookahead_conf, + {"name": "strategy_test_v3_with_lookahead_bias"}, + ) + lookahead_analysis.current_analysis.false_indicators = indicators + single_mock.return_value = lookahead_analysis + text_table_mock = MagicMock() + mocker.patch.multiple( + "freqtrade.optimize.analysis.lookahead_helpers.LookaheadAnalysisSubFunctions", + initialize_single_lookahead_analysis=single_mock, + text_table_lookahead_analysis_instances=text_table_mock, + ) + + LookaheadAnalysisSubFunctions.start(lookahead_conf) + + text_table_mock.assert_called_once_with( + lookahead_conf, + [lookahead_analysis], + caption=expected_caption_text + ) + + def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf): analysis = Analysis() analysis.has_bias = True @@ -199,6 +265,54 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf assert len(data) == 3 + +@pytest.mark.parametrize( + "caption", + [ + "", + "A test caption", + None, + False, + ], + ids=( + "Pass empty string", + "Pass non-empty string", + "Pass None", + "Don't pass caption", + ) +) +def test_lookahead_helper_text_table_lookahead_analysis_instances__caption( + caption, + lookahead_conf, + mocker, +): + """Test that the caption is passed in the table kwargs when calling print_rich_table().""" + + print_rich_table_mock = MagicMock() + mocker.patch( + "freqtrade.optimize.analysis.lookahead_helpers.print_rich_table", + print_rich_table_mock, + ) + lookahead_analysis = LookaheadAnalysis( + lookahead_conf, + { + "name": "strategy_test_v3_with_lookahead_bias", + "location": Path(lookahead_conf["strategy_path"], f"{lookahead_conf['strategy']}.py"), + } + ) + kwargs = {} + if caption is not False: + kwargs["caption"] = caption + + LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( + lookahead_conf, [lookahead_analysis], **kwargs + ) + + assert print_rich_table_mock.call_args[-1]["table_kwargs"]["caption"] == ( + caption if caption is not False else None + ) + + def test_lookahead_helper_export_to_csv(lookahead_conf): import pandas as pd