55from fastapi_walletauth import JWTWalletAuthDep
66from utils .models import HummingbotInstanceConfig , StartStrategyRequest , InstanceResponse
77from hummingbot .core .gateway .gateway_http_client import GatewayHttpClient
8+ from routers .strategies_models import (
9+ StrategyError ,
10+ StrategyRegistry ,
11+ StrategyNotFoundError ,
12+ StrategyValidationError
13+ )
814
915router = APIRouter (tags = ["Bot Management" ])
1016accounts_service = AccountsService ()
1117docker_manager = DockerManager ()
1218gateway_client = GatewayHttpClient .get_instance ()
1319
20+ class BotError (StrategyError ):
21+ """Base class for bot-related errors"""
22+
23+ class BotNotFoundError (BotError ):
24+ """Raised when a bot cannot be found"""
25+
26+ class BotPermissionError (BotError ):
27+ """Raised when there's a permission error with bot operations"""
28+
29+ class BotConfigError (BotError ):
30+ """Raised when there's an error in bot configuration"""
1431
1532class CreateBotRequest (BaseModel ):
1633 strategy_name : str
1734 strategy_parameters : dict
1835 market : str
1936
20-
2137@router .post ("/bots" , response_model = InstanceResponse )
2238async def create_bot (request : CreateBotRequest , wallet_auth : JWTWalletAuthDep ):
23- bot_account = f"robotter_{ wallet_auth .address } _{ request .market } _{ request .strategy_name } "
24- accounts_service .add_account (bot_account )
25- wallet_address = await accounts_service .generate_bot_wallet (bot_account )
26-
27- # Save strategy configuration and market
28- bot_config = BotConfig (
29- strategy_name = request .strategy_name ,
30- parameters = request .strategy_parameters ,
31- market = request .market ,
32- wallet_address = wallet_auth .address ,
33- )
34- accounts_service .save_bot_config (bot_account , bot_config )
35- # Create Hummingbot instance
36- instance_config = HummingbotInstanceConfig (
37- instance_name = bot_account , credentials_profile = bot_account , image = "mlguys/hummingbot:mango" , market = request .market
38- )
39- result = docker_manager .create_hummingbot_instance (instance_config )
40-
41- if not result ["success" ]:
42- raise HTTPException (status_code = 500 , detail = result ["message" ])
43-
44- return InstanceResponse (instance_id = bot_account , wallet_address = wallet_address , market = request .market )
45-
39+ try :
40+ # Validate strategy exists and parameters
41+ try :
42+ strategy = StrategyRegistry .get_strategy (request .strategy_name )
43+ except StrategyNotFoundError as e :
44+ raise BotConfigError (f"Invalid strategy: { str (e )} " )
45+
46+ try :
47+ validate_strategy_parameters (strategy , request .strategy_parameters )
48+ except StrategyValidationError as e :
49+ raise BotConfigError (f"Invalid strategy parameters: { str (e )} " )
50+
51+ # Create bot account
52+ bot_account = f"robotter_{ wallet_auth .address } _{ request .market } _{ request .strategy_name } "
53+ try :
54+ accounts_service .add_account (bot_account )
55+ wallet_address = await accounts_service .generate_bot_wallet (bot_account )
56+ except Exception as e :
57+ raise BotError (f"Error creating bot account: { str (e )} " )
58+
59+ # Save strategy configuration and market
60+ try :
61+ bot_config = BotConfig (
62+ strategy_name = request .strategy_name ,
63+ parameters = request .strategy_parameters ,
64+ market = request .market ,
65+ wallet_address = wallet_auth .address ,
66+ )
67+ accounts_service .save_bot_config (bot_account , bot_config )
68+ except Exception as e :
69+ raise BotConfigError (f"Error saving bot configuration: { str (e )} " )
70+
71+ # Create Hummingbot instance
72+ try :
73+ instance_config = HummingbotInstanceConfig (
74+ instance_name = bot_account ,
75+ credentials_profile = bot_account ,
76+ image = "mlguys/hummingbot:mango" ,
77+ market = request .market
78+ )
79+ result = docker_manager .create_hummingbot_instance (instance_config )
80+
81+ if not result ["success" ]:
82+ raise BotError (result ["message" ])
83+ except Exception as e :
84+ raise BotError (f"Error creating Hummingbot instance: { str (e )} " )
85+
86+ return InstanceResponse (
87+ instance_id = bot_account ,
88+ wallet_address = wallet_address ,
89+ market = request .market
90+ )
91+
92+ except BotConfigError as e :
93+ raise HTTPException (status_code = 400 , detail = str (e ))
94+ except BotPermissionError as e :
95+ raise HTTPException (status_code = 403 , detail = str (e ))
96+ except BotError as e :
97+ raise HTTPException (status_code = 500 , detail = str (e ))
98+ except Exception as e :
99+ raise HTTPException (
100+ status_code = 500 ,
101+ detail = f"Unexpected error creating bot: { str (e )} "
102+ )
46103
47104@router .get ("/bots/{bot_id}/wallet" )
48105async def get_bot_wallet (bot_id : str , wallet_auth : JWTWalletAuthDep ):
49106 try :
50107 # Check if the bot belongs to the authenticated user
51108 if not bot_id .endswith (wallet_auth .address ):
52- raise HTTPException ( status_code = 403 , detail = "You don't have permission to access this bot" )
109+ raise BotPermissionError ( "You don't have permission to access this bot" )
53110
54- wallet_address = accounts_service .get_bot_wallet_address (bot_id )
55- return {"wallet_address" : wallet_address }
56- except Exception as e :
57- raise HTTPException (status_code = 404 , detail = str (e ))
111+ try :
112+ wallet_address = accounts_service .get_bot_wallet_address (bot_id )
113+ return {"wallet_address" : wallet_address }
114+ except Exception as e :
115+ raise BotNotFoundError (f"Bot wallet not found: { str (e )} " )
58116
117+ except BotPermissionError as e :
118+ raise HTTPException (status_code = 403 , detail = str (e ))
119+ except BotNotFoundError as e :
120+ raise HTTPException (status_code = 404 , detail = str (e ))
121+ except Exception as e :
122+ raise HTTPException (
123+ status_code = 500 ,
124+ detail = f"Unexpected error getting bot wallet: { str (e )} "
125+ )
59126
60127@router .post ("/bots/{bot_id}/start" )
61128async def start_bot (bot_id : str , start_request : StartStrategyRequest , wallet_auth : JWTWalletAuthDep ):
62- # Check if the bot belongs to the authenticated user
63- bot_config = accounts_service .get_bot_config (bot_id )
64- if not bot_config or bot_config .wallet_address != wallet_auth .address :
65- raise HTTPException (status_code = 403 , detail = "You don't have permission to start this bot" )
66-
67- # Check if Mango account exists and is associated with the bot's wallet
68- bot_wallet = accounts_service .get_bot_wallet_address (bot_id )
69-
70- # We should pass the wallet address
71- mango_account_info = await gateway_client .get_mango_account (
72- "solana" , "mainnet" , "mango_perpetual_solana_mainnet-beta" , bot_wallet
73- )
74-
75- if not mango_account_info or mango_account_info .get ("owner" ) != bot_wallet :
76- raise HTTPException (status_code = 400 , detail = "Invalid Mango account or not associated with the bot's wallet" )
77-
78- # Start the bot
79- strategy_config = accounts_service .get_strategy_config (bot_id )
80- start_config = {** strategy_config , ** start_request .parameters }
81-
82- response = docker_manager .start_bot (bot_id , start_config )
83- if not response ["success" ]:
84- raise HTTPException (status_code = 500 , detail = "Failed to start the bot" )
85-
86- return {"status" : "success" , "message" : "Bot started successfully" }
87-
88-
89- @router .post ("/bots/{bot_id}/stop" )
90- async def stop_bot (bot_id : str , wallet_auth : JWTWalletAuthDep ):
91- # Check if the bot belongs to the authenticated user
92- if not bot_id .endswith (wallet_auth .address ):
93- raise HTTPException (status_code = 403 , detail = "You don't have permission to stop this bot" )
94-
95- # Stop the bot and cancel all orders
96- response = docker_manager .stop_bot (bot_id )
97- if not response ["success" ]:
98- raise HTTPException (status_code = 500 , detail = "Failed to stop the bot" )
99-
100- # Cancel all orders through the gateway
101- bot_wallet = accounts_service .get_bot_wallet_address (bot_id )
102- cancel_orders_response = await gateway_client .clob_perp_get_orders ("solana" , "mainnet" , "mango_perpetual_solana_mainnet-beta" )
103-
104- if not cancel_orders_response ["success" ]:
105- raise HTTPException (status_code = 500 , detail = "Failed to cancel all orders" )
106-
107- return {"status" : "success" , "message" : "Bot stopped and all orders cancelled successfully" }
129+ try :
130+ # Check if the bot belongs to the authenticated user
131+ bot_config = accounts_service .get_bot_config (bot_id )
132+ if not bot_config or bot_config .wallet_address != wallet_auth .address :
133+ raise BotPermissionError ("You don't have permission to start this bot" )
134+
135+ # Check if Mango account exists and is associated with the bot's wallet
136+ try :
137+ bot_wallet = accounts_service .get_bot_wallet_address (bot_id )
138+ mango_account_info = await gateway_client .get_mango_account (
139+ "solana" , "mainnet" , "mango_perpetual_solana_mainnet-beta" , bot_wallet
140+ )
141+
142+ if not mango_account_info or mango_account_info .get ("owner" ) != bot_wallet :
143+ raise BotConfigError ("Invalid Mango account or not associated with the bot's wallet" )
144+ except Exception as e :
145+ raise BotConfigError (f"Error validating Mango account: { str (e )} " )
146+
147+ # Start the bot
148+ try :
149+ strategy_config = accounts_service .get_strategy_config (bot_id )
150+ start_config = {** strategy_config , ** start_request .parameters }
151+
152+ response = docker_manager .start_bot (bot_id , start_config )
153+ if not response ["success" ]:
154+ raise BotError ("Failed to start the bot" )
155+
156+ return {"status" : "success" , "message" : "Bot started successfully" }
157+ except Exception as e :
158+ raise BotError (f"Error starting bot: { str (e )} " )
159+
160+ except BotPermissionError as e :
161+ raise HTTPException (status_code = 403 , detail = str (e ))
162+ except BotConfigError as e :
163+ raise HTTPException (status_code = 400 , detail = str (e ))
164+ except BotError as e :
165+ raise HTTPException (status_code = 500 , detail = str (e ))
166+ except Exception as e :
167+ raise HTTPException (
168+ status_code = 500 ,
169+ detail = f"Unexpected error starting bot: { str (e )} "
170+ )
171+
172+ def validate_strategy_parameters (strategy , parameters : dict ) -> None :
173+ """Validate strategy parameters against their constraints"""
174+ for param_name , value in parameters .items ():
175+ if param_name not in strategy .parameters :
176+ raise StrategyValidationError (f"Unknown parameter: { param_name } " )
177+
178+ param = strategy .parameters [param_name ]
179+ constraints = param .constraints
180+
181+ if constraints :
182+ if constraints .min_value is not None and value < constraints .min_value :
183+ raise StrategyValidationError (
184+ f"Parameter { param_name } value { value } is below minimum { constraints .min_value } "
185+ )
186+
187+ if constraints .max_value is not None and value > constraints .max_value :
188+ raise StrategyValidationError (
189+ f"Parameter { param_name } value { value } is above maximum { constraints .max_value } "
190+ )
191+
192+ if constraints .valid_values is not None and value not in constraints .valid_values :
193+ raise StrategyValidationError (
194+ f"Parameter { param_name } value { value } is not one of the valid values: { constraints .valid_values } "
195+ )
0 commit comments