Configuration
where every setting lives.
Three places hold settings: config.json for the whole desk, the console for the main switches, and environment variables for keys and the server. This page says which one wins and lists the keys you are most likely to change.
01Where settings live
The commands on this page are written for the shell of macOS and Linux (bash or zsh). On Windows, run them in WSL (Windows Subsystem for Linux, Ubuntu) or Git Bash; in PowerShell, set a variable with $env:NAME="value" instead of export NAME=value. The node and npm commands work in any of them.
| Place | What goes there | When it applies |
|---|---|---|
src/defaults.mjs | Every default, with a comment on each key. Do not edit it; read it. (src/config.mjs loads and type-checks your settings.) | Always, unless overridden |
config.json | Your overrides of any key from src/defaults.mjs. Created from config.example.json, ignored by git. | At start. Restart after a change. |
Console (/admin) | Mode, bankroll, limits, lanes, smart-wallet rules, liquidation map, research refresh, keys; the leverage profile with the perps module. | At start and immediately after each save. A console value overrides config.json once you change it from the console default (table). |
| Environment variables | Keys, Telegram ids, dashboard port and password, console secret key. | At start. Keys in the environment win over keys stored in the console. |
02How config.json works
- You only write the keys you change. Nested objects are merged with the defaults, so
{"limits": {"maxOpen": 3}}keeps every other limit. - Lists replace the default list entirely (for example
news.feeds). - A value of the wrong type is rejected at start with one clear line, for example
config: …config.json: bankrollSol: expected number, got string ("10"). Invalid JSON is reported with the file name. modemust bepaper,alertorlive. Anything else stops the desk at start, andliverefuses to start until its checks pass (Live trading).node src/cli.mjs doctorprints warnings for contradictions: zero or negative limits, placeholder wallets, a stop at or above the entry, exposure caps that make the last slots unreachable, and more.
The example file:
{
"mode": "paper",
"bankrollSol": 10,
"modules": { "perps": false, "polymarket": false, "jev": false },
"whales": { "wallets": ["PASTE_A_SOLANA_WALLET_YOU_FOLLOW"] },
"news": {
"feeds": [
"https://cointelegraph.com/rss",
"https://www.coindesk.com/arc/outboundfeeds/rss/",
"https://decrypt.co/feed"
]
}
}
PASTE_A_SOLANA_WALLET_YOU_FOLLOW is a placeholder. Replace it or make the list empty (how).
03The keys you will change most
| Key | Default | What it does |
|---|---|---|
mode | paper | paper: trades on paper with live data. alert: notifies only, no paper book. live: real orders with your keys (Live trading). |
modules | all false | The optional modules perps, polymarket and jev. While a module is off, its books do not run and the dashboard and console hide them (Optional modules). |
bankrollSol | 10 | Starting paper bankroll of the memecoin book, in SOL. |
loopSeconds | 20 | Seconds between desk cycles (kept at 5 or more). |
dataDir | data | Where state, journal, recordings and the console database are written. |
solanaRpc | public mainnet | Solana HTTP RPC for wallet reads and FOMO collection. |
pump.wsUrl | public mainnet WS | WebSocket for the real-time pump.fun stream. Use a private one (below). |
telegram.readChannels | false | Opt-in for channel reading: SHILL reads the channels below only when this is true. Reading channel pages may be against Telegram's terms of service and the channel owners' wishes (read this first). |
telegram.channels | [] | Public Telegram channels read through their public web preview page for contract addresses and $TICKERs, while telegram.readChannels is on. Bare names only. |
whales.wallets | [] | Solana wallets whose buys count as a confirming signal. Empty by default; add wallets you follow, or use the smart-wallet list. |
news.feeds | 6 RSS feeds | News sources. Add X accounts through your own RSSHub or nitter instance. |
news.llm | claude-opus-5, effort low, 8 per cycle | Claude model used when ANTHROPIC_API_KEY is set. |
ev.minEv, ev.kellyFraction, ev.maxPerTokenPct | 0.03, 0.2, 2 | The core EV gate: minimum expected return after costs, fraction of Kelly, and hard cap per token in % of bankroll. |
limits | maxOpen 5, dailyLossPct 10, cooldownMinutes 60, maxExposurePct 10 | Memecoin book limits. |
protections.hardStop.redPct | 20 | Drawdown from the equity peak at which everything is sold and the desk latches (kill switch). |
portfolio.maxGrossUsd | 8000 | Cap on gross USD notional across all perp books (perps module). |
<book>.enabled | true | Turns a book or agent off: security, pump, wallets, liqmap, research, alloc. The books of the optional modules (macro, hlcopy, trend, basis, xarb, funding, flow, polymarket, polymm, jev) run only while their module is on; then their own enabled flag applies. |
Every other key (lane thresholds, exit ladders, allocator, books) is documented by the comment next to it in src/defaults.mjs, and the important ones on the page of the feature they belong to.
04Console fields and the config keys they set
config.json is the base. With the console on (npm start, npm run dash), a console field overrides its config key only when its value differs from the console default in the table below, that is, once you really changed it. Exception: Desk → Mode and every Live trading field always come from the console while it is running (defaults included): a mode: "live" or live.* in config.json is used only when the console is off (DASH=0 or DESK_ADMIN=0). See Live trading. The check runs at start and after every save. If you set a field back to its console default, the config.json value (or the built-in default) applies again.
| Console field | Config key | Console default |
|---|---|---|
| Desk → Paper bankroll (SOL) | bankrollSol | 10 |
| Desk → Max open memecoin positions | limits.maxOpen | 5 |
| Desk → Daily loss stop (%) | limits.dailyLossPct | 10 |
| Memecoin lanes → Moonshot / Graduation / Revival / Smart money lane | pump.lanes.<lane>.enabled | on |
| Memecoin lanes → … ticket (% of paper equity) | pump.lanes.<lane>.stakePct | 1 (graduation 1.5) |
| Smart wallets → Rounds before a wallet can be smart | wallets.minRounds | 8 |
| Smart wallets → Minimum mean hold (s) | wallets.minMeanHoldSec | 60 |
| Smart wallets → Smart wallets needed for a consensus | pump.lanes.smart.minWallets | 3 |
| Smart wallets → Live candidate feeds | wallets.feeds | empty |
| Liquidation map → Refresh the map in the background | liqmap.enabled | on |
| Liquidation map → Coins | liqmap.coins | BTC ETH SOL HYPE XRP DOGE SUI BNB |
| Liquidation map → Accounts read per refresh | liqmap.wallets | 2000 |
| Liquidation map → Refresh every (min) | liqmap.everyMinutes | 30 |
| Liquidation map → Alert when price is within (%) | liqmap.alertWithinPct | 1.5 |
| Liquidation map → …holding at least (USD) | liqmap.alertMinUsd | 5,000,000 |
| Research → Refresh research in the background | research.enabled | on |
| Research → Wallet book every (min) | research.walletsEveryMinutes | 60 |
| Research → Backtests and lane replay every (hours) | research.fullEveryHours | 24 |
| Desk → Mode | mode | paper (also alert, live) |
| Live trading → confirmation, Dry run, pump.fun / Hyperliquid on, caps | live.* | empty, on, off, 0.1 SOL / $100 / $500 (all caps) |
| Trend & leverage → Leverage profile (perps module) | leverage.defaultProfile | growth |
One-off commands such as liq, wallets, replay or leverage do not start the console, so they use config.json and the defaults only. If you changed a value in the console, set the same value in config.json when you want those commands to match the running desk.
05Solana RPC and the pump.fun WebSocket
The public Solana endpoint drops heavy logsSubscribe streams. For a stable pump.fun stream, create a free endpoint at Helius, QuickNode or Chainstack and set it in one of two places:
- Console (owner): Integrations → Solana RPC / WebSocket URL. Enter the
https://URL. The desk uses it assolanaRpcand derives the WebSocket (pump.wsUrl) by replacinghttpwithws, at start and right after each save. The console's Solana RPC health check tests the same URL. When filled in, it wins overconfig.json. - config.json, if your provider's WebSocket URL is not simply the HTTP URL with
ws:
{
"solanaRpc": "https://<your-rpc-host>/?api-key=<key>",
"pump": { "wsUrl": "wss://<your-rpc-host>/?api-key=<key>" }
}
RPC URLs usually contain an API key, so the console stores this field as a secret: encrypted at rest, set by the owner only, never shown back after saving. If you put the URL in config.json instead, keep that file out of any public repository.
06Environment variables
| Variable | Purpose | |
|---|---|---|
ANTHROPIC_API_KEY | Claude news scoring and the AI analyst. | Optional |
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID | Alerts and commands (Telegram bot). | Optional |
TELEGRAM_OWNER_ID | Your Telegram user id; required for commands in a group chat. | Optional |
TYPESAFE_API_KEY | The Jev forecaster (jev module). | Optional |
SOLANA_PRIVATE_KEY | Live pump.fun trading wallet (Live trading). | Live only |
HL_API_WALLET_KEY, HL_ACCOUNT_ADDRESS | Live Hyperliquid API wallet and the account it trades for (perps module). | Live only |
ADMIN_SECRET_KEY | Encrypts the console's stored secrets. Generate one with openssl rand -hex 32. If unset, a key is generated into data/.admin-secret-key. Losing it makes stored secrets unreadable (Secrets and backups). | Recommended on a server |
ADMIN_SETUP_TOKEN | The one-time secret that creates the console owner at /admin: at least 24 characters, for example openssl rand -hex 24. Once the owner exists it is never accepted again; remove it. Left empty, the server prints a one-time setup code to its log instead (Create the owner). | Recommended on a server |
DASH_PORT | Dashboard and console port. Default 8790. | Optional |
DASH_HOST | Address the dashboard listens on. Default 127.0.0.1; the Docker setup uses 0.0.0.0 inside the container. | Optional |
DASH_TOKEN | Extra dashboard password (?token= or Authorization: Bearer). Needed when the console is off. | Optional |
DASH_ALLOWED_HOSTS | Public host names the dashboard answers behind a reverse proxy, comma-separated (Deployment). Without it, only loopback hosts are answered. | Optional |
TRUST_PROXY_HOPS | Number of reverse proxies in front of the console (usually 1 behind Caddy or nginx), so sign-in limits count each visitor by their own address (Deployment). Empty = 0. | Optional |
DASH=0 | npm start without dashboard and console. | Optional |
DESK_ADMIN=0 | Dashboard without the console. | Optional |
DASH_DATA | npm run dash reads this data folder instead of data/ (for replays). | Optional |
The desk loads .env next to package.json on start (cp .env.example .env); a variable already set in your shell or process manager wins. You can also export them in your shell, or set them in your process manager (Deployment).
export TELEGRAM_BOT_TOKEN=<token>
export TELEGRAM_CHAT_ID=<chat id>
npm start
07Temporary overrides for research
replay and sweep accept --set key=value, repeatable, without touching config.json. Values are read as JSON when they parse (numbers, true/false, lists). A key that does not exist is refused, so a typo cannot silently change nothing.
node src/cli.mjs replay --set pump.lanes.moonshot.enabled=false --set ev.minEv=0.05