observation_momentum
This page covers observation_momentum, one of the five built-in strategies, on Kalshi, Polymarket and Polymarket US: its momentum signal, the sizing staircase, each parameter, venue differences, backtests and an example. Rules all five share, such as order sizes and how to write params, are on the Built-in Strategies page. Every number here is an illustrative example, not a suggested setting.
observation_momentum buys in the direction of a recent price move. Each loop, for each market:
- It compares the newest and oldest of the last
lookback_periodsprices. The difference is the move. - If the move is smaller than
momentum_thresholdeither way, it resets its streak to 0 and does nothing. - Otherwise it buys YES on an up move or NO on a down move, provided
price_floor< price <price_ceiling(and on Kalshi, it is the series' current market). - The order size comes from
position_scaleand the streak of consecutive signals.
On Kalshi there is no exit: positions are held to settlement, though a buy on the other side nets against what you hold. On Polymarket and Polymarket US the strategy manages its position:
- When a qualifying move goes against the side you hold, it sells that side instead of buying, and the streak restarts.
- It stops buying once holdings reach
max_position, and cuts each order to what still fits. - It places no new order while one is resting. An unfilled buy is cancelled after two loops.
observation_momentum: the signal, then the size staircase
The signal (Kalshi: the market's last 5 trades)
On Polymarket and Polymarket US the five prices are the Bot's own loop samples instead of trades. On regular Polymarket each order is raised to the market's minimum order size (often 5 shares), so the staircase starts at that minimum.
Order size on consecutive qualifying loops
- linear: 1, 2, 3, …
- exponential: 1, 2, 4, …
Total held after each loop on Kalshi (no exit, no holdings cap; on Polymarket and Polymarket US holdings stop at max_position)
| Scale | L1 | L2 | L3 | L4 | L5 | L6 |
|---|---|---|---|---|---|---|
| linear | 1 | 3 | 6 | 10 | 10 | 11 |
| exponential | 1 | 3 | 7 | 12 | 12 | 13 |
lookback_periods: 5, momentum_threshold: 0.03, max_position: 5. At loop 4, exponential would be 8 and is capped at 5. The bars and holdings rows assume every order is accepted and fills. Studio pre-fills Max contracts per order at max_position, so each order fits, but on Kalshi and Polymarket US a buy is refused once held plus resting contracts would pass max_position: at loop 3 the size-3 (or size-4) order is refused, the streak doesn't advance, and the Bot retries that size on every qualifying loop until momentum stalls.| Parameter | Type | Default | Allowed values | What it does |
|---|---|---|---|---|
lookback_periods | whole number | Required | ≥ 2 | How many recent prices to compare, newest against oldest. |
momentum_threshold | number (YES dollars) | Required | > 0 | Smallest move, up or down, that counts as momentum. |
position_scale | text | linear | linear, exponential | How order size grows over consecutive signals. |
lookback_periods
The same number means different time spans on different venues:
- Kalshi: the market's last N trades, fetched every loop. The trade timestamps set the window, not
loop.interval. It needs at least 2 trades. - Polymarket and Polymarket US: the Bot's own last N loop samples, one per
loop.interval, so the window is about (N − 1) ×loop.interval. It starts comparing as soon as 2 samples exist. The samples reset on restart and at each new window of a rolling Polymarket series.
lookback_periods never controls order size.
momentum_threshold
The move is in absolute dollars, not a percentage. Kalshi prices move in whole cents, so a threshold of 0.01 or less fires on almost any change.
On Kalshi, if no new trade prints, the last N trades don't change, so the move stays above the threshold and the Bot buys again on every loop with a growing size. It stops when a new trade moves the price back, or when the 120-second fresh-trade check starts refusing orders.
position_scale
For the n-th consecutive signal, counting from 0:
linear: min(max_position, n + 1), so 1, 2, 3, …exponential: min(max_position, 2^n), so 1, 2, 4, 8, …
An omitted key means linear. Any value other than linear or exponential, including Exponential, is rejected. There is no fixed-size option; max_position: 1 is the only way to get 1-contract orders.
The streak advances after an order is accepted and resets when the move drops below momentum_threshold, or after a reversal sale on Polymarket and Polymarket US. A refused or skipped order doesn't advance it. Studio pre-fills Max contracts per order at max_position, the largest order the streak can reach (see Deployment risk limits).
Warning: On Kalshi the strategy has no exit and keeps buying on every qualifying loop until the open-contract cap, the lower of
max_positionand Max open contracts, refuses further buys. On Polymarket and Polymarket US it stops atmax_positionand sells on a reversal.
observation_momentum by venue
| Kalshi | Polymarket | Polymarket US | |
|---|---|---|---|
lookback_periods counts | Trades | Loop samples | Loop samples |
| Entry order | Limit at the last trade price (NO: 1 − price) | Limit at the bought token's book mid, at least the market minimum | Limit at the current price (NO: 1 − price) |
| Total holdings bounded by | The lower of max_position and Max open contracts | max_position | The lower of max_position and Max open contracts |
| Exit | None | Sells the held token at its bid when the move reverses | Market sells of the held side when the move reverses, split to fit Max contracts per order |
| Backtest | Yes | Runs, but records no trades | No |
On regular Polymarket every order is raised to the market's minimum order size (often 5 shares), so a streak starts at the minimum and grows only once the scale passes it. With max_position below the minimum, the strategy can't trade there and logs why.
observation_momentum backtests
The backtest reads trade prices only. It waits until N bars with a trade price have accumulated, then compares the latest close with the close N − 1 bars earlier. Sizes follow the same position_scale rule, holdings on each side are capped at max_position, and there is no reversal sale. Live on Kalshi, held plus resting contracts on both sides together are capped at the lower of max_position and Max open contracts. Polymarket history is order-book data with no last-trade prices, so a Polymarket backtest of this strategy records no trades.
observation_momentum example
Illustrative only: trade momentum on the most liquid bracket of the current Kalshi NYC high-temperature event. When YES has moved about 4 cents or more over the last 5 trades it buys in that direction, sizing consecutive signals 1, 2, 3. max_portfolio_positions: 1 refuses a buy in a new market while your account already holds another. Studio pre-fills Max contracts per order at 3 and Max dollars per order at $3, which covers the size-3 order at up to the 0.90 price ceiling ($2.70). Holding 1 + 2 + 3 would need max_position of at least 6: at 3, the size-3 order is refused once sizes 1 and 2 have filled. The strategy reads no weather data.
version: 1
platform: kalshi
strategy: observation_momentum
market:
series_ticker: "KXHIGHNY"
selection: most_liquid
risk:
max_position: 3
price_floor: 0.10
price_ceiling: 0.90
max_portfolio_positions: 1
loop:
interval: 60
params:
lookback_periods: 5
momentum_threshold: 0.04
position_scale: linear