lp-agent
Original:🇺🇸 English
Translated
1 scripts
Manage concentrated liquidity (CLMM) positions on DEXs like Meteora and Raydium. Create, monitor, and rebalance LP positions automatically.
8installs
Sourcehummingbot/skills
Added on
NPX Install
npx skill4agent add hummingbot/skills lp-agentTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →lp-agent
This skill manages concentrated liquidity (CLMM) positions on decentralized exchanges like Meteora (Solana) and Raydium. It provides automated LP position management with rebalancing capabilities similar to the LP Manager controller.
Prerequisites
Before using this skill, ensure hummingbot-api and MCP are running:
bash
bash <(curl -s https://raw.githubusercontent.com/hummingbot/skills/main/skills/lp-agent/scripts/check_prerequisites.sh)If not installed, use the skill first.
hummingbot-deployQuick Start
1. Find a Pool
Use the MCP tool:
manage_gateway_clmm# List popular pools on Meteora
manage_gateway_clmm(action="list_pools", connector="meteora")
# Search for specific pools
manage_gateway_clmm(action="list_pools", connector="meteora", search_term="SOL")
# Get detailed pool info
manage_gateway_clmm(action="get_pool_info", connector="meteora", network="solana-mainnet-beta", pool_address="<address>")2. Create LP Position
# First, see the LP executor config schema
manage_executors(executor_type="lp_executor")
# Create position with quote only (buy base as price drops)
manage_executors(
action="create",
executor_config={
"type": "lp_executor",
"connector_name": "meteora/clmm",
"pool_address": "<pool_address>",
"trading_pair": "SOL-USDC",
"base_token": "SOL",
"quote_token": "USDC",
"base_amount": 0,
"quote_amount": 100,
"lower_price": 180,
"upper_price": 200,
"side": 1
}
)Side values:
- = Both-sided (base + quote)
0 - = Buy (quote-only, range below current price)
1 - = Sell (base-only, range above current price)
2
IMPORTANT - Verify Position Creation:
After creating an executor, you MUST verify the position was actually created on-chain. Follow these steps:
Step 1: Get the executor ID from the creation response
The call returns the executor_id. Use this ID for verification.
manage_executors(action="create")Step 2: Poll executor state until it changes from OPENING
manage_executors(action="get", executor_id="<executor_id>")Check :
custom_info.state- → Transaction in progress, wait 5-10 seconds and check again
OPENING - or
IN_RANGE→ Position created successfully ✓OUT_OF_RANGE - or
FAILED→ Transaction failed ✗RETRIES_EXCEEDED
Step 3: Confirm position exists on-chain
Even if state shows success, verify the position actually exists:
manage_gateway_clmm(
action="get_positions",
connector="meteora",
network="solana-mainnet-beta",
pool_address="<pool_address>"
)The response should contain a position with matching and .
lower_priceupper_priceIf verification fails:
- Stop the failed executor:
manage_executors(action="stop", executor_id="<id>", keep_position=false) - Check the error in if available
custom_info.error - Common issues: range too wide (reduce width), insufficient balance, network congestion
IMPORTANT - Range Width Limits (check BEFORE opening position):
Meteora DLMM pools have bin limits. Each bin represents a small price increment based on :
bin_step- → Each bin is 0.01% apart
bin_step=1 - → Each bin is 0.1% apart
bin_step=10 - → Each bin is 1% apart
bin_step=100
Maximum bins per position is ~69 due to Solana account size limits.
Calculate maximum range width:
max_width_pct = bin_step * 69 / 100
user_width_pct = (upper_price - lower_price) / lower_price * 100Before creating any position, the agent MUST:
- Get pool info:
manage_gateway_clmm(action="get_pool_info", connector="meteora", network="solana-mainnet-beta", pool_address="<address>") - Extract from response
bin_step - Calculate
max_width_pct = bin_step * 69 / 100 - If user's range exceeds max, warn user and suggest narrower range
- Check wallet balance: user needs token amounts + at least 0.06 SOL for position rent
- Use to check balances
get_portfolio_overview - Warn if insufficient SOL or token balance
- Use
Examples:
- : max ~0.69% width
bin_step=1 - : max ~6.9% width
bin_step=10 - : max ~69% width
bin_step=100
3. Set Default Preferences (Optional)
Save commonly-used settings to avoid repeating them. Ask user which values they want to default:
# View current preferences
manage_executors(action="get_preferences")
# Save connector/pair defaults when creating
manage_executors(
action="create",
executor_config={
"type": "lp_executor",
"connector_name": "meteora/clmm",
"trading_pair": "SOL-USDC",
...
},
save_as_default=true
)What can be defaulted:
- - e.g.,
connector_name(must includemeteora/clmmsuffix)/clmm - - e.g.,
trading_pairSOL-USDC - - Meteora only: 0=Spot, 1=Curve, 2=Bid-Ask
extra_params.strategyType
What should NOT be defaulted:
- - Determined by amounts at creation time
side - /
base_token- Inferred from trading_pairquote_token - /
lower_price- Market-dependentupper_price
Defaults stored at .
~/.hummingbot_mcp/executor_preferences.md4. Monitor Positions
# List all LP positions
manage_executors(action="search", executor_types=["lp_executor"])
# Get specific position details
manage_executors(action="get", executor_id="<executor_id>")
# Get positions summary
manage_executors(action="get_summary")5. Manage Positions
# Collect fees (via Gateway CLMM)
manage_gateway_clmm(
action="collect_fees",
connector="meteora",
network="solana-mainnet-beta",
position_address="<position_nft_address>"
)
# Close position
manage_executors(action="stop", executor_id="<executor_id>", keep_position=false)Position Types
Double-Sided (Both Tokens)
Provide liquidity with both base and quote tokens. Best when you expect price to stay within range.
Lower Current Upper
|---------------|---------------|
|<-- Quote zone | Base zone -->|- Price goes UP: You sell base, accumulate quote
- Price goes DOWN: You buy base with quote
Single-Sided: Quote Only (side=1)
Position entire range BELOW current price. You're buying base as price drops.
Lower Upper Current
|---------------|--------|
|<---- Buy zone ---->|Single-Sided: Base Only (side=2)
Position entire range ABOVE current price. You're selling base as price rises.
Current Lower Upper
|--------|---------------|
|<-- Sell zone -->|Rebalancing Strategy (Agent-Driven)
When price moves out of your position range, the agent handles rebalancing automatically.
Step 1: Monitor Position State
manage_executors(action="get", executor_id="<id>")Check :
custom_info.state- → No action needed
IN_RANGE - → Wait for rebalance delay, then rebalance
OUT_OF_RANGE
Rebalance delay: Wait for position to be out of range for a set time (default: 60 seconds). Ask user to confirm delay before starting.
Step 2: Determine Rebalance Direction
Compare with and :
custom_info.current_pricecustom_info.lower_pricecustom_info.upper_priceIf current_price < lower_price (price dropped below range):
- You're now holding mostly BASE tokens
- Strategy: Create BASE-ONLY position ABOVE current price
- This lets you sell base as price recovers
If current_price > upper_price (price rose above range):
- You're now holding mostly QUOTE tokens
- Strategy: Create QUOTE-ONLY position BELOW current price
- This lets you buy base if price drops
Step 3: Close Old Position
manage_executors(action="stop", executor_id="<old_id>", keep_position=false)This returns tokens to wallet. Use the returned amounts for the new position.
Step 4: Create New Single-Sided Position
Use tokens received from close. For position width W% (ask user, check bin_step limits):
Price below range → base-only position ABOVE current price (side=2):
new_lower_price = current_price
new_upper_price = current_price * (1 + W/100)
base_amount = <amount received from close>
quote_amount = 0
side = 2Price above range → quote-only position BELOW current price (side=1):
new_lower_price = current_price * (1 - W/100)
new_upper_price = current_price
base_amount = 0
quote_amount = <amount received from close>
side = 1manage_executors(action="create", executor_config={
"type": "lp_executor",
"connector_name": "<same_connector>",
"pool_address": "<same_pool>",
"trading_pair": "<same_pair>",
"base_amount": <base_amount>,
"quote_amount": <quote_amount>,
"lower_price": <new_lower_price>,
"upper_price": <new_upper_price>,
"side": <side>
})Step 5: Verify New Position
manage_executors(action="get", executor_id="<new_id>")Confirm is or (not or ).
custom_info.stateIN_RANGEOUT_OF_RANGEOPENINGFAILEDMCP Tools Reference
manage_gateway_clmm
| Action | Parameters | Description |
|---|---|---|
| connector, search_term, sort_key, limit | Browse available pools |
| connector, network, pool_address | Get pool details |
| connector, network, pool_address | Get positions in a pool |
| connector, network, pool_address, lower_price, upper_price, base_token_amount, quote_token_amount | Open position directly |
| connector, network, position_address | Close position |
| connector, network, position_address | Collect accumulated fees |
manage_executors
| Action | Parameters | Description |
|---|---|---|
| (none) | executor_type="lp_executor" | Show config schema with your defaults |
| executor_config, save_as_default | Create LP executor (optionally save as default) |
| executor_types=["lp_executor"] | List LP executors |
| executor_id | Get executor details |
| executor_id, keep_position | Stop executor |
| - | Get overall summary |
| - | View preferences file |
| preferences_content | Save edited preferences |
| - | Reset to default template |
Scripts
| Script | Purpose |
|---|---|
| Verify API, Gateway, wallet setup |
LP Executor Config Schema
json
{
"type": "lp_executor",
"connector_name": "meteora/clmm",
"pool_address": "2sfXxxxx...",
"trading_pair": "SOL-USDC",
"base_token": "SOL",
"quote_token": "USDC",
"base_amount": 0,
"quote_amount": 100,
"lower_price": 70,
"upper_price": 90,
"side": 1,
"extra_params": {
"strategyType": 0
}
}Fields:
- : CLMM connector - append
connector_nameto connector name (e.g.,/clmm)meteora/clmm - : Pool contract address
pool_address - : Format "BASE-QUOTE"
trading_pair - /
base_amount: Token amounts (set one to 0 for single-sided)quote_amount - /
lower_price: Position price boundsupper_price - : 0=both, 1=buy (quote-only), 2=sell (base-only)
side - : Connector-specific (Meteora strategyType: 0=Spot, 1=Curve, 2=Bid-Ask)
extra_params
Supported Connectors:
- - Tested and fully supported
meteora/clmm - Other Gateway CLMM connectors - Should work but not yet tested
To list available CLMM connectors:
manage_gateway_config(resource_type="connectors", action="list")Append to any CLMM connector name when creating executors.
/clmmExample: Full LP Management Flow
# 1. Check prerequisites
bash <(curl -s https://raw.githubusercontent.com/hummingbot/skills/main/skills/lp-agent/scripts/check_prerequisites.sh)
# 2. Find a pool
manage_gateway_clmm(action="list_pools", connector="meteora", search_term="SOL", sort_key="volume")
# 3. Get pool details (note the bin_step for range calculation)
manage_gateway_clmm(action="get_pool_info", connector="meteora", network="solana-mainnet-beta", pool_address="2sfXxxxx")
# Example response shows: bin_step=10, current_price=190
# Max range width = 10 * 69 / 100 = 6.9%
# Use conservative 5% width: lower=185.5, upper=194.5
# 4. Create position
manage_executors(action="create", executor_config={
"type": "lp_executor",
"connector_name": "meteora/clmm",
"pool_address": "2sfXxxxx",
"trading_pair": "SOL-USDC",
"base_token": "SOL",
"quote_token": "USDC",
"base_amount": 0,
"quote_amount": 100,
"lower_price": 185.5,
"upper_price": 194.5,
"side": 1
})
# 5. VERIFY position was created (critical step!)
manage_executors(action="get", executor_id="<id>")
# Check custom_info.state:
# - "OPENING" → wait and check again
# - "IN_RANGE" or "OUT_OF_RANGE" → success!
# - "FAILED" → check error, possibly reduce range width
# 6. Monitor position
manage_executors(action="get", executor_id="<id>")
# 7. If out of range for 60+ seconds, rebalance (see Rebalancing Strategy)
# - Close: manage_executors(action="stop", executor_id="<id>", keep_position=false)
# - Reopen with tokens received as single-sided position
# 8. When done, close
manage_executors(action="stop", executor_id="<id>", keep_position=false)Error Handling
| Error | Cause | Solution |
|---|---|---|
| "Prerequisites not met" | API or MCP not running | Run |
| "Pool not found" | Invalid pool address | Use list_pools to find valid pools |
| "Insufficient balance" | Not enough tokens | Check wallet balance, reduce amounts |
| "Position not in range" | Price outside bounds | Wait or rebalance |
| "InvalidRealloc" | Position range spans too many bins | Reduce range width (see bin_step limits above) |
| State stuck at "OPENING" | Transaction failed silently | Stop executor and retry with narrower range |