Merge pull request #1746 from hroff-1902/json-defaults
Support for defaults in json schema
This commit is contained in:
commit
bf56e25404
@ -56,8 +56,10 @@
|
||||
},
|
||||
"exchange": {
|
||||
"name": "bittrex",
|
||||
"sandbox": false,
|
||||
"key": "your_exchange_key",
|
||||
"secret": "your_exchange_secret",
|
||||
"password": "",
|
||||
"ccxt_config": {"enableRateLimit": true},
|
||||
"ccxt_async_config": {
|
||||
"enableRateLimit": false,
|
||||
|
@ -194,7 +194,7 @@ optional arguments:
|
||||
### How to use **--refresh-pairs-cached** parameter?
|
||||
|
||||
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
|
||||
`--refresh-pairs-cached` to force Backtesting to update the data it has.
|
||||
|
@ -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.
|
||||
| `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).
|
||||
| `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.key` | 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.key` | '' | API key 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_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)
|
||||
|
@ -116,7 +116,7 @@ Return a summary of your profit/loss and performance.
|
||||
|
||||
### /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.
|
||||
|
||||
|
@ -9,7 +9,7 @@ from argparse import Namespace
|
||||
from logging.handlers import RotatingFileHandler
|
||||
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 freqtrade import OperationalException, constants
|
||||
@ -34,6 +34,27 @@ def set_loggers(log_level: int = 0) -> None:
|
||||
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 to read and init the bot configuration
|
||||
@ -331,7 +352,7 @@ class Configuration(object):
|
||||
:return: Returns the config if valid, otherwise throw an exception
|
||||
"""
|
||||
try:
|
||||
validate(conf, constants.CONF_SCHEMA, Draft4Validator)
|
||||
ValidatorWithDefaults(constants.CONF_SCHEMA).validate(conf)
|
||||
return conf
|
||||
except ValidationError as exception:
|
||||
logger.critical(
|
||||
|
@ -173,10 +173,10 @@ CONF_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {'type': 'string'},
|
||||
'sandbox': {'type': 'boolean'},
|
||||
'key': {'type': 'string'},
|
||||
'secret': {'type': 'string'},
|
||||
'password': {'type': 'string'},
|
||||
'sandbox': {'type': 'boolean', 'default': False},
|
||||
'key': {'type': 'string', 'default': ''},
|
||||
'secret': {'type': 'string', 'default': ''},
|
||||
'password': {'type': 'string', 'default': ''},
|
||||
'uid': {'type': 'string'},
|
||||
'pair_whitelist': {
|
||||
'type': 'array',
|
||||
@ -199,7 +199,7 @@ CONF_SCHEMA = {
|
||||
'ccxt_config': {'type': 'object'},
|
||||
'ccxt_async_config': {'type': 'object'}
|
||||
},
|
||||
'required': ['name', 'key', 'secret', 'pair_whitelist']
|
||||
'required': ['name', 'pair_whitelist']
|
||||
},
|
||||
'edge': {
|
||||
'type': 'object',
|
||||
|
@ -53,7 +53,7 @@ class FreqtradeBot(object):
|
||||
|
||||
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.wallets = Wallets(self.config, self.exchange)
|
||||
|
@ -68,7 +68,7 @@ class Backtesting(object):
|
||||
self.config['dry_run'] = True
|
||||
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.fee = self.exchange.get_fee()
|
||||
|
||||
|
@ -18,6 +18,15 @@ from freqtrade.state import RunMode
|
||||
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:
|
||||
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
|
||||
Configuration(Namespace())
|
||||
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]
|
||||
|
Loading…
Reference in New Issue
Block a user