Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@
CreateExecutorResponse,
StopExecutorRequest,
StopExecutorResponse,
DeleteExecutorResponse,
ExecutorFilterRequest,
ExecutorResponse,
ExecutorDetailResponse,
Expand Down Expand Up @@ -389,7 +388,6 @@
"CreateExecutorResponse",
"StopExecutorRequest",
"StopExecutorResponse",
"DeleteExecutorResponse",
"ExecutorFilterRequest",
"ExecutorResponse",
"ExecutorDetailResponse",
Expand Down
18 changes: 4 additions & 14 deletions models/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,6 @@ class ExecutorFilterRequest(PaginationParams):
None,
description="Filter by status (RUNNING, TERMINATED, etc.)"
)
include_completed: bool = Field(
default=False,
description="Include recently completed executors"
)


# ========================================
Expand Down Expand Up @@ -365,31 +361,25 @@ class StopExecutorResponse(BaseModel):


class ExecutorsSummaryResponse(BaseModel):
"""Summary of all executors."""
"""Summary of active executors."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"total_active": 5,
"total_completed": 23,
"total_pnl_quote": 1234.56,
"total_volume_quote": 50000.00,
"by_type": {"position_executor": 3, "grid_executor": 2},
"by_connector": {"binance_perpetual": 4, "binance": 1},
"by_status": {"RUNNING": 5, "TERMINATED": 23}
"by_status": {"RUNNING": 5}
}
}
)

total_active: int = Field(description="Number of active executors")
total_completed: int = Field(description="Number of completed executors")
total_pnl_quote: float = Field(description="Total PnL across all executors")
total_volume_quote: float = Field(description="Total volume across all executors")
total_pnl_quote: float = Field(description="Total PnL across active executors")
total_volume_quote: float = Field(description="Total volume across active executors")
by_type: Dict[str, int] = Field(description="Executor count by type")
by_connector: Dict[str, int] = Field(description="Executor count by connector")
by_status: Dict[str, int] = Field(description="Executor count by status")


class DeleteExecutorResponse(BaseModel):
"""Response after deleting an executor from tracking."""
message: str = Field(description="Success message")
executor_id: str = Field(description="Executor identifier that was removed")
59 changes: 9 additions & 50 deletions routers/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from models.executors import (
CreateExecutorRequest,
CreateExecutorResponse,
DeleteExecutorResponse,
ExecutorDetailResponse,
ExecutorFilterRequest,
ExecutorResponse,
Expand Down Expand Up @@ -75,27 +74,27 @@ async def list_executors(
executor_service: ExecutorService = Depends(get_executor_service)
):
"""
Get list of active executors with optional filtering.
Get list of executors with optional filtering.

Returns active executors from memory combined with completed executors from database.

Filters:
- `account_names`: Filter by specific accounts
- `connector_names`: Filter by connectors
- `trading_pairs`: Filter by trading pairs
- `executor_types`: Filter by executor types
- `status`: Filter by status (RUNNING, TERMINATED, etc.)
- `include_completed`: Include recently completed executors

Returns paginated list of executor summaries.
"""
try:
# Get filtered executors
executors = executor_service.get_executors(
# Get filtered executors (active from memory + completed from DB)
executors = await executor_service.get_executors(
account_name=filter_request.account_names[0] if filter_request.account_names else None,
connector_name=filter_request.connector_names[0] if filter_request.connector_names else None,
trading_pair=filter_request.trading_pairs[0] if filter_request.trading_pairs else None,
executor_type=filter_request.executor_types[0] if filter_request.executor_types else None,
status=filter_request.status,
include_completed=filter_request.include_completed
status=filter_request.status
)

# Apply additional multi-value filters
Expand Down Expand Up @@ -167,13 +166,15 @@ async def get_executor(
"""
Get detailed information about a specific executor.

Checks active executors in memory first, then falls back to database for completed executors.

Returns full executor information including:
- Current status and PnL
- Full configuration
- Executor-specific custom information
"""
try:
executor = executor_service.get_executor(executor_id)
executor = await executor_service.get_executor(executor_id)

if not executor:
raise HTTPException(status_code=404, detail=f"Executor {executor_id} not found")
Expand Down Expand Up @@ -214,48 +215,6 @@ async def stop_executor(
raise HTTPException(status_code=500, detail=f"Error stopping executor: {str(e)}")


@router.delete("/{executor_id}", response_model=DeleteExecutorResponse)
async def delete_executor(
executor_id: str,
executor_service: ExecutorService = Depends(get_executor_service)
):
"""
Remove an executor from tracking.

The executor must be already stopped/completed. This removes it from
the active tracking list but preserves database records for historical queries.

Returns success message if removed.
"""
try:
# Check if executor exists
executor = executor_service.get_executor(executor_id)
if not executor:
raise HTTPException(status_code=404, detail=f"Executor {executor_id} not found")

# Check if still active
if executor.get("is_active", False):
raise HTTPException(
status_code=400,
detail=f"Cannot delete active executor. Stop it first using POST /executors/{executor_id}/stop"
)

# Remove from tracking
removed = executor_service.remove_completed_executor(executor_id)
if not removed:
raise HTTPException(status_code=404, detail=f"Executor {executor_id} not found in completed list")

return DeleteExecutorResponse(
message=f"Executor {executor_id} removed from tracking",
executor_id=executor_id
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error deleting executor {executor_id}: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Error deleting executor: {str(e)}")


# ========================================
# Position Hold Endpoints
# ========================================
Expand Down
Loading