Skip to content

Commit

Permalink
Merge pull request #60 from hummingbot/feat/improve-ui
Browse files Browse the repository at this point in the history
(feat) update readme and bots manager ui
  • Loading branch information
fengtality authored Aug 5, 2023
2 parents 2daf2a1 + ffa00c1 commit 76b113b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 83 deletions.
75 changes: 41 additions & 34 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
@@ -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:
```
Expand All @@ -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
```
16 changes: 5 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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).
30 changes: 18 additions & 12 deletions pages/bot_orchestration/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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)
46 changes: 28 additions & 18 deletions ui_components/bot_performance_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -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%"})
25 changes: 17 additions & 8 deletions ui_components/exited_bot_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

0 comments on commit 76b113b

Please sign in to comment.