Back to blog
Strategies4 min read

The Fee-Gated Scalper Still Paid the Toll

This week I gave a small online model one hour and a fresh $1,000 PredArena strategy account. The rule was simple: use live public order books, keep costs

This week I gave a small online model one hour and a fresh $1,000 PredArena strategy account. The rule was simple: use live public order books, keep costs low, trade often, and publish the exact mechanics.

The strategy was a fee-gated microdrift scalper. It watched tight Kalshi books through PredArena, estimated the next short-horizon mid-price move from the last quote change and top-of-book imbalance, then bought the YES or NO side only when the predicted move cleared a spread and fee buffer. PredArena simulated the fills, fees, positions, and PnL. No external real-money brokerage or exchange account was connected.

Strategy Summary

In plain English: buy a small amount of the side whose top-of-book pressure and recent mid-price drift point in the same direction, then exit quickly when the mark earns enough to pay the toll, loses too much, gets stale, or reaches the end of the run.

The implementation lives in `scripts/experiments/weekly_ai_strategy.mjs`. The run artifacts are `experiments/weekly-ai-strategy/20260704004933/summary.json`, `experiments/weekly-ai-strategy/20260704004933/orders.json`, and `experiments/weekly-ai-strategy/20260704004933/blog.md`.

Exact Signal

Each cycle discovered liquid open Kalshi markets, pulled depth-20 order books from `https://api.elections.kalshi.com/trade-api/v2`, and built one candidate for each YES and NO side. The features were:

- Bias term: `1` - `momentumCents`: current mid minus previous mid, in cents - `imbalance`: `(bidQuantity - askQuantity) / (bidQuantity + askQuantity)` - `spreadCents`: ask minus bid, in cents, entered as a negative cost feature - `volatilityCents`: exponentially smoothed absolute mid-price change, entered as a negative cost feature

The online model started with weights `[0,0.35,0.05,0.03,-0.02]` and updated after each observed quote change with clipped linear error. The final weights were `[0.015000000000000019,0.35,2.6007657904227673e-15,0.015000000000000005,-0.02]`.

Entry and Exit Rules

Entry used market orders in the PredArena paper portfolio API, with live Kalshi order-book depth determining the simulated fill. A candidate could enter only when spread was at most 3.50 cents, top-of-book size could fill 3 contracts, there was no existing position in the same side, open positions were below 8, and gross open exposure was below $180.00.

The score was:

```js score = predictedMoveCents - spreadCents * 0.45 - 0.4 ```

The bot bought when `score >= 0.2`. If no model signal appeared after 120 seconds, it allowed a small exploration probe only on books with spreads at or below 2.50 cents.

Exit used the same paper order API to sell the exact opened quantity. A position was flattened on the first matching rule: per-contract PnL at least $0.018, per-contract PnL at or below -$0.035, holding time above 6 minutes, or end-of-run flattening.

Position Sizing and Controls

Position size was 3 contracts per entry. The account started at $1000.00. Maximum simultaneous positions were 8. Maximum gross open exposure was $180.00. The loop cadence was about 10 seconds, subject to API and order-book latency. All orders were PredArena simulated market orders against live Kalshi books, so the venue/feed was Kalshi market data and the execution venue was PredArena paper trading.

Representative Implementation

```js function buildFeatures({ momentumCents, imbalance, spreadCents, volatilityCents }) { return [1, momentumCents, imbalance, -spreadCents, -volatilityCents]; }

const predictedMoveCents = predict(model, features); const score = predictedMoveCents - spreadCents * 0.45 - 0.4;

if (score >= config.minScoreCents && snapshot.spread <= config.maxSpread) { await placeOrder({ action: "buy", ticker: signal.ticker, direction: signal.direction, quantity: config.quantity, type: "market", }); } ```

Results

It ended down $5.50 after the account was flattened. The account placed 160 orders over 60 minutes, including 80 exits. Total fees recorded by PredArena were $3.00.

MetricValue
Starting balance$1000.00
Final account balance$994.50
Realized PnL$-5.50
Net PnL vs start$-5.50
Orders160
Round trips80
Gross bought$20.00
Fees$3.00
Open mark value$0.00
Cadence~10s polling
Best closed tradeYES KXNEWPOPE-70-MZUP: $-0.05
Worst closed tradeYES KXNEXTNATOSECGEN-99-PPAV: $-0.09

Trade Examples

- YES KXXISUCCESSOR-45JAN01-LXI: bought 3 at $0.110, sold at $0.100, exit=max_hold, realized PnL=$-0.08. - YES KXNEXTNATOSECGEN-99-ULEY: bought 3 at $0.060, sold at $0.050, exit=max_hold, realized PnL=$-0.06.

Was It Profitable?

No. The ending balance was $994.50, so the account lost $5.50 after simulated fees and flattening.

What To Change Next

The next version should stop using market orders as the default. The signal was deliberately cheap, but taker execution is still the expensive part of this style. A better attempt would post maker limits near the best bid, cancel stale quotes quickly, and let the model predict fill probability as well as price movement. It should also segment markets by fee waiver and realized micro-volatility instead of using one global threshold.

This run was not profitable, and it left a reproducible ledger rather than a vibe.

Research sources

Test your prediction market agent before live capital

Route supported market orders to PredArena and inspect fills, slippage, fees, balances, and order history against live Kalshi-style order book depth.

Create a free sandbox keyRead the API docs

Related reading

StrategiesThe One-Hour Model Did Not Beat the SpreadTrading LogA Tiny Bot Traded Knicks-Spurs Q4