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
12 changes: 12 additions & 0 deletions hummingbot_mcp/executor_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@
# Note: side is determined by amounts at creation time, not defaulted
```

### Swap Executor Defaults

```yaml
swap_executor:
# Set your preferred defaults here (all optional, ask user if not set):
# connector_name: jupiter/router
# trading_pair: SOL-USDC
# side: BUY # BUY or SELL
# amount: "1.0" # Base token amount
# slippage_pct: "0.5" # Optional: override default slippage
```

---

*Last updated: Never*
Expand Down
110 changes: 110 additions & 0 deletions hummingbot_mcp/guides/swap_executor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
### Swap Executor
**Execute single swaps on Gateway AMM connectors with retry logic.**

Executes token swaps on Gateway-connected DEXs (Jupiter, Raydium, etc.) with
built-in retry handling for transaction timeouts and failures.

**Use when:**
- Executing a single token swap on Solana DEXs
- Need reliable swap execution with automatic retries
- Trading via Gateway AMM connectors

**Avoid when:**
- Need complex trading strategies (use other executors)
- Want to manage LP positions (use lp_executor)
- Trading on CEX (use order_executor)

#### State Machine

```
NOT_STARTED → EXECUTING → COMPLETED (success)
→ FAILED (max retries)
```

- **NOT_STARTED**: Initial state, swap not yet attempted
- **EXECUTING**: Swap submitted, waiting for confirmation (with retries)
- **COMPLETED**: Swap successfully completed
- **FAILED**: Swap failed after max retry attempts

#### Key Parameters

**Required:**
- `connector_name`: Gateway router connector (e.g., `jupiter/router`)
- `trading_pair`: Token pair (e.g., `SOL-USDC`)
- `side`: `BUY` (1) or `SELL` (2)
- `amount`: Amount of base token to swap

**Optional:**
- `slippage_pct`: Override default slippage tolerance

#### Side (Trade Direction)

**BUY (side=1 or "BUY"):**
- Buy base token using quote token
- Example: BUY SOL-USDC means buy SOL, pay USDC
- `amount` specifies how much SOL you want to receive

**SELL (side=2 or "SELL"):**
- Sell base token to receive quote token
- Example: SELL SOL-USDC means sell SOL, receive USDC
- `amount` specifies how much SOL you want to sell

#### Example Configurations

**Buy SOL with USDC:**
```yaml
swap_executor:
connector_name: jupiter/router
trading_pair: SOL-USDC
side: BUY
amount: "1.0" # Buy 1 SOL
```

**Sell SOL for USDC:**
```yaml
swap_executor:
connector_name: jupiter/router
trading_pair: SOL-USDC
side: SELL
amount: "0.5" # Sell 0.5 SOL
```

**With custom slippage:**
```yaml
swap_executor:
connector_name: jupiter/router
trading_pair: SOL-USDC
side: BUY
amount: "1.0"
slippage_pct: "1.0" # 1% slippage tolerance
```

#### Retry Behavior

The SwapExecutor uses the GatewayRetryMixin for robust error handling:

- **Transaction timeouts**: Automatically retries (chain congestion)
- **Price movement errors**: Retries without counting against max retries
- **Slippage errors**: Retries without counting against max retries
- **Max retries**: Default 10 attempts before failing

#### Custom Info (Executor Response)

When querying an executor, the `custom_info` field contains:
- `state`: Current state (NOT_STARTED, EXECUTING, COMPLETED, FAILED)
- `side`: BUY or SELL
- `amount`: Requested amount
- `executed_amount`: Actual executed amount
- `executed_price`: Average execution price (quote per base)
- `tx_fee`: Transaction fee paid
- `tx_hash`: Transaction signature/hash (Solana transaction ID)
- `exchange_order_id`: Same as tx_hash (for compatibility)
- `current_retries`: Number of retries so far
- `max_retries_reached`: True if executor gave up

#### Important Notes

- **Gateway Required**: SwapExecutor requires a running Gateway instance with the AMM connector configured
- **Atomic Operation**: Swaps are atomic - they either complete fully or not at all
- **No P&L Tracking**: Single swaps don't track P&L (no entry/exit pair)
- **Fee Tracking**: Transaction fees are tracked and reported
7 changes: 6 additions & 1 deletion hummingbot_mcp/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,12 +534,17 @@ class GatewaySwapRequest(BaseModel):
"Example: '1.5' for 1.5% slippage tolerance"
)

# Execute-specific parameter
# Execute-specific parameters
wallet_address: str | None = Field(
default=None,
description="Wallet address for execute action (optional, uses default wallet if not provided)"
)

account_name: str | None = Field(
default=None,
description="Account name for execute action (optional, default: 'master_account')"
)

# Get status parameter
transaction_hash: str | None = Field(
default=None,
Expand Down
10 changes: 10 additions & 0 deletions hummingbot_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ async def manage_executors(
⭐ PRIORITY: This is the DEFAULT tool for ALL trading operations:
- **Buying/Selling**: Use `order_executor` — supports MARKET, LIMIT, LIMIT_MAKER, LIMIT_CHASER strategies,
USD amounts ('$100'), leverage, position_action OPEN/CLOSE. To cancel: use action="stop" on the executor.
- **DEX Swaps**: Use `swap_executor` — single swaps on Gateway AMM connectors (Jupiter, Raydium) with retry logic.
- **LP Positions**: Use `lp_executor` — opens/manages CLMM positions on Meteora, Raydium with full lifecycle tracking.
Use `explore_dex_pools` to discover pools first (list_pools, get_pool_info).
- **Grid/DCA/Position strategies**: Use grid_executor, dca_executor, position_executor.
Expand All @@ -689,6 +690,14 @@ async def manage_executors(
Supports single-sided (base or quote only) and double-sided positions.
Auto-close feature enables limit-order-style LP positions.

## swap_executor
**Execute single swaps on Gateway AMM connectors with retry logic.**
Use when: Executing a single token swap on Solana DEXs (Jupiter, Raydium), need reliable execution with retries.
Avoid when: Need complex trading strategies, want LP positions (use lp_executor), trading on CEX (use order_executor).
Connector format: `jupiter/router` (Gateway router connector).
Parameters: connector_name, trading_pair, side (BUY/SELL), amount (base token).
Swaps are atomic — they either complete fully or not at all.

## position_executor
Takes directional positions with defined entry, stop-loss, and take-profit levels.
Use when: Clear directional view, want automated SL/TP, defined risk/reward.
Expand Down Expand Up @@ -722,6 +731,7 @@ async def manage_executors(
- dca_executor: `amounts_quote` (list of quote amounts per level, e.g., [100, 100, 150])
- order_executor: `amount` (base currency, or '$100' for USD value)
- lp_executor: `base_amount` and/or `quote_amount` (token amounts to provide as liquidity)
- swap_executor: `amount` (base token amount to swap)
Never assume or default these values. Always check the guide first via progressive disclosure.

IMPORTANT - Grid Executor Side:
Expand Down
3 changes: 2 additions & 1 deletion hummingbot_mcp/tools/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ async def manage_executors(client: Any, request: ManageExecutorsRequest) -> dict
"- **dca_executor** — Dollar-cost averaging for gradual position building\n"
"- **grid_executor** — Grid trading across multiple price levels in ranging markets\n"
"- **order_executor** — Simple BUY/SELL order with execution strategy\n"
"- **lp_executor** — Liquidity provision on CLMM DEXs (Meteora, Raydium)\n\n"
"- **lp_executor** — Liquidity provision on CLMM DEXs (Meteora, Raydium)\n"
"- **swap_executor** — Single swap on Gateway AMM connectors (Jupiter, Raydium)\n\n"
"Provide `executor_type` to see the configuration schema."
)

Expand Down
37 changes: 25 additions & 12 deletions hummingbot_mcp/tools/gateway_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,12 @@ async def manage_gateway_swaps(client: Any, request: GatewaySwapRequest) -> dict
}

# ============================================
# EXECUTE - Execute swap transaction
# EXECUTE - Execute swap via swap_executor
# ============================================
elif request.action == "execute":
# Validate required parameters
if not request.connector:
raise ToolError("connector is required for execute action")
if not request.network:
raise ToolError("network is required for execute action")
if not request.trading_pair:
raise ToolError("trading_pair is required for execute action")
if not request.side:
Expand All @@ -86,22 +84,37 @@ async def manage_gateway_swaps(client: Any, request: GatewaySwapRequest) -> dict
if "-" not in request.trading_pair:
raise ToolError(f"Invalid trading_pair format. Expected 'BASE-QUOTE', got '{request.trading_pair}'")

result = await client.gateway_swap.execute_swap(
connector=request.connector,
network=request.network,
trading_pair=request.trading_pair,
side=request.side,
amount=Decimal(request.amount),
slippage_pct=Decimal(request.slippage_pct or "1.0"),
wallet_address=request.wallet_address
# Build connector name in gateway format: {connector}/router
connector_name = f"{request.connector}/router"

# Convert side to numeric (BUY=1, SELL=2)
side_value = 1 if request.side.upper() == "BUY" else 2

# Build executor config
executor_config = {
"type": "swap_executor",
"connector_name": connector_name,
"trading_pair": request.trading_pair,
"side": side_value,
"amount": str(request.amount),
}
if request.slippage_pct:
executor_config["slippage_pct"] = str(request.slippage_pct)

# Create swap executor
result = await client.executors.create_executor(
executor_config=executor_config,
account_name=request.account_name or "master_account"
)

return {
"action": "execute",
"trading_pair": request.trading_pair,
"side": request.side,
"amount": request.amount,
"wallet_address": request.wallet_address or "(default)",
"connector_name": connector_name,
"executor_id": result.get("executor_id"),
"status": result.get("status"),
"result": result
}

Expand Down
Loading