from __future__ import annotations from typing import Any from nicegui import ui from app.components import GreeksTable from app.pages.common import dashboard_page, strategy_catalog from app.services.runtime import get_data_service @ui.page("/options") async def options_page() -> None: chain_data = await get_data_service().get_options_chain("GLD") chain = list(chain_data.get("rows") or [*chain_data.get("calls", []), *chain_data.get("puts", [])]) expiries = list(chain_data.get("expirations") or sorted({row["expiry"] for row in chain})) strike_values = sorted({float(row["strike"]) for row in chain}) selected_expiry = {"value": expiries[0] if expiries else None} strike_range = { "min": strike_values[0] if strike_values else 0.0, "max": strike_values[-1] if strike_values else 0.0, } selected_strategy = {"value": strategy_catalog()[0]["label"]} chosen_contracts: list[dict[str, Any]] = [] with dashboard_page( "Options Chain", "Browse GLD contracts, filter by expiry and strike range, inspect Greeks, and attach contracts to hedge workflows.", "options", ): with ui.row().classes("w-full gap-6 max-lg:flex-col"): with ui.card().classes( "w-full rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900" ): ui.label("Filters").classes("text-lg font-semibold text-slate-900 dark:text-slate-100") expiry_select = ui.select(expiries, value=selected_expiry["value"], label="Expiry").classes("w-full") min_strike = ui.number( "Min strike", value=strike_range["min"], min=strike_values[0] if strike_values else 0.0, max=strike_values[-1] if strike_values else 0.0, step=5, ).classes("w-full") max_strike = ui.number( "Max strike", value=strike_range["max"], min=strike_values[0] if strike_values else 0.0, max=strike_values[-1] if strike_values else 0.0, step=5, ).classes("w-full") strategy_select = ui.select( [item["label"] for item in strategy_catalog()], value=selected_strategy["value"], label="Add to hedge strategy", ).classes("w-full") source_label = f"Source: {chain_data.get('source', 'unknown')}" if chain_data.get("updated_at"): source_label += f" · Updated {chain_data['updated_at']}" ui.label(source_label).classes("text-xs text-slate-500 dark:text-slate-400") if chain_data.get("error"): ui.label(f"Options data unavailable: {chain_data['error']}").classes( "text-xs text-amber-700 dark:text-amber-300" ) selection_card = ui.card().classes( "w-full rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900" ) chain_table = ui.html("").classes("w-full") greeks = GreeksTable([]) def filtered_rows() -> list[dict[str, Any]]: if not selected_expiry["value"]: return [] return [ row for row in chain if row["expiry"] == selected_expiry["value"] and strike_range["min"] <= float(row["strike"]) <= strike_range["max"] ] def render_selection() -> None: selection_card.clear() with selection_card: ui.label("Strategy Integration").classes("text-lg font-semibold text-slate-900 dark:text-slate-100") ui.label(f"Target strategy: {selected_strategy['value']}").classes( "text-sm text-slate-500 dark:text-slate-400" ) if not chosen_contracts: ui.label("No contracts added yet.").classes("text-sm text-slate-500 dark:text-slate-400") return for contract in chosen_contracts[-3:]: ui.label( f"{contract['symbol']} · premium ${float(contract['premium']):.2f} · IV {float(contract.get('impliedVolatility', 0.0)):.1%}" ).classes("text-sm text-slate-600 dark:text-slate-300") def add_to_strategy(contract: dict[str, Any]) -> None: chosen_contracts.append(contract) render_selection() greeks.set_options(chosen_contracts[-6:]) ui.notify( f"Added {contract['symbol']} to {selected_strategy['value']}", color="positive", ) def render_chain() -> None: rows = filtered_rows() chain_table.content = ( """
| Contract | Type | Strike | Bid / Ask | Last | IV | Greeks | Action |
|---|---|---|---|---|---|---|---|
| {row['symbol']} | {row['type'].upper()} | ${float(row['strike']):.2f} | ${float(row['bid']):.2f} / ${float(row['ask']):.2f} | ${float(row.get('lastPrice', row.get('premium', 0.0))):.2f} | {float(row.get('impliedVolatility', 0.0)):.1%} | Δ {float(row.get('delta', 0.0)):+.3f} · Γ {float(row.get('gamma', 0.0)):.3f} · Θ {float(row.get('theta', 0.0)):+.3f} | Use quick-add buttons below |
| No contracts match the current filter. | |||||||