From 2db053695351b0ce16d4df388a5acdacebf42fe1 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 2 Jul 2018 00:36:38 +0200 Subject: [PATCH 1/9] now handles non git repo for octobot's folder --- tools/commands.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/commands.py b/tools/commands.py index da4337a47..e9c2f1b40 100644 --- a/tools/commands.py +++ b/tools/commands.py @@ -3,7 +3,7 @@ import sys import threading -from git import Repo +from git import Repo, InvalidGitRepositoryError from backtesting.collector.data_collector import DataCollector from config.cst import ORIGIN_URL, GIT_ORIGIN @@ -17,7 +17,12 @@ class Commands: def update(logger, catch=False): logger.info("Updating...") try: - repo = Repo(os.getcwd()) + try: + repo = Repo(os.getcwd()) + except InvalidGitRepositoryError: + logger.error("Impossible to update if OctoBot. " + "This error can appear when OctoBot's folder is not a git repository.") + return # git = repo.git # check origin @@ -47,7 +52,12 @@ def update(logger, catch=False): @staticmethod def check_bot_update(logger, log=True): - repo = Repo(os.getcwd()) + try: + repo = Repo(os.getcwd()) + except InvalidGitRepositoryError: + logger.warning("Impossible to check if OctoBot is up to date. " + "This error can appear when OctoBot's folder is not a git repository.") + return True try: diff = list(repo.iter_commits('{0}..{1}/{0}'.format(repo.active_branch.name, GIT_ORIGIN))) From bc91c48c1172e243139fcca4ec43bac7da20478e Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 2 Jul 2018 18:56:32 +0200 Subject: [PATCH 2/9] added sleep when exception on order refresh not to spam interfaces --- trading/trader/orders_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/trading/trader/orders_manager.py b/trading/trader/orders_manager.py index 567e44064..77f5139f8 100644 --- a/trading/trader/orders_manager.py +++ b/trading/trader/orders_manager.py @@ -130,6 +130,7 @@ def run(self): except Exception as e: self.logger.error("Error when updating orders") self.logger.exception(e) + sleep(self.order_refresh_time) if not Backtesting.enabled(self.config): sleep(self.order_refresh_time) From 84b375e3c28530f3b5dd9ddedf53cce8493742ac Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 21:13:08 +0200 Subject: [PATCH 3/9] #281 [Tentacles] handle in development tentacles --- config/cst.py | 3 +- evaluator/abstract_evaluator.py | 3 +- tools/config_manager.py | 7 ++ .../templates/description_tentacle.template | 3 +- .../tentacle_package_manager.py | 67 ++++++++++--------- tools/tentacle_manager/tentacle_util.py | 13 +++- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/config/cst.py b/config/cst.py index d14b2e1d2..e6a989764 100644 --- a/config/cst.py +++ b/config/cst.py @@ -75,7 +75,7 @@ # DEBUG options CONFIG_DEBUG_OPTION_PERF = "performance_analyser" CONFIG_DEBUG_OPTION_PERF_REFRESH_TIME_MIN = 5 -CONFIG_DEBUG_OPTION = "DEBUG" +CONFIG_DEBUG_OPTION = "DEV_MODE" # SERVICES CONFIG_CATEGORY_SERVICES = "services" @@ -179,6 +179,7 @@ TENTACLE_MODULE_TYPE = "type" TENTACLE_MODULE_SUBTYPE = "subtype" TENTACLE_MODULE_VERSION = "version" +TENTACLE_MODULE_DEV = "developing" TENTACLE_MODULE_CONFIG_FILES = "config_files" TENTACLE_CREATOR_PATH = "tentacle_creator" TENTACLE_TEMPLATE_DESCRIPTION = "description" diff --git a/evaluator/abstract_evaluator.py b/evaluator/abstract_evaluator.py index d4322d297..f6c3dd0eb 100644 --- a/evaluator/abstract_evaluator.py +++ b/evaluator/abstract_evaluator.py @@ -3,6 +3,7 @@ from config.cst import * from evaluator.Dispatchers.abstract_dispatcher import * +from tools.config_manager import ConfigManager class AbstractEvaluator: @@ -86,7 +87,7 @@ def eval(self) -> None: self.ensure_eval_note_is_not_expired() self.eval_impl() except Exception as e: - if CONFIG_DEBUG_OPTION in self.config and self.config[CONFIG_DEBUG_OPTION]: + if ConfigManager.is_in_dev_mode(self.config): raise e else: self.logger.error("Exception in eval_impl(): " + str(e)) diff --git a/tools/config_manager.py b/tools/config_manager.py index 739884f33..6abc70c31 100644 --- a/tools/config_manager.py +++ b/tools/config_manager.py @@ -4,6 +4,7 @@ import shutil from config.config import load_config +from config.cst import CONFIG_DEBUG_OPTION class ConfigManager: @@ -42,3 +43,9 @@ def check_config(config_file): load_config(config_file=config_file, error=True) except Exception as e: raise e + + @staticmethod + def is_in_dev_mode(config): + if CONFIG_DEBUG_OPTION in config and config[CONFIG_DEBUG_OPTION]: + return True + return False diff --git a/tools/tentacle_creator/templates/description_tentacle.template b/tools/tentacle_creator/templates/description_tentacle.template index b88085349..86254279a 100644 --- a/tools/tentacle_creator/templates/description_tentacle.template +++ b/tools/tentacle_creator/templates/description_tentacle.template @@ -7,5 +7,6 @@ $tentacle_description: { "version": "{{ version }}", "requirements": {{ requirements }}, "config_files": [], - "tests": [] + "tests": [], + "developing": true } diff --git a/tools/tentacle_manager/tentacle_package_manager.py b/tools/tentacle_manager/tentacle_package_manager.py index 0299d702d..933946aff 100644 --- a/tools/tentacle_manager/tentacle_package_manager.py +++ b/tools/tentacle_manager/tentacle_package_manager.py @@ -9,7 +9,7 @@ TENTACLE_DESCRIPTION_IS_URL, TENTACLE_TYPES, EVALUATOR_CONFIG_FOLDER, TENTACLE_MODULE_NAME, TENTACLE_MODULE_TYPE, \ TENTACLE_MODULE_SUBTYPE, TENTACLE_MODULE_VERSION, TENTACLE_MODULE_CONFIG_FILES, TENTACLE_MODULE_REQUIREMENTS, \ TENTACLE_MODULE_REQUIREMENT_WITH_VERSION, TENTACLES_PATH, PYTHON_INIT_FILE, TENTACLE_MODULE_TESTS, \ - TentacleManagerActions, CONFIG_DEFAULT_EVALUATOR_FILE, CONFIG_EVALUATOR_FILE_PATH + TentacleManagerActions, CONFIG_DEFAULT_EVALUATOR_FILE, CONFIG_EVALUATOR_FILE_PATH, TENTACLE_MODULE_DEV class TentaclePackageManager: @@ -100,41 +100,46 @@ def process_module(self, action, package, module_name, package_localisation, is_ module_subtype = parsed_module[TENTACLE_MODULE_SUBTYPE] module_tests = parsed_module[TENTACLE_MODULE_TESTS] module_file_content = "" + module_dev = parsed_module[TENTACLE_MODULE_DEV] module_test_files = {test: "" for test in module_tests} if module_tests else {} - if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: - module_loc = "{0}.py".format(TentacleUtil.create_localization_from_type(package_localisation, - module_type, - module_subtype, - module_name)) + if module_dev is None or not module_dev: + if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: + module_loc = "{0}.py".format(TentacleUtil.create_localization_from_type(package_localisation, + module_type, + module_subtype, + module_name)) - if is_url: - module_file_content = TentaclePackageUtil.get_package_file_content_from_url(module_loc) - else: - with open(module_loc, "r") as module_file: - module_file_content = module_file.read() - - if module_test_files: - for test in module_tests: - test_loc = "{0}.py".format(TentacleUtil.create_localization_from_type(package_localisation, - module_type, - module_subtype, - test, - True)) - - if is_url: - module_test_files[test] = TentaclePackageUtil.get_package_file_content_from_url(test_loc) - else: - with open(test_loc, "r") as module_file: - module_test_files[test] = module_file.read() + if is_url: + module_file_content = TentaclePackageUtil.get_package_file_content_from_url(module_loc) + else: + with open(module_loc, "r") as module_file: + module_file_content = module_file.read() - if self._process_action_on_module(action, module_type, module_subtype, parsed_module[TENTACLE_MODULE_VERSION], - module_file_content, module_test_files, target_folder, module_name): - # manage module config - self._try_action_on_config(action, package, module_name, is_url, package_localisation) + if module_test_files: + for test in module_tests: + test_loc = "{0}.py".format(TentacleUtil.create_localization_from_type(package_localisation, + module_type, + module_subtype, + test, + True)) - if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: - self._try_action_on_requirements(action, package, module_name) + if is_url: + module_test_files[test] = TentaclePackageUtil.get_package_file_content_from_url(test_loc) + else: + with open(test_loc, "r") as module_file: + module_test_files[test] = module_file.read() + + if self._process_action_on_module(action, module_type, module_subtype, parsed_module[TENTACLE_MODULE_VERSION], + module_file_content, module_test_files, target_folder, module_name): + # manage module config + self._try_action_on_config(action, package, module_name, is_url, package_localisation) + + if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: + self._try_action_on_requirements(action, package, module_name) + else: + self.logger.error("{0} is currently on development, " + "it will not be installed (or activated DEV_MODE)".format(module_name)) def _should_do_something(self, action, module_name, module_version, need_this_exact_version=False, requiring=None): if action == TentacleManagerActions.UPDATE: diff --git a/tools/tentacle_manager/tentacle_util.py b/tools/tentacle_manager/tentacle_util.py index 9b9ab0485..49e1fb84a 100644 --- a/tools/tentacle_manager/tentacle_util.py +++ b/tools/tentacle_manager/tentacle_util.py @@ -10,7 +10,8 @@ TENTACLES_INSTALL_FOLDERS, TENTACLES_PATH, TENTACLES_EVALUATOR_PATH, TENTACLES_TRADING_PATH, \ TENTACLES_EVALUATOR_REALTIME_PATH, TENTACLES_EVALUATOR_TA_PATH, TENTACLES_EVALUATOR_SOCIAL_PATH, \ TENTACLES_EVALUATOR_STRATEGIES_PATH, TENTACLES_EVALUATOR_UTIL_PATH, TENTACLES_TRADING_MODE_PATH, \ - TENTACLES_PYTHON_INIT_CONTENT, PYTHON_INIT_FILE, TENTACLE_MODULE_TESTS, TENTACLES_TEST_PATH + TENTACLES_PYTHON_INIT_CONTENT, PYTHON_INIT_FILE, TENTACLE_MODULE_TESTS, TENTACLES_TEST_PATH, TENTACLE_MODULE_DEV +from tools.config_manager import ConfigManager def delete_tentacles_arch(): @@ -124,7 +125,9 @@ def parse_module_header(module_header_content): TENTACLE_MODULE_REQUIREMENTS: extract_tentacle_requirements(module_header_content), TENTACLE_MODULE_TESTS: extract_tentacle_tests(module_header_content), TENTACLE_MODULE_CONFIG_FILES: module_header_content[TENTACLE_MODULE_CONFIG_FILES] - if TENTACLE_MODULE_CONFIG_FILES in module_header_content else None + if TENTACLE_MODULE_CONFIG_FILES in module_header_content else None, + TENTACLE_MODULE_DEV: module_header_content[TENTACLE_MODULE_DEV] + if TENTACLE_MODULE_DEV in module_header_content else None } @@ -232,3 +235,9 @@ def is_module_in_list(module_name, module_version, module_list): else: return get_full_module_identifier(module_name, module_version) \ in module_list + + +def install_on_development(config, module_dev): + if module_dev and ConfigManager.is_in_dev_mode(config): + return True + return False From 465709eb68e792e00fb54b503b2331cb9974df7e Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 21:27:15 +0200 Subject: [PATCH 4/9] #281 Fix --- .../tentacle_package_manager.py | 17 +++++------ tools/tentacle_manager/tentacle_util.py | 28 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/tools/tentacle_manager/tentacle_package_manager.py b/tools/tentacle_manager/tentacle_package_manager.py index 933946aff..e03f5046c 100644 --- a/tools/tentacle_manager/tentacle_package_manager.py +++ b/tools/tentacle_manager/tentacle_package_manager.py @@ -103,7 +103,7 @@ def process_module(self, action, package, module_name, package_localisation, is_ module_dev = parsed_module[TENTACLE_MODULE_DEV] module_test_files = {test: "" for test in module_tests} if module_tests else {} - if module_dev is None or not module_dev: + if TentacleUtil.install_on_development(self.config, module_dev): if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: module_loc = "{0}.py".format(TentacleUtil.create_localization_from_type(package_localisation, module_type, @@ -130,7 +130,8 @@ def process_module(self, action, package, module_name, package_localisation, is_ with open(test_loc, "r") as module_file: module_test_files[test] = module_file.read() - if self._process_action_on_module(action, module_type, module_subtype, parsed_module[TENTACLE_MODULE_VERSION], + if self._process_action_on_module(action, module_type, module_subtype, + parsed_module[TENTACLE_MODULE_VERSION], module_file_content, module_test_files, target_folder, module_name): # manage module config self._try_action_on_config(action, package, module_name, is_url, package_localisation) @@ -138,8 +139,8 @@ def process_module(self, action, package, module_name, package_localisation, is_ if action == TentacleManagerActions.INSTALL or action == TentacleManagerActions.UPDATE: self._try_action_on_requirements(action, package, module_name) else: - self.logger.error("{0} is currently on development, " - "it will not be installed (or activated DEV_MODE)".format(module_name)) + self.logger.warning("{0} is currently on development, " + "it will not be installed (or activated DEV_MODE)".format(module_name)) def _should_do_something(self, action, module_name, module_version, need_this_exact_version=False, requiring=None): if action == TentacleManagerActions.UPDATE: @@ -212,7 +213,7 @@ def _try_action_on_requirements(self, action, package, module_name): self._should_do_something(action, requirement_module_name, requirement_module_version, True, module_name): try: - req_package, _, localisation, is_url, destination = self.tentacle_manager.\ + req_package, _, localisation, is_url, destination = self.tentacle_manager. \ get_package_in_lists(requirement_module_name, requirement_module_version) if req_package: @@ -224,7 +225,7 @@ def _try_action_on_requirements(self, action, package, module_name): .format(requirement_data[TENTACLE_MODULE_REQUIREMENT_WITH_VERSION])) except Exception as e: - error = "failed for tentacle module requirement '{0}' of module {1} ({2})"\ + error = "failed for tentacle module requirement '{0}' of module {1} ({2})" \ .format(requirement_module_name, module_name, e) if action == TentacleManagerActions.INSTALL: self.logger.error("installation {0}".format(error)) @@ -238,7 +239,7 @@ def _try_action_on_requirements(self, action, package, module_name): if not success: if action == TentacleManagerActions.UPDATE: # uninstall module - req_package, _, localisation, is_url, destination = self.tentacle_manager.\ + req_package, _, localisation, is_url, destination = self.tentacle_manager. \ get_package_in_lists(module_name) if req_package: self.process_module(TentacleManagerActions.UNINSTALL, req_package, module_name, @@ -247,7 +248,7 @@ def _try_action_on_requirements(self, action, package, module_name): elif action == TentacleManagerActions.INSTALL: # uninstall module and requirements for module_to_remove in applied_modules: - req_package, _, localisation, is_url, destination = self.tentacle_manager.\ + req_package, _, localisation, is_url, destination = self.tentacle_manager. \ get_package_in_lists(module_to_remove) if req_package: self.process_module(TentacleManagerActions.UNINSTALL, req_package, module_to_remove, diff --git a/tools/tentacle_manager/tentacle_util.py b/tools/tentacle_manager/tentacle_util.py index 49e1fb84a..90aac4262 100644 --- a/tools/tentacle_manager/tentacle_util.py +++ b/tools/tentacle_manager/tentacle_util.py @@ -83,17 +83,17 @@ def _create_arch_module_extremity(architecture, types_subdir, type_path, with_in def get_tentacles_arch(): tentacles_content_folder = { - TENTACLES_EVALUATOR_PATH: [ - TENTACLES_EVALUATOR_REALTIME_PATH, - TENTACLES_EVALUATOR_SOCIAL_PATH, - TENTACLES_EVALUATOR_TA_PATH, - TENTACLES_EVALUATOR_STRATEGIES_PATH, - TENTACLES_EVALUATOR_UTIL_PATH - ], - TENTACLES_TRADING_PATH: [ - TENTACLES_TRADING_MODE_PATH - ] - } + TENTACLES_EVALUATOR_PATH: [ + TENTACLES_EVALUATOR_REALTIME_PATH, + TENTACLES_EVALUATOR_SOCIAL_PATH, + TENTACLES_EVALUATOR_TA_PATH, + TENTACLES_EVALUATOR_STRATEGIES_PATH, + TENTACLES_EVALUATOR_UTIL_PATH + ], + TENTACLES_TRADING_PATH: [ + TENTACLES_TRADING_MODE_PATH + ] + } tentacle_architecture = { TENTACLES_PATH: [tentacles_content_folder, {TENTACLES_TEST_PATH: tentacles_content_folder}] } @@ -238,6 +238,12 @@ def is_module_in_list(module_name, module_version, module_list): def install_on_development(config, module_dev): + # is not on development + if module_dev is None or not module_dev: + return True + + # is on development if module_dev and ConfigManager.is_in_dev_mode(config): return True + return False From eed12c087d111b8bd0ab2ace39d70dbbf351cb11 Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 22:30:44 +0200 Subject: [PATCH 5/9] #283 Prepare --- config/cst.py | 6 ++++++ docs/CHANGELOG.md | 13 +++++++++++++ evaluator/RealTime/realtime_evaluator.py | 5 ++--- evaluator/Social/social_evaluator.py | 5 ++--- evaluator/Strategies/strategies_evaluator.py | 15 ++++++++++++--- evaluator/TA/TA_evaluator.py | 6 +++++- evaluator/abstract_evaluator.py | 18 ++++++++++++++++-- tools/time_frame_manager.py | 10 ++++++++++ 8 files changed, 66 insertions(+), 12 deletions(-) diff --git a/config/cst.py b/config/cst.py index e6a989764..de4dbc11b 100644 --- a/config/cst.py +++ b/config/cst.py @@ -209,6 +209,12 @@ CONFIG_DEFAULT_EVALUATOR_FILE = "config/default_evaluator_config.json" +# Tentacle Config +STRATEGIES_REQUIRED_TIME_FRAME = "required_time_frames" +STRATEGIES_REQUIRED_EVALUATORS = "required_evaluators" +TRADING_MODE_REQUIRED_STRATEGIES = "required_strategies" + + class TentacleManagerActions(Enum): INSTALL = 1 UNINSTALL = 2 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6e0f70e7c..d48e15f5d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,18 @@ *It is strongly advised to perform an update of your tentacles after updating OctoBot.* +Changelog for 0.1.4_2-beta +==================== +*Released date : July 3 2018* + +# Concerned issues : + #281 [Tentacles] handle in development tentacles + +# New features : + - In development tentacles + +# Bug fix : + - + Changelog for 0.1.4_1-beta ==================== *Released date : July 1 2018* diff --git a/evaluator/RealTime/realtime_evaluator.py b/evaluator/RealTime/realtime_evaluator.py index f325e7392..2713e0798 100644 --- a/evaluator/RealTime/realtime_evaluator.py +++ b/evaluator/RealTime/realtime_evaluator.py @@ -23,9 +23,8 @@ def __init__(self): self.load_config() @classmethod - def get_config_file_name(cls): - return "{0}/{1}/{2}/{3}".format(CONFIG_EVALUATOR, CONFIG_EVALUATOR_REALTIME, EVALUATOR_CONFIG_FOLDER, - cls.get_name() + CONFIG_FILE_EXT) + def get_config_file_name(cls, config_evaluator_type=CONFIG_EVALUATOR_REALTIME): + return super().get_config_file_name(config_evaluator_type) def stop(self): self.keep_running = False diff --git a/evaluator/Social/social_evaluator.py b/evaluator/Social/social_evaluator.py index cc31aef4b..8188b9f5a 100644 --- a/evaluator/Social/social_evaluator.py +++ b/evaluator/Social/social_evaluator.py @@ -22,9 +22,8 @@ def __init__(self): self.load_config() @classmethod - def get_config_file_name(cls): - return "{0}/{1}/{2}/{3}/{4}".format(TENTACLES_PATH, TENTACLES_EVALUATOR_PATH, CONFIG_EVALUATOR_SOCIAL - , EVALUATOR_CONFIG_FOLDER, cls.get_name() + CONFIG_FILE_EXT) + def get_config_file_name(cls, config_evaluator_type=CONFIG_EVALUATOR_SOCIAL): + return super().get_config_file_name(config_evaluator_type) def stop(self): self.keep_running = False diff --git a/evaluator/Strategies/strategies_evaluator.py b/evaluator/Strategies/strategies_evaluator.py index 106fe30a4..5156ac828 100644 --- a/evaluator/Strategies/strategies_evaluator.py +++ b/evaluator/Strategies/strategies_evaluator.py @@ -1,7 +1,10 @@ from abc import * +from config.config import load_config +from config.cst import CONFIG_EVALUATOR_STRATEGIES, STRATEGIES_REQUIRED_TIME_FRAME, CONFIG_FILE_EXT from evaluator.abstract_evaluator import AbstractEvaluator from tools.evaluator_divergence_analyser import EvaluatorDivergenceAnalyser +from tools.time_frame_manager import TimeFrameManager class StrategiesEvaluator(AbstractEvaluator): @@ -29,12 +32,18 @@ def eval_impl(self) -> None: raise NotImplementedError("Eval_impl not implemented") @classmethod - @abstractmethod + def get_config_file_name(cls, config_evaluator_type=CONFIG_EVALUATOR_STRATEGIES): + return super().get_config_file_name(config_evaluator_type) + + @classmethod def get_required_time_frames(cls): - raise NotImplementedError("Get_required_time_frames not implemented") + config = cls.get_evaluator_config() + if STRATEGIES_REQUIRED_TIME_FRAME in config: + return TimeFrameManager.parse_time_frames(config[STRATEGIES_REQUIRED_TIME_FRAME]) + else: + raise Exception("'required_time_frames' is missing in {0}{1}".format(cls.get_name(), CONFIG_FILE_EXT)) @classmethod - @abstractmethod def get_required_evaluators(cls): raise NotImplementedError("Get_required_evaluators not implemented") diff --git a/evaluator/TA/TA_evaluator.py b/evaluator/TA/TA_evaluator.py index 5bfce0d5c..88977e680 100644 --- a/evaluator/TA/TA_evaluator.py +++ b/evaluator/TA/TA_evaluator.py @@ -1,7 +1,7 @@ import time from abc import * -from config.cst import MAX_TA_EVAL_TIME_SECONDS +from config.cst import MAX_TA_EVAL_TIME_SECONDS, CONFIG_EVALUATOR_TA from evaluator.abstract_evaluator import AbstractEvaluator @@ -24,6 +24,10 @@ def get_is_evaluable(self): def eval_impl(self) -> None: raise NotImplementedError("Eval_impl not implemented") + @classmethod + def get_config_file_name(cls, config_evaluator_type=CONFIG_EVALUATOR_TA): + return super().get_config_file_name(config_evaluator_type) + def eval(self) -> None: self.is_updating = True start_time = time.time() diff --git a/evaluator/abstract_evaluator.py b/evaluator/abstract_evaluator.py index f6c3dd0eb..157e951ef 100644 --- a/evaluator/abstract_evaluator.py +++ b/evaluator/abstract_evaluator.py @@ -1,5 +1,7 @@ -import time import copy +import time + +from config.config import load_config from config.cst import * from evaluator.Dispatchers.abstract_dispatcher import * @@ -38,6 +40,18 @@ def get_all_subclasses(cls): subclasses_list += subclass.get_all_subclasses() return subclasses_list + @classmethod + def get_config_file_name(cls, config_evaluator_type=None): + return "{0}/{1}/{2}/{3}/{4}".format(TENTACLES_PATH, TENTACLES_EVALUATOR_PATH, config_evaluator_type + , EVALUATOR_CONFIG_FOLDER, cls.get_name() + CONFIG_FILE_EXT) + + @classmethod + def get_evaluator_config(cls): + try: + return load_config(cls.get_config_file_name()) + except Exception as e: + raise e + # Used to provide a new logger for this particular indicator def set_logger(self, logger): self.logger = logger @@ -94,7 +108,7 @@ def eval(self) -> None: finally: if self.eval_note == "nan": self.eval_note = START_PENDING_EVAL_NOTE - self.logger.warning(str(self.symbol)+" evaluator returned 'nan' as eval_note, ignoring this value.") + self.logger.warning(str(self.symbol) + " evaluator returned 'nan' as eval_note, ignoring this value.") self.is_updating = False # eval new data diff --git a/tools/time_frame_manager.py b/tools/time_frame_manager.py index a06084687..d8d192290 100644 --- a/tools/time_frame_manager.py +++ b/tools/time_frame_manager.py @@ -50,3 +50,13 @@ def find_min_time_frame(time_frames, min_time_frame=None): except ValueError: pass return min_time_frame + + @staticmethod + def parse_time_frames(time_frames_string_list): + result_list = [] + for time_frame_string in time_frames_string_list: + try: + result_list.append(TimeFrames(time_frame_string)) + except ValueError: + pass + return result_list From 807457bcae0e70aba436939963c182325fcb9c7a Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 23:05:07 +0200 Subject: [PATCH 6/9] #283 [Tentacle Strategies & Trading Mode] add constants to config files --- docs/CHANGELOG.md | 1 + evaluator/Strategies/strategies_evaluator.py | 14 +++++-- evaluator/symbol_evaluator.py | 14 ++++--- tools/class_inspector.py | 17 ++++++++ trading/trader/modes/abstract_trading_mode.py | 42 +++++++++++++++---- 5 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 tools/class_inspector.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d48e15f5d..0cbbedc64 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,7 @@ Changelog for 0.1.4_2-beta # Concerned issues : #281 [Tentacles] handle in development tentacles + #283 [Tentacle Strategies & Trading Mode] add constants to config files # New features : - In development tentacles diff --git a/evaluator/Strategies/strategies_evaluator.py b/evaluator/Strategies/strategies_evaluator.py index 5156ac828..f2616e615 100644 --- a/evaluator/Strategies/strategies_evaluator.py +++ b/evaluator/Strategies/strategies_evaluator.py @@ -1,7 +1,7 @@ from abc import * -from config.config import load_config -from config.cst import CONFIG_EVALUATOR_STRATEGIES, STRATEGIES_REQUIRED_TIME_FRAME, CONFIG_FILE_EXT +from config.cst import CONFIG_EVALUATOR_STRATEGIES, STRATEGIES_REQUIRED_TIME_FRAME, CONFIG_FILE_EXT, \ + STRATEGIES_REQUIRED_EVALUATORS from evaluator.abstract_evaluator import AbstractEvaluator from tools.evaluator_divergence_analyser import EvaluatorDivergenceAnalyser from tools.time_frame_manager import TimeFrameManager @@ -41,11 +41,17 @@ def get_required_time_frames(cls): if STRATEGIES_REQUIRED_TIME_FRAME in config: return TimeFrameManager.parse_time_frames(config[STRATEGIES_REQUIRED_TIME_FRAME]) else: - raise Exception("'required_time_frames' is missing in {0}{1}".format(cls.get_name(), CONFIG_FILE_EXT)) + raise Exception("'{0}' is missing in {1}".format(STRATEGIES_REQUIRED_TIME_FRAME, + cls.get_config_file_name())) @classmethod def get_required_evaluators(cls): - raise NotImplementedError("Get_required_evaluators not implemented") + config = cls.get_evaluator_config() + if STRATEGIES_REQUIRED_EVALUATORS in config: + return config[STRATEGIES_REQUIRED_EVALUATORS] + else: + raise Exception("'{0}' is missing in {1}".format(STRATEGIES_REQUIRED_EVALUATORS, + cls.get_config_file_name())) class MixedStrategiesEvaluator(StrategiesEvaluator): diff --git a/evaluator/symbol_evaluator.py b/evaluator/symbol_evaluator.py index 767bc162e..5b4178967 100644 --- a/evaluator/symbol_evaluator.py +++ b/evaluator/symbol_evaluator.py @@ -1,4 +1,3 @@ -import inspect import logging from config.cst import EvaluatorMatrixTypes, CONFIG_TRADER_MODE, CONFIG_TRADER, START_PENDING_EVAL_NOTE @@ -7,6 +6,7 @@ from evaluator.TA import TAEvaluator from evaluator.evaluator_creator import EvaluatorCreator from evaluator.evaluator_matrix import EvaluatorMatrix +from tools.class_inspector import get_class_from_string from trading.trader import modes from trading.trader.modes import AbstractTradingMode @@ -41,11 +41,13 @@ def set_trader_simulators(self, simulator): def get_trading_mode_class(self): if CONFIG_TRADER in self.config and CONFIG_TRADER_MODE in self.config[CONFIG_TRADER]: - if any(m[0] == self.config[CONFIG_TRADER][CONFIG_TRADER_MODE] and - hasattr(m[1], '__bases__') and - AbstractTradingMode in m[1].__bases__ - for m in inspect.getmembers(modes)): - return getattr(modes, self.config[CONFIG_TRADER][CONFIG_TRADER_MODE]) + trading_mode_class = get_class_from_string( + self.config[CONFIG_TRADER][CONFIG_TRADER_MODE], + AbstractTradingMode, + modes) + + if trading_mode_class is not None: + return trading_mode_class raise Exception("Please specify a valid trading mode in your config file (trader -> mode)") diff --git a/tools/class_inspector.py b/tools/class_inspector.py new file mode 100644 index 000000000..752be3faa --- /dev/null +++ b/tools/class_inspector.py @@ -0,0 +1,17 @@ +import inspect + + +def get_class_from_string(class_string, parent, module): + if any(m[0] == class_string and + hasattr(m[1], '__bases__') and + parent in m[1].__bases__ + for m in inspect.getmembers(module)): + return getattr(module, class_string) + return None + + +def get_deep_class_from_string(class_string, module): + for m in inspect.getmembers(module): + if m[0] == class_string: + return getattr(module, class_string) + return None diff --git a/trading/trader/modes/abstract_trading_mode.py b/trading/trader/modes/abstract_trading_mode.py index 02e61abdf..b49225c36 100644 --- a/trading/trader/modes/abstract_trading_mode.py +++ b/trading/trader/modes/abstract_trading_mode.py @@ -1,10 +1,13 @@ -import os import logging +import os from abc import * from config.config import load_config -from config.cst import CONFIG_FILE_EXT, CONFIG_TRADING, CONFIG_TRADER, CONFIG_TRADER_MODES, EVALUATOR_CONFIG_FOLDER +from config.cst import CONFIG_FILE_EXT, EVALUATOR_CONFIG_FOLDER, \ + TRADING_MODE_REQUIRED_STRATEGIES, TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH +from evaluator import Strategies from evaluator.Util.advanced_manager import AdvancedManager +from tools.class_inspector import get_deep_class_from_string class AbstractTradingMode: @@ -21,10 +24,26 @@ def __init__(self, config, symbol_evaluator, exchange): self.symbol = symbol_evaluator.get_symbol() self._init_strategies_instances(symbol_evaluator.get_strategies_eval_list(exchange)) - @staticmethod - @abstractmethod - def get_required_strategies(): - raise NotImplementedError("get_required_strategies not implemented") + @classmethod + def get_required_strategies(cls): + config = cls.get_trading_mode_config() + if TRADING_MODE_REQUIRED_STRATEGIES in config: + strategies_classes = [] + for class_string in config[TRADING_MODE_REQUIRED_STRATEGIES]: + r = get_deep_class_from_string(class_string, Strategies) + if r is not None: + if r not in strategies_classes: + strategies_classes.append(r) + else: + raise Exception("{0} is not found, Octobot can't use {1}, please check {1}{2}".format( + class_string, + cls.get_name(), + cls.get_config_file_name())) + + return strategies_classes + else: + raise Exception("'{0}' is missing in {1}".format(TRADING_MODE_REQUIRED_STRATEGIES, + cls.get_config_file_name())) @classmethod def get_name(cls): @@ -32,8 +51,15 @@ def get_name(cls): @classmethod def get_config_file_name(cls): - return "{0}/{1}/{2}/{3}/{4}".format(CONFIG_TRADING, CONFIG_TRADER, CONFIG_TRADER_MODES, EVALUATOR_CONFIG_FOLDER, - cls.get_name() + CONFIG_FILE_EXT) + return "{0}/{1}/{2}/{3}/{4}".format(TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH + , EVALUATOR_CONFIG_FOLDER, cls.get_name() + CONFIG_FILE_EXT) + + @classmethod + def get_trading_mode_config(cls): + try: + return load_config(cls.get_config_file_name()) + except Exception as e: + raise e def get_strategy_instances_by_classes(self): return self.strategy_instances_by_classes From df9efc489f257910459a79448f1193c5af29c7e5 Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 23:31:49 +0200 Subject: [PATCH 7/9] #283 Fix tentacle creator --- config/cst.py | 1 + .../templates/Mode_config.template | 3 ++ .../templates/Strategies_config.template | 4 ++ .../templates/description_tentacle.template | 2 +- tools/tentacle_creator/tentacle_creator.py | 44 +++++++++++++++++-- 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 tools/tentacle_creator/templates/Mode_config.template create mode 100644 tools/tentacle_creator/templates/Strategies_config.template diff --git a/config/cst.py b/config/cst.py index de4dbc11b..b1807ddca 100644 --- a/config/cst.py +++ b/config/cst.py @@ -185,6 +185,7 @@ TENTACLE_TEMPLATE_DESCRIPTION = "description" TENTACLE_TEMPLATE_PATH = "templates" TENTACLE_TEMPLATE_PRE_EXT = "_tentacle" +TENTACLE_CONFIG_TEMPLATE_PRE_EXT = "_config" TENTACLE_TEMPLATE_EXT = ".template" TENTACLE_SONS = {"Social": TENTACLES_EVALUATOR_SOCIAL_PATH, diff --git a/tools/tentacle_creator/templates/Mode_config.template b/tools/tentacle_creator/templates/Mode_config.template new file mode 100644 index 000000000..81bb2a515 --- /dev/null +++ b/tools/tentacle_creator/templates/Mode_config.template @@ -0,0 +1,3 @@ +\{ + "required_strategies": ["FullMixedStrategiesEvaluator"] +} \ No newline at end of file diff --git a/tools/tentacle_creator/templates/Strategies_config.template b/tools/tentacle_creator/templates/Strategies_config.template new file mode 100644 index 000000000..f91241c0d --- /dev/null +++ b/tools/tentacle_creator/templates/Strategies_config.template @@ -0,0 +1,4 @@ +\{ + "required_time_frames" : ["30m", "1h", "2h", "4h", "1d"], + "required_evaluators" : ["*"] +} \ No newline at end of file diff --git a/tools/tentacle_creator/templates/description_tentacle.template b/tools/tentacle_creator/templates/description_tentacle.template index 86254279a..f7162075b 100644 --- a/tools/tentacle_creator/templates/description_tentacle.template +++ b/tools/tentacle_creator/templates/description_tentacle.template @@ -6,7 +6,7 @@ $tentacle_description: { "subtype": "{{ subtype }}", "version": "{{ version }}", "requirements": {{ requirements }}, - "config_files": [], + "config_files": {{ config }}, "tests": [], "developing": true } diff --git a/tools/tentacle_creator/tentacle_creator.py b/tools/tentacle_creator/tentacle_creator.py index c7c59db68..323f3c533 100644 --- a/tools/tentacle_creator/tentacle_creator.py +++ b/tools/tentacle_creator/tentacle_creator.py @@ -5,7 +5,8 @@ from config.cst import TENTACLE_CREATOR_PATH, TENTACLE_TEMPLATE_PATH, TOOLS_PATH, \ TENTACLE_TEMPLATE_DESCRIPTION, TENTACLE_TEMPLATE_EXT, TENTACLE_TEMPLATE_PRE_EXT, TENTACLE_PARENTS, TENTACLE_SONS, \ - EVALUATOR_ADVANCED_FOLDER, TENTACLES_PATH + EVALUATOR_ADVANCED_FOLDER, TENTACLES_PATH, TENTACLE_CONFIG_TEMPLATE_PRE_EXT, CONFIG_FILE_EXT, \ + EVALUATOR_CONFIG_FOLDER from tools.tentacle_manager.tentacle_util import get_tentacles_arch @@ -14,6 +15,7 @@ def __init__(self, config): self.tentacles_arch, _ = get_tentacles_arch() self.config = config self.templates = {} + self.config_templates = {} self.logger = logging.getLogger(self.__class__.__name__) @staticmethod @@ -25,9 +27,21 @@ def get_template_path(name): TENTACLE_TEMPLATE_PRE_EXT, TENTACLE_TEMPLATE_EXT) + @staticmethod + def get_config_template_path(name): + return "{0}/{1}/{2}/{3}{4}{5}".format(TOOLS_PATH, + TENTACLE_CREATOR_PATH, + TENTACLE_TEMPLATE_PATH, + name, + TENTACLE_CONFIG_TEMPLATE_PRE_EXT, + TENTACLE_TEMPLATE_EXT) + def get_templates(self): return self.templates + def get_config_templates(self): + return self.config_templates + def load_templates(self): self.templates["Description"] = open(self.get_template_path(TENTACLE_TEMPLATE_DESCRIPTION), "r").read() for tentacle_type in TENTACLE_SONS: @@ -36,6 +50,11 @@ def load_templates(self): except FileNotFoundError: pass + try: + self.config_templates[tentacle_type] = open(self.get_config_template_path(tentacle_type), "r").read() + except FileNotFoundError: + pass + def parse_commands(self, commands): command_help = "" for tentacle_type in TENTACLE_PARENTS: @@ -59,6 +78,7 @@ def create_tentacle(self, tentacle_type): new_tentacle = CreatedTentacle(self.config, tentacle_type, self) new_tentacle.ask_description(tentacle_type) new_tentacle.create_file() + new_tentacle.create_config_file() self.logger.info("{0} tentacle successfully created in {1}".format(new_tentacle.get_name(), new_tentacle.get_path())) except Exception as e: @@ -84,12 +104,17 @@ def __init__(self, config, tentacle_type, tentacle_creator): self.requirements = [] self.tests = [] + self.config_file = self.get_config_path() if self.subtype in self.tentacle_creator.get_config_templates() else[] self.logger = logging.getLogger(self.__class__.__name__) def get_path(self): return "{0}/{1}/{2}/{3}/{4}.py".format(TENTACLES_PATH, self.t_type, self.subtype, EVALUATOR_ADVANCED_FOLDER, self.name) + def get_config_path(self): + return "{0}/{1}/{2}/{3}/{4}{5}".format(TENTACLES_PATH, self.t_type, self.subtype, + EVALUATOR_CONFIG_FOLDER, self.name, CONFIG_FILE_EXT) + def get_name(self): return self.name @@ -119,7 +144,8 @@ def create_file(self): subtype=self.subtype, version=self.version, requirements=self.requirements, - tests=self.tests)) + tests=self.tests, + config=self.config_file)) tentacle_file.write("\n"+self.header_separator) tentacle_file.write(impl_template.render(name=self.name, big_name=self.name.title(), @@ -127,10 +153,22 @@ def create_file(self): subtype=self.subtype, version=self.version, requirements=self.requirements, - tests=self.tests)) + tests=self.tests, + config=self.config_file)) # TODO add __init__.py management else: raise Exception("A tentacle with this name already exists") except Exception as e: raise e + + def create_config_file(self): + try: + cfg_template = NativeEnvironment().from_string(self.tentacle_creator.get_config_templates()[self.subtype]) + if not os.path.isfile(self.get_config_path()): + with open(self.get_config_path(), "w") as config_file: + config_file.write(cfg_template.render()[1:]) + else: + raise Exception("A config with this name already exists") + except Exception: + pass From 11b2d795d3d754723a06392c390fe31ba2c340d2 Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 23:53:22 +0200 Subject: [PATCH 8/9] #282 prepare --- config/cst.py | 1 + docs/CHANGELOG.md | 1 + trading/trader/modes/abstract_trading_mode.py | 37 +++++++++++-------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/config/cst.py b/config/cst.py index b1807ddca..6557aba6b 100644 --- a/config/cst.py +++ b/config/cst.py @@ -214,6 +214,7 @@ STRATEGIES_REQUIRED_TIME_FRAME = "required_time_frames" STRATEGIES_REQUIRED_EVALUATORS = "required_evaluators" TRADING_MODE_REQUIRED_STRATEGIES = "required_strategies" +TRADING_MODE_SPECIFIC_SYMBOLS = "specific_symbols" class TentacleManagerActions(Enum): diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0cbbedc64..a671e90f1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog for 0.1.4_2-beta # New features : - In development tentacles + - Strategies and Trading Mode config creation with tentacle creator # Bug fix : - diff --git a/trading/trader/modes/abstract_trading_mode.py b/trading/trader/modes/abstract_trading_mode.py index b49225c36..3ae3112a9 100644 --- a/trading/trader/modes/abstract_trading_mode.py +++ b/trading/trader/modes/abstract_trading_mode.py @@ -4,7 +4,8 @@ from config.config import load_config from config.cst import CONFIG_FILE_EXT, EVALUATOR_CONFIG_FOLDER, \ - TRADING_MODE_REQUIRED_STRATEGIES, TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH + TRADING_MODE_REQUIRED_STRATEGIES, TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH, \ + TRADING_MODE_SPECIFIC_SYMBOLS from evaluator import Strategies from evaluator.Util.advanced_manager import AdvancedManager from tools.class_inspector import get_deep_class_from_string @@ -24,6 +25,22 @@ def __init__(self, config, symbol_evaluator, exchange): self.symbol = symbol_evaluator.get_symbol() self._init_strategies_instances(symbol_evaluator.get_strategies_eval_list(exchange)) + @classmethod + def get_name(cls): + return cls.__name__ + + @classmethod + def get_config_file_name(cls): + return "{0}/{1}/{2}/{3}/{4}".format(TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH + , EVALUATOR_CONFIG_FOLDER, cls.get_name() + CONFIG_FILE_EXT) + + @classmethod + def get_trading_mode_config(cls): + try: + return load_config(cls.get_config_file_name()) + except Exception as e: + raise e + @classmethod def get_required_strategies(cls): config = cls.get_trading_mode_config() @@ -46,20 +63,10 @@ def get_required_strategies(cls): cls.get_config_file_name())) @classmethod - def get_name(cls): - return cls.__name__ - - @classmethod - def get_config_file_name(cls): - return "{0}/{1}/{2}/{3}/{4}".format(TENTACLES_PATH, TENTACLES_TRADING_PATH, TENTACLES_TRADING_MODE_PATH - , EVALUATOR_CONFIG_FOLDER, cls.get_name() + CONFIG_FILE_EXT) - - @classmethod - def get_trading_mode_config(cls): - try: - return load_config(cls.get_config_file_name()) - except Exception as e: - raise e + def get_specific_symbols(cls): + config = cls.get_trading_mode_config() + if TRADING_MODE_SPECIFIC_SYMBOLS in config: + return config[TRADING_MODE_SPECIFIC_SYMBOLS] def get_strategy_instances_by_classes(self): return self.strategy_instances_by_classes From 948ce04f412bee96c356a36930d76d487e48e9da Mon Sep 17 00:00:00 2001 From: Paul Bouquet Date: Mon, 2 Jul 2018 23:54:40 +0200 Subject: [PATCH 9/9] [Version] Prepare new daily release (0.1.4_2-beta) --- README.md | 2 +- config/cst.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3d26aa42..37417428f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OctoBot [0.1.4-beta](https://github.com/Drakkar-Software/OctoBot/tree/dev/docs/CHANGELOG.md) +# OctoBot [0.1.4_2-beta](https://github.com/Drakkar-Software/OctoBot/tree/dev/docs/CHANGELOG.md) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/c83a127c42ba4a389ca86a92fba7c53c)](https://www.codacy.com/app/paul.bouquet/OctoBot?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot&utm_campaign=Badge_Grade) [![Build Status](https://api.travis-ci.org/Drakkar-Software/OctoBot.svg?branch=dev)](https://travis-ci.org/Drakkar-Software/OctoBot) [![Code Factor](https://www.codefactor.io/repository/github/Drakkar-Software/OctoBot/badge)](https://www.codefactor.io/repository/github/Drakkar-Software/OctoBot/overview/dev) [![Build Status](https://semaphoreci.com/api/v1/herklos/octobot/branches/dev/shields_badge.svg)](https://semaphoreci.com/herklos/octobot) [![Coverage Status](https://coveralls.io/repos/github/Drakkar-Software/OctoBot/badge.svg?branch=dev)](https://coveralls.io/github/Drakkar-Software/OctoBot?branch=dev) [![Codefresh build status]( https://g.codefresh.io/api/badges/build?repoOwner=Drakkar-Software&repoName=OctoBot&branch=dev&pipelineName=OctoBot&accountName=herklos_marketplace&type=cf-1)](https://g.codefresh.io/repositories/Drakkar-Software/OctoBot/builds?filter=trigger:build;branch:dev;service:5b06a377435197b088b1757a~OctoBot) [![Build status](https://ci.appveyor.com/api/projects/status/jr9o8sghywnued9x?svg=true)](https://ci.appveyor.com/project/Herklos/octobot)

Octobot Logo diff --git a/config/cst.py b/config/cst.py index 6557aba6b..1b50f3533 100644 --- a/config/cst.py +++ b/config/cst.py @@ -1,7 +1,7 @@ from enum import Enum SHORT_VERSION = "0.1.4" -MINOR_VERSION = "1" +MINOR_VERSION = "2" VERSION_DEV_PHASE = "beta" VERSION = "{0}-{1}".format(SHORT_VERSION, VERSION_DEV_PHASE) LONG_VERSION = "{0}_{1}-{2}".format(SHORT_VERSION, MINOR_VERSION, VERSION_DEV_PHASE)