import { useData, usePublish, type PanelRef } from '@jaspers-ai/sdk' import './styles.css' interface Opt { contract: string type: 'call' | 'put' strike: number expiration: string bid: number | null ask: number | null last: number | null volume: number | null open_interest: number | null iv: number | null delta: number | null gamma: number | null theta: number | null vega: number | null underlying_price: number | null [field: string]: unknown } interface State { underlying?: string expiration?: string strike_min?: number strike_max?: number } const today = () => new Date().toISOString().slice(0, 10) const f = (v: number | null | undefined, d = 2) => (typeof v === 'number' ? v.toFixed(d) : '–') const COLS = ['Bid', 'Ask', 'Last', 'Vol', 'OI', 'IV', 'Δ', 'Γ', 'Θ', 'Vega'] function cells(o: Opt | undefined) { return [f(o?.bid), f(o?.ask), f(o?.last), f(o?.volume, 0), f(o?.open_interest, 0), o?.iv != null ? `${(o.iv * 100).toFixed(1)}%` : '–', f(o?.delta, 3), f(o?.gamma, 4), f(o?.theta, 3), f(o?.vega, 3)] } export function ChainView({ panel }: { panel: PanelRef }) { const state = useData(`workspaces/${panel.workspaceId}/panels/${panel.id}/state`) as State | undefined if (state === undefined) return

Loading…

const underlying = (state.underlying ?? 'SPY').trim().toUpperCase() if (!state.expiration) return return } /** Finds the nearest expiration from the contracts reference, then shows that chain. */ function Nearest({ panel, underlying, state }: { panel: PanelRef; underlying: string; state: State }) { const { data, error } = useData('massive/option-contracts', { underlying, expiration_from: today(), limit: 1 }) const expiration = ((data ?? []) as { expiration?: string }[])[0]?.expiration if (expiration) return return } function Status({ panel, underlying, text, isError }: { panel: PanelRef; underlying: string; text: string; isError: boolean }) { usePublish(panel, { underlying, expiration: null, strikes: 0, underlyingPrice: null, error: isError ? text : null }) return

{text}

} function Chain({ panel, underlying, expiration, state }: { panel: PanelRef; underlying: string; expiration: string; state: State }) { const { data, error, loading, refetch } = useData('massive/option-chain', { underlying, expiration, strike_min: state.strike_min, strike_max: state.strike_max, limit: 1000, }) const rows = (data ?? []) as Opt[] const spot = rows.find((r) => typeof r.underlying_price === 'number')?.underlying_price ?? null const ranged = state.strike_min !== undefined || state.strike_max !== undefined const byStrike = new Map() for (const r of rows) { if (!ranged && spot !== null && Math.abs(r.strike - spot) > spot * 0.1) continue const e = byStrike.get(r.strike) ?? {} e[r.type === 'put' ? 'put' : 'call'] = r byStrike.set(r.strike, e) } const strikes = [...byStrike.keys()].sort((a, b) => a - b) usePublish(panel, { underlying, expiration, strikes: strikes.length, underlyingPrice: spot, error: error ?? null }) return (
{underlying} {expiration} · spot {f(spot)} {!ranged && spot !== null ? ' · strikes within 10%' : ''}
{error ?

{error}

: null} {!data && !error ?

Loading…

: null} {data && !strikes.length ?

No contracts for that expiration and strike range.

: null} {strikes.length ? ( {COLS.map((c) => )} )} {strikes.map((k) => { const { call, put } = byStrike.get(k)! const callItm = spot !== null && k < spot const putItm = spot !== null && k > spot return ( {cells(call).map((v, i) => )} {cells(put).map((v, i) => )} ) })}
Calls Strike Puts
{c} {COLS.map((c) => {c}
{v}{k.toFixed(2)}{v}
) : null}
) }