Skip to content

Commit f087f3b

Browse files
authored
Merge pull request #14 from hummingbot/feat/add_executors_router
Feat/add executors router
2 parents e269114 + e3d013b commit f087f3b

8 files changed

Lines changed: 430 additions & 32 deletions

File tree

hummingbot_api_client/client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ConnectorsRouter,
99
ControllersRouter,
1010
DockerRouter,
11+
ExecutorsRouter,
1112
GatewayRouter,
1213
GatewaySwapRouter,
1314
GatewayCLMMRouter,
@@ -38,6 +39,7 @@ def __init__(
3839
self._connectors: Optional[ConnectorsRouter] = None
3940
self._controllers: Optional[ControllersRouter] = None
4041
self._docker: Optional[DockerRouter] = None
42+
self._executors: Optional[ExecutorsRouter] = None
4143
self._gateway: Optional[GatewayRouter] = None
4244
self._gateway_swap: Optional[GatewaySwapRouter] = None
4345
self._gateway_clmm: Optional[GatewayCLMMRouter] = None
@@ -60,6 +62,7 @@ async def init(self) -> None:
6062
self._connectors = ConnectorsRouter(self._session, self.base_url)
6163
self._controllers = ControllersRouter(self._session, self.base_url)
6264
self._docker = DockerRouter(self._session, self.base_url)
65+
self._executors = ExecutorsRouter(self._session, self.base_url)
6366
self._gateway = GatewayRouter(self._session, self.base_url)
6467
self._gateway_swap = GatewaySwapRouter(self._session, self.base_url)
6568
self._gateway_clmm = GatewayCLMMRouter(self._session, self.base_url)
@@ -80,6 +83,7 @@ async def close(self) -> None:
8083
self._connectors = None
8184
self._controllers = None
8285
self._docker = None
86+
self._executors = None
8387
self._gateway = None
8488
self._gateway_swap = None
8589
self._gateway_clmm = None
@@ -137,6 +141,13 @@ def docker(self) -> DockerRouter:
137141
raise RuntimeError("Client not initialized. Call await client.init() first.")
138142
return self._docker
139143

144+
@property
145+
def executors(self) -> ExecutorsRouter:
146+
"""Access the executors router."""
147+
if self._executors is None:
148+
raise RuntimeError("Client not initialized. Call await client.init() first.")
149+
return self._executors
150+
140151
@property
141152
def gateway(self) -> GatewayRouter:
142153
"""Access the gateway router."""

hummingbot_api_client/routers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .connectors import ConnectorsRouter
77
from .controllers import ControllersRouter
88
from .docker import DockerRouter
9+
from .executors import ExecutorsRouter
910
from .gateway import GatewayRouter
1011
from .gateway_swap import GatewaySwapRouter
1112
from .gateway_clmm import GatewayCLMMRouter
@@ -23,6 +24,7 @@
2324
"ConnectorsRouter",
2425
"ControllersRouter",
2526
"DockerRouter",
27+
"ExecutorsRouter",
2628
"GatewayRouter",
2729
"GatewaySwapRouter",
2830
"GatewayCLMMRouter",
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
from typing import Optional, Dict, Any, List
2+
from .base import BaseRouter
3+
4+
5+
class ExecutorsRouter(BaseRouter):
6+
"""Executors router for managing trading executors and position holds."""
7+
8+
# Executor CRUD Operations
9+
async def create_executor(
10+
self,
11+
executor_config: Dict[str, Any],
12+
account_name: Optional[str] = None
13+
) -> Dict[str, Any]:
14+
"""
15+
Create a new executor with the given configuration.
16+
17+
Args:
18+
executor_config: Executor configuration dictionary
19+
account_name: Account to run the executor on (optional)
20+
21+
Returns:
22+
Created executor information
23+
24+
Example:
25+
executor = await client.executors.create_executor(
26+
{"type": "dca", "trading_pair": "BTC-USDT", ...},
27+
account_name="master_account"
28+
)
29+
"""
30+
params = {}
31+
if account_name is not None:
32+
params["account_name"] = account_name
33+
body = {"executor_config": executor_config}
34+
return await self._post("/executors/", json=body, params=params if params else None)
35+
36+
async def search_executors(
37+
self,
38+
executor_ids: Optional[List[str]] = None,
39+
controller_id: Optional[str] = None,
40+
executor_types: Optional[List[str]] = None,
41+
statuses: Optional[List[str]] = None,
42+
is_active: Optional[bool] = None,
43+
is_archived: Optional[bool] = None,
44+
trading_pair: Optional[str] = None,
45+
connector_name: Optional[str] = None,
46+
account_name: Optional[str] = None,
47+
side: Optional[str] = None,
48+
start_time_from: Optional[int] = None,
49+
start_time_to: Optional[int] = None,
50+
end_time_from: Optional[int] = None,
51+
end_time_to: Optional[int] = None
52+
) -> Dict[str, Any]:
53+
"""
54+
Search for executors with various filters.
55+
56+
Args:
57+
executor_ids: List of executor IDs to filter by
58+
controller_id: Controller ID to filter by
59+
executor_types: List of executor types to filter by
60+
statuses: List of statuses to filter by
61+
is_active: Filter by active status
62+
is_archived: Filter by archived status
63+
trading_pair: Trading pair to filter by
64+
connector_name: Connector name to filter by
65+
account_name: Account name to filter by
66+
side: Trade side to filter by ("buy" or "sell")
67+
start_time_from: Filter executors started after this timestamp
68+
start_time_to: Filter executors started before this timestamp
69+
end_time_from: Filter executors ended after this timestamp
70+
end_time_to: Filter executors ended before this timestamp
71+
72+
Returns:
73+
List of matching executors
74+
75+
Example:
76+
# Get all active executors
77+
executors = await client.executors.search_executors(is_active=True)
78+
79+
# Get executors for a specific trading pair
80+
executors = await client.executors.search_executors(
81+
trading_pair="BTC-USDT",
82+
connector_name="binance_perpetual"
83+
)
84+
"""
85+
filters = {}
86+
if executor_ids is not None:
87+
filters["executor_ids"] = executor_ids
88+
if controller_id is not None:
89+
filters["controller_id"] = controller_id
90+
if executor_types is not None:
91+
filters["executor_types"] = executor_types
92+
if statuses is not None:
93+
filters["statuses"] = statuses
94+
if is_active is not None:
95+
filters["is_active"] = is_active
96+
if is_archived is not None:
97+
filters["is_archived"] = is_archived
98+
if trading_pair is not None:
99+
filters["trading_pair"] = trading_pair
100+
if connector_name is not None:
101+
filters["connector_name"] = connector_name
102+
if account_name is not None:
103+
filters["account_name"] = account_name
104+
if side is not None:
105+
filters["side"] = side
106+
if start_time_from is not None:
107+
filters["start_time_from"] = start_time_from
108+
if start_time_to is not None:
109+
filters["start_time_to"] = start_time_to
110+
if end_time_from is not None:
111+
filters["end_time_from"] = end_time_from
112+
if end_time_to is not None:
113+
filters["end_time_to"] = end_time_to
114+
115+
return await self._post("/executors/search", json=filters)
116+
117+
async def get_summary(self) -> Dict[str, Any]:
118+
"""
119+
Get a summary of all executors.
120+
121+
Returns:
122+
Summary statistics for executors
123+
124+
Example:
125+
summary = await client.executors.get_summary()
126+
"""
127+
return await self._get("/executors/summary")
128+
129+
async def get_executor(self, executor_id: str) -> Dict[str, Any]:
130+
"""
131+
Get a specific executor by ID.
132+
133+
Args:
134+
executor_id: The executor ID to retrieve
135+
136+
Returns:
137+
Executor details
138+
139+
Example:
140+
executor = await client.executors.get_executor("exec_123")
141+
"""
142+
return await self._get(f"/executors/{executor_id}")
143+
144+
async def stop_executor(
145+
self,
146+
executor_id: str,
147+
keep_position: bool = False
148+
) -> Dict[str, Any]:
149+
"""
150+
Stop a running executor.
151+
152+
Args:
153+
executor_id: The executor ID to stop
154+
keep_position: If True, keep the position open after stopping
155+
156+
Returns:
157+
Stop operation result
158+
159+
Example:
160+
# Stop and close position
161+
result = await client.executors.stop_executor("exec_123")
162+
163+
# Stop but keep position open
164+
result = await client.executors.stop_executor("exec_123", keep_position=True)
165+
"""
166+
params = {"keep_position": str(keep_position).lower()}
167+
return await self._post(f"/executors/{executor_id}/stop", json={}, params=params)
168+
169+
async def delete_executor(self, executor_id: str) -> Dict[str, Any]:
170+
"""
171+
Delete an executor.
172+
173+
Args:
174+
executor_id: The executor ID to delete
175+
176+
Returns:
177+
Delete operation result
178+
179+
Example:
180+
result = await client.executors.delete_executor("exec_123")
181+
"""
182+
return await self._delete(f"/executors/{executor_id}")
183+
184+
# Position Hold Management
185+
async def get_positions_summary(self) -> Dict[str, Any]:
186+
"""
187+
Get summary of all positions held by executors.
188+
189+
Returns:
190+
Summary of held positions
191+
192+
Example:
193+
summary = await client.executors.get_positions_summary()
194+
"""
195+
return await self._get("/executors/positions/summary")
196+
197+
async def get_position_held(
198+
self,
199+
connector_name: str,
200+
trading_pair: str,
201+
account_name: Optional[str] = None
202+
) -> Dict[str, Any]:
203+
"""
204+
Get the position held for a specific connector and trading pair.
205+
206+
Args:
207+
connector_name: Exchange connector name
208+
trading_pair: Trading pair (e.g., "BTC-USDT")
209+
account_name: Account name (optional)
210+
211+
Returns:
212+
Position held information
213+
214+
Example:
215+
position = await client.executors.get_position_held(
216+
"binance_perpetual", "BTC-USDT", "master_account"
217+
)
218+
"""
219+
params = {}
220+
if account_name is not None:
221+
params["account_name"] = account_name
222+
return await self._get(
223+
f"/executors/positions/{connector_name}/{trading_pair}",
224+
params=params if params else None
225+
)
226+
227+
async def clear_position_held(
228+
self,
229+
connector_name: str,
230+
trading_pair: str,
231+
account_name: Optional[str] = None
232+
) -> Dict[str, Any]:
233+
"""
234+
Clear the position held for a specific connector and trading pair.
235+
236+
Args:
237+
connector_name: Exchange connector name
238+
trading_pair: Trading pair (e.g., "BTC-USDT")
239+
account_name: Account name (optional)
240+
241+
Returns:
242+
Clear operation result
243+
244+
Example:
245+
result = await client.executors.clear_position_held(
246+
"binance_perpetual", "BTC-USDT", "master_account"
247+
)
248+
"""
249+
params = {}
250+
if account_name is not None:
251+
params["account_name"] = account_name
252+
return await self._delete(
253+
f"/executors/positions/{connector_name}/{trading_pair}",
254+
params=params if params else None
255+
)
256+
257+
# Executor Types/Schema
258+
async def get_available_executor_types(self) -> List[str]:
259+
"""
260+
Get list of available executor types.
261+
262+
Returns:
263+
List of executor type names
264+
265+
Example:
266+
types = await client.executors.get_available_executor_types()
267+
# Returns: ["dca", "grid", "twap", ...]
268+
"""
269+
return await self._get("/executors/types/available")
270+
271+
async def get_executor_config_schema(self, executor_type: str) -> Dict[str, Any]:
272+
"""
273+
Get the configuration schema for a specific executor type.
274+
275+
Args:
276+
executor_type: The executor type to get schema for
277+
278+
Returns:
279+
JSON schema for the executor configuration
280+
281+
Example:
282+
schema = await client.executors.get_executor_config_schema("dca")
283+
"""
284+
return await self._get(f"/executors/types/{executor_type}/config")

0 commit comments

Comments
 (0)