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.
| Metric | Value |
| Starting balance | $1000.00 |
| Final account balance | $994.50 |
| Realized PnL | $-5.50 |
| Net PnL vs start | $-5.50 |
| Orders | 160 |
| Round trips | 80 |
| Gross bought | $20.00 |
| Fees | $3.00 |
| Open mark value | $0.00 |
| Cadence | ~10s polling |
| Best closed trade | YES KXNEWPOPE-70-MZUP: $-0.05 |
| Worst closed trade | YES 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.