Kalshi Perps Archetypes
This page covers the Kalshi Perps archetypes (platform: kalshi_perps): the rules for params, the ATR stop two of them place, and what each archetype does with every parameter. The other blocks (markets, loop, execution, sizing, risk and funding_awareness) work as described in Markets and Execution and Sizing and Risk, and All Kalshi Perps parameters lists every key in one table. Values in the examples are illustrative, not recommendations.
An archetype is a ready-made strategy you tune with params. Choose one with strategy: trend_rider, breakout_hunter or dip_reverter. All three work on closed candles of the market's own perp price, at the params.timeframe you choose.
Rules for params
- Only the archetype's own keys. Any other key fails with
params.<key>: unknown param for <strategy>, including a key from another archetype, such asmidline_filterontrend_rider. - Every key is optional. A missing key takes the default in the archetype's table below. Don't write a key as
null; that is rejected. Leave it out instead. - Numbers. Windows must be whole numbers of at least 1.
timeframemust be a string:1m,1h,4hor1d. There is no5mor15m. 4h candles are built by the Bot from 1h candles, in 4-hour blocks starting at 00:00 UTC. - Pair checks need both keys.
fast_windowbelowslow_windowandrsi_oversoldbelowrsi_overboughtare only checked when you write both keys.fast_window: 30on its own, against the defaultslow_windowof 21, saves and turns the strategy upside down.
The ATR stop
trend_rider (when atr_stop_multiple is above 0) and breakout_hunter (always) guard every position they open with a stop. It sits atr_stop_multiple × ATR(atr_window) below the Bot's average entry price for a long, or above it for a short, with ATR (average true range) measured on the archetype's candles in dollars per contract. For example, a long with a $6.40 average entry, an ATR of $0.05 and a multiple of 2 gets a stop at $6.30.
The Bot places the stop on the first tick it holds a position without one, usually the tick after the entry fills (a resting entry can fill between ticks), and before take-profit and stop-loss are checked. The log shows ATR stop armed @ <price>. From then on the Bot checks it on every tick, after take-profit and stop-loss, and closes the whole position if the mark reaches it. The log shows soft stop @ <price>.
How it behaves:
- It doesn't trail. It stays where it was placed.
- It lasts as long as the position. It is removed only once the Bot's position reads flat, never when a close is merely sent. If an immediate-or-cancel close fills nothing, the stop stays and fires again on the next tick while the mark is still past it.
- It survives restarts. The stop is saved with the Bot's position, so a stop, restart (including one caused by a Turbine software update), redeploy or update keeps it where it was. In paper trading, for a position opened before stops were saved, and after a runner upgrade that clears the saved position, the Bot places it again after the restart, from the average entry and the ATR at that moment.
- An update keeps it. Changing
atr_stop_multipleoratr_windowwith Update Bot doesn't move the stop of a position that is already open. Turning the stop off (trend_riderwithatr_stop_multiple: 0) removes it. - It needs candle history. An archetype doesn't enter until there are more than
atr_windowclosed candles for the ATR. If the ATR still can't be measured for an open position, the log showsATR stop not armed yetand the Bot tries again on every tick.
Tip:
risk.stop_loss_pctmakes a good backstop alongside it: it is measured from the average entry on every tick and needs no candles.
trend_rider
Moving-average trend following. On each tick it computes a signal from the latest closed candle: +1 when the fast average is above the slow one, −1 when below, 0 when equal. Then:
| Signal | Bot is long | Bot is flat | Bot is short |
|---|---|---|---|
| +1 (fast above slow) | Hold | Open long | Close now, open long on a later tick |
| −1 (fast below slow) | Close now, open short on a later tick | Open short | Hold |
| 0 (flat) | Close | Nothing | Close |
long_only treats −1 as 0, so it closes longs and never shorts. short_only is the mirror image.
| Param | Default | Allowed | What it does |
|---|---|---|---|
fast_window | 9 | whole number ≥ 1, below slow_window | Fast average length, in candles. |
slow_window | 21 | whole number ≥ 1 | Slow average length, in candles. |
timeframe | 1h | 1m, 1h, 4h, 1d | Candle size. |
ma_kind | ema | ema, sma | Exponential or simple averages, used for both. |
mode | long_short | long_short, long_only, short_only | Which sides it trades. |
confirm_closes | 1 | whole number ≥ 1 | Closed candles that must all agree before an entry. |
atr_stop_multiple | 0 (off) | ≥ 0 | ATR stop distance. 0 turns it off. |
atr_window | 14 | whole number ≥ 1 | ATR look-back for the stop. |
- No fresh crossover needed. It enters whenever it is flat and the signal holds. After a take-profit or stop-loss it can open again on the next tick.
confirm_closesdelays entries, not exits. With 2 or more, an entry waits for that many agreeing closes, but a single disagreeing close makes the signal 0, and 0 closes the position (signal flat).- Warm-up. Until enough closed candles exist for both averages, the Bot logs
warming indicatorsand does nothing. With the ATR stop on, entries also wait for more thanatr_windowclosed candles, loggingentry waits: ATR(<atr_window>) for the stop is warming; exits don't wait. - Log lines. A reversal closes with
flip to longorflip to short; a 0 signal closes withsignal flat.
version: 1
platform: kalshi_perps
strategy: trend_rider
strategy_name: BTC and ETH hourly trend, long only
strategy_name_origin: user
markets:
- ticker: KXBTCPERP
- ticker: KXETHPERP
loop:
interval: 60 # check every minute; signals only change when an hourly candle closes
execution:
entry_mode: taker # aggressive IOC limit order
max_slippage_pct: 0.5 # never pay more than 0.5% through the ask (also bounds exits)
sizing:
margin_usd: 25 # $25 margin x 2x = about $50 notional per entry, per market
leverage: 2
risk:
take_profit_pct: 6 # close the whole position after a 6% favorable move from average entry
stop_loss_pct: 3 # close after a 3% adverse move (about 6% of margin at 2x)
params:
fast_window: 9
slow_window: 21
timeframe: 1h
ma_kind: ema
mode: long_only # a bearish signal closes the long instead of opening a short
confirm_closes: 2 # enter only after 2 consecutive hourly closes agree
atr_stop_multiple: 2 # plus a fixed stop 2x ATR(14) below the average entry
atr_window: 14Each market is sized on its own: about $50 of notional per entry on each of the two, rounded down to whole contracts. After an entry fills, the Bot places its ATR stop on the next tick and keeps it, across restarts, until the position is closed.
breakout_hunter
Channel breakouts. When flat, it goes long when the latest closed candle closes above the highest high of the entry_window candles before it, and short when it closes below their lowest low. A long closes when a closed candle ends below the lowest low of the previous exit_window candles; a short closes above their highest high (exit channel breach). It never adds to a position, and the ATR stop is always on.
| Param | Default | Allowed | What it does |
|---|---|---|---|
entry_window | 20 | whole number ≥ 1 | Channel length for entries, in candles. |
exit_window | 10 | whole number ≥ 1 | Channel length for exits, in candles. |
timeframe | 1h | 1m, 1h, 4h, 1d | Candle size. |
atr_stop_multiple | 2.0 | above 0 | ATR stop distance. It can't be turned off. |
atr_window | 14 | whole number ≥ 1 | ATR look-back for the stop. |
midline_filter | true | any | Has no effect today. |
- Signals use closed candles. Entries and exits compare the last closed candle's close, not the live mark. The signal is re-checked on every tick, so after a stop-out the same breakout candle can trigger a new entry while it is still the latest one.
- Warm-up. It waits for the largest of
entry_window,exit_windowandatr_window, plus 2, closed candles, so it never opens a position before its exit channel and its ATR stop can work. midline_filterchanges nothing. It is meant to keep only breakouts on the trend side of the channel's midpoint, but a close above the channel high is always above the midpoint (and a close below the low is always below it), so it never changes a decision. You can leave it out.
version: 1
platform: kalshi_perps
strategy: breakout_hunter
strategy_name: SOL 4h Donchian breakout with resting entries
strategy_name_origin: user
markets:
- ticker: KXSOLPERP
loop:
interval: 120
execution:
entry_mode: limit
limit_offset_pct: 0.1 # rest 0.1% behind the touch (below the bid for longs)
limit_ttl_seconds: 120 # = loop.interval: an unfilled entry is re-priced every tick
max_slippage_pct: 0.5 # still used by TP/SL and channel-exit closes, which are always taker
sizing:
margin_usd: 20
leverage: 1.5
risk:
stop_loss_pct: 8
params:
entry_window: 20 # break above the prior 20-bar high / below the prior 20-bar low
exit_window: 10 # exit on the opposite 10-bar extreme
timeframe: 4h
atr_stop_multiple: 2 # stop 2x ATR(14) from the average entry; always on here
atr_window: 14Entries rest on the book, one at a time. Because limit_ttl_seconds equals loop.interval, an unfilled entry is cancelled on the next tick and, if the breakout still holds, replaced at the current price. The channel exit, the ATR stop and the stop-loss are still immediate orders bounded by max_slippage_pct.
dip_reverter
Mean reversion. When flat, it goes long when RSI is below rsi_oversold and the last close is below the lower Bollinger band. It goes short when RSI is above rsi_overbought and the close is above the upper band. It closes a long once a close reaches the middle band (the moving average) and a short once a close falls to it (middle-band target).
It has fixed guardrails that other strategies don't:
risk.stop_loss_pctis required.sizing.leverageis capped at 2.- It enters once per position: adds are refused.
- It pauses new entries while volatility has jumped: if ATR now is more than
vol_pause_mult× the ATR measuredatr_windowcandles earlier, it skips entries and logspaused: volatility regime. Exits keep working.
| Param | Default | Allowed | What it does |
|---|---|---|---|
rsi_window | 14 | whole number ≥ 1 | RSI look-back, in candles. |
rsi_oversold | 30 | above 0, below 100, below rsi_overbought | RSI level under which a long is allowed. |
rsi_overbought | 70 | above 0, below 100 | RSI level over which a short is allowed. |
bb_window | 20 | whole number ≥ 1 | Bollinger band look-back. |
bb_mult | 2.0 | above 0, up to 5 | Band width in standard deviations. |
timeframe | 1h | 1m, 1h, 4h, 1d | Candle size. |
vol_pause_mult | 1.5 | ≥ 1 | How much ATR must jump to pause entries. |
atr_window | 14 | whole number ≥ 1 | ATR look-back, and how many candles back the earlier ATR is taken. |
- RSI is for entries only. Exits use the middle band, plus your stop-loss.
- The pause compares with an earlier ATR, not with a long-run average. It also needs at least 2 ×
atr_window+ 1 closed candles of history; with less, entries are not paused.
version: 1
platform: kalshi_perps
strategy: dip_reverter
strategy_name: XRP hourly dip reverter, funding aware
strategy_name_origin: user
markets:
- ticker: KXXRPPERP
loop:
interval: 60
sizing:
margin_usd: 20
leverage: 1.5 # dip_reverter is capped at 2x
risk:
stop_loss_pct: 5 # required for dip_reverter
funding_awareness: true # skip an entry whose side would currently pay funding
params:
rsi_window: 14
rsi_oversold: 25
rsi_overbought: 75
bb_window: 20
bb_mult: 2.0
timeframe: 1h
vol_pause_mult: 1.5 # no new entries while ATR is more than 1.5x its level 14 candles earlier
atr_window: 14A long needs RSI(14) under 25 and the last hourly close under the lower band; a short is the mirror image. funding_awareness only blocks new entries on the paying side. It never closes a position that is paying funding.