A local-first tracker for the options wheel strategy.
Track cash-secured puts and covered calls, link full wheel cycles, and see your real annualized return on capital — premium income, assignment risk, and all. Your data never leaves your browser.
Most wheel traders track their trades in a spreadsheet that can tell them how much premium they collected — but not much else. Spreadsheets are bad at the things that actually matter for the wheel:
- Linking a full cycle. A put you sold in February, the assignment, the covered calls you wrote against the shares, the call that finally got assigned — that's one trade, economically. Wheelhouse stitches the legs back together automatically.
- Comparing trades fairly. A
$46credit on a 14-day put and a$300credit on a 45-day put aren't comparable until you annualize them against the capital each one tied up. - Knowing what's about to get assigned before it happens.
Wheelhouse does those three things, and it does them without a backend. No login, no upload,
no broker connection. Everything is computed in your browser and saved to localStorage.
| 🔗 Wheel-cycle linking | Legs are grouped into cycles automatically by walking each ticker's timeline. A cycle runs from the first put you sell (or shares you buy) until you're flat again. |
| ⚖️ Wheel vs. buy-and-hold | Every cycle shows what you'd have made just buying the shares at the strike and holding — so you can see whether the wheel is actually beating the stock. |
| 🔁 Roll tracking | Roll an open put or call (buy-to-close + sell a new one) in a single action; the two legs stay one continuous position in the cycle. |
| 📈 Annualized return on capital | Per-cycle return on the peak capital at risk, annualized — so a quick CSP and a long one are finally comparable. |
| Open short options ranked by probability of assignment (entered delta, or moneyness + days-to-expiry), with breakevens and one-click expire / assign / roll. | |
| 🎯 Profit-if-called | For shares you're holding under a covered call, see your exact cycle profit if they get called away. |
| 🗓️ Premium income calendar | GitHub-style heatmap of credit collected day by day, plus monthly and yearly totals. |
| ✨ Wheel Wrapped | A generated year-in-review card (premium, win rate, best cycle, top ticker) you can download as a PNG and share. |
| ✍️ Manual entry + CSV import | Quick entry form, a documented generic CSV, and a best-effort Wealthsimple activity-export parser. |
| 🔒 Local-first | No accounts, no servers, no telemetry. Export a JSON backup or CSV anytime. |
A cycle, expanded — wheel-vs-buy-and-hold, profit-if-called, and the linked legs:
Premium heatmap and the shareable Wheel Wrapped card:
git clone https://github.com/kennymkchan/wheelhouse.git
cd wheelhouse
npm install
npm run dev # http://localhost:3000Then click Load sample portfolio to see it populated, or Add your first trade.
It builds to a fully static site, so it hosts anywhere:
npm run build # outputs ./outDrop out/ on Vercel, Netlify, GitHub Pages, S3 — wherever. There's no server to run.
Each option position is a single row that records both the open and the outcome
(expired, assigned, bought to close, or still open). The engine
(src/lib/engine.ts) expands every position into open/close events,
sorts them per ticker, and walks the timeline:
- A cycle starts when you open from flat and ends when shares and open contracts return to zero.
- Premium collected = credits − buy-to-close debits − fees.
- Assigned puts add shares at the strike; assigned calls / sales realize share P&L against a proportional cost basis.
- Capital deployed is the peak of (cash securing open puts + cost basis of held shares) over the cycle.
- Annualized return = realized P&L ÷ capital deployed × (365 ÷ days held).
- Adjusted cost basis on shares you're holding = (share cost − net premium) ÷ shares.
- Rolls momentarily go flat (buy-to-close, then sell-to-open). The engine bridges that gap so a rolled position stays one cycle instead of splitting in two.
- Buy-and-hold benchmark deploys the cycle's capital into the shares at the entry strike and marks them to the realized exit (or your current price), then compares to the wheel.
It's all pure functions, so it's easy to read and easy to test:
npm test # runs scripts/test-engine.ts — cycle linking, rolls, and wheel mathGeneric format (recommended, round-trips the app's own export). One row per position:
type,ticker,kind/side,contracts/shares,strike/price,opendate/date,expiry,premium,fees,outcome,closeDate,closeCost,delta,notes
option,AAPL,put,1,180,2026-05-01,2026-05-16,210,1.05,expired,,,0.22,Weekly CSP
option,AAPL,put,1,185,2026-05-20,2026-06-06,240,1.05,assigned,2026-06-06,,0.41,Got assigned
stock,AAPL,sell,100,192,2026-06-20,,,0,1.00,,,,Called away
option,F,put,2,11,2026-06-02,2026-06-20,46,1.30,open,,,0.18,Open CSPtype:optionorstock- options:
kindisput(cash-secured) orcall(covered);premiumis the total credit in dollars;outcomeisopen/expired/assigned/closed - stock:
kind/sideisbuyorsell;strike/priceis price per share
Wealthsimple (beta). Point it at your activities-export-*.csv. It parses the OCC option
symbols (GOOGL 260427C00340000) and infers outcomes, but Wealthsimple's export doesn't
cleanly mark assignment vs. expiry — so review the preview and fix any leg in the Trades tab.
Next.js 16 · React 19 · TypeScript · Tailwind 4. Static export, zero runtime dependencies
beyond React. State lives in localStorage; the wheel engine is framework-agnostic and
lives in src/lib/.
Wheelhouse is a personal record-keeping tool, not financial advice, and the assignment-risk estimate is a rough heuristic — not a pricing model. Verify anything that matters against your broker. No quotes are fetched and nothing is transmitted anywhere.
MIT © Kenny Chan



