Merge pull request #1746 from hroff-1902/json-defaults

Support for defaults in json schema
This commit is contained in:
Matthias 2019-04-24 12:20:39 +02:00 committed by GitHub
commit bf56e25404
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 102 additions and 14 deletions

View File

@ -56,8 +56,10 @@
}, },
"exchange": { "exchange": {
"name": "bittrex", "name": "bittrex",
"sandbox": false,
"key": "your_exchange_key", "key": "your_exchange_key",
"secret": "your_exchange_secret", "secret": "your_exchange_secret",
"password": "",
"ccxt_config": {"enableRateLimit": true}, "ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": { "ccxt_async_config": {
"enableRateLimit": false, "enableRateLimit": false,

View File

@ -194,7 +194,7 @@ optional arguments:
### How to use **--refresh-pairs-cached** parameter? ### How to use **--refresh-pairs-cached** parameter?
The first time your run Backtesting, it will take the pairs you have The first time your run Backtesting, it will take the pairs you have
set in your config file and download data from Bittrex. set in your config file and download data from the Exchange.
If for any reason you want to update your data set, you use If for any reason you want to update your data set, you use
`--refresh-pairs-cached` to force Backtesting to update the data it has. `--refresh-pairs-cached` to force Backtesting to update the data it has.

View File

@ -40,10 +40,10 @@ Mandatory Parameters are marked as **Required**.
| `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` | 0 | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
| `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` | 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_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` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
| `exchange.name` | bittrex | **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).
| `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` | 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.key` | key | API key to use for the exchange. Only required when you are in production mode. | `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode.
| `exchange.secret` | secret | API secret to use for the exchange. Only required when you are in production mode. | `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode.
| `exchange.pair_whitelist` | [] | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. | `exchange.pair_whitelist` | [] | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.
| `exchange.pair_blacklist` | [] | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. | `exchange.pair_blacklist` | [] | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param.
| `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` | 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)

View File

@ -116,7 +116,7 @@ Return a summary of your profit/loss and performance.
### /forcebuy <pair> ### /forcebuy <pair>
> **BITTREX**: Buying ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`) > **BITTREX:** Buying ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`)
Note that for this to work, `forcebuy_enable` needs to be set to true. Note that for this to work, `forcebuy_enable` needs to be set to true.

View File

@ -9,7 +9,7 @@ from argparse import Namespace
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from jsonschema import Draft4Validator, validate from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match from jsonschema.exceptions import ValidationError, best_match
from freqtrade import OperationalException, constants from freqtrade import OperationalException, constants
@ -34,6 +34,27 @@ def set_loggers(log_level: int = 0) -> None:
logging.getLogger('telegram').setLevel(logging.INFO) logging.getLogger('telegram').setLevel(logging.INFO)
def _extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
for prop, subschema in properties.items():
if "default" in subschema:
instance.setdefault(prop, subschema["default"])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
return validators.extend(
validator_class, {"properties": set_defaults},
)
ValidatorWithDefaults = _extend_with_default(Draft4Validator)
class Configuration(object): class Configuration(object):
""" """
Class to read and init the bot configuration Class to read and init the bot configuration
@ -331,7 +352,7 @@ class Configuration(object):
:return: Returns the config if valid, otherwise throw an exception :return: Returns the config if valid, otherwise throw an exception
""" """
try: try:
validate(conf, constants.CONF_SCHEMA, Draft4Validator) ValidatorWithDefaults(constants.CONF_SCHEMA).validate(conf)
return conf return conf
except ValidationError as exception: except ValidationError as exception:
logger.critical( logger.critical(

View File

@ -173,10 +173,10 @@ CONF_SCHEMA = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'name': {'type': 'string'}, 'name': {'type': 'string'},
'sandbox': {'type': 'boolean'}, 'sandbox': {'type': 'boolean', 'default': False},
'key': {'type': 'string'}, 'key': {'type': 'string', 'default': ''},
'secret': {'type': 'string'}, 'secret': {'type': 'string', 'default': ''},
'password': {'type': 'string'}, 'password': {'type': 'string', 'default': ''},
'uid': {'type': 'string'}, 'uid': {'type': 'string'},
'pair_whitelist': { 'pair_whitelist': {
'type': 'array', 'type': 'array',
@ -199,7 +199,7 @@ CONF_SCHEMA = {
'ccxt_config': {'type': 'object'}, 'ccxt_config': {'type': 'object'},
'ccxt_async_config': {'type': 'object'} 'ccxt_async_config': {'type': 'object'}
}, },
'required': ['name', 'key', 'secret', 'pair_whitelist'] 'required': ['name', 'pair_whitelist']
}, },
'edge': { 'edge': {
'type': 'object', 'type': 'object',

View File

@ -53,7 +53,7 @@ class FreqtradeBot(object):
self.rpc: RPCManager = RPCManager(self) self.rpc: RPCManager = RPCManager(self)
exchange_name = self.config.get('exchange', {}).get('name', 'bittrex').title() exchange_name = self.config.get('exchange', {}).get('name').title()
self.exchange = ExchangeResolver(exchange_name, self.config).exchange self.exchange = ExchangeResolver(exchange_name, self.config).exchange
self.wallets = Wallets(self.config, self.exchange) self.wallets = Wallets(self.config, self.exchange)

View File

@ -68,7 +68,7 @@ class Backtesting(object):
self.config['dry_run'] = True self.config['dry_run'] = True
self.strategylist: List[IStrategy] = [] self.strategylist: List[IStrategy] = []
exchange_name = self.config.get('exchange', {}).get('name', 'bittrex').title() exchange_name = self.config.get('exchange', {}).get('name').title()
self.exchange = ExchangeResolver(exchange_name, self.config).exchange self.exchange = ExchangeResolver(exchange_name, self.config).exchange
self.fee = self.exchange.get_fee() self.fee = self.exchange.get_fee()

View File

@ -18,6 +18,15 @@ from freqtrade.state import RunMode
from freqtrade.tests.conftest import log_has from freqtrade.tests.conftest import log_has
@pytest.fixture(scope="function")
def all_conf():
config_file = Path(__file__).parents[2] / "config_full.json.example"
print(config_file)
configuration = Configuration(Namespace())
conf = configuration._load_config_file(str(config_file))
return conf
def test_load_config_invalid_pair(default_conf) -> None: def test_load_config_invalid_pair(default_conf) -> None:
default_conf['exchange']['pair_whitelist'].append('ETH-BTC') default_conf['exchange']['pair_whitelist'].append('ETH-BTC')
@ -608,3 +617,59 @@ def test_validate_tsl(default_conf):
default_conf['trailing_stop_positive_offset'] = 0.015 default_conf['trailing_stop_positive_offset'] = 0.015
Configuration(Namespace()) Configuration(Namespace())
configuration._validate_config_consistency(default_conf) configuration._validate_config_consistency(default_conf)
def test_load_config_default_exchange(all_conf) -> None:
"""
config['exchange'] subtree has required options in it
so it cannot be omitted in the config
"""
del all_conf['exchange']
assert 'exchange' not in all_conf
with pytest.raises(ValidationError,
match=r'\'exchange\' is a required property'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
def test_load_config_default_exchange_name(all_conf) -> None:
"""
config['exchange']['name'] option is required
so it cannot be omitted in the config
"""
del all_conf['exchange']['name']
assert 'name' not in all_conf['exchange']
with pytest.raises(ValidationError,
match=r'\'name\' is a required property'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
@pytest.mark.parametrize("keys", [("exchange", "sandbox", False),
("exchange", "key", ""),
("exchange", "secret", ""),
("exchange", "password", ""),
])
def test_load_config_default_subkeys(all_conf, keys) -> None:
"""
Test for parameters with default values in sub-paths
so they can be omitted in the config and the default value
should is added to the config.
"""
# Get first level key
key = keys[0]
# get second level key
subkey = keys[1]
del all_conf[key][subkey]
assert subkey not in all_conf[key]
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
assert subkey in all_conf[key]
assert all_conf[key][subkey] == keys[2]