Files
hummingbot-dashboard/frontend/components/bot_performance_card.py
2024-05-09 10:32:21 -03:00

191 lines
10 KiB
Python

from docker_manager import DockerManager
from streamlit_elements import mui, lazy
from frontend.components.dashboard import Dashboard
import streamlit as st
import time
from utils.os_utils import get_python_files_from_directory, get_yml_files_from_directory
from utils.status_parser import StatusParser
import pandas as pd
import datetime
TRADES_TO_SHOW = 5
WIDE_COL_WIDTH = 180
MEDIUM_COL_WIDTH = 150
def time_ago(ts):
now_utc = datetime.datetime.now(datetime.timezone.utc)
seconds_since_epoch_utc = now_utc.timestamp()
delta = round(seconds_since_epoch_utc - ts / 1000)
if delta < 60:
return f"{delta}s ago"
if delta < 3600:
return f"{delta // 60}m ago"
else:
return f"{delta // 3600}h ago"
class BotPerformanceCard(Dashboard.Item):
def __init__(self, board, x, y, w, h, **item_props):
super().__init__(board, x, y, w, h, **item_props)
@staticmethod
def set_strategy(_, childs, bot_name):
st.session_state.active_bots[bot_name]["selected_strategy"] = childs.props.value
@staticmethod
def start_strategy(bot_name, broker_client):
selected_strategy = st.session_state.active_bots[bot_name]["selected_strategy"]
if selected_strategy.endswith(".py"):
broker_client.start(script=selected_strategy)
elif selected_strategy.endswith(".yml"):
broker_client.import_strategy(strategy=selected_strategy.replace(".yml", ""))
time.sleep(0.5)
broker_client.start()
def __call__(self, bot_config: dict):
bot_name = bot_config["bot_name"]
scripts_directory = f"./hummingbot_files/bots/{bot_config['bot_name']}"
strategies_directory = f"{scripts_directory}/conf/strategies"
scripts = [file.split("/")[-1] for file in get_python_files_from_directory(scripts_directory)]
strategies = [file.split("/")[-1] for file in get_yml_files_from_directory(strategies_directory)]
if bot_config["selected_strategy"] is None:
st.session_state.active_bots[bot_name]["selected_strategy"] = ""
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 "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),
className=self._draggable_class,
)
if bot_config["is_running"]:
with mui.CardContent(sx={"flex": 1}):
# Balances Table
mui.Typography("Balances", variant="h6")
# # Convert list of dictionaries to DataFrame
balances = StatusParser(bot_config["status"], type="balances").parse()
if balances != "No balances":
df_balances = pd.DataFrame(balances)
balances_rows = df_balances.to_dict(orient='records')
balances_cols = [{'field': col, 'headerName': col} for col in df_balances.columns]
for column in balances_cols:
# Customize width for 'exchange' column
if column['field'] == 'Exchange':
column['width'] = WIDE_COL_WIDTH
mui.DataGrid(rows=balances_rows,
columns=balances_cols,
autoHeight=True,
density="compact",
disableColumnSelector=True,
hideFooter=True,
initialState={"columns": {"columnVisibilityModel": {"id": False}}})
else:
mui.Typography(str(balances), sx={"fontSize": "0.75rem"})
# Active Orders Table
mui.Typography("Active Orders", variant="h6", sx={"marginTop": 2})
# Convert list of dictionaries to DataFrame
try:
orders = StatusParser(bot_config["status"], type="orders").parse()
if orders not in ["No active maker orders", "Market connectors are not ready"]:
df_orders = pd.DataFrame(orders)
orders_rows = df_orders.to_dict(orient='records')
orders_cols = [{'field': col, 'headerName': col} for col in df_orders.columns]
for column in orders_cols:
# Customize width for 'exchange' column
if column['field'] == 'Exchange':
column['width'] = WIDE_COL_WIDTH
# Customize width for column
if column['field'] == 'Price':
column['width'] = MEDIUM_COL_WIDTH
mui.DataGrid(rows=orders_rows,
columns=orders_cols,
autoHeight=True,
density="compact",
disableColumnSelector=True,
hideFooter=True,
initialState={"columns": {"columnVisibilityModel": {"id": False}}})
else:
mui.Typography(str(orders), sx={"fontSize": "0.75rem"})
except ValueError as e:
mui.Typography(str(bot_config["status"]), sx={"fontSize": "0.75rem"})
# Trades Table
mui.Typography("Recent Trades", variant="h6", sx={"marginTop": 2})
df_trades = pd.DataFrame(bot_config["trades"])
if not df_trades.empty:
# Add 'id' column to the dataframe by concatenating 'trade_id' and 'trade_timestamp'
df_trades['id'] = df_trades.get('trade_id', '0').astype(str) + df_trades['trade_timestamp'].astype(str)
# Convert timestamp col to datetime
df_trades['trade_timestamp'] = df_trades['trade_timestamp'].astype(int)
# Show last X trades
df_trades = df_trades.sort_values(by='trade_timestamp', ascending=False)
df_trades = df_trades.head(TRADES_TO_SHOW)
df_trades['time_ago'] = df_trades['trade_timestamp'].apply(time_ago)
trades_rows = df_trades.to_dict(orient='records')
trades_cols = [{'field': col, 'headerName': col} for col in df_trades.columns]
for column in trades_cols:
# Customize width for 'market' column
if column['field'] == 'market':
column['width'] = WIDE_COL_WIDTH
if column['field'] == 'trade_timestamp':
column['width'] = MEDIUM_COL_WIDTH
mui.DataGrid(rows=trades_rows,
columns=trades_cols,
autoHeight=True,
density="compact",
disableColumnSelector=True,
hideFooter=True,
initialState={"columns": {"columnVisibilityModel": {"id": False, "trade_id": False, "trade_timestamp": False, "base_asset": False, "quote_asset": False, "raw_json": False}}})
else:
mui.Typography("No trades yet", 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 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)
with mui.Grid(item=True, xs=4):
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.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(InputProps={"readOnly": True},
label="Attach to instance",
value="docker attach " + bot_name,
sx={"width": "100%"})