mirror of
https://github.com/d0zingcat/deploy.git
synced 2026-05-13 15:09:33 +00:00
297 lines
10 KiB
Python
297 lines
10 KiB
Python
import re
|
|
import time
|
|
|
|
import pandas as pd
|
|
import streamlit as st
|
|
|
|
from frontend.st_utils import get_backend_api_client, initialize_st_page
|
|
|
|
initialize_st_page(icon="🙌", show_readme=False)
|
|
|
|
# Initialize backend client
|
|
backend_api_client = get_backend_api_client()
|
|
|
|
|
|
def get_controller_configs():
|
|
"""Get all controller configurations using the new API."""
|
|
try:
|
|
return backend_api_client.controllers.list_controller_configs()
|
|
except Exception as e:
|
|
st.error(f"Failed to fetch controller configs: {e}")
|
|
return []
|
|
|
|
|
|
def filter_hummingbot_images(images):
|
|
"""Filter images to only show Hummingbot-related ones."""
|
|
hummingbot_images = []
|
|
pattern = r'.+/hummingbot:'
|
|
|
|
for image in images:
|
|
try:
|
|
if re.match(pattern, image):
|
|
hummingbot_images.append(image)
|
|
except Exception:
|
|
continue
|
|
|
|
return hummingbot_images
|
|
|
|
|
|
def launch_new_bot(bot_name, image_name, credentials, selected_controllers, max_global_drawdown,
|
|
max_controller_drawdown):
|
|
"""Launch a new bot with the selected configuration."""
|
|
if not bot_name:
|
|
st.warning("You need to define the bot name.")
|
|
return False
|
|
if not image_name:
|
|
st.warning("You need to select the hummingbot image.")
|
|
return False
|
|
if not selected_controllers:
|
|
st.warning("You need to select the controllers configs. Please select at least one controller "
|
|
"config by clicking on the checkbox.")
|
|
return False
|
|
|
|
start_time_str = time.strftime("%Y%m%d-%H%M")
|
|
full_bot_name = f"{bot_name}-{start_time_str}"
|
|
|
|
try:
|
|
# Use the new deploy_v2_controllers method
|
|
deploy_config = {
|
|
"instance_name": full_bot_name,
|
|
"credentials_profile": credentials,
|
|
"controllers_config": [config.replace(".yml", "") for config in selected_controllers],
|
|
"image": image_name,
|
|
}
|
|
|
|
# Add optional drawdown parameters if set
|
|
if max_global_drawdown is not None and max_global_drawdown > 0:
|
|
deploy_config["max_global_drawdown_quote"] = max_global_drawdown
|
|
if max_controller_drawdown is not None and max_controller_drawdown > 0:
|
|
deploy_config["max_controller_drawdown_quote"] = max_controller_drawdown
|
|
|
|
backend_api_client.bot_orchestration.deploy_v2_controllers(**deploy_config)
|
|
st.success(f"Successfully deployed bot: {full_bot_name}")
|
|
time.sleep(3)
|
|
return True
|
|
|
|
except Exception as e:
|
|
st.error(f"Failed to deploy bot: {e}")
|
|
return False
|
|
|
|
|
|
def delete_selected_configs(selected_controllers):
|
|
"""Delete selected controller configurations."""
|
|
if selected_controllers:
|
|
try:
|
|
for config in selected_controllers:
|
|
# Remove .yml extension if present
|
|
config_name = config.replace(".yml", "")
|
|
response = backend_api_client.controllers.delete_controller_config(config_name)
|
|
st.success(f"Deleted {config_name}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
st.error(f"Failed to delete configs: {e}")
|
|
return False
|
|
else:
|
|
st.warning("You need to select the controllers configs that you want to delete.")
|
|
return False
|
|
|
|
|
|
# Page Header
|
|
st.title("🚀 Deploy Trading Bot")
|
|
st.subheader("Configure and deploy your automated trading strategy")
|
|
|
|
# Bot Configuration Section
|
|
with st.container(border=True):
|
|
st.info("🤖 **Bot Configuration:** Set up your bot instance with basic configuration")
|
|
|
|
# Create three columns for the configuration inputs
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
bot_name = st.text_input(
|
|
"Instance Name",
|
|
placeholder="Enter a unique name for your bot instance",
|
|
key="bot_name_input"
|
|
)
|
|
|
|
with col2:
|
|
try:
|
|
available_credentials = backend_api_client.accounts.list_accounts()
|
|
credentials = st.selectbox(
|
|
"Credentials Profile",
|
|
options=available_credentials,
|
|
index=0,
|
|
key="credentials_select"
|
|
)
|
|
except Exception as e:
|
|
st.error(f"Failed to fetch credentials: {e}")
|
|
credentials = st.text_input(
|
|
"Credentials Profile",
|
|
value="master_account",
|
|
key="credentials_input"
|
|
)
|
|
|
|
with col3:
|
|
try:
|
|
all_images = backend_api_client.docker.get_available_images("hummingbot")
|
|
available_images = filter_hummingbot_images(all_images)
|
|
|
|
if not available_images:
|
|
# Fallback to default if no hummingbot images found
|
|
available_images = ["hummingbot/hummingbot:latest"]
|
|
|
|
# Ensure default image is in the list
|
|
default_image = "hummingbot/hummingbot:latest"
|
|
if default_image not in available_images:
|
|
available_images.insert(0, default_image)
|
|
|
|
image_name = st.selectbox(
|
|
"Hummingbot Image",
|
|
options=available_images,
|
|
index=0,
|
|
key="image_select"
|
|
)
|
|
except Exception as e:
|
|
st.error(f"Failed to fetch available images: {e}")
|
|
image_name = st.text_input(
|
|
"Hummingbot Image",
|
|
value="hummingbot/hummingbot:latest",
|
|
key="image_input"
|
|
)
|
|
|
|
# Risk Management Section
|
|
with st.container(border=True):
|
|
st.warning("⚠️ **Risk Management:** Set maximum drawdown limits in USDT to protect your capital")
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
max_global_drawdown = st.number_input(
|
|
"Max Global Drawdown (USDT)",
|
|
min_value=0.0,
|
|
value=0.0,
|
|
step=100.0,
|
|
format="%.2f",
|
|
help="Maximum allowed drawdown across all controllers",
|
|
key="global_drawdown_input"
|
|
)
|
|
|
|
with col2:
|
|
max_controller_drawdown = st.number_input(
|
|
"Max Controller Drawdown (USDT)",
|
|
min_value=0.0,
|
|
value=0.0,
|
|
step=100.0,
|
|
format="%.2f",
|
|
help="Maximum allowed drawdown per controller",
|
|
key="controller_drawdown_input"
|
|
)
|
|
|
|
# Controllers Section
|
|
with st.container(border=True):
|
|
st.success("🎛️ **Controller Selection:** Select the trading controllers you want to deploy with this bot instance")
|
|
|
|
# Get controller configs
|
|
all_controllers_config = get_controller_configs()
|
|
|
|
# Prepare data for the table
|
|
data = []
|
|
for config in all_controllers_config:
|
|
# Handle case where config might be a string instead of dict
|
|
if isinstance(config, str):
|
|
st.warning(f"Unexpected config format: {config}. Expected a dictionary.")
|
|
continue
|
|
|
|
# Handle both old and new config format
|
|
config_name = config.get("id")
|
|
config_data = config.get("config", config) # New format has config nested
|
|
|
|
connector_name = config_data.get("connector_name", "Unknown")
|
|
trading_pair = config_data.get("trading_pair", "Unknown")
|
|
total_amount_quote = float(config_data.get("total_amount_quote", 0))
|
|
|
|
# Extract controller info
|
|
controller_name = config_data.get("controller_name", config_name)
|
|
controller_type = config_data.get("controller_type", "generic")
|
|
|
|
# Fix config base and version splitting
|
|
config_parts = config_name.split("_")
|
|
if len(config_parts) > 1:
|
|
version = config_parts[-1]
|
|
config_base = "_".join(config_parts[:-1])
|
|
else:
|
|
config_base = config_name
|
|
version = "NaN"
|
|
|
|
data.append({
|
|
"Select": False, # Checkbox column
|
|
"Config Base": config_base,
|
|
"Version": version,
|
|
"Controller Name": controller_name,
|
|
"Controller Type": controller_type,
|
|
"Connector": connector_name,
|
|
"Trading Pair": trading_pair,
|
|
"Amount (USDT)": f"${total_amount_quote:,.2f}",
|
|
"_config_name": config_name # Hidden column for reference
|
|
})
|
|
|
|
# Display info and action buttons
|
|
if data:
|
|
# Create DataFrame
|
|
df = pd.DataFrame(data)
|
|
|
|
# Use data_editor with checkbox column for selection
|
|
edited_df = st.data_editor(
|
|
df,
|
|
column_config={
|
|
"Select": st.column_config.CheckboxColumn(
|
|
"Select",
|
|
help="Select controllers to deploy or delete",
|
|
default=False,
|
|
),
|
|
"_config_name": None, # Hide this column
|
|
},
|
|
disabled=[col for col in df.columns if col != "Select"], # Only allow editing the Select column
|
|
hide_index=True,
|
|
use_container_width=True,
|
|
key="controller_table"
|
|
)
|
|
|
|
# Get selected controllers from the edited dataframe
|
|
selected_controllers = [
|
|
row["_config_name"]
|
|
for _, row in edited_df.iterrows()
|
|
if row["Select"]
|
|
]
|
|
|
|
# Display selected count
|
|
if selected_controllers:
|
|
st.success(f"✅ {len(selected_controllers)} controller(s) selected for deployment")
|
|
|
|
# Display action buttons
|
|
st.divider()
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
if st.button("🗑️ Delete Selected", type="secondary", use_container_width=True):
|
|
if selected_controllers:
|
|
if delete_selected_configs(selected_controllers):
|
|
st.rerun()
|
|
else:
|
|
st.warning("Please select at least one controller to delete")
|
|
|
|
with col2:
|
|
deploy_button_style = "primary" if selected_controllers else "secondary"
|
|
if st.button("🚀 Deploy Bot", type=deploy_button_style, use_container_width=True):
|
|
if selected_controllers:
|
|
with st.spinner('🚀 Starting Bot... This process may take a few seconds'):
|
|
if launch_new_bot(bot_name, image_name, credentials, selected_controllers,
|
|
max_global_drawdown, max_controller_drawdown):
|
|
st.rerun()
|
|
else:
|
|
st.warning("Please select at least one controller to deploy")
|
|
|
|
else:
|
|
st.warning("⚠️ No controller configurations available. Please create some configurations first.")
|