from __future__ import annotations from typing import Any, Sequence from nicegui import ui from app.models.option import OptionContract class GreeksTable: """Live Greeks table with simple risk-level color coding.""" def __init__(self, options: Sequence[OptionContract | dict[str, Any]] | None = None) -> None: self.options: Sequence[OptionContract | dict[str, Any]] = options or [] with ui.card().classes( "w-full rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900" ): with ui.row().classes("w-full items-center justify-between"): ui.label("Option Greeks").classes("text-lg font-semibold text-slate-900 dark:text-slate-100") ui.label("Live Risk Snapshot").classes( "rounded-full bg-violet-100 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-violet-700 dark:bg-violet-500/15 dark:text-violet-300" ) self.table_html = ui.html("").classes("w-full") self.set_options(self.options) def set_options(self, options: Sequence[OptionContract | dict[str, Any]]) -> None: self.options = options self.table_html.content = self._render_table() self.table_html.update() def _render_table(self) -> str: rows = [self._row_html(option) for option in self.options] return f"""
{''.join(rows) if rows else self._empty_row()}
Option Delta Gamma Theta Vega Rho
""" def _row_html(self, option: OptionContract | dict[str, Any]) -> str: if isinstance(option, OptionContract): label = f"{option.option_type.upper()} {option.strike:.2f}" greeks = { "delta": option.greeks.delta, "gamma": option.greeks.gamma, "theta": option.greeks.theta, "vega": option.greeks.vega, "rho": option.greeks.rho, } else: label = str(option.get("label") or option.get("symbol") or option.get("name") or "Option") greeks = { greek: float(option.get(greek, option.get("greeks", {}).get(greek, 0.0))) for greek in ("delta", "gamma", "theta", "vega", "rho") } cells = "".join( f'{value:+.4f}' for name, value in greeks.items() ) return ( '' f'{label}' f"{cells}" "" ) @staticmethod def _risk_class(name: str, value: float) -> str: magnitude = abs(value) if name == "gamma": if magnitude >= 0.08: return "text-rose-600 dark:text-rose-400" if magnitude >= 0.04: return "text-amber-600 dark:text-amber-400" return "text-emerald-600 dark:text-emerald-400" if name == "theta": if value <= -0.08: return "text-rose-600 dark:text-rose-400" if value <= -0.03: return "text-amber-600 dark:text-amber-400" return "text-emerald-600 dark:text-emerald-400" if magnitude >= 0.6: return "text-rose-600 dark:text-rose-400" if magnitude >= 0.3: return "text-amber-600 dark:text-amber-400" return "text-emerald-600 dark:text-emerald-400" @staticmethod def _empty_row() -> str: return ( '' "No options selected" "" )