""" This module contains the argument manager class """ from argparse import ArgumentParser, Namespace, _ArgumentGroup from functools import partial from pathlib import Path from typing import Any from freqtrade.commands.cli_options import AVAILABLE_CLI_OPTIONS from freqtrade.constants import DEFAULT_CONFIG ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] ARGS_STRATEGY = [ "strategy", "strategy_path", "recursive_strategy_search", "freqaimodel", "freqaimodel_path", ] ARGS_TRADE = ["db_url", "sd_notify", "dry_run", "dry_run_wallet", "fee"] ARGS_WEBSERVER: list[str] = [] ARGS_COMMON_OPTIMIZE = [ "timeframe", "timerange", "dataformat_ohlcv", "max_open_trades", "stake_amount", "fee", "pairs", ] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + [ "position_stacking", "enable_protections", "dry_run_wallet", "timeframe_detail", "strategy_list", "export", "exportfilename", "backtest_breakdown", "backtest_cache", "freqai_backtest_live_models", ] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + [ "hyperopt", "hyperopt_path", "position_stacking", "enable_protections", "dry_run_wallet", "timeframe_detail", "epochs", "spaces", "print_all", "print_colorized", "print_json", "hyperopt_jobs", "hyperopt_random_state", "hyperopt_min_trades", "hyperopt_loss", "disableparamexport", "hyperopt_ignore_missing_space", "analyze_per_epoch", ] ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] ARGS_LIST_STRATEGIES = [ "strategy_path", "print_one_column", "print_colorized", "recursive_strategy_search", ] ARGS_LIST_FREQAIMODELS = ["freqaimodel_path", "print_one_column", "print_colorized"] ARGS_LIST_HYPEROPTS = ["hyperopt_path", "print_one_column", "print_colorized"] ARGS_BACKTEST_SHOW = ["exportfilename", "backtest_show_pair_list", "backtest_breakdown"] ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"] ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"] ARGS_LIST_PAIRS = [ "exchange", "print_list", "list_pairs_print_json", "print_one_column", "print_csv", "base_currencies", "quote_currencies", "list_pairs_all", "trading_mode", ] ARGS_TEST_PAIRLIST = [ "user_data_dir", "verbosity", "config", "quote_currencies", "print_one_column", "list_pairs_print_json", "exchange", ] ARGS_CREATE_USERDIR = ["user_data_dir", "reset"] ARGS_BUILD_CONFIG = ["config"] ARGS_SHOW_CONFIG = ["user_data_dir", "config", "show_sensitive"] ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "strategy_path", "template"] ARGS_CONVERT_DATA_TRADES = ["pairs", "format_from_trades", "format_to", "erase", "exchange"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "trading_mode", "candle_types"] ARGS_CONVERT_TRADES = [ "pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades", "trading_mode", ] ARGS_LIST_DATA = [ "exchange", "dataformat_ohlcv", "dataformat_trades", "trades", "pairs", "trading_mode", "show_timerange", ] ARGS_DOWNLOAD_DATA = [ "pairs", "pairs_file", "days", "new_pairs_days", "include_inactive", "timerange", "download_trades", "convert_trades", "exchange", "timeframes", "erase", "dataformat_ohlcv", "dataformat_trades", "trading_mode", "prepend_data", ] ARGS_PLOT_DATAFRAME = [ "pairs", "indicators1", "indicators2", "plot_limit", "db_url", "trade_source", "export", "exportfilename", "timerange", "timeframe", "no_trades", ] ARGS_PLOT_PROFIT = [ "pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "timeframe", "plot_auto_open", ] ARGS_CONVERT_DB = ["db_url", "db_url_from"] ARGS_INSTALL_UI = ["erase_ui_only", "ui_version"] ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"] ARGS_HYPEROPT_LIST = [ "hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_list_min_trades", "hyperopt_list_max_trades", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time", "hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit", "hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit", "hyperopt_list_min_objective", "hyperopt_list_max_objective", "print_colorized", "print_json", "hyperopt_list_no_details", "hyperoptexportfilename", "export_csv", ] ARGS_HYPEROPT_SHOW = [ "hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", "print_json", "hyperoptexportfilename", "hyperopt_show_no_header", "disableparamexport", "backtest_breakdown", ] ARGS_ANALYZE_ENTRIES_EXITS = [ "exportfilename", "analysis_groups", "enter_reason_list", "exit_reason_list", "indicator_list", "entry_only", "exit_only", "timerange", "analysis_rejected", "analysis_to_csv", "analysis_csv_path", ] ARGS_STRATEGY_UPDATER = ["strategy_list", "strategy_path", "recursive_strategy_search"] ARGS_LOOKAHEAD_ANALYSIS = [ a for a in ARGS_BACKTEST if a not in ("position_stacking", "backtest_cache", "backtest_breakdown") ] + ["minimum_trade_amount", "targeted_trade_amount", "lookahead_analysis_exportfilename"] ARGS_RECURSIVE_ANALYSIS = ["timeframe", "timerange", "dataformat_ohlcv", "pairs", "startup_candle"] # Command level configs - keep at the bottom of the above definitions NO_CONF_REQURIED = [ "convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-freqaimodels", "list-hyperoptloss", "list-data", "hyperopt-list", "hyperopt-show", "backtest-filter", "plot-dataframe", "plot-profit", "show-trades", "trades-to-ohlcv", "strategy-updater", ] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] class Arguments: """ Arguments Class. Manage the arguments received by the cli """ def __init__(self, args: list[str] | None) -> None: self.args = args self._parsed_arg: Namespace | None = None def get_parsed_arg(self) -> dict[str, Any]: """ Return the list of arguments :return: List[str] List of arguments """ if self._parsed_arg is None: self._build_subcommands() self._parsed_arg = self._parse_args() return vars(self._parsed_arg) def _parse_args(self) -> Namespace: """ Parses given arguments and returns an argparse Namespace instance. """ parsed_arg = self.parser.parse_args(self.args) # Workaround issue in argparse with action='append' and default value # (see https://bugs.python.org/issue16399) # Allow no-config for certain commands (like downloading / plotting) if "config" in parsed_arg and parsed_arg.config is None: conf_required = "command" in parsed_arg and parsed_arg.command in NO_CONF_REQURIED if "user_data_dir" in parsed_arg and parsed_arg.user_data_dir is not None: user_dir = parsed_arg.user_data_dir else: # Default case user_dir = "user_data" # Try loading from "user_data/config.json" cfgfile = Path(user_dir) / DEFAULT_CONFIG if cfgfile.is_file(): parsed_arg.config = [str(cfgfile)] else: # Else use "config.json". cfgfile = Path.cwd() / DEFAULT_CONFIG if cfgfile.is_file() or not conf_required: parsed_arg.config = [DEFAULT_CONFIG] return parsed_arg def _build_args(self, optionlist: list[str], parser: ArgumentParser | _ArgumentGroup) -> None: for val in optionlist: opt = AVAILABLE_CLI_OPTIONS[val] parser.add_argument(*opt.cli, dest=val, **opt.kwargs) def _build_subcommands(self) -> None: """ Builds and attaches all subcommands. :return: None """ # Build shared arguments (as group Common Options) _common_parser = ArgumentParser(add_help=False) group = _common_parser.add_argument_group("Common arguments") self._build_args(optionlist=ARGS_COMMON, parser=group) _strategy_parser = ArgumentParser(add_help=False) strategy_group = _strategy_parser.add_argument_group("Strategy arguments") self._build_args(optionlist=ARGS_STRATEGY, parser=strategy_group) # Build main command self.parser = ArgumentParser( prog="freqtrade", description="Free, open source crypto trading bot" ) self._build_args(optionlist=["version"], parser=self.parser) from freqtrade.commands import ( start_analysis_entries_exits, start_backtesting, start_backtesting_show, start_convert_data, start_convert_db, start_convert_trades, start_create_userdir, start_download_data, start_edge, start_hyperopt, start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, start_list_freqAI_models, start_list_hyperopt_loss_functions, start_list_markets, start_list_strategies, start_list_timeframes, start_lookahead_analysis, start_new_config, start_new_strategy, start_plot_dataframe, start_plot_profit, start_recursive_analysis, start_show_config, start_show_trades, start_strategy_update, start_test_pairlist, start_trading, start_webserver, ) subparsers = self.parser.add_subparsers( dest="command", # Use custom message when no subhandler is added # shown from `main.py` # required=True ) # Add trade subcommand trade_cmd = subparsers.add_parser( "trade", help="Trade module.", parents=[_common_parser, _strategy_parser] ) trade_cmd.set_defaults(func=start_trading) self._build_args(optionlist=ARGS_TRADE, parser=trade_cmd) # add create-userdir subcommand create_userdir_cmd = subparsers.add_parser( "create-userdir", help="Create user-data directory.", ) create_userdir_cmd.set_defaults(func=start_create_userdir) self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd) # add new-config subcommand build_config_cmd = subparsers.add_parser( "new-config", help="Create new config", ) build_config_cmd.set_defaults(func=start_new_config) self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd) # add show-config subcommand show_config_cmd = subparsers.add_parser( "show-config", help="Show resolved config", ) show_config_cmd.set_defaults(func=start_show_config) self._build_args(optionlist=ARGS_SHOW_CONFIG, parser=show_config_cmd) # add new-strategy subcommand build_strategy_cmd = subparsers.add_parser( "new-strategy", help="Create new strategy", ) build_strategy_cmd.set_defaults(func=start_new_strategy) self._build_args(optionlist=ARGS_BUILD_STRATEGY, parser=build_strategy_cmd) # Add download-data subcommand download_data_cmd = subparsers.add_parser( "download-data", help="Download backtesting data.", parents=[_common_parser], ) download_data_cmd.set_defaults(func=start_download_data) self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) # Add convert-data subcommand convert_data_cmd = subparsers.add_parser( "convert-data", help="Convert candle (OHLCV) data from one format to another.", parents=[_common_parser], ) convert_data_cmd.set_defaults(func=partial(start_convert_data, ohlcv=True)) self._build_args(optionlist=ARGS_CONVERT_DATA_OHLCV, parser=convert_data_cmd) # Add convert-trade-data subcommand convert_trade_data_cmd = subparsers.add_parser( "convert-trade-data", help="Convert trade data from one format to another.", parents=[_common_parser], ) convert_trade_data_cmd.set_defaults(func=partial(start_convert_data, ohlcv=False)) self._build_args(optionlist=ARGS_CONVERT_DATA_TRADES, parser=convert_trade_data_cmd) # Add trades-to-ohlcv subcommand convert_trade_data_cmd = subparsers.add_parser( "trades-to-ohlcv", help="Convert trade data to OHLCV data.", parents=[_common_parser], ) convert_trade_data_cmd.set_defaults(func=start_convert_trades) self._build_args(optionlist=ARGS_CONVERT_TRADES, parser=convert_trade_data_cmd) # Add list-data subcommand list_data_cmd = subparsers.add_parser( "list-data", help="List downloaded data.", parents=[_common_parser], ) list_data_cmd.set_defaults(func=start_list_data) self._build_args(optionlist=ARGS_LIST_DATA, parser=list_data_cmd) # Add backtesting subcommand backtesting_cmd = subparsers.add_parser( "backtesting", help="Backtesting module.", parents=[_common_parser, _strategy_parser] ) backtesting_cmd.set_defaults(func=start_backtesting) self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) # Add backtesting-show subcommand backtesting_show_cmd = subparsers.add_parser( "backtesting-show", help="Show past Backtest results", parents=[_common_parser], ) backtesting_show_cmd.set_defaults(func=start_backtesting_show) self._build_args(optionlist=ARGS_BACKTEST_SHOW, parser=backtesting_show_cmd) # Add backtesting analysis subcommand analysis_cmd = subparsers.add_parser( "backtesting-analysis", help="Backtest Analysis module.", parents=[_common_parser] ) analysis_cmd.set_defaults(func=start_analysis_entries_exits) self._build_args(optionlist=ARGS_ANALYZE_ENTRIES_EXITS, parser=analysis_cmd) # Add edge subcommand edge_cmd = subparsers.add_parser( "edge", help="Edge module.", parents=[_common_parser, _strategy_parser] ) edge_cmd.set_defaults(func=start_edge) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) # Add hyperopt subcommand hyperopt_cmd = subparsers.add_parser( "hyperopt", help="Hyperopt module.", parents=[_common_parser, _strategy_parser], ) hyperopt_cmd.set_defaults(func=start_hyperopt) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) # Add hyperopt-list subcommand hyperopt_list_cmd = subparsers.add_parser( "hyperopt-list", help="List Hyperopt results", parents=[_common_parser], ) hyperopt_list_cmd.set_defaults(func=start_hyperopt_list) self._build_args(optionlist=ARGS_HYPEROPT_LIST, parser=hyperopt_list_cmd) # Add hyperopt-show subcommand hyperopt_show_cmd = subparsers.add_parser( "hyperopt-show", help="Show details of Hyperopt results", parents=[_common_parser], ) hyperopt_show_cmd.set_defaults(func=start_hyperopt_show) self._build_args(optionlist=ARGS_HYPEROPT_SHOW, parser=hyperopt_show_cmd) # Add list-exchanges subcommand list_exchanges_cmd = subparsers.add_parser( "list-exchanges", help="Print available exchanges.", parents=[_common_parser], ) list_exchanges_cmd.set_defaults(func=start_list_exchanges) self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd) # Add list-markets subcommand list_markets_cmd = subparsers.add_parser( "list-markets", help="Print markets on exchange.", parents=[_common_parser], ) list_markets_cmd.set_defaults(func=partial(start_list_markets, pairs_only=False)) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_markets_cmd) # Add list-pairs subcommand list_pairs_cmd = subparsers.add_parser( "list-pairs", help="Print pairs on exchange.", parents=[_common_parser], ) list_pairs_cmd.set_defaults(func=partial(start_list_markets, pairs_only=True)) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd) # Add list-strategies subcommand list_strategies_cmd = subparsers.add_parser( "list-strategies", help="Print available strategies.", parents=[_common_parser], ) list_strategies_cmd.set_defaults(func=start_list_strategies) self._build_args(optionlist=ARGS_LIST_STRATEGIES, parser=list_strategies_cmd) # Add list-Hyperopt loss subcommand list_hyperopt_loss_cmd = subparsers.add_parser( "list-hyperoptloss", help="Print available hyperopt loss functions.", parents=[_common_parser], ) list_hyperopt_loss_cmd.set_defaults(func=start_list_hyperopt_loss_functions) self._build_args(optionlist=ARGS_LIST_HYPEROPTS, parser=list_hyperopt_loss_cmd) # Add list-freqAI Models subcommand list_freqaimodels_cmd = subparsers.add_parser( "list-freqaimodels", help="Print available freqAI models.", parents=[_common_parser], ) list_freqaimodels_cmd.set_defaults(func=start_list_freqAI_models) self._build_args(optionlist=ARGS_LIST_FREQAIMODELS, parser=list_freqaimodels_cmd) # Add list-timeframes subcommand list_timeframes_cmd = subparsers.add_parser( "list-timeframes", help="Print available timeframes for the exchange.", parents=[_common_parser], ) list_timeframes_cmd.set_defaults(func=start_list_timeframes) self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd) # Add show-trades subcommand show_trades = subparsers.add_parser( "show-trades", help="Show trades.", parents=[_common_parser], ) show_trades.set_defaults(func=start_show_trades) self._build_args(optionlist=ARGS_SHOW_TRADES, parser=show_trades) # Add test-pairlist subcommand test_pairlist_cmd = subparsers.add_parser( "test-pairlist", help="Test your pairlist configuration.", ) test_pairlist_cmd.set_defaults(func=start_test_pairlist) self._build_args(optionlist=ARGS_TEST_PAIRLIST, parser=test_pairlist_cmd) # Add db-convert subcommand convert_db = subparsers.add_parser( "convert-db", help="Migrate database to different system", ) convert_db.set_defaults(func=start_convert_db) self._build_args(optionlist=ARGS_CONVERT_DB, parser=convert_db) # Add install-ui subcommand install_ui_cmd = subparsers.add_parser( "install-ui", help="Install FreqUI", ) install_ui_cmd.set_defaults(func=start_install_ui) self._build_args(optionlist=ARGS_INSTALL_UI, parser=install_ui_cmd) # Add Plotting subcommand plot_dataframe_cmd = subparsers.add_parser( "plot-dataframe", help="Plot candles with indicators.", parents=[_common_parser, _strategy_parser], ) plot_dataframe_cmd.set_defaults(func=start_plot_dataframe) self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) # Plot profit plot_profit_cmd = subparsers.add_parser( "plot-profit", help="Generate plot showing profits.", parents=[_common_parser, _strategy_parser], ) plot_profit_cmd.set_defaults(func=start_plot_profit) self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) # Add webserver subcommand webserver_cmd = subparsers.add_parser( "webserver", help="Webserver module.", parents=[_common_parser] ) webserver_cmd.set_defaults(func=start_webserver) self._build_args(optionlist=ARGS_WEBSERVER, parser=webserver_cmd) # Add strategy_updater subcommand strategy_updater_cmd = subparsers.add_parser( "strategy-updater", help="updates outdated strategy files to the current version", parents=[_common_parser], ) strategy_updater_cmd.set_defaults(func=start_strategy_update) self._build_args(optionlist=ARGS_STRATEGY_UPDATER, parser=strategy_updater_cmd) # Add lookahead_analysis subcommand lookahead_analayis_cmd = subparsers.add_parser( "lookahead-analysis", help="Check for potential look ahead bias.", parents=[_common_parser, _strategy_parser], ) lookahead_analayis_cmd.set_defaults(func=start_lookahead_analysis) self._build_args(optionlist=ARGS_LOOKAHEAD_ANALYSIS, parser=lookahead_analayis_cmd) # Add recursive_analysis subcommand recursive_analayis_cmd = subparsers.add_parser( "recursive-analysis", help="Check for potential recursive formula issue.", parents=[_common_parser, _strategy_parser], ) recursive_analayis_cmd.set_defaults(func=start_recursive_analysis) self._build_args(optionlist=ARGS_RECURSIVE_ANALYSIS, parser=recursive_analayis_cmd)