Merge pull request #2577 from xmatthias/configvalidation

"fix" config validation
This commit is contained in:
Matthias 2019-11-29 06:25:41 +01:00 committed by GitHub
commit e0e0bad7c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 155 additions and 134 deletions

View File

@ -89,9 +89,9 @@ The bot allows you to use multiple configuration files by specifying multiple
defined in the latter configuration files override parameters with the same name defined in the latter configuration files override parameters with the same name
defined in the previous configuration files specified in the command line earlier. defined in the previous configuration files specified in the command line earlier.
For example, you can make a separate configuration file with your key and secrete For example, you can make a separate configuration file with your key and secret
for the Exchange you use for trading, specify default configuration file with for the Exchange you use for trading, specify default configuration file with
empty key and secrete values while running in the Dry Mode (which does not actually empty key and secret values while running in the Dry Mode (which does not actually
require them): require them):
```bash ```bash
@ -104,7 +104,7 @@ and specify both configuration files when running in the normal Live Trade Mode:
freqtrade trade -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 secret on you local machine
by setting appropriate file permissions for the file which contains actual secrets and, additionally, by setting appropriate file permissions for the file which contains actual secrets and, additionally,
prevent unintended disclosure of sensitive private data when you publish examples prevent unintended disclosure of sensitive private data when you publish examples
of your configuration in the project issues or in the Internet. of your configuration in the project issues or in the Internet.

View File

@ -38,84 +38,92 @@ The prevelance for all Options is as follows:
Mandatory parameters are marked as **Required**, which means that they are required to be set in one of the possible ways. Mandatory parameters are marked as **Required**, which means that they are required to be set in one of the possible ways.
| Command | Default | Description | | Command | Description |
|----------|---------|-------------| |----------|-------------|
| `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) | `max_open_trades` | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades).<br> ***Datatype:*** *Positive integer or -1.*
| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. | `stake_currency` | **Required.** Crypto-currency used for trading. [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *String*
| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. | `stake_amount` | **Required.** Amount of crypto-currency your bot will use for each trade. Set it to `"unlimited"` to allow the bot to use all available balance. [More information below](#understand-stake_amount). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Positive float or `"unlimited"`.*
| `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals. | `amount_reserve_percent` | Reserve some amount in min pair stake amount. The bot will reserve `amount_reserve_percent` + stoploss value when calculating min pair stake amount in order to avoid possible trade refusals. <br>*Defaults to `0.05` (5%).* <br> ***Datatype:*** *Positive Float as ratio.*
| `ticker_interval` | [1m, 5m, 15m, 30m, 1h, 1d, ...] | The ticker interval to use (1min, 5 min, 15 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-the-strategy). | `ticker_interval` | The ticker interval to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *String*
| `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below. | `fiat_display_currency` | Fiat currency used to show your profits. [More information below](#what-values-can-be-used-for-fiat_display_currency). <br> ***Datatype:*** *String*
| `dry_run` | true | **Required.** Define if the bot must be in Dry-run or production mode. | `dry_run` | **Required.** Define if the bot must be in Dry Run or production mode. <br>*Defaults to `true`.* <br> ***Datatype:*** *Boolean*
| `dry_run_wallet` | 999.9 | Overrides the default amount of 999.9 stake currency units in the wallet used by the bot running in the Dry Run mode if you need it for any reason. | `dry_run_wallet` | Overrides the default amount of 999.9 stake currency units in the wallet used by the bot running in the Dry Run mode if you need it for any reason. <br> ***Datatype:*** *Float*
| `process_only_new_candles` | false | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-the-strategy). | `process_only_new_candles` | Enable processing of indicators only when new candles arrive. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
| `minimal_roi` | See below | Set the threshold in percent the bot will use to sell a trade. More information below. [Strategy Override](#parameters-in-the-strategy). | `minimal_roi` | **Required.** Set the threshold in percent the bot will use to sell a trade. [More information below](#understand-minimal_roi). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Dict*
| `stoploss` | -0.10 | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). | `stoploss` | **Required.** Value of the stoploss in percent used by the bot. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Float (as ratio)*
| `trailing_stop` | false | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). | `trailing_stop` | Enables trailing stoploss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Boolean*
| `trailing_stop_positive` | 0 | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). | `trailing_stop_positive` | Changes stoploss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Float*
| `trailing_stop_positive_offset` | 0 | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). | `trailing_stop_positive_offset` | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0` (no offset).* <br> ***Datatype:*** *Float*
| `trailing_only_offset_is_reached` | false | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). | `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
| `unfilledtimeout.buy` | 10 | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. | `unfilledtimeout.buy` | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. <br> ***Datatype:*** *Integer*
| `unfilledtimeout.sell` | 10 | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. | `unfilledtimeout.sell` | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. <br> ***Datatype:*** *Integer*
| `bid_strategy.ask_last_balance` | 0.0 | **Required.** Set the bidding price. More information [below](#understand-ask_last_balance). | `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#understand-ask_last_balance).
| `bid_strategy.use_order_book` | false | Allows buying of pair using the rates in Order Book Bids. | `bid_strategy.use_order_book` | Enable buying using the rates in Order Book Bids. <br> ***Datatype:*** *Boolean*
| `bid_strategy.order_book_top` | 0 | Bot will use the top N rate in Order Book Bids. I.e. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. | `bid_strategy.order_book_top` | Bot will use the top N rate in Order Book Bids. I.e. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. *Defaults to `1`.* <br> ***Datatype:*** *Positive Integer*
| `bid_strategy. check_depth_of_market.enabled` | false | Does not buy if the % difference of buy orders and sell orders is met in Order Book. | `bid_strategy. check_depth_of_market.enabled` | Do not buy if the difference of buy orders and sell orders is met in Order Book. <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. | `bid_strategy. check_depth_of_market.bids_to_ask_delta` | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. *Defaults to `0`.* <br> ***Datatype:*** *Float (as ratio)*
| `ask_strategy.use_order_book` | false | Allows selling of open traded pair using the rates in Order Book Asks. | `ask_strategy.use_order_book` | Enable selling of open trades using Order Book Asks. <br> ***Datatype:*** *Boolean*
| `ask_strategy.order_book_min` | 0 | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. | `ask_strategy.order_book_min` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> ***Datatype:*** *Positive Integer*
| `ask_strategy.order_book_max` | 0 | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. | `ask_strategy.order_book_max` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> ***Datatype:*** *Positive Integer*
| `ask_strategy.use_sell_signal` | true | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). | `ask_strategy.use_sell_signal` | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> ***Datatype:*** *Boolean*
| `ask_strategy.sell_profit_only` | false | Wait until the bot makes a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). | `ask_strategy.sell_profit_only` | Wait until the bot makes a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
| `ask_strategy.ignore_roi_if_buy_signal` | false | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). | `ask_strategy.ignore_roi_if_buy_signal` | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> ***Datatype:*** *Boolean*
| `order_types` | None | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy). | `order_types` | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).<br> ***Datatype:*** *Dict*
| `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). | `order_time_in_force` | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). <br> ***Datatype:*** *Dict*
| `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). <br> ***Datatype:*** *String*
| `exchange.sandbox` | false | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details. | `exchange.sandbox` | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.<br> ***Datatype:*** *Boolean*
| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** | `exchange.key` | API key to use for the exchange. Only required when you are in production mode. **Keep it in secret, do not disclose publicly.** <br> ***Datatype:*** *String*
| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** | `exchange.secret` | API secret to use for the exchange. Only required when you are in production mode. **Keep it in secret, do not disclose publicly.** <br> ***Datatype:*** *String*
| `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. ***Keep it in secrete, do not disclose publicly.*** | `exchange.password` | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. **Keep it in secret, do not disclose publicly.** <br> ***Datatype:*** *String*
| `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Not used by VolumePairList (see [below](#dynamic-pairlists)). | `exchange.pair_whitelist` | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Not used by VolumePairList (see [below](#dynamic-pairlists)). <br> ***Datatype:*** *List*
| `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting (see [below](#dynamic-pairlists)). | `exchange.pair_blacklist` | List of pairs the bot must absolutely avoid for trading and backtesting (see [below](#dynamic-pairlists)). <br> ***Datatype:*** *List*
| `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `exchange.ccxt_config` | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) <br> ***Datatype:*** *Dict*
| `exchange.ccxt_async_config` | None | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) <br> ***Datatype:*** *Dict*
| `exchange.markets_refresh_interval` | 60 | The interval in minutes in which markets are reloaded. | `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded. <br>*Defaults to `60` minutes.* <br> ***Datatype:*** *Positive Integer*
| `edge` | false | Please refer to [edge configuration document](edge.md) for detailed explanation. | `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation.
| `experimental.block_bad_exchanges` | true | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now. | `experimental.block_bad_exchanges` | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now. <br>*Defaults to `true`.* <br> ***Datatype:*** *Boolean*
| `pairlists` | StaticPairList | Define one or more pairlists to be used. [More information below](#dynamic-pairlists). | `pairlists` | Define one or more pairlists to be used. [More information below](#dynamic-pairlists). <br>*Defaults to `StaticPairList`.* <br> ***Datatype:*** *List of Dicts*
| `telegram.enabled` | true | **Required.** Enable or not the usage of Telegram. | `telegram.enabled` | Enable the usage of Telegram. <br> ***Datatype:*** *Boolean*
| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** | `telegram.token` | Your Telegram bot token. Only required if `telegram.enabled` is `true`. **Keep it in secret, do not disclose publicly.** <br> ***Datatype:*** *String*
| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** | `telegram.chat_id` | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. **Keep it in secret, do not disclose publicly.** <br> ***Datatype:*** *String*
| `webhook.enabled` | false | Enable usage of Webhook notifications | `webhook.enabled` | Enable usage of Webhook notifications <br> ***Datatype:*** *Boolean*
| `webhook.url` | false | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. | `webhook.url` | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. <br> ***Datatype:*** *String*
| `webhook.webhookbuy` | false | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. | `webhook.webhookbuy` | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. <br> ***Datatype:*** *String*
| `webhook.webhooksell` | false | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. | `webhook.webhooksell` | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. <br> ***Datatype:*** *String*
| `webhook.webhookstatus` | false | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. | `webhook.webhookstatus` | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. <br> ***Datatype:*** *String*
| `db_url` | `sqlite:///tradesv3.sqlite`| Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. | `api_server.enabled` | Enable usage of API Server. See the [API Server documentation](rest-api.md) for more details. <br> ***Datatype:*** *Boolean*
| `initial_state` | running | Defines the initial application state. More information below. | `api_server.listen_ip_address` | Bind IP address. See the [API Server documentation](rest-api.md) for more details. <br> ***Datatype:*** *IPv4*
| `forcebuy_enable` | false | Enables the RPC Commands to force a buy. More information below. | `api_server.listen_port` | Bind Port. See the [API Server documentation](rest-api.md) for more details. <br> ***Datatype:*** *Integer between 1024 and 65535*
| `strategy` | None | **Required** Defines Strategy class to use. Recommended to set via `--strategy NAME`. | `api_server.username` | Username for API server. See the [API Server documentation](rest-api.md) for more details. **Keep it in secret, do not disclose publicly.**<br> ***Datatype:*** *String*
| `strategy_path` | null | Adds an additional strategy lookup path (must be a directory). | `api_server.password` | Password for API server. See the [API Server documentation](rest-api.md) for more details. **Keep it in secret, do not disclose publicly.**<br> ***Datatype:*** *String*
| `internals.process_throttle_secs` | 5 | **Required.** Set the process throttle. Value in second. | `db_url` | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `true`, and to `sqlite:///tradesv3.sqlite` for production instances. <br> ***Datatype:*** *String, SQLAlchemy connect string*
| `internals.heartbeat_interval` | 60 | Print heartbeat message every X seconds. Set to 0 to disable heartbeat messages. | `initial_state` | Defines the initial application state. More information below. <br>*Defaults to `stopped`.* <br> ***Datatype:*** *Enum, either `stopped` or `running`*
| `internals.sd_notify` | false | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details. | `forcebuy_enable` | Enables the RPC Commands to force a buy. More information below. <br> ***Datatype:*** *Boolean*
| `logfile` | | Specify Logfile. Uses a rolling strategy of 10 files, with 1Mb per file. | `strategy` | **Required** Defines Strategy class to use. Recommended to be set via `--strategy NAME`. <br> ***Datatype:*** *ClassName*
| `user_data_dir` | cwd()/user_data | Directory containing user data. Defaults to `./user_data/`. | `strategy_path` | Adds an additional strategy lookup path (must be a directory). <br> ***Datatype:*** *String*
| `internals.process_throttle_secs` | Set the process throttle. Value in second. <br>*Defaults to `5` seconds.* <br> ***Datatype:*** *Positive Integer*
| `internals.heartbeat_interval` | Print heartbeat message every N seconds. Set to 0 to disable heartbeat messages. <br>*Defaults to `60` seconds.* <br> ***Datatype:*** *Positive Integer or 0*
| `internals.sd_notify` | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details. <br> ***Datatype:*** *Boolean*
| `logfile` | Specifies logfile name. Uses a rolling strategy for log file rotation for 10 files with the 1MB limit per file. <br> ***Datatype:*** *String*
| `user_data_dir` | Directory containing user data. <br> *Defaults to `./user_data/`*. <br> ***Datatype:*** *String*
### Parameters in the strategy ### Parameters in the strategy
The following parameters can be set in either configuration file or strategy. The following parameters can be set in either configuration file or strategy.
Values set in the configuration file always overwrite values set in the strategy. Values set in the configuration file always overwrite values set in the strategy.
* `ticker_interval`
* `minimal_roi` * `minimal_roi`
* `ticker_interval`
* `stoploss` * `stoploss`
* `trailing_stop` * `trailing_stop`
* `trailing_stop_positive` * `trailing_stop_positive`
* `trailing_stop_positive_offset` * `trailing_stop_positive_offset`
* `trailing_only_offset_is_reached`
* `process_only_new_candles` * `process_only_new_candles`
* `order_types` * `order_types`
* `order_time_in_force` * `order_time_in_force`
* `stake_currency`
* `stake_amount`
* `use_sell_signal` (ask_strategy) * `use_sell_signal` (ask_strategy)
* `sell_profit_only` (ask_strategy) * `sell_profit_only` (ask_strategy)
* `ignore_roi_if_buy_signal` (ask_strategy) * `ignore_roi_if_buy_signal` (ask_strategy)
@ -123,15 +131,19 @@ Values set in the configuration file always overwrite values set in the strategy
### Understand stake_amount ### Understand stake_amount
The `stake_amount` configuration parameter is an amount of crypto-currency your bot will use for each trade. The `stake_amount` configuration parameter is an amount of crypto-currency your bot will use for each trade.
The minimal value is 0.0005. If there is not enough crypto-currency in
the account an exception is generated. The minimal configuration value is 0.0001. Please check your exchange's trading minimums to avoid problems.
This setting works in combination with `max_open_trades`. The maximum capital engaged in trades is `stake_amount * max_open_trades`.
For example, the bot will at most use (0.05 BTC x 3) = 0.15 BTC, assuming a configuration of `max_open_trades=3` and `stake_amount=0.05`.
To allow the bot to trade all the available `stake_currency` in your account set To allow the bot to trade all the available `stake_currency` in your account set
```json ```json
"stake_amount" : "unlimited", "stake_amount" : "unlimited",
``` ```
In this case a trade amount is calclulated as: In this case a trade amount is calculated as:
```python ```python
currency_balance / (max_open_trades - current_open_trades) currency_balance / (max_open_trades - current_open_trades)
@ -472,7 +484,7 @@ creating trades on the exchange.
"db_url": "sqlite:///tradesv3.dryrun.sqlite", "db_url": "sqlite:///tradesv3.dryrun.sqlite",
``` ```
3. Remove your Exchange API key and secrete (change them by empty values or fake credentials): 3. Remove your Exchange API key and secret (change them by empty values or fake credentials):
```json ```json
"exchange": { "exchange": {

View File

@ -61,11 +61,16 @@ def validate_config_consistency(conf: Dict[str, Any]) -> None:
:param conf: Config in JSON format :param conf: Config in JSON format
:return: Returns None if everything is ok, otherwise throw an OperationalException :return: Returns None if everything is ok, otherwise throw an OperationalException
""" """
# validating trailing stoploss # validating trailing stoploss
_validate_trailing_stoploss(conf) _validate_trailing_stoploss(conf)
_validate_edge(conf) _validate_edge(conf)
_validate_whitelist(conf) _validate_whitelist(conf)
# validate configuration before returning
logger.info('Validating configuration ...')
validate_config_schema(conf)
def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None: def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None:

View File

@ -9,8 +9,6 @@ from typing import Any, Callable, Dict, List, Optional
from freqtrade import OperationalException, constants from freqtrade import OperationalException, constants
from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.config_validation import (validate_config_consistency,
validate_config_schema)
from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings
from freqtrade.configuration.directory_operations import (create_datadir, from freqtrade.configuration.directory_operations import (create_datadir,
create_userdata_dir) create_userdata_dir)
@ -84,10 +82,6 @@ class Configuration:
if 'pairlists' not in config: if 'pairlists' not in config:
config['pairlists'] = [] config['pairlists'] = []
# validate configuration before returning
logger.info('Validating configuration ...')
validate_config_schema(config)
return config return config
def load_config(self) -> Dict[str, Any]: def load_config(self) -> Dict[str, Any]:
@ -118,8 +112,6 @@ class Configuration:
process_temporary_deprecated_settings(config) process_temporary_deprecated_settings(config)
validate_config_consistency(config)
return config return config
def _process_logging_options(self, config: Dict[str, Any]) -> None: def _process_logging_options(self, config: Dict[str, Any]) -> None:

View File

@ -6,7 +6,6 @@ bot constants
DEFAULT_CONFIG = 'config.json' DEFAULT_CONFIG = 'config.json'
DEFAULT_EXCHANGE = 'bittrex' DEFAULT_EXCHANGE = 'bittrex'
PROCESS_THROTTLE_SECS = 5 # sec PROCESS_THROTTLE_SECS = 5 # sec
DEFAULT_TICKER_INTERVAL = 5 # min
HYPEROPT_EPOCH = 100 # epochs HYPEROPT_EPOCH = 100 # epochs
RETRY_TIMEOUT = 30 # sec RETRY_TIMEOUT = 30 # sec
DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss'
@ -66,13 +65,13 @@ MINIMAL_CONFIG = {
CONF_SCHEMA = { CONF_SCHEMA = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'max_open_trades': {'type': 'integer', 'minimum': -1}, 'max_open_trades': {'type': ['integer', 'number'], 'minimum': -1},
'ticker_interval': {'type': 'string', 'enum': TIMEFRAMES}, 'ticker_interval': {'type': 'string', 'enum': TIMEFRAMES},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']}, 'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']},
'stake_amount': { 'stake_amount': {
"type": ["number", "string"], 'type': ['number', 'string'],
"minimum": 0.0005, 'minimum': 0.0001,
"pattern": UNLIMITED_STAKE_AMOUNT 'pattern': UNLIMITED_STAKE_AMOUNT
}, },
'fiat_display_currency': {'type': 'string', 'enum': SUPPORTED_FIAT}, 'fiat_display_currency': {'type': 'string', 'enum': SUPPORTED_FIAT},
'dry_run': {'type': 'boolean'}, 'dry_run': {'type': 'boolean'},
@ -94,8 +93,8 @@ CONF_SCHEMA = {
'unfilledtimeout': { 'unfilledtimeout': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': {'type': 'number', 'minimum': 3}, 'buy': {'type': 'number', 'minimum': 1},
'sell': {'type': 'number', 'minimum': 10} 'sell': {'type': 'number', 'minimum': 1}
} }
}, },
'bid_strategy': { 'bid_strategy': {
@ -107,7 +106,7 @@ CONF_SCHEMA = {
'maximum': 1, 'maximum': 1,
'exclusiveMaximum': False, 'exclusiveMaximum': False,
'use_order_book': {'type': 'boolean'}, 'use_order_book': {'type': 'boolean'},
'order_book_top': {'type': 'number', 'maximum': 20, 'minimum': 1}, 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
'check_depth_of_market': { 'check_depth_of_market': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
@ -123,8 +122,8 @@ CONF_SCHEMA = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'use_order_book': {'type': 'boolean'}, 'use_order_book': {'type': 'boolean'},
'order_book_min': {'type': 'number', 'minimum': 1}, 'order_book_min': {'type': 'integer', 'minimum': 1},
'order_book_max': {'type': 'number', 'minimum': 1, 'maximum': 50}, 'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50},
'use_sell_signal': {'type': 'boolean'}, 'use_sell_signal': {'type': 'boolean'},
'sell_profit_only': {'type': 'boolean'}, 'sell_profit_only': {'type': 'boolean'},
'ignore_roi_if_buy_signal': {'type': 'boolean'} 'ignore_roi_if_buy_signal': {'type': 'boolean'}
@ -197,8 +196,8 @@ CONF_SCHEMA = {
'listen_ip_address': {'format': 'ipv4'}, 'listen_ip_address': {'format': 'ipv4'},
'listen_port': { 'listen_port': {
'type': 'integer', 'type': 'integer',
"minimum": 1024, 'minimum': 1024,
"maximum": 65535 'maximum': 65535
}, },
'username': {'type': 'string'}, 'username': {'type': 'string'},
'password': {'type': 'string'}, 'password': {'type': 'string'},
@ -211,7 +210,7 @@ CONF_SCHEMA = {
'internals': { 'internals': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'process_throttle_secs': {'type': 'number'}, 'process_throttle_secs': {'type': 'integer'},
'interval': {'type': 'integer'}, 'interval': {'type': 'integer'},
'sd_notify': {'type': 'boolean'}, 'sd_notify': {'type': 'boolean'},
} }
@ -253,32 +252,32 @@ CONF_SCHEMA = {
'edge': { 'edge': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
"enabled": {'type': 'boolean'}, 'enabled': {'type': 'boolean'},
"process_throttle_secs": {'type': 'integer', 'minimum': 600}, 'process_throttle_secs': {'type': 'integer', 'minimum': 600},
"calculate_since_number_of_days": {'type': 'integer'}, 'calculate_since_number_of_days': {'type': 'integer'},
"allowed_risk": {'type': 'number'}, 'allowed_risk': {'type': 'number'},
"capital_available_percentage": {'type': 'number'}, 'capital_available_percentage': {'type': 'number'},
"stoploss_range_min": {'type': 'number'}, 'stoploss_range_min': {'type': 'number'},
"stoploss_range_max": {'type': 'number'}, 'stoploss_range_max': {'type': 'number'},
"stoploss_range_step": {'type': 'number'}, 'stoploss_range_step': {'type': 'number'},
"minimum_winrate": {'type': 'number'}, 'minimum_winrate': {'type': 'number'},
"minimum_expectancy": {'type': 'number'}, 'minimum_expectancy': {'type': 'number'},
"min_trade_number": {'type': 'number'}, 'min_trade_number': {'type': 'number'},
"max_trade_duration_minute": {'type': 'integer'}, 'max_trade_duration_minute': {'type': 'integer'},
"remove_pumps": {'type': 'boolean'} 'remove_pumps': {'type': 'boolean'}
}, },
'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage'] 'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage']
} }
}, },
'anyOf': [
{'required': ['exchange']}
],
'required': [ 'required': [
'exchange',
'max_open_trades', 'max_open_trades',
'stake_currency', 'stake_currency',
'stake_amount', 'stake_amount',
'dry_run', 'dry_run',
'bid_strategy', 'bid_strategy',
'unfilledtimeout', 'unfilledtimeout',
'stoploss',
'minimal_roi',
] ]
} }

View File

@ -13,7 +13,8 @@ from pandas import DataFrame
from tabulate import tabulate from tabulate import tabulate
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.configuration import TimeRange, remove_credentials from freqtrade.configuration import (TimeRange, remove_credentials,
validate_config_consistency)
from freqtrade.data import history from freqtrade.data import history
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
@ -75,10 +76,12 @@ class Backtesting:
stratconf = deepcopy(self.config) stratconf = deepcopy(self.config)
stratconf['strategy'] = strat stratconf['strategy'] = strat
self.strategylist.append(StrategyResolver(stratconf).strategy) self.strategylist.append(StrategyResolver(stratconf).strategy)
validate_config_consistency(stratconf)
else: else:
# No strategy list specified, only one strategy # No strategy list specified, only one strategy
self.strategylist.append(StrategyResolver(self.config).strategy) self.strategylist.append(StrategyResolver(self.config).strategy)
validate_config_consistency(self.config)
if "ticker_interval" not in self.config: if "ticker_interval" not in self.config:
raise OperationalException("Ticker-interval needs to be set in either configuration " raise OperationalException("Ticker-interval needs to be set in either configuration "

View File

@ -9,7 +9,8 @@ from typing import Any, Dict
from tabulate import tabulate from tabulate import tabulate
from freqtrade import constants from freqtrade import constants
from freqtrade.configuration import TimeRange, remove_credentials from freqtrade.configuration import (TimeRange, remove_credentials,
validate_config_consistency)
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
@ -35,6 +36,8 @@ class EdgeCli:
self.exchange = Exchange(self.config) self.exchange = Exchange(self.config)
self.strategy = StrategyResolver(self.config).strategy self.strategy = StrategyResolver(self.config).strategy
validate_config_consistency(self.config)
self.edge = Edge(config, self.exchange, self.strategy) self.edge = Edge(config, self.exchange, self.strategy)
# Set refresh_pairs to false for edge-cli (it must be true for edge) # Set refresh_pairs to false for edge-cli (it must be true for edge)
self.edge._refresh_pairs = False self.edge._refresh_pairs = False

View File

@ -312,7 +312,7 @@ class ApiServer(RPC):
logger.info("LocalRPC - Profit Command Called") logger.info("LocalRPC - Profit Command Called")
stats = self._rpc_trade_statistics(self._config['stake_currency'], stats = self._rpc_trade_statistics(self._config['stake_currency'],
self._config['fiat_display_currency'] self._config.get('fiat_display_currency')
) )
return self.rest_dump(stats) return self.rest_dump(stats)

View File

@ -285,12 +285,7 @@ def test_volumepairlist_caching(mocker, markets, whitelist_conf, tickers):
def test_pairlistmanager_no_pairlist(mocker, markets, whitelist_conf, caplog): def test_pairlistmanager_no_pairlist(mocker, markets, whitelist_conf, caplog):
del whitelist_conf['pairlists'][0]['method']
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
with pytest.raises(OperationalException,
match=r"No Pairlist defined!"):
get_patched_freqtradebot(mocker, whitelist_conf)
assert log_has_re("No method in .*", caplog)
whitelist_conf['pairlists'] = [] whitelist_conf['pairlists'] = []

View File

@ -690,8 +690,8 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None
pair = 'XRP/BTC' pair = 'XRP/BTC'
# Test not buying # Test not buying
default_conf['stake_amount'] = 0.0000001
freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot = get_patched_freqtradebot(mocker, default_conf)
freqtradebot.config['stake_amount'] = 0.0000001
patch_get_signal(freqtradebot, (True, False)) patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
pair = 'TKN/BTC' pair = 'TKN/BTC'

View File

@ -23,7 +23,7 @@ _TEST_PASS = "SuperSecurePassword1!"
def botclient(default_conf, mocker): def botclient(default_conf, mocker):
default_conf.update({"api_server": {"enabled": True, default_conf.update({"api_server": {"enabled": True,
"listen_ip_address": "127.0.0.1", "listen_ip_address": "127.0.0.1",
"listen_port": "8080", "listen_port": 8080,
"username": _TEST_USER, "username": _TEST_USER,
"password": _TEST_PASS, "password": _TEST_PASS,
}}) }})
@ -133,7 +133,10 @@ def test_api__init__(default_conf, mocker):
def test_api_run(default_conf, mocker, caplog): def test_api_run(default_conf, mocker, caplog):
default_conf.update({"api_server": {"enabled": True, default_conf.update({"api_server": {"enabled": True,
"listen_ip_address": "127.0.0.1", "listen_ip_address": "127.0.0.1",
"listen_port": "8080"}}) "listen_port": 8080,
"username": "TestUser",
"password": "testPass",
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.api_server.threading.Thread', MagicMock()) mocker.patch('freqtrade.rpc.api_server.threading.Thread', MagicMock())
@ -146,7 +149,7 @@ def test_api_run(default_conf, mocker, caplog):
apiserver.run() apiserver.run()
assert server_mock.call_count == 1 assert server_mock.call_count == 1
assert server_mock.call_args_list[0][0][0] == "127.0.0.1" assert server_mock.call_args_list[0][0][0] == "127.0.0.1"
assert server_mock.call_args_list[0][0][1] == "8080" assert server_mock.call_args_list[0][0][1] == 8080
assert isinstance(server_mock.call_args_list[0][0][2], Flask) assert isinstance(server_mock.call_args_list[0][0][2], Flask)
assert hasattr(apiserver, "srv") assert hasattr(apiserver, "srv")
@ -158,14 +161,14 @@ def test_api_run(default_conf, mocker, caplog):
server_mock.reset_mock() server_mock.reset_mock()
apiserver._config.update({"api_server": {"enabled": True, apiserver._config.update({"api_server": {"enabled": True,
"listen_ip_address": "0.0.0.0", "listen_ip_address": "0.0.0.0",
"listen_port": "8089", "listen_port": 8089,
"password": "", "password": "",
}}) }})
apiserver.run() apiserver.run()
assert server_mock.call_count == 1 assert server_mock.call_count == 1
assert server_mock.call_args_list[0][0][0] == "0.0.0.0" assert server_mock.call_args_list[0][0][0] == "0.0.0.0"
assert server_mock.call_args_list[0][0][1] == "8089" assert server_mock.call_args_list[0][0][1] == 8089
assert isinstance(server_mock.call_args_list[0][0][2], Flask) assert isinstance(server_mock.call_args_list[0][0][2], Flask)
assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog) assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog)
assert log_has("Starting Local Rest Server.", caplog) assert log_has("Starting Local Rest Server.", caplog)
@ -186,7 +189,10 @@ def test_api_run(default_conf, mocker, caplog):
def test_api_cleanup(default_conf, mocker, caplog): def test_api_cleanup(default_conf, mocker, caplog):
default_conf.update({"api_server": {"enabled": True, default_conf.update({"api_server": {"enabled": True,
"listen_ip_address": "127.0.0.1", "listen_ip_address": "127.0.0.1",
"listen_port": "8080"}}) "listen_port": 8080,
"username": "TestUser",
"password": "testPass",
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.api_server.threading.Thread', MagicMock()) mocker.patch('freqtrade.rpc.api_server.threading.Thread', MagicMock())
mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock()) mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock())

View File

@ -173,7 +173,10 @@ def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None:
default_conf["telegram"]["enabled"] = False default_conf["telegram"]["enabled"] = False
default_conf["api_server"] = {"enabled": True, default_conf["api_server"] = {"enabled": True,
"listen_ip_address": "127.0.0.1", "listen_ip_address": "127.0.0.1",
"listen_port": "8080"} "listen_port": 8080,
"username": "TestUser",
"password": "TestPass",
}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
# Sleep to allow the thread to start # Sleep to allow the thread to start

View File

@ -144,9 +144,9 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
def test_status(default_conf, update, mocker, fee, ticker,) -> None: def test_status(default_conf, update, mocker, fee, ticker,) -> None:
update.message.chat.id = 123 update.message.chat.id = "123"
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
default_conf['telegram']['chat_id'] = 123 default_conf['telegram']['chat_id'] = "123"
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',

View File

@ -113,7 +113,7 @@ def test_send_msg(default_conf, mocker):
def test_exception_send_msg(default_conf, mocker, caplog): def test_exception_send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict() default_conf["webhook"] = get_webhook_dict()
default_conf["webhook"]["webhookbuy"] = None del default_conf["webhook"]["webhookbuy"]
webhook = Webhook(get_patched_freqtradebot(mocker, default_conf)) webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION}) webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION})

View File

@ -40,10 +40,16 @@ def test_load_config_invalid_pair(default_conf) -> None:
def test_load_config_missing_attributes(default_conf) -> None: def test_load_config_missing_attributes(default_conf) -> None:
default_conf.pop('exchange') conf = deepcopy(default_conf)
conf.pop('exchange')
with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"): with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"):
validate_config_schema(default_conf) validate_config_schema(conf)
conf = deepcopy(default_conf)
conf.pop('stake_currency')
with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"):
validate_config_schema(conf)
def test_load_config_incorrect_stake_amount(default_conf) -> None: def test_load_config_incorrect_stake_amount(default_conf) -> None:
@ -100,7 +106,6 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
assert validated_conf['max_open_trades'] == 0 assert validated_conf['max_open_trades'] == 0
assert 'internals' in validated_conf assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog)
def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
@ -132,7 +137,6 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
assert 'internals' in validated_conf assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog)
def test_from_config(default_conf, mocker, caplog) -> None: def test_from_config(default_conf, mocker, caplog) -> None:
@ -159,7 +163,6 @@ def test_from_config(default_conf, mocker, caplog) -> None:
assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
assert validated_conf['fiat_display_currency'] == "EUR" assert validated_conf['fiat_display_currency'] == "EUR"
assert 'internals' in validated_conf assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog)
assert isinstance(validated_conf['user_data_dir'], Path) assert isinstance(validated_conf['user_data_dir'], Path)
@ -191,7 +194,6 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
assert validated_conf['max_open_trades'] > 999999999 assert validated_conf['max_open_trades'] > 999999999
assert validated_conf['max_open_trades'] == float('inf') assert validated_conf['max_open_trades'] == float('inf')
assert log_has('Validating configuration ...', caplog)
assert "runmode" in validated_conf assert "runmode" in validated_conf
assert validated_conf['runmode'] == RunMode.DRY_RUN assert validated_conf['runmode'] == RunMode.DRY_RUN

View File

@ -543,8 +543,9 @@ def test_create_trades_too_small_stake_amount(default_conf, ticker, limit_buy_or
get_fee=fee, get_fee=fee,
) )
default_conf['stake_amount'] = 0.000000005
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
freqtrade.config['stake_amount'] = 0.000000005
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
assert not freqtrade.create_trades() assert not freqtrade.create_trades()