Related articles: The Agentic Quant Desk · Part 1: Building the Harness
Aerodrome (Base) LP management + sniping primitives, with a dashboard — in one repo. Discover pools, read gauge emissions, compute position PnL & impermanent loss, mint/stake/remove on-chain, and see it all in a Streamlit UI.
Extracted from a private trading desk. Mechanics only — pool discovery, gauge/PnL/IL reads, an on-chain executor, and a dashboard. The tuned sniping selection, range-sizing, and rebalance triggers (the edge) stay private; sniping is shown here as primitives + a trivial example rule you replace.
src/aero_lp/ on-chain primitives
pool discovery · gauge/emission · PnL & IL · mint/stake/remove executor
deployment registry · math · backtest CL valuation
src/aero_dashboard/ Streamlit UI
JSONL loaders · equity/drawdown/metrics components
metrics.py SessionAnalyzer · live.py glue → aero_lp
examples/ manage_demo.py (live PnL) · snipe_demo.py (rank + dry-run mint)
backtest_lp.py (offline sim) · sample-snapshots/ (demo data)
pip install -e . gives you the CLI, Python libraries, and dashboard:
aero-lp --help
aero-lp doctor
aero-lp schema
python -m aero_lp --help
aero-dashboardpip install -e .
streamlit run src/aero_dashboard/app.py # 3 bundled samples: equity, drawdown, metrics, hedgeAll LP analysis and operations are available through aero-lp. Work commands return a JSON envelope:
{"success": true, "data": {}, "timestamp": "..."}Discovery and research:
aero-lp doctor
aero-lp schema rank-pools
aero-lp tokens
aero-lp pools WETH cbBTC --tick-spacing 50
aero-lp rank-pools WETH cbBTC --tick-spacing 50
aero-lp pool-state <pool>
aero-lp suggest-range <pool> --width-bps 500
aero-lp backtest neutral --token0 WETH --token1 cbBTC --hours 240 --capital 10000
aero-lp backtest directional --token0 WETH --token1 cbBTC --hours 240 --capital 10000
aero-lp backtest hedge --symbol-a ETHUSDT --symbol-b BTCUSDT --capital 30000
aero-lp walk-forward --token0 WETH --token1 cbBTC --hours 1000 --window 168 --stride 24Live reads and dry-run operations:
aero-lp position-pnl <token_id> --gauge <gauge>
aero-lp should-rebalance <token_id> --gauge <gauge>
aero-lp live-apr <token_id> --gauge <gauge> --range-width-ticks 10
aero-lp mint --token0 WETH --token1 cbBTC --tick-spacing 50 --tick-lower <lo> --tick-upper <hi> --amount0 <raw> --amount1 <raw>
aero-lp rebalance <token_id> --tick-lower <lo> --tick-upper <hi> --gauge <gauge>Write commands are dry-run by default. Add --execute only after reviewing the dry-run output.
Use aero-lp schema <command> for required parameters, units, key JSON fields, common failures, and next actions.
Keys are encrypted at rest (Ethereum keystore — scrypt + AES-128-CTR). Only the password is needed at runtime; no plaintext key ever touches disk or the env.
python -m aero_lp.aerodrome.cli_keystore generate # new wallet → keystore (prompts password)
python -m aero_lp.aerodrome.cli_keystore encrypt # or encrypt a key you already have
python -m aero_lp.aerodrome.cli_keystore info # show address (no decrypt)
python -m aero_lp.aerodrome.cli_keystore verify # test decryptionDefault keystore file is base_wallet.json (override with KEYSTORE_PATH). At runtime:
export KEYSTORE_PASSWORD='...' # only secret in the env; key stays encrypted on diskThe executor resolves a key automatically, in order: explicit private_key= → encrypted keystore
(KEYSTORE_PATH + KEYSTORE_PASSWORD) → plaintext BASE_PRIVATE_KEY. So once the keystore + password
are set, construct AerodromeExecutor(...) with no private_key= and it signs from the keystore.
Helpers are re-exported for convenience:
from aero_lp import load_key, get_keystore_address, get_default_wallet_address
addr = get_default_wallet_address() # read address without decrypting.gitignore already excludes *keystore*.json and base_wallet*.json. A generated wallet still needs
manual funding (ETH for gas + pair tokens on Base).
export BASE_RPC_URL="https://mainnet.base.org"
aero-lp pools WETH cbBTC
aero-lp rank-pools WETH cbBTC
aero-lp suggest-range <pool> --width-bps 300
aero-lp mint --token0 WETH --token1 cbBTC --tick-spacing 50 --tick-lower <lo> --tick-upper <hi> --amount0 <raw> --amount1 <raw>
aero-lp stake <token_id> --gauge <gauge>See docs/running-lp-strategies.md for the complete CLI runbook. Writes default to dry-run; supply
RPC + keystore/private key (.env.example) and add --execute only to send. Verify deployment/gauge
consistency before staking a migrated pair.
The dashboard reads the same snapshot schema the toolkit (and the private bots) produce. live.py is the
bridge: it calls aero_lp.GaugeMonitor.get_position_pnl(...) and shapes the result into the exact
DataFrame the dashboard components render — so the same UI shows backtest samples (offline) or your live
book (RPC).
Full walkthrough — discover → range → mint → stake → monitor → rebalance → exit — for WETH/cbBTC
CL50 using either an S1-style sniper (emission-ranked entry) or an S17-style directional LP
(hold + rebalance): docs/running-lp-strategies.md.
Agents should use the shared project-local skill at
.agents/skills/quant-researcher/SKILL.md for LP
analysis and management. Codex, Claude, and Gemini wrappers only point to this shared source. It keeps
the agent workflow CLI-first and points to focused references for command contracts, live LP workflows,
backtest/session metrics, decision rules, troubleshooting, and dry-run-first safety checks.
A CL valuation engine ships in aero_lp.backtest (position value as price moves, IL vs HODL) and a
session scorer in aero_dashboard.SessionAnalyzer (return, Sharpe, drawdown, time-in-range,
rebalance efficiency — the same metrics used on paper/live data). What's not shipped is the price
feed and the rebalance rule — those are yours (real candles + your edge).
aero-lp backtest neutral --token0 WETH --token1 cbBTC --hours 240 --capital 10000 --out-dir /tmp/aero-neutral
aero-lp backtest directional --token0 WETH --token1 cbBTC --hours 240 --capital 10000 --offset-pct 0.25
aero-lp backtest hedge --symbol-a ETHUSDT --symbol-b BTCUSDT --capital 30000
aero-lp compare --token0 WETH --token1 cbBTC --hours 240
aero-lp session-summary /tmp/aero-neutral --strategy neutral --pair WETH-cbBTCRunnable examples remain under examples/, but the CLI is the agent/operator surface.
All examples fetch real Bybit prices (public klines, no API key) and fall back to synthetic GBM only if offline: The CLI backtests fetch real Bybit prices (public klines, no API key) and fall back to deterministic synthetic paths when offline. Pair→symbol resolution mirrors the live desk: direct (X/stable → that symbol), cross (WETH/cbBTC → ETHBTCUSDT), synthetic (ratio of two USDT legs).
Walk-forward (examples/walk_forward.py) — one path is one noisy number; slice a long history into
many windows and judge against the distribution:
aero-lp walk-forward --token0 WETH --token1 USDC --hours 1000 --window 168 --stride 24Your own signal/rule still replaces the stubs. (scenario_builder regime labelling / signal-driven sims
/ tuned policies stay private.)
Real APR, not a guess (examples/live_apr_backtest.py): emission APR is range/time-specific, so fetch
it from a live position rather than hardcoding —
aero-lp live-apr <token_id> --gauge <gauge> --range-width-ticks 12
aero-lp rank-pools WETH cbBTClive-apr returns the live position APR and optional range-sweep inputs; rank-pools gives prospective
pool emission comparisons.
The tuned sniping logic — snipe_engine (efficiency scoring / pool selection), vol_range (adaptive
range sizing), the rebalance/dust triggers, and the backtest's tuned layer (scenario_builder regime
construction, signal-driven sims, rebalance policies). Those are the alpha. You get the primitives + a
"pick the highest-emission pool" / "re-center on OOR" stub to replace with your own.
pip install -e ".[dev]" && pytest # primitives + IL math + dashboard render + alpha-absenceMIT.