6.5 KiB
05 — Local Config, Market Selection, and Operations
Google Sheets currently plays four roles: config store, market database,
selection UI, and dashboard. v2 replaces each with a local equivalent. The
test: a laptop with the repo, a .env, and a funded wallet is a complete
deployment. No Google account, no service-account JSON, no second IP running
a scanner.
1. Configuration files (replaces Hyperparameters + Selected Markets sheets)
All TOML, all in config/, all validated by pydantic at load, all
hot-reloadable via watchfiles (applied at the next quoter wake; invalid edits
are rejected with a logged diff, keeping the last good config — an edit can
never crash the bot).
config/config.toml — engine & account
[wallet]
# secrets stay in .env: PK, BROWSER_ADDRESS; this file only references them
signature_type = 2 # revisit after Phase 2 wallet spike (03 §1)
[engine]
debounce_ms = 200
reconcile_interval_s = 30
heartbeat = true # exchange dead-man switch (03 §3)
journal = true # raw WS/order journal for the backtester
[risk] # global — per-market caps live in markets.toml
max_total_exposure_usdc = 5000
max_event_group_loss_usdc = 1000 # neg-risk group worst-case
daily_loss_kill_usdc = 250
ws_stale_halt_s = 10
config/strategy.toml — named parameter profiles
Schema in 04 §8. Profiles (political-longdated, political-hot, …) replace
the Hyperparameters sheet's param_type groups.
config/markets.toml — the trade list (replaces Selected Markets sheet)
[[markets]]
slug = "will-x-win-the-2028-democratic-nomination"
profile = "political-longdated"
q_max_usdc = 800 # optional per-market overrides of the profile
[[markets]]
condition_id = "0x37a6de..." # slug or condition_id both accepted
profile = "political-hot"
enabled = false # keep the entry, stop quoting
The bot resolves slugs → tokens/metadata via the catalog at startup and on reload. Adding a market while running = edit file, save; the engine subscribes, seeds the book, starts quoting. Removing/disabling = cancel that market's orders, unsubscribe. This is the entire "market management UI".
.env keeps only secrets: PK, BROWSER_ADDRESS, optional RPC URL override.
SPREADSHEET_URL and credentials.json are deleted.
2. Market catalog & scanner (replaces data_updater + All/Volatility Markets sheets)
state.db (SQLite) holds the market catalog; the scanner is a subcommand, not
a second deployment:
polymaker scan— Gamma keyset sweep with server-side filters (03 §4): politics tag(s), active, min liquidity/volume, end-date window, rewards present. Enriches the shortlist with batch/booksdepth and reward-density estimates (the v1add_formula_paramsmath, kept) plus the maker-rebate estimate (taker-fee volume × rebate share — 03 §3). Writes to SQLite. Seconds per run; scheduled inside the bot process (e.g. every 15 min) — the "run on a different IP" advice dies with the crawl that motivated it.polymaker markets— rank/browse the catalog in the terminal (rich table):polymaker markets --tag politics --min-reward 20 --max-vol-sum 20 --sort score. Score = expected daily income (rewards + rebates) vs. volatility & spread risk — v1'sgm_reward_per_100/volatility_sumcomposite, recalibrated.polymaker markets add <slug> [--profile P]— appends tomarkets.toml(file remains the source of truth; the command is a convenience editor).- Volatility for ranking:
prices-historycoarse estimate at scan time; markets we quote get live WS-derived vol which supersedes it.
3. Runtime CLI (replaces update_stats.py + Summary sheet + eyeballing prints)
Single polymaker entrypoint (typer):
| Command | Purpose |
|---|---|
polymaker run [--paper] |
start the engine (paper mode: full pipeline, no order POSTs) |
polymaker status |
positions, open quotes, inventory, PnL, regime per market (reads SQLite; works while the bot runs) |
polymaker pnl [--daily] |
realized/unrealized PnL, reward + rebate income vs. estimates |
polymaker flatten [market] |
reduce-only mode (maker exits) for one/all markets |
polymaker cancel-all |
panic button (also runs automatically at startup) |
polymaker scan / markets |
catalog (above) |
polymaker sim replay ... |
backtester (04 §9) |
polymaker doctor |
preflight: wallet auth spike checks, pUSD balance/allowances, WS reachability, clock skew |
4. Observability
- structlog JSON logs to
logs/+ human-readable console. Every order decision logs its inputs (FV, σ, inventory, regime) — a quote is always explainable after the fact. - Journal (
journal/*.jsonl): raw WS in / orders out. Feeds the backtester and post-mortems. - SQLite is the queryable operational record: fills, orders, PnL marks, reward payouts, risk events. Any dashboard we want later (Grafana, a small web page) reads this — no scoping dependency on it now.
- Alerting (minimal, phase 4): kill-switch trips, WS-stale halts, reconcile divergence, heartbeat failures → log at CRITICAL + optional webhook URL in config (generic; Discord/Telegram/ntfy all take a POST).
- KPI set defined in 04 §9 (markouts, two-sided in-band uptime, fill ratio, income vs. estimate).
5. Deployment & runtime ops
- uv end-to-end (already adopted; finish the job):
requires-python = ">=3.12", refreshed lock,uv run polymaker ..., dev group with ruff, mypy, pytest, pytest-asyncio. Delete the 3.9 pin. - Placement: any always-on Linux box; latency to the CLOB (AWS us-east region) is the one infra knob that matters for queue position — a small VPS in us-east-1 beats a laptop on wifi. Not required for correctness.
- Supervision: systemd unit example in the README (
Restart=always); startup sequence is cancel-all → snapshot → resume, so a restart is always safe. The exchange heartbeat covers the gap between death and restart. - CI (GitHub Actions): ruff + mypy + pytest on PR. No deploy pipeline needed for a single-operator bot.
- State backup:
state.db+config/are the whole world; journal grows ~MBs/day per market — rotate + optionally compress with age.
6. Security notes
PKonly ever in.env(gitignored) / environment; never in TOML, logs, or SQLite. Log wallet addresses truncated.- The CLI never prints secrets;
doctorverifies without echoing. - Dependencies pinned via uv.lock; renovate/dependabot optional later.