From 6df15a7af9aa59740af1802fd4e2b7da02132279 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 19 Mar 2022 18:54:11 -0600 Subject: [PATCH 01/13] Recursively search subdirectories in user_data/strategies for a strategy --- freqtrade/resolvers/iresolver.py | 13 +++++++++---- freqtrade/resolvers/strategy_resolver.py | 10 ++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 3ab461041..cddc8b84d 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -44,7 +44,7 @@ class IResolver: @classmethod def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None, - extra_dir: Optional[str] = None) -> List[Path]: + extra_dirs: Optional[List[str]] = None) -> List[Path]: abs_paths: List[Path] = [] if cls.initial_search_path: @@ -53,9 +53,10 @@ class IResolver: if user_subdir: abs_paths.insert(0, config['user_data_dir'].joinpath(user_subdir)) - if extra_dir: + if extra_dirs: # Add extra directory to the top of the search paths - abs_paths.insert(0, Path(extra_dir).resolve()) + for dir in extra_dirs: + abs_paths.insert(0, Path(dir).resolve()) return abs_paths @@ -164,9 +165,13 @@ class IResolver: :return: Object instance or None """ + extra_dirs: List[str] = [] + if extra_dir: + extra_dirs.append(extra_dir) + abs_paths = cls.build_search_paths(config, user_subdir=cls.user_subdir, - extra_dir=extra_dir) + extra_dirs=extra_dirs) found_object = cls._load_object(paths=abs_paths, object_name=object_name, kwargs=kwargs) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 8ad7cdb59..4f5d22e45 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -7,8 +7,9 @@ import logging import tempfile from base64 import urlsafe_b64decode from inspect import getfullargspec +from os import walk from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES from freqtrade.exceptions import OperationalException @@ -166,10 +167,15 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ + extra_dirs: List[str] = [ + path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") + ] # sub-directories + if extra_dir: + extra_dirs.append(extra_dir) abs_paths = StrategyResolver.build_search_paths(config, user_subdir=USERPATH_STRATEGIES, - extra_dir=extra_dir) + extra_dirs=extra_dirs) if ":" in strategy_name: logger.info("loading base64 encoded strategy") From 185daf5772fc1e42d014898eedc8bbbebd4bcfb8 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 20 Mar 2022 03:02:14 -0600 Subject: [PATCH 02/13] add recursive command line option --- freqtrade/commands/cli_options.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index f30c25ba1..955c1ae53 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -82,6 +82,11 @@ AVAILABLE_CLI_OPTIONS = { help='Reset sample files to their original state.', action='store_true', ), + "recursive": Arg( + '-r', '--recursive', + help='Recursively search for a strategy in the strategies folder.', + metavar='store_true', + ), # Main options "strategy": Arg( '-s', '--strategy', From f44ae494fb0c51510d27176d21cc17fd201b7234 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 31 Mar 2022 08:11:05 -0600 Subject: [PATCH 03/13] Added recursive to configuration --- freqtrade/configuration/configuration.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1ba17a04d..916c2b675 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -267,6 +267,12 @@ class Configuration: self._args_to_config(config, argname='strategy_list', logstring='Using strategy list of {} strategies', logfun=len) + self._args_to_config( + config, + argname='recursive', + logstring='Recursively searching for a strategy in the strategies folder.', + ) + self._args_to_config(config, argname='timeframe', logstring='Overriding timeframe with Command line argument') From b4b809ff8e19e3a0918cc8ff9e6ad1dbbb538077 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 31 Mar 2022 08:16:21 -0600 Subject: [PATCH 04/13] changed recursive to recursive_strategy_search --- freqtrade/commands/cli_options.py | 4 ++-- freqtrade/configuration/configuration.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 955c1ae53..2ed42b299 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -82,8 +82,8 @@ AVAILABLE_CLI_OPTIONS = { help='Reset sample files to their original state.', action='store_true', ), - "recursive": Arg( - '-r', '--recursive', + "recursive_strategy_search": Arg( + '-r', '--recursive_strategy_search', help='Recursively search for a strategy in the strategies folder.', metavar='store_true', ), diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 916c2b675..ae3ed45be 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -269,7 +269,7 @@ class Configuration: self._args_to_config( config, - argname='recursive', + argname='recursive_strategy_search', logstring='Recursively searching for a strategy in the strategies folder.', ) From 2fe5a1594f153b68bc576ebaaa0ceeb8397d5279 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 31 Mar 2022 08:16:41 -0600 Subject: [PATCH 05/13] Add conditional to recursive strategy searching if in config --- freqtrade/resolvers/strategy_resolver.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 4f5d22e45..44f02e232 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -167,9 +167,13 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ - extra_dirs: List[str] = [ - path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") - ] # sub-directories + if config['recursive_strategy_search']: + extra_dirs: List[str] = [ + path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") + ] # sub-directories + else: + extra_dirs = [] + if extra_dir: extra_dirs.append(extra_dir) From 82e9f62381d4747dc87994349646b261e9a7a88d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Apr 2022 20:27:32 +0200 Subject: [PATCH 06/13] Add missing setting in arguments.py --- freqtrade/commands/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 28f7d7148..2fb8d3258 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -12,7 +12,7 @@ from freqtrade.constants import DEFAULT_CONFIG ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] -ARGS_STRATEGY = ["strategy", "strategy_path"] +ARGS_STRATEGY = ["strategy", "strategy_path", "recursive_strategy_search"] ARGS_TRADE = ["db_url", "sd_notify", "dry_run", "dry_run_wallet", "fee"] From d5ce868f1aa1d99c0f7d936cc88161611dc8aa4c Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 10 Apr 2022 18:44:33 -0600 Subject: [PATCH 07/13] removed 1 letter alias for recursive-strategy-folder --- freqtrade/commands/cli_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 2ed42b299..095aad6c3 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -83,7 +83,7 @@ AVAILABLE_CLI_OPTIONS = { action='store_true', ), "recursive_strategy_search": Arg( - '-r', '--recursive_strategy_search', + '--recursive_strategy_search', help='Recursively search for a strategy in the strategies folder.', metavar='store_true', ), From c876d42e369baae4d86d7abd2db035454a6904b9 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 10 Apr 2022 18:50:51 -0600 Subject: [PATCH 08/13] safe check for recursive_strategy_search in strategy_resolver --- freqtrade/resolvers/strategy_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 44f02e232..8a22dbd65 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -167,7 +167,7 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ - if config['recursive_strategy_search']: + if 'recursive_strategy_search' in config and config['recursive_strategy_search']: extra_dirs: List[str] = [ path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") ] # sub-directories From 64e6729ae94b0c6027dc45be48b6185f7b1dbf6a Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 10 Apr 2022 18:56:28 -0600 Subject: [PATCH 09/13] docs for recursive_strategy_search --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index 2cb5dfa93..9c1b3718c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -121,6 +121,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `order_types` | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `order_time_in_force` | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price.
*Defaults to `0.02` 2%).*
**Datatype:** Positive float +| `recursive_strategy_search` | Set to `true` to recursively search sub-directories inside `user_data/strategies` for a strategy.
**Datatype:** Boolean | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
**Datatype:** String | `exchange.sandbox` | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.
**Datatype:** Boolean | `exchange.key` | API key to use for the exchange. Only required when you are in production mode.
**Keep it in secret, do not disclose publicly.**
**Datatype:** String From c6927a1501d76b4e8b61ced9f5d1c53dbee75006 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Apr 2022 09:10:15 +0200 Subject: [PATCH 10/13] Fix argument spelling --- freqtrade/commands/arguments.py | 3 ++- freqtrade/commands/cli_options.py | 4 ++-- freqtrade/resolvers/iresolver.py | 9 ++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 2fb8d3258..e66f100c0 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -37,7 +37,8 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] -ARGS_LIST_STRATEGIES = ["strategy_path", "print_one_column", "print_colorized"] +ARGS_LIST_STRATEGIES = ["strategy_path", "print_one_column", "print_colorized", + "recursive_strategy_search"] ARGS_LIST_HYPEROPTS = ["hyperopt_path", "print_one_column", "print_colorized"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 095aad6c3..548a7f473 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -83,9 +83,9 @@ AVAILABLE_CLI_OPTIONS = { action='store_true', ), "recursive_strategy_search": Arg( - '--recursive_strategy_search', + '--recursive-strategy-search', help='Recursively search for a strategy in the strategies folder.', - metavar='store_true', + action='store_true', ), # Main options "strategy": Arg( diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index cddc8b84d..d310856d8 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -44,7 +44,7 @@ class IResolver: @classmethod def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None, - extra_dirs: Optional[List[str]] = None) -> List[Path]: + extra_dirs: List[str] = []) -> List[Path]: abs_paths: List[Path] = [] if cls.initial_search_path: @@ -53,10 +53,9 @@ class IResolver: if user_subdir: abs_paths.insert(0, config['user_data_dir'].joinpath(user_subdir)) - if extra_dirs: - # Add extra directory to the top of the search paths - for dir in extra_dirs: - abs_paths.insert(0, Path(dir).resolve()) + # Add extra directory to the top of the search paths + for dir in extra_dirs: + abs_paths.insert(0, Path(dir).resolve()) return abs_paths From ba92e09b7bab024fb7ea7a12d1b3df22cf33fc6a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Apr 2022 09:11:50 +0200 Subject: [PATCH 11/13] list-strategies should find recursively as well --- config_examples/config_full.example.json | 1 + freqtrade/commands/list_commands.py | 9 +++++---- freqtrade/optimize/hyperopt_tools.py | 3 ++- freqtrade/resolvers/iresolver.py | 11 +++++++++-- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/rpc/api_server/api_v1.py | 3 ++- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 7931476b4..8f14e1771 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -179,6 +179,7 @@ "disable_dataframe_checks": false, "strategy": "SampleStrategy", "strategy_path": "user_data/strategies/", + "recursive_strategy_search": false, "dataformat_ohlcv": "json", "dataformat_trades": "jsongz" } diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index 38fb098a0..1833db922 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -41,7 +41,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: print(tabulate(exchanges, headers=['Exchange name', 'Valid', 'reason'])) -def _print_objs_tabular(objs: List, print_colorized: bool) -> None: +def _print_objs_tabular(objs: List, print_colorized: bool, base_dir: Path) -> None: if print_colorized: colorama_init(autoreset=True) red = Fore.RED @@ -55,7 +55,7 @@ def _print_objs_tabular(objs: List, print_colorized: bool) -> None: names = [s['name'] for s in objs] objs_to_print = [{ 'name': s['name'] if s['name'] else "--", - 'location': s['location'].name, + 'location': s['location'].relative_to(base_dir), 'status': (red + "LOAD FAILED" + reset if s['class'] is None else "OK" if names.count(s['name']) == 1 else yellow + "DUPLICATE NAME" + reset) @@ -77,7 +77,8 @@ def start_list_strategies(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, not args['print_one_column']) + strategy_objs = StrategyResolver.search_all_objects( + directory, not args['print_one_column'], config.get('recursive_strategy_search', False)) # Sort alphabetically strategy_objs = sorted(strategy_objs, key=lambda x: x['name']) for obj in strategy_objs: @@ -89,7 +90,7 @@ def start_list_strategies(args: Dict[str, Any]) -> None: if args['print_one_column']: print('\n'.join([s['name'] for s in strategy_objs])) else: - _print_objs_tabular(strategy_objs, config.get('print_colorized', False)) + _print_objs_tabular(strategy_objs, config.get('print_colorized', False), directory) def start_list_timeframes(args: Dict[str, Any]) -> None: diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 8c84f772a..e836681c5 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -41,7 +41,8 @@ class HyperoptTools(): """ from freqtrade.resolvers.strategy_resolver import StrategyResolver directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, False) + strategy_objs = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = [s for s in strategy_objs if s['name'] == strategy_name] if strategies: strategy = strategies[0] diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index d310856d8..74b28dffe 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -182,18 +182,25 @@ class IResolver: ) @classmethod - def search_all_objects(cls, directory: Path, - enum_failed: bool) -> List[Dict[str, Any]]: + def search_all_objects(cls, directory: Path, enum_failed: bool, + recursive: bool = False) -> List[Dict[str, Any]]: """ Searches a directory for valid objects :param directory: Path to search :param enum_failed: If True, will return None for modules which fail. Otherwise, failing modules are skipped. + :param recursive: Recursively walk directory tree searching for strategies :return: List of dicts containing 'name', 'class' and 'location' entries """ logger.debug(f"Searching for {cls.object_type.__name__} '{directory}'") objects = [] for entry in directory.iterdir(): + if ( + recursive and entry.is_dir() + and not entry.name.startswith('__') + and not entry.name.startswith('.') + ): + objects.extend(cls.search_all_objects(entry, enum_failed, recursive=recursive)) # Only consider python files if entry.suffix != '.py': logger.debug('Ignoring %s', entry) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 8a22dbd65..60961b15b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -167,7 +167,7 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ - if 'recursive_strategy_search' in config and config['recursive_strategy_search']: + if config.get('recursive_strategy_search', False): extra_dirs: List[str] = [ path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") ] # sub-directories diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 5a34385da..fe6426178 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -243,7 +243,8 @@ def list_strategies(config=Depends(get_config)): directory = Path(config.get( 'strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) from freqtrade.resolvers.strategy_resolver import StrategyResolver - strategies = StrategyResolver.search_all_objects(directory, False) + strategies = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = sorted(strategies, key=lambda x: x['name']) return {'strategies': [x['name'] for x in strategies]} From aa5345190ec36bb20d03cc66c16c958c371b9214 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Apr 2022 09:19:18 +0200 Subject: [PATCH 12/13] Test recursive strategy-listing --- tests/commands/test_commands.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 1431bd22a..39294e568 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -892,6 +892,26 @@ def test_start_list_strategies(mocker, caplog, capsys): assert "legacy_strategy_v1.py" in captured.out assert CURRENT_TEST_STRATEGY in captured.out assert "LOAD FAILED" in captured.out + # Recursive + assert "TestStrategyNoImplements" not in captured.out + + # Test recursive + args = [ + "list-strategies", + "--strategy-path", + str(Path(__file__).parent.parent / "strategy" / "strats"), + '--no-color', + '--recursive-strategy-search' + ] + pargs = get_args(args) + # pargs['config'] = None + start_list_strategies(pargs) + captured = capsys.readouterr() + assert "TestStrategyLegacyV1" in captured.out + assert "legacy_strategy_v1.py" in captured.out + assert "StrategyTestV2" in captured.out + assert "TestStrategyNoImplements" in captured.out + assert "broken_strats/broken_futures_strategies.py" in captured.out def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): From 30f314d580807bdb83969981293c162bdafc9a76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Apr 2022 10:44:11 +0200 Subject: [PATCH 13/13] windows compatibility of test --- tests/commands/test_commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 39294e568..d1f54ad52 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -847,7 +847,7 @@ def test_start_convert_trades(mocker, caplog): assert convert_mock.call_count == 1 -def test_start_list_strategies(mocker, caplog, capsys): +def test_start_list_strategies(capsys): args = [ "list-strategies", @@ -911,7 +911,7 @@ def test_start_list_strategies(mocker, caplog, capsys): assert "legacy_strategy_v1.py" in captured.out assert "StrategyTestV2" in captured.out assert "TestStrategyNoImplements" in captured.out - assert "broken_strats/broken_futures_strategies.py" in captured.out + assert str(Path("broken_strats/broken_futures_strategies.py")) in captured.out def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):