Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,7 @@ cython_debug/

# PyPI configuration file
.pypirc

# Local config
.mcp.json
.vscode/
14 changes: 14 additions & 0 deletions hummingbot_mcp/executor_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@
# price: "95000"
```

### Lp Executor Defaults

```yaml
lp_executor:
# Set your preferred defaults here (all optional, ask user if not set):
# connector_name: meteora/clmm # Must include /clmm suffix
# trading_pair: SOL-USDC
# extra_params:
# strategyType: 0 # Meteora only: 0=Spot, 1=Curve, 2=Bid-Ask
#
# Note: base_token/quote_token are inferred from trading_pair
# Note: side is determined by amounts at creation time, not defaulted
```

---

*Last updated: Never*
Expand Down
6 changes: 6 additions & 0 deletions hummingbot_mcp/formatters/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ def format_executor_detail(executor: dict[str, Any]) -> str:
if close_timestamp is not None and close_timestamp != "N/A" and close_timestamp != 0:
output += f"Closed: {format_timestamp(close_timestamp, '%Y-%m-%d %H:%M:%S')}\n"

# Always show custom_info if present
if custom_info:
output += "\nCustom Info:\n"
for key, value in custom_info.items():
output += f" {key}: {value}\n"

return output


Expand Down
120 changes: 120 additions & 0 deletions hummingbot_mcp/guides/lp_executor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
### LP Executor
Manages liquidity provider positions on CLMM DEXs (Meteora, Raydium).
Opens positions within price bounds, monitors range status, tracks fees.

**Use when:**
- Providing liquidity on Solana DEXs
- Want automated position monitoring and fee tracking
- Earning trading fees from LP positions

**Avoid when:**
- Trading on CEX (use other executors)
- Want directional exposure only
- Not familiar with impermanent loss risks

#### State Machine

```
NOT_ACTIVE → OPENING → IN_RANGE ↔ OUT_OF_RANGE → CLOSING → COMPLETE
```

- **NOT_ACTIVE**: Initial state, no position yet
- **OPENING**: Transaction submitted to open position
- **IN_RANGE**: Position active, current price within bounds
- **OUT_OF_RANGE**: Position active but price outside bounds (no fees earned)
- **CLOSING**: Transaction submitted to close position
- **COMPLETE**: Position closed, executor finished

#### Key Parameters

**Required:**
- `connector_name`: CLMM connector in `connector/clmm` format (e.g., `meteora/clmm`, `raydium/clmm`)
- **IMPORTANT:** Must include the `/clmm` suffix — using just `meteora` will fail
- `trading_pair`: Token pair (e.g., `SOL-USDC`)
- `pool_address`: Pool contract address
- `lower_price` / `upper_price`: Price range bounds

**Liquidity:**
- `base_amount`: Amount of base token to provide (default: 0)
- `quote_amount`: Amount of quote token to provide (default: 0)
- `side`: Position side (0=BOTH, 1=BUY/quote-only, 2=SELL/base-only)

**Auto-Close (Limit Range Orders):**
- `auto_close_above_range_seconds`: Close when price >= upper_price for this many seconds
- `auto_close_below_range_seconds`: Close when price <= lower_price for this many seconds
- Set to `null` (default) to disable auto-close

#### Single-Sided vs Double-Sided Positions

**Single-sided (one asset only):**
- **Base token only** (e.g., 0.2 SOL): Creates a SELL position (`side=2`) with range ABOVE current price
- Position starts out-of-range, enters range when price rises
- SOL converts to USDC as price moves up through the range
- **Quote token only** (e.g., 50 USDC): Creates a BUY position (`side=1`) with range BELOW current price
- Position starts out-of-range, enters range when price falls
- USDC converts to SOL as price moves down through the range

**Double-sided (both assets):**
- When user specifies both `base_amount` and `quote_amount`, ask:
1. **Centered range** around current price? (±50% of position width above/below current price)
2. **Custom range**? (user specifies exact lower/upper bounds)
- Set `side=0` (BOTH) for double-sided positions

**Position Management:**
- `keep_position=false` (default): Close LP position when executor stops
- `keep_position=true`: Leave position open on-chain, stop monitoring only
- `position_offset_pct`: Offset from current price for single-sided positions (default: 0.01%)

#### Limit Range Orders (Auto-Close Feature)

Use `auto_close_above_range_seconds` and `auto_close_below_range_seconds` to create limit-order-style LP positions that automatically close when price moves through the range.

**SELL Limit (Take Profit on Long):**
```
side=2, base_amount=X, quote_amount=0
lower_price > current_price (range above current price)
auto_close_above_range_seconds=60
```
- Position starts OUT_OF_RANGE (price below range)
- When price rises into range: base → quote conversion, fees earned
- When price rises above range for 60s: position auto-closes with quote tokens

**BUY Limit (Accumulate on Dip):**
```
side=1, base_amount=0, quote_amount=X
upper_price < current_price (range below current price)
auto_close_below_range_seconds=60
```
- Position starts OUT_OF_RANGE (price above range)
- When price falls into range: quote → base conversion, fees earned
- When price falls below range for 60s: position auto-closes with base tokens

**Key Benefits:**
- Earn LP fees while price moves through your target range
- Automatic execution without monitoring
- Better fills than traditional limit orders (continuous conversion vs single fill)

#### Meteora Strategy Types (extra_params.strategyType)

- `0`: **Spot** — Uniform liquidity across range
- `1`: **Curve** — Concentrated around current price
- `2`: **Bid-Ask** — Liquidity at range edges

#### Important: Managing Positions

**Always use the executor tool (`manage_executors`) to open and close LP positions — NOT the gateway CLMM tool directly.**

- Opening/closing via `manage_gateway_clmm` bypasses the executor state machine and leaves the database out of sync
- Use `manage_executors` with `action="stop"` to properly close positions and update executor status
- If a position is closed externally (via gateway or UI), manually mark the executor as `TERMINATED` in the database

**Verifying position status:**
- If uncertain about position status, use `manage_gateway_clmm` with `action="get_positions"` to check on-chain state
- Compare on-chain positions with executor `custom_info.position_address`
- If position is closed on-chain but executor still shows `RUNNING`, manually update executor status in database to `TERMINATED`
- If position is open on-chain but executor still shows `OPENING`, the executor should eventually sync — if stuck, check API logs for errors

**Exception: Executor not found in API (404 error):**
- If API was restarted, executors may no longer exist in memory but positions remain on-chain
- In this case, use `manage_gateway_clmm` with `action="close_position"` to close the on-chain position directly
- Then manually update the executor status in the database to `TERMINATED`
6 changes: 6 additions & 0 deletions hummingbot_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,11 +958,17 @@ async def manage_gateway_config(
Resource Types:
- chains: Get all blockchain chains
- networks: List/get/update network configurations (format: 'chain-network')
- GET returns merged chain + network config including: defaultWallet, defaultNetwork, rpcProvider, nodeURL, chainID, etc.
- UPDATE can modify both network-level fields (nodeURL, chainID) and chain-level fields (defaultWallet, defaultNetwork)
- tokens: List/add/delete tokens per network
- connectors: List/get/update DEX connector configurations
- pools: List/add liquidity pools per connector/network
- wallets: Add/delete wallets for blockchain chains

Examples:
- Get network config with defaultWallet: resource_type="networks", action="get", network_id="solana-mainnet-beta"
- Set default wallet: resource_type="networks", action="update", network_id="solana-mainnet-beta", config_updates={"defaultWallet": "wallet_address"}

Args:
resource_type: Type of resource to manage
action: Action to perform on the resource
Expand Down
9 changes: 6 additions & 3 deletions 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
"- **position_executor** — Directional trading with entry, stop-loss, and take-profit\n"
"- **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\n"
"- **order_executor** — Simple BUY/SELL order with execution strategy\n"
"- **lp_executor** — Liquidity provision on CLMM DEXs (Meteora, Raydium)\n\n"
"Provide `executor_type` to see the configuration schema."
)

Expand Down Expand Up @@ -174,8 +175,10 @@ async def manage_executors(client: Any, request: ManageExecutorsRequest) -> dict
if request.save_as_default:
executor_preferences.update_defaults(executor_type, request.executor_config)

executor_id = result.get("executor_id") or result.get("id")

formatted = f"Executor created successfully!\n\n"
formatted += f"Executor ID: {result.get('id', 'N/A')}\n"
formatted += f"Executor ID: {executor_id or 'N/A'}\n"
formatted += f"Type: {executor_type}\n"
formatted += f"Account: {account}\n"

Expand All @@ -184,7 +187,7 @@ async def manage_executors(client: Any, request: ManageExecutorsRequest) -> dict

return {
"action": "create",
"executor_id": result.get("id"),
"executor_id": executor_id,
"executor_type": executor_type,
"account": account,
"config_used": merged_config,
Expand Down
4 changes: 4 additions & 0 deletions hummingbot_mcp/tools/gateway_clmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ async def manage_gateway_clmm_positions(client: Any, request: GatewayCLMMRequest
"connector": request.connector,
"network": request.network,
"pool_address": request.pool_address,
"wallet_address": request.wallet_address or "(default)",
"price_range": {
"lower_price": request.lower_price,
"upper_price": request.upper_price
Expand Down Expand Up @@ -281,6 +282,7 @@ async def manage_gateway_clmm_positions(client: Any, request: GatewayCLMMRequest
"connector": request.connector,
"network": request.network,
"position_address": request.position_address,
"wallet_address": request.wallet_address or "(default)",
"result": result
}

Expand Down Expand Up @@ -308,6 +310,7 @@ async def manage_gateway_clmm_positions(client: Any, request: GatewayCLMMRequest
"connector": request.connector,
"network": request.network,
"position_address": request.position_address,
"wallet_address": request.wallet_address or "(default)",
"result": result
}

Expand Down Expand Up @@ -335,6 +338,7 @@ async def manage_gateway_clmm_positions(client: Any, request: GatewayCLMMRequest
"connector": request.connector,
"network": request.network,
"pool_address": request.pool_address,
"wallet_address": request.wallet_address or "(default)",
"result": result
}

Expand Down
1 change: 1 addition & 0 deletions hummingbot_mcp/tools/gateway_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ async def manage_gateway_swaps(client: Any, request: GatewaySwapRequest) -> dict
"trading_pair": request.trading_pair,
"side": request.side,
"amount": request.amount,
"wallet_address": request.wallet_address or "(default)",
"result": result
}

Expand Down