Built-in Strategies
A built-in strategy is a ready-made trading template. You pick one with strategy: and tune it with a flat params: map. You don't write rules. Studio's AI writes this YAML for you; this section explains every setting it can use, so you can read the result, check it, and change it with confidence.
This page covers what all five strategies share: how they run, their order sizes, the rules for writing params, how they differ from custom, backtests and Deep Research ranges. Each strategy has its own page for its parameters, and the risk, loop and other blocks they combine with have a page of their own. All six pages are listed under In this section.
There are five built-in strategies:
| Strategy | In one line |
|---|---|
spread_capture | Quote a YES bid and a YES ask around the mid when the spread is wide enough. |
mean_reversion | Buy YES when YES is cheap or NO when YES is expensive, then exit near a target price. |
panic_fade | Buy YES after a sharp drop from the recent 5-minute high, then exit on a small recovery. |
observation_momentum | Buy in the direction of a recent price move, with a size that grows while the move continues. |
pre_announcement_drift | Enter once in a window before the market closes, and optionally exit just before close. |
The sixth strategy: value, custom, replaces params with your own rules. See How custom differs.
Note: Turbine is not a financial advisor. Every number in this section is an illustrative example of how a setting behaves, not a suggested threshold, size, or market. Nothing here implies that a strategy will make money. Backtest and paper trade before you deploy, and start small. See Risk & Limits.
In this section
- Built-in Strategy Risk, Loop and Other Blocks: How
riskandloopapply to built-in strategies, Kalshi's entry and portfolio caps and runtime checks, deployment risk limits, why a Kalshi Bot didn't trade, and which other blocks, such astrading_schedule, each venue accepts. - spread_capture: How the quote ladder works, its parameters, venue differences, backtests and an example.
- mean_reversion: Entry bands, the exit target band and cooldown, venue differences, backtests and examples.
- panic_fade: How it fades sharp drops, its parameters, one fade at a time, venue differences, backtests and examples.
- observation_momentum: The momentum signal, the sizing staircase, its parameters, venue differences, backtests and an example. On Polymarket and Polymarket US it sells on a reversal; on Kalshi it has no exit.
- pre_announcement_drift: The entry window, blackout and release exit, its parameters, venue differences, backtests and examples.
How built-in strategies run
Every loop.interval seconds the Bot finds its market or markets and runs the strategy's fixed logic once per market.
Every threshold is a YES price in dollars, from 0 to 1. 0.08 means 8 cents. This includes the thresholds that trigger NO buys: entry_high: 0.85 means "buy NO when YES is at 0.85 or higher".
Spreads, drops, gains and moves are worked out by subtracting prices, and the result can land a hair off the exact value. So a value exactly equal to a threshold may not count. A value one cent past the threshold always counts. For example, with recovery_exit: 0.05 a fade bought at 0.39 did not exit at 0.44, and did exit at 0.45.
The template decides a few things for you. They are not configurable:
- panic_fade always looks back over a 5-minute window.
- mean_reversion always exits inside a band of ±0.02 around its target.
- Order sizes follow fixed formulas, shown in Order sizes.
- The price the strategy reads, and the order types it sends, depend on the venue:
| Venue | Price the strategy reads | Entries | Exits |
|---|---|---|---|
| Kalshi | Last trade YES price, or the order-book mid if nothing has traded. observation_momentum reads the last N trades instead. | Limit order at the current price | Reduce-only, immediate-or-cancel limit at the best bid of the side held |
| Polymarket | YES token book mid, then the ask, the bid, then the last trade | Limit order at the bought token's book mid | Limit order at the best bid |
| Polymarket US | Last trade price, then the current price, then the book mid | Limit order at the current price | Market order |
spread_capture quotes are resting limit orders on every venue.
The other four strategies share these rules on every venue:
An entry counts once the venue accepts it. A skipped order leaves the strategy as it was: no cooldown starts, no fade is recorded, the momentum streak doesn't grow and drift doesn't count as entered. The strategy signals again on a later loop.
An unfilled entry is cancelled after two loops (at least 60 seconds). While it rests, the strategy places no other entry. A part that filled is kept and managed like any position.
Exits sell the side that is held, for what was bought. On Kalshi the Bot works out its own position from its own fills in this deployment, never more than your account holds. On Polymarket and Polymarket US, panic_fade and pre_announcement_drift sell what their own entry bought, and mean_reversion and observation_momentum sell what the account holds in the market.
No entries on top of a position the strategy isn't managing. If the account holds contracts in the market that the strategy didn't buy (a manual trade, another Bot's, or a position this Bot opened before a redeploy), it doesn't enter there until they're closed, and it never sells them. This applies to every strategy on Kalshi, and to panic_fade and pre_announcement_drift on Polymarket and Polymarket US. On those two venues mean_reversion and observation_momentum treat everything held in the market as their own position instead: they don't enter while it's held (momentum adds only up to
max_position), and their exits sell it.
Venue details: Kalshi, Polymarket, Polymarket US. For how a strategy document fits together, see the Strategy Reference overview. For how the price bounds, max_position and loop.interval affect each strategy, see How risk and loop apply.
Order sizes
There is no size parameter except fade_size. Sizes come from these formulas:
| Order | Kalshi | Polymarket | Polymarket US |
|---|---|---|---|
spread_capture quote | 1 contract | The market's minimum order size (often 5 shares) | 1 contract |
mean_reversion entry | min(max_position, 10) | The market minimum | min(max_position, 10) |
mean_reversion exit | Everything this Bot holds, on the side it holds | Every YES and NO share held | Your whole held position |
panic_fade entry | fade_size | fade_size, raised to the market minimum | fade_size |
panic_fade exit | The YES the fade bought | The YES the fade bought | The YES the fade bought |
observation_momentum entry | 1, 2, 3 … or 1, 2, 4 …, each capped at max_position | Same, raised to the market minimum and cut to what fits under max_position | Same, cut to what fits under max_position |
observation_momentum exit | None | Everything held on the other side, when the move reverses | Same as Polymarket |
pre_announcement_drift entry | min(max_position, 5) | min(max_position, 5), raised to the market minimum | min(max_position, 5) |
pre_announcement_drift release exit | What the entry bought, on its side | Same | Same |
On regular Polymarket an order below the market's minimum order size (often 5 shares) is raised to the minimum, but never past max_position: if the minimum doesn't fit, the entry is skipped and the Bot logs an entry_size blocked decision. panic_fade is the exception to the max_position check: a fade_size at or above the minimum is sent as is.
params.position_size is not a setting. It saves without an error and does nothing. To make a mean_reversion or drift entry smaller, lower risk.max_position.
Markets and memory
Kalshi. One Bot can run several markets. With market.series_ticker and market.selection: all it trades every bracket of the current event, and it keeps separate memory and a separate max_position for each one. New entries only go to the series' current market. Exits and quote cancels keep running on older markets you still hold. Deployment risk limits such as Max open contracts and Max daily notional traded are also checked per market, so total exposure grows with the number of brackets.
Polymarket and Polymarket US. One Bot trades one market and keeps one memory. On a rolling Polymarket series (market.series_slug or market.recurring) the Bot moves to the next window automatically and starts it with fresh memory: price history, the fade count, the momentum samples, the drift entry and the cooldown all reset. Shares still held in the old window settle with it.
On every venue, a strategy's memory (cooldown clocks, price windows, fade counts, streaks, entered flags) lives in the running Bot. A restart or redeploy clears it. On Kalshi a restart doesn't lose track of what the Bot holds, because its position comes from its own fills in the deployment: after a restart it still exits them. A redeploy or update starts a new deployment: the Bot doesn't manage positions the previous deployment bought, and doesn't enter that market while one is still open. Close those positions yourself.
Note: On Polymarket US, every built-in exit is a market order, split into orders that each fit Max contracts per order. In paper trading a market order fills against the whole visible book at once or is rejected; it never rests.
The params block
params is a flat map of the settings for your strategy. Each strategy's page, listed under In this section, lists every key it reads. A few rules apply to all of them:
- Only the listed keys are read. Any other key is ignored without a warning. Switching
strategydoes not translate your params, because each strategy reads different keys. - Numbers must be plain numbers.
entry_low: "0.15"orcooldown: "60"(quoted) is text and fails validation, required or optional. Whole-number keys accept3or3.0and reject2.5. - Write booleans as bare
trueorfalse."false"in quotes,noandofffail withmust be true or false without quotes. - Text options must be one of the listed values.
position_scaletakeslinearorexponential, anddrift_directiontakesauto,bullishorbearish. They are case-sensitive, soExponentialfails withmust be one of linear, exponential. - A key set to null (
~) counts as omitted. Optional keys fall back to their default; required keys fail validation. .nanand.infare rejected in every key, withmust be finite.
The strategy name itself is not case-sensitive: Mean_Reversion works.
# Rejected: entry_low is quoted, so it is text rather than a number
# error: mean_reversion requires params.entry_low and params.entry_high
version: 1
platform: kalshi
strategy: mean_reversion
market:
series_ticker: "KXBTC15M"
risk:
max_position: 1
price_floor: 0.05
price_ceiling: 0.95
loop:
interval: 30
params:
entry_low: "0.15"
entry_high: 0.85The error reads as if the key were missing, because a quoted number doesn't count as a number.
A quoted boolean is rejected too. Before this check, "false" in quotes switched post-only on:
# Rejected: post_only must be a bare boolean; the quoted "false" is text
# error: params.post_only: must be true or false without quotes
version: 1
platform: kalshi
strategy: spread_capture
market:
series_ticker: "KXBTC15M"
risk:
max_position: 3
price_floor: 0.05
price_ceiling: 0.95
loop:
interval: 30
params:
spread_floor: 0.04
order_count: 2
post_only: "false"How custom differs
With strategy: custom you write the logic yourself as rules: conditions over fields such as price, spread and position, with actions such as buy_yes and sell_all. params is ignored. Custom strategies can also use settings that built-in strategies can't, such as active_window, risk.max_notional, risk.max_loss, derived values, edge data and exit targets attached to entry rules. Venue support differs for each.
Built-in strategies are quicker to set up and read no external data. Custom rules take more work to write but let you express entries, exits and sizes exactly. See Custom Rules and Edge Data.
Backtests
| Strategy | Kalshi | Polymarket | Polymarket US |
|---|---|---|---|
spread_capture | Yes (resting-order replay) | No | No |
mean_reversion | Yes | Yes | No |
panic_fade | Yes | Yes | No |
observation_momentum | Yes | Runs, but records no trades | No |
pre_announcement_drift | Yes | Yes | No |
The backtest steps through historical bars: 1-minute candles, or order-book samples every loop.interval seconds. trading_schedule is honored. Where the backtest and the live Bot differ:
| Behavior | Live Bot | Backtest |
|---|---|---|
cooldown omitted | 60 seconds | 0 seconds |
recovery_exit omitted | 0.04 | recovery_target, default 0.50 |
recovery_exit: 0 | Exits once the price is back at the fade price | Treated as omitted |
post_only omitted (spread_capture) | Off | Always post-only |
| Entry fills | Limit order at the current price | Taker fill at the next bar's ask |
| Unfilled entry | Cancelled after two loops | Fills at the next bar, or not at all |
max_position for panic_fade, and observation_momentum on Kalshi | Not a holdings cap | Caps holdings on each side |
| Polymarket mean_reversion size | The market minimum | min(max_position, 10) |
| Deployment risk limits | Enforced on Kalshi and Polymarket US | Not simulated |
Set cooldown and recovery_exit explicitly, as unquoted numbers, so both runs use the same values. See Backtest and Backtest data.
Each strategy's page has a backtests section with the details for that strategy: spread_capture, mean_reversion, panic_fade, observation_momentum and pre_announcement_drift.
Typical research ranges
Deep Research runs are Kalshi-only. Unless a research plan proposes something else, Deep Research builds 100 variants around your own values. These are the ranges it explores by default, not recommended settings.
| Strategy | Varied by default | How the values are built | Example |
|---|---|---|---|
spread_capture | spread_floor, price_floor, price_ceiling | Your value × 0.5, 1, 1.5, 2 and 3 | spread_floor 0.02 → 0.01, 0.02, 0.03, 0.04, 0.06 |
mean_reversion | entry_low, entry_high, price_floor | 5 evenly spaced values around each band edge, kept inside 0.01 to 0.99; price_ceiling stays fixed | 0.15 and 0.85 → lows 0.01, 0.08, 0.15, 0.22, 0.29 and highs 0.71, 0.78, 0.85, 0.92, 0.99 |
panic_fade | panic_threshold, price_floor, price_ceiling | Your value × 0.5, 1, 1.5, 2 and 3 | 0.08 → 0.04, 0.08, 0.12, 0.16, 0.24 |
observation_momentum | momentum_threshold, price_floor, price_ceiling | Your value × 0.5, 1, 1.5, 2 and 3 | 0.03 → 0.015, 0.03, 0.045, 0.06, 0.09 |
pre_announcement_drift | entry_hours_before, blackout_minutes_before, price_floor | Whole numbers, 2 steps either side of your value; each step is a quarter of your value, rounded down, at least 1 | 6 → 4 to 8; 15 → 9, 12, 15, 18, 21 |
price_floor takes 4 values and price_ceiling 5, spaced around your own values: a floor of 0.05 becomes 0.01, 0.03, 0.05, 0.07 and a ceiling of 0.95 becomes 0.91, 0.93, 0.95, 0.97, 0.99. If your values leave no room for these grids, Deep Research falls back to fewer axes. For mean_reversion and pre_announcement_drift it first varies just one signal param (entry_low or entry_hours_before) with both price bounds. As a last resort it varies only the price bounds, on a fixed grid of 10 floors from 0.05 to 0.45 and 10 ceilings from 0.55 to 0.95, not built around your values.
Sizing and timing params (order_count, fade_size, cooldown, lookback_periods) stay fixed by default. A proposed research plan can vary any numeric params key that is written in your strategy, so write out an optional param if you want it included.