gloomberb 0.16.3 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -1
- package/src/api-client/askg.ts +43 -0
- package/src/api-client/auth.ts +15 -0
- package/src/api-client/beneficial-owners.ts +4 -0
- package/src/api-client/chat.ts +19 -0
- package/src/api-client/company-kpis.ts +14 -2
- package/src/api-client/data.ts +34 -16
- package/src/api-client/index.ts +12 -0
- package/src/api-client/normalizers.ts +27 -0
- package/src/api-client/paths.ts +88 -23
- package/src/api-client/request.ts +2 -1
- package/src/api-client/research-activity.ts +31 -0
- package/src/api-client/socket.ts +39 -8
- package/src/api-client/types.ts +57 -1
- package/src/api-client/upgrade-experiment.ts +90 -0
- package/src/app/global-shortcuts.ts +11 -1
- package/src/app/keybindings/actions.ts +10 -0
- package/src/app/pane-runtime/index.tsx +8 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
- package/src/app/pane-runtime/plugin-bindings.ts +65 -25
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
- package/src/app/runtime/desktop-deeplink.ts +5 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +13 -7
- package/src/architecture/core-builtin-allowlist.json +70 -0
- package/src/brokers/signed-in/adapter.ts +33 -2
- package/src/brokers/signed-in/client.ts +10 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
- package/src/brokers/signed-in/sign-in.ts +62 -5
- package/src/capabilities/registry.ts +8 -1
- package/src/capabilities/types.ts +2 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +359 -99
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +164 -26
- package/src/cli/commands/search.ts +29 -8
- package/src/cli/commands/system.ts +99 -19
- package/src/cli/commands/themes.ts +64 -0
- package/src/cli/commands/ticker.ts +62 -30
- package/src/cli/desktop-pane-shot.ts +97 -16
- package/src/cli/errors.ts +25 -2
- package/src/cli/freshness.ts +7 -4
- package/src/cli/fx-pair.ts +57 -0
- package/src/cli/help.ts +14 -1
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +9 -6
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +50 -6
- package/src/cli/pane-functions/capabilities.ts +8 -2
- package/src/cli/pane-functions/catalog.ts +13 -2
- package/src/cli/pane-functions/data.ts +36 -5
- package/src/cli/pane-functions/dom.ts +21 -13
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +67 -22
- package/src/cli/pane-functions/headless.ts +5 -4
- package/src/cli/pane-functions/index.ts +3 -0
- package/src/cli/pane-functions/market-session.ts +133 -0
- package/src/cli/pane-functions/options.ts +15 -4
- package/src/cli/pane-functions/report-notices.ts +63 -0
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +4 -6
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +105 -23
- package/src/cli/registry.ts +2 -1
- package/src/cli/result.ts +50 -13
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scroll-truncation.ts +34 -0
- package/src/components/chart/composite/composite-chart.tsx +43 -4
- package/src/components/chart/composite/legend.tsx +39 -6
- package/src/components/chart/composite/rasterizer.ts +64 -4
- package/src/components/chart/composite/series-cues.ts +71 -0
- package/src/components/chart/composite/text-renderer.ts +16 -8
- package/src/components/chart/composite/types.ts +3 -0
- package/src/components/command-bar/commands/direct/index.ts +9 -1
- package/src/components/command-bar/commands/plugin/items.ts +3 -1
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/routes/root/command-items.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/results.ts +40 -10
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/root/search-providers.ts +97 -14
- package/src/components/command-bar/surface/index.tsx +18 -4
- package/src/components/command-bar/theme-picker.tsx +48 -13
- package/src/components/command-bar/workflow/ops.ts +5 -3
- package/src/components/data-table/view.tsx +28 -9
- package/src/components/layout/floating-pane.tsx +6 -2
- package/src/components/layout/market-summary.ts +23 -9
- package/src/components/layout/pane/content.tsx +46 -29
- package/src/components/layout/pane/cycle-order.ts +2 -11
- package/src/components/layout/pane/header.tsx +2 -1
- package/src/components/layout/pane/title.ts +1 -1
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/active-drag.ts +1 -1
- package/src/components/layout/shell/drag/index.ts +4 -0
- package/src/components/layout/shell/drag/live.ts +76 -5
- package/src/components/layout/shell/drag/runtime.ts +8 -8
- package/src/components/layout/shell/index.tsx +47 -6
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +51 -10
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/shortcuts.ts +5 -2
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-text.tsx +9 -5
- package/src/components/metric-treemap/heat-surface.tsx +27 -8
- package/src/components/metric-treemap/heat-tiles.ts +48 -17
- package/src/components/onboarding/onboarding-frame.tsx +33 -10
- package/src/components/onboarding/onboarding-wizard.tsx +49 -40
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
- package/src/components/ui/button.tsx +10 -0
- package/src/components/ui/data-table/index.tsx +38 -0
- package/src/components/ui/data-table/types.ts +8 -1
- package/src/components/ui/data-table/visible-range.ts +2 -0
- package/src/components/ui/icon.tsx +11 -2
- package/src/components/ui/index.ts +1 -1
- package/src/core/connection-health.ts +1 -1
- package/src/core/state/app/layout-reducer.ts +89 -3
- package/src/core/state/app/state.ts +8 -5
- package/src/core/state/app/types.ts +13 -2
- package/src/data/config/layout.ts +55 -21
- package/src/data/config/store/fold-panes.ts +149 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/node.ts +27 -6
- package/src/data/config/store/normalize.ts +37 -3
- package/src/i18n/es.ts +14 -1
- package/src/i18n/ja.ts +14 -1
- package/src/i18n/ko.ts +14 -1
- package/src/i18n/zh-cn.ts +14 -1
- package/src/i18n/zh-tw.ts +14 -1
- package/src/layout/desks.ts +1 -2
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/entries.ts +7 -1
- package/src/market-data/coordinator/quotes.ts +2 -2
- package/src/market-data/market/freshness.ts +118 -18
- package/src/market-data/market/status.ts +40 -0
- package/src/market-data/options-alternatives.ts +69 -0
- package/src/market-data/quotes/contributions.ts +19 -2
- package/src/market-data/quotes/metadata.ts +16 -0
- package/src/market-data/quotes/screener-live-quotes.ts +56 -5
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/navigation.ts +1 -1
- package/src/plugins/builtin/account-management/pane.tsx +31 -4
- package/src/plugins/builtin/alerts/quotes.ts +3 -0
- package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
- package/src/plugins/builtin/analytics/risk-model.ts +37 -8
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
- package/src/plugins/builtin/backtest/pane.tsx +1 -1
- package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/broker-manager/model.ts +8 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +5 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
- package/src/plugins/builtin/chat/channels.ts +34 -36
- package/src/plugins/builtin/chat/command-bar.ts +138 -0
- package/src/plugins/builtin/chat/composer-commands.ts +21 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
- package/src/plugins/builtin/chat/content/footer.tsx +5 -3
- package/src/plugins/builtin/chat/content/index.tsx +21 -1
- package/src/plugins/builtin/chat/content/mentions.ts +2 -1
- package/src/plugins/builtin/chat/content/user-map.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +48 -0
- package/src/plugins/builtin/chat/conversations.ts +256 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +12 -6
- package/src/plugins/builtin/chat/ghost-user.ts +14 -0
- package/src/plugins/builtin/chat/index.tsx +40 -2
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
- package/src/plugins/builtin/chat/message/header.tsx +24 -15
- package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
- package/src/plugins/builtin/chat/message/render-state.ts +3 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +32 -2
- package/src/plugins/builtin/chat/pane.tsx +8 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
- package/src/plugins/builtin/cloud/askg/model.ts +129 -70
- package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
- package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
- package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +36 -1
- package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
- package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
- package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
- package/src/plugins/builtin/comment-letters/client.ts +1 -3
- package/src/plugins/builtin/company-kpis/client.ts +23 -8
- package/src/plugins/builtin/company-kpis/headless.ts +3 -1
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
- package/src/plugins/builtin/composite-plugins.ts +107 -64
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
- package/src/plugins/builtin/credit-boards/index.tsx +21 -21
- package/src/plugins/builtin/credit-boards/model.ts +0 -1
- package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
- package/src/plugins/builtin/debt-maturities/client.ts +37 -9
- package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +28 -7
- package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +42 -17
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/index.tsx +68 -22
- package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/view.ts +17 -0
- package/src/plugins/builtin/executives/data.ts +17 -6
- package/src/plugins/builtin/executives/pane.tsx +16 -7
- package/src/plugins/builtin/filing-events/pane.tsx +22 -5
- package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
- package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
- package/src/plugins/builtin/gpu/headless.ts +9 -3
- package/src/plugins/builtin/gpu/model.ts +14 -5
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/headless.ts +6 -1
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/insider/index.tsx +4 -4
- package/src/plugins/builtin/iv-history/charts.tsx +21 -7
- package/src/plugins/builtin/iv-history/client.ts +12 -2
- package/src/plugins/builtin/iv-history/format.ts +33 -2
- package/src/plugins/builtin/iv-history/headless.ts +22 -10
- package/src/plugins/builtin/iv-history/index.tsx +3 -3
- package/src/plugins/builtin/iv-history/model.ts +61 -13
- package/src/plugins/builtin/iv-history/pane.tsx +11 -3
- package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
- package/src/plugins/builtin/jobs/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
- package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
- package/src/plugins/builtin/layout-manager/index.tsx +9 -1
- package/src/plugins/builtin/macro-day/client.ts +24 -0
- package/src/plugins/builtin/macro-day/headless.ts +11 -8
- package/src/plugins/builtin/macro-day/index.tsx +3 -0
- package/src/plugins/builtin/macro-day/model.ts +11 -6
- package/src/plugins/builtin/macro-day/pane.tsx +12 -8
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +62 -44
- package/src/plugins/builtin/market-heatmap/data.ts +25 -5
- package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
- package/src/plugins/builtin/market-heatmap/model.ts +94 -3
- package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
- package/src/plugins/builtin/market-movers/headless.ts +31 -4
- package/src/plugins/builtin/market-movers/index.tsx +8 -1
- package/src/plugins/builtin/market-movers/model.ts +71 -17
- package/src/plugins/builtin/market-movers/table.ts +50 -23
- package/src/plugins/builtin/news/headless.ts +9 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
- package/src/plugins/builtin/openfda/client.ts +1 -1
- package/src/plugins/builtin/options/carry.ts +95 -0
- package/src/plugins/builtin/options/index.tsx +58 -5
- package/src/plugins/builtin/options/strike-window.ts +183 -0
- package/src/plugins/builtin/options/table.ts +55 -3
- package/src/plugins/builtin/options/types.ts +8 -1
- package/src/plugins/builtin/options/view.tsx +161 -12
- package/src/plugins/builtin/options-positioning/headless.ts +4 -2
- package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +73 -17
- package/src/plugins/builtin/options-scenario/headless.ts +71 -24
- package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
- package/src/plugins/builtin/options-scenario/index.tsx +9 -2
- package/src/plugins/builtin/options-scenario/model.ts +33 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
- package/src/plugins/builtin/pe-band/model.ts +2 -2
- package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
- package/src/plugins/builtin/perps/equity-row.tsx +8 -5
- package/src/plugins/builtin/perps/model.ts +16 -5
- package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
- package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
- package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
- package/src/plugins/builtin/plugin-module.ts +87 -4
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
- package/src/plugins/builtin/realized-vol/headless.ts +3 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
- package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
- package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
- package/src/plugins/builtin/risk-factors/data.ts +23 -9
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/seasonality/pane.tsx +1 -1
- package/src/plugins/builtin/sec/index.tsx +5 -5
- package/src/plugins/builtin/sectors/client.ts +10 -2
- package/src/plugins/builtin/sectors/headless.ts +35 -6
- package/src/plugins/builtin/sectors/index.tsx +18 -1
- package/src/plugins/builtin/sectors/sector-model.ts +150 -41
- package/src/plugins/builtin/shared/command-tab.ts +51 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
- package/src/plugins/builtin/shared/members-menu.tsx +4 -4
- package/src/plugins/builtin/shared/report-freshness.ts +50 -2
- package/src/plugins/builtin/shared/ticker-request.ts +25 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
- package/src/plugins/builtin/shared/volatility/options.ts +1 -46
- package/src/plugins/builtin/short-interest/index.tsx +19 -4
- package/src/plugins/builtin/short-interest/surface.tsx +4 -4
- package/src/plugins/builtin/short-volume/index.tsx +1 -10
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
- package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
- package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
- package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
- package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
- package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
- package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
- package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
- package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
- package/src/plugins/builtin/vol-surface/client.ts +6 -2
- package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
- package/src/plugins/builtin/vol-surface/headless.ts +124 -20
- package/src/plugins/builtin/vol-surface/model.ts +23 -11
- package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
- package/src/plugins/builtin/vol-surface/tables.ts +124 -0
- package/src/plugins/builtin/world-indices/client.ts +22 -4
- package/src/plugins/builtin/world-indices/headless.ts +54 -27
- package/src/plugins/builtin/world-indices/model.ts +4 -2
- package/src/plugins/builtin/world-indices/table.tsx +13 -2
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +1 -1
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/catalog-backend.ts +16 -9
- package/src/plugins/catalog-browser.ts +127 -63
- package/src/plugins/catalog-manifest.ts +73 -40
- package/src/plugins/catalog-ui.ts +26 -6
- package/src/plugins/extracted-plugins.ts +7 -1
- package/src/plugins/ownership.ts +150 -7
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/registry/host-actions.ts +16 -0
- package/src/plugins/registry/index.ts +84 -24
- package/src/plugins/registry/plugin-state.ts +25 -1
- package/src/plugins/runtime/context.tsx +29 -0
- package/src/react/input.ts +48 -1
- package/src/remote/app-host.tsx +5 -0
- package/src/remote/controller.ts +3 -53
- package/src/remote/matching.ts +69 -0
- package/src/remote/schema.ts +1 -1
- package/src/remote/semantic-tree.tsx +14 -0
- package/src/remote/types.ts +2 -0
- package/src/renderers/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +1 -1
- package/src/renderers/dom/host/drag-shield.ts +17 -6
- package/src/renderers/dom/host/live-frame.ts +10 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +13 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +11 -5
- package/src/sources/provider-router/financials.ts +42 -15
- package/src/sources/provider-router/history.ts +27 -11
- package/src/sources/provider-router/index.ts +1 -1
- package/src/sources/provider-router/primary.ts +23 -5
- package/src/sources/sec-edgar.ts +68 -8
- package/src/sync/core-contributors.ts +4 -3
- package/src/test-support/cli-context.ts +2 -1
- package/src/test-support/data-provider.ts +34 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/themes.ts +149 -0
- package/src/tickers/search/index.ts +22 -2
- package/src/tickers/search/ranking.ts +45 -1
- package/src/tickers/search/types.ts +3 -1
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/headless.ts +11 -0
- package/src/types/instrument.ts +8 -0
- package/src/types/plugin.ts +49 -3
- package/src/ui/host.tsx +8 -4
- package/src/ui/index.tsx +2 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history.ts +39 -6
- package/src/utils/rpc-timeout-error.ts +6 -2
- package/src/utils/sec-eps-basis.ts +159 -30
- package/src/utils/sec.ts +38 -34
- package/src/utils/throttled-fetch.ts +51 -22
|
@@ -5,10 +5,11 @@ import type { QueryEntry } from "../../../market-data/result-types";
|
|
|
5
5
|
import type { DataProvider } from "../../../types/data-provider";
|
|
6
6
|
import type { OptionsChain, Quote, TickerFinancials } from "../../../types/financials";
|
|
7
7
|
import { canonicalExchange, parsePublicTickerKey } from "../../../utils/exchanges";
|
|
8
|
+
import { expiryIsoDate, findListedExpiry, missingExpiryText } from "../../../utils/option-expiry";
|
|
8
9
|
import { surfaceTreasuryRate } from "../vol-surface/model";
|
|
9
10
|
import { daysToExpiryFrom, extractImpliedForward, optionMid, solveImpliedVolatility } from "../shared/volatility";
|
|
10
11
|
import type { YieldPoint } from "../yield-curve/treasury-data";
|
|
11
|
-
import { parseLegs, validatePosition, type ScenarioControls, type ScenarioLeg, type ScenarioPosition } from "./model";
|
|
12
|
+
import { parseLegs, scenarioCurrency, validatePosition, type ScenarioControls, type ScenarioLeg, type ScenarioPosition } from "./model";
|
|
12
13
|
import { abortable, abortError } from "../../../utils/async-deadline";
|
|
13
14
|
import { errorMessage } from "../../../utils/errors";
|
|
14
15
|
|
|
@@ -26,6 +27,8 @@ export interface ScenarioMarketSnapshot {
|
|
|
26
27
|
underlyingQuote: Quote | null;
|
|
27
28
|
rateAsOf: string[];
|
|
28
29
|
warnings: string[];
|
|
30
|
+
/** Why the requested expiry has no chain, naming the listed dates nearest it. */
|
|
31
|
+
missingExpiry?: string;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export interface ScenarioLoaderDependencies {
|
|
@@ -99,15 +102,31 @@ export async function loadScenarioMarket(
|
|
|
99
102
|
const validDividend = symbolMatches(financialsSymbol, symbol) && !fundamentals?.stale && !financialsEntry?.error
|
|
100
103
|
&& typeof fundamentals?.dividendYield === "number" && Number.isFinite(fundamentals.dividendYield) && fundamentals.dividendYield >= 0;
|
|
101
104
|
if (!validDividend) warnings.push("Dividend yield unavailable; supply an explicit assumption");
|
|
102
|
-
|
|
105
|
+
let chainEntry = entry(chainResult, "Options chain");
|
|
103
106
|
let chain = chainEntry ? resolveEntryValue(chainEntry) : null;
|
|
104
107
|
if (chain && !symbolMatches(chain.underlyingSymbol, symbol)) {
|
|
105
108
|
warnings.push("Options chain does not match the requested ticker"); chain = null;
|
|
106
109
|
}
|
|
107
110
|
const expirationDates = [...new Set((chain?.expirationDates ?? []).filter((expiration) =>
|
|
108
111
|
positive(expiration) && daysToExpiryFrom(expiration, now) > 0))].sort((a, b) => a - b);
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
// A typed date is that day's UTC midnight; the chain's own stamp for the
|
|
113
|
+
// expiry is matched by calendar date, as OMON matches it.
|
|
114
|
+
const listedExpiration = request.expiration == null ? undefined : findListedExpiry(request.expiration, expirationDates);
|
|
115
|
+
const onExpiry = (value: OptionsChain, expiration: number) => [...value.calls, ...value.puts]
|
|
116
|
+
.every((contract) => expiryIsoDate(contract.expiration) === expiryIsoDate(expiration));
|
|
117
|
+
if (chain && listedExpiration != null && listedExpiration !== request.expiration && !chain.calls.length && !chain.puts.length) {
|
|
118
|
+
// A source that keys chains by its exact stamp answered the midnight request empty.
|
|
119
|
+
chainEntry = await abortable(dependencies.loadOptions({ instrument: request.instrument, expirationDate: listedExpiration }, options)
|
|
120
|
+
.catch((error: unknown) => { warnings.push(`Options chain: ${errorMessage(error)}`); return null; }), request.signal, CANCELLED);
|
|
121
|
+
if (request.signal?.aborted) throw abortError(CANCELLED);
|
|
122
|
+
if (chainEntry?.error) warnings.push(`Options chain: ${chainEntry.error.message}`);
|
|
123
|
+
chain = chainEntry ? resolveEntryValue(chainEntry) : null;
|
|
124
|
+
}
|
|
125
|
+
let missingExpiry: string | undefined;
|
|
126
|
+
if (chain && request.expiration != null && listedExpiration == null) {
|
|
127
|
+
missingExpiry = sentence(missingExpiryText(request.expiration, expirationDates, symbol));
|
|
128
|
+
warnings.push(missingExpiry); chain = null;
|
|
129
|
+
} else if (chain && listedExpiration != null && !onExpiry(chain, listedExpiration)) {
|
|
111
130
|
warnings.push("Selected expiration unavailable in the returned options chain"); chain = null;
|
|
112
131
|
}
|
|
113
132
|
if (!chain) warnings.push("Options chain unavailable");
|
|
@@ -117,15 +136,22 @@ export async function loadScenarioMarket(
|
|
|
117
136
|
if (curveResult.status === "rejected") warnings.push(`Treasury: ${errorMessage(curveResult.reason)}`);
|
|
118
137
|
const rate = surfaceTreasuryRate(curve, rateExpiration == null ? NaN : daysToExpiryFrom(rateExpiration, now) / 365);
|
|
119
138
|
warnings.push(...rate.warnings);
|
|
139
|
+
// A currency does not go stale with its quote: the listing's quote, its snapshot, or its contracts name it.
|
|
140
|
+
const currency = [symbolMatches(quote?.symbol, symbol) ? quote?.currency : undefined,
|
|
141
|
+
symbolMatches(financialsSymbol, symbol) ? financials?.quote?.currency : undefined,
|
|
142
|
+
chain?.calls[0]?.currency, chain?.puts[0]?.currency].find((value) => !!value?.trim())?.trim().toUpperCase() ?? "";
|
|
120
143
|
return {
|
|
121
|
-
symbol, exchange: request.instrument.exchange, spot: quoteCurrent ? quote!.price : null, currency
|
|
144
|
+
symbol, exchange: request.instrument.exchange, spot: quoteCurrent ? quote!.price : null, currency,
|
|
122
145
|
asOf: quoteCurrent && positive(quote!.lastUpdated) ? quote!.lastUpdated : now,
|
|
123
146
|
chain, expirationDates, rate: rate.rate, dividendYield: validDividend ? fundamentals!.dividendYield! : null,
|
|
124
147
|
source: quoteCurrent ? quoteEntry?.source ?? quote!.providerId ?? null : null,
|
|
125
148
|
underlyingQuote: quoteCurrent ? quote : null, rateAsOf: rate.asOf, warnings: [...new Set(warnings)],
|
|
149
|
+
...(missingExpiry ? { missingExpiry } : {}),
|
|
126
150
|
};
|
|
127
151
|
}
|
|
128
152
|
|
|
153
|
+
const sentence = (text: string) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
154
|
+
|
|
129
155
|
function supplied(value: unknown): boolean { return value != null && value !== ""; }
|
|
130
156
|
|
|
131
157
|
function numericSetting(settings: Record<string, unknown>, key: string): number | undefined {
|
|
@@ -175,13 +201,18 @@ export function scenarioPositionFromSettings(
|
|
|
175
201
|
const dividendYield = dividendValue == null ? market?.dividendYield : dividendValue / 100;
|
|
176
202
|
const legText = typeof settings.legs === "string" ? settings.legs : "";
|
|
177
203
|
let legs = legText.trim() ? parseLegs(legText) : [];
|
|
204
|
+
const strike = numericSetting(settings, "strike");
|
|
205
|
+
if (strike != null && !(strike > 0)) throw new Error("strike must be positive");
|
|
206
|
+
if (strike != null && !supplied(settings.strategy)) throw new Error("strike picks the strike of --strategy; add --strategy put, vertical or straddle");
|
|
178
207
|
if (!legs.length && supplied(settings.strategy)) {
|
|
179
|
-
if (settings.strategy
|
|
180
|
-
if (!market?.chain || market.warnings.includes("Options chain is stale"))
|
|
208
|
+
if (!isScenarioStrategy(settings.strategy)) throw new Error("strategy must be vertical, straddle or put");
|
|
209
|
+
if (!market?.chain || market.warnings.includes("Options chain is stale")) {
|
|
210
|
+
throw new Error(market?.missingExpiry ?? "A current options chain is required to seed a strategy");
|
|
211
|
+
}
|
|
181
212
|
if (spot == null) throw new Error("A current underlying price or explicit --spot is required");
|
|
182
213
|
// Leg IVs are solved at the market's own spot and time, never at a what-if override.
|
|
183
214
|
const pricing = market.spot != null && rate != null ? { spot: market.spot, asOf: market.asOf, rate } : null;
|
|
184
|
-
const seeded = scenarioStrategyLegs(market.chain, spot, settings.strategy, pricing);
|
|
215
|
+
const seeded = scenarioStrategyLegs(market.chain, spot, settings.strategy, pricing, strike);
|
|
185
216
|
legs = seeded.legs;
|
|
186
217
|
// Mids priced off the parity forward are worth their cost only at the spot
|
|
187
218
|
// that forward implies on the scenario's own rate and dividend yield. The
|
|
@@ -198,7 +229,7 @@ export function scenarioPositionFromSettings(
|
|
|
198
229
|
if (dividendYield == null) throw new Error("Dividend yield unavailable; supply --dividend-yield as an annual percentage");
|
|
199
230
|
const position: ScenarioPosition = {
|
|
200
231
|
symbol, exchange: exchange ?? market?.exchange,
|
|
201
|
-
currency:
|
|
232
|
+
currency: scenarioCurrency(settings.currency, market?.currency, exchange ?? market?.exchange),
|
|
202
233
|
spot, rate, dividendYield, asOf: dateSetting(settings, "asOf") ?? market?.asOf ?? Date.now(), legs,
|
|
203
234
|
};
|
|
204
235
|
const error = validatePosition(position);
|
|
@@ -210,6 +241,9 @@ export function scenarioPositionFromSettings(
|
|
|
210
241
|
return position;
|
|
211
242
|
}
|
|
212
243
|
|
|
244
|
+
type ScenarioStrategy = "vertical" | "straddle" | "put";
|
|
245
|
+
const isScenarioStrategy = (value: unknown): value is ScenarioStrategy => value === "vertical" || value === "straddle" || value === "put";
|
|
246
|
+
|
|
213
247
|
interface StrategyPricing { spot: number; asOf: number; rate: number }
|
|
214
248
|
|
|
215
249
|
const usableVolatility = (value: unknown): value is number => positive(value) && value <= 5;
|
|
@@ -230,9 +264,13 @@ interface StrategyParity { forward: number; daysToExpiry: number }
|
|
|
230
264
|
* keeps a usable provider IV, and without one it is not eligible. `parity` is
|
|
231
265
|
* the forward a seeded leg was solved against, so the caller can start the
|
|
232
266
|
* scenario at the spot it implies.
|
|
267
|
+
*
|
|
268
|
+
* Each strategy is anchored at the strike nearest spot, or at `strike` when
|
|
269
|
+
* one is given: a long put (a protective put), a call vertical from the
|
|
270
|
+
* anchor to the next listed strike up, or a straddle at the anchor.
|
|
233
271
|
*/
|
|
234
272
|
function scenarioStrategyLegs(
|
|
235
|
-
chain: OptionsChain, spot: number, strategy:
|
|
273
|
+
chain: OptionsChain, spot: number, strategy: ScenarioStrategy, pricing: StrategyPricing | null, strike?: number,
|
|
236
274
|
): { legs: ScenarioLeg[]; parity: StrategyParity | null } {
|
|
237
275
|
const parity = new Map<number, StrategyParity | null>();
|
|
238
276
|
const parityFor = (expiration: number) => {
|
|
@@ -256,12 +294,30 @@ function scenarioStrategyLegs(
|
|
|
256
294
|
if (usableVolatility(volatility)) return [{ contract, mid, volatility, solved: true }];
|
|
257
295
|
return usableVolatility(contract.impliedVolatility) ? [{ contract, mid, volatility: contract.impliedVolatility, solved: false }] : [];
|
|
258
296
|
});
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
297
|
+
const nearest = (a: { contract: { strike: number } }, b: { contract: { strike: number } }) =>
|
|
298
|
+
Math.abs(a.contract.strike - spot) - Math.abs(b.contract.strike - spot);
|
|
299
|
+
const anchored = ({ contract }: { contract: { strike: number } }) => strike == null || Math.abs(contract.strike - strike) < 1e-9;
|
|
300
|
+
const calls = quoted(chain.calls, "call").sort(nearest);
|
|
301
|
+
const puts = quoted(chain.puts, "put").sort(nearest);
|
|
302
|
+
const missing = (candidates: { contract: { strike: number } }[]) => {
|
|
303
|
+
if (strike == null) return new Error("The selected chain has no complete quoted strategy with usable IV");
|
|
304
|
+
const listed = [...new Set(candidates.map(({ contract }) => contract.strike))]
|
|
305
|
+
.sort((a, b) => Math.abs(a - strike) - Math.abs(b - strike)).slice(0, 4).sort((a, b) => a - b);
|
|
306
|
+
return new Error(`The selected chain has no complete quoted ${strategy} at strike ${strike} with usable IV`
|
|
307
|
+
+ (listed.length ? `; nearest quoted strikes: ${listed.join(", ")}` : ""));
|
|
308
|
+
};
|
|
309
|
+
if (strategy === "put") {
|
|
310
|
+
const put = puts.find(anchored);
|
|
311
|
+
if (!put) throw missing(puts);
|
|
312
|
+
return { legs: [{ id: put.contract.contractSymbol, side: "put", quantity: 1, strike: put.contract.strike,
|
|
313
|
+
expiration: put.contract.expiration, price: put.mid, priceSource: "mid", volatility: put.volatility, multiplier: 100,
|
|
314
|
+
...(put.solved ? { volatilitySource: "mid" as const } : {}) }],
|
|
315
|
+
parity: put.solved ? parityFor(put.contract.expiration) : null };
|
|
316
|
+
}
|
|
317
|
+
const first = calls.find((entry) => anchored(entry) && (strategy === "vertical"
|
|
318
|
+
? calls.some(({ contract: other }) => other.expiration === entry.contract.expiration && other.strike > entry.contract.strike)
|
|
319
|
+
: puts.some(({ contract: put }) => put.expiration === entry.contract.expiration && put.strike === entry.contract.strike)));
|
|
320
|
+
if (!first) throw missing(calls);
|
|
265
321
|
const second = strategy === "vertical"
|
|
266
322
|
? calls.filter(({ contract }) => contract.expiration === first.contract.expiration && contract.strike > first.contract.strike)
|
|
267
323
|
.sort((a, b) => a.contract.strike - b.contract.strike)[0]!
|
|
@@ -270,7 +326,7 @@ function scenarioStrategyLegs(
|
|
|
270
326
|
const legs = [first, second].map(({ contract, mid, volatility, solved }, index): ScenarioLeg => ({
|
|
271
327
|
id: contract.contractSymbol, side: index === 1 && strategy === "straddle" ? "put" : "call",
|
|
272
328
|
quantity: index === 1 && strategy === "vertical" ? -1 : 1, strike: contract.strike,
|
|
273
|
-
expiration: contract.expiration, price: mid, volatility, multiplier: 100,
|
|
329
|
+
expiration: contract.expiration, price: mid, priceSource: "mid", volatility, multiplier: 100,
|
|
274
330
|
...(solved ? { volatilitySource: "mid" as const } : {}),
|
|
275
331
|
}));
|
|
276
332
|
return { legs, parity: first.solved || second.solved ? parityFor(first.contract.expiration) : null };
|
|
@@ -1,38 +1,66 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
3
|
+
import {
|
|
4
|
+
expirationOptionSeconds, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER, readOptionExpiration,
|
|
5
|
+
} from "../../../utils/option-expiry";
|
|
3
6
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
4
7
|
import {
|
|
5
8
|
createScenarioDependencies, loadScenarioMarket, scenarioControlsFromSettings, scenarioPositionFromSettings,
|
|
6
9
|
type ScenarioMarketSnapshot,
|
|
7
10
|
} from "./client";
|
|
8
|
-
import { buildScenario, parseLegs } from "./model";
|
|
11
|
+
import { buildScenario, currencyLabel, parseLegs, scenarioValueUnit } from "./model";
|
|
12
|
+
import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget } from "./hedge";
|
|
9
13
|
|
|
10
14
|
// Float noise at a flat origin or a breakeven prints as 0.00, not -0.00.
|
|
11
15
|
const money = (value: unknown) => typeof value === "number" ? (Math.abs(value) < 0.005 ? 0 : value).toFixed(2) : "--";
|
|
16
|
+
const percent = (value: unknown) => typeof value === "number" ? `${(value * 100).toFixed(2)}%` : "--";
|
|
12
17
|
const supplied = (value: unknown) => value != null && value !== "";
|
|
18
|
+
// A seeded entry is a quote midpoint, often to the half cent (5.495), so it keeps up to four decimals.
|
|
19
|
+
const premium = (value: unknown) => typeof value === "number"
|
|
20
|
+
? value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 4, useGrouping: false }) : "--";
|
|
21
|
+
/** Entry prices and spot are quoted per share of the underlying. */
|
|
22
|
+
const PRICE_UNIT = "per share";
|
|
23
|
+
/** The Valuation entries, in model order, with the unit each is in. */
|
|
24
|
+
const VALUATION_LABELS: Record<string, (currency: string) => string> = {
|
|
25
|
+
price: () => "Value", pnl: () => "P&L", delta: () => "Delta (shares)", gamma: () => "Gamma (shares per 1.00 move)",
|
|
26
|
+
thetaPerDay: (currency) => `Theta (${currency} per day)`, vegaPerPoint: (currency) => `Vega (${currency} per vol point)`,
|
|
27
|
+
rhoPerPoint: (currency) => `Rho (${currency} per rate point)`,
|
|
28
|
+
};
|
|
13
29
|
|
|
14
30
|
export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
15
31
|
shape: "bundle", argument: { kind: "ticker", description: "Underlying ticker" },
|
|
16
32
|
describe: (args) => `OSA ${args.symbols[0] ?? ""}`,
|
|
33
|
+
description: "A typed or chain-seeded options position valued across spot, date and volatility: its legs, value and Greeks, expiry risk and a "
|
|
34
|
+
+ "scenario P&L grid. With --nav and --budget-bps, how many contracts a hedge budget buys, the premium spent, the notional protected and "
|
|
35
|
+
+ "coverage of an equity sleeve.",
|
|
17
36
|
discovery: { screenshotReadiness: "ready", limitations: ["European scenario values do not model early assignment or exercise."] },
|
|
18
37
|
options: [
|
|
19
38
|
{ key: "tab", type: "enum", values: ["payoff", "grid", "legs"].map((value) => ({ value })), defaultValue: "payoff",
|
|
20
39
|
description: "Initial view", pluginState: { pluginId: "ticker-research", key: "activeTabId" } },
|
|
21
|
-
{ key: "legs", type: "string", description: "Semicolon-separated side,strike,YYYY-MM-DD,signed quantity,entry price,IV percent[,multiplier]" },
|
|
22
|
-
{ key: "strategy", type: "enum", values: [{ value: "vertical" }, { value: "straddle" }
|
|
23
|
-
|
|
40
|
+
{ key: "legs", type: "string", description: "Semicolon-separated side,strike,YYYY-MM-DD,signed quantity,entry price per share,IV percent[,multiplier]" },
|
|
41
|
+
{ key: "strategy", type: "enum", values: [{ value: "vertical" }, { value: "straddle" }, { value: "put" }],
|
|
42
|
+
description: "Build a position from current two-sided chain quotes: a call vertical, a straddle, or one long put (a protective put)" },
|
|
43
|
+
{ key: "strike", type: "string", description: "The --strategy strike: the put, the vertical's long call or the straddle. Nearest the money when omitted" },
|
|
44
|
+
{ key: "expiration", type: "string", placeholder: OPTION_EXPIRATION_PLACEHOLDER, example: "--strategy vertical --expiration 2027-01-15",
|
|
45
|
+
description: `Chain expiry for --strategy, as ${OPTION_EXPIRATION_FORMAT}`, normalize: (value) => expirationOptionSeconds(value) },
|
|
24
46
|
{ key: "spot", type: "string", description: "Underlying price override, per share" },
|
|
25
47
|
{ key: "rate", type: "string", description: "Continuously compounded annual risk-free rate, percent" },
|
|
26
48
|
{ key: "dividendYield", aliases: ["dividend-yield"], type: "string", description: "Continuous annual dividend yield, percent" },
|
|
27
|
-
{ key: "currency", type: "string", description: "
|
|
49
|
+
{ key: "currency", type: "string", description: "Currency of the inputs, such as USD; the underlying's when omitted" },
|
|
28
50
|
{ key: "asOf", aliases: ["as-of"], type: "string", description: "Position valuation timestamp: UTC ISO or YYYY-MM-DD" },
|
|
29
51
|
{ key: "date", type: "string", description: "Scenario date: UTC ISO or YYYY-MM-DD" },
|
|
30
52
|
{ key: "volShift", aliases: ["vol-shift"], type: "string", defaultValue: "0", description: "Parallel volatility shift, percentage points" },
|
|
31
53
|
{ key: "spotRange", aliases: ["spot-range"], type: "string", defaultValue: "30", description: "Spot grid range above and below current spot, percent" },
|
|
54
|
+
{ key: "nav", type: "string", description: "Portfolio NAV for a hedge budget, such as 100m, 2.5bn, 250k, 1,000,000 or $100m. Needs --budget-bps" },
|
|
55
|
+
{ key: "budgetBps", aliases: ["budget-bps"], type: "string", example: "--strategy put --strike 700 --expiration 2027-01-15 --nav 100m --budget-bps 50",
|
|
56
|
+
description: "Hedge budget in basis points of --nav (50 is 0.5%): the report says how many of the position it buys, the premium, and the notional protected" },
|
|
57
|
+
{ key: "sleeve", aliases: ["equity-sleeve"], type: "string", description: "Equity sleeve the hedge protects, for coverage = notional / sleeve. Same amount forms as --nav" },
|
|
32
58
|
],
|
|
33
59
|
async load(args, ctx) {
|
|
34
60
|
const symbol = args.symbols[0]!;
|
|
35
61
|
const settings: Record<string, unknown> = { ...ctx.settings, ...args.options, symbol };
|
|
62
|
+
// Bad hedge inputs fail before any market request.
|
|
63
|
+
hedgeInputsFromSettings(settings);
|
|
36
64
|
const suppliedInputs = !!settings.legs && ["spot", "rate", "dividendYield"].every((key) => settings[key] != null && settings[key] !== "");
|
|
37
65
|
let market: ScenarioMarketSnapshot;
|
|
38
66
|
if (suppliedInputs) {
|
|
@@ -42,8 +70,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
42
70
|
} else {
|
|
43
71
|
const instrument = await resolveHeadlessInstrument(ctx, symbol);
|
|
44
72
|
const typedExpiries = typeof settings.legs === "string" && settings.legs.trim() ? parseLegs(settings.legs).map((leg) => leg.expiration) : [];
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
const expiration = readOptionExpiration(args.options.expiration);
|
|
74
|
+
market = await loadScenarioMarket({ instrument, expiration: expiration ?? undefined,
|
|
47
75
|
rateExpiration: typedExpiries.length ? Math.min(...typedExpiries) : undefined, signal: ctx.signal,
|
|
48
76
|
}, createScenarioDependencies(ctx.marketData, ctx.apiClient));
|
|
49
77
|
}
|
|
@@ -51,13 +79,21 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
51
79
|
const position = scenarioPositionFromSettings(settings, market);
|
|
52
80
|
const controls = position ? scenarioControlsFromSettings(settings, position) : null;
|
|
53
81
|
const scenario = position && controls ? buildScenario(position, controls) : null;
|
|
54
|
-
const
|
|
55
|
-
|
|
82
|
+
const hedge = scenarioHedgeBudget(settings, position, market);
|
|
83
|
+
// A typed rate or yield stands in for the source that could not supply one.
|
|
84
|
+
const marketWarnings = market.warnings.filter((warning) => !(supplied(settings.dividendYield) && warning.startsWith("Dividend yield unavailable"))
|
|
85
|
+
&& !(supplied(settings.rate) && warning.startsWith("Treasury")));
|
|
86
|
+
const errors = scenario ? [...new Set([...marketWarnings, ...scenario.warnings])]
|
|
87
|
+
: [...marketWarnings, "No position supplied; add --legs or select --strategy"];
|
|
56
88
|
const midVolatility = !!position?.legs.some((leg) => leg.volatilitySource === "mid");
|
|
57
89
|
// A seeded strategy starts at the spot its quote mids imply; the last print is reported beside it.
|
|
58
90
|
const lastPrint = midVolatility && !supplied(settings.spot) && market.spot != null && money(market.spot) !== money(position!.spot)
|
|
59
91
|
? market.spot : null;
|
|
60
92
|
const chain = market.chain;
|
|
93
|
+
const currency = position ? currencyLabel(position.currency) : null;
|
|
94
|
+
// Value, P&L, risk and the grid add every contract; entry prices and spot are per share.
|
|
95
|
+
const valueUnit = position ? scenarioValueUnit(position) : null;
|
|
96
|
+
const per = `${currency} ${valueUnit}`;
|
|
61
97
|
return {
|
|
62
98
|
freshness: suppliedInputs
|
|
63
99
|
? { source: "Your inputs", status: "not-a-feed", basis: "scenario" }
|
|
@@ -66,33 +102,44 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
66
102
|
sections: scenario ? [
|
|
67
103
|
{ title: "Position", columns: [
|
|
68
104
|
{ key: "side", header: "Side" }, { key: "strike", header: "Strike" }, { key: "expiry", header: "Expiry" },
|
|
69
|
-
{ key: "quantity", header: "
|
|
105
|
+
{ key: "quantity", header: "Contracts" }, { key: "price", header: "Entry/share", format: premium },
|
|
70
106
|
{ key: "volatility", header: "IV", format: (value) => typeof value === "number" ? `${(value * 100).toFixed(2)}%` : "--" },
|
|
71
107
|
...(midVolatility ? [{ key: "volatilityFrom", header: "IV from" }] : []),
|
|
72
108
|
{ key: "multiplier", header: "Multiplier" },
|
|
73
109
|
], rows: position!.legs.map((leg) => ({ ...leg, expiry: new Date(leg.expiration * 1000).toISOString().slice(0, 10),
|
|
74
110
|
...(midVolatility ? { volatilityFrom: leg.volatilitySource === "mid" ? "quote mid" : "provider" } : {}) })) },
|
|
75
|
-
|
|
76
|
-
{ key: "spot", label: "
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{ label: "
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
{ title: "Inputs", entries: [
|
|
112
|
+
{ key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot,
|
|
113
|
+
formatted: `${money(position!.spot)} ${PRICE_UNIT}` },
|
|
114
|
+
...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint, formatted: `${money(lastPrint)} ${PRICE_UNIT}` }] : []),
|
|
115
|
+
{ key: "rate", label: "Rate", value: position!.rate, formatted: percent(position!.rate) },
|
|
116
|
+
{ key: "dividendYield", label: "Dividend yield", value: position!.dividendYield, formatted: percent(position!.dividendYield) },
|
|
117
|
+
{ key: "currency", label: "Currency", value: position!.currency, formatted: currency! },
|
|
118
|
+
] },
|
|
119
|
+
...(hedge ? [{ title: "Hedge budget", entries: hedgeReportEntries(hedge) }] : []),
|
|
120
|
+
{ title: `Valuation (${per})`, entries: Object.entries(scenario.valuation).map(([key, value]) => ({ key,
|
|
121
|
+
label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value) })) },
|
|
122
|
+
{ title: `Expiry risk (${per})`, entries: [
|
|
123
|
+
{ key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens,
|
|
124
|
+
formatted: scenario.expiryRisk.breakevens.map(money).join(", ") || "none" },
|
|
125
|
+
{ key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
|
|
126
|
+
{ key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
|
|
127
|
+
...(scenario.expiryRisk.reason ? [{ key: "limitation", label: "Limitation", value: scenario.expiryRisk.reason }] : []),
|
|
85
128
|
] },
|
|
86
|
-
{ title:
|
|
87
|
-
{ key: "spot", header: "Spot"
|
|
129
|
+
{ title: `Scenario grid (P&L, ${per})`, columns: [
|
|
130
|
+
{ key: "spot", header: "Spot/share", format: money },
|
|
131
|
+
{ key: "move", header: "Move", format: (value) => typeof value === "number" && Number.isFinite(value) ? `${(value * 100).toFixed(1)}%` : "--" },
|
|
88
132
|
...scenario.dates.map((date, index) => ({ key: `date${index}`, header: new Date(date).toISOString().slice(0, 10), format: money })),
|
|
89
133
|
], rows: scenario.grid.map((row) => ({ spot: row.spot, move: row.move,
|
|
90
134
|
...Object.fromEntries(row.values.map((value, index) => [`date${index}`, value])) })) },
|
|
91
135
|
] : [],
|
|
92
136
|
complete: scenario != null && errors.length === 0,
|
|
93
137
|
unavailableSymbols: scenario ? [] : [symbol], errors,
|
|
94
|
-
metadata: { scenario, position, controls, market, inputSource: suppliedInputs ? "user" : "market",
|
|
95
|
-
unit: position?.currency || "currency unspecified",
|
|
138
|
+
metadata: { scenario, position, controls, market, ...(hedge ? { hedge } : {}), inputSource: suppliedInputs ? "user" : "market",
|
|
139
|
+
unit: position?.currency || "currency unspecified",
|
|
140
|
+
// Leg entry prices and spot are per share; value, P&L, risk, Greeks in currency and the grid are for valueUnit.
|
|
141
|
+
...(position ? { units: { currency: position.currency, price: PRICE_UNIT, value: valueUnit } } : {}),
|
|
142
|
+
methodology: "docs/research-data.md#options-scenario-analysis" },
|
|
96
143
|
};
|
|
97
144
|
},
|
|
98
145
|
};
|