Commit Graph

35 Commits

Author SHA1 Message Date
ojy
d79b98baca Scanner: IV/HV column + composite EDGE signal
User clarified the real entry signal: not just high IV, but IV
higher than HV — that's what makes premium "rich" relative to
realized vol and gives the mean-reversion edge for premium sellers.

Changes:
- New IV/HV column (was already computed server-side as r.ivHv but
  not displayed). Colored cheap/fair/rich/very-rich. Sortable.
- New EDGE composite badge in the Flags cell: lights green when
  IV Rank ≥ 60 AND IV/HV ≥ 1.20 — premium expensive in own history
  AND not justified by what stock is actually doing.
- Summary row gains an "EDGE setups" count card.
- Page header copy reworked to explain the three richness signals
  (IV Δ%, IV Rank, IV/HV) and why the composite EDGE flag matters.

Live test confirms the discrimination: INTC / AAPL / NVDA flag as
EDGE; TSLA (high IV/HV but low Rank — its normal regime) and SPY /
COIN (IV/HV high because realized vol is unusually quiet, not
because IV is rich) are correctly excluded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 09:53:16 +00:00
ojy
e2eca5ef66 Scanner: add IV Rank + IV Percentile columns
IV Rank shows where current ATM IV sits in its 1-year (min, max)
range from saved snapshot history. The industry-standard entry
metric: >=60 = expensive (sell premium), <=30 = cheap (buy
premium). Also exposes IV Percentile (share of past snapshots
with strictly lower IV) via the tooltip.

- snapshots.ts: new getIvRange + getIvPercentile queries with
  a min-samples gate so the metric is hidden until n>=5
- datafetch.ts: ScanResult gains ivRank, ivPercentile, ivRankN,
  ivRankSpanDays
- options.ts: error stub updated with new fields
- scanner.html: new sortable IV Rank column with chip-styled
  color coding (green/grey/yellow/red); summary row gains a
  "High IV Rank (>=60)" count card; header text explains the
  new metric and the >=60 / <=30 entry rule of thumb

Live INTC scan: IV Rank 100 (1-year peak) confirms the position's
short-premium structure was entered into expensive vol -
mean-reversion tailwind is the edge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 09:40:10 +00:00
ojy
0a6e357a78 INTC trade plan §12.7: clarify that conversion IS the companion
Earlier §12.7 suggested opening a separate long call alongside the
iron fly as a "companion" to capture moonshot upside. That framing
was wrong: it doubles capital at risk while buying expensive 80% IV,
and is redundant with the §6 conversion plan.

The real companion is what the iron fly becomes after the 3-month
conversion — a long $160 call at ~$45 net cost basis, financed by
realized theta. Same exposure as a $1,870 outright purchase, at 4%
of the cost.

Updated §12.7 to:
- Remove the "add a small companion now" suggestion
- Reframe the conversion product as the companion
- List the rejected alternatives explicitly so future-me doesn't
  re-litigate them

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 09:24:13 +00:00
ojy
398c7cd11b Add INTC iron-fly trade plan (position #5)
Reference document for the open INTC broken-wing iron butterfly
entered 2026-05-13 (Jan-2027 expiry). Covers:

- Position snapshot + thesis + EV analysis across 3 vol regimes
- Phase-by-phase management plan (Mermaid timeline)
- Conversion play: close 3 legs at month 3 once theta paid in,
  leaving a near-cost-free long call 160 for upside capture
- Crash playbook for -20% drops
- Hard rules + decision matrix by price zone
- Trade journal + performance tracking templates
- "10/10 plan" section: pre-staged broker orders, multi-name
  diversification, IV-regime entry gate, % of portfolio sizing,
  and the long-call-diagonal alternative for high-conviction
  directional views

Filed under /trades/ as a living document; revise at each decision
point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 09:11:03 +00:00
ojy
fc57aecd98 Vol Surface: cache-first load + auto-pick next-month 3rd Friday
On page load and on every symbol Lookup, target the 3rd Friday of
the next calendar month (the standard US monthly options expiry).
If that exact date isn't listed (e.g., June 19, 2026 is Juneteenth
so SPY's monthly is the Thursday 06-18), fall back to the nearest
available expiration.

Data flow:
  1. Init reads {symbol, expirations} from ViewState.
  2. Computes target expiry (3rd Fri next month).
  3. Hits a new per-symbol cache at
     localStorage['optionsPricer:surfaceCache'][symbol:expiry].
     - Hit AND cache.date === today → render instantly, no network.
     - Hit but stale (cache.date !== today) → refetch.
     - Miss → fetch expirations + load surface.
  4. fetchExpirations() now auto-selects the target expiry and
     triggers _loadForTargetExpiry (cache-aware) — entering a new
     symbol now produces a rendered surface with one Enter press.
  5. Successful loadSurface writes the response into the cache
     under today's date; cache is pruned to 50 entries.

Analytics is no longer stuffed into ViewState (only the lightweight
symbol/expirations/expiry pointer is), so the per-symbol cache is
the single source of truth for surface data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 08:08:05 +00:00
ojy
d89ad179f3 Vol Surface: HV-vs-IV card as rowspan-2 to the right of toolbar
Wrap the toolbar card and the skew-metric badges (ATM IV / RR25 /
Fly25) in a left column, and place the HV-vs-IV card in a sibling
right column with h-100 d-flex flex-column so it stretches to the
full height of both stacked items — effectively a rowspan=2 layout.

On screens narrower than lg the right column drops below as a
single full-width card.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 08:00:32 +00:00
ojy
f8aa3cdaae Vol Surface: move HV-vs-IV card into the toolbar row
User wanted the comparison sitting alongside the Lookup / Expiry /
Load Surface controls instead of in the page header. Now it lives
on the right side of the toolbar row (ms-sm-auto), still hidden
until data is loaded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:58:09 +00:00
ojy
52af71d7f4 Vol Surface: top-right HV-vs-IV comparison card
Adds a compact card in the page header that shows ATM IV alongside
realized vol over 20/30/60-day windows, the IV-minus-HV spread in
vol points, and a RICH/FAIR/CHEAP verdict (driven by IV/HV30 ratio:
>=1.20x = RICH, <=0.80x = CHEAP, otherwise FAIR). Lets you eyeball
whether options are priced rich relative to recent realized vol the
moment the surface loads.

- datafetch.ts: extract annualizedVolWindow helper; new
  fetchHistoricalVolWindows() returns hv20/hv30/hv60 from one
  ~90-day Yahoo historical pull
- options.ts: /api/analytics includes hvWindows in response
- surface.html: top-right hviv-card with per-window rows + footer
  showing IV/HV ratio and sample size

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:55:07 +00:00
ojy
2e565fae4d Add Top Movers screener (mid-cap+, options-tradable)
New /movers page surfaces Yahoo Finance's predefined screeners
(day_gainers, day_losers, most_actives, most_shorted_stocks)
filtered to common equities with market cap >= $2B, so every
listed name has a deep options chain. Per-row actions jump
straight into Chain / Vol Surface / IV Spike Scanner, or pin
the symbol to the Tracker watchlist.

- datafetch.ts: fetchMovers(category, count) using yf.screener,
  post-filtered to quoteType=EQUITY and marketCap >= $2B
- options.ts: GET /api/movers?category=&count=
- movers.html: Tabler page with 4-tab segmented control, sortable
  table, summary cards, volume-vs-avg ratio highlighting hot names
- All page sidebars: insert "Movers" link between Vol Surface
  and Scanner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:47:32 +00:00
ojy
3ea5fd5209 Scanner: spike = current ATM IV >= +30% above recent baseline
Replaces the IV/HV >= 1.5 heuristic with a baseline-relative jump:
a symbol is flagged SPIKE when current ATM IV is at least 30%
above its recent baseline (e.g., 10% -> 13%+). Baseline prefers
the 30-day average of saved snapshots; falls back to HV30 when
no scan history exists. Each scan persists a snapshot so the
baseline self-improves over time. BIG MOVE (|chg%| >= 3%) is now
shown as a separate badge instead of a spike requirement.

- snapshots.ts: add getAverageAtmIv(symbol, days)
- datafetch.ts: save snapshot per scan; compute baselineIv,
  baselineSrc, baselineN, ivJumpPct; spike from jump threshold
- options.ts: /api/scan returns new baseline + jump fields
- scanner.html: header copy, table columns (Baseline IV, IV Δ%),
  default sort by ivJumpPct desc, separate SPIKE/BIG MOVE badges

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:39:24 +00:00
ojy
2cd3d6ece8 IV Spike Scanner
Backend: GET /api/scan?symbols=SYM1,SYM2,... — for each symbol fetches
the front-expiry options chain plus 30-day realized vol and returns
{ spot, change, changePct, atmIv, hv30, ivHv, spike, expiry }. Spike flag
is on when IV/HV ≥ 1.5 or |today's % change| ≥ 3. Defaults to ~15 popular
tickers when no list is given; cap of 30 symbols/scan.

Frontend: new scanner.html page — symbol input (with "Use defaults" / "Use
watchlist" shortcuts), summary cards (count · spikes · biggest mover ·
highest IV/HV), sortable results table with spike rows highlighted, and
shortcut buttons to open each symbol on Chain or Surface.

Scanner added to all sidebars between Vol Surface and Strategy P/L.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:32:14 +00:00
ojy
46574c4f51 Standardize sidebar across all 7 pages
Every page now uses the same brand logo (candle chart icon) and the same
seven nav items in the same order with identical tabler-style icons:
Dashboard · Options Chain · Vol Surface · Strategy P/L · Positions ·
Tracker · Settings. Only the active item differs per page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:17:40 +00:00
ojy
65cb6cc5f2 Tracker watchlist: summary cards + auto-load first symbol
Each watched symbol now appears as a small card showing live spot, ATM IV
(color-coded by level), and RR25 — fetched from /api/analytics. Click a
card to load that symbol's full IV/RR/Fly history charts; the active card
gets a blue border.

On page open: if there's a watchlist but no history loaded yet, auto-open
the first watched symbol so the page isn't empty. Refresh button re-pulls
all watchlist metrics. Empty state hints at adding symbols from the
Strategy page's "Save to Tracker" button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:14:06 +00:00
ojy
d03a90312c Rename Positions row button to "P/L Chart"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:09:53 +00:00
ojy
c4507ec075 Positions: "Open" button loads the position into the Strategy page
Each row has a new Open button that copies the order's legs (locked, since
they're real fills) into the StrategyStore basket for that symbol — wiping
only that symbol's basket (with a confirm if it already has legs) — and
navigates to strategy.html so you can analyze, roll or adjust the position.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:08:52 +00:00
ojy
9de7f14573 Split Positions from Tracker; add Settings (commission)
Tracker is for symbol price/IV history, not positions. The "Save to
Tracker" button now adds the symbol to a watchlist (localStorage); the
Tracker page shows the watchlist as clickable chips.

New "Enter Position" button on the Strategy page posts the active legs
to /api/orders, then opens the new Positions page.

New Positions page (positions.html): lists entered positions with live
mid value, round-trip commission, gross & net P/L (Net = Gross − round-
trip commission), per-symbol filter, summary totals, close/reopen and
remove actions.

New Settings page (settings.html) configures the commission used on
Positions. Defaults to Interactive Brokers Fixed / IBKR Lite: $0.65
per contract, $1.00 minimum per order
(https://www.interactivebrokers.com/en/pricing/commissions-options.php).
Per-leg vs per-order toggle for complex orders.

Sidebar nav now: Dashboard · Options Chain · Vol Surface · Strategy P/L
· Positions · Tracker · Settings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:04:52 +00:00
ojy
c1520d7962 Tracked orders — persist strategy positions in SQLite
Backend:
- New orders table (symbol, name, legs_json, entry_cost, created_at,
  closed_at, status, note) sharing the snapshots.db
- Endpoints:
  POST   /api/orders          save a position
  GET    /api/orders?symbol=  list (most recent first)
  GET    /api/orders/:id      single
  PATCH  /api/orders/:id      { status:'open'|'closed', note? }
  DELETE /api/orders/:id      remove

Strategy page:
- "Save to Tracker" button posts the currently-active legs (with name,
  net cost, side, qty, entry, IV, lock) as an order

Tracker page:
- New "Tracked Orders" section above the IV history charts: lists each
  saved position for the current symbol with strategy name, leg summary,
  entered cost, current value, P/L $ and %, opened date, status, and
  Close/Reopen + Remove actions. Live P/L uses /api/chain mids for each
  unique leg expiry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 06:54:40 +00:00
ojy
58f898b47d Master expiry control — link all legs to one date
The Expiry column header now has a master expiry selector with a 🔓/🔒
toggle. Locking it snaps every leg to the most common expiry, then any
change to the master propagates to all legs (each re-priced from its new
expiry's chain). Per-leg expiry selects are disabled while locked.
Lock resets when switching symbols or clearing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 06:46:49 +00:00
ojy
0b546ebf41 Show DTE badge in the strategy page title strip
Adds a blue "23 DTE" badge next to the strategy name in the header (or
"14 / 42 DTE" when active legs span multiple expiries — calendar/diagonal).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:30:55 +00:00
ojy
283100c453 Show DTE per leg + per-expiry remaining DTE for multi-expiry strategies
- Legs table: small DTE chip next to each leg's expiry (e.g. "14d", "<1d",
  "exp" — absolute days from today)
- Chart header: when active legs span multiple expiries (calendar/diagonal),
  a "Remaining: 2026-05-15 (0d) · 2026-06-20 (36d)" line shows how many
  days each unique expiry has left at the current slider position

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:27:29 +00:00
ojy
dbf7c8e9d2 Editable expiry per leg — fetches the new chain on demand
The Expiry cell is now a dropdown of available expiries for that leg's
symbol (fetched from /api/expirations on page open / symbol switch /
Reload). Picking a different expiry pulls that expiry's chain on-demand
(cached), finds the same strike (or the closest available) for the leg's
type, and updates entry price, IV and mark. Lock clears since it's a new
contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:13:25 +00:00
ojy
d5d347e0fb Replace drag-pan with ‹ / › buttons in the chart header
Removed click-and-drag panning; the chart header now has ‹ and › buttons
that shift the price window left/right by ~40% of the current half-width
per click. Fit still re-centres and resets zoom.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:06:46 +00:00
ojy
03afae3d04 Drag-to-pan the P/L chart price range
Hold the left mouse button on the chart and drag left/right to slide the
underlying-price window; the curve is re-sampled as you drag so it always
extends to the edges. Fit / switching symbols / clear-all re-centre and
reset zoom.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:03:49 +00:00
ojy
6ffde10391 Editable strike in legs — re-prices from loaded chain
The Strike cell becomes a dropdown of available strikes (for that leg's
symbol+expiry+type) drawn from the cached chain — seeded from chain.html's
last-loaded chain (localStorage) and refreshed by the strategy page's own
per-expiry chain fetch on open / Reload. Picking a new strike updates the
leg's entry price, IV and mark from that contract and clears the lock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 05:00:34 +00:00
ojy
957558fc76 Fix unreadable chart annotation labels
Spot / break-even / zero-line labels now use solid high-contrast badges
(dark text on yellow, white text on blue, light text on dark) with padding
and a border instead of transparent/low-contrast backgrounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:52:47 +00:00
ojy
6ecc69df99 Add price-range zoom controls to the P/L chart
−/+ buttons (with a ±% readout) and a Fit button in the chart header widen
or narrow the underlying-price window the curve is sampled over, so you can
inspect a tight range around spot or zoom out to see the full payoff shape.
Bumped sampling density to 221 points.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:49:23 +00:00
ojy
703c305cf1 Multi-symbol strategy baskets + symbol picker
The store now keeps one basket per symbol (v2 schema, auto-migrates v1).
Adding a leg from a different symbol no longer wipes the basket — it creates
that symbol's basket and makes it active. The Strategy page shows a symbol
picker at the top (when >1 symbol is saved) to switch between them; switching
auto-fetches that symbol's live marks. "Clear all" clears only the current
symbol's legs; new StrategyStore.clearAll() wipes everything.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:47:16 +00:00
ojy
e0cbd798b6 Auto-fetch live marks on strategy page open
On opening the Strategy page, automatically pull the current spot, each leg's
live mid (Mark column) and IV — without touching entry prices. The Reload
button still re-prices unlocked legs to the current mark. Clarified the legs
table hint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:38:07 +00:00
ojy
5348bf643a Round leg prices to 2 decimals everywhere
Entry price, mark and strike are now rounded to 2dp on add, on edit, on
market reload, and on load (self-heals legacy entries) — fixes display of
float artifacts like "15.000000001". Also persist the leg's locked/currentMark
fields in the store.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:28:21 +00:00
ojy
9219220270 Add per-leg entry-price lock + market Reload button
- Each leg has a 🔓/🔒 toggle next to its entry price; locked = entry stays
  fixed (your fill), unlocked = entry re-prices to the current mark on Reload
- New "Mark" column shows each leg's current market mid (with delta vs entry)
- "Refresh spot" button replaced by "Reload": re-fetches spot, plus each leg's
  current mark and IV from the live chain (per unique expiry), re-pricing
  unlocked legs and refreshing IVs used by the T+0 curve
- reload() no longer resets the days-to-expiry slider on edits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:22:59 +00:00
ojy
fb773869b9 Move legs table to bottom; per-leg show/hide in chart
- Legs table now sits below the P/L chart and stats
- Each leg row has a leftmost "Show" checkbox; unchecked legs are excluded
  from the chart, stats, breakevens and net Greeks (and dimmed in the table),
  so you can isolate or build up a strategy incrementally
- All derived values (chart, netCost, maxDTE/minDTE, strategy name, stats)
  now operate on the active (checked) legs only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:17:07 +00:00
ojy
57473ec271 Sharpen P/L extrema & curve at strike kinks
Evaluate the expiration P/L at the exact strike prices (and sample bounds),
not just on the coarse grid — so max profit / max loss are exact for
piecewise payoffs (butterfly peak now $800 not ~$679) and the chart renders
sharp vertices for butterflies, condors, etc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:12:16 +00:00
ojy
2ebc0eeb20 Fix expiration P/L curve in strategy analyzer
- Use exact (not floored) days-to-earliest-expiry so the "Expiration P/L"
  curve uses true intrinsic value — long call/put now show a flat loss floor
  at -premium, a sharp kink at the strike, and the correct breakeven, instead
  of a soft-cornered curve with ~1 day of residual time value
- Clamp the y-axis when one tail is unbounded so the loss floor stays visible
  instead of being squished to a sliver by the runaway profit/loss tail
- Slightly tighter default x-window; denser sampling (161 pts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:07:26 +00:00
ojy
3109df842d Add strategy P/L analyzer + view-state persistence
- New strategy.html: thinkorswim-style P/L diagram (expiration + T+N curves
  via Black-Scholes, days-to-expiry slider, net debit/credit, max profit/loss
  with unbounded detection, breakevens, net Greeks, auto-detected strategy name)
- chain.html: per-row Buy/Sell buttons add legs to a localStorage basket;
  basket badge in toolbar; auto-scroll to ATM row on load
- Persist per-page view state (symbol, expiry, loaded data, charts) across
  navigation via viewstate-store.js for chain/surface/tracker/dashboard
- New assets: blackscholes.js (frontend BS port), strategy-store.js, viewstate-store.js
- Strategy P/L nav link added to all sidebars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 04:01:57 +00:00
ojy
d08c2230a8 Initial commit — options pricing dashboard
Full-stack options analytics app: IV surface, Greeks, skew metrics,
vol term structure. Yahoo Finance data with Black-Scholes IV computation
and historical vol fallback for after-hours data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 03:22:23 +00:00