Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: detect-private-key
- id: detect-private-key

- repo: https://github.com/pre-commit/mirrors-eslint
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.10.0
hooks:
- id: eslint
files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
- id: eslint
files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
types: [file]

- repo: https://github.com/CoinAlpha/git-hooks
- repo: https://github.com/CoinAlpha/git-hooks
rev: 78f0683233a09c68a072fd52740d32c0376d4f0f
hooks:
- id: detect-wallet-private-key
- id: detect-wallet-private-key
types: [file]
exclude: .json

- repo: https://github.com/pycqa/isort
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
files: "\\.(py)$"
args: [--settings-path=pyproject.toml]

- repo: https://github.com/pycqa/flake8
rev: 3.9.2
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
additional_dependencies: ['flake8']
additional_dependencies: ["flake8"]
args: [--max-line-length=130]
4 changes: 2 additions & 2 deletions backend/services/backend_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def post(self, endpoint: str, payload: Optional[Dict] = None, params: Optional[D
response = requests.post(url, json=payload, params=params, auth=self.auth)
return self._process_response(response)

def get(self, endpoint: str):
def get(self, endpoint: str) -> Any:
"""
Get request to the backend API.
:param endpoint:
Expand Down Expand Up @@ -146,7 +146,7 @@ def get_active_bots_status(self):
endpoint = "get-active-bots-status"
return self.get(endpoint)

def get_all_controllers_config(self):
def get_all_controllers_config(self) -> List[dict]:
"""Get all controller configurations."""
endpoint = "all-controller-configs"
return self.get(endpoint)
Expand Down
2 changes: 1 addition & 1 deletion environment_conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies:
- pip:
- hummingbot
- pydantic
- streamlit==1.33.0
- streamlit==1.40.0
- watchdog
- python-dotenv
- plotly==5.24.1
Expand Down
17 changes: 17 additions & 0 deletions frontend/pages/landing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import streamlit as st

# readme section
st.markdown("# 📊 Hummingbot Dashboard")
st.markdown("Hummingbot Dashboard is an open source application that helps you create, backtest, and optimize "
"various types of algo trading strategies. Afterwards, you can deploy them as "
"[Hummingbot](http://hummingbot.org)")
st.write("---")
st.header("Watch the Hummingbot Dashboard Tutorial!")
st.video("https://youtu.be/7eHiMPRBQLQ?si=PAvCq0D5QDZz1h1D")
st.header("Feedback and issues")
st.write(
"Please give us feedback in the **#dashboard** channel of the "
"[hummingbot discord](https://discord.gg/hummingbot)! 🙏")
st.write(
"If you encounter any bugs or have suggestions for improvement, please create an issue in the "
"[hummingbot dashboard github](https://github.com/hummingbot/dashboard).")
56 changes: 30 additions & 26 deletions frontend/pages/permissions.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
from st_pages import Page, Section
import streamlit as st


def main_page():
return [Page("main.py", "Hummingbot Dashboard", "📊")]
return [st.Page("frontend/pages/landing.py", title="Hummingbot Dashboard", icon="📊", url_path="landing")]


def public_pages():
return [
Section("Config Generator", "🎛️"),
Page("frontend/pages/config/grid_strike/app.py", "Grid Strike", "🎳"),
Page("frontend/pages/config/pmm_simple/app.py", "PMM Simple", "👨‍🏫"),
Page("frontend/pages/config/pmm_dynamic/app.py", "PMM Dynamic", "👩‍🏫"),
Page("frontend/pages/config/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"),
Page("frontend/pages/config/bollinger_v1/app.py", "Bollinger V1", "📈"),
Page("frontend/pages/config/macd_bb_v1/app.py", "MACD_BB V1", "📊"),
Page("frontend/pages/config/supertrend_v1/app.py", "SuperTrend V1", "👨‍🔬"),
Page("frontend/pages/config/xemm_controller/app.py", "XEMM Controller", "⚡️"),
Section("Data", "💾"),
Page("frontend/pages/data/download_candles/app.py", "Download Candles", "💹"),
Section("Community Pages", "👨‍👩‍👧‍👦"),
Page("frontend/pages/data/token_spreads/app.py", "Token Spreads", "🧙"),
Page("frontend/pages/data/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"),
Page("frontend/pages/performance/bot_performance/app.py", "Strategy Performance", "📈"),
]
return {
"Config Generator": [
st.Page("frontend/pages/config/grid_strike/app.py", title="Grid Strike", icon="🎳", url_path="grid_strike"),
st.Page("frontend/pages/config/pmm_simple/app.py", title="PMM Simple", icon="👨‍🏫", url_path="pmm_simple"),
st.Page("frontend/pages/config/pmm_dynamic/app.py", title="PMM Dynamic", icon="👩‍🏫", url_path="pmm_dynamic"),
st.Page("frontend/pages/config/dman_maker_v2/app.py", title="D-Man Maker V2", icon="🤖", url_path="dman_maker_v2"),
st.Page("frontend/pages/config/bollinger_v1/app.py", title="Bollinger V1", icon="📈", url_path="bollinger_v1"),
st.Page("frontend/pages/config/macd_bb_v1/app.py", title="MACD_BB V1", icon="📊", url_path="macd_bb_v1"),
st.Page("frontend/pages/config/supertrend_v1/app.py", title="SuperTrend V1", icon="👨‍🔬", url_path="supertrend_v1"),
st.Page("frontend/pages/config/xemm_controller/app.py", title="XEMM Controller", icon="⚡️", url_path="xemm_controller"),
],
"Data": [
st.Page("frontend/pages/data/download_candles/app.py", title="Download Candles", icon="💹", url_path="download_candles"),
],
"Community Pages": [
st.Page("frontend/pages/data/token_spreads/app.py", title="Token Spreads", icon="🧙", url_path="token_spreads"),
st.Page("frontend/pages/data/tvl_vs_mcap/app.py", title="TVL vs Market Cap", icon="🦉", url_path="tvl_vs_mcap"),
st.Page("frontend/pages/performance/bot_performance/app.py", title="Strategy Performance", icon="📈", url_path="bot_performance"),
]
}


def private_pages():
return [
Section("Bot Orchestration", "🐙"),
Page("frontend/pages/orchestration/instances/app.py", "Instances", "🦅"),
Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy V2", "🚀"),
Page("frontend/pages/orchestration/credentials/app.py", "Credentials", "🔑"),
Page("frontend/pages/orchestration/portfolio/app.py", "Portfolio", "💰"),
]
return {
"Bot Orchestration": [
st.Page("frontend/pages/orchestration/instances/app.py", title="Instances", icon="🦅", url_path="instances"),
st.Page("frontend/pages/orchestration/launch_bot_v2/app.py", title="Deploy V2", icon="🚀", url_path="launch_bot_v2"),
st.Page("frontend/pages/orchestration/credentials/app.py", title="Credentials", icon="🔑", url_path="credentials"),
st.Page("frontend/pages/orchestration/portfolio/app.py", title="Portfolio", icon="💰", url_path="portfolio"),
]
}
50 changes: 38 additions & 12 deletions frontend/st_utils.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
import inspect
import os.path
from pathlib import Path
from typing import Optional, Union

import pandas as pd
import streamlit as st
import streamlit_authenticator as stauth
import yaml
from st_pages import add_page_title, show_pages
from streamlit.commands.page_config import InitialSideBarState, Layout
from yaml import SafeLoader

from CONFIG import AUTH_SYSTEM_ENABLED
from frontend.pages.permissions import main_page, private_pages, public_pages


def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_state="expanded"):
def initialize_st_page(title: str, icon: str, layout: Layout = 'wide', initial_sidebar_state: InitialSideBarState = "expanded"):
st.set_page_config(
page_title=title,
page_icon=icon,
layout=layout,
initial_sidebar_state=initial_sidebar_state
)
caller_frame = inspect.currentframe().f_back

add_page_title(layout=layout, initial_sidebar_state=initial_sidebar_state)

# Add page title
st.title(title)

# Get caller frame info safely
frame: Optional[Union[inspect.FrameInfo, inspect.Traceback]] = None
try:
caller_frame = inspect.currentframe()
if caller_frame is not None:
caller_frame = caller_frame.f_back
if caller_frame is not None:
frame = inspect.getframeinfo(caller_frame)
except Exception:
pass

current_directory = Path(os.path.dirname(inspect.getframeinfo(caller_frame).filename))
readme_path = current_directory / "README.md"
with st.expander("About This Page"):
st.write(readme_path.read_text())
if frame is not None:
current_directory = Path(os.path.dirname(frame.filename))
readme_path = current_directory / "README.md"
with st.expander("About This Page"):
st.write(readme_path.read_text())


def download_csv_button(df: pd.DataFrame, filename: str, key: str):
Expand Down Expand Up @@ -87,7 +100,11 @@ def get_backend_api_client():

def auth_system():
if not AUTH_SYSTEM_ENABLED:
show_pages(main_page() + private_pages() + public_pages())
return {
"Main": main_page(),
**private_pages(),
**public_pages(),
}
else:
with open('credentials.yml') as file:
config = yaml.load(file, Loader=SafeLoader)
Expand All @@ -99,13 +116,22 @@ def auth_system():
config['cookie']['key'],
config['cookie']['expiry_days'],
)
show_pages(main_page() + public_pages())
# Show only public pages for non-authenticated users
st.session_state.authenticator.login()
if st.session_state["authentication_status"] is False:
st.error('Username/password is incorrect')
elif st.session_state["authentication_status"] is None:
st.warning('Please enter your username and password')
return {
"Main": main_page(),
**public_pages()
}
else:
st.session_state.authenticator.logout(location="sidebar")
st.sidebar.write(f'Welcome *{st.session_state["name"]}*')
show_pages(main_page() + private_pages() + public_pages())
# Show all pages for authenticated users
return {
"Main": main_page(),
**private_pages(),
**public_pages(),
}
91 changes: 72 additions & 19 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,76 @@
from frontend.st_utils import auth_system


def patch_modules_streamlit_elements(file_path: str, old_line: str, new_line: str):
import os

import streamlit_elements

relative_file_path = "core/callback.py"
library_root = list(streamlit_elements.__path__)[0]
file_path = os.path.join(library_root, relative_file_path)

with open(file_path, "r") as file:
lines = file.readlines()

is_changed = False
for index, line in enumerate(lines):
if old_line in line:
print(f"Replacing line {index + 1} in {file_path}")
lines[index] = line.replace(old_line, new_line)
is_changed = True

if is_changed:
with open(file_path, "w") as file:
file.writelines(lines)
import importlib
importlib.reload(streamlit_elements)

return True

def patch_streamlit_elements():
# # fix 1.34.0
# patch_modules_streamlit_elements(
# "core/callback.py",
# "from streamlit.components.v1 import components",
# "from streamlit.components.v1 import custom_component as components\n",
# )


#fix 1.40.0
patch_modules_streamlit_elements(
"core/callback.py",
' user_key = kwargs.get("user_key", None)\n',
"""
try:
user_key = None
new_callback_data = kwargs[
"ctx"
].session_state._state._new_session_state.get(
"streamlit_elements.core.frame.elements_frame", None
)
if new_callback_data is not None:
user_key = new_callback_data._key
except:
user_key = None
""".rstrip()
+ "\n",
)


if __name__ == "__main__":
patch_streamlit_elements()

def main():
# readme section
st.markdown("# 📊 Hummingbot Dashboard")
st.markdown("Hummingbot Dashboard is an open source application that helps you create, backtest, and optimize "
"various types of algo trading strategies. Afterwards, you can deploy them as "
"[Hummingbot](http://hummingbot.org)")
st.write("---")
st.header("Watch the Hummingbot Dashboard Tutorial!")
st.video("https://youtu.be/7eHiMPRBQLQ?si=PAvCq0D5QDZz1h1D")
st.header("Feedback and issues")
st.write(
"Please give us feedback in the **#dashboard** channel of the "
"[hummingbot discord](https://discord.gg/hummingbot)! 🙏")
st.write(
"If you encounter any bugs or have suggestions for improvement, please create an issue in the "
"[hummingbot dashboard github](https://github.com/hummingbot/dashboard).")


auth_system()
main()
# Get the navigation structure based on auth state
pages = auth_system()

# Set up navigation once
pg = st.navigation(pages)

# Run the selected page
pg.run()


if __name__ == "__main__":
main()
Loading