Kalshi Market Selection
The market block tells a Kalshi event-contract strategy (platform: kalshi) which markets to trade. This page covers each selector, how it resolves live and in backtests, how selection picks brackets on a rolling series, and what happens when more than one selector is set. For every other key, see All Kalshi parameters.
A Kalshi strategy needs at least one market selector. Pick exactly one of these four:
| Selector | Resolves to | Rolls to new markets | Backtest |
|---|---|---|---|
ticker | That one market | No | Yes |
event_ticker | The first open market Kalshi lists for the event, re-checked every cycle | Within the event only | Yes, against past events of the same series (most liquid bracket of each) |
series_ticker | The series' current event: one bracket, or every bracket with selection: all, plus series markets you still hold | Yes, automatically | Yes |
query | The first open event whose title contains your text, then its first open market | Can change between cycles | No |
An empty value counts as not set. Polymarket selectors (slug, condition_id, neg_risk and the rest) are rejected on Kalshi, even neg_risk: false.
market.ticker
Pins the strategy to exactly one market, for example a single game or a specific strike. The Bot never moves to another market. Once the market closes, every order attempt is refused because the market is no longer open, so the Bot stops trading on its own.
Don't use a ticker for recurring markets such as 15-minute or hourly crypto: the strategy stops as soon as that one market closes. Use series_ticker instead.
Backtest: supported. The backtest replays that market's own history, so it fails for a market that has no history yet (typical for a future one-off event).
market.event_ticker
Each cycle, the Bot asks Kalshi for the event's open markets and trades the first one in Kalshi's list. It doesn't rank by liquidity or volume, and it trades only one market of the event. If Kalshi's list order changes, the traded market can change between cycles. If the event has no open markets, that cycle errors with No open markets for event ....
Use it for events that have a single market. For a multi-outcome event, pin the ticker of the outcome you want.
Backtest: supported with a twist. The backtest always replays past events from the event's series, not the event you named, and in each one it trades the most liquid bracket (as a series_ticker backtest does). Live, the Bot trades the first open market Kalshi lists for your event. Read the result as a check against similar past events.
market.series_ticker
Follows a recurring series (for example KXBTC15M, KXBTC or KXHIGHNY) and always trades its current event. Every cycle the Bot:
- Lists the series' open events.
- Takes the markets that close soonest. Together these are the current event's brackets.
- Picks brackets to trade with
selection. - Also evaluates every other market of the same series where your account still holds a position or this Bot has a resting order. In markets from an older event, exit rules run but new entries are blocked (the log says the market is retained for management only). Brackets of the current event stay open for entries.
Rollover gaps. Kalshi lists the next market a few seconds after the previous one closes. When no current market is listed, the Bot checks every 5 seconds and waits up to 360 s for 5-minute series, 960 s for 15-minute series, 1800 s for hourly, daily and weekly series, and 180 s when the frequency is unknown. After that the cycle errors with No open markets in series ... and tries again next cycle. A long wait happens inside the cycle, so that cycle takes longer than loop.interval.
Note: If Kalshi labels a series' events with a cadence but doesn't report the series' frequency, the Bot can't tell which events are current and trades nothing; each cycle ends with
No open markets in series .... If you see that message on every cycle, not just around a rollover, this series can't be traded withseries_ticker; stop the Bot and pin atickerorevent_tickerinstead.
Per-market limits apply to each market of the series. max_entries_per_market: 1 on a 15-minute series means one entry per 15-minute market, which can be up to 96 a day, not one in total. On a series with several brackets per event it can be more, because the Bot can end up entering more than one bracket (see selection).
Exit rules on those older markets sell only this Bot's attributed inventory, including positions explicitly adopted at deployment, whether or not a trading_schedule is present. Manual trades and other Bots' positions are excluded (see Selling).
Backtest: supported. The series' events in the backtest window are replayed, and selection is honored.
market.selection
Only valid with series_ticker, and only when no other selector is set. Anything else fails with market.selection: is supported only with a Kalshi market.series_ticker selector.
| Value | What the Bot trades |
|---|---|
most_liquid (default) | One bracket of the current event: brackets with a real two-sided quote first, then higher volume. If the top bracket shows no quote, the Bot checks up to 8 live order books and takes the first with both sides. The pick is re-made every cycle. A bracket you still hold keeps being evaluated, and entry rules can fire there too, so the Bot can hold more than one bracket if the most liquid one changes. |
all | Every bracket of the current event, one after another within each cycle. Each bracket gets its own rule evaluation and its own per-market limits. |
With all, per-market limits are not divided across brackets. Six brackets with max_notional: 0.60 can spend up to 6 × $0.60 = $3.60. Only max_loss (whole deployment) and max_portfolio_positions (whole account) are shared.
One bracket or every bracket, and where each limit applies
selection: most_liquid (default)
- 61° or belowNot evaluated
- 62° to 63°Not evaluated
- 64° to 65°Rules run
- 66° to 67°Not evaluated
- 68° to 69°Not evaluated
- 70° or aboveNot evaluated
One bracket is picked each cycle: a two-sided quote first, then the highest volume. The pick can change between cycles, and a bracket you still hold keeps running its rules, entries included. Each bracket the Bot enters has its own $0.60 budget.
selection: all
- 61° or belowRules run
- 62° to 63°Rules run
- 64° to 65°Rules run
- 66° to 67°Rules run
- 68° to 69°Rules run
- 70° or aboveRules run
Every bracket, one after another in each cycle, each with its own limits. Budgets add up instead of being split (6 × $0.60 = $3.60). In this example the 1-entry cap binds first (2 contracts at ≤ $0.12 = ≤ $0.24 per bracket), and max_portfolio_positions: 4 blocks a buy in a 5th market while 4 are active.
- Whole Kalshi account
- Whole deployment
- Each bracket
max_portfolio_positions: 4Counts every market with a position in your account, from any source, plus markets with this Bot's resting orders.max_loss: 3.00One P&L ledger shared by every bracket.- 61° or below1 entry · $0.60 budget · 2 open contracts
- 62° to 63°1 entry · $0.60 budget · 2 open contracts
- 64° to 65°1 entry · $0.60 budget · 2 open contracts
- 66° to 67°1 entry · $0.60 budget · 2 open contracts
- 68° to 69°1 entry · $0.60 budget · 2 open contracts
- 70° or above1 entry · $0.60 budget · 2 open contracts
max_entries_per_market: 1, max_notional: 0.60 and a Max open contracts limit of 2. Bracket names are illustrative. Per-bracket limits are never divided across brackets, so exposure grows with the bracket count.# Rejected: selection only works with series_ticker, not with a pinned ticker
# error: market.selection: is supported only with a Kalshi market.series_ticker selector
version: 1
platform: kalshi
strategy: custom
market:
ticker: KXHIGHNY-26OCT15-B65.5
selection: all
risk:
max_position: 2
price_ceiling: 0.30
loop:
interval: 60
rules:
- name: take_profit
when:
all:
- {field: yes_position_size, op: ">", value: 0}
- {field: yes_best_bid, op: ">=", value: 0.45}
action: sell_yes
- name: buy_cheap
when:
all:
- {field: position_size, op: "==", value: 0}
- {field: yes_best_ask, op: "<=", value: 0.12}
action: buy_yesmarket.query
Searches Kalshi at run time. Each cycle the Bot reads one page of open events, takes the first whose title contains your text (ignoring case), and trades that event's first open market. If nothing matches, the cycle errors with No markets matching query ....
The match is fragile and can change from one cycle to the next. Before you deploy, ask Studio to resolve the query into a ticker, event_ticker or series_ticker.
Backtest: not supported. A backtest refuses a query selector.
Selector precedence
Studio accepts more than one selector in a document, but live trading and backtests read them in a different order:
- Live:
series_tickerwins whenever it is set. Without a series:ticker, thenevent_ticker, thenquery. - Backtest:
ticker, thenevent_ticker, thenseries_ticker.
With two selectors, the backtest can test a different market from the one your Bot trades. Set exactly one.