Kalshi Risk and Loop Settings
This page covers the risk block and loop.interval of a Kalshi event-contract strategy (platform: kalshi): what each limit counts, when it resets, which strategy types accept it, and how it behaves in backtests. For every other key, see All Kalshi parameters.
Risk
The risk block holds your strategy-level limits. max_position and price_ceiling are required; every other key is optional and 0 means off. These limits sit on top of the deployment risk limits you confirm when you deploy. An order must pass both.
Some limits count per market, some per deployment, and one across your whole account:
| Limit | Scope | Resets on redeploy | Strategy types |
|---|---|---|---|
max_position | Per market (per bracket) | n/a | All |
price_floor, price_ceiling | Per order | n/a | All |
max_entries_per_market | Per market, per deployment | Yes | All |
max_notional | Per market, per deployment | Yes | Custom |
max_loss | Whole deployment, all markets | Yes | Custom |
max_portfolio_positions | Whole Kalshi account | n/a | All |
A deployment starts each time you deploy or update the Bot. A restart of the same deployment keeps its counters.
risk.max_position
Type: whole number of contracts, required, greater than 0. No upper bound.
The largest position the strategy should hold in one market (one bracket with selection: all).
- Built-in strategies size orders from it: spread_capture stops refreshing its quotes in a market once it holds this many contracts, mean_reversion buys min(max_position, 10), pre_announcement_drift buys min(max_position, 5), and observation_momentum sizes orders up to it. panic_fade sizes by
fade_size. - Custom strategies: a buy rule's
sizecan't exceed it (entry size exceeds risk.max_position from an empty position), and neither can the buy sizes of one compositeordersrule added together (composite buy orders together exceed risk.max_position from an empty position), because all of its legs rest at once.
While the Bot runs, every buy, built-in or custom, is refused if the contracts held plus resting in that market would pass the lower of max_position and the deployment's Max open contracts limit. Studio pre-fills Max open contracts from max_position. Raising Max open contracts above max_position doesn't let the Bot hold more; setting it lower makes it hold less. The refusal names the limit that bound: open contracts would be 3.0, exceeding risk.max_position=2. Sells are never capped.
Both counts include everything your account holds or has resting in that market, including positions from manual trades or other Bots.
Backtest: supported. Backtests block any buy that would push held plus resting contracts above max_position, for custom strategies too.
risk.price_floor
Type: dollars per contract. Default: 0. Allowed: 0 up to (not including) 1, and below price_ceiling.
Lower edge of the price band. What it does depends on the strategy type:
- Custom rules:
buy_yesandbuy_noplace a limit order at the current ask, clamped into the band. If the ask is below the floor, the limit is raised to the floor. That order can still fill at the cheaper ask, so the floor doesn't stop cheap buys. If you want to skip cheap entries, add a condition such asyes_best_ask >= 0.05to the rule. - Built-in strategies use it as an entry gate or a quote limit. For example, mean_reversion only buys YES above the floor, and panic_fade exits a fade when the price drops below it.
For buy_no the band applies to the NO price. Sells are never clamped.
With max_notional, every buy rule must satisfy size × price_floor ≤ max_notional (an omitted size counts as 1).
Backtest: supported, with one difference: depending on the replay path, an out-of-band buy is either clamped (as live) or skipped.
risk.price_ceiling
Type: dollars per contract, required. Allowed: above 0 up to 1, and above price_floor. Leaving it out fails with risk.price_ceiling: must be in (0, 1].
Upper edge of the price band. On custom rules, a buy whose ask is above the ceiling is not skipped: the Bot places a limit bid at the ceiling, and that bid rests on the book until it fills or is canceled. A resting entry like this counts toward max_entries_per_market and max_notional until it is canceled unfilled.
To avoid resting bids when the market is above your limit, add an explicit condition to the rule, such as yes_best_ask <= 0.60.
The price band clamps custom buys; it doesn't skip them
- Price band
- Current ask
- Limit price the Bot sends
- A · ask $0.40 → limit $0.40 Inside the band: fills at the ask.
- B · ask $0.72 → bid $0.60 rests Not skipped: the bid rests and counts toward max_entries_per_market and max_notional if set.
- C · ask $0.02 → limit $0.05 Can still fill at the cheaper $0.02.
price_floor: 0.05 to price_ceiling: 0.60. A buy_yes or buy_no rule sends a limit order at the current ask, moved into the band. For buy_no the band applies to the NO price. Sells are never clamped. To skip a buy instead, add a condition such as yes_best_ask <= 0.60.Backtest: supported, with the same clamp-or-skip difference as the floor.
risk.max_loss
Type: dollars. Default: 0 (off). Allowed: ≥ 0, and above 0 only on custom Kalshi strategies. A positive value on a built-in strategy fails with risk.max_loss: requires durable deployment-run attribution, currently supported only for Kalshi custom strategies; a negative value fails with must be >= 0.
A loss halt for the whole deployment, not a per-trade stop-loss. The Bot measures the deployment's P&L across every market it traded: money from sales, minus the cost of what it sold, minus fees, plus what it still holds valued at the current bid, minus what that cost. Only fills from this deployment's own orders count, plus any positions you explicitly adopt when you deploy.
When P&L reaches −max_loss:
- The Bot records the halt so it survives restarts.
- It cancels its resting orders and sells its inventory at the bid with immediate-or-cancel orders, retrying each cycle until the inventory is gone.
- It never opens a new position in that deployment again. Deploy or update the Bot to start a new deployment.
The halt also triggers if your account's position in a traded market stops matching what the deployment's own fills explain, or a resting order there isn't this Bot's (reason attribution_mismatch). Don't trade by hand, or run another Bot, in the same markets as a Bot with max_loss.
It fails closed: if a held side has no bid, P&L can't be measured and that market's cycle errors instead of trading. If the Bot can't save its risk state, it blocks new entries instead of trading without a reliable loss check (reason risk_state_unavailable).
max_loss is checked every cycle the Bot evaluates a market. It is not checked before active_window.start. After active_window.end it keeps running in every market the Bot still holds or has resting orders in (see Active window), and an edge data or derived outage doesn't stop it either.
Backtest: supported. The backtest applies the same calculation to simulated fills.
risk.max_entries_per_market
Type: whole number. Default: 0 (off).
The most buy orders the Bot may place in each market during one deployment. Before every buy, the Bot checks this deployment's entry orders in that market on the exchange:
- An order uses a slot if it has any fill, is still resting, or its final state is unknown.
- An order that ended canceled with no fill gives its slot back.
- When the slots are used up, the buy is skipped (
entry skipped: max entries per market reached). Sells are never limited.
At 1 the cap is exact, which makes it the simplest way to say "enter this market once". Above 1 it is conservative, because several partly filled orders each use a slot.
The count is per market (each new market of a series starts at 0) and per deployment (deploying or updating starts a fresh count; a restart does not).
Note: When the cap skips a built-in strategy's entry, the strategy doesn't advance: no cooldown starts, no fade is recorded, the momentum streak doesn't grow and drift isn't marked as entered. Built-in exits only sell what the Bot holds.
max_entries_per_marketis still not recommended withspread_capture, whose YES sell quotes keep going out once the cap is reached, and a YES sell while you hold no YES opens a NO position. Seerisk.max_entries_per_marketon Kalshi.
Backtest: supported. Blocked buys show the reason max_entries_per_market.
risk.max_notional
Type: dollars. Default: 0 (off). Allowed: ≥ 0, and above 0 only on custom strategies (risk.max_notional: is only supported for custom strategies).
A spend budget for entries in each market, not a limit on current exposure. The Bot adds up this deployment's entry orders in that market, each priced at its limit price: filled contracts plus contracts still open on orders that aren't finished. NO entries count at the NO price.
- A single buy is shrunk to fit: the Bot buys floor((max_notional − spent) ÷ limit price) contracts. If less than 1 contract fits, the buy is skipped (
max_notional budget exhausted). - A composite
ordersbatch is skipped entirely if it doesn't fit. It is never shrunk. - Selling does not give budget back. Once spent, the budget stays spent until you redeploy.
Because spend is counted at the limit price, a fill at a better price leaves some budget unused.
Every buy rule must be able to fit at least once at the floor price: size × price_floor must not exceed max_notional, or the save fails with entry cannot satisfy risk.max_notional within the configured price bounds. With size: 5 and price_floor: 0.05, 0.24 fails and 0.25 passes.
max_notional is a spend budget per market
- Buy 1
- Buy 2
- Buy 3 (shrunk)
- Buy 1: 2 contracts at $0.30Counted at the $0.30 limit price, filled or still resting$0.60 of $1.50
- Buy 2: 2 contracts at $0.30Spend adds up per market$1.20 of $1.50
- Buy 3: asks for 2, shrunk to 1floor(($1.50 − $1.20) ÷ $0.30) = 1$1.50 of $1.50
- Sell the whole positionNo refund: the budget stays spent$1.50 of $1.50
- Buy 4Skipped: all 3 entry slots are used (checked first), and the budget is spent tooskipped
max_notional: 1.50 and buy rules of size: 2 filling at $0.30. The budget is per market and per deployment; only a new deploy or update resets it. The example also sets max_entries_per_market: 3, which is checked before the budget, so Buy 4 is reported as an entry-cap skip.Note: Always set
sizeon buy rules when you usemax_notional. Live, an omitted size buys 1 contract, but series backtests can size an omitted-size buy to the whole remaining budget, so the backtest may not match what your Bot does.
Backtest: supported for ordinary (taker) backtests. Strategies that rest quotes with orders can't be backtested with max_notional set (risk.max_notional enforcement is not simulated for maker backtests yet).
risk.max_portfolio_positions
Type: whole number. Default: 0 (off).
Before every buy, the Bot builds the set of markets where your Kalshi account holds a position (from any source, including manual trades and other Bots) plus markets where this Bot has a resting order. If the buy is in a market that isn't in the set yet and the set already has this many markets, the buy is blocked. Adding to a market already in the set is allowed, and sells are never blocked.
Because it counts your whole account, positions you hold elsewhere use up slots. Besides max_loss, it is the only risk limit shared across brackets with selection: all.
Backtest: supported, but the backtest only counts this strategy's simulated positions, not your real account.
Loop interval
loop.interval is the number of seconds the Bot waits after finishing one evaluation cycle before starting the next. It is required, must be a whole number of at least 10 (must be >= 10 seconds (prevents API rate limiting)), and has no maximum or default. Studio's AI usually suggests 30.
It is not a fixed clock. The Bot runs a full cycle (find markets, read books, check rules, place orders) and then sleeps. The real gap between cycles is cycle time plus interval, and it grows with many brackets, slow responses or a series rollover wait.
active_window boundaries are checked at the start of a cycle, so they can be seen up to one interval plus the cycle time late. A trading_schedule has its own once-a-minute timer and doesn't depend on the interval.
Backtest: decisions happen every loop.interval seconds or at the resolution of the historical data, whichever is longer. With sub-minute Kalshi data, intervals of 60 s or less must be a multiple of 5.