# Live feed settings and Compose

Every live-feed flag and its environment variable, and the live feed from a stack file or Compose, with your own key and a ticker or event list shared with your bot.

Every live-feed flag has an environment variable, so a container or orchestrator sets the whole [live feed](https://www.rystic.ai/docs/simulators/kalshi/features/live-feed.md) with no argv. Boot a detached instance in Docker, feed it and read its feeder's log:

<!-- example: skip network -->
```bash
rystic run kalshi-twin --live --runner docker -d --name live
rystic feed kalshi-twin --instance live
rystic logs --feed live
```

`--runner docker` runs the container with a feeder on this host beside it, and `rystic feed --instance` feeds a detached instance by name.

### Flags and environment

| Flag | Env | Default | Meaning |
|---|---|---|---|
| `--live` | `RYSTIC_LIVE=1` | off | feed the simulator from the exchange |
| `--live-carry N` | `RYSTIC_LIVE_CARRY` | `0` | pre-carry the busiest N markets; `0` carries nothing until the state UI picks; ignored when markets or events are named |
| `--live-markets a,b` | `RYSTIC_LIVE_MARKETS` | none | carry exactly these markets before anything is picked; the state UI can change the list later |
| `--live-markets-file path` | `RYSTIC_LIVE_MARKETS_FILE` | none | read the startup watch list from a shared file; an alternative to `--live-markets` |
| `--live-events a,b` | `RYSTIC_LIVE_EVENTS` | none | carry every open market of these events, looked up again on every rescan; adds to `--live-markets` |
| `--live-events-file path` | `RYSTIC_LIVE_EVENTS_FILE` | none | read the startup event list from a shared file; an alternative to `--live-events` |
| `--live-series a,b` | `RYSTIC_LIVE_SERIES` | `*` | series to carry; `*` offers every open market on the exchange |
| `--live-max-markets N` | `RYSTIC_LIVE_MAX_MARKETS` | `500` | how many of the busiest open markets the state UI offers |
| `--live-shards N` | `RYSTIC_LIVE_SHARDS` | `3` | exchange sockets the carried markets spread over, at most 5 |
| `--live-env` | `RYSTIC_LIVE_ENV` | the simulator's default (`prod`) | exchange environment; `demo` needs your own demo key |

Flag beats environment beats default. Every `--live-x-y` reads `RYSTIC_LIVE_X_Y`. A `--live-*` flag or `live_*` stack key without live mode is refused. So is a `RYSTIC_LIVE_*` variable, unless the simulator declares no live feed. `--live=false` (`RYSTIC_LIVE=0`, `live: false`) turns live off on purpose and ignores them.

A `RYSTIC_LIVE_*` name that is no live setting is refused, naming the setting it's closest to. `RYSTIC_LIVE_EVENT_FILE`, for example, gets "did you mean `RYSTIC_LIVE_EVENTS_FILE`?". `run`, `up` and `feed` warn about any variable a typo away from a Rystic one.

1. The exchange socket lives in the CLI process, under `--runner docker` too, where the feeder runs on the host beside the container.
2. `--live-shards` plus the sockets of any recorder running on the same key must stay at or under Kalshi's limit of five.
3. `stop` ends the feeder first, then the simulator. `restart` re-runs the recorded spec, feed included.

### In a stack file

```yaml
services:
  c: {product: kalshi-twin, port: 0, live: true, live_markets: "KXBTC15M-26SEP1712-T1, KXBTC15M-26SEP1712-T2"}
```

`live: true` takes every `--live-*` default, environment included. Each `--live-*` flag in the table above has a stack key, its name with underscores for dashes, such as `live_markets` for `--live-markets`. An explicit empty string clears an inherited list. `tapes`, `tape` and `copies` are the replay keys. Compose export preserves explicit live settings and mounts `live_markets_file` and `live_events_file`.

### In Compose

Keep the default `rystic run` command and the credential the export wrote, and add variables under the service's `environment`. Quote numeric and boolean values:

```yaml
environment:
  RYSTIC_TOKEN: ${RYSTIC_TOKEN}
  RYSTIC_LIVE: "1"
  RYSTIC_LIVE_SERIES: KXBTC15M
  RYSTIC_LIVE_SHARDS: "2"
  RYSTIC_LIVE_CARRY: "1"
```

The CLI and its feeder run inside this container, so no host feeder or Docker socket mount is needed. `command: ["rystic", "run", "--live=false"]` overrides `RYSTIC_LIVE: "1"` for a replay or scenario run. The health check confirms the HTTP server is available. For live data, wait for the selected markets and their books as well, since discovery and exchange subscription happen after HTTP startup. `RYSTIC_LIVE_SHARDS` is capped at 5, because Kalshi permits at most 5 sockets per key across all clients.

To use your own key, put `key_id` (the key id, one line) and `kalshi.pem` next to `compose.yaml` and add Compose secrets, with no key variables needed. A `token` file holding your `rys_…` API key, as the `rystic_token` secret, replaces `RYSTIC_TOKEN` the same way. So a shared `compose.yaml` holds no per-person value:

```yaml
services:
  kalshi-twin:
    secrets: [rystic_token, kalshi_key_id, kalshi_private_key]
secrets:
  rystic_token: {file: ./token}
  kalshi_key_id: {file: ./key_id}
  kalshi_private_key: {file: ./kalshi.pem}
```

`rystic doctor kalshi-twin` then reports `live feed … (from /run/secrets)`. The container runs as uid 10001. Compose mounts a file secret with the host file's owner and mode, and ignores `uid` and `mode` on one. So a `0600` file fails with `permission denied` and the uid that needs it. On a Linux host, grant that uid read first, rather than making the key readable by every user:

- `setfacl -m u:10001:r token key_id kalshi.pem`, from the `acl` package
- or `sudo chown 10001 token key_id kalshi.pem`

`KALSHI_KEY_ID` holds the id itself, and a path there is refused and pointed at `KALSHI_KEY_ID_FILE`. `RYSTIC_TOKEN_FILE`, `KALSHI_KEY_ID_FILE` and `KALSHI_PRIVATE_KEY_FILE` point at files anywhere else in the container. `KALSHI_PRIVATE_KEY_B64` carries the PEM inline.

### Share a ticker file with your bot

Create `tickers.txt` next to `compose.yaml`, and put in it the exact open market tickers your bot uses, one per line or comma-separated. Blank lines, duplicates and `#` comments are accepted. Add these settings to the simulator service:

```yaml
environment:
  RYSTIC_TOKEN: ${RYSTIC_TOKEN}
  RYSTIC_LIVE: "1"
  RYSTIC_LIVE_MARKETS_FILE: /config/tickers.txt
volumes:
  - rystic-leases:/var/lib/rystic/leases
  - ./tickers.txt:/config/tickers.txt:ro
```

Mount the same host file into your bot service, and configure the bot to read `/config/tickers.txt` with its own file option. Rystic doesn't prescribe the bot's flag or variable name, and both containers read the same list without a shell wrapper:

```yaml
volumes:
  - ./tickers.txt:/config/tickers.txt:ro
```

The equivalent explicit command is `command: ["rystic", "run", "--live", "--live-markets-file", "/config/tickers.txt"]`. Use either `RYSTIC_LIVE_MARKETS` or the file, not both. Rystic reads the file once at startup and doesn't watch it, and the state UI can change the watch list afterward. After editing the file, recreate the services so both processes and bind mounts read the new contents:

<!-- example: skip network -->
```bash
docker compose up -d --force-recreate
```

A CLI `rystic restart` reuses the resolved startup list recorded for that instance. Explicit tickers survive activity and offer-size filters, but must still be open and within `RYSTIC_LIVE_SERIES`. The feeder log names tickers that aren't available.

### Share an events file with your bot

If your bot trades by event, share event tickers instead of market tickers. Put them in `events.txt`, in the same format as `tickers.txt`, and set `RYSTIC_LIVE_EVENTS_FILE` (or pass `--live-events-file`):

```yaml
environment:
  RYSTIC_LIVE: "1"
  RYSTIC_LIVE_EVENTS_FILE: /config/events.txt
volumes:
  - ./events.txt:/config/events.txt:ro
```

1. The feeder carries every open market of each named event. It looks each event up again on every rescan, so a strike the exchange adds later is carried from the next scan, and a market that closes is dropped.
2. Name event tickers (`KXBTCD-26OCT0617`), not series (`KXBTCD`) or market tickers. The feeder log names a series or an open market listed as an event, with the flag that carries it.
3. An event with no open markets carries nothing and is named once in the feeder log. Name an upcoming event ahead of time, and its markets are carried once they open.
4. Events add to `RYSTIC_LIVE_MARKETS` or its file, so set both to carry named markets as well. `RYSTIC_LIVE_EVENTS` and the events file are alternatives, like their market equivalents.
5. `--live-series` doesn't narrow named events: each one is looked up directly.
6. Like the markets file, the events file is read once at startup, and a CLI `rystic restart` reuses the resolved event list.

---

Source: https://www.rystic.ai/docs/simulators/kalshi/features/live-feed-settings · Markdown: https://www.rystic.ai/docs/simulators/kalshi/features/live-feed-settings.md
