Getting started
Installation
Run a simulator on your machine and point your bot at localhost. Same state every run.
What you need
- Mac or Linux, 64-bit. (Windows: zip on request.)
- An install link from Rystic, or a license file named like
rystic-license-acme-2026.json. - No account, no signup. Docker only if you want containers later.
No license yet? Request access.
Install
With an install link — paste the whole line; the long l= value is your license:
curl -fsSL "https://www.rystic.ai/api/install?l=eyJwYXlsb2FkIjoiZXlKc2FXTmxi..." | sh
With a license file:
curl -fsSL https://www.rystic.ai/install.sh | sh
rystic login ./rystic-license-acme-2026.json
Either way, success ends with your license activated:
installed rystic 0.0.20 → /usr/local/bin/rystic # or ~/.local/bin; RYSTIC_BIN overrides
license: /Users/you/.rystic/rystic-license.json
licensee: Acme Trading
products: [kalshi-twin]
expires: 2027-08-19 (valid)
- The license is verified on your machine and parked in
~/.rystic. Nothing phones home. - If Docker is running,
loginalso logs it in toregistry.rystic.ai.--no-dockerskips that. rystic license activate <file>is the same command minus the Docker half. You will meet both spellings.
Check everything
rystic doctor # add the product name if your license covers more than one
Every ✗ is followed by a → line with the command that fixes it. ! is a warning, not a failure. Before your first pull, ✗ install no installed version is normal — the first run pulls for you.
First run
rystic run kalshi-twin
kalshi-twin 0.0.25 (model f34d80dd, licensed to Acme Trading until 2027-08-19) → http://localhost:8080 [native default]
That line is the simulator’s identity: version, model hash, licensee, URL, runner. Port is 8080 unless you say otherwise; --port 0 takes a free one and prints it. Ctrl-C stops it.
In the background:
rystic run kalshi-twin -d --name dev --port 0
rystic ps
rystic stop dev
Run a bot against it
A simulator starts empty, deliberately. It holds no markets until you give it some, so every run begins from a state you chose. Naming a scenario is how you choose:
rystic run kalshi-twin --scenario tight_book # 1¢ spread, 25 deep a side, nothing moving
The 45 worlds are compiled into the binary — no data files, no network. GET /_rystic/scenarios lists them; SCENARIOS.md ships beside the simulator.
Simulator already running? Hand it a world instead of restarting. This resets state, applies the world, answers 204:
curl -X POST http://localhost:8080/_rystic/scenario \
-H 'content-type: application/json' -d '{"name":"tight_book"}'
Second terminal — the sample bot that ships with the simulator, 40 arbitrary orders:
python3 ~/.rystic/twins/kalshi-twin/0.0.25/random-walker.py
40 orders -> 201×20, 400×8, 409×12
the simulator coped: every order answered, account and book still read
The split into accepted, malformed and refused is the point — a simulator that took all 40 would not be modelling an exchange. Those counts are what a freshly seeded tight_book gives; re-POST the scenario to get them exactly again.
Skip the scenario and there is nothing to trade: the bot exits 1 and points at the replay routes. Replay needs a tape archive a fresh install does not have — seed a named world instead. Replay is the richer path once you have captured a market day; see Backtesting with batch.
Next
- The rystic CLI — every command, flag, exit code.
- Backtesting with batch — your bot against captured market days.
- Docker — the same simulator as a container; your license is the credential.
If it didn’t work
| you saw | it means | do this |
|---|---|---|
curl: … error: 403 (22, or 56 on macOS curl) | the install link’s l= value was cut short | paste the full link again, or use the license-file route |
command not found: rystic | the install folder isn’t on your PATH | open a new terminal; the installer printed the folder |
no license file found … (exit 3) | no license activated on this machine | rystic license activate ./rystic-license-acme-2026.json |
signature does not verify | the file was altered or re-encoded in transit | re-download it exactly as sent; ask for a fresh one |
license_expired | past its date | ask Rystic for a renewal |
asset_not_found … carries no build for this license | that version was never cut for you | ask Rystic, or rystic pull -version <x.y.z> one you have |
no markets in the twin's world … (exit 1) | the simulator serves, but nothing gave it a market | --scenario tight_book, or POST /_rystic/scenario |
| exit 8, runtime unavailable | Docker isn’t running (only with --runner docker) | start Docker, or drop the flag — native needs none |
Stuck anyway? Send us rystic doctor output — it names the broken piece without revealing your license.