Autonomous Meteora DLMM liquidity management agent for Solana, powered by LLMs.
Meridian runs continuous screening and management cycles, deploying capital into high-quality Meteora DLMM pools and closing positions based on live PnL, yield, and range data. It learns from every position it closes.
- Screens pools — scans Meteora DLMM pools against configurable thresholds (fee/TVL ratio, organic score, holder count, mcap, bin step) and surfaces high-quality opportunities
- Manages positions — monitors, claims fees, and closes LP positions autonomously; decides to STAY, CLOSE, or REDEPLOY based on live data
- Learns from performance — studies top LPers in target pools, saves structured lessons, and evolves screening thresholds based on closed position history
- Discord signals — optional Discord listener watches LP Army channels for Solana token calls and queues them for screening
- Telegram chat — full agent chat via Telegram, plus cycle reports and OOR alerts
- Claude Code integration — run AI-powered screening and management directly from your terminal using Claude Code slash commands
Meridian runs a ReAct agent loop — each cycle the LLM reasons over live data, calls tools, and acts. Two specialized agents run on independent cron schedules:
| Agent | Default interval | Role |
|---|---|---|
| Screening Agent | Every 30 min | Pool screening — finds and deploys into the best candidate |
| Management Agent | Every 10 min | Position management — evaluates each open position and acts |
Meridian's agent harness is the runtime wrapper around every autonomous cycle. It gives both main and experimental agents the same control loop: load live state, inject relevant memory, expose only role-appropriate tools, execute tool calls, and return a readable cycle report.
The harness also keeps a structured decision log in decision-log.json for deployments, closes, skips, and no-deploy outcomes. Each entry records the actor, pool or position, summary, reason, key risks, metrics, and rejected alternatives. Recent decisions are injected back into the system prompt and are available through get_recent_decisions, so the agent can answer "why did you deploy?", "why did you close?", or "why did you skip?" without guessing after the fact.
Data sources:
@meteora-ag/dlmmSDK — on-chain position data, active bin, deploy/close transactions- Meteora DLMM PnL API — position yield, fee accrual, PnL
- Pool screening API — fee/TVL ratios, volume, organic scores, holder counts
- Jupiter API — token audit, mcap, launchpad, price stats
Agents are powered via OpenRouter and can be swapped for any compatible model.
- Node.js 18+
- OpenRouter API key
- Solana wallet (base58 private key)
- Solana RPC endpoint (Helius recommended)
- Telegram bot token (optional)
- Claude Code CLI (optional, for terminal slash commands)
git clone https://github.com/yunus-0x/meridian
cd meridian
npm installnpm run setupThe wizard writes both files at the repo root:
Goes in .env |
Goes in user-config.json |
|---|---|
WALLET_PRIVATE_KEY, OPENROUTER_API_KEY, RPC_URL, HELIUS_API_KEY |
Risk preset, deploy size, max positions |
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_ALLOWED_USER_IDS |
Strategy, screening filters, exit rules, trailing TP |
DRY_RUN |
Position sizing, cycle intervals, per-role LLM models, solMode |
TELEGRAM_CHAT_ID only needs to live in .env — setup also copies it to user-config.json when provided. Takes about 2 minutes.
Or set up manually:
Create .env:
WALLET_PRIVATE_KEY=your_base58_private_key
RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
OPENROUTER_API_KEY=sk-or-...
HELIUS_API_KEY=your_helius_key # for wallet balance lookups
TELEGRAM_BOT_TOKEN=123456:ABC... # optional — for notifications + chat
TELEGRAM_CHAT_ID= # auto-filled on first message
DRY_RUN=true # set false for live tradingNever put your private key or API keys in
user-config.json— use.envonly. Both files are gitignored.
Optional encrypted .env flow:
cp .env .env.raw
printf "replace-with-a-long-local-key\n" > .envrypt
npm run env:encryptMeridian loads envrypt-style encrypted values automatically. Keep .env.raw and .envrypt local; both are gitignored.
Copy config and edit as needed:
cp user-config.example.json user-config.jsonSee Config reference below.
npm run dev # dry run — no on-chain transactions
npm start # live modeOn startup Meridian fetches your wallet balance, open positions, and top pool candidates, then begins autonomous cycles immediately.
PM2 is the recommended way to keep Telegram control online on a VPS. Always start via the ecosystem file so the working directory and script path stay pinned to the repo:
npm install
npm run pm2:start # uses ecosystem.config.cjs — do NOT use "pm2 start index.js"
pm2 saveAfter .env, user-config.json, or code changes:
npm run pm2:restart # re-reads .env on each restart
npm run pm2:logsTo update an existing PM2 install:
git pull
npm install
npm run pm2:restart
pm2 saveIf a previous PM2 run was started incorrectly, reset it once:
pm2 delete meridian
npm run pm2:start
pm2 savePM2 vs npm start
npm start |
PM2 | |
|---|---|---|
| Terminal | Interactive REPL | Headless daemon |
| Cron / Telegram | Starts after REPL banner | Starts immediately on boot |
| First screening | On cron schedule | May run one cycle right at startup |
| Best for | Local dev / testing | VPS / 24-7 operation |
On startup, logs show Repo: ... | cwd: ... | PM2 id: .... Repo and cwd must match. If they differ, delete the process and use npm run pm2:start again.
Common PM2 issues
| Symptom | Likely cause | Fix |
|---|---|---|
Crash loop after git pull |
npm install skipped |
npm install && npm run pm2:restart |
| Missing wallet / API keys | Started with pm2 start index.js from wrong directory |
pm2 delete meridian && npm run pm2:start |
.env changes ignored |
Old PM2 env snapshot | npm run pm2:restart (.env now overrides stale PM2 env) |
Telegram 401 Unauthorized |
Invalid TELEGRAM_BOT_TOKEN (not chat ID) |
Fix token in .env; if encrypted, ensure .envrypt exists |
| Telegram commands ignored | Missing/wrong TELEGRAM_CHAT_ID |
Set in .env (or telegramChatId in user-config.json) |
| Duplicate polling / 409 errors | nohup node index.js or second PM2 instance running |
Kill stray processes; run only one PM2 app |
| Encrypted env crash at boot | # encrypted lines without .envrypt key |
Add .envrypt or use plain .env values |
Avoid nohup node index.js — it runs outside PM2 and can leave a duplicate Telegram poller fighting the managed process.
npm startStarts the full autonomous agent with cron-based screening + management cycles and an interactive REPL. The prompt shows a live countdown to the next cycle:
[manage: 8m 12s | screen: 24m 3s]
>
REPL commands:
| Command | Description |
|---|---|
/status |
Wallet balance and open positions |
/candidates |
Re-screen and display top pool candidates |
/learn |
Study top LPers across all current candidate pools |
/learn <pool_address> |
Study top LPers for a specific pool |
/thresholds |
Current screening thresholds and performance stats |
/evolve |
Trigger threshold evolution from performance data (needs 5+ closed positions) |
/stop |
Graceful shutdown |
<anything> |
Free-form chat — ask the agent anything, request actions, analyze pools |
Install Claude Code and use it from inside the meridian directory. Claude Code has built-in agents and slash commands that use the meridian CLI under the hood.
cd meridian
claude| Command | What it does |
|---|---|
/screen |
Full AI screening cycle — checks Discord queue, reads config, fetches candidates, runs deep research, and deploys if a winner is found |
/manage |
Full AI management cycle — checks all positions, evaluates PnL, claims fees, closes OOR/losing positions |
/balance |
Check wallet SOL and token balances |
/positions |
List all open DLMM positions with range status |
/candidates |
Fetch and enrich top pool candidates (pool metrics + token audit + smart money) |
/study-pool |
Study top LPers on a specific pool |
/pool-ohlcv |
Fetch price/volume history for a pool |
/pool-compare |
Compare all Meteora DLMM pools for a token pair by APR, fee/TVL ratio, and volume |
Two specialized sub-agents run inside Claude Code:
screener — pool screening specialist. Invoke when you want to evaluate candidates, analyse token risk, or deploy a position. Has access to Jupiter token audit, smart-wallet checks, and all strategy logic.
manager — position management specialist. Invoke when reviewing open positions, assessing PnL, claiming fees, or closing positions.
To trigger an agent directly, just describe what you want:
> screen for new pools and deploy if you find something good
> review all my positions and close anything out of range
> what do you think of the SOL/BONK pool?
Run screening or management on a timer inside Claude Code:
/loop 30m /screen # screen every 30 minutes
/loop 10m /manage # manage every 10 minutes
The meridian CLI gives you direct access to every tool with JSON output — useful for scripting, debugging, or piping into other tools.
npm install -g . # install globally (once)
meridian <command> [flags]Or run without installing:
node cli.js <command> [flags]Positions & PnL
meridian positions
meridian pnl <position_address>
meridian wallet-positions --wallet <addr>Screening
meridian candidates --limit 5
meridian pool-detail --pool <addr> [--timeframe 5m]
meridian active-bin --pool <addr>
meridian search-pools --query <name_or_symbol>
meridian study --pool <addr> [--limit 4]Token research
meridian token-info --query <mint_or_symbol>
meridian token-holders --mint <addr> [--limit 20]
meridian token-narrative --mint <addr>Deploy & manage
meridian deploy --pool <addr> --amount <sol> [--bins-below 69] [--bins-above 0] [--strategy bid_ask|spot|curve] [--dry-run]
meridian claim --position <addr>
meridian close --position <addr> [--skip-swap] [--dry-run]
meridian swap --from <mint> --to <mint> --amount <n> [--dry-run]
meridian add-liquidity --position <addr> --pool <addr> [--amount-x <n>] [--amount-y <n>] [--strategy spot]
meridian withdraw-liquidity --position <addr> --pool <addr> [--bps 10000]Agent cycles
meridian screen [--dry-run] [--silent] # one AI screening cycle
meridian manage [--dry-run] [--silent] # one AI management cycle
meridian start [--dry-run] # start autonomous agent with cron jobsConfig
meridian config get
meridian config set <key> <value>Learning & memory
meridian lessons
meridian lessons add "your lesson text"
meridian performance [--limit 200]
meridian evolve
meridian pool-memory --pool <addr>Blacklist
meridian blacklist list
meridian blacklist add --mint <addr> --reason "reason"Discord signals
meridian discord-signals
meridian discord-signals clearBalance
meridian balanceFlags
| Flag | Effect |
|---|---|
--dry-run |
Skip all on-chain transactions |
--silent |
Suppress Telegram notifications for this run |
The Discord listener watches configured channels (e.g. LP Army) for Solana token calls and queues them as signals for the screener agent.
cd discord-listener
npm installAdd to your root .env:
DISCORD_USER_TOKEN=your_discord_account_token # from browser DevTools → Network
DISCORD_GUILD_ID=the_server_id
DISCORD_CHANNEL_IDS=channel1,channel2 # comma-separated
DISCORD_MIN_FEES_SOL=5 # minimum pool fees to pass pre-checkThis uses a selfbot (personal account automation, not a bot token). Use responsibly.
cd discord-listener
npm startOr run it in a separate terminal alongside the main agent. Signals are written to discord-signals.json and picked up automatically by /screen and node cli.js screen.
Each incoming token address passes through a pre-check pipeline before being queued:
- Dedup — ignores addresses seen in the last 10 minutes
- Blacklist — rejects blacklisted token mints
- Pool resolution — resolves the address to a Meteora DLMM pool
- Rug check — checks deployer against
deployer-blacklist.json - Fees check — rejects pools below
DISCORD_MIN_FEES_SOL
Signals that pass all checks are queued with status pending. The screener picks up pending signals and processes them as priority candidates before running the normal screening cycle.
Add known rug/farm deployer wallet addresses to deployer-blacklist.json:
{
"_note": "Known farm/rug deployers — add addresses to auto-reject their pools",
"addresses": [
"WaLLeTaDDressHere"
]
}- Create a bot via @BotFather and copy the token
- Add to
.env:
TELEGRAM_BOT_TOKEN=<token>
TELEGRAM_CHAT_ID=<your chat id> # .env alone is enough; also saved to user-config by setup
TELEGRAM_ALLOWED_USER_IDS=<user id> # required for group/supergroup controlMeridian does not auto-register the first chat for safety — you must set TELEGRAM_CHAT_ID explicitly. For groups, also set TELEGRAM_ALLOWED_USER_IDS or inbound commands are ignored.
401 Unauthorized in logs means a bad TELEGRAM_BOT_TOKEN (invalid, revoked, or encrypted without a working .envrypt key) — not a chat ID problem.
Meridian sends notifications automatically for:
- Management cycle reports (reasoning + decisions)
- Screening cycle reports (what it found, whether it deployed)
- OOR alerts when a position leaves range past
outOfRangeWaitMinutes - Deploy: pair, amount, position address, tx hash
- Close: pair and PnL
| Command | Action |
|---|---|
/positions |
List open positions with progress bar |
/close <n> |
Close position by list index |
/set <n> <note> |
Set a note on a position |
You can also chat freely via Telegram using the same interface as the REPL. Only allowed user IDs can issue commands in groups.
All fields are optional — defaults shown. Edit user-config.json.
| Field | Default | Description |
|---|---|---|
minFeeActiveTvlRatio |
0.05 |
Minimum fee/active-TVL ratio |
minTvl |
10000 |
Minimum pool TVL (USD) |
maxTvl |
150000 |
Maximum pool TVL (USD) |
minVolume |
500 |
Minimum pool volume |
minOrganic |
60 |
Minimum organic score (0–100) |
minHolders |
500 |
Minimum token holder count |
minMcap |
150000 |
Minimum market cap (USD) |
maxMcap |
10000000 |
Maximum market cap (USD) |
minBinStep |
80 |
Minimum bin step |
maxBinStep |
125 |
Maximum bin step |
timeframe |
5m |
Candle timeframe for screening |
category |
trending |
Pool category filter |
minTokenFeesSol |
30 |
Minimum all-time fees in SOL |
maxBotHoldersPct |
30 |
Maximum bot holder % (Jupiter audit) |
maxTop10Pct |
60 |
Maximum top-10 holder concentration |
blockedLaunchpads |
[] |
Launchpad names to never deploy into |
| Field | Default | Description |
|---|---|---|
deployAmountSol |
0.5 |
Base SOL per new position |
positionSizePct |
0.35 |
Fraction of deployable balance to use |
maxDeployAmount |
50 |
Maximum SOL cap per position |
gasReserve |
0.2 |
Minimum SOL to keep for gas |
minSolToOpen |
0.55 |
Minimum wallet SOL before opening |
outOfRangeWaitMinutes |
30 |
Minutes OOR before acting |
stopLossPct |
-15 |
Close position if price drops by this % |
takeProfitPct |
5 |
Close when fees earned reach this % of capital |
trailingTakeProfit |
true |
Enable trailing take-profit |
trailingTriggerPct |
3 |
Activate trailing TP at this PnL % |
trailingDropPct |
1.5 |
Close when PnL drops this % from peak |
strategy |
bid_ask |
LP strategy: spot, bid_ask, or curve |
| Field | Default | Description |
|---|---|---|
managementIntervalMin |
10 |
Management cycle frequency (minutes) |
screeningIntervalMin |
30 |
Screening cycle frequency (minutes) |
| Field | Default | Description |
|---|---|---|
managementModel |
openai/gpt-oss-20b:free |
LLM for management cycles |
screeningModel |
openai/gpt-oss-20b:free |
LLM for screening cycles |
generalModel |
openai/gpt-oss-20b:free |
LLM for REPL / chat |
Override model at runtime:
node cli.js config set screeningModel anthropic/claude-opus-4-5
After every closed position the agent runs studyTopLPers on candidate pools, analyzes on-chain behavior of top performers (hold duration, entry/exit timing, win rates), and saves concrete lessons. Lessons are injected into subsequent agent cycles as part of the system context.
Add a lesson manually:
node cli.js lessons add "Never deploy into pump.fun tokens under 2h old"After 5+ positions have been closed, run:
node cli.js evolveThis analyzes closed position performance (win rate, avg PnL, fee yields) and automatically adjusts screening thresholds in user-config.json. Changes take effect immediately.
HiveMind sync uses Agent Meridian at https://api.agentmeridian.xyz by default with the built-in public key. Agents can register, pull shared lessons/presets, and push learning events without a separate registration flow.
What you get:
- Shared lessons from other Meridian agents
- Strategy presets and crowd performance context
- Role-aware lessons injected into future screener/manager prompts when
hiveMindPullModeisauto
What you share:
- Lessons from
lessons.json - Closed-position performance events: pool, pool name, base mint, strategy, close reason, PnL, fees, and hold time
- Agent heartbeat metadata: agent ID, version, timestamp, and basic capability flags
- Private keys and wallet balances are never sent
HiveMind failures are non-blocking. If Agent Meridian is unavailable, the agent logs a warning and keeps running.
No manual HiveMind registration command is required for the shared Agent Meridian setup. agentId is generated automatically on startup if it is missing.
To use a private HiveMind API key, check the Telegram announcement channel and set it as hiveMindApiKey.
Relevant config fields:
{
"agentId": "",
"hiveMindUrl": "",
"hiveMindApiKey": "",
"hiveMindPullMode": "auto"
}Blank hiveMindUrl and hiveMindApiKey values intentionally fall back to the Agent Meridian defaults. Set hiveMindPullMode to manual if you do not want shared lessons and presets pulled automatically.
There is currently no empty-string disable path for HiveMind; blank values fall back to the built-in Agent Meridian defaults. A true off switch should be implemented as an explicit config flag before documenting HiveMind as disabled by clearing fields.
LLM_BASE_URL=http://localhost:1234/v1
LLM_API_KEY=lm-studio
LLM_MODEL=your-local-model-nameAny OpenAI-compatible endpoint works.
index.js Main entry: REPL + cron orchestration + Telegram bot polling
agent.js ReAct loop: LLM → tool call → repeat
config.js Runtime config from user-config.json + .env (repo-root paths)
repo-root.js Stable absolute repo path — used by PM2, state files, and .env loading
prompt.js System prompt builder (SCREENER / MANAGER / GENERAL roles)
state.js Position registry (state.json)
decision-log.js Structured decision log for deploy, close, skip, and no-deploy rationale
lessons.js Learning engine: records performance, derives lessons, evolves thresholds
pool-memory.js Per-pool deploy history + snapshots
strategy-library.js Saved LP strategies
telegram.js Telegram bot: polling + notifications
hivemind.js Agent Meridian HiveMind sync
smart-wallets.js KOL/alpha wallet tracker
token-blacklist.js Permanent token blacklist
cli.js Direct CLI — every tool as a subcommand with JSON output
tools/
definitions.js Tool schemas (OpenAI format)
executor.js Tool dispatch + safety checks
dlmm.js Meteora DLMM SDK wrapper
screening.js Pool discovery
wallet.js SOL/token balances + Jupiter swap
token.js Token info, holders, narrative
study.js Top LPer study via LPAgent API
discord-listener/
index.js Selfbot Discord listener
pre-checks.js Signal pre-check pipeline
.claude/
agents/
screener.md Claude Code screener sub-agent
manager.md Claude Code manager sub-agent
commands/
screen.md /screen slash command
manage.md /manage slash command
balance.md /balance slash command
positions.md /positions slash command
candidates.md /candidates slash command
study-pool.md /study-pool slash command
pool-ohlcv.md /pool-ohlcv slash command
pool-compare.md /pool-compare slash command
This software is provided as-is, with no warranty. Running an autonomous trading agent carries real financial risk — you can lose funds. Always start with DRY_RUN=true to verify behavior before going live. Never deploy more capital than you can afford to lose. This is not financial advice.
The authors are not responsible for any losses incurred through use of this software.