// 01What We Were Building
This isn't a hedge fund story. There's no VC money, no quant team, no Bloomberg terminal. It's two of us — me and Mewtwo, my AI ops agent powered by Claude — building an automated trading engine from a Lenovo ThinkCentre M75n running Ubuntu in my living room.
The target: Kalshi's binary prediction market for Solana price movements. Specifically KXSOL15M — will SOL's price be higher in the next 15 minutes? You bet YES or NO. You're right or you're wrong. No hedging. No partial positions.
For an AI system, that's actually perfect. It's a pure classification problem. And classification is something well-designed systems can get good at. The question was whether we could design one good enough.
# Final pipeline (every 5 minutes via cron)
radar.js → resolve-trades.js → pre-check.js → analyze-sentiment.js → live-trade.js
# Data sources
Binance API → SOL price data (free, no auth)
Perplexity Sonar → real-time news sentiment
Gemini Flash → LLM sentiment scoring
Kalshi REST API → RSA-PSS SHA256 auth, live orders
// 02Phase 1: Paper Trading (April 4–12)
Before we risked a dollar, we built a scoring engine using 8 technical indicators: RSI, EMA crossovers, Volume Spikes, Momentum, Bollinger Bands, HTF Alignment, Order Book Imbalance, and Sentiment Analysis. Each signal contributes points to a directional score. When the score passes a threshold, the system places a bet.
We ran 69 paper trades. The patterns were clear immediately.
What the data told us
| Session | Win Rate | Decision |
|---|---|---|
| NY Afterhours | 68% | Golden window — keep |
| NY Market | 59% | Keep, raise threshold |
| NY Evening | 38% | Disabled |
| NY Overnight | 38% | Disabled |
| Direction | Win Rate | Decision |
|---|---|---|
| UP signals | 69% | Keep |
| DOWN signals | 45% | Disabled outside Afterhours |
The biggest decision in this phase: we had added a hard RSI filter to skip trades when RSI was in a danger zone. Logical. Protective. And completely wrong. Out of 11 trades the filter blocked, 10 would have been winners. 9% accuracy rate. We deleted it immediately.
Your best defense can be your worst offense. The RSI filter was logically sound. It had a 9% accuracy rate. Delete it.
// 03Phase 2: The Technical Hell of Going Live (April 12–15)
Going live was supposed to be simple. Write live-trade.js, flip a switch. It took 3+ days of debugging before the first real trade landed. Here's what actually broke.
/markets list endpoint returns prices as yes_ask_dollars — dollar values, not cents. We were looking for the old format. A raw debug script revealed the mismatch. Fixed by reading yes_ask_dollars directly and converting at the point of use.initialized. These show yes_ask_dollars: 0.0000. Fixed by fetching all markets, filtering for openTime ≤ now < closeTime, and checking ask price after a secondary detailed fetch.status: "executed" with settlement_status: "N/A" — not "filled" and "settled" as our code expected. Trades sat open forever. Fixed by falling back to price-movement resolution when Kalshi's settlement status was unavailable.loadBalance() was re-creating the default balance on certain error conditions. Added explicit try/catch with logging to track every re-initialization event.pre-check.js instead of running radar.js. The bot wasn't actually trading. Fixed.// 04Phase 3: The Losing Streak
| # | Date | Direction | Score | Session | P&L |
|---|---|---|---|---|---|
| 1 | Apr 13, 6:55 PM | UP | 65 | Afterhours | −$124.46 |
| 2 | Apr 14, 9:50 AM | UP | 70 | NY Market | −$49.88 |
| 3 | Apr 15, 5:20 PM | DOWN | 75 | Afterhours | −$49.40 |
| 4 | Apr 15, 6:10 PM | UP | 60 | Afterhours | −$49.50 |
| 5 | Apr 15, 6:40 PM | UP | 60 | Afterhours | −$49.68 |
Trade 1 used $124 sizing — we hadn't fixed the 25% compounding logic yet. Trades 4 and 5 both had RSI overbought conditions and price above the Bollinger upper band. We were taking UP signals in conditions that directly contradicted an UP thesis. That's not a bad market. That's a bad system.
We fixed it immediately: hard guards to skip UP when RSI > 75 or price is above BB Upper. Disabled ALL DOWN signals. Fixed trade sizing to $10 base with controlled compounding. None of that mattered yet — what mattered was that we realized we'd been doing live experimentation with real money in place of proper backtesting.
// 05Phase 4: 15 Optimization Rounds in One Day
We built a backtesting system from scratch. The Node.js version crashed repeatedly with silent exits. We ported it to Python — it hit a NameError on BASE_THRESHOLD and multiple indentation errors. We ported it back to Node.js with better error handling. Then we ran 15 rounds of optimization.
| Round | Win Rate | Trades | Notes |
|---|---|---|---|
| 1 | 51.16% | 215 | Initial run — coin flip |
| 2–4 | 22% | 19–27 | Too strict, mostly losses |
| 5–6 | — | 0 | Filters too aggressive, no trades |
| 7–8 | 50.0% | 182 | Breakeven with both directions |
| 10–11 | 49.73% | 185 | Stuck at coin flip |
| 12 | — | 0 | Over-optimized to nothing |
After Round 12 we realized: this is not a filtering problem. RSI, EMA, Bollinger Bands, Momentum — these indicators consistently produce a 50% win rate across all parameter combinations. The signal set doesn't have a predictive edge.
That was the moment everything had to change.
// 06Phase 5: The Radical Shift (Round 13)
We threw out every indicator we'd built. RSI, EMA crossovers, Momentum, Volume spikes — gone. We started over with price action patterns and mean reversion logic.
The new signal set
Round 13 results: 4 trades, 4 wins, 100% win rate. Too few trades to call it — but the direction was clear. We relaxed the filters.
Round 14: 203 trades, 57.64% win rate, +$310. Real volume, real edge.
Then we got surgical. We removed every signal and session that wasn't pulling its weight:
| Removed | WR | Reason |
|---|---|---|
| Bullish Engulfing | 33% | Liability — actively hurting us |
| Bullish Pin Bar | 52.94% | Not strong enough to justify |
| NY Market session | 50% | Coin flip — cut it |
| Extreme Overshoot | 62.96% | Below threshold |
| NY Evening | 57.69% | Below threshold |
Added a NY Overnight session bonus (+20 points) — the data showed overnight was outperforming everything.
// 07The Final Deployed Strategy
# Active signals
Bearish Engulfing → DOWN +100 pts
Bearish Pin Bar → DOWN +80 pts
Extreme Undershoot → UP +120 pts # strongest signal
# Active sessions
NY Afterhours 4–8 PM EDT 71.43% WR
NY Overnight midnight–9 AM 75.61% WR +20pt bonus
# Filters
HTF 1-hour trend: skip UP if bearish, skip DOWN if bullish
Score threshold: 70+
# Risk management
Base size: $10 · Compounding: +25% of last win
Max size: $500 · 3 consecutive wins → reset to $10
20% drawdown from peak → cap at $10
// 08What This Actually Teaches
// 09What's Next
The strategy is deployed. The bot is live on the M75n, running every 5 minutes, placing real orders. The backtested edge is 75% — now we need live validation across 30+ trades to know if that holds in production.
If it does, the questions get interesting: Can this apply to other Kalshi markets — BTC, ETH, macro events? Can the mean reversion logic generalize to Polymarket or Metaculus? What happens when we expand the candle pattern set?
But that's next month's problem. Right now the bot is running. The results will tell us if we were right. We're not waiting to find out.