feat(lp-agent): add Solana DEX LP Expert — autonomous CLMM liquidity provisioning - #162
Conversation
Stopping a strategy that still holds open positions silently stranded them. `engine.stop()` cleans up executor tracking (executors land in SYSTEM_CLEANUP) while the on-chain position stays live, so the normal `stop_executor(keep_position=False)` close path can no longer reach it. Recovery means going around the executor layer entirely. This is only wrong for agents whose shutdown policy declares `on_kill_switch: flatten_all` -- those are saying their positions are risk to close, not spot to keep. For them a plain stop is almost always a mistake; `shutdown` is the operation that winds down per shutdown.md. Guard `/stop` for exactly that case: 409 with the open executors and positions named, pointing at `shutdown`, with `force=true` as the explicit override. Other policies (keep_all, keep_spot_close_perp) are untouched, as is stopping an agent that holds nothing. `open_risk()` reuses the winddown's own executor/position lookups so the guard and the shutdown path cannot disagree about what "open" means. In the all-instances branch every engine is guarded before any is stopped, so a 409 can't leave half the instances down. Verified against the failure it fixes. Before, stopping the LP agent with two live positions succeeded and left both as SYSTEM_CLEANUP with ~$25 stranded on-chain and no alert. After, the same stop returns 409 naming the open position, and `shutdown` attempts the flatten and raises its stranded-position alert when a swap fails -- no silent orphan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154EUNSY4ocqsWPKZWgwBJn
…-limit guidance Adds the LP Slot Operator agent (scans GeckoTerminal for memecoin CLMM pools, ranks by fee yield, runs LP Executors across Meteora/Orca/Raydium with per-slot TP/SL) and documents three failure modes found while running it live. shutdown.md -- the swap-back leg is not guaranteed: `keep_position=false` is meant to remove liquidity AND swap the base tokens back to quote. Observed twice that only the first leg ran: liquidity came out, the swap silently failed, and the full base position sat as spot (BONK 1.74M, PUMP 2588, ANSEM 24.6, JIMOTHY 253) while every lp_executor reported a clean EARLY_STOP. A clean executor status is not evidence the tokens were converted. The winddown now says so, and requires re-reading the on-chain balance and retrying until it is ~0. order_executor.md -- Jupiter rate limits masquerade as NO_ROUTE_FOUND: A throttled swap surfaces as `NO_ROUTE_FOUND` carrying the text `Unexpected token 'R', "Rate limit"... is not valid JSON`. The route is fine and the token is tradable; treating it as unsellable is the wrong read. Documents the tell and the pacing that follows from it -- ~2s between swaps with a Jupiter key (api.jup.ag, ~60 req/min), >=15-20s without one (lite-api.jup.ag). The same three sells failed after 10+ retries each on the keyless tier and went through with 0-1 retries once a key was configured. order_executor.md -- executed_amount_base reports the amount REQUESTED: Slippage and fees mean the wallet receives less (observed -0.06% to -0.44%). Feeding it straight into a downstream call that must spend those tokens asks for more than you hold and fails on-chain. Apply a 0.995 haircut or read the true post-swap balance. strategy.md carries the same rule at the swap->LP-open seam, where it caused every "SIMULATION_FAILED" open until fixed. strategy.md also gains the venue width clamp in granularity units rather than percent: Meteora bins = ln(Pu/Pl)/ln(1+bin_step/10000) < 69, Orca/Raydium spacings = ln(Pu/Pl)/(ln(1.0001)*tick_spacing) <= 120. Percent width is not comparable across pools -- two Orca opens at an identical 20.8% split on tick_spacing alone (16 -> 118 spacings opened; 8 -> 237 failed). Runtime session data (journals, snapshots) is deliberately not tracked, matching the existing market_making_expert layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154EUNSY4ocqsWPKZWgwBJn
1b9d01b to
414f427
Compare
… guidance The gateway (feat/robinhood-chain) now detects Jupiter throttling, retries with backoff, and returns a clean 429 instead of masquerading as NO_ROUTE_FOUND — so the "don't blacklist / pace swaps ≥15-20s" guidance is superseded. Keep only the separate executed_amount_base haircut note (unrelated to rate limits). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
7117d0c to
bdc364c
Compare
Cut ~30% of the per-tick system prompt — removed verbose prose, repeated examples, and the exact range-bound formulas (they live in the lp_range_config skill). Kept every load-bearing fact: network-vs-venue mapping, the asymmetric venue-flip range placement, the bracket-price HARD GUARDRAIL, the ~69-bin Meteora cap, and the fees/TVL ranking. Defer detail to the skills. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
e1b1d4c to
3da5fda
Compare
|
Commit 36fc10d
Test base_pct 0, 20, and 100 to catch the conflicting range instructions. ✅
Verify unavailable executor/position data blocks stop rather than allowing it. ✅
Confirm scanner results for SOL/USDC pools regardless of GeckoTerminal token orientation across Meteora, Orca, and Raydium. ❌
|
…t on either pool side
QA found two blockers in lp_scanner:
- Leaving "exclude_mints" blank in the Telegram config form submits "" (a
string), which failed Pydantic list validation before the routine could
run. venues/exclude_pools/exclude_mints now coerce comma/space-separated
text ("" -> []) via a before-validator.
- The quote filter compared GeckoTerminal's quote_token_symbol only, so a
SOL-quoted scan dropped every pool Gecko happens to list as SOL/USDC
(SOL as base) - only XXX-SOL pairs ever appeared. The quote side is now
matched by mint on either side of the pool and flipped into base-quote
orientation when Gecko lists it inverted (price taken from
quote_token_price_usd in that case). Verified live: 13 SOL/USDC pools
previously dropped now rank.
Unsupported quote assets now error out early instead of silently matching
nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both ❌ items are addressed in f1160c3: Blank SOL/USDC pools now appear regardless of GeckoTerminal orientation. The scanner previously kept a pool only when Gecko's Also, an unsupported 🤖 Generated with Claude Code |
…ositions" This reverts commit 544a63e.


Adds the Solana DEX LP Expert, an autonomous agent that provides concentrated liquidity to memecoin pools on Solana — plus a Solana-specific note in the
order_executorguide that came out of running it live against a real wallet.The agent
Each tick it monitors open LP slots, exits any that hit take-profit / stop-loss or idle out of range, and fills one free slot with the best-yielding pool it doesn't already hold.
lp_scannerroutine pulls GeckoTerminal pools across Meteora / Orca / Raydium and ranks by fee yield (fees ÷ TVL) over a configurable window. The quote asset is matched by mint on either pool side, so a SOL-quoted scan also finds pools GeckoTerminal lists as SOL/USDC (SOL as base) — orientation is normalized before ranking. Telegram-form inputs are tolerated: list fields accept comma/space-separated text, and blank coerces to empty.slots, each opened at abase_pctbase/quote ratio (default 20/80).side=3RANGE,keep_position=false), range placed asymmetrically so an even Spot distribution lands the target ratio.out_of_range_max_sec) that recycles capital sitting out of range earning nothing.shutdown.mddeclareson_kill_switch: flatten_all: positions are risk to close, never spot to keep.Files:
AGENT.md,shutdown.md,strategies/lp_slot_operator/strategy.md, 4 skills (pool_ranking,lp_range_config,slot_exit,lp_bot_report), and thelp_scannerroutine. Runtime session data (journals, snapshots) is deliberately untracked, matching the existingmarket_making_expertlayout.Two correctness rules that took live runs to find
Range bounds flip by venue. Meteora quotes SOL-per-memecoin (memecoin above P); Orca and Raydium invert it (memecoin below P). Apply one convention to the other and the bounds don't bracket the live price — the open fails on-chain with no funds moved and the slot silently stays empty.
Width clamps must be in granularity units, not percent. Percent width isn't comparable across pools:
Two Orca opens at an identical 20.8% width landed on opposite sides purely on
tick_spacing:16→ 118 spacings opened fine,8→ 237 failed. Successes observed at 75 / 76 / 98 / 118 spacings; the only failure was 237. Any percent-based rule of thumb is wrong in both directions — it rejects good ranges and admits failing ones.The winddown's swap-back leg is not guaranteed
keep_position=falseshould remove liquidity and swap the base tokens back to quote. Observed twice that only the first leg ran: liquidity came out, the swap silently failed, and the full base position sat as spot while everylp_executorreported a cleanEARLY_STOP.A clean executor status is not evidence the tokens were converted.
shutdown.mdsays so and requires re-reading the on-chain balance and retrying until it's ~0.order_executorguide: Solana sizing noteexecuted_amount_baseis the amount REQUESTED, not received. The agent swaps quote→base, then immediately opens the LP with what it just bought. Butorder_executorreports the requested amount (62), while slippage and fees mean the wallet actually receives less (61.962753, −0.06%; another case −0.44%). Passing the reported fill intobase_amountasks the pool for tokens that aren't there — the open fails,position_address=null, no funds move.Fix, now documented in the guide:
base_amount = executed_amount_base × 0.995, or read the true post-swap balance. This was the cause of every "SIMULATION_FAILED" open. After the fix: 7 opens across two sessions, 0 failures (previously 3 and 2 failures per session).Testing
Run live against a real Solana wallet across sessions 8–11: start/stop/shutdown, 3 concurrent LP slots, TP/SL and out-of-range idle exits, and full winddown. All positions verified closed on-chain — position accounts and NFTs checked directly via RPC, not just the API, after the API's
total_value_quotewas found to misreport a slot's value by ~2.7×. Final wallet reconciled to chain.No secrets included — the staged diff was scanned for API keys, RPC credentials, wallet addresses, and tokens before commit.
🤖 Generated with Claude Code
https://claude.ai/code/session_0154EUNSY4ocqsWPKZWgwBJn