use jsonschema instead of custom type validations
This commit is contained in:
		| @@ -11,4 +11,5 @@ matplotlib==2.0.2 | |||||||
| PYQT5==5.9 | PYQT5==5.9 | ||||||
| scikit-learn==0.19.0 | scikit-learn==0.19.0 | ||||||
| scipy==0.19.1 | scipy==0.19.1 | ||||||
| stockstats==0.2.0 | stockstats==0.2.0 | ||||||
|  | jsonschema==2.6.0 | ||||||
							
								
								
									
										144
									
								
								utils.py
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								utils.py
									
									
									
									
									
								
							| @@ -1,102 +1,82 @@ | |||||||
| import json | import json | ||||||
| import logging | import logging | ||||||
| from typing import List |  | ||||||
|  |  | ||||||
|  | from jsonschema import validate | ||||||
| from wrapt import synchronized | from wrapt import synchronized | ||||||
| from bittrex.bittrex import Bittrex |  | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| _cur_conf = None | _cur_conf = None | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Required json-schema for user specified config | ||||||
|  | _conf_schema = { | ||||||
|  |     'type': 'object', | ||||||
|  |     'properties': { | ||||||
|  |         'max_open_trades': {'type': 'integer'}, | ||||||
|  |         'stake_currency': {'type': 'string'}, | ||||||
|  |         'stake_amount': {'type': 'number'}, | ||||||
|  |         'dry_run': {'type': 'boolean'}, | ||||||
|  |         'minimal_roi': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'patternProperties': { | ||||||
|  |                 '^[0-9.]+$': {'type': 'number'} | ||||||
|  |             }, | ||||||
|  |             'minProperties': 1 | ||||||
|  |         }, | ||||||
|  |         'poloniex': {'$ref': '#/definitions/exchange'}, | ||||||
|  |         'bittrex': {'$ref': '#/definitions/exchange'}, | ||||||
|  |         'telegram': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'properties': { | ||||||
|  |                 'enabled': {'type': 'boolean'}, | ||||||
|  |                 'token': {'type': 'string'}, | ||||||
|  |                 'chat_id': {'type': 'string'}, | ||||||
|  |             }, | ||||||
|  |             'required': ['enabled', 'token', 'chat_id'] | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     'definitions': { | ||||||
|  |         'exchange': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'properties': { | ||||||
|  |                 'enabled': {'type': 'boolean'}, | ||||||
|  |                 'key': {'type': 'string'}, | ||||||
|  |                 'secret': {'type': 'string'}, | ||||||
|  |                 'pair_whitelist': { | ||||||
|  |                     'type': 'array', | ||||||
|  |                     'items': {'type': 'string'}, | ||||||
|  |                     'uniqueItems': True | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             'required': ['enabled', 'key', 'secret', 'pair_whitelist'] | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     'anyOf': [ | ||||||
|  |         {'required': ['poloniex']}, | ||||||
|  |         {'required': ['bittrex']} | ||||||
|  |     ], | ||||||
|  |     'required': [ | ||||||
|  |         'max_open_trades', | ||||||
|  |         'stake_currency', | ||||||
|  |         'stake_amount', | ||||||
|  |         'dry_run', | ||||||
|  |         'minimal_roi', | ||||||
|  |         'telegram' | ||||||
|  |     ] | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @synchronized | @synchronized | ||||||
| def get_conf(filename: str='config.json') -> dict: | def get_conf(filename: str='config.json') -> dict: | ||||||
|     """ |     """ | ||||||
|     Loads the config into memory and returns the instance of it |     Loads the config into memory validates it | ||||||
|  |     and returns the singleton instance | ||||||
|     :return: dict |     :return: dict | ||||||
|     """ |     """ | ||||||
|     global _cur_conf |     global _cur_conf | ||||||
|     if not _cur_conf: |     if not _cur_conf: | ||||||
|         with open(filename) as file: |         with open(filename) as file: | ||||||
|             _cur_conf = json.load(file) |             _cur_conf = json.load(file) | ||||||
|             validate_conf(_cur_conf) |             validate(_cur_conf, _conf_schema) | ||||||
|     return _cur_conf |     return _cur_conf | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_conf(conf: dict) -> None: |  | ||||||
|     """ |  | ||||||
|     Validates if the minimal possible config is provided |  | ||||||
|     :param conf: config as dict |  | ||||||
|     :return: None, raises ValueError if something is wrong |  | ||||||
|     """ |  | ||||||
|     if not isinstance(conf.get('max_open_trades'), int): |  | ||||||
|         raise ValueError('max_open_trades must be a int') |  | ||||||
|     if not isinstance(conf.get('stake_currency'), str): |  | ||||||
|         raise ValueError('stake_currency must be a str') |  | ||||||
|     if not isinstance(conf.get('stake_amount'), float): |  | ||||||
|         raise ValueError('stake_amount  must be a float') |  | ||||||
|     if not isinstance(conf.get('dry_run'), bool): |  | ||||||
|         raise ValueError('dry_run must be a boolean') |  | ||||||
|     if not isinstance(conf.get('minimal_roi'), dict): |  | ||||||
|         raise ValueError('minimal_roi must be a dict') |  | ||||||
|  |  | ||||||
|     for index, (minutes, threshold) in enumerate(conf.get('minimal_roi').items()): |  | ||||||
|         if not isinstance(minutes, str): |  | ||||||
|             raise ValueError('minimal_roi[{}].key must be a string'.format(index)) |  | ||||||
|         if not isinstance(threshold, float): |  | ||||||
|             raise ValueError('minimal_roi[{}].value must be a float'.format(index)) |  | ||||||
|  |  | ||||||
|     if conf.get('telegram'): |  | ||||||
|         telegram = conf.get('telegram') |  | ||||||
|         if not isinstance(telegram.get('token'), str): |  | ||||||
|             raise ValueError('telegram.token must be a string') |  | ||||||
|         if not isinstance(telegram.get('chat_id'), str): |  | ||||||
|             raise ValueError('telegram.chat_id must be a string') |  | ||||||
|  |  | ||||||
|     if conf.get('poloniex'): |  | ||||||
|         poloniex = conf.get('poloniex') |  | ||||||
|         if not isinstance(poloniex.get('key'), str): |  | ||||||
|             raise ValueError('poloniex.key must be a string') |  | ||||||
|         if not isinstance(poloniex.get('secret'), str): |  | ||||||
|             raise ValueError('poloniex.secret must be a string') |  | ||||||
|         if not isinstance(poloniex.get('pair_whitelist'), list): |  | ||||||
|             raise ValueError('poloniex.pair_whitelist must be a list') |  | ||||||
|         if poloniex.get('enabled', False): |  | ||||||
|             raise ValueError('poloniex is currently not implemented') |  | ||||||
|             #if not poloniex.get('pair_whitelist'): |  | ||||||
|             #    raise ValueError('poloniex.pair_whitelist must contain some pairs') |  | ||||||
|  |  | ||||||
|     if conf.get('bittrex'): |  | ||||||
|         bittrex = conf.get('bittrex') |  | ||||||
|         if not isinstance(bittrex.get('key'), str): |  | ||||||
|             raise ValueError('bittrex.key must be a string') |  | ||||||
|         if not isinstance(bittrex.get('secret'), str): |  | ||||||
|             raise ValueError('bittrex.secret must be a string') |  | ||||||
|         if not isinstance(bittrex.get('pair_whitelist'), list): |  | ||||||
|             raise ValueError('bittrex.pair_whitelist must be a list') |  | ||||||
|         if bittrex.get('enabled', False): |  | ||||||
|             if not bittrex.get('pair_whitelist'): |  | ||||||
|                 raise ValueError('bittrex.pair_whitelist must contain some pairs') |  | ||||||
|             validate_bittrex_pairs(bittrex.get('pair_whitelist')) |  | ||||||
|  |  | ||||||
|     if conf.get('poloniex', {}).get('enabled', False) \ |  | ||||||
|             and conf.get('bittrex', {}).get('enabled', False): |  | ||||||
|         raise ValueError('Cannot use poloniex and bittrex at the same time') |  | ||||||
|  |  | ||||||
|     logger.info('Config is valid ...') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_bittrex_pairs(pairs: List[str]) -> None: |  | ||||||
|     """ |  | ||||||
|     Validates if all given pairs exist on bittrex |  | ||||||
|     :param pairs: list of str |  | ||||||
|     :return: None |  | ||||||
|     """ |  | ||||||
|     data = Bittrex(None, None).get_markets() |  | ||||||
|     if not data['success']: |  | ||||||
|         raise RuntimeError('BITTREX: {}'.format(data['message'])) |  | ||||||
|     available_markets = [market['MarketName'].replace('-', '_')for market in data['result']] |  | ||||||
|     for pair in pairs: |  | ||||||
|         if pair not in available_markets: |  | ||||||
|             raise ValueError('Invalid pair: {}'.format(pair)) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user