From ffa00c1199963c2c6fc11bcbc40b16a5c5b46337 Mon Sep 17 00:00:00 2001 From: Michael Feng Date: Sat, 5 Aug 2023 11:44:02 -0700 Subject: [PATCH] (feat) update readme and bots manager ui --- INSTALLATION.md | 75 +++++++++++++++------------ README.md | 16 ++---- pages/bot_orchestration/app.py | 30 ++++++----- ui_components/bot_performance_card.py | 46 +++++++++------- ui_components/exited_bot_card.py | 25 ++++++--- 5 files changed, 109 insertions(+), 83 deletions(-) diff --git a/INSTALLATION.md b/INSTALLATION.md index 1b60c15d..b07f1c6f 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -1,62 +1,43 @@ -## Requirements - -You will need to install [StreamLit](https://streamlit.io/). For information about Streamlit installation, see the instructions located at https://docs.streamlit.io/library/get-started/installation. - -You will also need to install either [Anaconda](https://www.anaconda.com/) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html) to get Conda: -* [Anaconda](https://www.anaconda.com/) is a comprehensive Python distribution that includes a large number of pre-installed data science libraries and packages. It is designed to be an all-in-one solution for data science and machine learning tasks. When you install Anaconda, it comes with a collection of popular Python packages like NumPy, pandas, matplotlib, scikit-learn, and more. -* [Miniconda](https://docs.conda.io/en/latest/miniconda.html) is a minimal version of Anaconda. It includes only the essential components, such as Python interpreter and Conda package manager. Unlike Anaconda, Miniconda doesn't come with pre-installed packages, which makes its download size much smaller. This repository is maintained by Hummingbot Foundation as a companion for users of [Hummingbot](https://github.com/hummingbot/hummingbot), the open source framework for building high-frequency crypto trading bots. Watch this video to understand how it works: https://www.loom.com/share/72d05bcbaf4048a399e3f9247d756a63 +## Requirements + +* 8 GB memory or more (On AWS, this is a `t2.large` instance) +* Linux / Debian / MacOS + ## Installation -1. Install Steamlit and Conda packages utilizing their instructions for your specific environment: -* Install [StreamLit](https://docs.streamlit.io/library/get-started/installation) -* Install [Anaconda](https://docs.anaconda.com/free/anaconda/install/index.html) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html) +1 - Install dependencies: + +* [Docker Engine](https://docs.docker.com/engine/install/ubuntu/) +* [Miniconda](https://docs.conda.io/en/latest/miniconda.html) or [Anaconda](https://www.anaconda.com/): -2. Clone this repo and navigate to the created directory +2 - Clone repo and navigate to the created directory ```bash git clone https://github.com/hummingbot/dashboard.git cd dashboard ``` -3. Run command to create an isolated `conda` environment and install dependencies -``` +3 - Create `conda` environment and install dependencies +```bash make env_create ``` -4. Activate the isolated 'conda' environment +4 - Activate the isolated 'conda' environment ```bash conda activate dashboard ``` -5. Run the app +5 - Start the dashboard ```bash streamlit run main.py ``` -## Data Feed - -Your `dashboard` environment needs to have access to the database for your Hummingbot environment. This is done by setting up a symbolic link to the 'data' directory of your running Hummingbot instance. - -The data directory differs for Docker versus Source installed Hummingbot. Data directory for each is as follows: -* Docker installed: /path/to/hummingbot/hummingbot_files/data -* Source installed: /path/to/hummingbot/data - - -Create a symlink to your Hummingbot `/data` directory -```bash -# replace `/path/to/hummingbotdata` with the actual path -ln -s /path/to/hummingbotdata data - -# if you need to remove the symlink -unlink data -``` - -## Updating Dependencies +## Updating To update the `dashboard` environment for changes to dependencies defined in `environment.yml`, remove the environment and re-create it: ``` @@ -69,3 +50,29 @@ To updated the `dashboard` source for latest version, run: cd dashboard git pull ``` + +## Troubleshooting + +### Docker permissions + +If you get an error like `Permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock`, run this command to enable Docker permissions: +``` +sudo chmod 6666 /var/run/docker.sock +``` + +### Sym-link data directory + +To use the [Strategy Performance page](https://github.com/hummingbot/dashboard/wiki/%F0%9F%9A%80-Strategy-Performance), you need to establish a symbolic link to the `data` directory of your running Hummingbot instance: + +The `data` directory differs for Docker versus Source installed Hummingbot: +* Docker installed: `/path/to/hummingbot/hummingbot_files/data` +* Source installed: `/path/to/hummingbot/data` + +Create a symlink to your Hummingbot `/data` directory +```bash +# replace `/path/to/hummingbotdata` with the actual path +ln -s /path/to/hummingbotdata data + +# if you need to remove the symlink +unlink data +``` diff --git a/README.md b/README.md index 65ed391a..4ef84854 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Hummingbot Dashboard is a community project to build dashboards that help you de Here are the current dashboards in the collection: -* 🚀 **Strategy Performance**: Analyze the performance of a running Hummingbot instance * 🐙 **Bot Orchestration**: Deploy and manage Hummingbot instances -* ⚙️ **Backtest Manager**: Deploy and manage backtests of directional strategies +* ⚙️ **Backtest Manager**: Backtest strategies against historical data and optimize hyper-parameters +* 🚀 **Strategy Performance**: Analyze the performance of a running Hummingbot instance * 🗂 **Candles Downloader**: Download historical exchange data as OHLVC candles. Supports multiple trading pairs and custom time ranges/intervals. * 🔍 **DB Inspector**: Inspect and analyze the orders and trades data contained in a Hummingbot strategy database * 🧙 **Token Spreads**: Identify cross-exchange trading opportunities by analyzing differences in token spreads across venues @@ -18,14 +18,8 @@ This project is built using [StreamLit](https://streamlit.io/) and uses Anaconda See [Installation](https://github.com/hummingbot/dashboard/blob/main/INSTALLATION.md) for how to install and update the dashboard. -### Contributions - -We welcome contributions from the community! See [Contribution](https://github.com/hummingbot/dashboard/blob/main/CONTRIBUTING.md) for more information. - -### Meetings - -We hold bi-weekly livestream Dashboard project meetings. You can participate on our [Discord](https://discord.gg/hummingbot) -* Alternating Wenesdays, 3pm GMT / 11am EST / 8am PST / 11pm SIN -* Design, Status, Demos, etc +### Contributions and feedback +We welcome contributions from the community! See [Contributing](https://github.com/hummingbot/dashboard/blob/main/CONTRIBUTING.md) for more information. +To provide feedback, submit a [Github issue](https://github.com/hummingbot/dashboard/issues) or join the #dashboard channel in our [Discord](https://discord.gg/hummingbot). diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index 5d48b9bc..b378f560 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -15,7 +15,7 @@ from ui_components.exited_bot_card import ExitedBotCard from utils.st_utils import initialize_st_page -initialize_st_page(title="Bot Orchestration", icon="🐙", initial_sidebar_state="expanded") +initialize_st_page(title="Bots Manager", icon="🦅", initial_sidebar_state="collapsed") if "is_broker_running" not in st.session_state: st.session_state.is_broker_running = False @@ -118,37 +118,43 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int with elements("create_bot"): - with mui.Grid(container=True, spacing=4): + with mui.Grid(container=True, spacing=2): with mui.Grid(item=True, xs=6): with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - with mui.Grid(container=True, spacing=4): + with mui.Grid(container=True, spacing=2): with mui.Grid(item=True, xs=12): - mui.Typography("🚀 Create Instance", variant="h4") + mui.Typography("🚀 Create Instance", variant="h5") with mui.Grid(item=True, xs=8): mui.TextField(label="Bot Name", variant="outlined", onChange=lazy(sync("new_bot_name")), sx={"width": "100%"}) with mui.Grid(item=True, xs=4): - with mui.Button(onClick=launch_new_bot, variant="contained", color="success"): + with mui.Button(onClick=launch_new_bot, + variant="outlined", + color="success", + sx={"width": "100%", "height": "100%"}): mui.icon.AddCircleOutline() mui.Typography("Create") with mui.Grid(item=True, xs=6): with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - with mui.Grid(container=True, spacing=4): + with mui.Grid(container=True, spacing=2): with mui.Grid(item=True, xs=12): - mui.Typography("🐙 Manage Broker", variant="h4") + mui.Typography("🐙 Manage Broker", variant="h5") with mui.Grid(item=True, xs=8): mui.Typography("Hummingbot Broker helps you control and monitor your bot instances.") with mui.Grid(item=True, xs=4): - button_text = "Stop Broker" if st.session_state.is_broker_running else "Start Broker" + button_text = "Stop" if st.session_state.is_broker_running else "Start" color = "error" if st.session_state.is_broker_running else "success" icon = mui.icon.Stop if st.session_state.is_broker_running else mui.icon.PlayCircle - with mui.Button(onClick=manage_broker_container, color=color, variant="contained"): + with mui.Button(onClick=manage_broker_container, + color=color, + variant="outlined", + sx={"width": "100%", "height": "100%"}): icon() mui.Typography(button_text) with elements("active_instances_board"): - with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - mui.Typography("🦅 Active Instances", variant="h4") + with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): + mui.Typography("🦅 Active Instances", variant="h5") if st.session_state.is_broker_running: quantity_of_active_bots = len(st.session_state.active_bots) if quantity_of_active_bots > 0: @@ -174,7 +180,7 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int st.session_state.exited_bots[exited_instance] = ExitedBotCard(exited_instances_board, x, y, CARD_WIDTH, 1) with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - mui.Typography("💤 Inactive Instances", variant="h4") + mui.Typography("💤 Stopped Instances", variant="h5") with exited_instances_board(): for bot, card in st.session_state.exited_bots.items(): card(bot) diff --git a/ui_components/bot_performance_card.py b/ui_components/bot_performance_card.py index 6b4579ee..6b936021 100644 --- a/ui_components/bot_performance_card.py +++ b/ui_components/bot_performance_card.py @@ -40,46 +40,56 @@ def __call__(self, bot_config: dict): with mui.Card(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, elevation=2): - color = "green" if bot_config["is_running"] else "red" - subheader_message = "Running " + st.session_state.active_bots[bot_name]["selected_strategy"] if bot_config["is_running"] else "Stopped" + color = "green" if bot_config["is_running"] else "grey" + subheader_message = "Running " + st.session_state.active_bots[bot_name]["selected_strategy"] if bot_config["is_running"] else "Not running" mui.CardHeader( title=bot_config["bot_name"], subheader=subheader_message, avatar=mui.Avatar("🤖", sx={"bgcolor": color}), - action=mui.IconButton(mui.icon.Stop, onClick=lambda: bot_config["broker_client"].stop()) if bot_config[ - "is_running"] else mui.IconButton(mui.icon.BuildCircle), + # action=mui.IconButton(mui.icon.Stop, onClick=lambda: bot_config["broker_client"].stop()) if bot_config[ + # "is_running"] else mui.IconButton(mui.icon.BuildCircle), className=self._draggable_class, ) if bot_config["is_running"]: with mui.CardContent(sx={"flex": 1}): with mui.Paper(elevation=2, sx={"padding": 2, "marginBottom": 2}): - mui.Typography("Status", variant="h6") - mui.Typography(bot_config["status"]) + mui.Typography("Status") + mui.Typography(bot_config["status"], sx={"fontSize": "0.75rem"}) with mui.Accordion(sx={"padding": 2, "marginBottom": 2}): with mui.AccordionSummary(expandIcon="▼"): - mui.Typography("Trades" + "(" + str(len(bot_config["trades"])) + ")", variant="h6") + mui.Typography("Trades" + "(" + str(len(bot_config["trades"])) + ")") with mui.AccordionDetails(): - mui.Typography(str(bot_config["trades"])) - mui.Typography("Run the following command in Bash/Terminal to attach to the bot instance:") - mui.TextField(disabled=True, value="docker attach " + bot_name, sx={"width": "100%"}) - + mui.Typography(str(bot_config["trades"]), sx={"fontSize": "0.75rem"}) else: with mui.CardContent(sx={"flex": 1}): with mui.Grid(container=True, spacing=2): with mui.Grid(item=True, xs=12): - mui.Typography("Select a strategy:") + mui.Typography("Select a strategy config file (.yml) or script (.py) to run:") with mui.Grid(item=True, xs=8): with mui.Select(onChange=lazy(lambda x, y: self.set_strategy(x, y, bot_name)), sx={"width": "100%"}): + for strategy in strategies: + mui.MenuItem(strategy, value=strategy, divider=True, sx={"fontWeight": "bold"}) for script in scripts: mui.MenuItem(script, value=script) - for strategy in strategies: - mui.MenuItem(strategy, value=strategy) with mui.Grid(item=True, xs=4): - with mui.Button(onClick=lambda x: self.start_strategy(bot_name, bot_config["broker_client"]), variant="contained", color="success"): + with mui.Button(onClick=lambda x: self.start_strategy(bot_name, bot_config["broker_client"]), + variant="outlined", + color="success", + sx={"width": "100%", "height": "100%"}): mui.icon.PlayCircle() mui.Typography("Start") with mui.CardActions(): - with mui.Button(onClick=lambda: DockerManager().stop_container(bot_name), variant="contained", color="error"): - mui.icon.DeleteForever() - mui.Typography("Stop Instance") + with mui.Grid(container=True, spacing=2): + with mui.Grid(item=True, xs=6): + with mui.Button(onClick=lambda: DockerManager().stop_container(bot_name), + variant="outlined", + color="error", + sx={"width": "100%", "height": "100%"}): + mui.icon.DeleteForever() + mui.Typography("Stop Instance") + with mui.Grid(item=True, xs=6): + mui.TextField(disabled=True, + label="Attach to bot instance", + value="docker attach " + bot_name, + sx={"width": "100%"}) diff --git a/ui_components/exited_bot_card.py b/ui_components/exited_bot_card.py index a701f3e3..6d4c2538 100644 --- a/ui_components/exited_bot_card.py +++ b/ui_components/exited_bot_card.py @@ -20,19 +20,28 @@ def remove_container(bot_name): def __call__(self, bot_name: str): with mui.Card(key=self._key, - sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, + sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, elevation=2): mui.CardHeader( title=bot_name, subheader="Stopped", - avatar=mui.Avatar("💀", sx={"bgcolor": "grey"}), + avatar=mui.Avatar("💀", sx={"bgcolor": "black"}), className=self._draggable_class, ) with mui.CardActions(): - with mui.Button(onClick=lambda: DockerManager().start_container(bot_name), variant="contained", color="success", sx={"pr": "2"}): - mui.icon.PlayCircle() - mui.Typography("Start Container") - with mui.Button(onClick=lambda: self.remove_container(bot_name), variant="contained", color="error"): - mui.icon.DeleteForever() - mui.Typography("Delete Container") + with mui.Grid(container=True, spacing=2): + with mui.Grid(item=True, xs=6): + with mui.Button(onClick=lambda: DockerManager().start_container(bot_name), + variant="outlined", + color="success", + sx={"width": "100%"}): + mui.icon.PlayCircle() + mui.Typography("Start Instance") + with mui.Grid(item=True, xs=6): + with mui.Button(onClick=lambda: self.remove_container(bot_name), + variant="outlined", + color="error", + sx={"width": "100%"}): + mui.icon.DeleteForever() + mui.Typography("Delete Instance")