From 2222d8a5fac6850d7d924e9404a44a65cb10f68f Mon Sep 17 00:00:00 2001
From: Tyler Hoang <tyler@tylerhoang.xyz>
Date: Thu, 21 May 2026 14:49:56 -0700
Subject: fix: rewrite polar smile with correct closed-lens geometry
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Piecewise angleFor: puts go CCW (ATM→bottom), calls go CW (ATM→bottom)
  so both wings meet at the same bottom point and the path closes with Z
- Dynamic ivToR computed from actual IV range across all expiries
  (inner floor 60px, outer wall 240px) instead of hardcoded percentages
- IV rings: 5 evenly-spaced steps between real ivLo/ivHi
- Curve fill: rgba(194,170,122,0.12) matching design reference
- Non-selected curves: index-based styles (solid fg-1, then dashed fg-2/3/4)
- Eye price at font-size 30, label lowercase 'call · fair'
- Dashed marker-line spoke from center to selected-K dot
- Clicking a curve switches the selected expiry via onPickT
- Strike ticks use round multiples of a price-adaptive step
- Fix IvHeatmap xlabs duplicate key error (key by index, not rounded K)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 frontend/components/prism/options/OptionsPage.tsx | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'frontend/components/prism/options/OptionsPage.tsx')

diff --git a/frontend/components/prism/options/OptionsPage.tsx b/frontend/components/prism/options/OptionsPage.tsx
index 7fcf5c3..4065d9f 100644
--- a/frontend/components/prism/options/OptionsPage.tsx
+++ b/frontend/components/prism/options/OptionsPage.tsx
@@ -150,14 +150,18 @@ export function OptionsPage({ overview, ticker }: OptionsPageProps) {
       <div className="opt-col">
         <PolarSmile
           S={inputs.S}
+          K={inputs.K}
+          T={expiry.T}
           r={r}
           q={q}
           atmSigma={atmSigma30}
-          K={inputs.K}
-          T={expiry.T}
           type={inputs.type}
           expiries={EXPIRIES}
-          selectedExpiryIdx={expiryIdx}
+          selectedT={expiry.T}
+          onPickT={T => {
+            const idx = EXPIRIES.findIndex(e => Math.abs(e.T - T) < 1e-6);
+            if (idx >= 0) selectExpiry(idx);
+          }}
         />
         <IvHeatmap
           S={inputs.S}
-- 
cgit v1.3-2-g0d8e

