Skip to content

Commit

Permalink
Added Main Configuration File, Added Functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Youbadawy committed Feb 6, 2021
1 parent 260e152 commit 615e225
Show file tree
Hide file tree
Showing 12 changed files with 1,114 additions and 0 deletions.
51 changes: 51 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"max_open_trades": 10,
"stake_currency": "BTC",
"stake_amount": 0.1,
"tradable_balance_ratio": 0.99,
"fiat_display_currency": "USD",
"timeframe": "5m",
"dry_run": true,

"exchange": {
"name": "binance",
"key": "",
"secret": "",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": true,
"rateLimit": 200
},
"pair_whitelist": [
"ETH/BTC",
"LINK/BTC",
"BNB/BTC",
"LTC/BTC",
"XRP/BTC",
"YFI/BTC",
"INJ/BTC",
"XMR/BTC"
],
"pair_blacklist": [

]
},
"pairlists": [
{"method": "StaticPairList"}
],
"telegram": {
"enabled": true,
"token": "",
"chat_id": ""
},
"api_server": {
"enabled": false,
"listen_ip_address": "127.0.0.1",
"listen_port": 8080,
"verbosity": "info",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "",
"password": ""
}
}
51 changes: 51 additions & 0 deletions config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"max_open_trades": 10,
"stake_currency": "BTC",
"stake_amount": 0.1,
"tradable_balance_ratio": 0.99,
"fiat_display_currency": "USD",
"timeframe": "5m",
"dry_run": true,

"exchange": {
"name": "binance",
"key": "",
"secret": "",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": true,
"rateLimit": 200
},
"pair_whitelist": [
"ETH/BTC",
"LINK/BTC",
"BNB/BTC",
"LTC/BTC",
"XRP/BTC",
"YFI/BTC",
"INJ/BTC",
"XMR/BTC"
],
"pair_blacklist": [

]
},
"pairlists": [
{"method": "StaticPairList"}
],
"telegram": {
"enabled": true,
"token": "",
"chat_id": ""
},
"api_server": {
"enabled": false,
"listen_ip_address": "127.0.0.1",
"listen_port": 8080,
"verbosity": "info",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "",
"password": ""
}
}
2 changes: 2 additions & 0 deletions finrl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
""" Finrl bot """
__version__ = '2021.02'
1 change: 1 addition & 0 deletions finrl/config/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from finrl.config.configuration import Configuration
132 changes: 132 additions & 0 deletions finrl/config/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
This module contains the configuration class
"""
import logging
import warnings
from copy import deepcopy
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional

from finrl import constants
from finrl.config.load_config import load_config_file
from finrl.loggers import setup_logging
from finrl.misc import deep_merge_dicts, json_load
from finrl.state import NON_UTIL_MODES, TRADING_MODES, RunMode


logger = logging.getLogger(__name__)

class Configuration:
"""
Class to read and init the bot configuration
Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
"""

def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
self.args = args
self.config: Optional[Dict[str, Any]] = None
self.runmode = runmode

def get_config(self) -> Dict[str, Any]:
"""
Return the config. Use this method to get the bot config
:return: Dict: Bot config
"""
if self.config is None:
self.config = self.load_config()

return self.config

@staticmethod
def from_files(files: List[str]) -> Dict[str, Any]:
"""
Iterate through the config files passed in, loading all of them
and merging their contents.
Files are loaded in sequence, parameters in later configuration files
override the same parameter from an earlier file (last definition wins).
Runs through the whole Configuration initialization, so all expected config entries
are available to interactive environments.
:param files: List of file paths
:return: configuration dictionary
"""
c = Configuration({'config': files}, RunMode.OTHER)
return c.get_config()

def load_from_files(self, files: List[str]) -> Dict[str, Any]:

# Keep this method as staticmethod, so it can be used from interactive environments
config: Dict[str, Any] = {}

if not files:
return deepcopy(constants.MINIMAL_CONFIG)

# We expect here a list of config filenames
for path in files:
logger.info(f'Using config: {path} ...')

# Merge config options, overwriting old values
config = deep_merge_dicts(load_config_file(path), config)

# Normalize config
if 'internals' not in config:
config['internals'] = {}
# TODO: This can be deleted along with removal of deprecated
# experimental settings
if 'ask_strategy' not in config:
config['ask_strategy'] = {}

if 'pairlists' not in config:
config['pairlists'] = []

return config

def load_config(self) -> Dict[str, Any]:
"""
Extract information for sys.argv and load the bot configuration
:return: Configuration dictionary
"""
# Load all configs
config: Dict[str, Any] = self.load_from_files(self.args.get("config", []))

# Keep a copy of the original configuration file
config['original_config'] = deepcopy(config)

self._resolve_pairs_list(config)

return config

def _resolve_pairs_list(self, config: Dict[str, Any]) -> None:
"""
Helper for download script.
Takes first found:
* -p (pairs argument)
* --pairs-file
* whitelist from config
"""

if "pairs" in config:
return

if "pairs_file" in self.args and self.args["pairs_file"]:
pairs_file = Path(self.args["pairs_file"])
logger.info(f'Reading pairs file "{pairs_file}".')
# Download pairs from the pairs file if no config is specified
# or if pairs file is specified explicitely
if not pairs_file.exists():
raise OperationalException(f'No pairs file found with path "{pairs_file}".')
with pairs_file.open('r') as f:
config['pairs'] = json_load(f)
config['pairs'].sort()
return

if 'config' in self.args and self.args['config']:
logger.info("Using pairlist from configuration.")
config['pairs'] = config.get('exchange', {}).get('pair_whitelist')
else:
# Fall back to /dl_path/pairs.json
pairs_file = config['datadir'] / 'pairs.json'
if pairs_file.exists():
with pairs_file.open('r') as f:
config['pairs'] = json_load(f)
if 'pairs' in config:
config['pairs'].sort()
63 changes: 63 additions & 0 deletions finrl/config/load_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
This module contain functions to load the configuration file
"""
import logging
import re
import sys
from pathlib import Path
from typing import Any, Dict

import rapidjson

from finrl.exceptions import OperationalException


logger = logging.getLogger(__name__)


CONFIG_PARSE_MODE = rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS


def log_config_error_range(path: str, errmsg: str) -> str:
"""
Parses configuration file and prints range around error
"""
if path != '-':
offsetlist = re.findall(r'(?<=Parse\serror\sat\soffset\s)\d+', errmsg)
if offsetlist:
offset = int(offsetlist[0])
text = Path(path).read_text()
# Fetch an offset of 80 characters around the error line
subtext = text[offset-min(80, offset):offset+80]
segments = subtext.split('\n')
if len(segments) > 3:
# Remove first and last lines, to avoid odd truncations
return '\n'.join(segments[1:-1])
else:
return subtext
return ''


def load_config_file(path: str) -> Dict[str, Any]:
"""
Loads a config file from the given path
:param path: path as str
:return: configuration as dictionary
"""
try:
# Read config from stdin if requested in the options
with open(path) if path != '-' else sys.stdin as file:
config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE)
except FileNotFoundError:
raise OperationalException(
f'Config file "{path}" not found!'
' Please create a config file or check whether it exists.')
except rapidjson.JSONDecodeError as e:
err_range = log_config_error_range(path, str(e))
raise OperationalException(
f'{e}\n'
f'Please verify the following segment of your configuration:\n{err_range}'
if err_range else 'Please verify your configuration file for syntax errors.'
)

return config
Loading

0 comments on commit 615e225

Please sign in to comment.