Merge pull request #2397 from freqtrade/feat/new_args_system

require subcommand for all actions
This commit is contained in:
Matthias 2019-11-14 06:28:42 +01:00 committed by GitHub
commit 9b050523e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 476 additions and 239 deletions

View File

@ -28,11 +28,11 @@ jobs:
name: pytest name: pytest
- script: - script:
- cp config.json.example config.json - cp config.json.example config.json
- freqtrade --datadir tests/testdata backtesting - freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy
name: backtest name: backtest
- script: - script:
- cp config.json.example config.json - 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 name: hyperopt
- script: flake8 - script: flake8
name: flake8 name: flake8

View File

@ -24,3 +24,5 @@ RUN pip install numpy --no-cache-dir \
COPY . /freqtrade/ COPY . /freqtrade/
RUN pip install -e . --no-cache-dir RUN pip install -e . --no-cache-dir
ENTRYPOINT ["freqtrade"] ENTRYPOINT ["freqtrade"]
# Default to trade mode
CMD [ "trade" ]

View File

@ -23,7 +23,7 @@ if [ $? -ne 0 ]; then
fi fi
# Run backtest # 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 if [ $? -ne 0 ]; then
echo "failed running backtest" echo "failed running backtest"

View File

@ -45,7 +45,7 @@ freqtrade --datadir user_data/data/bittrex-20180101 backtesting
#### With a (custom) strategy file #### With a (custom) strategy file
```bash ```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. Where `-s SampleStrategy` refers to the class name within the strategy file `sample_strategy.py` found in the `freqtrade/user_data/strategies` directory.

View File

@ -5,20 +5,18 @@ This page explains the different parameters of the bot and how to run it.
!!! Note !!! Note
If you've used `setup.sh`, don't forget to activate your virtual environment (`source .env/bin/activate`) before running freqtrade commands. If you've used `setup.sh`, don't forget to activate your virtual environment (`source .env/bin/activate`) before running freqtrade commands.
## Bot commands ## Bot commands
``` ```
usage: freqtrade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] usage: freqtrade [-h] [-V]
[--userdir PATH] [-s NAME] [--strategy-path PATH] {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit}
[--db-url PATH] [--sd-notify]
{backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit}
... ...
Free, open source crypto trading bot Free, open source crypto trading bot
positional arguments: 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. backtesting Backtesting module.
edge Edge module. edge Edge module.
hyperopt Hyperopt module. hyperopt Hyperopt module.
@ -32,6 +30,27 @@ positional arguments:
optional arguments: optional arguments:
-h, --help show this help message and exit -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). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. --logfile FILE Log to the file specified.
-V, --version show program's version number and exit -V, --version show program's version number and exit
@ -43,15 +62,12 @@ optional arguments:
Path to directory with historical backtesting data. Path to directory with historical backtesting data.
--userdir PATH, --user-data-dir PATH --userdir PATH, --user-data-dir PATH
Path to userdata directory. 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? ### 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: the `-c/--config` command line option:
```bash ```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 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): require them):
```bash ```bash
freqtrade -c ./config.json freqtrade trade -c ./config.json
``` ```
and specify both configuration files when running in the normal Live Trade Mode: and specify both configuration files when running in the normal Live Trade Mode:
```bash ```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 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: a strategy class called `AwesomeStrategy` to load it:
```bash ```bash
freqtrade --strategy AwesomeStrategy freqtrade trade --strategy AwesomeStrategy
``` ```
If the bot does not find your strategy file, it will display in an error 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!): checked before the default locations (The passed path must be a directory!):
```bash ```bash
freqtrade --strategy AwesomeStrategy --strategy-path /some/directory freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory
``` ```
#### How to install a strategy? #### 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: in production mode. Example command:
```bash ```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 ## 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`. Backtesting also uses the config specified via `-c/--config`.
``` ```
usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--max_open_trades INT] [-d PATH] [--userdir PATH] [-s NAME]
[--strategy-path PATH] [-i TICKER_INTERVAL]
[--timerange TIMERANGE] [--max_open_trades INT]
[--stake_amount STAKE_AMOUNT] [--fee FLOAT] [--stake_amount STAKE_AMOUNT] [--fee FLOAT]
[--eps] [--dmmp] [--eps] [--dmmp]
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
@ -211,11 +229,29 @@ optional arguments:
--export EXPORT Export backtest results, argument are: trades. --export EXPORT Export backtest results, argument are: trades.
Example: `--export=trades` Example: `--export=trades`
--export-filename PATH --export-filename PATH
Save backtest results to the file with this filename Save backtest results to the file with this filename.
(default: `user_data/backtest_results/backtest- Requires `--export` to be set as well. Example:
result.json`). Requires `--export` to be set as well. `--export-filename=user_data/backtest_results/backtest
Example: `--export-filename=user_data/backtest_results _today.json`
/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. The first time your run Backtesting, you will need to download some historic data first.
This can be accomplished by using `freqtrade download-data`. 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 ## Hyperopt commands
@ -231,12 +267,14 @@ To optimize your strategy, you can use hyperopt parameter hyperoptimization
to find optimal parameter values for your stategy. 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] [--max_open_trades INT]
[--stake_amount STAKE_AMOUNT] [--fee FLOAT] [--stake_amount STAKE_AMOUNT] [--fee FLOAT]
[--customhyperopt NAME] [--hyperopt-path PATH] [--hyperopt NAME] [--hyperopt-path PATH] [--eps]
[--eps] [-e INT] [-e INT]
[-s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] [--spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]]
[--dmmp] [--print-all] [--no-color] [--print-json] [--dmmp] [--print-all] [--no-color] [--print-json]
[-j JOBS] [--random-state INT] [--min-trades INT] [-j JOBS] [--random-state INT] [--min-trades INT]
[--continue] [--hyperopt-loss NAME] [--continue] [--hyperopt-loss NAME]
@ -254,16 +292,15 @@ optional arguments:
Specify stake_amount. Specify stake_amount.
--fee FLOAT Specify fee ratio. Will be applied twice (on trade --fee FLOAT Specify fee ratio. Will be applied twice (on trade
entry and exit). entry and exit).
--customhyperopt NAME --hyperopt NAME Specify hyperopt class name which will be used by the
Specify hyperopt class name (default: bot.
`DefaultHyperOpt`). --hyperopt-path PATH Specify additional lookup path for Hyperopt and
--hyperopt-path PATH Specify additional lookup path for Hyperopts and
Hyperopt Loss functions. Hyperopt Loss functions.
--eps, --enable-position-stacking --eps, --enable-position-stacking
Allow buying the same pair multiple times (position Allow buying the same pair multiple times (position
stacking). stacking).
-e INT, --epochs INT Specify number of epochs (default: 100). -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 Specify which parameters to hyperopt. Space-separated
list. Default: `all`. list. Default: `all`.
--dmmp, --disable-max-market-positions --dmmp, --disable-max-market-positions
@ -292,8 +329,27 @@ optional arguments:
generate completely different results, since the generate completely different results, since the
target for optimization is different. Built-in target for optimization is different. Built-in
Hyperopt-loss-functions are: DefaultHyperOptLoss, Hyperopt-loss-functions are: DefaultHyperOptLoss,
OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default: OnlyProfitHyperOptLoss, SharpeHyperOptLoss (default:
`DefaultHyperOptLoss`). `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 ## Edge commands
@ -301,7 +357,9 @@ optional arguments:
To know your trade expectancy and winrate against historical data, you can use Edge. 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] [--max_open_trades INT] [--stake_amount STAKE_AMOUNT]
[--fee FLOAT] [--stoplosses STOPLOSS_RANGE] [--fee FLOAT] [--stoplosses STOPLOSS_RANGE]
@ -324,6 +382,24 @@ optional arguments:
(without any space). Example: (without any space). Example:
`--stoplosses=-0.01,-0.1,-0.001` `--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). To understand edge and how to read the results, please read the [edge documentation](edge.md).

View File

@ -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`. | `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. | `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. | `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). | `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.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. | `internals.heartbeat_interval` | 60 | Print heartbeat message every X seconds. Set to 0 to disable heartbeat messages.

View File

@ -160,7 +160,7 @@ docker run -d \
-v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/config.json:/freqtrade/config.json \
-v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -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 !!! Note
@ -202,7 +202,7 @@ docker run -d \
-v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/config.json:/freqtrade/config.json \
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
-v ~/.freqtrade/user_data/:/freqtrade/user_data/ \ -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. Head over to the [Backtesting Documentation](backtesting.md) for more details.

View File

@ -235,7 +235,7 @@ An example of its output:
### Update cached pairs with the latest data ### Update cached pairs with the latest data
Edge requires historic data the same way as backtesting does. 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 ### Precising stoploss range

View File

@ -4,7 +4,7 @@
### The bot does not start ### 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: This could have the following reasons:

View File

@ -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. We strongly recommend to use `screen` or `tmux` to prevent any connection loss.
```bash ```bash
freqtrade -c config.json hyperopt --customhyperopt <hyperoptname> -e 5000 --spaces all freqtrade hyperopt --config config.json --hyperopt <hyperoptname> -e 5000 --spaces all
``` ```
Use `<hyperoptname>` as the name of the custom hyperopt used. Use `<hyperoptname>` 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. 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 ```bash
freqtrade --strategy SampleStrategy hyperopt --customhyperopt SampleHyperopt freqtrade hyperopt --strategy SampleStrategy --customhyperopt SampleHyperopt
``` ```
### Running Hyperopt with Smaller Search Space ### Running Hyperopt with Smaller Search Space

View File

@ -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. 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 ```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. *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.

View File

@ -23,13 +23,15 @@ The `freqtrade plot-dataframe` subcommand shows an interactive graph with three
Possible arguments: 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 ...]] [--indicators1 INDICATORS1 [INDICATORS1 ...]]
[--indicators2 INDICATORS2 [INDICATORS2 ...]] [--indicators2 INDICATORS2 [INDICATORS2 ...]]
[--plot-limit INT] [--db-url PATH] [--plot-limit INT] [--db-url PATH]
[--trade-source {DB,file}] [--export EXPORT] [--trade-source {DB,file}] [--export EXPORT]
[--export-filename PATH] [--export-filename PATH]
[--timerange TIMERANGE] [--timerange TIMERANGE] [-i TICKER_INTERVAL]
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -62,6 +64,28 @@ optional arguments:
/backtest_today.json` /backtest_today.json`
--timerange TIMERANGE --timerange TIMERANGE
Specify what timerange of data to use. 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. You will almost certainly want to specify a custom strategy! This can be done by adding `-s Classname` / `--strategy ClassName` to the command.
``` bash ``` 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 ### 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: To plot multiple pairs, separate them with a space:
``` bash ``` 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) To plot a timerange (to zoom in)
``` bash ``` 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`: To plot trades stored in a database use `--db-url` in combination with `--trade-source DB`:
``` bash ``` 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 <filename>` To plot trades from a backtesting result, use `--export-filename <filename>`
``` bash ``` 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 ## 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: 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] [--timerange TIMERANGE] [--export EXPORT]
[--export-filename PATH] [--db-url PATH] [--export-filename PATH] [--db-url PATH]
[--trade-source {DB,file}] [--trade-source {DB,file}] [-i TICKER_INTERVAL]
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -159,6 +184,22 @@ optional arguments:
--trade-source {DB,file} --trade-source {DB,file}
Specify the source for trades (Can be DB or file Specify the source for trades (Can be DB or file
(backtest file)) Default: 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.
``` ```

View File

@ -65,7 +65,7 @@ docker run -d \
-v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
-p 127.0.0.1:8080:8080 \ -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" !!! Danger "Security warning"

View File

@ -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) 2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
```bash ```bash
freqtrade --strategy AwesomeStrategy freqtrade trade --strategy AwesomeStrategy
``` ```
## Change your strategy ## 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. Future versions will require this to be set.
```bash ```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) **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` If you want to use a strategy from a different directory you can pass `--strategy-path`
```bash ```bash
freqtrade --strategy AwesomeStrategy --strategy-path /some/directory freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory
``` ```
### Common mistakes when developing strategies ### Common mistakes when developing strategies

View File

@ -6,7 +6,7 @@ After=network.target
# Set WorkingDirectory and ExecStart to your file paths accordingly # Set WorkingDirectory and ExecStart to your file paths accordingly
# NOTE: %h will be resolved to /home/<username> # NOTE: %h will be resolved to /home/<username>
WorkingDirectory=%h/freqtrade WorkingDirectory=%h/freqtrade
ExecStart=/usr/bin/freqtrade ExecStart=/usr/bin/freqtrade trade
Restart=on-failure Restart=on-failure
[Install] [Install]

View File

@ -6,7 +6,7 @@ After=network.target
# Set WorkingDirectory and ExecStart to your file paths accordingly # Set WorkingDirectory and ExecStart to your file paths accordingly
# NOTE: %h will be resolved to /home/<username> # NOTE: %h will be resolved to /home/<username>
WorkingDirectory=%h/freqtrade WorkingDirectory=%h/freqtrade
ExecStart=/usr/bin/freqtrade --sd-notify ExecStart=/usr/bin/freqtrade trade --sd-notify
Restart=always Restart=always
#Restart=on-failure #Restart=on-failure

View File

@ -13,7 +13,7 @@ ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_dat
ARGS_STRATEGY = ["strategy", "strategy_path"] 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", ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
"max_open_trades", "stake_amount", "fee"] "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", ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "download_trades", "exchange",
"timeframes", "erase"] "timeframes", "erase"]
ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
"trade_source", "export", "exportfilename", "timerange", "ticker_interval"] "db_url", "trade_source", "export", "exportfilename",
"timerange", "ticker_interval"]
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
"trade_source", "ticker_interval"] "trade_source", "ticker_interval"]
@ -61,11 +62,6 @@ class Arguments:
def __init__(self, args: Optional[List[str]]) -> None: def __init__(self, args: Optional[List[str]]) -> None:
self.args = args self.args = args
self._parsed_arg: Optional[argparse.Namespace] = None 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]: def get_parsed_arg(self) -> Dict[str, Any]:
""" """
@ -73,7 +69,7 @@ class Arguments:
:return: List[str] List of arguments :return: List[str] List of arguments
""" """
if self._parsed_arg is None: if self._parsed_arg is None:
self._load_args() self._build_subcommands()
self._parsed_arg = self._parse_args() self._parsed_arg = self._parse_args()
return vars(self._parsed_arg) return vars(self._parsed_arg)
@ -84,22 +80,17 @@ class Arguments:
""" """
parsed_arg = self.parser.parse_args(self.args) 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 # Workaround issue in argparse with action='append' and default value
# (see https://bugs.python.org/issue16399) # (see https://bugs.python.org/issue16399)
# Allow no-config for certain commands (like downloading / plotting) # Allow no-config for certain commands (like downloading / plotting)
if (parsed_arg.config is None if ('config' in parsed_arg and parsed_arg.config is None and
and subparser not in NO_CONF_ALLOWED ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or
and ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))):
or (subparser not in NO_CONF_REQURIED))):
parsed_arg.config = [constants.DEFAULT_CONFIG] parsed_arg.config = [constants.DEFAULT_CONFIG]
return parsed_arg return parsed_arg
def _build_args(self, optionlist, parser=None): def _build_args(self, optionlist, parser):
parser = parser or self.parser
for val in optionlist: for val in optionlist:
opt = AVAILABLE_CLI_OPTIONS[val] opt = AVAILABLE_CLI_OPTIONS[val]
@ -110,38 +101,68 @@ class Arguments:
Builds and attaches all subcommands. Builds and attaches all subcommands.
:return: None :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.optimize import start_backtesting, start_hyperopt, start_edge
from freqtrade.utils import (start_create_userdir, start_download_data, from freqtrade.utils import (start_create_userdir, start_download_data,
start_list_exchanges, start_list_timeframes, start_list_exchanges, start_list_markets,
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 # 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) backtesting_cmd.set_defaults(func=start_backtesting)
self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd)
# Add edge subcommand # 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) edge_cmd.set_defaults(func=start_edge)
self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd)
# Add hyperopt subcommand # 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) hyperopt_cmd.set_defaults(func=start_hyperopt)
self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd)
# add create-userdir subcommand # add create-userdir subcommand
create_userdir_cmd = subparsers.add_parser('create-userdir', 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) create_userdir_cmd.set_defaults(func=start_create_userdir)
self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd) self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd)
# Add list-exchanges subcommand # Add list-exchanges subcommand
list_exchanges_cmd = subparsers.add_parser( list_exchanges_cmd = subparsers.add_parser(
'list-exchanges', 'list-exchanges',
help='Print available exchanges.' help='Print available exchanges.',
parents=[_common_parser],
) )
list_exchanges_cmd.set_defaults(func=start_list_exchanges) list_exchanges_cmd.set_defaults(func=start_list_exchanges)
self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd) self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd)
@ -149,7 +170,8 @@ class Arguments:
# Add list-timeframes subcommand # Add list-timeframes subcommand
list_timeframes_cmd = subparsers.add_parser( list_timeframes_cmd = subparsers.add_parser(
'list-timeframes', '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) list_timeframes_cmd.set_defaults(func=start_list_timeframes)
self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd) self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd)
@ -157,7 +179,8 @@ class Arguments:
# Add list-markets subcommand # Add list-markets subcommand
list_markets_cmd = subparsers.add_parser( list_markets_cmd = subparsers.add_parser(
'list-markets', '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)) list_markets_cmd.set_defaults(func=partial(start_list_markets, pairs_only=False))
self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_markets_cmd) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_markets_cmd)
@ -165,7 +188,8 @@ class Arguments:
# Add list-pairs subcommand # Add list-pairs subcommand
list_pairs_cmd = subparsers.add_parser( list_pairs_cmd = subparsers.add_parser(
'list-pairs', '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)) list_pairs_cmd.set_defaults(func=partial(start_list_markets, pairs_only=True))
self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd)
@ -173,16 +197,17 @@ class Arguments:
# Add download-data subcommand # Add download-data subcommand
download_data_cmd = subparsers.add_parser( download_data_cmd = subparsers.add_parser(
'download-data', 'download-data',
help='Download backtesting data.' help='Download backtesting data.',
parents=[_common_parser],
) )
download_data_cmd.set_defaults(func=start_download_data) download_data_cmd.set_defaults(func=start_download_data)
self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd)
# Add Plotting subcommand # Add Plotting subcommand
from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
plot_dataframe_cmd = subparsers.add_parser( plot_dataframe_cmd = subparsers.add_parser(
'plot-dataframe', '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) plot_dataframe_cmd.set_defaults(func=start_plot_dataframe)
self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd)
@ -190,7 +215,8 @@ class Arguments:
# Plot profit # Plot profit
plot_profit_cmd = subparsers.add_parser( plot_profit_cmd = subparsers.add_parser(
'plot-profit', 'plot-profit',
help='Generate plot showing profits.' help='Generate plot showing profits.',
parents=[_common_parser],
) )
plot_profit_cmd.set_defaults(func=start_plot_profit) plot_profit_cmd.set_defaults(func=start_plot_profit)
self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd)

View File

@ -65,9 +65,8 @@ AVAILABLE_CLI_OPTIONS = {
# Main options # Main options
"strategy": Arg( "strategy": Arg(
'-s', '--strategy', '-s', '--strategy',
help='Specify strategy class name (default: `%(default)s`).', help='Specify strategy class name which will be used by the bot.',
metavar='NAME', metavar='NAME',
default='DefaultStrategy',
), ),
"strategy_path": Arg( "strategy_path": Arg(
'--strategy-path', '--strategy-path',
@ -86,6 +85,11 @@ AVAILABLE_CLI_OPTIONS = {
help='Notify systemd service manager.', help='Notify systemd service manager.',
action='store_true', 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 # Optimize common
"ticker_interval": Arg( "ticker_interval": Arg(
'-i', '--ticker-interval', '-i', '--ticker-interval',
@ -136,7 +140,7 @@ AVAILABLE_CLI_OPTIONS = {
), ),
"exportfilename": Arg( "exportfilename": Arg(
'--export-filename', '--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. ' 'Requires `--export` to be set as well. '
'Example: `--export-filename=user_data/backtest_results/backtest_today.json`', 'Example: `--export-filename=user_data/backtest_results/backtest_today.json`',
metavar='PATH', metavar='PATH',
@ -156,14 +160,13 @@ AVAILABLE_CLI_OPTIONS = {
), ),
# Hyperopt # Hyperopt
"hyperopt": Arg( "hyperopt": Arg(
'--customhyperopt', '--hyperopt',
help='Specify hyperopt class name (default: `%(default)s`).', help='Specify hyperopt class name which will be used by the bot.',
metavar='NAME', metavar='NAME',
default=constants.DEFAULT_HYPEROPT,
), ),
"hyperopt_path": Arg( "hyperopt_path": Arg(
'--hyperopt-path', '--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', metavar='PATH',
), ),
"epochs": Arg( "epochs": Arg(
@ -174,7 +177,7 @@ AVAILABLE_CLI_OPTIONS = {
default=constants.HYPEROPT_EPOCH, default=constants.HYPEROPT_EPOCH,
), ),
"spaces": Arg( "spaces": Arg(
'-s', '--spaces', '--spaces',
help='Specify which parameters to hyperopt. Space-separated list. ' help='Specify which parameters to hyperopt. Space-separated list. '
'Default: `%(default)s`.', 'Default: `%(default)s`.',
choices=['all', 'buy', 'sell', 'roi', 'stoploss'], choices=['all', 'buy', 'sell', 'roi', 'stoploss'],

View File

@ -93,7 +93,7 @@ class Configuration:
:return: Configuration dictionary :return: Configuration dictionary
""" """
# Load all configs # 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 # Keep a copy of the original configuration file
config['original_config'] = deepcopy(config) config['original_config'] = deepcopy(config)
@ -153,7 +153,7 @@ class Configuration:
self._process_logging_options(config) self._process_logging_options(config)
# Set strategy if not specified in config and or if it's non default # 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")}) config.update({'strategy': self.args.get("strategy")})
self._args_to_config(config, argname='strategy_path', 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"]: if 'sd_notify' in self.args and self.args["sd_notify"]:
config['internals'].update({'sd_notify': True}) 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: def _process_datadir_options(self, config: Dict[str, Any]) -> None:
""" """
Extract information for sys.argv and load directory configurations Extract information for sys.argv and load directory configurations

View File

@ -9,8 +9,6 @@ PROCESS_THROTTLE_SECS = 5 # sec
DEFAULT_TICKER_INTERVAL = 5 # min DEFAULT_TICKER_INTERVAL = 5 # min
HYPEROPT_EPOCH = 100 # epochs HYPEROPT_EPOCH = 100 # epochs
RETRY_TIMEOUT = 30 # sec RETRY_TIMEOUT = 30 # sec
DEFAULT_STRATEGY = 'DefaultStrategy'
DEFAULT_HYPEROPT = 'DefaultHyperOpt'
DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss'
DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite'
DEFAULT_DB_DRYRUN_URL = 'sqlite://' DEFAULT_DB_DRYRUN_URL = 'sqlite://'

View File

@ -15,7 +15,6 @@ from typing import Any, List
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.configuration import Arguments from freqtrade.configuration import Arguments
from freqtrade.worker import Worker
logger = logging.getLogger('freqtrade') logger = logging.getLogger('freqtrade')
@ -33,16 +32,19 @@ def main(sysargv: List[str] = None) -> None:
arguments = Arguments(sysargv) arguments = Arguments(sysargv)
args = arguments.get_parsed_arg() args = arguments.get_parsed_arg()
# A subcommand has been issued. # Call subcommand.
# Means if Backtesting or Hyperopt have been called we exit the bot
if 'func' in args: if 'func' in args:
args['func'](args) return_code = args['func'](args)
# TODO: fetch return_code as returned by the command function here
return_code = 0
else: else:
# Load and run worker # No subcommand was issued.
worker = Worker(args) raise OperationalException(
worker.run() "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 <command> --help`."
)
except SystemExit as e: except SystemExit as e:
return_code = e return_code = e

View File

@ -78,7 +78,7 @@ def start_hyperopt(args: Dict[str, Any]) -> None:
except Timeout: except Timeout:
logger.info("Another running instance of freqtrade Hyperopt detected.") logger.info("Another running instance of freqtrade Hyperopt detected.")
logger.info("Simultaneous execution of multiple Hyperopt commands is not supported. " 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.") "or on separate machines.")
logger.info("Quitting now.") logger.info("Quitting now.")
# TODO: return False here in order to help freqtrade to exit # TODO: return False here in order to help freqtrade to exit

View File

@ -1,6 +1,6 @@
""" """
IHyperOpt interface IHyperOpt interface
This module defines the interface to apply for hyperopts This module defines the interface to apply for hyperopt
""" """
import logging import logging
import math import math
@ -27,8 +27,8 @@ def _format_exception_message(method: str, space: str) -> str:
class IHyperOpt(ABC): class IHyperOpt(ABC):
""" """
Interface for freqtrade hyperopts Interface for freqtrade hyperopt
Defines the mandatory structure must follow any custom hyperopts Defines the mandatory structure must follow any custom hyperopt
Class attributes you can use: Class attributes you can use:
ticker_interval -> int: value of the ticker interval to use for the strategy ticker_interval -> int: value of the ticker interval to use for the strategy

View File

@ -1,6 +1,6 @@
""" """
IHyperOptLoss interface 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 from abc import ABC, abstractmethod
@ -11,7 +11,7 @@ from pandas import DataFrame
class IHyperOptLoss(ABC): 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.) Defines the custom loss function (`hyperopt_loss_function()` which is evaluated every epoch.)
""" """
ticker_interval: str ticker_interval: str

View File

@ -1,14 +1,14 @@
# pragma pylint: disable=attribute-defined-outside-init # pragma pylint: disable=attribute-defined-outside-init
""" """
This module load custom hyperopts This module load custom hyperopt
""" """
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Dict from typing import Optional, Dict
from freqtrade import OperationalException 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_interface import IHyperOpt
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
@ -20,7 +20,6 @@ class HyperOptResolver(IResolver):
""" """
This class contains all the logic to load custom hyperopt class This class contains all the logic to load custom hyperopt class
""" """
__slots__ = ['hyperopt'] __slots__ = ['hyperopt']
def __init__(self, config: Dict) -> None: def __init__(self, config: Dict) -> None:
@ -28,9 +27,12 @@ class HyperOptResolver(IResolver):
Load the custom class from config parameter Load the custom class from config parameter
:param config: configuration dictionary :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, self.hyperopt = self._load_hyperopt(hyperopt_name, config,
extra_dir=config.get('hyperopt_path')) 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 This class contains all the logic to load custom hyperopt loss class
""" """
__slots__ = ['hyperoptloss'] __slots__ = ['hyperoptloss']
def __init__(self, config: Dict = None) -> None: def __init__(self, config: Dict) -> None:
""" """
Load the custom class from config parameter 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 # Verify the hyperopt_loss is in the configuration, otherwise fallback to the
hyperopt_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS # default hyperopt loss
hyperoptloss_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS
self.hyperoptloss = self._load_hyperoptloss( 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 # Assign ticker_interval to be used in hyperopt
self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval']) self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval'])
if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'): if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'):
raise OperationalException( 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( def _load_hyperoptloss(
self, hyper_loss_name: str, config: Dict, self, hyper_loss_name: str, config: Dict,

View File

@ -32,8 +32,11 @@ class StrategyResolver(IResolver):
""" """
config = config or {} config = config or {}
# Verify the strategy is in the configuration, otherwise fallback to the default strategy if not config.get('strategy'):
strategy_name = config.get('strategy') or constants.DEFAULT_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, self.strategy: IStrategy = self._load_strategy(strategy_name,
config=config, config=config,
extra_dir=config.get('strategy_path')) extra_dir=config.get('strategy_path'))

View File

@ -39,6 +39,17 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str
return config 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: def start_list_exchanges(args: Dict[str, Any]) -> None:
""" """
Print available exchanges 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: 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() :param args: Cli args from Arguments()
:return: None :return: None
""" """

View File

@ -252,6 +252,7 @@ def default_conf(testdatadir):
"db_url": "sqlite://", "db_url": "sqlite://",
"user_data_dir": Path("user_data"), "user_data_dir": Path("user_data"),
"verbosity": 3, "verbosity": 3,
"strategy": "DefaultStrategy"
} }
return configuration return configuration

View File

@ -184,9 +184,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'backtesting'
] ]
config = setup_configuration(get_args(args), RunMode.BACKTEST) config = setup_configuration(get_args(args), RunMode.BACKTEST)
@ -217,10 +217,10 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
) )
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'backtesting',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--enable-position-stacking', '--enable-position-stacking',
'--disable-max-market-positions', '--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) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'backtesting'
] ]
with pytest.raises(DependencyException, match=r'.*stake amount.*'): 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) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'backtesting'
] ]
args = get_args(args) args = get_args(args)
start_backtesting(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) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'--datadir', str(testdatadir), '--datadir', str(testdatadir),
'backtesting',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--timerange', '1510694220-1510700340', '--timerange', '1510694220-1510700340',
'--enable-position-stacking', '--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) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--datadir', str(testdatadir), '--datadir', str(testdatadir),
'backtesting',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--timerange', '1510694220-1510700340', '--timerange', '1510694220-1510700340',
'--enable-position-stacking', '--enable-position-stacking',

View File

@ -15,9 +15,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'edge'
] ]
config = setup_configuration(get_args(args), RunMode.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 = [ args = [
'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'edge',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--timerange', ':100', '--timerange', ':100',
'--stoplosses=-0.01,-0.10,-0.001' '--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) patched_configuration_load_config_file(mocker, edge_conf)
args = [ args = [
'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'edge'
] ]
args = get_args(args) args = get_args(args)
start_edge(args) start_edge(args)

View File

@ -26,7 +26,10 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def hyperopt(default_conf, mocker): def hyperopt(default_conf, mocker):
default_conf.update({'spaces': ['all']}) default_conf.update({
'spaces': ['all'],
'hyperopt': 'DefaultHyperOpt',
})
patch_exchange(mocker) patch_exchange(mocker)
return Hyperopt(default_conf) 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) patched_configuration_load_config_file(mocker, default_conf)
args = [ args = [
'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'hyperopt' '--hyperopt', 'DefaultHyperOpt',
] ]
config = setup_configuration(get_args(args), RunMode.HYPEROPT) config = setup_configuration(get_args(args), RunMode.HYPEROPT)
@ -100,9 +104,10 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
) )
args = [ args = [
'--config', 'config.json',
'--datadir', '/foo/bar',
'hyperopt', 'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--datadir', '/foo/bar',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--timerange', ':100', '--timerange', ':100',
'--enable-position-stacking', '--enable-position-stacking',
@ -157,7 +162,8 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt',
MagicMock(return_value=hyperopt(default_conf)) 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_indicators')
assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_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"}) default_conf.update({'hyperopt': "NonExistingHyperoptClass"})
with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'): 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: 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', 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss',
MagicMock(return_value=hl) MagicMock(return_value=hl)
) )
x = HyperOptLossResolver(default_conf, ).hyperoptloss x = HyperOptLossResolver(default_conf).hyperoptloss
assert hasattr(x, "hyperopt_loss_function") 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"}) default_conf.update({'hyperopt_loss': "NonExistingLossClass"})
with pytest.raises(OperationalException, match=r'Impossible to load HyperoptLoss.*'): 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: 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) patch_exchange(mocker)
args = [ args = [
'--config', 'config.json',
'hyperopt', 'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
@ -220,8 +235,9 @@ def test_start(mocker, default_conf, caplog) -> None:
patch_exchange(mocker) patch_exchange(mocker)
args = [ args = [
'--config', 'config.json',
'hyperopt', 'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
@ -242,8 +258,9 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
patch_exchange(mocker) patch_exchange(mocker)
args = [ args = [
'--config', 'config.json',
'hyperopt', 'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
@ -258,8 +275,9 @@ def test_start_filelock(mocker, default_conf, caplog) -> None:
patch_exchange(mocker) patch_exchange(mocker)
args = [ args = [
'--config', 'config.json',
'hyperopt', 'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
@ -412,6 +430,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
@ -515,10 +534,12 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
def test_generate_optimizer(mocker, default_conf) -> None: def test_generate_optimizer(mocker, default_conf) -> None:
default_conf.update({'config': 'config.json.example'}) default_conf.update({'config': 'config.json.example',
default_conf.update({'timerange': None}) 'hyperopt': 'DefaultHyperOpt',
default_conf.update({'spaces': 'all'}) 'timerange': None,
default_conf.update({'hyperopt_min_trades': 1}) 'spaces': 'all',
'hyperopt_min_trades': 1,
})
trades = [ trades = [
('TRX/BTC', 0.023117, 0.000233, 100) ('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): def test_clean_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
@ -598,6 +620,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog):
def test_continue_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
@ -628,6 +651,7 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
@ -666,6 +690,7 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'roi stoploss', 'spaces': 'roi stoploss',
@ -705,6 +730,7 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'roi stoploss', 'spaces': 'roi stoploss',
@ -747,6 +773,7 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) -
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
@ -781,6 +808,7 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'buy', 'spaces': 'buy',
@ -827,6 +855,7 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'sell', 'spaces': 'sell',
@ -875,6 +904,7 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'config': 'config.json.example', default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': space, 'spaces': space,

View File

@ -54,21 +54,30 @@ def test_load_strategy_base64(result, caplog, default_conf):
def test_load_strategy_invalid_directory(result, caplog, default_conf): def test_load_strategy_invalid_directory(result, caplog, default_conf):
default_conf['strategy'] = 'SampleStrategy'
resolver = StrategyResolver(default_conf) resolver = StrategyResolver(default_conf)
extra_dir = Path.cwd() / 'some/path' extra_dir = Path.cwd() / 'some/path'
resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir) 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 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): def test_load_not_found_strategy(default_conf):
strategy = StrategyResolver(default_conf) default_conf['strategy'] = 'NotFoundStrategy'
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match=r"Impossible to load Strategy 'NotFoundStrategy'. " match=r"Impossible to load Strategy 'NotFoundStrategy'. "
r"This class does not exist or contains Python code errors."): 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): def test_strategy(result, default_conf):

View File

@ -11,7 +11,7 @@ from freqtrade.configuration.cli_options import check_int_positive
# Parse common command-line-arguments. Used for all tools # Parse common command-line-arguments. Used for all tools
def test_parse_args_none() -> None: def test_parse_args_none() -> None:
arguments = Arguments([]) arguments = Arguments(['trade'])
assert isinstance(arguments, Arguments) assert isinstance(arguments, Arguments)
x = arguments.get_parsed_arg() x = arguments.get_parsed_arg()
assert isinstance(x, dict) assert isinstance(x, dict)
@ -19,7 +19,7 @@ def test_parse_args_none() -> None:
def test_parse_args_defaults() -> 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["config"] == ['config.json']
assert args["strategy_path"] is None assert args["strategy_path"] is None
assert args["datadir"] is None assert args["datadir"] is None
@ -27,27 +27,27 @@ def test_parse_args_defaults() -> None:
def test_parse_args_config() -> 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'] 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'] assert args["config"] == ['/dev/null']
args = Arguments(['--config', '/dev/null', args = Arguments(['trade', '--config', '/dev/null',
'--config', '/dev/zero'],).get_parsed_arg() '--config', '/dev/zero'],).get_parsed_arg()
assert args["config"] == ['/dev/null', '/dev/zero'] assert args["config"] == ['/dev/null', '/dev/zero']
def test_parse_args_db_url() -> None: 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' assert args["db_url"] == 'sqlite:///test.sqlite'
def test_parse_args_verbose() -> None: def test_parse_args_verbose() -> None:
args = Arguments(['-v']).get_parsed_arg() args = Arguments(['trade', '-v']).get_parsed_arg()
assert args["verbosity"] == 1 assert args["verbosity"] == 1
args = Arguments(['--verbose']).get_parsed_arg() args = Arguments(['trade', '--verbose']).get_parsed_arg()
assert args["verbosity"] == 1 assert args["verbosity"] == 1
@ -69,7 +69,7 @@ def test_parse_args_invalid() -> None:
def test_parse_args_strategy() -> 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' assert args["strategy"] == 'SomeStrategy'
@ -79,7 +79,7 @@ def test_parse_args_strategy_invalid() -> None:
def test_parse_args_strategy_path() -> 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' assert args["strategy_path"] == '/some/path'
@ -98,8 +98,8 @@ def test_parse_args_backtesting_invalid() -> None:
def test_parse_args_backtesting_custom() -> None: def test_parse_args_backtesting_custom() -> None:
args = [ args = [
'-c', 'test_conf.json',
'backtesting', 'backtesting',
'-c', 'test_conf.json',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--strategy-list', '--strategy-list',
'DefaultStrategy', 'DefaultStrategy',
@ -108,7 +108,7 @@ def test_parse_args_backtesting_custom() -> None:
call_args = Arguments(args).get_parsed_arg() call_args = Arguments(args).get_parsed_arg()
assert call_args["config"] == ['test_conf.json'] assert call_args["config"] == ['test_conf.json']
assert call_args["verbosity"] == 0 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["func"] is not None
assert call_args["ticker_interval"] == '1m' assert call_args["ticker_interval"] == '1m'
assert type(call_args["strategy_list"]) is list 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: def test_parse_args_hyperopt_custom() -> None:
args = [ args = [
'-c', 'test_conf.json',
'hyperopt', 'hyperopt',
'-c', 'test_conf.json',
'--epochs', '20', '--epochs', '20',
'--spaces', 'buy' '--spaces', 'buy'
] ]
@ -126,7 +126,7 @@ def test_parse_args_hyperopt_custom() -> None:
assert call_args["config"] == ['test_conf.json'] assert call_args["config"] == ['test_conf.json']
assert call_args["epochs"] == 20 assert call_args["epochs"] == 20
assert call_args["verbosity"] == 0 assert call_args["verbosity"] == 0
assert call_args["subparser"] == 'hyperopt' assert call_args["command"] == 'hyperopt'
assert call_args["spaces"] == ['buy'] assert call_args["spaces"] == ['buy']
assert call_args["func"] is not None assert call_args["func"] is not None
assert callable(call_args["func"]) assert callable(call_args["func"])
@ -134,8 +134,8 @@ def test_parse_args_hyperopt_custom() -> None:
def test_download_data_options() -> None: def test_download_data_options() -> None:
args = [ args = [
'--datadir', 'datadir/directory',
'download-data', 'download-data',
'--datadir', 'datadir/directory',
'--pairs-file', 'file_with_pairs', '--pairs-file', 'file_with_pairs',
'--days', '30', '--days', '30',
'--exchange', 'binance' '--exchange', 'binance'
@ -150,8 +150,8 @@ def test_download_data_options() -> None:
def test_plot_dataframe_options() -> None: def test_plot_dataframe_options() -> None:
args = [ args = [
'-c', 'config.json.example',
'plot-dataframe', 'plot-dataframe',
'-c', 'config.json.example',
'--indicators1', 'sma10', 'sma100', '--indicators1', 'sma10', 'sma100',
'--indicators2', 'macd', 'fastd', 'fastk', '--indicators2', 'macd', 'fastd', 'fastk',
'--plot-limit', '30', '--plot-limit', '30',
@ -186,7 +186,7 @@ def test_config_notallowed(mocker) -> None:
] ]
pargs = Arguments(args).get_parsed_arg() pargs = Arguments(args).get_parsed_arg()
assert pargs["config"] is None assert "config" not in pargs
# When file exists: # When file exists:
mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) 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() pargs = Arguments(args).get_parsed_arg()
# config is not added even if it exists, since create-userdir is in the notallowed list # 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: def test_config_notrequired(mocker) -> None:

View File

@ -68,7 +68,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
def test__args_to_config(caplog): def test__args_to_config(caplog):
arg_list = ['--strategy-path', 'TestTest'] arg_list = ['trade', '--strategy-path', 'TestTest']
args = Arguments(arg_list).get_parsed_arg() args = Arguments(arg_list).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
config = {} config = {}
@ -96,7 +96,7 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
default_conf['max_open_trades'] = 0 default_conf['max_open_trades'] = 0
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([]).get_parsed_arg() args = Arguments(['trade']).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
@ -121,7 +121,7 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
configsmock 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() args = Arguments(arg_list).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() 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 default_conf['max_open_trades'] = -1
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([]).get_parsed_arg() args = Arguments(['trade']).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() 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: def test_load_config(default_conf, mocker) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([]).get_parsed_arg() args = Arguments(['trade']).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
assert validated_conf.get('strategy') == 'DefaultStrategy'
assert validated_conf.get('strategy_path') is None assert validated_conf.get('strategy_path') is None
assert 'edge' not in validated_conf 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) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--strategy-path', '/some/path', '--strategy-path', '/some/path',
'--db-url', 'sqlite:///someurl', '--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) patched_configuration_load_config_file(mocker, conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--strategy-path', '/some/path' '--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) patched_configuration_load_config_file(mocker, conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--strategy-path', '/some/path' '--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) patched_configuration_load_config_file(mocker, conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--strategy-path', '/some/path' '--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) patched_configuration_load_config_file(mocker, conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--strategy-path', '/some/path' '--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 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: def test_load_custom_strategy(default_conf, mocker) -> None:
default_conf.update({ default_conf.update({
'strategy': 'CustomStrategy', 'strategy': 'CustomStrategy',
@ -310,7 +331,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None:
}) })
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([]).get_parsed_arg() args = Arguments(['trade']).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() 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) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'trade',
'--strategy', 'TestStrategy', '--strategy', 'TestStrategy',
'--db-url', 'sqlite:///tmp/testdb', '--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) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'backtesting'
] ]
args = Arguments(arglist).get_parsed_arg() 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) lambda x, *args, **kwargs: Path(x)
) )
arglist = [ arglist = [
'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--userdir', "/tmp/freqtrade", '--userdir', "/tmp/freqtrade",
'backtesting',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--enable-position-stacking', '--enable-position-stacking',
'--disable-max-market-positions', '--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) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'--config', 'config.json',
'backtesting', 'backtesting',
'--config', 'config.json',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--export', '/bar/foo', '--export', '/bar/foo',
'--strategy-list', '--strategy-list',
@ -568,7 +590,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
# Prevent setting loggers # Prevent setting loggers
mocker.patch('freqtrade.loggers._set_loggers', MagicMock) mocker.patch('freqtrade.loggers._set_loggers', MagicMock)
arglist = ['-vvv'] arglist = ['trade', '-vvv']
args = Arguments(arglist).get_parsed_arg() args = Arguments(arglist).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
@ -620,7 +642,7 @@ def test_set_logfile(default_conf, mocker):
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'--logfile', 'test_file.log', 'trade', '--logfile', 'test_file.log',
] ]
args = Arguments(arglist).get_parsed_arg() args = Arguments(arglist).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
@ -636,7 +658,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None:
default_conf['forcebuy_enable'] = True default_conf['forcebuy_enable'] = True
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([]).get_parsed_arg() args = Arguments(['trade']).get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
@ -847,8 +869,8 @@ def test_pairlist_resolving():
def test_pairlist_resolving_with_config(mocker, default_conf): def test_pairlist_resolving_with_config(mocker, default_conf):
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
arglist = [ arglist = [
'--config', 'config.json',
'download-data', 'download-data',
'--config', 'config.json',
] ]
args = Arguments(arglist).get_parsed_arg() args = Arguments(arglist).get_parsed_arg()
@ -861,8 +883,8 @@ def test_pairlist_resolving_with_config(mocker, default_conf):
# Override pairs # Override pairs
arglist = [ arglist = [
'--config', 'config.json',
'download-data', 'download-data',
'--config', 'config.json',
'--pairs', 'ETH/BTC', 'XRP/BTC', '--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())) mocker.patch.object(Path, "open", MagicMock(return_value=MagicMock()))
arglist = [ arglist = [
'--config', 'config.json',
'download-data', 'download-data',
'--config', 'config.json',
'--pairs-file', 'pairs.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)) mocker.patch.object(Path, "exists", MagicMock(return_value=False))
arglist = [ arglist = [
'--config', 'config.json',
'download-data', 'download-data',
'--config', 'config.json',
'--pairs-file', 'pairs.json', '--pairs-file', 'pairs.json',
] ]

View File

@ -11,10 +11,16 @@ from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.main import main from freqtrade.main import main
from freqtrade.state import State from freqtrade.state import State
from freqtrade.worker import Worker 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) 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: def test_parse_args_backtesting(mocker) -> None:
""" """
Test that main() can start backtesting and also ensure we can pass some specific arguments 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] call_args = backtesting_mock.call_args[0][0]
assert call_args["config"] == ['config.json'] assert call_args["config"] == ['config.json']
assert call_args["verbosity"] == 0 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["func"] is not None
assert callable(call_args["func"]) assert callable(call_args["func"])
assert call_args["ticker_interval"] is None 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] call_args = hyperopt_mock.call_args[0][0]
assert call_args["config"] == ['config.json'] assert call_args["config"] == ['config.json']
assert call_args["verbosity"] == 0 assert call_args["verbosity"] == 0
assert call_args["subparser"] == 'hyperopt' assert call_args["command"] == 'hyperopt'
assert call_args["func"] is not None assert call_args["func"] is not None
assert callable(call_args["func"]) 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.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
args = ['-c', 'config.json.example'] args = ['trade', '-c', 'config.json.example']
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): 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.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
args = ['-c', 'config.json.example'] args = ['trade', '-c', 'config.json.example']
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): 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.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
args = ['-c', 'config.json.example'] args = ['trade', '-c', 'config.json.example']
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
@ -114,15 +120,15 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
OperationalException("Oh snap!")]) OperationalException("Oh snap!")])
mocker.patch('freqtrade.worker.Worker._worker', worker_mock) mocker.patch('freqtrade.worker.Worker._worker', worker_mock)
patched_configuration_load_config_file(mocker, default_conf) 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.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', 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) worker = Worker(args=args, config=default_conf)
with pytest.raises(SystemExit): 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 log_has('Using config: config.json.example ...', caplog)
assert worker_mock.call_count == 4 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.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', 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) worker = Worker(args=args, config=default_conf)
freqtrade = worker.freqtrade freqtrade = worker.freqtrade

View File

@ -281,8 +281,8 @@ def test_generate_profit_graph(testdatadir):
def test_start_plot_dataframe(mocker): def test_start_plot_dataframe(mocker):
aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock()) aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
args = [ args = [
"--config", "config.json.example",
"plot-dataframe", "plot-dataframe",
"--config", "config.json.example",
"--pairs", "ETH/BTC" "--pairs", "ETH/BTC"
] ]
start_plot_dataframe(get_args(args)) 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): def test_start_plot_profit(mocker):
aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock())
args = [ args = [
"--config", "config.json.example",
"plot-profit", "plot-profit",
"--config", "config.json.example",
"--pairs", "ETH/BTC" "--pairs", "ETH/BTC"
] ]
start_plot_profit(get_args(args)) start_plot_profit(get_args(args))

View File

@ -14,7 +14,7 @@ from tests.conftest import get_args, log_has, patch_exchange
def test_setup_utils_configuration(): def test_setup_utils_configuration():
args = [ args = [
'--config', 'config.json.example', 'list-exchanges', '--config', 'config.json.example',
] ]
config = setup_utils_configuration(get_args(args), RunMode.OTHER) 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 # Test with --config config.json.example
args = [ args = [
'--config', 'config.json.example',
"list-timeframes", "list-timeframes",
'--config', 'config.json.example',
] ]
start_list_timeframes(get_args(args)) start_list_timeframes(get_args(args))
captured = capsys.readouterr() captured = capsys.readouterr()
@ -139,8 +139,8 @@ def test_list_timeframes(mocker, capsys):
# Test with --one-column # Test with --one-column
args = [ args = [
'--config', 'config.json.example',
"list-timeframes", "list-timeframes",
'--config', 'config.json.example',
"--one-column", "--one-column",
] ]
start_list_timeframes(get_args(args)) start_list_timeframes(get_args(args))
@ -182,8 +182,8 @@ def test_list_markets(mocker, markets, capsys):
# Test with --config config.json.example # Test with --config config.json.example
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--print-list", "--print-list",
] ]
start_list_markets(get_args(args), False) 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") patch_exchange(mocker, api_mock=api_mock, id="bittrex")
# Test with --all: all markets # Test with --all: all markets
args = [ args = [
'--config', 'config.json.example',
"list-markets", "--all", "list-markets", "--all",
'--config', 'config.json.example',
"--print-list", "--print-list",
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)
@ -221,8 +221,8 @@ def test_list_markets(mocker, markets, capsys):
# Test list-pairs subcommand: active pairs # Test list-pairs subcommand: active pairs
args = [ args = [
'--config', 'config.json.example',
"list-pairs", "list-pairs",
'--config', 'config.json.example',
"--print-list", "--print-list",
] ]
start_list_markets(get_args(args), True) 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 # Test list-pairs subcommand with --all: all pairs
args = [ args = [
'--config', 'config.json.example',
"list-pairs", "--all", "list-pairs", "--all",
'--config', 'config.json.example',
"--print-list", "--print-list",
] ]
start_list_markets(get_args(args), True) start_list_markets(get_args(args), True)
@ -246,8 +246,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=ETH, LTC # active markets, base=ETH, LTC
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "ETH", "LTC", "--base", "ETH", "LTC",
"--print-list", "--print-list",
] ]
@ -259,8 +259,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC # active markets, base=LTC
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "LTC", "--base", "LTC",
"--print-list", "--print-list",
] ]
@ -272,8 +272,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, quote=USDT, USD # active markets, quote=USDT, USD
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--quote", "USDT", "USD", "--quote", "USDT", "USD",
"--print-list", "--print-list",
] ]
@ -285,8 +285,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, quote=USDT # active markets, quote=USDT
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--quote", "USDT", "--quote", "USDT",
"--print-list", "--print-list",
] ]
@ -298,8 +298,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=USDT # active markets, base=LTC, quote=USDT
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "LTC", "--quote", "USDT", "--base", "LTC", "--quote", "USDT",
"--print-list", "--print-list",
] ]
@ -311,8 +311,8 @@ def test_list_markets(mocker, markets, capsys):
# active pairs, base=LTC, quote=USDT # active pairs, base=LTC, quote=USDT
args = [ args = [
'--config', 'config.json.example',
"list-pairs", "list-pairs",
'--config', 'config.json.example',
"--base", "LTC", "--quote", "USD", "--base", "LTC", "--quote", "USD",
"--print-list", "--print-list",
] ]
@ -324,8 +324,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=USDT, NONEXISTENT # active markets, base=LTC, quote=USDT, NONEXISTENT
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "LTC", "--quote", "USDT", "NONEXISTENT", "--base", "LTC", "--quote", "USDT", "NONEXISTENT",
"--print-list", "--print-list",
] ]
@ -337,8 +337,8 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=NONEXISTENT # active markets, base=LTC, quote=NONEXISTENT
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "LTC", "--quote", "NONEXISTENT", "--base", "LTC", "--quote", "NONEXISTENT",
"--print-list", "--print-list",
] ]
@ -350,8 +350,8 @@ def test_list_markets(mocker, markets, capsys):
# Test tabular output # Test tabular output
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)
captured = capsys.readouterr() captured = capsys.readouterr()
@ -360,8 +360,8 @@ def test_list_markets(mocker, markets, capsys):
# Test tabular output, no markets found # Test tabular output, no markets found
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--base", "LTC", "--quote", "NONEXISTENT", "--base", "LTC", "--quote", "NONEXISTENT",
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)
@ -372,8 +372,8 @@ def test_list_markets(mocker, markets, capsys):
# Test --print-json # Test --print-json
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--print-json" "--print-json"
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)
@ -384,8 +384,8 @@ def test_list_markets(mocker, markets, capsys):
# Test --print-csv # Test --print-csv
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--print-csv" "--print-csv"
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)
@ -396,8 +396,8 @@ def test_list_markets(mocker, markets, capsys):
# Test --one-column # Test --one-column
args = [ args = [
'--config', 'config.json.example',
"list-markets", "list-markets",
'--config', 'config.json.example',
"--one-column" "--one-column"
] ]
start_list_markets(get_args(args), False) start_list_markets(get_args(args), False)

View File

@ -12,7 +12,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
class SampleHyperOpts(IHyperOpt): class SampleHyperOpt(IHyperOpt):
""" """
This is a sample Hyperopt to inspire you. This is a sample Hyperopt to inspire you.
Feel free to customize it. Feel free to customize it.

View File

@ -14,7 +14,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
class AdvancedSampleHyperOpts(IHyperOpt): class AdvancedSampleHyperOpt(IHyperOpt):
""" """
This is a sample hyperopt to inspire you. This is a sample hyperopt to inspire you.
Feel free to customize it. Feel free to customize it.