(feat) add error handling for active instances

This commit is contained in:
cardosofede
2024-05-26 17:51:47 -05:00
parent 1a5ad0b6ec
commit 06ce36d304
3 changed files with 236 additions and 192 deletions

View File

@@ -109,7 +109,9 @@ class BackendAPIClient:
url = f"{self.base_url}/get-bot-status/{bot_name}" url = f"{self.base_url}/get-bot-status/{bot_name}"
response = requests.get(url) response = requests.get(url)
if response.status_code == 200: if response.status_code == 200:
return response.json()["data"] return response.json()
else:
return {"status": "error", "data": "Bot not found"}
def get_bot_history(self, bot_name: str): def get_bot_history(self, bot_name: str):
"""Get the historical data of a bot.""" """Get the historical data of a bot."""
@@ -127,7 +129,7 @@ class BackendAPIClient:
if response.status_code == 200: if response.status_code == 200:
return response.json() # Successful request return response.json() # Successful request
else: else:
return response.json() # Handle errors or no data found return {"status": "error", "data": "No active bots found"}
def get_all_controllers_config(self): def get_all_controllers_config(self):
"""Get all controller configurations.""" """Get all controller configurations."""

View File

@@ -1,3 +1,5 @@
import time
from streamlit_elements import mui from streamlit_elements import mui
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
@@ -44,10 +46,17 @@ class BotPerformanceCardV2(Dashboard.Item):
def _handle_active_row_selection(self, params, _): def _handle_active_row_selection(self, params, _):
self._active_controller_config_selected = params self._active_controller_config_selected = params
def stop_controllers(self, bot_name): def _handle_errors_row_selection(self, params, _):
self._error_controller_config_selected = params
def stop_active_controllers(self, bot_name):
for controller in self._active_controller_config_selected: for controller in self._active_controller_config_selected:
self._backend_api_client.stop_controller_from_bot(bot_name, controller) self._backend_api_client.stop_controller_from_bot(bot_name, controller)
def stop_errors_controllers(self, bot_name):
for controller in self._error_controller_config_selected:
self._backend_api_client.stop_controller_from_bot(bot_name, controller)
def start_controllers(self, bot_name): def start_controllers(self, bot_name):
for controller in self._stopped_controller_config_selected: for controller in self._stopped_controller_config_selected:
self._backend_api_client.start_controller_from_bot(bot_name, controller) self._backend_api_client.start_controller_from_bot(bot_name, controller)
@@ -56,166 +65,210 @@ class BotPerformanceCardV2(Dashboard.Item):
try: try:
controller_configs = backend_api_client.get_all_configs_from_bot(bot_name) controller_configs = backend_api_client.get_all_configs_from_bot(bot_name)
bot_status = backend_api_client.get_bot_status(bot_name) bot_status = backend_api_client.get_bot_status(bot_name)
is_running = False # Controllers Table
if bot_status and len(bot_status) > 0: active_controllers_list = []
# Controllers Table stopped_controllers_list = []
active_controllers_list = [] error_controllers_list = []
stopped_controllers_list = [] total_global_pnl_quote = 0
total_global_pnl_quote = 0 total_volume_traded = 0
total_volume_traded = 0 total_open_order_volume = 0
total_open_order_volume = 0 total_imbalance = 0
total_imbalance = 0 total_unrealized_pnl_quote = 0
total_unrealized_pnl_quote = 0 if bot_status.get("status") == "error":
for controller, inner_dict in bot_status.items(): with mui.Card(key=self._key,
controller_config = next((config for config in controller_configs if config.get("id") == controller), sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
{}) elevation=2):
kill_switch_status = True if controller_config.get("manual_kill_switch") is True else False mui.CardHeader(
realized_pnl_quote = inner_dict.get("realized_pnl_quote", 0) title=bot_name,
unrealized_pnl_quote = inner_dict.get("unrealized_pnl_quote", 0) subheader="Not Available",
global_pnl_quote = inner_dict.get("global_pnl_quote", 0) avatar=mui.Avatar("🤖", sx={"bgcolor": "red"}),
volume_traded = inner_dict.get("volume_traded", 0) className=self._draggable_class)
open_order_volume = inner_dict.get("open_order_volume", 0) mui.Alert(f"An error occurred while fetching bot status of the bot {bot_name}. Please check the bot client.", severity="error")
imbalance = inner_dict.get("imbalance", 0) else:
controller_info = { bot_data = bot_status.get("data")
"id": controller, is_running = bot_data.get("status") == "running"
"realized_pnl_quote": realized_pnl_quote, performance = bot_data.get("performance")
"unrealized_pnl_quote": unrealized_pnl_quote, if is_running:
"global_pnl_quote": global_pnl_quote, for controller, inner_dict in performance.items():
"volume_traded": volume_traded, controller_status = inner_dict.get("status")
"open_order_volume": open_order_volume, if controller_status == "error":
"imbalance": imbalance, error_controllers_list.append(
} {"id": controller, "error": inner_dict.get("error")})
if len(controller_info) > 0: continue
is_running = True controller_performance = inner_dict.get("performance")
if kill_switch_status: controller_config = next((config for config in controller_configs if config.get("id") == controller), {})
stopped_controllers_list.append(controller_info) kill_switch_status = True if controller_config.get("manual_kill_switch") is True else False
else: realized_pnl_quote = controller_performance.get("realized_pnl_quote", 0)
active_controllers_list.append(controller_info) unrealized_pnl_quote = controller_performance.get("unrealized_pnl_quote", 0)
total_global_pnl_quote += global_pnl_quote global_pnl_quote = controller_performance.get("global_pnl_quote", 0)
total_volume_traded += volume_traded volume_traded = controller_performance.get("volume_traded", 0)
total_open_order_volume += open_order_volume open_order_volume = controller_performance.get("open_order_volume", 0)
total_imbalance += imbalance imbalance = controller_performance.get("imbalance", 0)
total_unrealized_pnl_quote += unrealized_pnl_quote 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,
}
if kill_switch_status:
stopped_controllers_list.append(controller_info)
else:
active_controllers_list.append(controller_info)
total_global_pnl_quote += global_pnl_quote
total_volume_traded += volume_traded
total_open_order_volume += open_order_volume
total_imbalance += imbalance
total_unrealized_pnl_quote += unrealized_pnl_quote
total_global_pnl_pct = total_global_pnl_quote / total_volume_traded if total_volume_traded > 0 else 0 total_global_pnl_pct = total_global_pnl_quote / total_volume_traded if total_volume_traded > 0 else 0
if is_running:
status = "Running"
color = "green"
else:
status = "Stopped"
color = "red"
with mui.Card(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
elevation=2):
mui.CardHeader(
title=bot_name,
subheader=status,
avatar=mui.Avatar("🤖", sx={"bgcolor": color}),
action=mui.IconButton(mui.icon.Stop, onClick=lambda: stop_bot(bot_name)) if is_running else mui.IconButton(mui.icon.Archive, onClick=lambda: archive_bot(bot_name)),
className=self._draggable_class)
if is_running: if is_running:
with mui.CardContent(sx={"flex": 1}): status = "Running"
with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}): color = "green"
with mui.Grid(item=True, xs=2): else:
with mui.Paper(key=self._key, status = "Stopped"
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, color = "red"
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("🏦 NET PNL", variant="h6")
mui.Typography(f"$ {total_global_pnl_quote:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📊 NET PNL (%)", variant="h6")
mui.Typography(f"{total_global_pnl_pct:.2%}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💸 Volume Traded", variant="h6")
mui.Typography(f"$ {total_volume_traded:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📖 Liquidity Placed", variant="h6")
mui.Typography(f"$ {total_open_order_volume:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💹 Unrealized PNL", variant="h6")
mui.Typography(f"$ {total_unrealized_pnl_quote:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📊 Imbalance", variant="h6")
mui.Typography(f"$ {total_imbalance:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}): with mui.Card(key=self._key,
with mui.Grid(item=True, xs=11): sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
with mui.Paper(key=self._key, elevation=2):
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, mui.CardHeader(
"overflow": "hidden"}, title=bot_name,
elevation=1): subheader=status,
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): avatar=mui.Avatar("🤖", sx={"bgcolor": color}),
mui.Typography("🚀 Active Controllers", variant="h6") action=mui.IconButton(mui.icon.Stop, onClick=lambda: stop_bot(bot_name)) if is_running else mui.IconButton(mui.icon.Archive, onClick=lambda: archive_bot(bot_name)),
mui.DataGrid( className=self._draggable_class)
rows=active_controllers_list, if is_running:
columns=self.DEFAULT_COLUMNS, with mui.CardContent(sx={"flex": 1}):
autoHeight=True, with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}):
density="compact", with mui.Grid(item=True, xs=2):
checkboxSelection=True, with mui.Paper(key=self._key,
disableSelectionOnClick=True, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
onSelectionModelChange=self._handle_active_row_selection, "overflow": "hidden"},
hideFooter=True elevation=1):
) with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
with mui.Grid(item=True, xs=1): mui.Typography("🏦 NET PNL", variant="h6")
with mui.Button(onClick=lambda x: self.stop_controllers(bot_name), mui.Typography(f"$ {total_global_pnl_quote:.3f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
variant="outlined", with mui.Grid(item=True, xs=2):
color="warning", with mui.Paper(key=self._key,
sx={"width": "100%", "height": "100%"}): sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
mui.icon.AddCircleOutline() "overflow": "hidden"},
mui.Typography("Stop") elevation=1):
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}): with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
with mui.Grid(item=True, xs=11): mui.Typography("📊 NET PNL (%)", variant="h6")
with mui.Paper(key=self._key, mui.Typography(f"{total_global_pnl_pct:.3%}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, with mui.Grid(item=True, xs=2):
"overflow": "hidden"}, with mui.Paper(key=self._key,
elevation=1): sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): "overflow": "hidden"},
mui.Typography("💤 Stopped Controllers", variant="h6") elevation=1):
mui.DataGrid( with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
rows=stopped_controllers_list, mui.Typography("💸 Volume Traded", variant="h6")
columns=self.DEFAULT_COLUMNS, mui.Typography(f"$ {total_volume_traded:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
autoHeight=True, with mui.Grid(item=True, xs=2):
density="compact", with mui.Paper(key=self._key,
checkboxSelection=True, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
disableSelectionOnClick=True, "overflow": "hidden"},
onSelectionModelChange=self._handle_stopped_row_selection, elevation=1):
hideFooter=True with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
) mui.Typography("📖 Liquidity Placed", variant="h6")
with mui.Grid(item=True, xs=1): mui.Typography(f"$ {total_open_order_volume:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Button(onClick=lambda x: self.start_controllers(bot_name), with mui.Grid(item=True, xs=2):
variant="outlined", with mui.Paper(key=self._key,
color="success", sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
sx={"width": "100%", "height": "100%"}): "overflow": "hidden"},
mui.icon.AddCircleOutline() elevation=1):
mui.Typography("Start") with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💹 Unrealized PNL", variant="h6")
mui.Typography(f"$ {total_unrealized_pnl_quote:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📊 Imbalance", variant="h6")
mui.Typography(f"$ {total_imbalance:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("🚀 Active Controllers", variant="h6")
mui.DataGrid(
rows=active_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_active_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.stop_active_controllers(bot_name),
variant="outlined",
color="warning",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Stop")
if len(stopped_controllers_list) > 0:
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💤 Stopped Controllers", variant="h6")
mui.DataGrid(
rows=stopped_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_stopped_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.start_controllers(bot_name),
variant="outlined",
color="success",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Start")
if len(error_controllers_list) > 0:
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💀 Controllers with errors", variant="h6")
mui.DataGrid(
rows=error_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_errors_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.stop_errors_controllers(bot_name),
variant="outlined",
color="warning",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Stop")
except Exception as e: except Exception as e:
print(e) print(e)
with mui.Card(key=self._key, with mui.Card(key=self._key,
@@ -229,4 +282,4 @@ class BotPerformanceCardV2(Dashboard.Item):
className=self._draggable_class) className=self._draggable_class)
with mui.CardContent(sx={"flex": 1}): with mui.CardContent(sx={"flex": 1}):
mui.Typography("An error occurred while fetching bot status.", sx={"padding": "10px 15px 10px 15px"}) mui.Typography("An error occurred while fetching bot status.", sx={"padding": "10px 15px 10px 15px"})
mui.Typography(str(e), sx={"padding": "10px 15px 10px 15px"}) mui.Typography(str(e), sx={"padding": "10px 15px 10px 15px"})

View File

@@ -21,7 +21,7 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int
return sorted(x_y, key=lambda x: (x[1], x[0])) return sorted(x_y, key=lambda x: (x[1], x[0]))
def update_active_bots(api_client, active_instances_board): def update_active_bots(api_client):
active_bots_response = api_client.get_active_bots_status() active_bots_response = api_client.get_active_bots_status()
if active_bots_response.get("status") == "success": if active_bots_response.get("status") == "success":
current_active_bots = active_bots_response.get("data") current_active_bots = active_bots_response.get("data")
@@ -29,16 +29,12 @@ def update_active_bots(api_client, active_instances_board):
new_bots = set(current_active_bots.keys()) - set(stored_bots.keys()) new_bots = set(current_active_bots.keys()) - set(stored_bots.keys())
removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys()) removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys())
for bot in new_bots:
x, y = get_grid_positions(1)[0] # Get a new position
card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT)
st.session_state.active_instances_board.bot_cards.append((card, bot))
for bot in removed_bots: for bot in removed_bots:
st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot] st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot]
positions = get_grid_positions(len(current_active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT)
st.session_state.active_instances_board.bot_cards.sort(key=lambda x: x[1]) # Sort by bot name for bot, (x, y) in zip(new_bots, positions[:len(new_bots)]):
card = BotPerformanceCardV2(st.session_state.active_instances_board.dashboard, x, y, CARD_WIDTH, CARD_HEIGHT)
st.session_state.active_instances_board.bot_cards.append((card, bot))
initialize_st_page(title="Instances", icon="🦅") initialize_st_page(title="Instances", icon="🦅")
@@ -49,26 +45,23 @@ if not api_client.is_docker_running():
st.stop() st.stop()
if "active_instances_board" not in st.session_state: if "active_instances_board" not in st.session_state:
try: active_bots_response = api_client.get_active_bots_status()
active_bots_response = api_client.get_active_bots_status() bot_cards = []
active_bots = active_bots_response.get("data") board = Dashboard()
bot_cards = [] st.session_state.active_instances_board = SimpleNamespace(
board = Dashboard() dashboard=board,
if active_bots: bot_cards=bot_cards,
positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) )
for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): active_bots = active_bots_response.get("data")
card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT) number_of_bots = len(active_bots)
bot_cards.append((card, bot)) if number_of_bots > 0:
st.session_state.active_instances_board = SimpleNamespace( positions = get_grid_positions(number_of_bots, NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT)
dashboard=board, for (bot, bot_info), (x, y) in zip(active_bots.items(), positions):
bot_cards=bot_cards, bot_status = api_client.get_bot_status(bot)
) card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT)
except Exception as e: st.session_state.active_instances_board.bot_cards.append((card, bot))
st.error(f"Error fetching active bots, reload the page if persists: {e}")
st.rerun()
else: else:
active_instances_board = st.session_state.active_instances_board update_active_bots(api_client)
update_active_bots(api_client, active_instances_board)
with elements("active_instances_board"): with elements("active_instances_board"):
with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): with mui.Paper(sx={"padding": "2rem"}, variant="outlined"):
@@ -76,7 +69,3 @@ with elements("active_instances_board"):
for card, bot in st.session_state.active_instances_board.bot_cards: for card, bot in st.session_state.active_instances_board.bot_cards:
with st.session_state.active_instances_board.dashboard(): with st.session_state.active_instances_board.dashboard():
card(bot) card(bot)
while True:
time.sleep(2)
st.rerun()