An autonomous Solana trading agent. An LLM makes live buy/sell decisions on a single target token from a real mainnet wallet, and a btop-style Textual dashboard shows you what it's doing.
- Runs a cycle every N seconds. Each cycle fetches on-chain balances, recent price history, and trade history, hands them to an LLM, and records the model's decision (BUY, SELL, or HOLD) along with the reason.
- Executes real swaps through Jupiter DEX aggregator. SOL, USDC, and one configured target token are the only mints it can touch — a tool-layer guard rejects anything else.
- Tracks cost basis in a lot-based ledger. Every position change (bot trade, external deposit, external withdrawal, gas) is a cost-basis event; realized and unrealized P&L come from FIFO lot matching instead of volume math. A reconciler runs before every cycle and at startup to absorb any on-chain drift the bot didn't cause.
- Shows it all live in a 3×3 Textual grid: Portfolio, Health (P&L gauge), Trade ledger, Market Charts (SOL/target price plus RSI / IPP / rolling-volatility sparklines for the target token), Level5 Billing (inference cost), Cycle status, and a scrollable log tail.
- Bills its own inference through your choice of Level5 or UsePod — Solana-native pay-as-you-go LLM proxies. Run account-based (fund a USDC balance once, draw per request) or fully accountless via UsePod's x402 endpoint (no account/token — the bot pays each call straight from the local wallet, bounded by config spend caps). Pick with
--provider {level5,usepod,usepod-x402}at startup (or theproviderconfig key); the bot remembers your choice in the saved credentials file.
Linux / macOS:
curl -LsSf https://raw.githubusercontent.com/Sortis-AI/pod_trades/main/install.sh | bashInstalls git, uv, and Python 3.12 (via snap → apt on Linux or brew on macOS), clones the repo to ~/pod-the-trader, runs uv sync, and drops a launcher at ~/.local/bin/pod-the-trader.
Windows 10 / 11 (run in PowerShell inside Windows Terminal — not legacy cmd.exe):
irm https://raw.githubusercontent.com/Sortis-AI/pod_trades/main/install.ps1 | iexInstalls git (via winget), uv, and Python 3.12, clones the repo to %USERPROFILE%\pod-the-trader, runs uv sync, and drops a launcher at %USERPROFILE%\.local\bin\pod-the-trader.cmd.
Then open a new shell and run:
pod-the-traderOn first launch you'll see a disclaimer. Type I ACCEPT to continue, then walk through the Level5 registration and wallet-generation prompts. If you let the wizard generate a new wallet, it will print the private key exactly once and require you to type I SAVED IT to confirm you've backed it up — this is your only chance to capture it, so copy it into a password manager or write it down before continuing.
To upgrade later, run pod-the-trader update — it pulls the latest code and re-runs uv sync.
Heads-up: the bot moves real funds on Solana mainnet. Start by funding the wallet with a small amount you're comfortable losing entirely, watch it run for a few cycles, and read the disclaimer carefully — it's shown at every launch for a reason.
- OS: Linux, macOS, or Windows 10/11. On Windows, use PowerShell inside Windows Terminal — legacy
cmd.execannot render the Textual dashboard. - Python: 3.12+
- Wallet: a Solana keypair. The installer can generate one for you on first run; the private key lives at
~/.pod_the_trader/keypair.json(%USERPROFILE%\.pod_the_trader\keypair.jsonon Windows) and is never transmitted. - Provider account (Level5 or UsePod): the installer walks you through creating one. Pick whichever provider you prefer (
--provider level5or--provider usepod) and fund it with USDC. Level5 also issues promotional credits in some campaigns; UsePod is USDC-only. The bot refuses to trade when the balance falls below a configured floor. - Some SOL in your trading wallet to cover Jupiter gas.
There are three providers. Level5 and UsePod are account-based: register once, fund the account with USDC, and the bot draws from that server-side balance per request. UsePod x402 is accountless: there's no registration, no API token, and no dashboard — the bot pays for each inference call directly from your local Solana wallet over the x402 protocol.
Level5 and UsePod expose a byte-identical API surface (UsePod is a Rust port of Level5), so the only operator-facing differences are:
- Default model name. Level5 has its own catalog (e.g.
minimax-m2.7); UsePod proxies OpenAI/Anthropic-shaped names (e.g.claude-sonnet-4-6,gpt-5.3-codex). Updateagent.modelin your config when you switch. - Credits. Level5 supports promotional credits separate from USDC; UsePod is USDC-only. The dashboard hides the Credits row automatically when UsePod is active.
- Dashboard URL. Level5 uses
https://level5.cloud/dashboard/<token>; UsePod useshttps://usepod.ai/dashboard?token=<token>.
UsePod x402 (accountless, usepod-x402). No account or token — the wallet is the identity. Each request hits https://api.usepod.ai/proxy/x402/v1, receives an HTTP 402 payment challenge, and the bot pays the quoted USDC on-chain from the trading wallet, then retries. Because real funds move automatically on every call, two config-editable safety caps bound the spend (see the usepod-x402 config section):
per_request_cap_usdc(default0.50) — reject any single 402 quote above this; the cycle aborts rather than overpay a malformed/hostile quote.max_daily_x402_spend_usdc(default10.0) — once cumulative x402 spend in a UTC day reaches this, inference pauses until the next day.
Inference spend competes with trading capital in the same wallet (min_balance_threshold_usdc gates it). There's no dashboard — spend is visible on-chain.
UsePod's x402 runs in cap-with-surplus-credit mode: a payment charges your actual usage and credits the unused remainder (cap − actual) to a balance keyed to your wallet, applied to later calls — so one payment typically funds many requests and the bot pays only when a fresh 402 arrives. Because the quoted amount_microunits is a cap pre-authorization (the full amount leaves the wallet on-chain; the surplus is recoverable only as future inference credit, not a refund), set per_request_cap_usdc at or above the gateway's cap quote — if x402 calls keep aborting on the per-request cap, raise it.
You can pick a provider three ways, listed in increasing precedence:
- Config file — set
provider: "usepod-x402"at the top of your YAML. - CLI flag —
pod-the-trader --provider usepod-x402(overrides config). Valid values:level5,usepod,usepod-x402. - Env-skipped onboarding — set
LEVEL5_API_TOKENorUSEPOD_API_TOKEN. The wizard reads only the active provider's variable; cross-pairs are ignored. (x402 needs no token — only a funded wallet.)
The first time you launch, the wizard asks you to pick a provider. Account-based providers then offer register / paste-token / skip; the accountless x402 option just confirms the wallet will pay. The choice is saved in ~/.pod_the_trader/level5_credentials.json (filename kept for backward compatibility) along with the provider key, so subsequent launches don't re-prompt.
Defaults live in pod_the_trader/config/defaults.yaml. Override any of them with a user YAML file passed via --config:
pod-the-trader --config ~/my-trader.yamlThe knobs you'll care about most:
| Key | Default | What it does |
|---|---|---|
trading.target_token_address |
EN2nn…SQUIRE |
The token the bot is trying to trade profitably. Can also be set via TARGET_TOKEN_ADDRESS env var. |
trading.max_position_size_usdc |
100 |
Hard cap on position size, in USDC. |
trading.max_slippage_bps |
150 |
Max Jupiter slippage tolerance, in basis points. |
trading.min_trade_size_usdc |
1.0 |
Swaps below this USD value are rejected at the tool layer. Network fees would exceed the trade value. |
trading.max_daily_trades |
20 |
Per-day safety cap. |
trading.cooldown_seconds |
300 |
Seconds to wait between cycles. |
trading.max_price_impact_pct |
1.5 |
Refuse swaps with worse Jupiter-reported price impact. |
trading.fallback_slice_usdc |
25.0 |
Slice size used when Jupiter doesn't report liquidity_usd on the latest tick. Prevents the min($150, 0.015 * liquidity_usd) sizing rule from collapsing to $0 and silently blocking trades. |
provider |
level5 |
Active LLM-proxy provider. One of level5, usepod, or usepod-x402 (accountless, wallet-paid). Override at startup with --provider. |
level5.min_balance_threshold_usdc |
0.1 |
Pause trading when Level5 balance drops below this floor. |
level5.base_domain |
level5.cloud |
Host for the Level5 API (api.<domain>) and dashboard (<domain>/dashboard/<token>). Override on the command line with --base-domain. |
usepod.min_balance_threshold_usdc |
0.1 |
Pause trading when UsePod balance drops below this floor. |
usepod.base_domain |
usepod.ai |
Host for the UsePod API and dashboard (UsePod uses <domain>/dashboard?token=<token>). |
usepod-x402.x402_asset |
USDC |
Asset used to settle accountless x402 inference charges. |
usepod-x402.per_request_cap_usdc |
0.50 |
Reject (and abort the cycle on) any single x402 quote above this — bounds a bad/hostile quote. |
usepod-x402.max_daily_x402_spend_usdc |
10.0 |
Pause x402 inference once cumulative USDC spend in a UTC day reaches this. |
usepod-x402.min_balance_threshold_usdc |
1.0 |
Pause trading/inference when the wallet's USDC (the x402 budget) drops below this floor. |
agent.model |
minimax-m3 |
The LLM the active provider proxies to. UsePod expects OpenAI/Anthropic-shaped names like claude-sonnet-4-6 or gpt-5.3-codex; Level5 has its own model catalog. Change this when you switch providers. |
agent.max_iterations_per_turn |
10 |
Max tool-call iterations per cycle. |
# Full dashboard (default if stdout is a real terminal)
pod-the-trader
# Plain CLI mode (default if stdout is piped)
pod-the-trader --cli
# Force TUI even when output is piped
pod-the-trader --tui
# Custom config
pod-the-trader --config path/to/config.yaml
# Choose an LLM-proxy provider (default: level5)
pod-the-trader --provider usepod
# Point at an alternate deployment host (host-only override; useful for
# self-hosted Level5-compatible deployments).
pod-the-trader --base-domain alt.example.com
# Pull the latest code and re-sync dependencies
pod-the-trader updateKeybindings inside the TUI:
| Key | Action |
|---|---|
q |
Quit (graceful shutdown — finishes current cycle) |
Ctrl+C |
Same as q |
| Click wallet address in Portfolio panel | Copy wallet to clipboard |
| Click dashboard URL in Level5 panel | Open Level5 dashboard in default browser |
On exit the bot prints a summary of the current session: closed trades, realized P&L, unrealized P&L, open position, gas spent.
┌─ Trading agent (async) ───────────────────────────────────────┐
│ │
│ Level5 balance ──┐ │
│ Price sample ──┼──▶ Reconcile on-chain ──▶ Run LLM turn │
│ Wallet snapshot ──┘ │ │
│ │ │
│ Tool registry ◀─────────┤ │
│ │ │ │
│ ┌──────────────────────┼─────────────────────┐ │
│ │ │ │ │ │ │
│ Market Portfolio History Trading Solana │
│ tools tools tools tools tools │
│ │ │
│ Jupiter DEX aggregator │
│ │ │
└───────────────────────────────────────┼───────────────────────┘
│
Solana mainnet
- Agent (
pod_the_trader/agent/core.py) runs the cycle loop, builds the system prompt, enforces that BUY/SELL decisions are backed by an actualexecute_swapcall, and publishes events to a TUI publisher. The prompt encodes a mean-reversion strategy keyed on the Inference Payback Period (IPP) —500_000 × target_price_usd≈ days for $1/day of inference yield to repay the market price of 500,000 target tokens — with bands at IPP 180 / 365 / 500 mapping to BUY / NEUTRAL / HOLD-ONLY / TRIM regimes. - Tools (
pod_the_trader/tools/) are the only way the model touches the world. Every swap entry point is gated by a route guard (SOL/USDC/target only) and a minimum-trade-size guard ($1 default). - Lot ledger (
pod_the_trader/data/lot_ledger.py) is an event-sourced cost-basis ledger. FIFO matching produces closed segments with entry and exit prices; realized P&L falls out of those directly. - Reconciler (
pod_the_trader/data/reconciler.py) compares ledger open-lot sum against the actual on-chain balance every cycle and at startup, and emits synthetic events to absorb any drift. - TUI (
pod_the_trader/tui/) is a Textual app that implements the Publisher protocol; the agent sends events, widgets update reactively. - Level5 client (
pod_the_trader/level5/) handles account registration, balance queries, and auto-deposit from the trading wallet when inference credits run low.
This software moves real funds on mainnet. Read pod_the_trader/disclaimer.py for the full list of things that can go wrong. Short version:
- The LLM can hallucinate, misread data, and pick bad trade sizes. It has done all of these during development.
- There is no testnet mode, no dry run, and no undo for on-chain transactions.
- Your private key lives at
~/.pod_the_trader/keypair.json(or%USERPROFILE%\.pod_the_trader\keypair.jsonon Windows). Anyone with access to that file can drain your wallet. The bot restricts it to the owning user only —chmod 0o600on POSIX,icacls /inheritance:r /grant:r <user>:Fon Windows — but filesystem permissions are the last line of defense, not the first. - Memecoin trading is high-risk; most positions lose money. Do not put more into the trading wallet than you can afford to lose entirely.
The bot enforces several hard guards to reduce foot-gun potential:
- Tradeable universe: only SOL, USDC, and the configured target token. Anything else is rejected by the tool layer.
- Minimum trade size: swaps under
min_trade_size_usdc(default $1) are rejected so dust trades don't bleed fees. - Decision-execution enforcement: if the model writes
DECISION: SELLbut never callsexecute_swap, the trade loop catches it, nudges the model to either execute or downgrade, and falls back toHOLDif it still doesn't comply — so the dashboard can never show a sell that didn't happen. - Disclaimer on every startup: no bypass, no env var shortcut. You type
I ACCEPTevery time.
# Install dev dependencies
uv sync --extra dev
# Run the full suite
uv run pytest tests/
# Lint
uv run ruff check pod_the_trader/ tests/
uv run ruff format pod_the_trader/ tests/
# End-to-end smoke test (clears conversation state, runs for 60s, checks the log)
bash scripts/check.sh 60The test suite is mostly unit tests against real component boundaries with mocked external services (Level5, Jupiter, Solana RPC). The TUI widgets are tested through Textual App.run_test() so reactive behavior runs for real.
Releases are tagged vX.Y.Z and published on GitHub. Operator machines pull them in via pod-the-trader update, which does git fetch && git reset --hard origin/<branch> && uv sync — no version pin to bump, no installer change required. The installers (install.sh, install.ps1) clone HEAD, so new installs also pick up the latest release automatically.
End-to-end release procedure:
# 1. Bump the version. Bundling the bump in the same commit as the
# user-visible change is the established pattern — see e63dd52
# (0.2.0) and 21d2bad (0.2.1).
sed -i 's/^version = ".*"/version = "X.Y.Z"/' pyproject.toml
# 2. Regenerate uv.lock (this happens implicitly on the next `uv run`,
# but doing it explicitly keeps the lock change in the same commit
# as the version bump). Pass `--extra dev` so lint/test tools stay
# installed — a bare `uv sync` will uninstall them.
uv sync --extra dev
# 3. Lint + tests must pass before tagging.
uv run ruff check pod_the_trader/ tests/
uv run ruff format pod_the_trader/ tests/
uv run pytest -q
# 4. Commit. Stage specific files only — never `git add -A`.
git add pyproject.toml uv.lock <other-changed-files>
git commit -m "<one-line summary>"
# 5. Annotated tag with a short body listing the commits included.
git tag -a vX.Y.Z -m "X.Y.Z: <one-line summary>
<short-sha-1> <subject>
<short-sha-2> <subject>"
# 6. Push the branch first, then the tag — never the tag alone, or
# GitHub will create a release whose commits are not yet on main.
git push origin main
git push origin vX.Y.Z
# 7. Publish the GitHub release. Notes follow the v0.2.0/v0.2.1
# template: short summary, one section per change with the "why",
# config additions table, operator-visible gaps that remain.
gh release create vX.Y.Z \
--title "vX.Y.Z — <short title>" \
--notes "$(cat <<'EOF'
<markdown release notes>
EOF
)"Conventions:
- Never force-push
mainor rewrite published tags. - Never skip pre-commit hooks (
--no-verify) or bypass signing on a release commit. - A release that changes the strategy prompt should call out behavior changes the operator will see on the next cycle — strategies in this repo are live on mainnet wallets, not lab experiments.
- If a release introduces a config field, add it to the Configuration table in the same pass.
pod_the_trader/
├── agent/ # LLM trading loop + conversation memory
├── config/ # defaults.yaml + validated loader
├── data/ # trade ledger, lot ledger, price log, wallet log, reconciler
├── level5/ # Level5 account, balance, auto-funding
├── tools/ # LLM-callable tools (swap, portfolio, market, history)
├── trading/ # Jupiter DEX, Portfolio, transaction builder
├── tui/ # Textual dashboard + widgets
├── wallet/ # Keypair management + setup prompts
├── disclaimer.py # Startup disclaimer (I ACCEPT gate)
└── main.py # Entry point (CLI + TUI paths)
tests/ # Unit + integration + e2e tests
scripts/ # check.sh, e2e_test.sh, debug helpers
docs/ # Screenshots + user-facing docs
install.sh # One-shot installer (curl | bash)
Pod The Trader is free software released under the GNU General Public License v3.0 or later. See LICENSE for the full text.
Pod The Trader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This is the full disclaimer shown at every launch. You must type I ACCEPT before the bot will start. It is reproduced here verbatim from pod_the_trader/disclaimer.py so you can read it before you install.
POD THE TRADER — DISCLAIMER
This is autonomous trading software that will buy and sell real tokens on the Solana mainnet using your wallet and your money. Before you start it, read this and make sure you understand what you're agreeing to.
REAL FUNDS. Every trade moves actual SOL, USDC, and SPL tokens from your wallet. There is no testnet mode, no dry run, no simulation layer. Losses are real and on-chain transactions are irreversible.
LLM-DRIVEN DECISIONS. Trading decisions are made by a large language model (minimax-m3 via the configured LLM proxy). The model can hallucinate, misread market data, make arithmetic errors, pick bad sizes, or act on stale context. It has already done all of these during development. Do not assume it will make profitable trades.
NO WARRANTY. This software is experimental and provided as-is, with no guarantee of correctness, profitability, uptime, or data integrity. Recent history includes pricing bugs, unintended trade routes, dust trades, and decision-execution mismatches. More bugs almost certainly remain.
YOU ARE THE OPERATOR. You are responsible for monitoring the bot, setting sensible position limits, funding the wallet appropriately, and shutting it down if something looks wrong. The bot will not stop itself just because it's losing money.
NOT FINANCIAL ADVICE. Nothing this software outputs — on-screen, in logs, or in summaries — constitutes financial, legal, tax, or investment advice. Memecoin trading is high-risk and most positions lose money.
KEY CUSTODY. Your private key lives in
~/.pod_the_trader/. Anyone with access to that file can drain your wallet. You are solely responsible for the security of that file and the machine it sits on.NO RECOURSE. If the bot loses your money, executes an unintended trade, fails to execute an intended trade, or misreports P&L, there is no one to appeal to. Do not put more into this wallet than you can afford to lose entirely.
By continuing, you confirm that you have read and understood the above, that you accept full responsibility for any losses, and that you are running this software voluntarily and at your own risk.
