diff --git a/freqtrade/optimize/lookahead_analysis.py b/freqtrade/optimize/lookahead_analysis.py index e40322c88..4f3d7a4d0 100755 --- a/freqtrade/optimize/lookahead_analysis.py +++ b/freqtrade/optimize/lookahead_analysis.py @@ -250,12 +250,19 @@ class LookaheadAnalysis: self.analyze_row(idx, result_row) # check and report signals - if (self.current_analysis.false_entry_signals > 0 or - self.current_analysis.false_exit_signals > 0 or - len(self.current_analysis.false_indicators) > 0): - logger.info(f" => {self.local_config['strategy']} + : bias detected!") + if self.current_analysis.total_signals < self.local_config['minimum_trade_amount']: + logger.info(f" -> {self.local_config['strategy']} : too few trades. " + f"We only found {self.current_analysis.total_signals} trades. " + f"Hint: Extend the timerange " + f"to get at least {self.local_config['minimum_trade_amount']} " + f"or lower the value of minimum_trade_amount.") + self.failed_bias_check = True + elif (self.current_analysis.false_entry_signals > 0 or + self.current_analysis.false_exit_signals > 0 or + len(self.current_analysis.false_indicators) > 0): + logger.info(f" => {self.local_config['strategy']} : bias detected!") self.current_analysis.has_bias = True + self.failed_bias_check = False else: logger.info(self.local_config['strategy'] + ": no bias detected") - - self.failed_bias_check = False + self.failed_bias_check = False diff --git a/freqtrade/optimize/lookahead_analysis_helpers.py b/freqtrade/optimize/lookahead_analysis_helpers.py index f212d8403..49f225943 100644 --- a/freqtrade/optimize/lookahead_analysis_helpers.py +++ b/freqtrade/optimize/lookahead_analysis_helpers.py @@ -16,12 +16,24 @@ logger = logging.getLogger(__name__) class LookaheadAnalysisSubFunctions: @staticmethod - def text_table_lookahead_analysis_instances(lookahead_instances: List[LookaheadAnalysis]): + def text_table_lookahead_analysis_instances( + config: Dict[str, Any], + lookahead_instances: List[LookaheadAnalysis]): headers = ['filename', 'strategy', 'has_bias', 'total_signals', 'biased_entry_signals', 'biased_exit_signals', 'biased_indicators'] data = [] for inst in lookahead_instances: - if inst.failed_bias_check: + if config['minimum_trade_amount'] > inst.current_analysis.total_signals: + data.append( + [ + inst.strategy_obj['location'].parts[-1], + inst.strategy_obj['name'], + "too few trades caught " + f"({inst.current_analysis.total_signals}/{config['minimum_trade_amount']})." + f"Test failed." + ] + ) + elif inst.failed_bias_check: data.append( [ inst.strategy_obj['location'].parts[-1], @@ -77,14 +89,21 @@ class LookaheadAnalysisSubFunctions: index=None) for inst in lookahead_analysis: - new_row_data = {'filename': inst.strategy_obj['location'].parts[-1], - 'strategy': inst.strategy_obj['name'], - 'has_bias': inst.current_analysis.has_bias, - 'total_signals': inst.current_analysis.total_signals, - 'biased_entry_signals': inst.current_analysis.false_entry_signals, - 'biased_exit_signals': inst.current_analysis.false_exit_signals, - 'biased_indicators': ",".join(inst.current_analysis.false_indicators)} - csv_df = add_or_update_row(csv_df, new_row_data) + # only update if + if (inst.current_analysis.total_signals > config['minimum_trade_amount'] + and inst.failed_bias_check is not True): + new_row_data = {'filename': inst.strategy_obj['location'].parts[-1], + 'strategy': inst.strategy_obj['name'], + 'has_bias': inst.current_analysis.has_bias, + 'total_signals': + int(inst.current_analysis.total_signals), + 'biased_entry_signals': + int(inst.current_analysis.false_entry_signals), + 'biased_exit_signals': + int(inst.current_analysis.false_exit_signals), + 'biased_indicators': + ",".join(inst.current_analysis.false_indicators)} + csv_df = add_or_update_row(csv_df, new_row_data) logger.info(f"saving {config['lookahead_analysis_exportfilename']}") csv_df.to_csv(config['lookahead_analysis_exportfilename'], index=False) @@ -122,7 +141,7 @@ class LookaheadAnalysisSubFunctions: config['backtest_cache'] = 'none' strategy_objs = StrategyResolver.search_all_objects( - config, enum_failed=False, recursive=config.get('recursive_strategy_search', False)) + config, enum_failed=False, recursive=config.get('recursive_strategy_search', False)) lookaheadAnalysis_instances = [] @@ -147,7 +166,7 @@ class LookaheadAnalysisSubFunctions: # report the results if lookaheadAnalysis_instances: LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( - lookaheadAnalysis_instances) + config, lookaheadAnalysis_instances) if config.get('lookahead_analysis_exportfilename') is not None: LookaheadAnalysisSubFunctions.export_to_csv(config, lookaheadAnalysis_instances) else: diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index 814c9f3b8..476627c57 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -115,30 +115,40 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf instance = LookaheadAnalysis(lookahead_conf, strategy_obj) instance.current_analysis = analysis table, headers, data = (LookaheadAnalysisSubFunctions. - text_table_lookahead_analysis_instances([instance])) + text_table_lookahead_analysis_instances(lookahead_conf, [instance])) - # check row contents for a try that errored out + # check row contents for a try that has too few signals assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py' assert data[0][1] == 'strategy_test_v3_with_lookahead_bias' - assert data[0][2].__contains__('error') + assert data[0][2].__contains__('too few trades') assert len(data[0]) == 3 + # now check for an error which occured after enough trades + analysis.total_signals = 12 + analysis.false_entry_signals = 11 + analysis.false_exit_signals = 10 + instance = LookaheadAnalysis(lookahead_conf, strategy_obj) + instance.current_analysis = analysis + table, headers, data = (LookaheadAnalysisSubFunctions. + text_table_lookahead_analysis_instances(lookahead_conf, [instance])) + assert data[0][2].__contains__("error") + # edit it into not showing an error instance.failed_bias_check = False table, headers, data = (LookaheadAnalysisSubFunctions. - text_table_lookahead_analysis_instances([instance])) + text_table_lookahead_analysis_instances(lookahead_conf, [instance])) assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py' assert data[0][1] == 'strategy_test_v3_with_lookahead_bias' assert data[0][2] # True - assert data[0][3] == 5 - assert data[0][4] == 4 - assert data[0][5] == 3 + assert data[0][3] == 12 + assert data[0][4] == 11 + assert data[0][5] == 10 assert data[0][6] == '' analysis.false_indicators.append('falseIndicator1') analysis.false_indicators.append('falseIndicator2') table, headers, data = (LookaheadAnalysisSubFunctions. - text_table_lookahead_analysis_instances([instance])) + text_table_lookahead_analysis_instances(lookahead_conf, [instance])) assert data[0][6] == 'falseIndicator1, falseIndicator2' @@ -146,8 +156,8 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf assert len(data) == 1 # check amount of multiple rows - table, headers, data = (LookaheadAnalysisSubFunctions. - text_table_lookahead_analysis_instances([instance, instance, instance])) + table, headers, data = (LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( + lookahead_conf, [instance, instance, instance])) assert len(data) == 3 @@ -165,9 +175,9 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): # 1st check: create a new file and verify its contents analysis1 = Analysis() analysis1.has_bias = True - analysis1.total_signals = 5 - analysis1.false_entry_signals = 4 - analysis1.false_exit_signals = 3 + analysis1.total_signals = 12 + analysis1.false_entry_signals = 11 + analysis1.false_exit_signals = 10 analysis1.false_indicators.append('falseIndicator1') analysis1.false_indicators.append('falseIndicator2') lookahead_conf['lookahead_analysis_exportfilename'] = "temp_csv_lookahead_analysis.csv" @@ -178,6 +188,7 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): } instance1 = LookaheadAnalysis(lookahead_conf, strategy_obj1) + instance1.failed_bias_check = False instance1.current_analysis = analysis1 LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance1]) @@ -186,7 +197,7 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): expected_values1 = [ [ 'file1.py', 'strat1', True, - 5, 4, 3, + 12, 11, 10, "falseIndicator1,falseIndicator2" ], ] @@ -202,7 +213,7 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): expected_values2 = [ [ 'file1.py', 'strat1', False, - 10, 11, 12, + 22, 21, 20, "falseIndicator3,falseIndicator4" ], ] @@ -210,9 +221,9 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): analysis2 = Analysis() analysis2.has_bias = False - analysis2.total_signals = 10 - analysis2.false_entry_signals = 11 - analysis2.false_exit_signals = 12 + analysis2.total_signals = 22 + analysis2.false_entry_signals = 21 + analysis2.false_exit_signals = 20 analysis2.false_indicators.append('falseIndicator3') analysis2.false_indicators.append('falseIndicator4') @@ -222,6 +233,7 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): } instance2 = LookaheadAnalysis(lookahead_conf, strategy_obj2) + instance2.failed_bias_check = False instance2.current_analysis = analysis2 LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance2]) @@ -233,12 +245,12 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): expected_values3 = [ [ 'file1.py', 'strat1', False, - 10, 11, 12, + 22, 21, 20, "falseIndicator3,falseIndicator4" ], [ 'file3.py', 'strat3', True, - 20, 21, 22, "falseIndicator5,falseIndicator6" + 32, 31, 30, "falseIndicator5,falseIndicator6" ], ] @@ -246,9 +258,9 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): analysis3 = Analysis() analysis3.has_bias = True - analysis3.total_signals = 20 - analysis3.false_entry_signals = 21 - analysis3.false_exit_signals = 22 + analysis3.total_signals = 32 + analysis3.false_entry_signals = 31 + analysis3.false_exit_signals = 30 analysis3.false_indicators.append('falseIndicator5') analysis3.false_indicators.append('falseIndicator6') lookahead_conf['lookahead_analysis_exportfilename'] = "temp_csv_lookahead_analysis.csv" @@ -259,6 +271,7 @@ def test_lookahead_helper_export_to_csv(lookahead_conf): } instance3 = LookaheadAnalysis(lookahead_conf, strategy_obj3) + instance3.failed_bias_check = False instance3.current_analysis = analysis3 LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance3])