gloomberb 0.16.2 → 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/account-cache.ts +45 -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/command-options.ts +74 -0
- package/src/cli/commands/command-utils.ts +29 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +524 -148
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +223 -42
- 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 +200 -73
- package/src/cli/desktop-pane-shot.ts +100 -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 +40 -7
- package/src/cli/history-rows.ts +187 -15
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +21 -13
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +55 -7
- package/src/cli/pane-functions/capabilities.ts +13 -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 +89 -28
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +71 -24
- package/src/cli/pane-functions/headless.ts +168 -35
- package/src/cli/pane-functions/index.ts +12 -2
- 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 +12 -1
- package/src/cli/pane-functions/resolver.ts +32 -8
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +109 -23
- package/src/cli/registry.ts +8 -4
- package/src/cli/report-tables.ts +460 -0
- package/src/cli/result.ts +63 -16
- 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/export.ts +6 -51
- package/src/components/data-table/view.tsx +31 -10
- 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 +67 -35
- package/src/components/layout/shell/cursor-occlusion.ts +6 -5
- package/src/components/layout/shell/drag/index.ts +16 -7
- package/src/components/layout/shell/drag/live.ts +193 -0
- package/src/components/layout/shell/drag/overlays.tsx +179 -35
- package/src/components/layout/shell/drag/runtime.ts +62 -50
- package/src/components/layout/shell/drag/slide.ts +10 -0
- package/src/components/layout/shell/index.tsx +84 -57
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
- package/src/components/layout/shell/native/surfaces.ts +30 -14
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +461 -180
- 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/terminal-pointer-runtime.ts +18 -18
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
- 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/data/json-persistence.ts +17 -3
- package/src/data/json-storage.ts +2 -0
- package/src/data/memory-resource-store.ts +46 -0
- 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 +156 -11
- 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/freshness.ts +5 -0
- 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/pane-model.ts +10 -6
- 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/attention/pane.tsx +1 -1
- 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/catalysts/detail.tsx +1 -1
- 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-attention/pane.tsx +1 -1
- 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/client.ts +3 -6
- package/src/plugins/builtin/holders/format.ts +31 -1
- package/src/plugins/builtin/holders/headless.ts +32 -6
- package/src/plugins/builtin/holders/pane.tsx +25 -13
- package/src/plugins/builtin/holders/report-header.ts +46 -5
- 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 +121 -45
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
- 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/members/pane.tsx +10 -0
- 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/perps/pane.tsx +1 -1
- 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/account-metrics.ts +16 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
- 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/research/relative-valuation-model.ts +7 -3
- 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/model.ts +2 -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 +260 -0
- 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/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/themes/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
- package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
- 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/basemap-layer.tsx +2 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
- package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -6
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
- package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
- package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
- package/src/plugins/builtin/world-venue-map/model.ts +16 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
- 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/browser/app-services.ts +3 -1
- package/src/renderers/dom/data-table/dom.ts +31 -0
- package/src/renderers/dom/data-table/index.tsx +5 -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 +113 -17
- package/src/renderers/dom/host/drag-shield.ts +35 -0
- package/src/renderers/dom/host/live-frame.ts +62 -0
- package/src/renderers/dom/host/mouse.ts +8 -1
- package/src/renderers/dom/host/scroll-box.tsx +6 -5
- package/src/renderers/dom/host/tabs.tsx +3 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +26 -5
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
- package/src/renderers/electrobun/shared/protocol.ts +12 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/host/native.ts +0 -10
- package/src/renderers/electrobun/view/host/window-move.ts +28 -0
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
- package/src/renderers/electrobun/view/ui-host.tsx +2 -5
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/renderers/opentui/ui-host.tsx +7 -1
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/batches.ts +3 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +13 -7
- package/src/sources/provider-router/financials.ts +106 -16
- 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 +24 -6
- package/src/sources/sec-edgar.ts +68 -8
- package/src/state/pane-scroll-registry.tsx +67 -35
- 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/margin-account.ts +108 -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/resolution.ts +15 -0
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/financials.ts +32 -3
- 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 +16 -0
- package/src/ui/index.tsx +10 -3
- package/src/ui/scroll-box-axis.ts +26 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/currency-units.ts +8 -3
- package/src/utils/depositary-receipt.ts +54 -10
- package/src/utils/display-number.ts +63 -0
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fundamentals.ts +17 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history-integrity.ts +19 -6
- 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
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
formatMaybePercent,
|
|
13
13
|
formatMoneyCompact,
|
|
14
14
|
formatSignedCompact,
|
|
15
|
+
holderValueCurrency,
|
|
15
16
|
resolveHolderOwnershipPercent,
|
|
16
17
|
} from "./format";
|
|
17
18
|
import { buildRows, sortRows } from "./table-model";
|
|
@@ -19,6 +20,8 @@ import type { HolderData } from "../../../types/financials";
|
|
|
19
20
|
import type { HolderColumnId } from "./types";
|
|
20
21
|
import { SEC_FILINGS } from "../shared/report-freshness";
|
|
21
22
|
import {
|
|
23
|
+
holderShareBasisMarker,
|
|
24
|
+
holderShareBasisNote,
|
|
22
25
|
holderValueBasis,
|
|
23
26
|
holdersHeaderLine,
|
|
24
27
|
moneyColumnHeader,
|
|
@@ -28,8 +31,11 @@ import {
|
|
|
28
31
|
|
|
29
32
|
/** Columns a source fills only when it reports the quarter's change. */
|
|
30
33
|
const CHANGE_COLUMN_KEYS = new Set(["changeShares", "changePercent"]);
|
|
34
|
+
/** Shown only when some row is held as depositary receipts, as on a home line abroad. */
|
|
35
|
+
const BASIS_COLUMN_KEY = "shareBasis";
|
|
31
36
|
import type { BeneficialOwnersPayload } from "../../../api-client/beneficial-owners";
|
|
32
37
|
import { fetchBeneficialOwners, type BeneficialOwnersRequest } from "./beneficial-client";
|
|
38
|
+
import { resolveHeadlessIssuerListing } from "../shared/headless-market-data";
|
|
33
39
|
import type { BeneficialColumnId } from "./beneficial-model";
|
|
34
40
|
import {
|
|
35
41
|
BENEFICIAL_REPORT_COLUMNS,
|
|
@@ -60,6 +66,11 @@ const HOLDER_COLUMNS: HeadlessPaneColumn[] = [
|
|
|
60
66
|
align: "right",
|
|
61
67
|
format: (value) => value == null ? "-" : formatCompact(Number(value)),
|
|
62
68
|
},
|
|
69
|
+
{
|
|
70
|
+
key: BASIS_COLUMN_KEY,
|
|
71
|
+
header: "Basis",
|
|
72
|
+
format: (_value, row) => holderShareBasisMarker(row),
|
|
73
|
+
},
|
|
63
74
|
{
|
|
64
75
|
key: "changeShares",
|
|
65
76
|
header: "Chg",
|
|
@@ -126,7 +137,11 @@ export interface HoldersHeadlessDependencies {
|
|
|
126
137
|
|
|
127
138
|
const defaultDependencies: HoldersHeadlessDependencies = {
|
|
128
139
|
loadSnapshot: (symbol, _args, ctx) => loadHolderSnapshot(ctx.marketData, symbol),
|
|
129
|
-
|
|
140
|
+
// A listing abroad names its venue and company, so it never reads a US namesake's owners.
|
|
141
|
+
loadBeneficialOwners: async (symbol, request, ctx) => fetchBeneficialOwners(symbol, {
|
|
142
|
+
...request,
|
|
143
|
+
listing: await resolveHeadlessIssuerListing(ctx, symbol),
|
|
144
|
+
}, {
|
|
130
145
|
client: ctx.apiClient,
|
|
131
146
|
signal: ctx.signal,
|
|
132
147
|
}),
|
|
@@ -238,6 +253,8 @@ export function createHoldersHeadless(
|
|
|
238
253
|
}
|
|
239
254
|
const { data, marketCap } = await dependencies.loadSnapshot(symbol, args, ctx);
|
|
240
255
|
const currency = data.currency ?? "USD";
|
|
256
|
+
// A London line's values can be dollars from the 13F filings; they are labelled and formatted as such.
|
|
257
|
+
const valueCurrency = holderValueCurrency(data, currency)!;
|
|
241
258
|
const sorted = sortRows(buildRows(data), {
|
|
242
259
|
columnId: SORT_COLUMNS[String(args.options.sort)] ?? "value",
|
|
243
260
|
direction: args.options.order === "asc" ? "asc" : "desc",
|
|
@@ -253,19 +270,23 @@ export function createHoldersHeadless(
|
|
|
253
270
|
changePercent: row.changePercent ?? null,
|
|
254
271
|
percentHeld: resolveHolderOwnershipPercent(row, marketCap) ?? null,
|
|
255
272
|
reportDate: row.reportDate ?? null,
|
|
256
|
-
currency,
|
|
273
|
+
currency: valueCurrency,
|
|
274
|
+
shareBasis: row.shareBasis ?? null,
|
|
257
275
|
}));
|
|
258
276
|
const total = data.summary?.institutionsCount ?? null;
|
|
259
277
|
const changeReported = rows.some((row) => row.changeShares != null || row.changePercent != null);
|
|
260
278
|
const reportDate = sharedReportDate(rows);
|
|
261
|
-
const valueBasis = holderValueBasis(reportDate);
|
|
279
|
+
const valueBasis = holderValueBasis(reportDate, data.valueBasis, valueCurrency);
|
|
262
280
|
const positionsBasis = rows.length > 0 ? nonUsHolderCaveat(data.exchange, currency) : null;
|
|
281
|
+
const shareBasisNote = holderShareBasisNote(data, rows);
|
|
282
|
+
const markedRows = rows.some((row) => holderShareBasisMarker(row));
|
|
263
283
|
// The title names the ticker and the closing line carries the as-of date.
|
|
264
284
|
const header = holdersHeaderLine({
|
|
265
285
|
name: data.name,
|
|
266
286
|
symbol: data.symbol && data.symbol !== symbol ? data.symbol : null,
|
|
267
287
|
exchange: data.exchange,
|
|
268
|
-
currency,
|
|
288
|
+
currency: valueCurrency,
|
|
289
|
+
listingCurrency: data.currency,
|
|
269
290
|
shown: rows.length,
|
|
270
291
|
reported: sorted.length,
|
|
271
292
|
total,
|
|
@@ -278,13 +299,18 @@ export function createHoldersHeadless(
|
|
|
278
299
|
// A change column that is empty on every row says nothing; the line above explains it once.
|
|
279
300
|
columns: HOLDER_COLUMNS
|
|
280
301
|
.filter((column) => changeReported || !CHANGE_COLUMN_KEYS.has(column.key))
|
|
281
|
-
.
|
|
302
|
+
.filter((column) => markedRows || column.key !== BASIS_COLUMN_KEY)
|
|
303
|
+
.map((column) => column.key === "value" ? { ...column, header: moneyColumnHeader(column.header, valueCurrency) } : column),
|
|
282
304
|
rows,
|
|
283
305
|
metadata: {
|
|
284
306
|
symbol: data.symbol || symbol,
|
|
285
307
|
name: data.name ?? null,
|
|
286
308
|
exchange: data.exchange ?? null,
|
|
287
309
|
currency,
|
|
310
|
+
valueCurrency,
|
|
311
|
+
...(data.isDepositaryReceipt != null ? { isDepositaryReceipt: data.isDepositaryReceipt } : {}),
|
|
312
|
+
...(data.shareBasis ? { shareBasis: data.shareBasis } : {}),
|
|
313
|
+
...(data.adrRatio != null ? { adrRatio: data.adrRatio } : {}),
|
|
288
314
|
asOf: data.asOf ?? null,
|
|
289
315
|
summary: data.summary ?? null,
|
|
290
316
|
shown: rows.length,
|
|
@@ -294,7 +320,7 @@ export function createHoldersHeadless(
|
|
|
294
320
|
valueBasis,
|
|
295
321
|
changeReported,
|
|
296
322
|
positionsBasis,
|
|
297
|
-
notices: [header, basis, positionsBasis].filter((line): line is string => !!line),
|
|
323
|
+
notices: [header, basis, positionsBasis, shareBasisNote].filter((line): line is string => !!line),
|
|
298
324
|
},
|
|
299
325
|
};
|
|
300
326
|
},
|
|
@@ -26,8 +26,11 @@ import {
|
|
|
26
26
|
formatMaybePercent,
|
|
27
27
|
formatMoneyCompact,
|
|
28
28
|
formatSignedCompact,
|
|
29
|
+
holderStakeMarketCap,
|
|
30
|
+
holderValueCurrency,
|
|
29
31
|
resolveHolderOwnershipPercent,
|
|
30
32
|
} from "./format";
|
|
33
|
+
import { holderShareBasisMarker } from "./report-header";
|
|
31
34
|
import {
|
|
32
35
|
buildColumns,
|
|
33
36
|
hasHolderChanges,
|
|
@@ -40,14 +43,15 @@ import {
|
|
|
40
43
|
import { loadHolderData } from "./client";
|
|
41
44
|
import { HoldersTreemap } from "./treemap";
|
|
42
45
|
import { BeneficialOwnersView } from "./beneficial-view";
|
|
46
|
+
import { useIssuerListing } from "../shared/ticker-request";
|
|
43
47
|
import type { HolderColumn, HolderColumnId, HolderRow, SortPreference, ViewMode } from "./types";
|
|
44
48
|
import { loadHolder13FMatches, type Holder13FMatch } from "./thirteenf-match";
|
|
45
49
|
import { useSampledValue, useTickerQuoteStream } from "../../../state/hooks/live-ticker-financials";
|
|
46
50
|
|
|
47
51
|
const HOLDER_MARKET_CAP_SAMPLE_MS = 5_000;
|
|
48
52
|
|
|
49
|
-
/** The "13F"
|
|
50
|
-
const
|
|
53
|
+
/** The width of a three-letter badge ("13F", "ADR"): the label and its padding. */
|
|
54
|
+
const BADGE_WIDTH = 5;
|
|
51
55
|
|
|
52
56
|
export function HoldersView({ focused, width, height }: { focused: boolean; width: number; height: number }) {
|
|
53
57
|
const { nativePaneChrome } = useUiCapabilities();
|
|
@@ -62,6 +66,7 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
|
|
|
62
66
|
const fundMatchAbortRef = useRef<AbortController | null>(null);
|
|
63
67
|
|
|
64
68
|
const exchange = ticker?.metadata.exchange ?? "";
|
|
69
|
+
const issuerListing = useIssuerListing(ticker);
|
|
65
70
|
const loadHolders = useMemo(() => symbol ? async (forceRefresh: boolean) => {
|
|
66
71
|
if (!dataProvider) throw new Error("Holder data unavailable");
|
|
67
72
|
return loadHolderData(dataProvider, symbol, exchange, forceRefresh ? { cacheMode: "refresh" } : undefined);
|
|
@@ -70,13 +75,14 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
|
|
|
70
75
|
// refresh keeps the last holders; the failure goes to the footer.
|
|
71
76
|
const { data, loading, error, reload } = useAsyncResource<HolderData>(loadHolders, { keepPreviousData: true });
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
// The values' own currency: dollars from the 13F filings on a London line, not its pence.
|
|
79
|
+
const currency = holderValueCurrency(data, ticker?.metadata.currency || "USD")!;
|
|
74
80
|
// A stake without a reported percentage is the holding's value over the
|
|
75
|
-
// market cap. The stream keeps the cap
|
|
76
|
-
// stakes follow it at most every few
|
|
81
|
+
// market cap, when both are in one currency. The stream keeps the cap
|
|
82
|
+
// current, about once a second; the stakes follow it at most every few
|
|
83
|
+
// seconds so ticks do not re-sort them.
|
|
77
84
|
useTickerQuoteStream(ticker ? symbol : null, ticker, { surface: "detail", visible: false, weight: 30 });
|
|
78
|
-
const
|
|
79
|
-
const liveMarketCap = financials?.quote?.currency && financials.quote.currency !== currency ? undefined : quoteMarketCap;
|
|
85
|
+
const liveMarketCap = holderStakeMarketCap(financials?.quote?.marketCap, financials?.quote?.currency, currency);
|
|
80
86
|
const marketCap = useSampledValue(liveMarketCap, HOLDER_MARKET_CAP_SAMPLE_MS, `${symbol ?? ""}:${currency}`);
|
|
81
87
|
const rows = useMemo(() => buildRows(data), [data]);
|
|
82
88
|
const sortedRows = useMemo(() => sortRows(rows, sortPreference, marketCap), [marketCap, rows, sortPreference]);
|
|
@@ -216,17 +222,22 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
|
|
|
216
222
|
switch (column.id) {
|
|
217
223
|
case "holder": {
|
|
218
224
|
const color = rowState.selected ? colors.selectedText : colors.textBright;
|
|
219
|
-
|
|
220
|
-
//
|
|
221
|
-
|
|
225
|
+
// ADR: a position held as receipts on a home line's list. 13F: a fund
|
|
226
|
+
// whose filing opens on Enter or o.
|
|
227
|
+
const receipts = holderShareBasisMarker(row);
|
|
228
|
+
const fund = fundMatches.has(row.id);
|
|
229
|
+
if (!receipts && !fund) return { text: row.name, color, attributes: TextAttributes.BOLD };
|
|
230
|
+
const badges = (receipts ? 1 : 0) + (fund ? 1 : 0);
|
|
231
|
+
// The name gives way first, so the markers survive a narrow column.
|
|
222
232
|
return {
|
|
223
|
-
text:
|
|
233
|
+
text: [row.name, receipts, fund ? "13F" : ""].filter(Boolean).join(" "),
|
|
224
234
|
content: (
|
|
225
235
|
<Box flexDirection="row" gap={1} width={column.width} overflow="hidden">
|
|
226
236
|
<Text fg={color} attributes={TextAttributes.BOLD}>
|
|
227
|
-
{clipToDisplayWidth(row.name, Math.max(1, column.width -
|
|
237
|
+
{clipToDisplayWidth(row.name, Math.max(1, column.width - badges * (BADGE_WIDTH + 1)))}
|
|
228
238
|
</Text>
|
|
229
|
-
<Badge label=
|
|
239
|
+
{receipts ? <Badge label={receipts} /> : null}
|
|
240
|
+
{fund ? <Badge label="13F" tone="accent" /> : null}
|
|
230
241
|
</Box>
|
|
231
242
|
),
|
|
232
243
|
};
|
|
@@ -328,6 +339,7 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
|
|
|
328
339
|
focused={focused}
|
|
329
340
|
width={width}
|
|
330
341
|
symbol={symbol ?? null}
|
|
342
|
+
listing={issuerListing}
|
|
331
343
|
holderRows={rows}
|
|
332
344
|
fundMatches={fundMatches}
|
|
333
345
|
onCycleView={toggleView}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { HolderData } from "../../../types/financials";
|
|
1
2
|
import { currencyUnitLabel, resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
3
|
+
import { adrRatioText } from "../../../utils/depositary-receipt";
|
|
2
4
|
import { canonicalExchange, isUsListingExchange, resolveExchangeTimeZone } from "../../../utils/exchanges";
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -12,7 +14,10 @@ export interface HolderReportFacts {
|
|
|
12
14
|
/** Left out when the report title already names it. */
|
|
13
15
|
symbol?: string | null;
|
|
14
16
|
exchange?: string | null;
|
|
17
|
+
/** The unit the values are in. */
|
|
15
18
|
currency?: string | null;
|
|
19
|
+
/** The listing's price currency, when the values are in another one. */
|
|
20
|
+
listingCurrency?: string | null;
|
|
16
21
|
/** Left out when the report's closing line already carries it. */
|
|
17
22
|
asOf?: string | null;
|
|
18
23
|
/** Rows printed. */
|
|
@@ -46,10 +51,13 @@ function venueLabel(exchange?: string | null): string {
|
|
|
46
51
|
|
|
47
52
|
/** What the list is, after the listing is named: its unit, date and how much of it is shown. */
|
|
48
53
|
export function holderListFacts(
|
|
49
|
-
facts: Pick<HolderReportFacts, "currency" | "asOf" | "shown" | "reported" | "total">,
|
|
54
|
+
facts: Pick<HolderReportFacts, "currency" | "listingCurrency" | "asOf" | "shown" | "reported" | "total">,
|
|
50
55
|
): string[] {
|
|
56
|
+
const unit = currencyUnitLabel(facts.currency);
|
|
57
|
+
// Dollars on a London line: say they are the values' unit, not the listing's.
|
|
58
|
+
const otherUnit = unit && facts.listingCurrency?.trim() && facts.listingCurrency.trim() !== facts.currency?.trim();
|
|
51
59
|
return [
|
|
52
|
-
|
|
60
|
+
otherUnit ? `values in ${unit}` : unit,
|
|
53
61
|
facts.asOf ? `as of ${facts.asOf}` : "",
|
|
54
62
|
holderCountText(facts.shown, facts.reported, facts.total),
|
|
55
63
|
].filter(Boolean);
|
|
@@ -69,9 +77,42 @@ export function sharedReportDate(rows: ReadonlyArray<{ reportDate?: unknown }>):
|
|
|
69
77
|
return dates.size === 1 && only ? only : null;
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
/**
|
|
81
|
+
* How a holder value was reached: reported shares times the latest price, or,
|
|
82
|
+
* where Gloom Cloud priced the filings themselves, times the security's price
|
|
83
|
+
* at the report date in the values' currency.
|
|
84
|
+
*/
|
|
85
|
+
export function holderValueBasis(
|
|
86
|
+
reportDate: string | null,
|
|
87
|
+
basis?: HolderData["valueBasis"] | null,
|
|
88
|
+
valueCurrency?: string | null,
|
|
89
|
+
): string {
|
|
90
|
+
const shares = `shares reported${reportDate ? ` at ${reportDate}` : ""}`;
|
|
91
|
+
if (basis === "period_end_price") {
|
|
92
|
+
const unit = valueCurrency?.trim();
|
|
93
|
+
return `${shares} x period-end price${unit ? ` (${unit})` : ""}`;
|
|
94
|
+
}
|
|
95
|
+
return `${shares} x latest price`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The short marker a holder row carries when its position is held as depositary receipts. */
|
|
99
|
+
export function holderShareBasisMarker(row: { shareBasis?: unknown }): string {
|
|
100
|
+
return row.shareBasis === "depositary_receipt" ? "ADR" : "";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* What the share counts are, when Gloom Cloud says: which rows of a home
|
|
105
|
+
* line are positions in the receipts, or that a receipt line's rows all are.
|
|
106
|
+
*/
|
|
107
|
+
export function holderShareBasisNote(
|
|
108
|
+
data: Pick<HolderData, "shareBasis" | "adrRatio">,
|
|
109
|
+
rows: ReadonlyArray<{ shareBasis?: unknown }>,
|
|
110
|
+
): string | null {
|
|
111
|
+
const ratio = adrRatioText(data.adrRatio);
|
|
112
|
+
const each = ratio ? ` (${ratio})` : "";
|
|
113
|
+
if (rows.some((row) => holderShareBasisMarker(row))) return `ADR: shares held as depositary receipts${each}.`;
|
|
114
|
+
if (rows.length > 0 && data.shareBasis === "depositary_receipt") return `Shares are depositary receipts${each}.`;
|
|
115
|
+
return null;
|
|
75
116
|
}
|
|
76
117
|
|
|
77
118
|
/** A listing outside the US, by its venue, or by a currency other than the dollar. */
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
type StatItem,
|
|
23
23
|
} from "../../../components";
|
|
24
24
|
import { useDebouncedPluginPaneState, usePluginPaneState } from "../../runtime";
|
|
25
|
-
import {
|
|
25
|
+
import { mayBeUsEquityTicker } from "../../../utils/sec";
|
|
26
26
|
import { formatCompact } from "../../../utils/format";
|
|
27
27
|
import { nextHeaderSort } from "../../../utils/sort-values";
|
|
28
28
|
import { formatShortDate } from "../../../utils/datetime-format";
|
|
@@ -130,7 +130,7 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
130
130
|
[setOpenItemIdState],
|
|
131
131
|
);
|
|
132
132
|
const [sort, setSort] = useState<StackSortPreference<InsiderColumnId>>({ columnId: "date", direction: "desc" });
|
|
133
|
-
const eligibleTicker =
|
|
133
|
+
const eligibleTicker = mayBeUsEquityTicker(ticker);
|
|
134
134
|
const instrument = instrumentFromTicker(ticker, ticker?.metadata.ticker ?? null);
|
|
135
135
|
|
|
136
136
|
const filingsEntry = useSecFilingsQuery(
|
|
@@ -458,7 +458,7 @@ export const insiderModule: PluginModule = {
|
|
|
458
458
|
description: "Insider transaction activity for the selected ticker.",
|
|
459
459
|
keywords: ["insider", "form 4", "ownership", "transactions", "ins"],
|
|
460
460
|
shortcut: "INS",
|
|
461
|
-
canCreate: (_context, options) => !options?.ticker ||
|
|
461
|
+
canCreate: (_context, options) => !options?.ticker || mayBeUsEquityTicker(options.ticker),
|
|
462
462
|
}),
|
|
463
463
|
headless: insiderHeadless,
|
|
464
464
|
},
|
|
@@ -471,7 +471,7 @@ export const insiderModule: PluginModule = {
|
|
|
471
471
|
order: 47,
|
|
472
472
|
component: InsiderView,
|
|
473
473
|
instruments: ["equity"],
|
|
474
|
-
isVisible: ({ ticker }) =>
|
|
474
|
+
isVisible: ({ ticker }) => mayBeUsEquityTicker(ticker),
|
|
475
475
|
});
|
|
476
476
|
},
|
|
477
477
|
};
|
|
@@ -3,7 +3,8 @@ import { CompositeChart } from "../../../components";
|
|
|
3
3
|
import { resolveChartPalette } from "../../../components/chart/core/palette";
|
|
4
4
|
import { formatCompositeSeriesValue } from "../../../components/chart/composite/format";
|
|
5
5
|
import { staticSeries } from "../../../components/chart/static/series";
|
|
6
|
-
import { useThemeColors } from "../../../theme/theme-context";
|
|
6
|
+
import { useThemeColors, useThemeId } from "../../../theme/theme-context";
|
|
7
|
+
import { themeSeriesColors } from "../../../theme/series-colors";
|
|
7
8
|
import type { ResolvedSeries } from "../../../time-series/types";
|
|
8
9
|
import { formatPoints } from "./format";
|
|
9
10
|
import type { DatedValue, IvHistoryModel } from "./model";
|
|
@@ -11,8 +12,15 @@ import type { DatedValue, IvHistoryModel } from "./model";
|
|
|
11
12
|
const PANELS = [{ id: "vol", height: 3 }, { id: "spread", height: 1 }];
|
|
12
13
|
/** The chart drawing blue: IV90 must read apart from amber IV30 in every theme. */
|
|
13
14
|
const IV90_COLOR = "#4c9aff";
|
|
14
|
-
/** The
|
|
15
|
-
const
|
|
15
|
+
/** The quote capture point must stand apart from the amber IV30 line it continues. */
|
|
16
|
+
const QUOTE_COLOR = "#c084fc";
|
|
17
|
+
/** IV1Y: pink, apart from amber IV30, blue IV90, green HV and the violet quote points. */
|
|
18
|
+
const IV365_COLOR = "#f783ac";
|
|
19
|
+
/**
|
|
20
|
+
* Palette slots for a theme with its own series colours (the colour-blind
|
|
21
|
+
* ones), where the up colour HV takes elsewhere is the same blue as IV90.
|
|
22
|
+
*/
|
|
23
|
+
const THEMED_SLOTS = { iv30: 1, iv90: 0, hv: 2, quote: 5, iv365: 4 } as const;
|
|
16
24
|
|
|
17
25
|
function volSeries(id: string, label: string, color: string, points: readonly DatedValue[], panelId: string,
|
|
18
26
|
style: ResolvedSeries["style"] = "line"): ResolvedSeries {
|
|
@@ -27,22 +35,28 @@ function formatLegendValue(value: number, series: ResolvedSeries): string {
|
|
|
27
35
|
return series.id === "spread" ? `${formatPoints(value / 100)} pts` : formatCompositeSeriesValue(value, series);
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
function ivHistorySeries(model: IvHistoryModel, colors: { iv30: string; iv90: string; hv: string; quote: string; spread: string },
|
|
38
|
+
function ivHistorySeries(model: IvHistoryModel, colors: { iv30: string; iv90: string; iv365: string; hv: string; quote: string; spread: string },
|
|
31
39
|
hvLabel: string): ResolvedSeries[] {
|
|
32
40
|
return [
|
|
33
41
|
volSeries("iv30", "IV 30d", colors.iv30, model.iv30, "vol"),
|
|
34
42
|
volSeries("iv90", "IV 90d", colors.iv90, model.iv90, "vol"),
|
|
43
|
+
// Drawn only once there is a 1Y close, so an empty legend entry never suggests one.
|
|
44
|
+
...(model.iv365.length ? [volSeries("iv365", "IV 1Y", colors.iv365, model.iv365, "vol")] : []),
|
|
35
45
|
volSeries("hv", hvLabel, colors.hv, model.hv, "vol"),
|
|
36
|
-
...(model.quoteIv30.length ? [volSeries("iv30-quote", "IV 30d
|
|
46
|
+
...(model.quoteIv30.length ? [volSeries("iv30-quote", "IV 30d quote", colors.quote, model.quoteIv30, "vol", "points")] : []),
|
|
37
47
|
{ ...volSeries("spread", `IV 30d - ${hvLabel}`, colors.spread, model.spread, "spread", "columns"), ...SPREAD_UNIT },
|
|
38
48
|
];
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
export function IvHistoryChart({ model, width, height, hvLabel, focused = false }: { model: IvHistoryModel; width: number; height: number; hvLabel: string; focused?: boolean }) {
|
|
42
52
|
const colors = useThemeColors();
|
|
53
|
+
const themed = themeSeriesColors(useThemeId());
|
|
43
54
|
const palette = resolveChartPalette(colors);
|
|
44
|
-
const series = useMemo(() => ivHistorySeries(model,
|
|
45
|
-
|
|
55
|
+
const series = useMemo(() => ivHistorySeries(model, themed
|
|
56
|
+
? { iv30: themed[THEMED_SLOTS.iv30]!, iv90: themed[THEMED_SLOTS.iv90]!, iv365: themed[THEMED_SLOTS.iv365]!, hv: themed[THEMED_SLOTS.hv]!,
|
|
57
|
+
quote: themed[THEMED_SLOTS.quote]!, spread: colors.textDim }
|
|
58
|
+
: { iv30: colors.warning, iv90: IV90_COLOR, iv365: IV365_COLOR, hv: colors.positive, quote: QUOTE_COLOR, spread: colors.textDim },
|
|
59
|
+
hvLabel), [model, colors, themed, hvLabel]);
|
|
46
60
|
return <CompositeChart series={series} panels={PANELS} width={width} height={height} focused={focused} navigable={false} showLegend showTimeAxis formatValue={formatLegendValue}
|
|
47
61
|
remoteKind="implied-volatility-history"
|
|
48
62
|
colors={{ background: palette.bgColor, grid: palette.gridColor, crosshair: palette.crosshairColor, text: colors.text,
|
|
@@ -33,6 +33,10 @@ export interface IvStats {
|
|
|
33
33
|
high: number | null;
|
|
34
34
|
samples: number;
|
|
35
35
|
windowStart: string | null;
|
|
36
|
+
/** Prior sessions the rank needs: 120, or 60 for IV1Y. Absent from older servers. */
|
|
37
|
+
minSamples?: number;
|
|
38
|
+
/** Why rank or percentile is empty, when one is. */
|
|
39
|
+
rankNote?: string | null;
|
|
36
40
|
}
|
|
37
41
|
interface IvReading {
|
|
38
42
|
date: string;
|
|
@@ -51,8 +55,12 @@ export interface IvHistoryPayload {
|
|
|
51
55
|
symbol: string;
|
|
52
56
|
asOf: string;
|
|
53
57
|
status: IvCoverageStatus;
|
|
54
|
-
coverage: {
|
|
55
|
-
|
|
58
|
+
coverage: {
|
|
59
|
+
source: "seed" | "demand"; addedAt: string; backfilledThrough: string | null; since: string | null;
|
|
60
|
+
/** First trade close with a one-year reading; the 1Y series starts later and is sparser. */
|
|
61
|
+
iv365Since?: string | null;
|
|
62
|
+
} | null;
|
|
63
|
+
stats: { iv30: IvStats | null; iv90: IvStats | null; iv365?: IvStats | null };
|
|
56
64
|
latest: IvReading | null;
|
|
57
65
|
series: IvPoint[];
|
|
58
66
|
warnings: string[];
|
|
@@ -62,6 +70,8 @@ export interface IvScreenRow {
|
|
|
62
70
|
status: "ready" | "queued";
|
|
63
71
|
iv30: IvStats | null;
|
|
64
72
|
iv90: IvStats | null;
|
|
73
|
+
iv365?: IvStats | null;
|
|
74
|
+
iv365Since?: string | null;
|
|
65
75
|
latest: IvReading | null;
|
|
66
76
|
skew: { date: string; put25: number; call25: number; skew: number } | null;
|
|
67
77
|
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IvMethod } from "./client";
|
|
2
|
+
import type { IvStatRow, IvStatUnit, RichCheap, RichCheapDates } from "./model";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* VCA's sign conventions, and what VIX is beside an at-the-money IV30, one
|
|
6
|
+
* line each: under the table, in the shot and in the report's notes. The skew
|
|
7
|
+
* is the stored 25-delta put and call IVs at a 30-day constant maturity, from
|
|
8
|
+
* the latest quote capture; both differences are decimal IV shown in points.
|
|
9
|
+
*/
|
|
10
|
+
export const VCA_DEFINITIONS = [
|
|
11
|
+
"25D skew: 30-day 25-delta put IV minus 25-delta call IV, in vol points; positive means puts are richer. 30-90: IV30 minus IV90.",
|
|
12
|
+
"VIX is Cboe's 30-day S&P 500 implied vol from the whole SPX strike strip, skew included; IV30 here is at the money, so VIX usually reads higher.",
|
|
13
|
+
] as const;
|
|
2
14
|
|
|
3
15
|
export const formatVol = (value: number | null | undefined) => value == null || !Number.isFinite(value) ? "--" : `${(value * 100).toFixed(1)}%`;
|
|
4
16
|
export const formatPoints = (value: number | null | undefined) =>
|
|
@@ -13,6 +25,25 @@ export function formatStat(value: number | null | undefined, unit: IvStatUnit):
|
|
|
13
25
|
if (value == null || !Number.isFinite(value)) return "--";
|
|
14
26
|
return unit === "vol" ? formatVol(value) : unit === "points" ? formatPoints(value) : value.toFixed(2);
|
|
15
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* How a stored reading was taken. A quote capture is the option quote mids
|
|
30
|
+
* from the minutes before the close, taken once a session and never updated:
|
|
31
|
+
* it is not a live feed, so it is not called live.
|
|
32
|
+
*/
|
|
33
|
+
const readingSource = (method: IvMethod | null | undefined) => method === "quote-mid" ? "quote" : "close";
|
|
16
34
|
export function statSource(row: Pick<IvStatRow, "method">): string {
|
|
17
|
-
return row.method === "
|
|
35
|
+
return row.method === "prices" ? "closes" : row.method ? readingSource(row.method) : "";
|
|
36
|
+
}
|
|
37
|
+
/** "2026-10-09 quote": the session a reading belongs to and how it was taken. */
|
|
38
|
+
export const readingLabel = (date: string, method: IvMethod | null | undefined, short = false) =>
|
|
39
|
+
`${short ? shortDate(date) : date} ${readingSource(method)}`;
|
|
40
|
+
/**
|
|
41
|
+
* The dates every row agrees on, said once: the reading's, and the trade close
|
|
42
|
+
* its rank is measured on. `short` is for a footer with little room: month and
|
|
43
|
+
* day, and no window.
|
|
44
|
+
*/
|
|
45
|
+
export function sharedDates({ reading, rank }: Pick<RichCheapDates, "reading" | "rank">, short = false): string[] {
|
|
46
|
+
if (reading?.method === "trade-close" && reading.date === rank) return [readingLabel(reading.date, reading.method, short)];
|
|
47
|
+
return [reading && `IV30 ${readingLabel(reading.date, reading.method, short)}`,
|
|
48
|
+
rank && (short ? `IVR/IVP ${shortDate(rank)} close` : `IVR/IVP vs 52 weeks to ${rank} close`)].filter((part): part is string => !!part);
|
|
18
49
|
}
|
|
@@ -2,8 +2,8 @@ import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
|
2
2
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
3
3
|
import { createRealizedVolatilityDependencies, loadRealizedVolatilityHistory } from "../realized-vol/client";
|
|
4
4
|
import { createHvDependencies, loadIvHistory, loadIvScreen, loadRealizedVolatilities } from "./client";
|
|
5
|
-
import { formatPoints, formatRank, formatStat, formatVol, verdictLabel } from "./format";
|
|
6
|
-
import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap,
|
|
5
|
+
import { formatPoints, formatRank, formatStat, formatVol, readingLabel, sharedDates, VCA_DEFINITIONS, verdictLabel } from "./format";
|
|
6
|
+
import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, type RichCheapRow, richCheapDates, VCA_LIMIT, VCA_PRESETS } from "./model";
|
|
7
7
|
import { vcaUniverse } from "./universe";
|
|
8
8
|
|
|
9
9
|
const METHODOLOGY = "docs/research-data.md#implied-volatility-history";
|
|
@@ -14,7 +14,8 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
14
14
|
freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date", oldest: null },
|
|
15
15
|
describe: (args) => `HIVG ${args.symbols[0] ?? ""}`,
|
|
16
16
|
discovery: { aliases: ["HIVG"], screenshotReadiness: "live-dom", dataRequirements: ["Cloud stored implied volatility", "Daily price history"],
|
|
17
|
-
limitations: ["History starts February 2024 (
|
|
17
|
+
limitations: ["History starts February 2024 (daily option trade closes)", "Rank and percentile use trade-close readings only",
|
|
18
|
+
"IV1Y needs long-dated trades: it can start later, has gaps, and ranks from 60 prior sessions"] },
|
|
18
19
|
options: [
|
|
19
20
|
{ key: "lookback", type: "enum", values: [{ value: "1Y" }, { value: "2Y" }, { value: "ALL" }], defaultValue: "1Y", description: "Visible history" },
|
|
20
21
|
{ key: "hvWindow", type: "enum", values: HV_WINDOWS.map((window) => ({ value: String(window) })), defaultValue: "20", description: "Realized volatility window in sessions" },
|
|
@@ -28,8 +29,10 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
28
29
|
const model = projectIvHistory(payload, prices.history, { lookback: String(args.options.lookback) as IvLookback,
|
|
29
30
|
hvWindow: (Number(args.options.hvWindow) === 30 ? 30 : 20) as HvWindow });
|
|
30
31
|
const hvByDay = new Map(model.hv.map((point) => [point.date.getTime(), point.value]));
|
|
32
|
+
const iv365ByDay = new Map(model.iv365.map((point) => [point.date.getTime(), point.value]));
|
|
31
33
|
const rows = model.iv30.map((point, index) => ({ date: point.date.toISOString().slice(0, 10), iv30: point.value,
|
|
32
34
|
iv90: model.iv90.find((entry) => entry.date.getTime() === point.date.getTime())?.value ?? null,
|
|
35
|
+
iv365: iv365ByDay.get(point.date.getTime()) ?? null,
|
|
33
36
|
hv: hvByDay.get(point.date.getTime()) ?? null, spread: model.spread[index]?.value ?? null })).reverse();
|
|
34
37
|
const errors = [prices.error, ...model.warnings].filter((value): value is string => !!value);
|
|
35
38
|
return {
|
|
@@ -45,11 +48,12 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
45
48
|
{ key: "samples", header: "Sessions" },
|
|
46
49
|
], rows: model.stats.map((row) => ({ ...row })) },
|
|
47
50
|
{ title: "Daily history", columns: [{ key: "date", header: "Session" },
|
|
48
|
-
...["iv30", "iv90", "hv"].map((key) => ({ key
|
|
51
|
+
...[["iv30", "IV30"], ["iv90", "IV90"], ["iv365", "IV1Y"], ["hv", "HV"]].map(([key, header]) => ({ key: key!, header: header!, format: percent })),
|
|
49
52
|
{ key: "spread", header: "IV-HV", format: (value: unknown) => typeof value === "number" ? formatPoints(value) : "--" }], rows },
|
|
50
53
|
],
|
|
51
54
|
complete: payload.status === "ready" && errors.length === 0,
|
|
52
55
|
unavailableSymbols: model.iv30.length ? [] : [instrument.symbol], errors,
|
|
56
|
+
...(model.oneYearNote ? { notes: [model.oneYearNote] } : {}),
|
|
53
57
|
metadata: { status: payload.status, coverage: payload.coverage, latest: payload.latest, stats: payload.stats,
|
|
54
58
|
unit: "decimal annualized volatility", methodology: METHODOLOGY },
|
|
55
59
|
};
|
|
@@ -58,7 +62,8 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
58
62
|
|
|
59
63
|
export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
60
64
|
shape: "bundle",
|
|
61
|
-
|
|
65
|
+
// Dated by the readings themselves; the response's own date is the day it was fetched.
|
|
66
|
+
freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date" },
|
|
62
67
|
argument: { kind: "symbol-list", optional: true, maximum: VCA_LIMIT, description: "US option underlyings; defaults to index and sector ETFs." },
|
|
63
68
|
options: [{ key: "preset", type: "enum", values: [{ value: "etfs" }, { value: "megacaps" }], defaultValue: "etfs", description: "Preset when no symbols are given" }],
|
|
64
69
|
describe: "Volatility rich/cheap",
|
|
@@ -73,24 +78,31 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
73
78
|
]);
|
|
74
79
|
const rows = projectRichCheap(payload.rows, hv).sort((a, b) => (b.percentile ?? -1) - (a.percentile ?? -1));
|
|
75
80
|
const queued = rows.filter((row) => row.status === "queued").map((row) => row.symbol);
|
|
76
|
-
const
|
|
81
|
+
const dates = richCheapDates(rows);
|
|
77
82
|
const hasSkew = rows.some((row) => row.skew != null);
|
|
83
|
+
const hasIv1y = rows.some((row) => row.iv1y != null), hasIv1yRank = rows.some((row) => row.iv1yPercentile != null);
|
|
84
|
+
const dated = (value: unknown) => typeof value === "string" ? value : "--";
|
|
78
85
|
return {
|
|
79
|
-
sections: [{ title:
|
|
86
|
+
sections: [{ title: ["Rich/cheap", universe.label, ...sharedDates(dates)].join(" · "), columns: [
|
|
80
87
|
{ key: "symbol", header: "Symbol" }, { key: "iv30", header: "IV30", format: percent },
|
|
81
|
-
...(
|
|
88
|
+
...(dates.reading ? [] : [{ key: "date", header: "As of", format: (value: unknown, row?: unknown) =>
|
|
89
|
+
typeof value === "string" ? readingLabel(value, (row as RichCheapRow).method) : "--" }]),
|
|
90
|
+
...(dates.rankApart ? [{ key: "rankDate", header: "Rank as of", format: (value: unknown) => typeof value === "string" ? `${value} close` : "--" }] : []),
|
|
82
91
|
{ key: "rank", header: "IVR", format: (value: unknown) => formatRank(value as number | null) },
|
|
83
92
|
{ key: "percentile", header: "IVP", format: (value: unknown) => formatRank(value as number | null) },
|
|
84
93
|
{ key: "verdict", header: "Rich/Cheap", format: (value: unknown) => verdictLabel(value as never) },
|
|
94
|
+
...(hasIv1y ? [{ key: "iv1y", header: "IV1Y", format: percent }] : []),
|
|
95
|
+
...(hasIv1yRank ? [{ key: "iv1yPercentile", header: "IVP1Y", format: (value: unknown) => formatRank(value as number | null) }] : []),
|
|
85
96
|
{ key: "termSlope", header: "30-90", format: (value: unknown) => formatPoints(value as number | null) },
|
|
86
97
|
...(hasSkew ? [{ key: "skew", header: "25D skew", format: (value: unknown) => formatPoints(value as number | null) }] : []),
|
|
98
|
+
...(hasSkew && dates.skewApart ? [{ key: "skewDate", header: "Skew as of", format: dated }] : []),
|
|
87
99
|
{ key: "hv", header: "HV20", format: percent },
|
|
88
100
|
{ key: "ivHv", header: "IV/HV", format: (value: unknown) => typeof value === "number" ? value.toFixed(2) : "--" },
|
|
89
101
|
], rows: rows.map((row) => ({ ...row })) }],
|
|
90
102
|
complete: !queued.length && !universe.error, unavailableSymbols: queued,
|
|
91
103
|
errors: [universe.error].filter((value): value is string => !!value),
|
|
92
|
-
...
|
|
93
|
-
metadata: {
|
|
104
|
+
notes: [...queued.length ? [`Queued for backfill: ${queued.join(", ")}.`] : [], ...VCA_DEFINITIONS],
|
|
105
|
+
metadata: { presets: VCA_PRESETS, methodology: METHODOLOGY },
|
|
94
106
|
};
|
|
95
107
|
},
|
|
96
108
|
};
|
|
@@ -26,12 +26,12 @@ export const ivHistoryModule: PluginModule = {
|
|
|
26
26
|
paneTemplates: [
|
|
27
27
|
{ ...createTickerSurfacePaneTemplate({
|
|
28
28
|
id: "iv-history-pane", paneId: "iv-history", label: "Implied Volatility History",
|
|
29
|
-
description: "30
|
|
30
|
-
keywords: ["hivg", "implied", "volatility", "history", "iv rank", "ivr", "percentile", "vrp"], shortcut: "HIVG", publicShare: true,
|
|
29
|
+
description: "30-day, 90-day and one-year ATM implied volatility since 2024 against realized, with IV rank and percentile.",
|
|
30
|
+
keywords: ["hivg", "implied", "volatility", "history", "iv rank", "ivr", "percentile", "vrp", "iv1y", "leaps"], shortcut: "HIVG", publicShare: true,
|
|
31
31
|
}), headless: ivHistoryHeadless },
|
|
32
32
|
{
|
|
33
33
|
id: "iv-screen-pane", paneId: "iv-screen", label: "Volatility Rich/Cheap",
|
|
34
|
-
description: "IV rank, percentile, term slope, skew and IV/HV across a list, ranked rich to cheap.",
|
|
34
|
+
description: "IV rank, percentile, one-year IV, term slope, skew and IV/HV across a list, ranked rich to cheap.",
|
|
35
35
|
keywords: ["vca", "rich", "cheap", "iv rank", "implied volatility", "screen"],
|
|
36
36
|
shortcut: { prefix: "VCA", argKind: "ticker-list" as const, argOptional: true }, headless: ivScreenHeadless,
|
|
37
37
|
createInstance: (_context, options) => ({
|