From 1c9475c415698799a60109ffcc897514977b961d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:07:50 +0200 Subject: [PATCH 01/31] (feat) remove original file explorer --- ui_components/file_explorer.py | 55 ---------------------------------- 1 file changed, 55 deletions(-) diff --git a/ui_components/file_explorer.py b/ui_components/file_explorer.py index f0037061..e69de29b 100644 --- a/ui_components/file_explorer.py +++ b/ui_components/file_explorer.py @@ -1,55 +0,0 @@ -import streamlit as st -from streamlit_elements import mui, elements - -from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ - get_yml_files_from_directory, load_file, remove_file -from .dashboard import Dashboard - - -class FileExplorer(Dashboard.Item): - _directory = "hummingbot_files/bot_configs" - - @staticmethod - def set_selected_file(_, node_id): - st.session_state.selected_file = node_id - - @staticmethod - def delete_file(): - if st.session_state.selected_file: - if st.session_state.selected_file.endswith(".py") or st.session_state.selected_file.endswith(".yml"): - remove_file(st.session_state.selected_file) - else: - st.error("You can't delete the directory since it's a volume." - "If you want to do it, go to the orchestrate tab and delete the container") - - def edit_file(self): - short_path = st.session_state.selected_file.replace(self._directory, "") - language = "python" if st.session_state.selected_file.endswith(".py") else "yaml" - if st.session_state.selected_file.endswith(".py") or st.session_state.selected_file.endswith(".yml"): - st.session_state.editor_tabs[short_path] = {"content": load_file(st.session_state.selected_file), - "language": language, - "file_path": st.session_state.selected_file} - - def __call__(self): - bots = [bot.split("/")[-2] for bot in get_directories_from_directory(self._directory) if - "data_downloader" not in bot] - with mui.Paper(key=self._key, - sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, - elevation=1): - with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): - with mui.Grid(container=True, spacing=4, sx={"display": "flex", "alignItems": "center"}): - with mui.Grid(item=True, xs=6, sx={"display": "flex", "alignItems": "center"}): - mui.icon.Folder() - mui.Typography("/bot_configs/") - with mui.Grid(item=True, xs=6, sx={"display": "flex", "justifyContent": "flex-end"}): - mui.IconButton(mui.icon.Delete, onClick=self.delete_file, sx={"mx": 1}) - mui.IconButton(mui.icon.Edit, onClick=self.edit_file, sx={"mx": 1}) - with mui.Box(sx={"overflow": "auto"}): - with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, - onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): - for bot in bots: - with mui.lab.TreeItem(nodeId=bot, label=f"🤖{bot}"): - for file in get_python_files_from_directory(f"{self._directory}/{bot}/scripts"): - mui.lab.TreeItem(nodeId=file, label=f"🐍{file.split('/')[-1]}") - for file in get_yml_files_from_directory(f"{self._directory}/{bot}/conf/strategies"): - mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") From a3915cbd1f81c781ef0643f13176566077319397 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 17:59:19 +0200 Subject: [PATCH 02/31] (feat) add file explorer base --- ui_components/file_explorer_base.py | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 ui_components/file_explorer_base.py diff --git a/ui_components/file_explorer_base.py b/ui_components/file_explorer_base.py new file mode 100644 index 00000000..6b001cdb --- /dev/null +++ b/ui_components/file_explorer_base.py @@ -0,0 +1,64 @@ +import streamlit as st +from streamlit_elements import mui, elements + +from utils.os_utils import load_file, remove_file +from .dashboard import Dashboard + + +class FileExplorerBase(Dashboard.Item): + + def __init__(self, board, x, y, w, h, directory, **item_props): + super().__init__(board, x, y, w, h, **item_props) + self._directory = directory + self._tabs = {} + self.selected_file = None + + def set_selected_file(self, _, node_id): + self.selected_file = node_id + + def delete_file(self): + if self.is_file_editable: + remove_file(self.selected_file) + else: + st.error("You can't delete the directory since it's a volume." + "If you want to do it, go to the orchestrate tab and delete the container") + + @property + def tabs(self): + return self._tabs + + def add_file_to_tab(self): + language = "python" if self.selected_file.endswith(".py") else "yaml" + if self.is_file_editable: + self._tabs[self.selected_file] = {"content": load_file(self.selected_file), + "language": language} + + def remove_file_from_tab(self): + if self.is_file_editable and self.selected_file in self._tabs: + del self._tabs[self.selected_file] + + @property + def is_file_editable(self): + return self.selected_file and \ + (self.selected_file.endswith(".py") or self.selected_file.endswith(".yml") + or "log" in self.selected_file) + + def add_tree_view(self): + raise NotImplementedError + + def __call__(self): + with mui.Paper(key=self._key, + sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, + elevation=1): + with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): + with mui.Grid(container=True, spacing=4, sx={"display": "flex", "alignItems": "center"}): + with mui.Grid(item=True, xs=6, sx={"display": "flex", "alignItems": "center"}): + mui.icon.Folder() + mui.Typography("File Explorer") + with mui.Grid(item=True, xs=6, sx={"display": "flex", "justifyContent": "flex-end"}): + mui.IconButton(mui.icon.Delete, onClick=self.delete_file, sx={"mx": 1}) + mui.IconButton(mui.icon.Edit, onClick=self.add_file_to_tab, sx={"mx": 1}) + mui.IconButton(mui.icon.Close, onClick=self.remove_file_from_tab, sx={"mx": 1}) + with mui.Box(sx={"overflow": "auto"}): + self.add_tree_view() + From 61483af333cd700930a9a20caf61397f330cc677 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 17:59:28 +0200 Subject: [PATCH 03/31] (feat) add functionality to check logs --- utils/os_utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/os_utils.py b/utils/os_utils.py index 32dc9995..6dbf01ad 100644 --- a/utils/os_utils.py +++ b/utils/os_utils.py @@ -68,6 +68,11 @@ def get_python_files_from_directory(directory: str) -> list: return py_files +def get_log_files_from_directory(directory: str) -> list: + log_files = glob.glob(directory + "/**/*.log*", recursive=True) + return log_files + + def get_yml_files_from_directory(directory: str) -> list: yml = glob.glob(directory + "/**/*.yml", recursive=True) return yml From b55b2a26a7e1d819da7e16e5d8d7d6bca14b768f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 17:59:41 +0200 Subject: [PATCH 04/31] (feat) remove full path for tabs --- ui_components/editor.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ui_components/editor.py b/ui_components/editor.py index 6ba14a1d..be24976a 100644 --- a/ui_components/editor.py +++ b/ui_components/editor.py @@ -26,31 +26,33 @@ def save_file(self): if len(self._tabs) > 0: label = list(self._tabs.keys())[self._index] content = self.get_content(label) - full_path = self.get_file_path(label) - file_name = full_path.split("/")[-1] - path = "/".join(full_path.split("/")[:-1]) + file_name = label.split("/")[-1] + path = "/".join(label.split("/")[:-1]) save_file(name=file_name, content=content, path=path) st.info("File saved") def _change_tab(self, _, index): self._index = index + @property + def tabs(self): + return self._tabs + def update_content(self, label, content): self._tabs[label]["content"] = content - def add_tab(self, label, default_content, language, file_path): + def add_tab(self, label, default_content, language): self._tabs[label] = { "content": default_content, "language": language, - "file_path": file_path } + def remove_tab(self, label): + del self._tabs[label] + def get_content(self, label): return self._tabs[label]["content"] - def get_file_path(self, label): - return self._tabs[label]["file_path"] - def __call__(self): with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): From b0821c5dca5c22ce0b45f963aaa4a1f5595988aa Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 17:59:55 +0200 Subject: [PATCH 05/31] (feat) add specific bot file explorer --- ui_components/bots_file_explorer.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 ui_components/bots_file_explorer.py diff --git a/ui_components/bots_file_explorer.py b/ui_components/bots_file_explorer.py new file mode 100644 index 00000000..92c75c0c --- /dev/null +++ b/ui_components/bots_file_explorer.py @@ -0,0 +1,24 @@ +from streamlit_elements import mui + +from ui_components.file_explorer_base import FileExplorerBase +from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ + get_yml_files_from_directory, get_log_files_from_directory + + +class BotsFileExplorer(FileExplorerBase): + def add_tree_view(self): + bots = [bot.split("/")[-2] for bot in get_directories_from_directory(self._directory) if + "data_downloader" not in bot] + with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, + onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): + for bot in bots: + with mui.lab.TreeItem(nodeId=bot, label=f"🤖{bot}"): + with mui.lab.TreeItem(nodeId=f"scripts_{bot}", label="🐍Scripts"): + for file in get_python_files_from_directory(f"{self._directory}/{bot}/scripts"): + mui.lab.TreeItem(nodeId=file, label=f"📄{file.split('/')[-1]}") + with mui.lab.TreeItem(nodeId=f"strategies_{bot}", label="📜Strategies"): + for file in get_yml_files_from_directory(f"{self._directory}/{bot}/conf/strategies"): + mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") + with mui.lab.TreeItem(nodeId=f"logs_{bot}", label="🗄️Logs"): + for file in get_log_files_from_directory(f"{self._directory}/{bot}/logs"): + mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") From 954a7109e78cf40e2cbbdc043b09c1bcee886550 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:10:42 +0200 Subject: [PATCH 06/31] (feat) refactor bot_orchestration --- pages/bot_orchestration/app.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index f40a09f3..6479fa0b 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -1,4 +1,3 @@ -from glob import glob from types import SimpleNamespace from commlib.exceptions import RPCClientTimeoutError @@ -12,7 +11,9 @@ from hbotrc import BotCommands from ui_components.bot_performance_card import BotPerformanceCard +from ui_components.bots_file_explorer import BotsFileExplorer from ui_components.dashboard import Dashboard +from ui_components.editor import Editor from ui_components.exited_bot_card import ExitedBotCard from utils.st_utils import initialize_st_page @@ -33,6 +34,10 @@ if "selected_strategy" not in st.session_state: st.session_state.selected_strategy = None +if "editor_tabs" not in st.session_state: + st.session_state.editor_tabs = {} + + def manage_broker_container(): if st.session_state.is_broker_running: docker_manager.stop_container("hummingbot-broker") From 26291607c89a5259aefccad1be915906561a3209 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 18:18:19 +0200 Subject: [PATCH 07/31] (feat) remove directory from constructor --- ui_components/file_explorer_base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui_components/file_explorer_base.py b/ui_components/file_explorer_base.py index 6b001cdb..066b5ced 100644 --- a/ui_components/file_explorer_base.py +++ b/ui_components/file_explorer_base.py @@ -7,9 +7,8 @@ class FileExplorerBase(Dashboard.Item): - def __init__(self, board, x, y, w, h, directory, **item_props): + def __init__(self, board, x, y, w, h, **item_props): super().__init__(board, x, y, w, h, **item_props) - self._directory = directory self._tabs = {} self.selected_file = None From 76acf6735db6259f3715fc692fcb1b06d2be0e7b Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 18:18:28 +0200 Subject: [PATCH 08/31] (feat) use directory from constants --- ui_components/bots_file_explorer.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui_components/bots_file_explorer.py b/ui_components/bots_file_explorer.py index 92c75c0c..6b9da2ec 100644 --- a/ui_components/bots_file_explorer.py +++ b/ui_components/bots_file_explorer.py @@ -1,5 +1,6 @@ from streamlit_elements import mui +import constants from ui_components.file_explorer_base import FileExplorerBase from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ get_yml_files_from_directory, get_log_files_from_directory @@ -7,18 +8,19 @@ class BotsFileExplorer(FileExplorerBase): def add_tree_view(self): - bots = [bot.split("/")[-2] for bot in get_directories_from_directory(self._directory) if + directory = constants.BOTS_FOLDER + bots = [bot.split("/")[-2] for bot in get_directories_from_directory(directory) if "data_downloader" not in bot] with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): for bot in bots: with mui.lab.TreeItem(nodeId=bot, label=f"🤖{bot}"): with mui.lab.TreeItem(nodeId=f"scripts_{bot}", label="🐍Scripts"): - for file in get_python_files_from_directory(f"{self._directory}/{bot}/scripts"): + for file in get_python_files_from_directory(f"{directory}/{bot}/scripts"): mui.lab.TreeItem(nodeId=file, label=f"📄{file.split('/')[-1]}") with mui.lab.TreeItem(nodeId=f"strategies_{bot}", label="📜Strategies"): - for file in get_yml_files_from_directory(f"{self._directory}/{bot}/conf/strategies"): + for file in get_yml_files_from_directory(f"{directory}/{bot}/conf/strategies"): mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") with mui.lab.TreeItem(nodeId=f"logs_{bot}", label="🗄️Logs"): - for file in get_log_files_from_directory(f"{self._directory}/{bot}/logs"): + for file in get_log_files_from_directory(f"{directory}/{bot}/logs"): mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") From b83cc8571637ec97f2655aa464cf57af2d81ad75 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 18:18:39 +0200 Subject: [PATCH 09/31] (feat) create directional strat file explorer --- .../directional_strategies_file_explorer.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ui_components/directional_strategies_file_explorer.py diff --git a/ui_components/directional_strategies_file_explorer.py b/ui_components/directional_strategies_file_explorer.py new file mode 100644 index 00000000..9f8f4363 --- /dev/null +++ b/ui_components/directional_strategies_file_explorer.py @@ -0,0 +1,15 @@ +from streamlit_elements import mui + +import constants +from ui_components.file_explorer_base import FileExplorerBase +from utils.os_utils import get_python_files_from_directory + + +class DirectionalStrategiesFileExplorer(FileExplorerBase): + def add_tree_view(self): + with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, + onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): + with mui.lab.TreeItem(nodeId="directional_strategies", label=f"⚔️Directional Strategies"): + strategies = get_python_files_from_directory(constants.DIRECTIONAL_STRATEGIES_PATH) + for strategy in strategies: + mui.lab.TreeItem(nodeId=strategy, label=f"🐍{strategy.split('/')[-1]}") From 94cee869674db0869fa970bb61d7cb5caf6ed30a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:11:15 +0200 Subject: [PATCH 10/31] (feat) refactor bot_orchestration --- pages/bot_orchestration/app.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index 6479fa0b..65f79442 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -180,3 +180,35 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int with exited_instances_board(): for bot, card in st.session_state.exited_bots.items(): card(bot) + + +with manage: + if "w" not in st.session_state: + board = Dashboard() + w = SimpleNamespace( + dashboard=board, + file_explorer=BotsFileExplorer(board, 0, 0, 3, 7, constants.BOTS_FOLDER), + editor=Editor(board, 4, 0, 9, 7), + ) + st.session_state.w = w + + else: + w = st.session_state.w + + # Add new tabs + for tab_name, content in w.file_explorer.tabs.items(): + if tab_name not in w.editor.tabs: + w.editor.add_tab(tab_name, content["content"], content["language"]) + + # Remove deleted tabs + for tab_name in list(w.editor.tabs.keys()): + if tab_name not in w.file_explorer.tabs: + w.editor.remove_tab(tab_name) + + with elements("bot_config"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + mui.Typography("🗂Files Management", variant="h3", sx={"margin-bottom": "2rem"}) + event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True) + with w.dashboard(): + w.file_explorer() + w.editor() From 3bf274511a78f56d987c8daa7de76dd077845e35 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 18:19:11 +0200 Subject: [PATCH 11/31] (feat) unify create and modify tabs --- pages/backtest_manager/app.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pages/backtest_manager/app.py b/pages/backtest_manager/app.py index 583e0d6d..a01709d6 100644 --- a/pages/backtest_manager/app.py +++ b/pages/backtest_manager/app.py @@ -21,9 +21,9 @@ if "strategy_params" not in st.session_state: st.session_state.strategy_params = {} -create, modify, backtest, optimize, analyze = st.tabs(["Create", "Modify", "Backtest", "Optimize", "Analyze"]) +build, backtest, optimize, analyze = st.tabs(["Build", "Backtest", "Optimize", "Analyze"]) -with create: +with build: # TODO: # * Add videos explaining how to the triple barrier method works and how the backtesting is designed, # link to video of how to create a strategy, etc in a toggle. @@ -51,9 +51,6 @@ path=constants.DIRECTIONAL_STRATEGIES_PATH) st.success(f"Strategy {strategy_name} saved successfully") -with modify: - pass - with backtest: # TODO: # * Add videos explaining how to the triple barrier method works and how the backtesting is designed, From 587cb2be1433d5e0e34adaa331d6b524722f26e3 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 3 Aug 2023 18:24:57 +0200 Subject: [PATCH 12/31] (feat) move hotkey to editor --- pages/bot_orchestration/app.py | 1 - ui_components/editor.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index 65f79442..1c97f159 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -208,7 +208,6 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int with elements("bot_config"): with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): mui.Typography("🗂Files Management", variant="h3", sx={"margin-bottom": "2rem"}) - event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True) with w.dashboard(): w.file_explorer() w.editor() diff --git a/ui_components/editor.py b/ui_components/editor.py index be24976a..5e825ede 100644 --- a/ui_components/editor.py +++ b/ui_components/editor.py @@ -1,6 +1,6 @@ from functools import partial import streamlit as st -from streamlit_elements import mui, editor, sync, lazy +from streamlit_elements import mui, editor, sync, lazy, event from utils.os_utils import save_file from .dashboard import Dashboard @@ -87,4 +87,5 @@ def __call__(self): with mui.Stack(direction="row", spacing=2, alignItems="center", sx={"padding": "10px"}): mui.Button("Apply", variant="contained", onClick=sync()) + event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True) mui.Typography("Or press ctrl+s", sx={"flex": 1}) From aa97cf658d0eafc2bc12502214dc49e6d4a641d0 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:05:11 +0200 Subject: [PATCH 13/31] (feat) add optimizations file explorer card --- ui_components/optimizations_file_explorer.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ui_components/optimizations_file_explorer.py diff --git a/ui_components/optimizations_file_explorer.py b/ui_components/optimizations_file_explorer.py new file mode 100644 index 00000000..8c7c6059 --- /dev/null +++ b/ui_components/optimizations_file_explorer.py @@ -0,0 +1,15 @@ +from streamlit_elements import mui + +import constants +from ui_components.file_explorer_base import FileExplorerBase +from utils.os_utils import get_python_files_from_directory + + +class OptimizationsStrategiesFileExplorer(FileExplorerBase): + def add_tree_view(self): + with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, + onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): + with mui.lab.TreeItem(nodeId="optimization_strategies", label=f"🔬Optimizations"): + optimizations = get_python_files_from_directory(constants.OPTIMIZATIONS_PATH) + for optimization in optimizations: + mui.lab.TreeItem(nodeId=optimization, label=f"🐍{optimization.split('/')[-1]}") From ce8316851484171ab825e577881fc523cc10b35e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:05:32 +0200 Subject: [PATCH 14/31] (feat) add optimizations run card --- ui_components/optimization_run_card.py | 59 ++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ui_components/optimization_run_card.py diff --git a/ui_components/optimization_run_card.py b/ui_components/optimization_run_card.py new file mode 100644 index 00000000..435a9ba5 --- /dev/null +++ b/ui_components/optimization_run_card.py @@ -0,0 +1,59 @@ +import threading + +import optuna +from streamlit_elements import mui, lazy + +import constants +from utils.os_utils import get_python_files_from_directory, \ + get_function_from_file +from .dashboard import Dashboard + + +class OptimizationRunCard(Dashboard.Item): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._optimization_name = None + self._number_of_trials = 2000 + + def _set_optimization_name(self, _, childs): + self._optimization_name = childs.props.value + + def _set_number_of_trials(self, event): + self._number_of_trials = int(event.target.value) + + def _run_optimization(self): + study_name = self._optimization_name.split('/')[-1].split('.')[0] + study = optuna.create_study(direction="maximize", study_name=study_name, + storage="sqlite:///data/backtesting/backtesting_report.db", + load_if_exists=True) + objective = get_function_from_file(file_path=self._optimization_name, + function_name="objective") + + def optimization_process(): + study.optimize(objective, n_trials=self._number_of_trials) + + optimization_thread = threading.Thread(target=optimization_process) + optimization_thread.start() + + def __call__(self): + optimizations = get_python_files_from_directory(constants.OPTIMIZATIONS_PATH) + with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): + with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): + mui.icon.AutoFixHigh() + mui.Typography("Run a optimization", variant="h6") + + with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): + if len(optimizations) == 0: + mui.Alert("No optimizations available, please create one.", severity="warning", sx={"width": "100%"}) + return + else: + if self._optimization_name is None: + self._optimization_name = optimizations[0] + with mui.Select(label="Select strategy", defaultValue=optimizations[0], + variant="standard", onChange=lazy(self._set_optimization_name)): + for optimization in optimizations: + mui.MenuItem(f"{optimization.split('/')[-1].split('.')[0]}", value=optimization) + mui.TextField(defaultValue=self._optimization_name, label="Number of trials", type="number", + variant="standard", onChange=lazy(self._set_number_of_trials)) + mui.IconButton(mui.icon.PlayCircleFilled, sx={"color": "primary.main"}, + onClick=self._run_optimization) From 0a834ee0f1f627f1685c9fc5fac9a1a19c4902f8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:05:44 +0200 Subject: [PATCH 15/31] (feat) add optimizations creation card --- ui_components/optimization_creation_card.py | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ui_components/optimization_creation_card.py diff --git a/ui_components/optimization_creation_card.py b/ui_components/optimization_creation_card.py new file mode 100644 index 00000000..923f5a90 --- /dev/null +++ b/ui_components/optimization_creation_card.py @@ -0,0 +1,51 @@ +from streamlit_elements import mui, lazy +import datetime + +import constants +from utils.file_templates import strategy_optimization_template +from utils.os_utils import save_file, load_directional_strategies +from .dashboard import Dashboard + + +class OptimizationCreationCard(Dashboard.Item): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + today = datetime.datetime.now() + self._optimization_version = f"{today.day:02d}-{today.month:02d}-{today.year}" + self._optimization_name = None + self._strategy_name = None + + def _set_optimization_version(self, event): + self._optimization_version = event.target.value + + def _set_strategy_name(self, _, childs): + self._strategy_name = childs.props.value + + def _create_optimization(self, strategy_info): + strategy_code = strategy_optimization_template(strategy_info) + save_file(name=f"{self._strategy_name.lower()}_v_{self._optimization_version}.py", content=strategy_code, + path=constants.OPTIMIZATIONS_PATH) + + def __call__(self): + available_strategies = load_directional_strategies(constants.DIRECTIONAL_STRATEGIES_PATH) + strategy_names = list(available_strategies.keys()) + with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): + with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): + mui.icon.NoteAdd() + mui.Typography("Create a new optimization", variant="h6") + + with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): + if len(strategy_names) == 0: + mui.Alert("No strategies available, please create one to optimize it", severity="warning", sx={"width": "100%"}) + return + else: + if self._strategy_name is None: + self._strategy_name = strategy_names[0] + with mui.Select(label="Select strategy", defaultValue=strategy_names[0], + variant="standard", onChange=lazy(self._set_strategy_name)): + for strategy in strategy_names: + mui.MenuItem(strategy, value=strategy) + mui.TextField(defaultValue=self._optimization_version, label="Optimization version", + variant="standard", onChange=lazy(self._set_optimization_version)) + mui.IconButton(mui.icon.AddCircle, sx={"color": "primary.main"}, + onClick=lambda x: self._create_optimization(available_strategies[self._strategy_name])) From 55c0fa3b242542f1ae47b568846aa7aebe6f89c9 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:05:54 +0200 Subject: [PATCH 16/31] (feat) add config to user attrs --- utils/file_templates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/file_templates.py b/utils/file_templates.py index ff8be847..d4d55fcf 100644 --- a/utils/file_templates.py +++ b/utils/file_templates.py @@ -111,6 +111,7 @@ def objective(trial): trial.set_user_attr("profit_factor", strategy_analysis.profit_factor()) trial.set_user_attr("duration_in_hours", strategy_analysis.duration_in_minutes() / 60) trial.set_user_attr("avg_trading_time_in_hours", strategy_analysis.avg_trading_time_in_minutes() / 60) + trial.set_user_attr("config", config.dict()) return strategy_analysis.net_profit_pct() except Exception as e: traceback.print_exc() From ae5a556ab8de6b024851024bd5c6b5274557018e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:06:09 +0200 Subject: [PATCH 17/31] (feat) add directional strategy creation card --- .../directional_strategy_creation_card.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 ui_components/directional_strategy_creation_card.py diff --git a/ui_components/directional_strategy_creation_card.py b/ui_components/directional_strategy_creation_card.py new file mode 100644 index 00000000..03f978bc --- /dev/null +++ b/ui_components/directional_strategy_creation_card.py @@ -0,0 +1,39 @@ +from streamlit_elements import mui, lazy + +import constants +from utils.file_templates import directional_strategy_template +from utils.os_utils import save_file +from .dashboard import Dashboard + + +class DirectionalStrategyCreationCard(Dashboard.Item): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._strategy_name = "CustomStrategy" + self._strategy_type = "directional" + + def _set_strategy_name(self, event): + self._strategy_name = event.target.value + + def _set_strategy_type(self, _, childs): + self._strategy_type = childs.props.value + + def _create_strategy(self): + if self._strategy_type == "directional": + strategy_code = directional_strategy_template(self._strategy_name) + save_file(name=f"{self._strategy_name.lower()}.py", content=strategy_code, + path=constants.DIRECTIONAL_STRATEGIES_PATH) + + def __call__(self): + with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): + with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): + mui.icon.NoteAdd() + mui.Typography("Create new strategy", variant="h6") + + with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): + with mui.Select(label="Select strategy type", defaultValue="directional", + variant="standard", onChange=lazy(self._set_strategy_type)): + mui.MenuItem("Directional", value="directional") + mui.TextField(defaultValue="CustomStrategy", label="Strategy Name", + variant="standard", onChange=lazy(self._set_strategy_name)) + mui.IconButton(mui.icon.AddCircle, onClick=self._create_strategy, sx={"color": "primary.main"}) From 6cbd877f1b8cc030e4e557798424ee606385c3d5 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 15:06:38 +0200 Subject: [PATCH 18/31] (feat) refactor backtest manager tab --- pages/backtest_manager/app.py | 161 +++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/pages/backtest_manager/app.py b/pages/backtest_manager/app.py index a01709d6..b39a7bda 100644 --- a/pages/backtest_manager/app.py +++ b/pages/backtest_manager/app.py @@ -1,21 +1,26 @@ -import datetime -import threading +import time import webbrowser +from types import SimpleNamespace import streamlit as st -from streamlit_ace import st_ace +from streamlit_elements import elements, mui import constants from quants_lab.strategy.strategy_analysis import StrategyAnalysis +from ui_components.dashboard import Dashboard +from ui_components.directional_strategies_file_explorer import DirectionalStrategiesFileExplorer +from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard +from ui_components.editor import Editor +from ui_components.optimization_creation_card import OptimizationCreationCard +from ui_components.optimization_run_card import OptimizationRunCard +from ui_components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer from utils import os_utils -from utils.file_templates import strategy_optimization_template, directional_strategy_template -from utils.os_utils import load_directional_strategies, save_file, get_function_from_file -import optuna +from utils.os_utils import load_directional_strategies from utils.st_utils import initialize_st_page -initialize_st_page(title="Backtest Manager", icon="⚙️") +initialize_st_page(title="Backtest Manager", icon="⚙️", initial_sidebar_state="collapsed") # Start content here if "strategy_params" not in st.session_state: @@ -28,28 +33,37 @@ # * Add videos explaining how to the triple barrier method works and how the backtesting is designed, # link to video of how to create a strategy, etc in a toggle. # * Add functionality to start strategy creation from scratch or by duplicating an existing one - c1, c2 = st.columns([4, 1]) - with c1: - # TODO: Allow change the strategy name and see the effect in the code - strategy_name = st.text_input("Strategy class name", value="CustomStrategy") - with c2: - update_strategy_name = st.button("Update Strategy Name") - c1, c2 = st.columns([4, 1]) - with c1: - # TODO: every time that we save and run the optimizations, we should save the code in a file - # so the user then can correlate the results with the code. - if update_strategy_name: - st.session_state.directional_strategy_code = st_ace(key="create_directional_strategy", - value=directional_strategy_template(strategy_name), - language='python', - keybinding='vscode', - theme='pastel_on_dark') - with c2: - if st.button("Save strategy"): - save_file(name=f"{strategy_name.lower()}.py", content=st.session_state.directional_strategy_code, - path=constants.DIRECTIONAL_STRATEGIES_PATH) - st.success(f"Strategy {strategy_name} saved successfully") + if "ds_board" not in st.session_state: + board = Dashboard() + ds_board = SimpleNamespace( + dashboard=board, + create_strategy_card=DirectionalStrategyCreationCard(board, 0, 0, 3, 1), + file_explorer=DirectionalStrategiesFileExplorer(board, 0, 2, 3, 7), + editor=Editor(board, 4, 2, 9, 7), + ) + st.session_state.ds_board = ds_board + + else: + ds_board = st.session_state.ds_board + + # Add new tabs + for tab_name, content in ds_board.file_explorer.tabs.items(): + if tab_name not in ds_board.editor.tabs: + ds_board.editor.add_tab(tab_name, content["content"], content["language"]) + + # Remove deleted tabs + for tab_name in list(ds_board.editor.tabs.keys()): + if tab_name not in ds_board.file_explorer.tabs: + ds_board.editor.remove_tab(tab_name) + + with elements("directional_strategies"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + mui.Typography("🗂Directional Strategies", variant="h3", sx={"margin-bottom": "2rem"}) + with ds_board.dashboard(): + ds_board.create_strategy_card() + ds_board.file_explorer() + ds_board.editor() with backtest: # TODO: @@ -117,50 +131,53 @@ with optimize: # TODO: # * Add videos explaining how to use the optimization tool, quick intro to optuna, etc in a toggle - with st.container(): - c1, c2, c3 = st.columns([1, 1, 1]) - with c1: - strategies = load_directional_strategies(constants.DIRECTIONAL_STRATEGIES_PATH) - strategy_to_optimize = st.selectbox("Select strategy to optimize", strategies.keys()) - with c2: - today = datetime.datetime.today() - # TODO: add hints about the study name - STUDY_NAME = st.text_input("Study name", - f"{strategy_to_optimize}_study_{today.day:02d}-{today.month:02d}-{today.year}") - with c3: - generate_optimization_code_button = st.button("Generate Optimization Code") - if st.button("Launch optuna dashboard"): - os_utils.execute_bash_command(f"optuna-dashboard sqlite:///data/backtesting/backtesting_report.db") - webbrowser.open("http://127.0.0.1:8080/dashboard", new=2) - - c1, c2 = st.columns([4, 1]) - if generate_optimization_code_button: - with c1: - # TODO: every time that we save and run the optimizations, we should save the code in a file - # so the user then can correlate the results with the code. - st.session_state.optimization_code = st_ace(key="create_optimization_code", - value=strategy_optimization_template( - strategy_info=strategies[strategy_to_optimize]), - language='python', - keybinding='vscode', - theme='pastel_on_dark') - if "optimization_code" in st.session_state: - with c2: - if st.button("Run optimization"): - save_file(name=f"{STUDY_NAME}.py", content=st.session_state.optimization_code, path=constants.OPTIMIZATIONS_PATH) - study = optuna.create_study(direction="maximize", study_name=STUDY_NAME, - storage="sqlite:///data/backtesting/backtesting_report.db", - load_if_exists=True) - objective = get_function_from_file(file_path=f"{constants.OPTIMIZATIONS_PATH}/{STUDY_NAME}.py", - function_name="objective") - - - def optimization_process(): - study.optimize(objective, n_trials=2000) - - - optimization_thread = threading.Thread(target=optimization_process) - optimization_thread.start() + + def run_optuna_dashboard(): + os_utils.execute_bash_command(f"optuna-dashboard sqlite:///data/backtesting/backtesting_report.db") + time.sleep(5) + webbrowser.open("http://127.0.0.1:8080/dashboard", new=2) + + if "op_board" not in st.session_state: + board = Dashboard() + op_board = SimpleNamespace( + dashboard=board, + run_optimization_card=OptimizationRunCard(board, 0, 0, 3, 1), + create_optimization_card=OptimizationCreationCard(board, 0, 1, 3, 1), + file_explorer=OptimizationsStrategiesFileExplorer(board, 0, 2, 3, 5), + editor=Editor(board, 4, 2, 9, 7), + ) + st.session_state.op_board = op_board + + else: + op_board = st.session_state.op_board + + # Add new tabs + for tab_name, content in op_board.file_explorer.tabs.items(): + if tab_name not in op_board.editor.tabs: + op_board.editor.add_tab(tab_name, content["content"], content["language"]) + + # Remove deleted tabs + for tab_name in list(op_board.editor.tabs.keys()): + if tab_name not in op_board.file_explorer.tabs: + op_board.editor.remove_tab(tab_name) + + with elements("optimizations"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with mui.Grid(container=True, spacing=2): + with mui.Grid(item=True, xs=10): + mui.Typography("🧪Optimizations", variant="h3", sx={"margin-bottom": "2rem"}) + with mui.Grid(item=True, xs=2): + with mui.Button(variant="outlined", color="primary", size="large", + sx={"height": "100%", "width": "100%"}, onClick=run_optuna_dashboard): + mui.icon.AutoGraph() + mui.Typography("Run Optuna Dashboard", variant="body1") + + + with op_board.dashboard(): + op_board.run_optimization_card() + op_board.create_optimization_card() + op_board.file_explorer() + op_board.editor() with analyze: From e57cf1b092bf2987665a728e70cea08b07024ad1 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:38:21 +0200 Subject: [PATCH 19/31] (feat) remove commented line --- utils/st_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/st_utils.py b/utils/st_utils.py index 52ff9518..5875109c 100644 --- a/utils/st_utils.py +++ b/utils/st_utils.py @@ -13,7 +13,6 @@ def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_sta layout=layout, initial_sidebar_state=initial_sidebar_state ) - # st.title(f"{icon} {title}") caller_frame = inspect.currentframe().f_back add_page_title(layout="wide") From 4b68f2c1ab4f404c135f5d0864c0ac5a0e8ebc61 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:38:30 +0200 Subject: [PATCH 20/31] (feat) add simulate page --- pages/backtest_manager/simulate.py | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 pages/backtest_manager/simulate.py diff --git a/pages/backtest_manager/simulate.py b/pages/backtest_manager/simulate.py new file mode 100644 index 00000000..260cbf5b --- /dev/null +++ b/pages/backtest_manager/simulate.py @@ -0,0 +1,90 @@ +import time +import webbrowser +from types import SimpleNamespace + +import streamlit as st +from streamlit_elements import elements, mui + +import constants +from quants_lab.strategy.strategy_analysis import StrategyAnalysis +from ui_components.dashboard import Dashboard +from ui_components.directional_strategies_file_explorer import DirectionalStrategiesFileExplorer +from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard +from ui_components.editor import Editor +from ui_components.optimization_creation_card import OptimizationCreationCard +from ui_components.optimization_run_card import OptimizationRunCard +from ui_components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer +from utils import os_utils +from utils.os_utils import load_directional_strategies + +from utils.st_utils import initialize_st_page + + +initialize_st_page(title="Simulate", icon="📈", initial_sidebar_state="collapsed") + +# Start content here +if "strategy_params" not in st.session_state: + st.session_state.strategy_params = {} + + +# TODO: +# * Add videos explaining how to the triple barrier method works and how the backtesting is designed, +# link to video of how to create a strategy, etc in a toggle. +# * Add performance analysis graphs of the backtesting run +strategies = load_directional_strategies(constants.DIRECTIONAL_STRATEGIES_PATH) +strategy_to_optimize = st.selectbox("Select strategy to backtest", strategies.keys()) +strategy = strategies[strategy_to_optimize] +strategy_config = strategy["config"] +field_schema = strategy_config.schema()["properties"] +st.write("## Strategy parameters") +c1, c2 = st.columns([5, 1]) +with c1: + columns = st.columns(4) + column_index = 0 + for field_name, properties in field_schema.items(): + field_type = properties["type"] + with columns[column_index]: + if field_type in ["number", "integer"]: + field_value = st.number_input(field_name, + value=properties["default"], + min_value=properties.get("minimum"), + max_value=properties.get("maximum"), + key=field_name) + elif field_type == "string": + field_value = st.text_input(field_name, value=properties["default"]) + elif field_type == "boolean": + # TODO: Add support for boolean fields in optimize tab + field_value = st.checkbox(field_name, value=properties["default"]) + else: + raise ValueError(f"Field type {field_type} not supported") + st.session_state["strategy_params"][field_name] = field_value + column_index = (column_index + 1) % 4 +with c2: + add_positions = st.checkbox("Add positions", value=True) + add_volume = st.checkbox("Add volume", value=True) + add_pnl = st.checkbox("Add PnL", value=True) + + run_backtesting_button = st.button("Run Backtesting!") +if run_backtesting_button: + config = strategy["config"](**st.session_state["strategy_params"]) + strategy = strategy["class"](config=config) + # TODO: add form for order amount, leverage, tp, sl, etc. + + market_data, positions = strategy.run_backtesting( + start='2021-04-01', + order_amount=50, + leverage=20, + initial_portfolio=100, + take_profit_multiplier=2.3, + stop_loss_multiplier=1.2, + time_limit=60 * 60 * 3, + std_span=None, + ) + strategy_analysis = StrategyAnalysis( + positions=positions, + candles_df=market_data, + ) + st.text(strategy_analysis.text_report()) + # TODO: check why the pnl is not being plotted + strategy_analysis.create_base_figure(volume=add_volume, positions=add_positions, trade_pnl=add_pnl) + st.plotly_chart(strategy_analysis.figure(), use_container_width=True) From bcb23be8a387736bd5416e91ff0ec4012baefe4f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:38:34 +0200 Subject: [PATCH 21/31] (feat) add optimize page --- pages/backtest_manager/optimize.py | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 pages/backtest_manager/optimize.py diff --git a/pages/backtest_manager/optimize.py b/pages/backtest_manager/optimize.py new file mode 100644 index 00000000..1c7f4965 --- /dev/null +++ b/pages/backtest_manager/optimize.py @@ -0,0 +1,68 @@ +import time +import webbrowser +from types import SimpleNamespace + +import streamlit as st +from streamlit_elements import elements, mui + +import constants +from quants_lab.strategy.strategy_analysis import StrategyAnalysis +from ui_components.dashboard import Dashboard +from ui_components.directional_strategies_file_explorer import DirectionalStrategiesFileExplorer +from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard +from ui_components.editor import Editor +from ui_components.optimization_creation_card import OptimizationCreationCard +from ui_components.optimization_run_card import OptimizationRunCard +from ui_components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer +from utils import os_utils +from utils.os_utils import load_directional_strategies + +from utils.st_utils import initialize_st_page + +initialize_st_page(title="Optimize", icon="🧪", initial_sidebar_state="collapsed") + + +def run_optuna_dashboard(): + os_utils.execute_bash_command(f"optuna-dashboard sqlite:///data/backtesting/backtesting_report.db") + time.sleep(5) + webbrowser.open("http://127.0.0.1:8080/dashboard", new=2) + + +if "op_board" not in st.session_state: + board = Dashboard() + op_board = SimpleNamespace( + dashboard=board, + create_optimization_card=OptimizationCreationCard(board, 0, 0, 6, 1), + run_optimization_card=OptimizationRunCard(board, 6, 0, 6, 1), + file_explorer=OptimizationsStrategiesFileExplorer(board, 0, 2, 3, 7), + editor=Editor(board, 4, 2, 9, 7), + ) + st.session_state.op_board = op_board + +else: + op_board = st.session_state.op_board + +# Add new tabs +for tab_name, content in op_board.file_explorer.tabs.items(): + if tab_name not in op_board.editor.tabs: + op_board.editor.add_tab(tab_name, content["content"], content["language"]) + +# Remove deleted tabs +for tab_name in list(op_board.editor.tabs.keys()): + if tab_name not in op_board.file_explorer.tabs: + op_board.editor.remove_tab(tab_name) + +with elements("optimizations"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with mui.Grid(container=True, spacing=2): + with mui.Grid(item=True, xs=10): + pass + with mui.Grid(item=True, xs=2): + with mui.Fab(variant="extended", color="primary", size="large", onClick=run_optuna_dashboard): + mui.Typography("Open Optuna Dashboard", variant="body1") + + with op_board.dashboard(): + op_board.create_optimization_card() + op_board.run_optimization_card() + op_board.file_explorer() + op_board.editor() From d2ab10c58be4c902e571b66f7d3cbafd2a787aa8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:38:46 +0200 Subject: [PATCH 22/31] (feat) remove old file explorer --- ui_components/file_explorer.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ui_components/file_explorer.py diff --git a/ui_components/file_explorer.py b/ui_components/file_explorer.py deleted file mode 100644 index e69de29b..00000000 From 8f2a0e674e0ad6dbfd7d8ccd0cd5869cf5601ed1 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:02 +0200 Subject: [PATCH 23/31] (feat) add specific file explorers --- ui_components/directional_strategies_file_explorer.py | 3 ++- ui_components/optimizations_file_explorer.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui_components/directional_strategies_file_explorer.py b/ui_components/directional_strategies_file_explorer.py index 9f8f4363..80739724 100644 --- a/ui_components/directional_strategies_file_explorer.py +++ b/ui_components/directional_strategies_file_explorer.py @@ -8,7 +8,8 @@ class DirectionalStrategiesFileExplorer(FileExplorerBase): def add_tree_view(self): with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, - onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): + onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id), + defaultExpanded=["directional_strategies"]): with mui.lab.TreeItem(nodeId="directional_strategies", label=f"⚔️Directional Strategies"): strategies = get_python_files_from_directory(constants.DIRECTIONAL_STRATEGIES_PATH) for strategy in strategies: diff --git a/ui_components/optimizations_file_explorer.py b/ui_components/optimizations_file_explorer.py index 8c7c6059..c0354ab4 100644 --- a/ui_components/optimizations_file_explorer.py +++ b/ui_components/optimizations_file_explorer.py @@ -8,8 +8,9 @@ class OptimizationsStrategiesFileExplorer(FileExplorerBase): def add_tree_view(self): with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, - onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id)): - with mui.lab.TreeItem(nodeId="optimization_strategies", label=f"🔬Optimizations"): + onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id), + defaultExpanded=["optimization_strategies"]): + with mui.lab.TreeItem(nodeId="optimization_strategies", label=f"🔬Studies"): optimizations = get_python_files_from_directory(constants.OPTIMIZATIONS_PATH) for optimization in optimizations: mui.lab.TreeItem(nodeId=optimization, label=f"🐍{optimization.split('/')[-1]}") From 426ffdbfd0c37659071fc75fefb608e8ba34f39a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:10 +0200 Subject: [PATCH 24/31] (feat) add creation cards --- .../directional_strategy_creation_card.py | 22 ++++++--- ui_components/optimization_creation_card.py | 46 +++++++++++-------- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/ui_components/directional_strategy_creation_card.py b/ui_components/directional_strategy_creation_card.py index 03f978bc..cc42777e 100644 --- a/ui_components/directional_strategy_creation_card.py +++ b/ui_components/directional_strategy_creation_card.py @@ -30,10 +30,18 @@ def __call__(self): mui.icon.NoteAdd() mui.Typography("Create new strategy", variant="h6") - with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): - with mui.Select(label="Select strategy type", defaultValue="directional", - variant="standard", onChange=lazy(self._set_strategy_type)): - mui.MenuItem("Directional", value="directional") - mui.TextField(defaultValue="CustomStrategy", label="Strategy Name", - variant="standard", onChange=lazy(self._set_strategy_name)) - mui.IconButton(mui.icon.AddCircle, onClick=self._create_strategy, sx={"color": "primary.main"}) + with mui.Grid(container=True, spacing=2, sx={"padding": "10px"}): + with mui.Grid(item=True, xs=5): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.FormHelperText("Template name") + with mui.Select(label="Select strategy type", defaultValue="directional", + variant="standard", onChange=lazy(self._set_strategy_type)): + mui.MenuItem("Directional", value="directional") + with mui.Grid(item=True, xs=5): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.TextField(defaultValue="CustomStrategy", label="Strategy Name", + variant="standard", onChange=lazy(self._set_strategy_name)) + with mui.Grid(item=True, xs=2): + with mui.Button(variant="contained", onClick=self._create_strategy): + mui.icon.Add() + mui.Typography("Create", variant="body1") diff --git a/ui_components/optimization_creation_card.py b/ui_components/optimization_creation_card.py index 923f5a90..d2247618 100644 --- a/ui_components/optimization_creation_card.py +++ b/ui_components/optimization_creation_card.py @@ -29,23 +29,33 @@ def _create_optimization(self, strategy_info): def __call__(self): available_strategies = load_directional_strategies(constants.DIRECTIONAL_STRATEGIES_PATH) strategy_names = list(available_strategies.keys()) - with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): + with mui.Paper(key=self._key, + sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, + elevation=1): with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): mui.icon.NoteAdd() - mui.Typography("Create a new optimization", variant="h6") - - with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): - if len(strategy_names) == 0: - mui.Alert("No strategies available, please create one to optimize it", severity="warning", sx={"width": "100%"}) - return - else: - if self._strategy_name is None: - self._strategy_name = strategy_names[0] - with mui.Select(label="Select strategy", defaultValue=strategy_names[0], - variant="standard", onChange=lazy(self._set_strategy_name)): - for strategy in strategy_names: - mui.MenuItem(strategy, value=strategy) - mui.TextField(defaultValue=self._optimization_version, label="Optimization version", - variant="standard", onChange=lazy(self._set_optimization_version)) - mui.IconButton(mui.icon.AddCircle, sx={"color": "primary.main"}, - onClick=lambda x: self._create_optimization(available_strategies[self._strategy_name])) + mui.Typography("Create study", variant="h6") + if len(strategy_names) == 0: + mui.Alert("No strategies available, please create one to optimize it", severity="warning", + sx={"width": "100%"}) + return + else: + if self._strategy_name is None: + self._strategy_name = strategy_names[0] + with mui.Grid(container=True, spacing=2, sx={"padding": "10px"}): + with mui.Grid(item=True, xs=4): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.FormHelperText("Strategy name") + with mui.Select(label="Select strategy", defaultValue=strategy_names[0], + variant="standard", onChange=lazy(self._set_strategy_name)): + for strategy in strategy_names: + mui.MenuItem(strategy, value=strategy) + with mui.Grid(item=True, xs=4): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.TextField(defaultValue=self._optimization_version, label="Optimization version", + variant="standard", onChange=lazy(self._set_optimization_version)) + with mui.Grid(item=True, xs=4): + with mui.Button(variant="contained", onClick=lambda x: self._create_optimization( + available_strategies[self._strategy_name]), sx={"width": "100%"}): + mui.icon.Add() + mui.Typography("Create", variant="body1") From 81d1f2e02be26c3c68d525324b6bcbfc31316283 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:30 +0200 Subject: [PATCH 25/31] (feat) remove app since it's splitted now --- pages/backtest_manager/app.py | 187 ---------------------------------- 1 file changed, 187 deletions(-) delete mode 100644 pages/backtest_manager/app.py diff --git a/pages/backtest_manager/app.py b/pages/backtest_manager/app.py deleted file mode 100644 index b39a7bda..00000000 --- a/pages/backtest_manager/app.py +++ /dev/null @@ -1,187 +0,0 @@ -import time -import webbrowser -from types import SimpleNamespace - -import streamlit as st -from streamlit_elements import elements, mui - -import constants -from quants_lab.strategy.strategy_analysis import StrategyAnalysis -from ui_components.dashboard import Dashboard -from ui_components.directional_strategies_file_explorer import DirectionalStrategiesFileExplorer -from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard -from ui_components.editor import Editor -from ui_components.optimization_creation_card import OptimizationCreationCard -from ui_components.optimization_run_card import OptimizationRunCard -from ui_components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer -from utils import os_utils -from utils.os_utils import load_directional_strategies - -from utils.st_utils import initialize_st_page - - -initialize_st_page(title="Backtest Manager", icon="⚙️", initial_sidebar_state="collapsed") - -# Start content here -if "strategy_params" not in st.session_state: - st.session_state.strategy_params = {} - -build, backtest, optimize, analyze = st.tabs(["Build", "Backtest", "Optimize", "Analyze"]) - -with build: - # TODO: - # * Add videos explaining how to the triple barrier method works and how the backtesting is designed, - # link to video of how to create a strategy, etc in a toggle. - # * Add functionality to start strategy creation from scratch or by duplicating an existing one - - if "ds_board" not in st.session_state: - board = Dashboard() - ds_board = SimpleNamespace( - dashboard=board, - create_strategy_card=DirectionalStrategyCreationCard(board, 0, 0, 3, 1), - file_explorer=DirectionalStrategiesFileExplorer(board, 0, 2, 3, 7), - editor=Editor(board, 4, 2, 9, 7), - ) - st.session_state.ds_board = ds_board - - else: - ds_board = st.session_state.ds_board - - # Add new tabs - for tab_name, content in ds_board.file_explorer.tabs.items(): - if tab_name not in ds_board.editor.tabs: - ds_board.editor.add_tab(tab_name, content["content"], content["language"]) - - # Remove deleted tabs - for tab_name in list(ds_board.editor.tabs.keys()): - if tab_name not in ds_board.file_explorer.tabs: - ds_board.editor.remove_tab(tab_name) - - with elements("directional_strategies"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - mui.Typography("🗂Directional Strategies", variant="h3", sx={"margin-bottom": "2rem"}) - with ds_board.dashboard(): - ds_board.create_strategy_card() - ds_board.file_explorer() - ds_board.editor() - -with backtest: - # TODO: - # * Add videos explaining how to the triple barrier method works and how the backtesting is designed, - # link to video of how to create a strategy, etc in a toggle. - # * Add performance analysis graphs of the backtesting run - strategies = load_directional_strategies(constants.DIRECTIONAL_STRATEGIES_PATH) - strategy_to_optimize = st.selectbox("Select strategy to backtest", strategies.keys()) - strategy = strategies[strategy_to_optimize] - strategy_config = strategy["config"] - field_schema = strategy_config.schema()["properties"] - st.write("## Strategy parameters") - c1, c2 = st.columns([5, 1]) - with c1: - columns = st.columns(4) - column_index = 0 - for field_name, properties in field_schema.items(): - field_type = properties["type"] - with columns[column_index]: - if field_type in ["number", "integer"]: - field_value = st.number_input(field_name, - value=properties["default"], - min_value=properties.get("minimum"), - max_value=properties.get("maximum"), - key=field_name) - elif field_type == "string": - field_value = st.text_input(field_name, value=properties["default"]) - elif field_type == "boolean": - # TODO: Add support for boolean fields in optimize tab - field_value = st.checkbox(field_name, value=properties["default"]) - else: - raise ValueError(f"Field type {field_type} not supported") - st.session_state["strategy_params"][field_name] = field_value - column_index = (column_index + 1) % 4 - with c2: - add_positions = st.checkbox("Add positions", value=True) - add_volume = st.checkbox("Add volume", value=True) - add_pnl = st.checkbox("Add PnL", value=True) - - run_backtesting_button = st.button("Run Backtesting!") - if run_backtesting_button: - config = strategy["config"](**st.session_state["strategy_params"]) - strategy = strategy["class"](config=config) - # TODO: add form for order amount, leverage, tp, sl, etc. - - market_data, positions = strategy.run_backtesting( - start='2021-04-01', - order_amount=50, - leverage=20, - initial_portfolio=100, - take_profit_multiplier=2.3, - stop_loss_multiplier=1.2, - time_limit=60 * 60 * 3, - std_span=None, - ) - strategy_analysis = StrategyAnalysis( - positions=positions, - candles_df=market_data, - ) - st.text(strategy_analysis.text_report()) - # TODO: check why the pnl is not being plotted - strategy_analysis.create_base_figure(volume=add_volume, positions=add_positions, trade_pnl=add_pnl) - st.plotly_chart(strategy_analysis.figure(), use_container_width=True) - -with optimize: - # TODO: - # * Add videos explaining how to use the optimization tool, quick intro to optuna, etc in a toggle - - def run_optuna_dashboard(): - os_utils.execute_bash_command(f"optuna-dashboard sqlite:///data/backtesting/backtesting_report.db") - time.sleep(5) - webbrowser.open("http://127.0.0.1:8080/dashboard", new=2) - - if "op_board" not in st.session_state: - board = Dashboard() - op_board = SimpleNamespace( - dashboard=board, - run_optimization_card=OptimizationRunCard(board, 0, 0, 3, 1), - create_optimization_card=OptimizationCreationCard(board, 0, 1, 3, 1), - file_explorer=OptimizationsStrategiesFileExplorer(board, 0, 2, 3, 5), - editor=Editor(board, 4, 2, 9, 7), - ) - st.session_state.op_board = op_board - - else: - op_board = st.session_state.op_board - - # Add new tabs - for tab_name, content in op_board.file_explorer.tabs.items(): - if tab_name not in op_board.editor.tabs: - op_board.editor.add_tab(tab_name, content["content"], content["language"]) - - # Remove deleted tabs - for tab_name in list(op_board.editor.tabs.keys()): - if tab_name not in op_board.file_explorer.tabs: - op_board.editor.remove_tab(tab_name) - - with elements("optimizations"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - with mui.Grid(container=True, spacing=2): - with mui.Grid(item=True, xs=10): - mui.Typography("🧪Optimizations", variant="h3", sx={"margin-bottom": "2rem"}) - with mui.Grid(item=True, xs=2): - with mui.Button(variant="outlined", color="primary", size="large", - sx={"height": "100%", "width": "100%"}, onClick=run_optuna_dashboard): - mui.icon.AutoGraph() - mui.Typography("Run Optuna Dashboard", variant="body1") - - - with op_board.dashboard(): - op_board.run_optimization_card() - op_board.create_optimization_card() - op_board.file_explorer() - op_board.editor() - - -with analyze: - # TODO: - # * Add graphs for all backtesting results - # * Add management of backtesting results (delete, rename, save, share, upload s3, etc) - pass From 79e5321b677b516a3408e0d6f442d1f75dae63ed Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:38 +0200 Subject: [PATCH 26/31] (feat) add optimization_run_card.py --- ui_components/optimization_run_card.py | 37 +++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/ui_components/optimization_run_card.py b/ui_components/optimization_run_card.py index 435a9ba5..91aa086c 100644 --- a/ui_components/optimization_run_card.py +++ b/ui_components/optimization_run_card.py @@ -42,18 +42,25 @@ def __call__(self): mui.icon.AutoFixHigh() mui.Typography("Run a optimization", variant="h6") - with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): - if len(optimizations) == 0: - mui.Alert("No optimizations available, please create one.", severity="warning", sx={"width": "100%"}) - return - else: - if self._optimization_name is None: - self._optimization_name = optimizations[0] - with mui.Select(label="Select strategy", defaultValue=optimizations[0], - variant="standard", onChange=lazy(self._set_optimization_name)): - for optimization in optimizations: - mui.MenuItem(f"{optimization.split('/')[-1].split('.')[0]}", value=optimization) - mui.TextField(defaultValue=self._optimization_name, label="Number of trials", type="number", - variant="standard", onChange=lazy(self._set_number_of_trials)) - mui.IconButton(mui.icon.PlayCircleFilled, sx={"color": "primary.main"}, - onClick=self._run_optimization) + if len(optimizations) == 0: + mui.Alert("No optimizations available, please create one.", severity="warning", sx={"width": "100%"}) + return + else: + if self._optimization_name is None: + self._optimization_name = optimizations[0] + with mui.Grid(container=True, spacing=2, sx={"padding": "10px"}): + with mui.Grid(item=True, xs=4): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.FormHelperText("Study name") + with mui.Select(defaultValue=optimizations[0], + variant="standard", onChange=lazy(self._set_optimization_name)): + for optimization in optimizations: + mui.MenuItem(f"{optimization.split('/')[-1].split('.')[0]}", value=optimization) + with mui.Grid(item=True, xs=4): + with mui.FormControl(variant="standard", sx={"width": "100%"}): + mui.TextField(defaultValue=self._optimization_name, label="Number of trials", type="number", + variant="standard", onChange=lazy(self._set_number_of_trials)) + with mui.Grid(item=True, xs=4): + with mui.Button(variant="contained", onClick=self._run_optimization, sx={"width": "100%"}): + mui.icon.PlayCircleFilled() + mui.Typography("Run", variant="button") From 0aab8280a76f2ec6ee8c489596ab208eb337f2cd Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:46 +0200 Subject: [PATCH 27/31] (feat) refactor pages structure --- main.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 9a9536b1..98b2e6fc 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ import streamlit as st -from st_pages import Page, Section, show_pages, add_page_title +from st_pages import Page, Section, show_pages from utils.st_utils import initialize_st_page @@ -8,12 +8,16 @@ show_pages( [ Page("main.py", "Hummingbot Dashboard", "📊"), - Section("Foundation Pages", "🏠"), - Page("pages/bot_orchestration/app.py", "Bot Orchestration", "🐙"), + Section("Bot Orchestration", "🐙"), + Page("pages/bot_orchestration/app.py", "Bots Manager", "🦅"), Page("pages/file_manager/app.py", "File Manager", "🗂"), + Section("Backtest Manager", "⚙️"), + Page("pages/backtest_manager/create.py", "Create", "⚔️"), + Page("pages/backtest_manager/optimize.py", "Optimize", "🧪"), + Page("pages/backtest_manager/analyze.py", "Analyze", "🔬"), + Page("pages/backtest_manager/simulate.py", "Simulate", "📈"), Section("Community Pages", "👨‍👩‍👧‍👦"), Page("pages/strategy_performance/app.py", "Strategy Performance", "🚀"), - Page("pages/backtest_manager/app.py", "Backtest Manager", "⚙️"), Page("pages/candles_downloader/app.py", "Candles Downloader", "🗂"), Page("pages/db_inspector/app.py", "DB Inspector", "🔍"), Page("pages/token_spreads/app.py", "Token Spreads", "🧙"), From 09b6c0f1347ce47953c0d731b78fcd950e087aed Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:39:55 +0200 Subject: [PATCH 28/31] (feat) add create strategies page --- pages/backtest_manager/create.py | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 pages/backtest_manager/create.py diff --git a/pages/backtest_manager/create.py b/pages/backtest_manager/create.py new file mode 100644 index 00000000..2b0c7d9e --- /dev/null +++ b/pages/backtest_manager/create.py @@ -0,0 +1,49 @@ +from types import SimpleNamespace + +import streamlit as st +from streamlit_elements import elements, mui + +from ui_components.dashboard import Dashboard +from ui_components.directional_strategies_file_explorer import DirectionalStrategiesFileExplorer +from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard +from ui_components.editor import Editor + +from utils.st_utils import initialize_st_page + + +initialize_st_page(title="Create", icon="️⚔️", initial_sidebar_state="collapsed") + +# TODO: +# * Add videos explaining how to the triple barrier method works and how the backtesting is designed, +# link to video of how to create a strategy, etc in a toggle. +# * Add functionality to start strategy creation from scratch or by duplicating an existing one + +if "ds_board" not in st.session_state: + board = Dashboard() + ds_board = SimpleNamespace( + dashboard=board, + create_strategy_card=DirectionalStrategyCreationCard(board, 0, 0, 12, 1), + file_explorer=DirectionalStrategiesFileExplorer(board, 0, 2, 3, 7), + editor=Editor(board, 4, 2, 9, 7), + ) + st.session_state.ds_board = ds_board + +else: + ds_board = st.session_state.ds_board + +# Add new tabs +for tab_name, content in ds_board.file_explorer.tabs.items(): + if tab_name not in ds_board.editor.tabs: + ds_board.editor.add_tab(tab_name, content["content"], content["language"]) + +# Remove deleted tabs +for tab_name in list(ds_board.editor.tabs.keys()): + if tab_name not in ds_board.file_explorer.tabs: + ds_board.editor.remove_tab(tab_name) + +with elements("directional_strategies"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with ds_board.dashboard(): + ds_board.create_strategy_card() + ds_board.file_explorer() + ds_board.editor() From f4300bb9eb7aeea036b423fbbaa6249ac24f38d9 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:40:10 +0200 Subject: [PATCH 29/31] (feat) add file manager tab --- pages/file_manager/app.py | 54 ++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/pages/file_manager/app.py b/pages/file_manager/app.py index 4843ca8a..56c81806 100644 --- a/pages/file_manager/app.py +++ b/pages/file_manager/app.py @@ -1,44 +1,40 @@ -import streamlit as st -from streamlit_elements import elements, mui, lazy, sync, event - from types import SimpleNamespace - import streamlit as st -from streamlit_elements import elements, mui, lazy, sync, event +from streamlit_elements import elements, mui +from ui_components.bots_file_explorer import BotsFileExplorer from ui_components.dashboard import Dashboard -from ui_components.file_explorer import FileExplorer from ui_components.editor import Editor from utils.st_utils import initialize_st_page -initialize_st_page(title="File Manager", icon="🗂️", initial_sidebar_state="expanded") -if "editor_tabs" not in st.session_state: - st.session_state.editor_tabs = {} +initialize_st_page(title="File Manager", icon="🗂️", initial_sidebar_state="collapsed") -# TODO: Use this to save the current file selected -if "selected_file" not in st.session_state: - st.session_state.selected_file = "" -if "w" not in st.session_state: +if "fe_board" not in st.session_state: board = Dashboard() - w = SimpleNamespace( + fe_board = SimpleNamespace( dashboard=board, - file_explorer=FileExplorer(board, 0, 0, 3, 7), + file_explorer=BotsFileExplorer(board, 0, 0, 3, 7), editor=Editor(board, 4, 0, 9, 7), ) - st.session_state.w = w + st.session_state.fe_board = fe_board + else: - w = st.session_state.w - -for tab_name, content in st.session_state.editor_tabs.items(): - if tab_name not in w.editor._tabs: - w.editor.add_tab(tab_name, content["content"], content["language"], content["file_path"]) - -with elements("bot_config"): - with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - mui.Typography("Select a file and click ✏️ to edit it or 🗑️ to delete it. Click 💾 to save your changes.", variant="body1", sx={"margin-bottom": "2rem"}) - event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True) - with w.dashboard(): - w.file_explorer() - w.editor() + fe_board = st.session_state.fe_board + +# Add new tabs +for tab_name, content in fe_board.file_explorer.tabs.items(): + if tab_name not in fe_board.editor.tabs: + fe_board.editor.add_tab(tab_name, content["content"], content["language"]) + +# Remove deleted tabs +for tab_name in list(fe_board.editor.tabs.keys()): + if tab_name not in fe_board.file_explorer.tabs: + fe_board.editor.remove_tab(tab_name) + +with elements("file_manager"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with fe_board.dashboard(): + fe_board.file_explorer() + fe_board.editor() From eba8e067bae137125cd3c3737a697e14952653f4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:40:26 +0200 Subject: [PATCH 30/31] (feat) adapt bot orchestration page --- pages/bot_orchestration/app.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index 1c97f159..b67f6469 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -11,9 +11,7 @@ from hbotrc import BotCommands from ui_components.bot_performance_card import BotPerformanceCard -from ui_components.bots_file_explorer import BotsFileExplorer from ui_components.dashboard import Dashboard -from ui_components.editor import Editor from ui_components.exited_bot_card import ExitedBotCard from utils.st_utils import initialize_st_page @@ -180,34 +178,3 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int with exited_instances_board(): for bot, card in st.session_state.exited_bots.items(): card(bot) - - -with manage: - if "w" not in st.session_state: - board = Dashboard() - w = SimpleNamespace( - dashboard=board, - file_explorer=BotsFileExplorer(board, 0, 0, 3, 7, constants.BOTS_FOLDER), - editor=Editor(board, 4, 0, 9, 7), - ) - st.session_state.w = w - - else: - w = st.session_state.w - - # Add new tabs - for tab_name, content in w.file_explorer.tabs.items(): - if tab_name not in w.editor.tabs: - w.editor.add_tab(tab_name, content["content"], content["language"]) - - # Remove deleted tabs - for tab_name in list(w.editor.tabs.keys()): - if tab_name not in w.file_explorer.tabs: - w.editor.remove_tab(tab_name) - - with elements("bot_config"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - mui.Typography("🗂Files Management", variant="h3", sx={"margin-bottom": "2rem"}) - with w.dashboard(): - w.file_explorer() - w.editor() From d17ad757b585ab30cc9205f4f4dd47a1ca253844 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 4 Aug 2023 18:40:53 +0200 Subject: [PATCH 31/31] (feat) add analyze page --- pages/backtest_manager/analyze.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pages/backtest_manager/analyze.py diff --git a/pages/backtest_manager/analyze.py b/pages/backtest_manager/analyze.py new file mode 100644 index 00000000..c0dc2095 --- /dev/null +++ b/pages/backtest_manager/analyze.py @@ -0,0 +1,4 @@ +from utils.st_utils import initialize_st_page + + +initialize_st_page(title="Analyze", icon="🔬", initial_sidebar_state="collapsed")