Skip to content

Commit

Permalink
Merge pull request #143 from hummingbot/feat/enhance_controllers_conf…
Browse files Browse the repository at this point in the history
…ig_management

Feat/enhance controllers config management
  • Loading branch information
cardosofede authored Jun 21, 2024
2 parents ee05848 + 7f5a58c commit be50f01
Show file tree
Hide file tree
Showing 25 changed files with 547 additions and 297 deletions.
234 changes: 119 additions & 115 deletions backend/services/backend_api_client.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions environment_conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies:
- pip
- pip:
- hummingbot
- numpy==1.26.4
- streamlit==1.33.0
- watchdog
- python-dotenv
Expand Down
81 changes: 63 additions & 18 deletions frontend/components/bot_performance_card.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import time

import pandas as pd
import streamlit as st
from hummingbot.strategy_v2.models.executors import CloseType
from streamlit_elements import mui

from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from frontend.components.dashboard import Dashboard

from backend.services.backend_api_client import BackendAPIClient
from frontend.st_utils import get_backend_api_client

TRADES_TO_SHOW = 5
WIDE_COL_WIDTH = 250
MEDIUM_COL_WIDTH = 170
SMALL_COL_WIDTH = 100
backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
ULTRA_WIDE_COL_WIDTH = 300
WIDE_COL_WIDTH = 160
MEDIUM_COL_WIDTH = 140
SMALL_COL_WIDTH = 110
backend_api_client = get_backend_api_client()


def stop_bot(bot_name):
Expand All @@ -26,19 +27,23 @@ def archive_bot(bot_name):
class BotPerformanceCardV2(Dashboard.Item):
DEFAULT_COLUMNS = [
{"field": 'id', "headerName": 'ID', "width": WIDE_COL_WIDTH},
{"field": 'controller', "headerName": 'Controller', "width": SMALL_COL_WIDTH, "editable": False},
{"field": 'connector', "headerName": 'Connector', "width": SMALL_COL_WIDTH, "editable": False},
{"field": 'trading_pair', "headerName": 'Trading Pair', "width": SMALL_COL_WIDTH, "editable": False},
{"field": 'realized_pnl_quote', "headerName": 'Realized PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'unrealized_pnl_quote', "headerName": 'Unrealized PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'global_pnl_quote', "headerName": 'NET PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'volume_traded', "headerName": 'Volume ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'open_order_volume', "headerName": 'Open Order Volume ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'imbalance', "headerName": 'Imbalance ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'volume_traded', "headerName": 'Volume ($)', "width": SMALL_COL_WIDTH, "editable": False},
{"field": 'open_order_volume', "headerName": 'Liquidity Placed ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'imbalance', "headerName": 'Imbalance ($)', "width": SMALL_COL_WIDTH, "editable": False},
{"field": 'close_types', "headerName": 'Close Types', "width": ULTRA_WIDE_COL_WIDTH, "editable": False}
]
_active_controller_config_selected = []
_stopped_controller_config_selected = []

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
self._backend_api_client = get_backend_api_client()

def _handle_stopped_row_selection(self, params, _):
self._stopped_controller_config_selected = params
Expand Down Expand Up @@ -88,6 +93,8 @@ def __call__(self, bot_name: str):
bot_data = bot_status.get("data")
is_running = bot_data.get("status") == "running"
performance = bot_data.get("performance")
error_logs = bot_data.get("error_logs")
general_logs = bot_data.get("general_logs")
if is_running:
for controller, inner_dict in performance.items():
controller_status = inner_dict.get("status")
Expand All @@ -97,21 +104,35 @@ def __call__(self, bot_name: str):
continue
controller_performance = inner_dict.get("performance")
controller_config = next((config for config in controller_configs if config.get("id") == controller), {})
controller_name = controller_config.get("controller_name", controller)
connector_name = controller_config.get("connector_name", "NaN")
trading_pair = controller_config.get("trading_pair", "NaN")
kill_switch_status = True if controller_config.get("manual_kill_switch") is True else False
realized_pnl_quote = controller_performance.get("realized_pnl_quote", 0)
unrealized_pnl_quote = controller_performance.get("unrealized_pnl_quote", 0)
global_pnl_quote = controller_performance.get("global_pnl_quote", 0)
volume_traded = controller_performance.get("volume_traded", 0)
open_order_volume = controller_performance.get("open_order_volume", 0)
imbalance = controller_performance.get("imbalance", 0)
close_types = controller_performance.get("close_type_counts", {})
tp = close_types.get("CloseType.TAKE_PROFIT", 0)
sl = close_types.get("CloseType.STOP_LOSS", 0)
time_limit = close_types.get("CloseType.TIME_LIMIT", 0)
ts = close_types.get("CloseType.TRAILING_STOP", 0)
refreshed = close_types.get("CloseType.EARLY_STOP", 0)
close_types_str = f"TP: {tp} | SL: {sl} | TS: {ts} | TL: {time_limit} | RS: {refreshed}"
controller_info = {
"id": controller,
"realized_pnl_quote": realized_pnl_quote,
"unrealized_pnl_quote": unrealized_pnl_quote,
"global_pnl_quote": global_pnl_quote,
"volume_traded": volume_traded,
"open_order_volume": open_order_volume,
"imbalance": imbalance,
"controller": controller_name,
"connector": connector_name,
"trading_pair": trading_pair,
"realized_pnl_quote": round(realized_pnl_quote, 2),
"unrealized_pnl_quote": round(unrealized_pnl_quote, 2),
"global_pnl_quote": round(global_pnl_quote, 2),
"volume_traded": round(volume_traded, 2),
"open_order_volume": round(open_order_volume, 2),
"imbalance": round(imbalance, 2),
"close_types": close_types_str,
}
if kill_switch_status:
stopped_controllers_list.append(controller_info)
Expand Down Expand Up @@ -269,6 +290,30 @@ def __call__(self, bot_name: str):
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Stop")
with mui.Accordion(sx={"padding": "10px 15px 10px 15px"}):
with mui.AccordionSummary(expandIcon=mui.icon.ExpandMoreIcon()):
mui.Typography("Error Logs")
with mui.AccordionDetails(sx={"display": "flex", "flexDirection": "column"}):
if len(error_logs) > 0:
for log in error_logs[:50]:
timestamp = log.get("timestamp")
message = log.get("msg")
logger_name = log.get("logger_name")
mui.Typography(f"{timestamp} - {logger_name}: {message}")
else:
mui.Typography("No error logs available.")
with mui.Accordion(sx={"padding": "10px 15px 10px 15px"}):
with mui.AccordionSummary(expandIcon=mui.icon.ExpandMoreIcon()):
mui.Typography("General Logs")
with mui.AccordionDetails(sx={"display": "flex", "flexDirection": "column"}):
if len(general_logs) > 0:
for log in general_logs[:50]:
timestamp = pd.to_datetime(int(log.get("timestamp")), unit="s")
message = log.get("msg")
logger_name = log.get("logger_name")
mui.Typography(f"{timestamp} - {logger_name}: {message}")
else:
mui.Typography("No general logs available.")
except Exception as e:
print(e)
with mui.Card(key=self._key,
Expand Down
35 changes: 25 additions & 10 deletions frontend/components/config_loader.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import streamlit as st
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT

from backend.services.backend_api_client import BackendAPIClient
from frontend.st_utils import get_backend_api_client
from frontend.utils import generate_random_name

backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
backend_api_client = get_backend_api_client()


def get_default_config_loader(controller_name: str):
use_default_config = st.checkbox("Use default config", value=True)
all_configs = backend_api_client.get_all_controllers_config()
if use_default_config:
st.session_state["default_config"] = {}
else:
configs = [config for config in all_configs if config["controller_name"] == controller_name]
default_config = st.selectbox("Select a config", [config["id"] for config in configs])
st.session_state["default_config"] = next((config for config in all_configs if config["id"] == default_config), {})
existing_configs = [config["id"].split("_")[0] for config in all_configs]
default_dict = {"id": generate_random_name(existing_configs)}
default_config = st.session_state.get("default_config")
config_controller_name = st.session_state.get("controller_name", controller_name)
st.write(f"controller_name: {controller_name} | config_controller_name: {config_controller_name}")
if default_config is None or controller_name != config_controller_name:
st.session_state["default_config"] = default_dict
with st.expander("Configurations", expanded=True):
c1, c2 = st.columns(2)
with c1:
use_default_config = st.checkbox("Use default config", value=True)
with c2:
if not use_default_config:
configs = [config for config in all_configs if config["controller_name"] == controller_name]
if len(configs) > 0:
default_config = st.selectbox("Select a config", [config["id"] for config in configs])
st.session_state["default_config"] = next((config for config in all_configs if config["id"] == default_config), None)
st.session_state["default_config"]["id"] = st.session_state["default_config"]["id"].split("_")[0]
else:
st.warning("No existing configs found for this controller.")


3 changes: 2 additions & 1 deletion frontend/components/deploy_v2_with_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from backend.services.backend_api_client import BackendAPIClient
from frontend.st_utils import get_backend_api_client


class LaunchV2WithControllers:
Expand All @@ -14,7 +15,7 @@ class LaunchV2WithControllers:
]

def __init__(self):
self._backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
self._backend_api_client = get_backend_api_client()
self._controller_configs_available = self._backend_api_client.get_all_controllers_config()
self._controller_config_selected = []
self._bot_name = None
Expand Down
12 changes: 9 additions & 3 deletions frontend/components/executors_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs


def get_executors_distribution_inputs(default_spreads=[0.01, 0.02], default_amounts=[0.2, 0.8]):
def get_executors_distribution_inputs(use_custom_spread_units=False):
default_amounts = [0.2, 0.8]
default_config = st.session_state.get("default_config", {})
buy_spreads = default_config.get("buy_spreads", default_spreads)
sell_spreads = default_config.get("sell_spreads", default_spreads)
if use_custom_spread_units:
buy_spreads = [spread / 100 for spread in default_config.get("buy_spreads", [1, 2])]
sell_spreads = [spread / 100 for spread in default_config.get("sell_spreads", [1, 2])]
else:
buy_spreads = default_config.get("buy_spreads", [0.01, 0.02])
sell_spreads = default_config.get("sell_spreads", [0.01, 0.02])

buy_amounts_pct = default_config.get("buy_amounts_pct", default_amounts)
sell_amounts_pct = default_config.get("sell_amounts_pct", default_amounts)
buy_order_levels_def = len(buy_spreads)
Expand Down
Loading

0 comments on commit be50f01

Please sign in to comment.