diff --git a/.travis.yml b/.travis.yml index 1cc22dfbd..8aaff553f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,11 +28,11 @@ jobs: name: pytest - script: - cp config.json.example config.json - - freqtrade --datadir tests/testdata backtesting + - freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy name: backtest - script: - cp config.json.example config.json - - freqtrade --datadir tests/testdata --strategy SampleStrategy hyperopt --customhyperopt SampleHyperOpts -e 5 + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt name: hyperopt - script: flake8 name: flake8 diff --git a/Dockerfile b/Dockerfile index 21432f89f..dc9b04403 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,3 +24,5 @@ RUN pip install numpy --no-cache-dir \ COPY . /freqtrade/ RUN pip install -e . --no-cache-dir ENTRYPOINT ["freqtrade"] +# Default to trade mode +CMD [ "trade" ] diff --git a/build_helpers/publish_docker.sh b/build_helpers/publish_docker.sh index 839ca0876..b8318c196 100755 --- a/build_helpers/publish_docker.sh +++ b/build_helpers/publish_docker.sh @@ -23,7 +23,7 @@ if [ $? -ne 0 ]; then fi # Run backtest -docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} --datadir /tests/testdata backtesting +docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy DefaultStrategy if [ $? -ne 0 ]; then echo "failed running backtest" diff --git a/docs/backtesting.md b/docs/backtesting.md index 6d21fa2bf..19814303b 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -45,7 +45,7 @@ freqtrade --datadir user_data/data/bittrex-20180101 backtesting #### With a (custom) strategy file ```bash -freqtrade -s SampleStrategy backtesting +freqtrade backtesting -s SampleStrategy ``` Where `-s SampleStrategy` refers to the class name within the strategy file `sample_strategy.py` found in the `freqtrade/user_data/strategies` directory. diff --git a/docs/bot-usage.md b/docs/bot-usage.md index a0437976f..4665878d4 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -5,20 +5,18 @@ This page explains the different parameters of the bot and how to run it. !!! Note If you've used `setup.sh`, don't forget to activate your virtual environment (`source .env/bin/activate`) before running freqtrade commands. - ## Bot commands ``` -usage: freqtrade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] - [--db-url PATH] [--sd-notify] - {backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} +usage: freqtrade [-h] [-V] + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} ... Free, open source crypto trading bot positional arguments: - {backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} + trade Trade module. backtesting Backtesting module. edge Edge module. hyperopt Hyperopt module. @@ -32,6 +30,27 @@ positional arguments: optional arguments: -h, --help show this help message and exit + -V, --version show program's version number and exit + +``` + +### Bot trading commands + +``` +usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--db-url PATH] [--sd-notify] [--dry-run] + +optional arguments: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite://` for Dry Run). + --sd-notify Notify systemd service manager. + --dry-run Enforce dry-run for trading (removes Exchange secrets + and simulates trades). + +Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). --logfile FILE Log to the file specified. -V, --version show program's version number and exit @@ -43,15 +62,12 @@ optional arguments: Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH Path to userdata directory. - -s NAME, --strategy NAME - Specify strategy class name (default: - `DefaultStrategy`). - --strategy-path PATH Specify additional strategy lookup path. - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite://` for Dry Run). - --sd-notify Notify systemd service manager. +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. ``` ### How to specify which configuration file be used? @@ -60,7 +76,7 @@ The bot allows you to select which configuration file you want to use by means o the `-c/--config` command line option: ```bash -freqtrade -c path/far/far/away/config.json +freqtrade trade -c path/far/far/away/config.json ``` Per default, the bot loads the `config.json` configuration file from the current @@ -79,13 +95,13 @@ empty key and secrete values while running in the Dry Mode (which does not actua require them): ```bash -freqtrade -c ./config.json +freqtrade trade -c ./config.json ``` and specify both configuration files when running in the normal Live Trade Mode: ```bash -freqtrade -c ./config.json -c path/to/secrets/keys.config.json +freqtrade trade -c ./config.json -c path/to/secrets/keys.config.json ``` This could help you hide your private Exchange key and Exchange secrete on you local machine @@ -134,7 +150,7 @@ In `user_data/strategies` you have a file `my_awesome_strategy.py` which has a strategy class called `AwesomeStrategy` to load it: ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` If the bot does not find your strategy file, it will display in an error @@ -149,7 +165,7 @@ This parameter allows you to add an additional strategy lookup path, which gets checked before the default locations (The passed path must be a directory!): ```bash -freqtrade --strategy AwesomeStrategy --strategy-path /some/directory +freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory ``` #### How to install a strategy? @@ -165,7 +181,7 @@ using `--db-url`. This can also be used to specify a custom database in production mode. Example command: ```bash -freqtrade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite +freqtrade trade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite ``` ## Backtesting commands @@ -173,8 +189,10 @@ freqtrade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite Backtesting also uses the config specified via `-c/--config`. ``` -usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] - [--max_open_trades INT] +usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [-i TICKER_INTERVAL] + [--timerange TIMERANGE] [--max_open_trades INT] [--stake_amount STAKE_AMOUNT] [--fee FLOAT] [--eps] [--dmmp] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] @@ -211,11 +229,29 @@ optional arguments: --export EXPORT Export backtest results, argument are: trades. Example: `--export=trades` --export-filename PATH - Save backtest results to the file with this filename - (default: `user_data/backtest_results/backtest- - result.json`). Requires `--export` to be set as well. - Example: `--export-filename=user_data/backtest_results - /backtest_today.json` + Save backtest results to the file with this filename. + Requires `--export` to be set as well. Example: + `--export-filename=user_data/backtest_results/backtest + _today.json` + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. ``` @@ -223,7 +259,7 @@ optional arguments: The first time your run Backtesting, you will need to download some historic data first. This can be accomplished by using `freqtrade download-data`. -Check the corresponding [help page section](backtesting.md#Getting-data-for-backtesting-and-hyperopt) for more details +Check the corresponding [Data Downloading](data-download.md) section for more details ## Hyperopt commands @@ -231,12 +267,14 @@ To optimize your strategy, you can use hyperopt parameter hyperoptimization to find optimal parameter values for your stategy. ``` -usage: freqtrade hyperopt [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] +usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [-i TICKER_INTERVAL] [--timerange TIMERANGE] [--max_open_trades INT] [--stake_amount STAKE_AMOUNT] [--fee FLOAT] - [--customhyperopt NAME] [--hyperopt-path PATH] - [--eps] [-e INT] - [-s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] + [--hyperopt NAME] [--hyperopt-path PATH] [--eps] + [-e INT] + [--spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] [--dmmp] [--print-all] [--no-color] [--print-json] [-j JOBS] [--random-state INT] [--min-trades INT] [--continue] [--hyperopt-loss NAME] @@ -254,16 +292,15 @@ optional arguments: Specify stake_amount. --fee FLOAT Specify fee ratio. Will be applied twice (on trade entry and exit). - --customhyperopt NAME - Specify hyperopt class name (default: - `DefaultHyperOpt`). - --hyperopt-path PATH Specify additional lookup path for Hyperopts and + --hyperopt NAME Specify hyperopt class name which will be used by the + bot. + --hyperopt-path PATH Specify additional lookup path for Hyperopt and Hyperopt Loss functions. --eps, --enable-position-stacking Allow buying the same pair multiple times (position stacking). -e INT, --epochs INT Specify number of epochs (default: 100). - -s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...], --spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...] + --spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...] Specify which parameters to hyperopt. Space-separated list. Default: `all`. --dmmp, --disable-max-market-positions @@ -292,8 +329,27 @@ optional arguments: generate completely different results, since the target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, - OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default: + OnlyProfitHyperOptLoss, SharpeHyperOptLoss (default: `DefaultHyperOptLoss`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. ``` ## Edge commands @@ -301,7 +357,9 @@ optional arguments: To know your trade expectancy and winrate against historical data, you can use Edge. ``` -usage: freqtrade edge [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] +usage: freqtrade edge [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [-i TICKER_INTERVAL] [--timerange TIMERANGE] [--max_open_trades INT] [--stake_amount STAKE_AMOUNT] [--fee FLOAT] [--stoplosses STOPLOSS_RANGE] @@ -324,6 +382,24 @@ optional arguments: (without any space). Example: `--stoplosses=-0.01,-0.1,-0.001` +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. ``` To understand edge and how to read the results, please read the [edge documentation](edge.md). diff --git a/docs/configuration.md b/docs/configuration.md index 6439c9258..b7607e619 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -95,7 +95,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `db_url` | `sqlite:///tradesv3.sqlite`| Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. | `initial_state` | running | Defines the initial application state. More information below. | `forcebuy_enable` | false | Enables the RPC Commands to force a buy. More information below. -| `strategy` | DefaultStrategy | Defines Strategy class to use. +| `strategy` | None | **Required** Defines Strategy class to use. Recommended to set via `--strategy NAME`. | `strategy_path` | null | Adds an additional strategy lookup path (must be a directory). | `internals.process_throttle_secs` | 5 | **Required.** Set the process throttle. Value in second. | `internals.heartbeat_interval` | 60 | Print heartbeat message every X seconds. Set to 0 to disable heartbeat messages. diff --git a/docs/docker.md b/docs/docker.md index a06d82bdc..ff5bf7f25 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -160,7 +160,7 @@ docker run -d \ -v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ - freqtrade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy + freqtrade trade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy ``` !!! Note @@ -202,7 +202,7 @@ docker run -d \ -v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -v ~/.freqtrade/user_data/:/freqtrade/user_data/ \ - freqtrade --strategy AwsomelyProfitableStrategy backtesting + freqtrade backtesting --strategy AwsomelyProfitableStrategy ``` Head over to the [Backtesting Documentation](backtesting.md) for more details. diff --git a/docs/edge.md b/docs/edge.md index 6374da9e6..c7b088476 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -235,7 +235,7 @@ An example of its output: ### Update cached pairs with the latest data Edge requires historic data the same way as backtesting does. -Please refer to the [download section](backtesting.md#Getting-data-for-backtesting-and-hyperopt) of the documentation for details. +Please refer to the [Data Downloading](data-download.md) section of the documentation for details. ### Precising stoploss range diff --git a/docs/faq.md b/docs/faq.md index b9c662085..2416beae4 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -4,7 +4,7 @@ ### The bot does not start -Running the bot with `freqtrade --config config.json` does show the output `freqtrade: command not found`. +Running the bot with `freqtrade trade --config config.json` does show the output `freqtrade: command not found`. This could have the following reasons: diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 3c42a0428..6c1505e75 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -245,7 +245,7 @@ Because hyperopt tries a lot of combinations to find the best parameters it will We strongly recommend to use `screen` or `tmux` to prevent any connection loss. ```bash -freqtrade -c config.json hyperopt --customhyperopt -e 5000 --spaces all +freqtrade hyperopt --config config.json --hyperopt -e 5000 --spaces all ``` Use `` as the name of the custom hyperopt used. @@ -281,7 +281,7 @@ freqtrade hyperopt --timerange 20180401-20180501 Hyperopt can reuse `populate_indicators`, `populate_buy_trend`, `populate_sell_trend` from your strategy, assuming these methods are **not** in your custom hyperopt file, and a strategy is provided. ```bash -freqtrade --strategy SampleStrategy hyperopt --customhyperopt SampleHyperopt +freqtrade hyperopt --strategy SampleStrategy --customhyperopt SampleHyperopt ``` ### Running Hyperopt with Smaller Search Space diff --git a/docs/installation.md b/docs/installation.md index 1330e0994..593da2acf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -192,7 +192,7 @@ python3 -m pip install -e . If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins. ```bash -freqtrade -c config.json +freqtrade trade -c config.json ``` *Note*: If you run the bot on a server, you should consider using [Docker](docker.md) or a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout. diff --git a/docs/plotting.md b/docs/plotting.md index ca56bdaba..982a5cd65 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -23,13 +23,15 @@ The `freqtrade plot-dataframe` subcommand shows an interactive graph with three Possible arguments: ``` -usage: freqtrade plot-dataframe [-h] [-p PAIRS [PAIRS ...]] +usage: freqtrade plot-dataframe [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [-p PAIRS [PAIRS ...]] [--indicators1 INDICATORS1 [INDICATORS1 ...]] [--indicators2 INDICATORS2 [INDICATORS2 ...]] [--plot-limit INT] [--db-url PATH] [--trade-source {DB,file}] [--export EXPORT] [--export-filename PATH] - [--timerange TIMERANGE] + [--timerange TIMERANGE] [-i TICKER_INTERVAL] optional arguments: -h, --help show this help message and exit @@ -62,6 +64,28 @@ optional arguments: /backtest_today.json` --timerange TIMERANGE Specify what timerange of data to use. + -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name (default: + `DefaultStrategy`). + --strategy-path PATH Specify additional strategy lookup path. ``` @@ -83,7 +107,7 @@ Use `--indicators1` for the main plot and `--indicators2` for the subplot below You will almost certainly want to specify a custom strategy! This can be done by adding `-s Classname` / `--strategy ClassName` to the command. ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --indicators1 sma ema --indicators2 macd ``` ### Further usage examples @@ -91,25 +115,25 @@ freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma To plot multiple pairs, separate them with a space: ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH XRP/ETH +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH XRP/ETH ``` To plot a timerange (to zoom in) ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --timerange=20180801-20180805 +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --timerange=20180801-20180805 ``` To plot trades stored in a database use `--db-url` in combination with `--trade-source DB`: ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB +freqtrade plot-dataframe --strategy AwesomeStrategy --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB ``` To plot trades from a backtesting result, use `--export-filename ` ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH +freqtrade plot-dataframe --strategy AwesomeStrategy --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH ``` ## Plot profit @@ -133,10 +157,11 @@ The third graph can be useful to spot outliers, events in pairs that cause profi Possible options for the `freqtrade plot-profit` subcommand: ``` -usage: freqtrade plot-profit [-h] [-p PAIRS [PAIRS ...]] +usage: freqtrade plot-profit [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-p PAIRS [PAIRS ...]] [--timerange TIMERANGE] [--export EXPORT] [--export-filename PATH] [--db-url PATH] - [--trade-source {DB,file}] + [--trade-source {DB,file}] [-i TICKER_INTERVAL] optional arguments: -h, --help show this help message and exit @@ -159,6 +184,22 @@ optional arguments: --trade-source {DB,file} Specify the source for trades (Can be DB or file (backtest file)) Default: file + -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. ``` diff --git a/docs/rest-api.md b/docs/rest-api.md index d0e4c8247..70e090569 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -65,7 +65,7 @@ docker run -d \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -p 127.0.0.1:8080:8080 \ - freqtrade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy + freqtrade trade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy ``` !!! Danger "Security warning" diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 34f86f2ce..03aecd6ba 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -13,7 +13,7 @@ Let assume you have a class called `AwesomeStrategy` in the file `awesome-strate 2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name) ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` ## Change your strategy @@ -45,7 +45,7 @@ The current version is 2 - which is also the default when it's not set explicitl Future versions will require this to be set. ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` **For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py) @@ -485,7 +485,7 @@ The strategy template is located in the file If you want to use a strategy from a different directory you can pass `--strategy-path` ```bash -freqtrade --strategy AwesomeStrategy --strategy-path /some/directory +freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory ``` ### Common mistakes when developing strategies diff --git a/freqtrade.service b/freqtrade.service index 9de9f13c7..df220ed39 100644 --- a/freqtrade.service +++ b/freqtrade.service @@ -6,7 +6,7 @@ After=network.target # Set WorkingDirectory and ExecStart to your file paths accordingly # NOTE: %h will be resolved to /home/ WorkingDirectory=%h/freqtrade -ExecStart=/usr/bin/freqtrade +ExecStart=/usr/bin/freqtrade trade Restart=on-failure [Install] diff --git a/freqtrade.service.watchdog b/freqtrade.service.watchdog index ba491fa53..66ea00d76 100644 --- a/freqtrade.service.watchdog +++ b/freqtrade.service.watchdog @@ -6,7 +6,7 @@ After=network.target # Set WorkingDirectory and ExecStart to your file paths accordingly # NOTE: %h will be resolved to /home/ WorkingDirectory=%h/freqtrade -ExecStart=/usr/bin/freqtrade --sd-notify +ExecStart=/usr/bin/freqtrade trade --sd-notify Restart=always #Restart=on-failure diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index b0156fcd1..29d0d98a2 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -13,7 +13,7 @@ ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_dat ARGS_STRATEGY = ["strategy", "strategy_path"] -ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"] +ARGS_TRADE = ["db_url", "sd_notify", "dry_run"] ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", "max_open_trades", "stake_amount", "fee"] @@ -42,8 +42,9 @@ ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "download_trades", "exchange", "timeframes", "erase"] -ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", - "trade_source", "export", "exportfilename", "timerange", "ticker_interval"] +ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", + "db_url", "trade_source", "export", "exportfilename", + "timerange", "ticker_interval"] ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "ticker_interval"] @@ -61,11 +62,6 @@ class Arguments: def __init__(self, args: Optional[List[str]]) -> None: self.args = args self._parsed_arg: Optional[argparse.Namespace] = None - self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') - - def _load_args(self) -> None: - self._build_args(optionlist=ARGS_MAIN) - self._build_subcommands() def get_parsed_arg(self) -> Dict[str, Any]: """ @@ -73,7 +69,7 @@ class Arguments: :return: List[str] List of arguments """ if self._parsed_arg is None: - self._load_args() + self._build_subcommands() self._parsed_arg = self._parse_args() return vars(self._parsed_arg) @@ -84,22 +80,17 @@ class Arguments: """ parsed_arg = self.parser.parse_args(self.args) - # When no config is provided, but a config exists, use that configuration! - subparser = parsed_arg.subparser if 'subparser' in parsed_arg else None - # 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 (parsed_arg.config is None - and subparser not in NO_CONF_ALLOWED - and ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() - or (subparser not in NO_CONF_REQURIED))): + if ('config' in parsed_arg and parsed_arg.config is None and + ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or + not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))): parsed_arg.config = [constants.DEFAULT_CONFIG] return parsed_arg - def _build_args(self, optionlist, parser=None): - parser = parser or self.parser + def _build_args(self, optionlist, parser): for val in optionlist: opt = AVAILABLE_CLI_OPTIONS[val] @@ -110,38 +101,68 @@ class Arguments: Builds and attaches all subcommands. :return: None """ + # Build shared arguments (as group Common Options) + _common_parser = argparse.ArgumentParser(add_help=False) + group = _common_parser.add_argument_group("Common arguments") + self._build_args(optionlist=ARGS_COMMON, parser=group) + + _strategy_parser = argparse.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 = argparse.ArgumentParser(description='Free, open source crypto trading bot') + self._build_args(optionlist=['version'], parser=self.parser) + from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge from freqtrade.utils import (start_create_userdir, start_download_data, - start_list_exchanges, start_list_timeframes, - start_list_markets) + start_list_exchanges, start_list_markets, + start_list_timeframes, start_trading) + from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit - subparsers = self.parser.add_subparsers(dest='subparser') + 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 backtesting subcommand - backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.') + 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 edge subcommand - edge_cmd = subparsers.add_parser('edge', help='Edge module.') + 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.') + 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 create-userdir subcommand create_userdir_cmd = subparsers.add_parser('create-userdir', - help="Create user-data directory.") + 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 list-exchanges subcommand list_exchanges_cmd = subparsers.add_parser( 'list-exchanges', - help='Print available 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) @@ -149,7 +170,8 @@ class Arguments: # Add list-timeframes subcommand list_timeframes_cmd = subparsers.add_parser( 'list-timeframes', - help='Print available ticker intervals (timeframes) for the exchange.' + help='Print available ticker intervals (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) @@ -157,7 +179,8 @@ class Arguments: # Add list-markets subcommand list_markets_cmd = subparsers.add_parser( 'list-markets', - help='Print markets on exchange.' + 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) @@ -165,7 +188,8 @@ class Arguments: # Add list-pairs subcommand list_pairs_cmd = subparsers.add_parser( 'list-pairs', - help='Print pairs on exchange.' + 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) @@ -173,16 +197,17 @@ class Arguments: # Add download-data subcommand download_data_cmd = subparsers.add_parser( 'download-data', - help='Download backtesting 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 Plotting subcommand - from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit plot_dataframe_cmd = subparsers.add_parser( 'plot-dataframe', - help='Plot candles with indicators.' + 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) @@ -190,7 +215,8 @@ class Arguments: # Plot profit plot_profit_cmd = subparsers.add_parser( 'plot-profit', - help='Generate plot showing profits.' + help='Generate plot showing profits.', + parents=[_common_parser], ) plot_profit_cmd.set_defaults(func=start_plot_profit) self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 697e048db..6dc5ef026 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -65,9 +65,8 @@ AVAILABLE_CLI_OPTIONS = { # Main options "strategy": Arg( '-s', '--strategy', - help='Specify strategy class name (default: `%(default)s`).', + help='Specify strategy class name which will be used by the bot.', metavar='NAME', - default='DefaultStrategy', ), "strategy_path": Arg( '--strategy-path', @@ -86,6 +85,11 @@ AVAILABLE_CLI_OPTIONS = { help='Notify systemd service manager.', action='store_true', ), + "dry_run": Arg( + '--dry-run', + help='Enforce dry-run for trading (removes Exchange secrets and simulates trades).', + action='store_true', + ), # Optimize common "ticker_interval": Arg( '-i', '--ticker-interval', @@ -136,7 +140,7 @@ AVAILABLE_CLI_OPTIONS = { ), "exportfilename": Arg( '--export-filename', - help='Save backtest results to the file with this filename (default: `%(default)s`). ' + help='Save backtest results to the file with this filename. ' 'Requires `--export` to be set as well. ' 'Example: `--export-filename=user_data/backtest_results/backtest_today.json`', metavar='PATH', @@ -156,14 +160,13 @@ AVAILABLE_CLI_OPTIONS = { ), # Hyperopt "hyperopt": Arg( - '--customhyperopt', - help='Specify hyperopt class name (default: `%(default)s`).', + '--hyperopt', + help='Specify hyperopt class name which will be used by the bot.', metavar='NAME', - default=constants.DEFAULT_HYPEROPT, ), "hyperopt_path": Arg( '--hyperopt-path', - help='Specify additional lookup path for Hyperopts and Hyperopt Loss functions.', + help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.', metavar='PATH', ), "epochs": Arg( @@ -174,7 +177,7 @@ AVAILABLE_CLI_OPTIONS = { default=constants.HYPEROPT_EPOCH, ), "spaces": Arg( - '-s', '--spaces', + '--spaces', help='Specify which parameters to hyperopt. Space-separated list. ' 'Default: `%(default)s`.', choices=['all', 'buy', 'sell', 'roi', 'stoploss'], diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 4637e3e5d..3bf5e63a6 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -93,7 +93,7 @@ class Configuration: :return: Configuration dictionary """ # Load all configs - config: Dict[str, Any] = self.load_from_files(self.args["config"]) + config: Dict[str, Any] = self.load_from_files(self.args.get("config", [])) # Keep a copy of the original configuration file config['original_config'] = deepcopy(config) @@ -153,7 +153,7 @@ class Configuration: self._process_logging_options(config) # Set strategy if not specified in config and or if it's non default - if self.args.get("strategy") != constants.DEFAULT_STRATEGY or not config.get('strategy'): + if self.args.get("strategy") or not config.get('strategy'): config.update({'strategy': self.args.get("strategy")}) self._args_to_config(config, argname='strategy_path', @@ -171,6 +171,10 @@ class Configuration: if 'sd_notify' in self.args and self.args["sd_notify"]: config['internals'].update({'sd_notify': True}) + self._args_to_config(config, argname='dry_run', + logstring='Parameter --dry-run detected, ' + 'overriding dry_run to: {} ...') + def _process_datadir_options(self, config: Dict[str, Any]) -> None: """ Extract information for sys.argv and load directory configurations diff --git a/freqtrade/constants.py b/freqtrade/constants.py index f34232bb1..dabda910c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,8 +9,6 @@ PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec -DEFAULT_STRATEGY = 'DefaultStrategy' -DEFAULT_HYPEROPT = 'DefaultHyperOpt' DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite://' diff --git a/freqtrade/main.py b/freqtrade/main.py index 4d6f0dce7..0a2adf71a 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -15,7 +15,6 @@ from typing import Any, List from freqtrade import OperationalException from freqtrade.configuration import Arguments -from freqtrade.worker import Worker logger = logging.getLogger('freqtrade') @@ -33,16 +32,19 @@ def main(sysargv: List[str] = None) -> None: arguments = Arguments(sysargv) args = arguments.get_parsed_arg() - # A subcommand has been issued. - # Means if Backtesting or Hyperopt have been called we exit the bot + # Call subcommand. if 'func' in args: - args['func'](args) - # TODO: fetch return_code as returned by the command function here - return_code = 0 + return_code = args['func'](args) else: - # Load and run worker - worker = Worker(args) - worker.run() + # No subcommand was issued. + raise OperationalException( + "Usage of Freqtrade requires a subcommand to be specified.\n" + "To have the previous behavior (bot executing trades in live/dry-run modes, " + "depending on the value of the `dry_run` setting in the config), run freqtrade " + "as `freqtrade trade [options...]`.\n" + "To see the full list of options available, please use " + "`freqtrade --help` or `freqtrade --help`." + ) except SystemExit as e: return_code = e diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 3adf5eb43..1f2f588ef 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -78,7 +78,7 @@ def start_hyperopt(args: Dict[str, Any]) -> None: except Timeout: logger.info("Another running instance of freqtrade Hyperopt detected.") logger.info("Simultaneous execution of multiple Hyperopt commands is not supported. " - "Hyperopt module is resource hungry. Please run your Hyperopts sequentially " + "Hyperopt module is resource hungry. Please run your Hyperopt sequentially " "or on separate machines.") logger.info("Quitting now.") # TODO: return False here in order to help freqtrade to exit diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index ac41ba92f..2b2a81f00 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -1,6 +1,6 @@ """ IHyperOpt interface -This module defines the interface to apply for hyperopts +This module defines the interface to apply for hyperopt """ import logging import math @@ -27,8 +27,8 @@ def _format_exception_message(method: str, space: str) -> str: class IHyperOpt(ABC): """ - Interface for freqtrade hyperopts - Defines the mandatory structure must follow any custom hyperopts + Interface for freqtrade hyperopt + Defines the mandatory structure must follow any custom hyperopt Class attributes you can use: ticker_interval -> int: value of the ticker interval to use for the strategy diff --git a/freqtrade/optimize/hyperopt_loss_interface.py b/freqtrade/optimize/hyperopt_loss_interface.py index b11b6e661..879a9f0e9 100644 --- a/freqtrade/optimize/hyperopt_loss_interface.py +++ b/freqtrade/optimize/hyperopt_loss_interface.py @@ -1,6 +1,6 @@ """ IHyperOptLoss interface -This module defines the interface for the loss-function for hyperopts +This module defines the interface for the loss-function for hyperopt """ from abc import ABC, abstractmethod @@ -11,7 +11,7 @@ from pandas import DataFrame class IHyperOptLoss(ABC): """ - Interface for freqtrade hyperopts Loss functions. + Interface for freqtrade hyperopt Loss functions. Defines the custom loss function (`hyperopt_loss_function()` which is evaluated every epoch.) """ ticker_interval: str diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 1ad53fe33..df1ff182c 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -1,14 +1,14 @@ # pragma pylint: disable=attribute-defined-outside-init """ -This module load custom hyperopts +This module load custom hyperopt """ import logging from pathlib import Path from typing import Optional, Dict from freqtrade import OperationalException -from freqtrade.constants import DEFAULT_HYPEROPT, DEFAULT_HYPEROPT_LOSS +from freqtrade.constants import DEFAULT_HYPEROPT_LOSS from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver @@ -20,7 +20,6 @@ class HyperOptResolver(IResolver): """ This class contains all the logic to load custom hyperopt class """ - __slots__ = ['hyperopt'] def __init__(self, config: Dict) -> None: @@ -28,9 +27,12 @@ class HyperOptResolver(IResolver): Load the custom class from config parameter :param config: configuration dictionary """ + if not config.get('hyperopt'): + raise OperationalException("No Hyperopt set. Please use `--hyperopt` to specify " + "the Hyperopt class to use.") + + hyperopt_name = config['hyperopt'] - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT self.hyperopt = self._load_hyperopt(hyperopt_name, config, extra_dir=config.get('hyperopt_path')) @@ -72,27 +74,28 @@ class HyperOptLossResolver(IResolver): """ This class contains all the logic to load custom hyperopt loss class """ - __slots__ = ['hyperoptloss'] - def __init__(self, config: Dict = None) -> None: + def __init__(self, config: Dict) -> None: """ Load the custom class from config parameter - :param config: configuration dictionary or None + :param config: configuration dictionary """ - config = config or {} - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS + # Verify the hyperopt_loss is in the configuration, otherwise fallback to the + # default hyperopt loss + hyperoptloss_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS + self.hyperoptloss = self._load_hyperoptloss( - hyperopt_name, config, extra_dir=config.get('hyperopt_path')) + hyperoptloss_name, config, extra_dir=config.get('hyperopt_path')) # Assign ticker_interval to be used in hyperopt self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval']) if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'): raise OperationalException( - f"Found hyperopt {hyperopt_name} does not implement `hyperopt_loss_function`.") + f"Found HyperoptLoss class {hyperoptloss_name} does not " + "implement `hyperopt_loss_function`.") def _load_hyperoptloss( self, hyper_loss_name: str, config: Dict, diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 5bea74027..102816981 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -32,8 +32,11 @@ class StrategyResolver(IResolver): """ config = config or {} - # Verify the strategy is in the configuration, otherwise fallback to the default strategy - strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY + if not config.get('strategy'): + raise OperationalException("No strategy set. Please use `--strategy` to specify " + "the strategy class to use.") + + strategy_name = config['strategy'] self.strategy: IStrategy = self._load_strategy(strategy_name, config=config, extra_dir=config.get('strategy_path')) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 03cf7dabf..5ad134ef9 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -39,6 +39,17 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str return config +def start_trading(args: Dict[str, Any]) -> int: + """ + Main entry point for trading mode + """ + from freqtrade.worker import Worker + # Load and run worker + worker = Worker(args) + worker.run() + return 0 + + def start_list_exchanges(args: Dict[str, Any]) -> None: """ Print available exchanges @@ -57,7 +68,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: def start_create_userdir(args: Dict[str, Any]) -> None: """ - Create "user_data" directory to contain user data strategies, hyperopts, ...) + Create "user_data" directory to contain user data strategies, hyperopt, ...) :param args: Cli args from Arguments() :return: None """ diff --git a/tests/conftest.py b/tests/conftest.py index 4feae6a60..c5cfd702b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -252,6 +252,7 @@ def default_conf(testdatadir): "db_url": "sqlite://", "user_data_dir": Path("user_data"), "verbosity": 3, + "strategy": "DefaultStrategy" } return configuration diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 508c12e89..b722dd3f8 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -184,9 +184,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] config = setup_configuration(get_args(args), RunMode.BACKTEST) @@ -217,10 +217,10 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> ) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - 'backtesting', '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', @@ -269,9 +269,9 @@ def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] with pytest.raises(DependencyException, match=r'.*stake amount.*'): @@ -286,9 +286,9 @@ def test_start(mocker, fee, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] args = get_args(args) start_backtesting(args) @@ -817,10 +817,10 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', str(testdatadir), - 'backtesting', '--ticker-interval', '1m', '--timerange', '1510694220-1510700340', '--enable-position-stacking', @@ -866,9 +866,9 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--datadir', str(testdatadir), - 'backtesting', '--ticker-interval', '1m', '--timerange', '1510694220-1510700340', '--enable-position-stacking', diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index 2c45a8d51..ddfa7156e 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -15,9 +15,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'edge' ] config = setup_configuration(get_args(args), RunMode.EDGE) @@ -45,10 +45,10 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N ) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - 'edge', '--ticker-interval', '1m', '--timerange', ':100', '--stoplosses=-0.01,-0.10,-0.001' @@ -79,9 +79,9 @@ def test_start(mocker, fee, edge_conf, caplog) -> None: patched_configuration_load_config_file(mocker, edge_conf) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'edge' ] args = get_args(args) start_edge(args) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 23d8a887c..e19835e58 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -26,7 +26,10 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, @pytest.fixture(scope='function') def hyperopt(default_conf, mocker): - default_conf.update({'spaces': ['all']}) + default_conf.update({ + 'spaces': ['all'], + 'hyperopt': 'DefaultHyperOpt', + }) patch_exchange(mocker) return Hyperopt(default_conf) @@ -69,8 +72,9 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca patched_configuration_load_config_file(mocker, default_conf) args = [ + 'hyperopt', '--config', 'config.json', - 'hyperopt' + '--hyperopt', 'DefaultHyperOpt', ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -100,9 +104,10 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo ) args = [ - '--config', 'config.json', - '--datadir', '/foo/bar', 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', + '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', '--enable-position-stacking', @@ -157,7 +162,8 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', MagicMock(return_value=hyperopt(default_conf)) ) - x = HyperOptResolver(default_conf, ).hyperopt + default_conf.update({'hyperopt': 'DefaultHyperOpt'}) + x = HyperOptResolver(default_conf).hyperopt assert not hasattr(x, 'populate_indicators') assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_sell_trend') @@ -174,7 +180,15 @@ def test_hyperoptresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt': "NonExistingHyperoptClass"}) with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'): - HyperOptResolver(default_conf, ).hyperopt + HyperOptResolver(default_conf).hyperopt + + +def test_hyperoptresolver_noname(default_conf): + default_conf['hyperopt'] = '' + with pytest.raises(OperationalException, + match="No Hyperopt set. Please use `--hyperopt` to specify " + "the Hyperopt class to use."): + HyperOptResolver(default_conf) def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: @@ -184,7 +198,7 @@ def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss', MagicMock(return_value=hl) ) - x = HyperOptLossResolver(default_conf, ).hyperoptloss + x = HyperOptLossResolver(default_conf).hyperoptloss assert hasattr(x, "hyperopt_loss_function") @@ -192,7 +206,7 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt_loss': "NonExistingLossClass"}) with pytest.raises(OperationalException, match=r'Impossible to load HyperoptLoss.*'): - HyperOptLossResolver(default_conf, ).hyperopt + HyperOptLossResolver(default_conf).hyperopt def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None: @@ -203,8 +217,9 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -220,8 +235,9 @@ def test_start(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -242,8 +258,9 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -258,8 +275,9 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -412,6 +430,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -515,10 +534,12 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None: def test_generate_optimizer(mocker, default_conf) -> None: - default_conf.update({'config': 'config.json.example'}) - default_conf.update({'timerange': None}) - default_conf.update({'spaces': 'all'}) - default_conf.update({'hyperopt_min_trades': 1}) + default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', + 'timerange': None, + 'spaces': 'all', + 'hyperopt_min_trades': 1, + }) trades = [ ('TRX/BTC', 0.023117, 0.000233, 100) @@ -582,6 +603,7 @@ def test_generate_optimizer(mocker, default_conf) -> None: def test_clean_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -598,6 +620,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -628,6 +651,7 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -666,6 +690,7 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) -> patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -705,6 +730,7 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -747,6 +773,7 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) - patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -781,6 +808,7 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'buy', @@ -827,6 +855,7 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'sell', @@ -875,6 +904,7 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': space, diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 12770f2c7..97affc99c 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -54,21 +54,30 @@ def test_load_strategy_base64(result, caplog, default_conf): def test_load_strategy_invalid_directory(result, caplog, default_conf): + default_conf['strategy'] = 'SampleStrategy' resolver = StrategyResolver(default_conf) extra_dir = Path.cwd() / 'some/path' resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) - assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) + assert 'rsi' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) def test_load_not_found_strategy(default_conf): - strategy = StrategyResolver(default_conf) + default_conf['strategy'] = 'NotFoundStrategy' with pytest.raises(OperationalException, match=r"Impossible to load Strategy 'NotFoundStrategy'. " r"This class does not exist or contains Python code errors."): - strategy._load_strategy(strategy_name='NotFoundStrategy', config=default_conf) + StrategyResolver(default_conf) + + +def test_load_strategy_noname(default_conf): + default_conf['strategy'] = '' + with pytest.raises(OperationalException, + match="No strategy set. Please use `--strategy` to specify " + "the strategy class to use."): + StrategyResolver(default_conf) def test_strategy(result, default_conf): diff --git a/tests/test_arguments.py b/tests/test_arguments.py index e874e6769..d8fbace0f 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -11,7 +11,7 @@ from freqtrade.configuration.cli_options import check_int_positive # Parse common command-line-arguments. Used for all tools def test_parse_args_none() -> None: - arguments = Arguments([]) + arguments = Arguments(['trade']) assert isinstance(arguments, Arguments) x = arguments.get_parsed_arg() assert isinstance(x, dict) @@ -19,7 +19,7 @@ def test_parse_args_none() -> None: def test_parse_args_defaults() -> None: - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() assert args["config"] == ['config.json'] assert args["strategy_path"] is None assert args["datadir"] is None @@ -27,27 +27,27 @@ def test_parse_args_defaults() -> None: def test_parse_args_config() -> None: - args = Arguments(['-c', '/dev/null']).get_parsed_arg() + args = Arguments(['trade', '-c', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] - args = Arguments(['--config', '/dev/null']).get_parsed_arg() + args = Arguments(['trade', '--config', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] - args = Arguments(['--config', '/dev/null', + args = Arguments(['trade', '--config', '/dev/null', '--config', '/dev/zero'],).get_parsed_arg() assert args["config"] == ['/dev/null', '/dev/zero'] def test_parse_args_db_url() -> None: - args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg() + args = Arguments(['trade', '--db-url', 'sqlite:///test.sqlite']).get_parsed_arg() assert args["db_url"] == 'sqlite:///test.sqlite' def test_parse_args_verbose() -> None: - args = Arguments(['-v']).get_parsed_arg() + args = Arguments(['trade', '-v']).get_parsed_arg() assert args["verbosity"] == 1 - args = Arguments(['--verbose']).get_parsed_arg() + args = Arguments(['trade', '--verbose']).get_parsed_arg() assert args["verbosity"] == 1 @@ -69,7 +69,7 @@ def test_parse_args_invalid() -> None: def test_parse_args_strategy() -> None: - args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg() + args = Arguments(['trade', '--strategy', 'SomeStrategy']).get_parsed_arg() assert args["strategy"] == 'SomeStrategy' @@ -79,7 +79,7 @@ def test_parse_args_strategy_invalid() -> None: def test_parse_args_strategy_path() -> None: - args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg() + args = Arguments(['trade', '--strategy-path', '/some/path']).get_parsed_arg() assert args["strategy_path"] == '/some/path' @@ -98,8 +98,8 @@ def test_parse_args_backtesting_invalid() -> None: def test_parse_args_backtesting_custom() -> None: args = [ - '-c', 'test_conf.json', 'backtesting', + '-c', 'test_conf.json', '--ticker-interval', '1m', '--strategy-list', 'DefaultStrategy', @@ -108,7 +108,7 @@ def test_parse_args_backtesting_custom() -> None: call_args = Arguments(args).get_parsed_arg() assert call_args["config"] == ['test_conf.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'backtesting' + assert call_args["command"] == 'backtesting' assert call_args["func"] is not None assert call_args["ticker_interval"] == '1m' assert type(call_args["strategy_list"]) is list @@ -117,8 +117,8 @@ def test_parse_args_backtesting_custom() -> None: def test_parse_args_hyperopt_custom() -> None: args = [ - '-c', 'test_conf.json', 'hyperopt', + '-c', 'test_conf.json', '--epochs', '20', '--spaces', 'buy' ] @@ -126,7 +126,7 @@ def test_parse_args_hyperopt_custom() -> None: assert call_args["config"] == ['test_conf.json'] assert call_args["epochs"] == 20 assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'hyperopt' + assert call_args["command"] == 'hyperopt' assert call_args["spaces"] == ['buy'] assert call_args["func"] is not None assert callable(call_args["func"]) @@ -134,8 +134,8 @@ def test_parse_args_hyperopt_custom() -> None: def test_download_data_options() -> None: args = [ - '--datadir', 'datadir/directory', 'download-data', + '--datadir', 'datadir/directory', '--pairs-file', 'file_with_pairs', '--days', '30', '--exchange', 'binance' @@ -150,8 +150,8 @@ def test_download_data_options() -> None: def test_plot_dataframe_options() -> None: args = [ - '-c', 'config.json.example', 'plot-dataframe', + '-c', 'config.json.example', '--indicators1', 'sma10', 'sma100', '--indicators2', 'macd', 'fastd', 'fastk', '--plot-limit', '30', @@ -186,7 +186,7 @@ def test_config_notallowed(mocker) -> None: ] pargs = Arguments(args).get_parsed_arg() - assert pargs["config"] is None + assert "config" not in pargs # When file exists: mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) @@ -195,7 +195,7 @@ def test_config_notallowed(mocker) -> None: ] pargs = Arguments(args).get_parsed_arg() # config is not added even if it exists, since create-userdir is in the notallowed list - assert pargs["config"] is None + assert "config" not in pargs def test_config_notrequired(mocker) -> None: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d27cd92f4..7959f28d1 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -68,7 +68,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None: def test__args_to_config(caplog): - arg_list = ['--strategy-path', 'TestTest'] + arg_list = ['trade', '--strategy-path', 'TestTest'] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) config = {} @@ -96,7 +96,7 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None: default_conf['max_open_trades'] = 0 patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -121,7 +121,7 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: configsmock ) - arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ] + arg_list = ['trade', '-c', 'test_conf.json', '--config', 'test2_conf.json', ] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -187,7 +187,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> default_conf['max_open_trades'] = -1 patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -211,11 +211,10 @@ def test_load_config_file_exception(mocker) -> None: def test_load_config(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('strategy') == 'DefaultStrategy' assert validated_conf.get('strategy_path') is None assert 'edge' not in validated_conf @@ -224,6 +223,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path', '--db-url', 'sqlite:///someurl', @@ -243,6 +243,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -259,6 +260,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -275,6 +277,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -293,6 +296,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -303,6 +307,23 @@ def test_load_config_with_params(default_conf, mocker) -> None: assert validated_conf.get('db_url') == DEFAULT_DB_DRYRUN_URL +@pytest.mark.parametrize("config_value,expected,arglist", [ + (True, True, ['trade', '--dry-run']), # Leave config untouched + (False, True, ['trade', '--dry-run']), # Override config untouched + (False, False, ['trade']), # Leave config untouched + (True, True, ['trade']), # Leave config untouched +]) +def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> None: + + default_conf['dry_run'] = config_value + patched_configuration_load_config_file(mocker, default_conf) + + configuration = Configuration(Arguments(arglist).get_parsed_arg()) + validated_conf = configuration.load_config() + + assert validated_conf.get('dry_run') is expected + + def test_load_custom_strategy(default_conf, mocker) -> None: default_conf.update({ 'strategy': 'CustomStrategy', @@ -310,7 +331,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None: }) patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -322,6 +343,7 @@ def test_show_info(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--db-url', 'sqlite:///tmp/testdb', ] @@ -338,9 +360,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] args = Arguments(arglist).get_parsed_arg() @@ -376,11 +398,11 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non lambda x, *args, **kwargs: Path(x) ) arglist = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', '--userdir', "/tmp/freqtrade", - 'backtesting', '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', @@ -427,8 +449,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--config', 'config.json', 'backtesting', + '--config', 'config.json', '--ticker-interval', '1m', '--export', '/bar/foo', '--strategy-list', @@ -568,7 +590,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: # Prevent setting loggers mocker.patch('freqtrade.loggers._set_loggers', MagicMock) - arglist = ['-vvv'] + arglist = ['trade', '-vvv'] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) @@ -620,7 +642,7 @@ def test_set_logfile(default_conf, mocker): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--logfile', 'test_file.log', + 'trade', '--logfile', 'test_file.log', ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) @@ -636,7 +658,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None: default_conf['forcebuy_enable'] = True patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -847,8 +869,8 @@ def test_pairlist_resolving(): def test_pairlist_resolving_with_config(mocker, default_conf): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', ] args = Arguments(arglist).get_parsed_arg() @@ -861,8 +883,8 @@ def test_pairlist_resolving_with_config(mocker, default_conf): # Override pairs arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs', 'ETH/BTC', 'XRP/BTC', ] @@ -883,8 +905,8 @@ def test_pairlist_resolving_with_config_pl(mocker, default_conf): mocker.patch.object(Path, "open", MagicMock(return_value=MagicMock())) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs-file', 'pairs.json', ] @@ -905,8 +927,8 @@ def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs-file', 'pairs.json', ] diff --git a/tests/test_main.py b/tests/test_main.py index d73edc0da..4e97c375d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -11,10 +11,16 @@ from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State from freqtrade.worker import Worker -from tests.conftest import (log_has, patch_exchange, +from tests.conftest import (log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) +def test_parse_args_None(caplog) -> None: + with pytest.raises(SystemExit): + main([]) + assert log_has_re(r"Usage of Freqtrade requires a subcommand.*", caplog) + + def test_parse_args_backtesting(mocker) -> None: """ Test that main() can start backtesting and also ensure we can pass some specific arguments @@ -29,7 +35,7 @@ def test_parse_args_backtesting(mocker) -> None: call_args = backtesting_mock.call_args[0][0] assert call_args["config"] == ['config.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'backtesting' + assert call_args["command"] == 'backtesting' assert call_args["func"] is not None assert callable(call_args["func"]) assert call_args["ticker_interval"] is None @@ -45,7 +51,7 @@ def test_main_start_hyperopt(mocker) -> None: call_args = hyperopt_mock.call_args[0][0] assert call_args["config"] == ['config.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'hyperopt' + assert call_args["command"] == 'hyperopt' assert call_args["func"] is not None assert callable(call_args["func"]) @@ -58,7 +64,7 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -75,7 +81,7 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -95,7 +101,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -114,15 +120,15 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: OperationalException("Oh snap!")]) mocker.patch('freqtrade.worker.Worker._worker', worker_mock) patched_configuration_load_config_file(mocker, default_conf) - reconfigure_mock = mocker.patch('freqtrade.main.Worker._reconfigure', MagicMock()) + reconfigure_mock = mocker.patch('freqtrade.worker.Worker._reconfigure', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = Arguments(['-c', 'config.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config.json.example']).get_parsed_arg() worker = Worker(args=args, config=default_conf) with pytest.raises(SystemExit): - main(['-c', 'config.json.example']) + main(['trade', '-c', 'config.json.example']) assert log_has('Using config: config.json.example ...', caplog) assert worker_mock.call_count == 4 @@ -141,7 +147,7 @@ def test_reconfigure(mocker, default_conf) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = Arguments(['-c', 'config.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config.json.example']).get_parsed_arg() worker = Worker(args=args, config=default_conf) freqtrade = worker.freqtrade diff --git a/tests/test_plotting.py b/tests/test_plotting.py index f0d9578ac..ec4df9125 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -281,8 +281,8 @@ def test_generate_profit_graph(testdatadir): def test_start_plot_dataframe(mocker): aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock()) args = [ - "--config", "config.json.example", "plot-dataframe", + "--config", "config.json.example", "--pairs", "ETH/BTC" ] start_plot_dataframe(get_args(args)) @@ -323,8 +323,8 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir): def test_start_plot_profit(mocker): aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) args = [ - "--config", "config.json.example", "plot-profit", + "--config", "config.json.example", "--pairs", "ETH/BTC" ] start_plot_profit(get_args(args)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 474a842d0..3dba7df1b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -14,7 +14,7 @@ from tests.conftest import get_args, log_has, patch_exchange def test_setup_utils_configuration(): args = [ - '--config', 'config.json.example', + 'list-exchanges', '--config', 'config.json.example', ] config = setup_utils_configuration(get_args(args), RunMode.OTHER) @@ -95,8 +95,8 @@ def test_list_timeframes(mocker, capsys): # Test with --config config.json.example args = [ - '--config', 'config.json.example', "list-timeframes", + '--config', 'config.json.example', ] start_list_timeframes(get_args(args)) captured = capsys.readouterr() @@ -139,8 +139,8 @@ def test_list_timeframes(mocker, capsys): # Test with --one-column args = [ - '--config', 'config.json.example', "list-timeframes", + '--config', 'config.json.example', "--one-column", ] start_list_timeframes(get_args(args)) @@ -182,8 +182,8 @@ def test_list_markets(mocker, markets, capsys): # Test with --config config.json.example args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), False) @@ -208,8 +208,8 @@ def test_list_markets(mocker, markets, capsys): patch_exchange(mocker, api_mock=api_mock, id="bittrex") # Test with --all: all markets args = [ - '--config', 'config.json.example', "list-markets", "--all", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), False) @@ -221,8 +221,8 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand: active pairs args = [ - '--config', 'config.json.example', "list-pairs", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), True) @@ -233,8 +233,8 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand with --all: all pairs args = [ - '--config', 'config.json.example', "list-pairs", "--all", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), True) @@ -246,8 +246,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=ETH, LTC args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "ETH", "LTC", "--print-list", ] @@ -259,8 +259,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--print-list", ] @@ -272,8 +272,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT, USD args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--quote", "USDT", "USD", "--print-list", ] @@ -285,8 +285,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--quote", "USDT", "--print-list", ] @@ -298,8 +298,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USDT", "--print-list", ] @@ -311,8 +311,8 @@ def test_list_markets(mocker, markets, capsys): # active pairs, base=LTC, quote=USDT args = [ - '--config', 'config.json.example', "list-pairs", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USD", "--print-list", ] @@ -324,8 +324,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT, NONEXISTENT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USDT", "NONEXISTENT", "--print-list", ] @@ -337,8 +337,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=NONEXISTENT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "NONEXISTENT", "--print-list", ] @@ -350,8 +350,8 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', ] start_list_markets(get_args(args), False) captured = capsys.readouterr() @@ -360,8 +360,8 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output, no markets found args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "NONEXISTENT", ] start_list_markets(get_args(args), False) @@ -372,8 +372,8 @@ def test_list_markets(mocker, markets, capsys): # Test --print-json args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-json" ] start_list_markets(get_args(args), False) @@ -384,8 +384,8 @@ def test_list_markets(mocker, markets, capsys): # Test --print-csv args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-csv" ] start_list_markets(get_args(args), False) @@ -396,8 +396,8 @@ def test_list_markets(mocker, markets, capsys): # Test --one-column args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--one-column" ] start_list_markets(get_args(args), False) diff --git a/user_data/hyperopts/sample_hyperopt.py b/user_data/hyperopts/sample_hyperopt.py index 2721ab405..3be05f121 100644 --- a/user_data/hyperopts/sample_hyperopt.py +++ b/user_data/hyperopts/sample_hyperopt.py @@ -12,7 +12,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.optimize.hyperopt_interface import IHyperOpt -class SampleHyperOpts(IHyperOpt): +class SampleHyperOpt(IHyperOpt): """ This is a sample Hyperopt to inspire you. Feel free to customize it. diff --git a/user_data/hyperopts/sample_hyperopt_advanced.py b/user_data/hyperopts/sample_hyperopt_advanced.py index c5d28878c..66182edcf 100644 --- a/user_data/hyperopts/sample_hyperopt_advanced.py +++ b/user_data/hyperopts/sample_hyperopt_advanced.py @@ -14,7 +14,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.optimize.hyperopt_interface import IHyperOpt -class AdvancedSampleHyperOpts(IHyperOpt): +class AdvancedSampleHyperOpt(IHyperOpt): """ This is a sample hyperopt to inspire you. Feel free to customize it.