gloomberb 0.16.4 → 0.16.6
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/README.md +14 -91
- package/package.json +5 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
- package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
- package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/central-bank-rates.ts +6 -1
- package/src/api-client/chat.ts +26 -0
- package/src/api-client/credit-boards.ts +2 -0
- package/src/api-client/data.ts +73 -3
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/europe-energy.ts +106 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +13 -1
- package/src/api-client/market-discovery.ts +13 -1
- package/src/api-client/normalizers.ts +32 -0
- package/src/api-client/paths.ts +7 -0
- package/src/api-client/perps.ts +8 -0
- package/src/api-client/request.ts +9 -2
- package/src/api-client/research-activity.ts +15 -10
- package/src/api-client/socket.ts +29 -0
- package/src/api-client/themes.ts +22 -1
- package/src/api-client/types.ts +78 -2
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +50 -2
- package/src/app/keybindings/actions.ts +12 -1
- package/src/app/pane-runtime/plugin-bindings.ts +2 -2
- package/src/app/pane-runtime/ticker-open-runtime.ts +16 -6
- package/src/app/runtime/desktop-deeplink.ts +10 -5
- package/src/app/runtime/startup.ts +1 -1
- package/src/app.tsx +6 -0
- package/src/architecture/core-builtin-allowlist.json +8 -26
- package/src/ask-gloom/pane-context.ts +114 -0
- package/src/ask-gloom/pane-request.ts +70 -0
- package/src/ask-gloom/pane-text.ts +201 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
- package/src/capabilities/chart-series.ts +19 -2
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/central-bank-events.ts +23 -0
- package/src/cli/command-options.ts +17 -5
- package/src/cli/commands/crypto-hints.ts +35 -2
- package/src/cli/commands/fred-series.ts +146 -0
- package/src/cli/commands/market.ts +501 -115
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-leaps.ts +5 -2
- package/src/cli/commands/overview.ts +132 -26
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +317 -13
- package/src/cli/commands/system.ts +55 -8
- package/src/cli/commands/ticker.ts +155 -18
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +76 -7
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/errors.ts +7 -5
- package/src/cli/exit-codes.ts +8 -0
- package/src/cli/export-source.ts +58 -0
- package/src/cli/freshness.ts +18 -7
- package/src/cli/glossary.ts +483 -0
- package/src/cli/help.ts +10 -0
- package/src/cli/helpers.ts +2 -5
- package/src/cli/history-rows.ts +118 -7
- package/src/cli/index-roots.ts +7 -0
- package/src/cli/index.ts +29 -9
- package/src/cli/known-gaps.ts +84 -0
- package/src/cli/listing-arg.ts +551 -97
- package/src/cli/not-a-ticker.ts +24 -9
- package/src/cli/pane-functions/access-gate.ts +13 -2
- package/src/cli/pane-functions/capabilities.ts +4 -3
- package/src/cli/pane-functions/catalog.ts +214 -40
- package/src/cli/pane-functions/data.ts +42 -8
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +37 -8
- package/src/cli/pane-functions/freshness.ts +393 -49
- package/src/cli/pane-functions/function-help.ts +31 -15
- package/src/cli/pane-functions/headless.ts +98 -30
- package/src/cli/pane-functions/index.ts +101 -19
- package/src/cli/pane-functions/market-session.ts +22 -8
- package/src/cli/pane-functions/options.ts +71 -22
- package/src/cli/pane-functions/report-notices.ts +25 -7
- package/src/cli/pane-functions/report.ts +15 -8
- package/src/cli/pane-functions/resolver.ts +74 -19
- package/src/cli/pane-functions/screenshot.ts +250 -27
- package/src/cli/perpetual-contract.ts +60 -0
- package/src/cli/registry.ts +3 -1
- package/src/cli/report-tables.ts +26 -7
- package/src/cli/result.ts +45 -11
- package/src/cli/series-search.ts +111 -0
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +27 -0
- package/src/cli/symbol-list.ts +154 -0
- package/src/cli/symbol-rows.ts +154 -0
- package/src/cli/ticker-resolution.ts +311 -27
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +14 -3
- package/src/components/chart/composite/composite-chart.tsx +71 -11
- package/src/components/chart/composite/crosshair-readout.tsx +150 -0
- package/src/components/chart/composite/downsample.ts +1 -1
- package/src/components/chart/composite/format.ts +29 -0
- package/src/components/chart/composite/interactions.ts +9 -2
- package/src/components/chart/composite/legend.tsx +100 -33
- package/src/components/chart/composite/panel-bitmap.ts +70 -35
- package/src/components/chart/composite/panel-labels.ts +18 -17
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +46 -21
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/chart/composite/rasterizer.ts +50 -8
- package/src/components/chart/composite/scene.ts +2 -1
- package/src/components/chart/composite/time-scale.ts +26 -9
- package/src/components/chart/composite/types.ts +18 -0
- package/src/components/chart/composite/use-composite-viewport.ts +30 -2
- package/src/components/chart/native/raster/crosshair.ts +8 -3
- package/src/components/chart/native/raster/primitives.ts +85 -21
- package/src/components/chart/native/surface/manager.ts +33 -8
- package/src/components/chart/surface3d/chart.tsx +1 -1
- package/src/components/command-bar/action-runtime.ts +1 -1
- package/src/components/command-bar/commands/collection-list.ts +210 -0
- package/src/components/command-bar/commands/collection.ts +40 -1
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -0
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/routing/actions.ts +1 -1
- package/src/components/command-bar/workflow/tickers.ts +42 -17
- package/src/components/layout/detached-pane-shell.tsx +68 -39
- package/src/components/layout/floating-pane.tsx +10 -1
- package/src/components/layout/header.tsx +25 -5
- package/src/components/layout/missing-pane.tsx +3 -3
- package/src/components/layout/pane/capture-stamp.ts +41 -0
- package/src/components/layout/pane/capture-status.ts +12 -0
- package/src/components/layout/pane/content.tsx +7 -13
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/keyboard.ts +3 -0
- package/src/components/layout/pane/footer/model.ts +41 -1
- package/src/components/layout/pane/header.tsx +50 -1
- package/src/components/layout/pane/index.tsx +21 -3
- package/src/components/layout/pane/sidebar.tsx +88 -21
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/cursor-occlusion.ts +2 -1
- package/src/components/layout/shell/drag/index.ts +39 -9
- package/src/components/layout/shell/drag/live.ts +61 -51
- package/src/components/layout/shell/drag/runtime.ts +54 -12
- package/src/components/layout/shell/index.tsx +84 -65
- package/src/components/layout/shell/menu.ts +14 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/actions.ts +32 -10
- package/src/components/layout/shell/pane/ask.ts +131 -0
- package/src/components/layout/shell/pane/capture.ts +162 -0
- package/src/components/layout/shell/pane/layers.tsx +218 -50
- package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
- package/src/components/layout/shell/shortcuts.ts +21 -4
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/markdown-text.tsx +62 -20
- package/src/components/metric-treemap/heat-surface.tsx +8 -1
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +2 -2
- package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/reader/article-reader.tsx +642 -0
- package/src/components/reader/article-text.ts +527 -0
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +43 -2
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +7 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +18 -6
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +12 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +6 -1
- package/src/components/ui/query-bar.tsx +10 -1
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/normalize.ts +7 -2
- package/src/data/config/store/portfolio-as-of-column.ts +57 -0
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7555 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +65 -5
- package/src/i18n/ja.ts +65 -5
- package/src/i18n/ko.ts +65 -5
- package/src/i18n/zh-cn.ts +65 -5
- package/src/i18n/zh-tw.ts +65 -5
- package/src/layout/desks.ts +1 -1
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/layout-marketplace/gallery.tsx +1 -1
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +84 -20
- package/src/market-data/market/session-close-note.ts +3 -2
- package/src/market-data/market/status.ts +19 -23
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +5 -0
- package/src/market-data/quotes/metadata.ts +19 -24
- package/src/market-data/quotes/resolution.ts +5 -0
- package/src/market-data/quotes/screener-live-quotes.ts +5 -4
- package/src/market-data/result-types.ts +2 -0
- package/src/notifications/app-notifier.ts +17 -1
- package/src/notifications/notification-log.ts +180 -0
- package/src/plugins/auto-update-node.ts +2 -2
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/model.ts +1 -56
- package/src/plugins/builtin/account-management/pane.tsx +6 -4
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/catalysts.ts +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/focus.ts +27 -0
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +47 -11
- package/src/plugins/builtin/alerts/pane.tsx +27 -1
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +8 -3
- package/src/plugins/builtin/analytics/risk-headless.ts +60 -27
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +35 -5
- package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/headless.ts +88 -18
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/model.ts +41 -1
- package/src/plugins/builtin/awards/pane.tsx +22 -13
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/backtest/headless.ts +2 -2
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/broker-manager/index.tsx +1 -0
- package/src/plugins/builtin/catalysts/headless.ts +1 -1
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +49 -12
- package/src/plugins/builtin/cds/headless.ts +119 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +214 -32
- package/src/plugins/builtin/cds/pane.tsx +58 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/client.ts +4 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +46 -16
- package/src/plugins/builtin/central-bank-rates/index.tsx +3 -1
- package/src/plugins/builtin/central-bank-rates/model.ts +99 -0
- package/src/plugins/builtin/central-bank-rates/pane.tsx +1 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +36 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +193 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +1 -1
- package/src/plugins/builtin/chart-composer/headless.ts +70 -5
- package/src/plugins/builtin/chart-composer/index.tsx +15 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +81 -38
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
- package/src/plugins/builtin/chat/content/composer.tsx +5 -1
- package/src/plugins/builtin/chat/content/footer.tsx +12 -0
- package/src/plugins/builtin/chat/content/index.tsx +89 -5
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
- package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
- package/src/plugins/builtin/chat/content/scroll.ts +127 -27
- package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
- package/src/plugins/builtin/chat/content/transcript.tsx +145 -78
- package/src/plugins/builtin/chat/content/types.ts +1 -0
- package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
- package/src/plugins/builtin/chat/controller/index.ts +123 -1
- package/src/plugins/builtin/chat/controller/message-loading.ts +3 -0
- package/src/plugins/builtin/chat/controller/messages.ts +7 -2
- package/src/plugins/builtin/chat/controller/realtime.ts +10 -1
- package/src/plugins/builtin/chat/controller/send.ts +61 -1
- package/src/plugins/builtin/chat/controller/state.ts +8 -0
- package/src/plugins/builtin/chat/ghost-user.ts +20 -0
- package/src/plugins/builtin/chat/index.tsx +23 -5
- package/src/plugins/builtin/chat/layout.ts +78 -5
- package/src/plugins/builtin/chat/message/ai-badge.tsx +18 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +55 -6
- package/src/plugins/builtin/chat/message/header.tsx +16 -4
- package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
- package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
- package/src/plugins/builtin/chat/message/render-state.ts +18 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +43 -4
- package/src/plugins/builtin/chat/message/types.ts +12 -0
- package/src/plugins/builtin/chat/reactions.ts +197 -0
- package/src/plugins/builtin/chat/share/flow.tsx +87 -0
- package/src/plugins/builtin/chat/share/targets.ts +27 -0
- package/src/plugins/builtin/chat/status-widget.tsx +6 -3
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +148 -27
- package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
- package/src/plugins/builtin/cloud/askg/model.ts +27 -8
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +94 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +141 -12
- package/src/plugins/builtin/cloud/askg/pending-question.ts +11 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
- package/src/plugins/builtin/cloud/plugin.tsx +19 -2
- package/src/plugins/builtin/cloud/team/model.ts +5 -0
- package/src/plugins/builtin/cloud/team/module.tsx +1 -0
- package/src/plugins/builtin/cloud/team/store.ts +8 -3
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +21 -7
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/headless.ts +97 -10
- package/src/plugins/builtin/company-kpis/model.ts +24 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +17 -16
- package/src/plugins/builtin/composite-plugins.ts +19 -13
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/cot/headless.ts +1 -1
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/cpi/headless.ts +3 -3
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +10 -1
- package/src/plugins/builtin/credit-boards/client.ts +6 -2
- package/src/plugins/builtin/credit-boards/headless.ts +108 -31
- package/src/plugins/builtin/credit-boards/index.tsx +2 -0
- package/src/plugins/builtin/credit-boards/model.ts +11 -1
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-conditions/client.ts +12 -9
- package/src/plugins/builtin/credit-conditions/headless.ts +13 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +18 -9
- package/src/plugins/builtin/credit-conditions/model.ts +19 -11
- package/src/plugins/builtin/credit-conditions/series.ts +16 -0
- package/src/plugins/builtin/credit-documents/headless.ts +33 -3
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/client.ts +3 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +7 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/dividend-yield/types.ts +2 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/headless.ts +12 -8
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/earnings/index.tsx +9 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +28 -2
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -1
- package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
- package/src/plugins/builtin/econ/fred-catalog.ts +198 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/equity-screener/headless.ts +3 -3
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
- package/src/plugins/builtin/exposure/drivers.ts +13 -0
- package/src/plugins/builtin/exposure/headless.ts +205 -13
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/index.tsx +1 -0
- package/src/plugins/builtin/exposure/model.ts +23 -3
- package/src/plugins/builtin/exposure/pane.tsx +14 -12
- package/src/plugins/builtin/fear-greed/index.tsx +18 -10
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +71 -10
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/client.ts +63 -0
- package/src/plugins/builtin/fx-matrix/export.ts +2 -1
- package/src/plugins/builtin/fx-matrix/headless.ts +154 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -4
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +58 -27
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +101 -16
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/function-index.ts +2 -0
- package/src/plugins/builtin/help/index.tsx +31 -9
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +36 -133
- package/src/plugins/builtin/holders/pane.tsx +11 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +26 -9
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
- package/src/plugins/builtin/iv-history/client.ts +20 -6
- package/src/plugins/builtin/iv-history/format.ts +9 -0
- package/src/plugins/builtin/iv-history/headless.ts +56 -21
- package/src/plugins/builtin/iv-history/index.tsx +2 -0
- package/src/plugins/builtin/iv-history/model.ts +143 -8
- package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +26 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/macro-day/client.ts +44 -3
- package/src/plugins/builtin/macro-day/headless.ts +13 -10
- package/src/plugins/builtin/macro-day/model.ts +56 -10
- package/src/plugins/builtin/macro-day/pane.tsx +30 -20
- package/src/plugins/builtin/market-heatmap/headless.ts +20 -5
- package/src/plugins/builtin/market-heatmap/index.tsx +6 -0
- package/src/plugins/builtin/market-heatmap/model.ts +19 -11
- package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/index.tsx +3 -2
- package/src/plugins/builtin/market-movers/model.ts +30 -0
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +8 -3
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +6 -8
- package/src/plugins/builtin/mna/distress/headless.ts +4 -4
- package/src/plugins/builtin/mna/headless.ts +18 -6
- package/src/plugins/builtin/money-markets/headless.ts +5 -4
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/news/index.tsx +0 -2
- package/src/plugins/builtin/news/wire/news/detail-view.tsx +53 -104
- package/src/plugins/builtin/news/wire/news/footer.ts +2 -25
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +0 -2
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/notes/index.tsx +1 -0
- package/src/plugins/builtin/options/index.tsx +3 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +4 -3
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +2 -1
- package/src/plugins/builtin/options-scenario/headless.ts +1 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
- package/src/plugins/builtin/perps/client.ts +90 -16
- package/src/plugins/builtin/perps/equity-row.tsx +52 -17
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +47 -32
- package/src/plugins/builtin/perps/index.tsx +23 -4
- package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
- package/src/plugins/builtin/perps/model.ts +20 -5
- package/src/plugins/builtin/perps/open-market.ts +15 -0
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +73 -67
- package/src/plugins/builtin/plugin-module.ts +3 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +5 -4
- package/src/plugins/builtin/portfolio-list/cell-version.ts +8 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +161 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +384 -74
- package/src/plugins/builtin/portfolio-list/cli/render.ts +437 -71
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +21 -4
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +121 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +104 -26
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +84 -18
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/mutations.ts +2 -9
- package/src/plugins/builtin/portfolio-list/pane/data.ts +5 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +52 -9
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +33 -11
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +21 -1
- package/src/plugins/builtin/portfolio-list/settings.ts +17 -0
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +9 -4
- package/src/plugins/builtin/portfolio-list/table.tsx +37 -7
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +63 -10
- package/src/plugins/builtin/power/headless.ts +77 -9
- package/src/plugins/builtin/power/index.tsx +28 -3
- package/src/plugins/builtin/power/model.ts +73 -2
- package/src/plugins/builtin/power/pane.tsx +37 -9
- package/src/plugins/builtin/power/prices-client.ts +139 -0
- package/src/plugins/builtin/power/prices-headless.ts +137 -0
- package/src/plugins/builtin/power/prices-model.ts +103 -0
- package/src/plugins/builtin/power/prices.tsx +179 -0
- package/src/plugins/builtin/rate-path/headless.ts +46 -18
- package/src/plugins/builtin/realized-vol/headless.ts +5 -0
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/research/index.tsx +2 -0
- package/src/plugins/builtin/research-search/article.ts +65 -0
- package/src/plugins/builtin/research-search/document-view.tsx +33 -215
- package/src/plugins/builtin/research-search/model.ts +0 -9
- package/src/plugins/builtin/research-search/pane.tsx +41 -30
- package/src/plugins/builtin/research-search/snippet.ts +0 -29
- package/src/plugins/builtin/revenue-breakdown/headless.ts +18 -6
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/sec/headless.ts +25 -3
- package/src/plugins/builtin/sec/index.tsx +2 -1
- package/src/plugins/builtin/series-inventory.ts +93 -0
- package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +30 -12
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/free-preview.ts +47 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +12 -1
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/shared/volatility/options.ts +4 -1
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +2 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +3 -2
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +2 -1
- package/src/plugins/builtin/short-volume/index.tsx +2 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/headless.ts +1 -1
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/headless.ts +43 -11
- package/src/plugins/builtin/supply-chain/model.ts +13 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +8 -6
- package/src/plugins/builtin/themes/headless.ts +74 -10
- package/src/plugins/builtin/themes/model.ts +71 -4
- package/src/plugins/builtin/themes/pane.tsx +22 -5
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +114 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +57 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +19 -55
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +86 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +26 -11
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +49 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +2 -1
- package/src/plugins/builtin/time-sales/index.tsx +2 -1
- package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
- package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
- package/src/plugins/builtin/vol-surface/headless.ts +51 -17
- package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
- package/src/plugins/builtin/vol-surface/model.ts +39 -6
- package/src/plugins/builtin/vol-surface/pane.tsx +14 -13
- package/src/plugins/builtin/vol-surface/stored.ts +63 -3
- package/src/plugins/builtin/vol-surface/surface.tsx +30 -2
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +6 -5
- package/src/plugins/builtin/world-venue-map/basemap.ts +6 -3
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +2 -1
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +24 -13
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +144 -13
- package/src/plugins/builtin/world-venue-map/model.ts +56 -19
- package/src/plugins/builtin/world-venue-map/pane.tsx +5 -2
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +4 -2
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/installer.ts +2 -2
- package/src/plugins/loader.ts +15 -0
- package/src/plugins/manager-node.ts +1 -1
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/feed.ts +2 -2
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/model.ts +6 -6
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
- package/src/plugins/registry/host-actions.ts +11 -0
- package/src/plugins/registry/index.ts +89 -4
- package/src/portfolio/collection-name.ts +31 -0
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/portfolio/valuation-basis.ts +79 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/react/input.ts +33 -1
- package/src/react/key-repeat.ts +86 -0
- package/src/remote/app-host.tsx +20 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +144 -7
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +65 -2
- package/src/remote/types.ts +30 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +4 -3
- package/src/renderers/browser/research-entry.ts +4 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +30 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +16 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
- package/src/renderers/dom/host/box.tsx +8 -0
- package/src/renderers/dom/host/chart-surface.tsx +33 -2
- package/src/renderers/dom/host/live-frame.ts +6 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/scroll-box.tsx +32 -0
- package/src/renderers/dom/host/size-observer.ts +41 -0
- package/src/renderers/dom/host/sticky-scroll.ts +128 -0
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/host/surface-3d.tsx +19 -7
- package/src/renderers/dom/host/surface-fit.ts +406 -0
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/input-host.tsx +1 -0
- package/src/renderers/dom/styles.css +102 -5
- package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
- package/src/renderers/electrobun/bun/index.ts +3 -2
- package/src/renderers/electrobun/shared/protocol.ts +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +29 -2
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +63 -20
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +5 -3
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +2 -0
- package/src/renderers/opentui/chart-surface.tsx +1 -0
- package/src/renderers/opentui/frame-capture.ts +33 -0
- package/src/renderers/opentui/host.tsx +10 -13
- package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
- package/src/renderers/opentui/pane-image/glyphs.json +1 -0
- package/src/renderers/opentui/pane-image/index.ts +107 -0
- package/src/renderers/opentui/pane-image/raster.ts +428 -0
- package/src/renderers/opentui/start.tsx +3 -3
- package/src/renderers/opentui/test-utils.tsx +3 -0
- package/src/renderers/opentui/ui-host.tsx +29 -1
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +108 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +153 -0
- package/src/series/validate.ts +171 -0
- package/src/shares/chat.ts +34 -0
- package/src/shares/dialog.tsx +295 -0
- package/src/shares/flow.tsx +56 -0
- package/src/shares/live.ts +14 -22
- package/src/shares/pane-image.ts +152 -0
- package/src/shares/personal-pane.ts +64 -0
- package/src/shares/portable-layout.ts +4 -2
- package/src/shares/prepare.ts +20 -0
- package/src/sources/gloomberb-cloud/index.ts +109 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +3 -0
- package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
- package/src/sources/history-result.ts +1 -0
- package/src/sources/history-retention.ts +11 -0
- package/src/sources/listing-symbols.ts +4 -1
- package/src/sources/listing-target.ts +4 -0
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +1 -1
- package/src/sources/provider-router/financial-routes.ts +1 -0
- package/src/sources/provider-router/financials.ts +13 -0
- package/src/sources/provider-router/history.ts +43 -25
- package/src/sources/provider-router/index.ts +14 -0
- package/src/sources/provider-router/search.ts +1 -0
- package/src/state/hooks/inline-tickers.ts +49 -11
- package/src/sync/core-contributors.ts +1 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/theme-context.tsx +15 -1
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +34 -1
- package/src/tickers/listing-name.ts +34 -0
- package/src/tickers/listing-venues.ts +132 -0
- package/src/tickers/open-target.ts +13 -4
- package/src/tickers/perpetual-listing.ts +151 -0
- package/src/tickers/prediction-listing.ts +26 -0
- package/src/tickers/search/index.ts +72 -11
- package/src/tickers/search/ranking.ts +98 -20
- package/src/tickers/search/types.ts +7 -1
- package/src/tickers/ticker-changes.ts +213 -0
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/economic.ts +2 -6
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/ice-credit.ts +39 -0
- package/src/time-series/resolve.ts +211 -29
- package/src/time-series/studies.ts +1 -0
- package/src/time-series/types.ts +12 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +10 -2
- package/src/types/data-provider.ts +11 -0
- package/src/types/financials.ts +62 -1
- package/src/types/headless.ts +113 -0
- package/src/types/instrument.ts +4 -0
- package/src/types/plugin.ts +52 -2
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker-changes.ts +26 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/context-menu.tsx +13 -1
- package/src/ui/host.tsx +57 -3
- package/src/ui/live-resize.ts +91 -0
- package/src/ui/live-stretch.ts +153 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/sticky-scroll.ts +7 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/central-banks.ts +76 -0
- package/src/utils/cli-output.ts +131 -15
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/currency-units.ts +11 -0
- package/src/utils/datetime-format.ts +27 -6
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +439 -52
- package/src/utils/double-escape-close.ts +15 -1
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +143 -1
- package/src/utils/format.ts +35 -5
- package/src/utils/forward-dividend.ts +14 -0
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +15 -5
- package/src/utils/fx-status.ts +6 -1
- package/src/utils/inline-content-tokenizer.ts +22 -3
- package/src/utils/market-capitalization.ts +8 -0
- package/src/utils/one-shot.ts +73 -0
- package/src/utils/options.ts +37 -8
- package/src/utils/price-history.ts +99 -42
- package/src/utils/sec-eps-basis.ts +24 -11
- package/src/utils/sec.ts +61 -7
- package/src/utils/utc-time.ts +63 -4
- package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/activation.ts +0 -0
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/ids.ts +0 -0
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/packs.ts +0 -0
- /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
|
@@ -1,15 +1,194 @@
|
|
|
1
|
-
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
|
-
import type { ExposureRequest } from "../../../api-client/exposure";
|
|
1
|
+
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/plugin";
|
|
2
|
+
import type { ExposureExtensionObservation, ExposurePayload, ExposureRequest } from "../../../api-client/exposure";
|
|
3
|
+
import { cliTerminalWidth } from "../../../utils/cli-output";
|
|
4
|
+
import { clipToDisplayWidth, displayWidth } from "../../../utils/format";
|
|
5
|
+
import { fiscalLabel } from "../company-kpis/model";
|
|
6
|
+
import { humanLabel } from "../shared/research-cells";
|
|
3
7
|
import { fetchExposure, fetchScenarios } from "./client";
|
|
4
|
-
import { DEFAULT_SCENARIO, parseCustomScenario, parseHoldings, TABS } from "./model";
|
|
8
|
+
import { DEFAULT_SCENARIO, exposureCellText, parseCustomScenario, parseHoldings, pathRows, portfolioRows, scenarioTitle, tableRows, TABS, type ExposureRow } from "./model";
|
|
5
9
|
import { portfolioHoldings, watchlistHoldings } from "./holdings";
|
|
10
|
+
import { describeCollections, findNamedCollection } from "../../../portfolio/collection-name";
|
|
11
|
+
import { driverAmount, driverRows, driverScope } from "./drivers";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* The text report prints each section as its pane tab draws it: exposure
|
|
15
|
+
* ranges and weights as percents, a driver's value compact with its unit
|
|
16
|
+
* (201.18B USD), its fiscal period as KPIS prints it (FY2024) and its scope in
|
|
17
|
+
* words. The title names the scenario, so a one-shock scenario needs no shock
|
|
18
|
+
* column. Every table fits 100 columns, or a narrower terminal: names and
|
|
19
|
+
* scopes take the room the figures leave and are cut at a word only when they
|
|
20
|
+
* cannot fit. Caveats print under their row; a holding's own caveats are the
|
|
21
|
+
* report's error lines. CSV, NDJSON and JSON keep the records whole.
|
|
22
|
+
*/
|
|
23
|
+
type CellId = Parameters<typeof exposureCellText>[1];
|
|
24
|
+
type TextRow = ExposureRow & { kind?: string; note?: string | null };
|
|
25
|
+
/** A column's cells, and how far it may narrow to fit: `min` lets it give way, cut at a word; `long` is a fuller form printed only when the whole table fits with it. */
|
|
26
|
+
interface TextColumn { key: string; header: string; read: (row: TextRow) => string; right?: boolean; min?: number; long?: (row: TextRow) => string }
|
|
27
|
+
type ColumnOptions = Omit<TextColumn, "key" | "header" | "read">;
|
|
28
|
+
const REPORT_WIDTH = 100;
|
|
29
|
+
/** The space the report renderer puts between columns. */
|
|
30
|
+
const GAP = 2;
|
|
31
|
+
|
|
32
|
+
const column = (key: string, header: string, read: TextColumn["read"], options: ColumnOptions = {}): TextColumn => ({ key, header, read, ...options });
|
|
33
|
+
const cell = (id: CellId, header: string, options?: ColumnOptions) => column(id, header, row => exposureCellText(row, id), options);
|
|
34
|
+
const holding = (header: string) => column("symbol", header, row => row.symbol);
|
|
35
|
+
/** The narrowest a name may get before the table gives up fitting, as the report renderer allows. */
|
|
36
|
+
const NAME = 8;
|
|
37
|
+
const period = cell("period", "Period");
|
|
38
|
+
const note: HeadlessPaneColumn = { key: "note", header: "Note", underRow: true };
|
|
39
|
+
const textRows = (items: TextRow[]) => items as unknown as HeadlessPaneRow[];
|
|
40
|
+
/** Caveats that belong to one row, printed under it; null prints nothing. */
|
|
41
|
+
const withNotes = (items: ExposureRow[], read: (row: ExposureRow) => string[]): TextRow[] => items.map(row => ({ ...row, note: read(row).join("\n") || null }));
|
|
42
|
+
|
|
43
|
+
/** Whole words that fit `width`, then an ellipsis, with no separator left dangling; a first word too long for it is cut. */
|
|
44
|
+
function clipWords(text: string, width: number): string {
|
|
45
|
+
if (displayWidth(text) <= width) return text;
|
|
46
|
+
let kept = "";
|
|
47
|
+
for (const word of text.split(" ")) {
|
|
48
|
+
const next = kept ? `${kept} ${word}` : word;
|
|
49
|
+
if (displayWidth(next) > width - 1) break;
|
|
50
|
+
kept = next;
|
|
51
|
+
}
|
|
52
|
+
kept = kept.replace(/[\s·,;:(/→-]+$/, "");
|
|
53
|
+
return kept ? `${kept}…` : clipToDisplayWidth(text, width);
|
|
54
|
+
}
|
|
55
|
+
/** The narrowest width past `width` at which a cut cell shows more of its text. */
|
|
56
|
+
function revealWidth(text: string, width: number): number {
|
|
57
|
+
const shown = clipWords(text, width);
|
|
58
|
+
for (let next = displayWidth(shown) + 1; next < displayWidth(text); next++) if (clipWords(text, next) !== shown) return next;
|
|
59
|
+
return displayWidth(text);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Column widths for `rows` within `budget`. The fuller forms print when the
|
|
64
|
+
* whole table fits with them. Otherwise the widest narrowing column gives up
|
|
65
|
+
* a cell at a time until the table fits, or each is at its floor; then the
|
|
66
|
+
* room a word cut leaves goes back, a word at a time, to the cut cell that
|
|
67
|
+
* needs the least of it.
|
|
68
|
+
*/
|
|
69
|
+
function fit(columns: TextColumn[], rows: TextRow[], budget: number): { read: TextColumn["read"]; width: number }[] {
|
|
70
|
+
const header = (index: number) => displayWidth(columns[index]!.header);
|
|
71
|
+
const measure = (reads: TextColumn["read"][]) => reads.map((read, index) => Math.max(header(index), ...rows.map(row => displayWidth(read(row)))));
|
|
72
|
+
const total = (widths: number[]) => widths.reduce((sum, width) => sum + width, 0) + GAP * (widths.length - 1);
|
|
73
|
+
const long = columns.map(item => item.long ?? item.read);
|
|
74
|
+
const fuller = measure(long);
|
|
75
|
+
if (total(fuller) <= budget) return columns.map((_, index) => ({ read: long[index]!, width: fuller[index]! }));
|
|
76
|
+
const reads = columns.map(item => item.read);
|
|
77
|
+
const narrowing = columns.flatMap((item, index) => item.min === undefined ? [] : [index]);
|
|
78
|
+
const floor = (index: number) => Math.max(columns[index]!.min!, header(index));
|
|
79
|
+
let widths = measure(reads);
|
|
80
|
+
for (let overflow = total(widths) - budget; overflow > 0; overflow--) {
|
|
81
|
+
const widest = narrowing.reduce((pick, index) => widths[index]! > floor(index) && (pick < 0 || widths[index]! > widths[pick]!) ? index : pick, -1);
|
|
82
|
+
if (widest < 0) break;
|
|
83
|
+
widths[widest]!--;
|
|
84
|
+
}
|
|
85
|
+
const shown = (allotted: number[]) => allotted.map((width, index) => !narrowing.includes(index) ? width
|
|
86
|
+
: Math.max(header(index), ...rows.map(row => displayWidth(clipWords(reads[index]!(row), width)))));
|
|
87
|
+
widths = shown(widths);
|
|
88
|
+
for (;;) {
|
|
89
|
+
const spare = budget - total(widths);
|
|
90
|
+
let best: { index: number; width: number } | null = null;
|
|
91
|
+
for (const index of narrowing) for (const row of rows) {
|
|
92
|
+
const text = reads[index]!(row);
|
|
93
|
+
if (displayWidth(text) <= widths[index]!) continue;
|
|
94
|
+
const width = revealWidth(text, widths[index]!);
|
|
95
|
+
if (width - widths[index]! <= spare && (!best || width - widths[index]! < best.width - widths[best.index]!)) best = { index, width };
|
|
96
|
+
}
|
|
97
|
+
if (!best) break;
|
|
98
|
+
widths = shown(widths.map((width, index) => index === best!.index ? best!.width : width));
|
|
99
|
+
}
|
|
100
|
+
return columns.map((_, index) => ({ read: reads[index]!, width: widths[index]! }));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The text report's columns for these rows, fitted when they print, to 100 columns or the terminal when it is narrower. */
|
|
104
|
+
function fitted(columns: TextColumn[], rows: TextRow[], under: HeadlessPaneColumn[] = []): HeadlessPaneColumn[] {
|
|
105
|
+
const layouts = new Map<number, ReturnType<typeof fit>>();
|
|
106
|
+
const layout = () => {
|
|
107
|
+
const budget = Math.min(REPORT_WIDTH, cliTerminalWidth() ?? REPORT_WIDTH);
|
|
108
|
+
if (!layouts.has(budget)) layouts.set(budget, fit(columns, rows, budget));
|
|
109
|
+
return layouts.get(budget)!;
|
|
110
|
+
};
|
|
111
|
+
return [...columns.map((item, index): HeadlessPaneColumn => ({ key: item.key, header: item.header, ...(item.right ? { align: "right" as const } : {}),
|
|
112
|
+
format: (_value, row) => { const { read, width } = layout()[index]!; return clipWords(read(row as unknown as TextRow), width); } })), ...under];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function holdingColumns(data: ExposurePayload): TextColumn[] {
|
|
116
|
+
return [holding("Holding"), ...(data.scenario.shocks.length > 1 ? [cell("label", "Shock", { min: NAME })] : []), cell("basis", "Basis"),
|
|
117
|
+
cell("exposure", "Exposure %", { right: true }), cell("classification", "Evidence", { min: 10 }), cell("impact", "Stress est. %", { right: true }), period, cell("weight", "NAV wt. %", { right: true })];
|
|
118
|
+
}
|
|
119
|
+
const PATH_COLUMNS = [holding("Holding"), cell("label", "Path", { min: NAME }), cell("basis", "Basis"), cell("exposure", "Exposure %", { right: true }),
|
|
120
|
+
cell("classification", "Evidence", { min: 10 }), cell("impact", "Stress est. %", { right: true }), period];
|
|
121
|
+
|
|
122
|
+
/** A credit line's figures repeat the instrument their scope names: "$1.0 billion revolver: drawn" reads Drawn. */
|
|
123
|
+
function driverLabel(driver: ExposureExtensionObservation): string {
|
|
124
|
+
const named = Object.values(driver.dimensions ?? {}).find(value => driver.label.startsWith(`${value}: `));
|
|
125
|
+
const rest = named ? driver.label.slice(named.length + 2) : driver.label;
|
|
126
|
+
return rest.charAt(0).toUpperCase() + rest.slice(1);
|
|
127
|
+
}
|
|
128
|
+
/** The scope in words, after the basis when the figure is not the reported one: Adjusted · diluted. */
|
|
129
|
+
function driverScopeText(driver: ExposureExtensionObservation): string {
|
|
130
|
+
return [driver.basis && driver.basis !== "reported" ? humanLabel(driver.basis) : "", driverScope(driver)].filter(Boolean).join(" · ") || "--";
|
|
131
|
+
}
|
|
132
|
+
const MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
|
|
133
|
+
/**
|
|
134
|
+
* A disclosure's fiscal period as KPIS prints it, FY2024 or Q3 FY2026, with
|
|
135
|
+
* the date it ended (FY2024 · Sep 28, 2024) as the fuller form. The source
|
|
136
|
+
* words it freely; wording that is not a fiscal year or quarter prints as is.
|
|
137
|
+
*/
|
|
138
|
+
function driverPeriod(driver: ExposureExtensionObservation): { short: string; long: string } {
|
|
139
|
+
const text = driver.period?.trim() || "--";
|
|
140
|
+
const match = /^(?:full\s+)?(?:Q([1-4])\s+)?(?:fiscal(?:\s+year)?\s+|FY\s?)(\d{4})(?:\s+ended\s+(.+))?$/i.exec(text);
|
|
141
|
+
if (!match) return { short: text, long: text };
|
|
142
|
+
const quarter = match[1] ? Number(match[1]) : null;
|
|
143
|
+
const short = fiscalLabel({ period: { start: null, end: null, kind: quarter ? "quarter" : "year", fiscalYear: Number(match[2]), fiscalQuarter: quarter, label: text } });
|
|
144
|
+
const ended = /^([A-Za-z]{3})[a-z]*\.?\s+(\d{1,2}),?\s+(\d{4})$/.exec(match[3]?.trim() ?? "");
|
|
145
|
+
const month = ended && MONTHS.includes(ended[1]!.toLowerCase()) ? `${ended[1]!.charAt(0).toUpperCase()}${ended[1]!.slice(1).toLowerCase()}` : null;
|
|
146
|
+
return { short, long: month ? `${short} · ${month} ${ended![2]}, ${ended![3]}` : short };
|
|
147
|
+
}
|
|
148
|
+
const DRIVER_COLUMNS = [holding("Holding"), column("label", "Company driver", row => row.driver ? driverLabel(row.driver) : row.label, { min: NAME }),
|
|
149
|
+
column("value", "Value", row => row.driver ? driverAmount(row.driver) : "--", { right: true }),
|
|
150
|
+
column("period", "Period", row => row.driver ? driverPeriod(row.driver).short : "--", { min: 10, long: row => row.driver ? driverPeriod(row.driver).long : "--" }),
|
|
151
|
+
column("scope", "Scope", row => row.driver ? driverScopeText(row.driver) : "--", { min: NAME })];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* An anonymous counterparty leads with the name its filing gives it, and the
|
|
155
|
+
* Kind column already says what it is: "Undisclosed customer (Customer one)"
|
|
156
|
+
* reads "Customer one (undisclosed)", which still tells two apart when cut.
|
|
157
|
+
*/
|
|
158
|
+
const targetName = (row: TextRow) => {
|
|
159
|
+
const match = row.kind ? new RegExp(`^Undisclosed ${row.kind} \\((.+)\\)$`, "i").exec(row.label) : null;
|
|
160
|
+
return match ? `${match[1]} (undisclosed)` : row.label;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Stress lines are always estimates, so the unknown share of holdings under a
|
|
165
|
+
* line says how partial it is. A concentration's share is unknown or an
|
|
166
|
+
* estimate, so it says only when its bound is partial, leaving out
|
|
167
|
+
* relationships without a share. With one holding, which
|
|
168
|
+
* holdings a line covers and their weight say nothing the Holdings table does
|
|
169
|
+
* not.
|
|
170
|
+
*/
|
|
171
|
+
function portfolioColumns(data: ExposurePayload, concentration: boolean, items: ExposureRow[]): TextColumn[] {
|
|
172
|
+
const several = data.holdings.length > 1;
|
|
173
|
+
// Signed exposure differs from gross only where a holding is short.
|
|
174
|
+
const signed = concentration && items.some(row => JSON.stringify(row.impact) !== JSON.stringify(row.exposure));
|
|
175
|
+
const partial = (row: ExposureRow) => row.incomplete && row.exposure !== null;
|
|
176
|
+
const holdings = several ? [holding("Holdings")] : [];
|
|
177
|
+
if (concentration) return [column("kind", "Kind", row => humanLabel(row.kind ?? "")), ...holdings, column("label", "Target", targetName, { min: NAME }),
|
|
178
|
+
cell("basis", "Basis"), cell("exposure", "Gross exp. %", { right: true }), ...(signed ? [cell("impact", "Signed exp. %", { right: true })] : []),
|
|
179
|
+
...(items.some(partial) ? [column("bound", "Bound", row => partial(row) ? "Partial" : "")] : []), period, ...(several ? [cell("weight", "Gross wt. %", { right: true })] : [])];
|
|
180
|
+
return [...holdings, ...(data.scenario.shocks.length > 1 ? [cell("label", "Target", { min: NAME })] : []), cell("basis", "Basis"),
|
|
181
|
+
cell("exposure", "Exposure %", { right: true }), cell("impact", "Stress est. %", { right: true }), period, cell("weight", "Covered wt. %", { right: true })];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const CONCENTRATION_VIEWS = ["country", "supplier", "customer"] as const;
|
|
6
185
|
|
|
7
186
|
export const exposureHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
8
187
|
shape: "bundle",
|
|
9
188
|
freshness: { status: "not-a-feed", basis: "company disclosures" },
|
|
10
189
|
argument: { kind: "free-text", optional: true, placeholder: "AAPL=60% NVDA=40%", description: "Tickers, signed NAV weights, PORT:<id>, or WATCH:<id>. Unweighted lists and watchlists use equal weights." },
|
|
11
190
|
discovery: { aliases: ["EXPO"], dataRequirements: ["Gloom Cloud geographic revenue, disclosed supply chains, KPIs, guidance and credit disclosures", "Portfolio NAV for local PORT weights"],
|
|
12
|
-
limitations: ["Pro: full holdings and up to four-hop paths; free: one holding, one hop", "Operating exposure estimates, never predicted stock returns", "Missing relationships and sensitivities remain unknown", "Basis and reporting period are kept separate"] },
|
|
191
|
+
limitations: ["Pro: full holdings and up to four-hop paths; free: one holding, one hop.", "Operating exposure estimates, never predicted stock returns.", "Missing relationships and sensitivities remain unknown.", "Basis and reporting period are kept separate."] },
|
|
13
192
|
options: [
|
|
14
193
|
{ key: "scenario", type: "string", settingKey: "scenario", defaultValue: "taiwan-disruption", description: "Library scenario id, or use --custom." },
|
|
15
194
|
{ key: "custom", type: "string", settingKey: "customScenario", description: "JSON scenario with label and shocks; supports multiple shocks and explicit transmission assumptions." },
|
|
@@ -19,19 +198,23 @@ export const exposureHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
19
198
|
{ key: "tab", type: "enum", settingKey: "tab", defaultValue: "table", values: TABS.map(t => ({ value: t.value })), description: "Pane tab." },
|
|
20
199
|
{ key: "view", type: "enum", settingKey: "view", defaultValue: "stress", values: ["stress", "country", "supplier", "customer"].map(value => ({ value })), description: "Portfolio stress or concentration grouping." },
|
|
21
200
|
],
|
|
22
|
-
describe: args =>
|
|
201
|
+
describe: (args, result) => {
|
|
202
|
+
const payload = result?.metadata?.payload as ExposurePayload | undefined;
|
|
203
|
+
return `Exposure | ${args.rawArgument || "holdings"}${payload ? ` | ${scenarioTitle(payload.scenario)}` : ""}`;
|
|
204
|
+
},
|
|
23
205
|
async load(args, ctx) {
|
|
24
206
|
const input = args.rawArgument.trim();
|
|
25
207
|
const source = /^(PORT|WATCH):(.+)$/i.exec(input);
|
|
26
208
|
let holdings: ExposureRequest["holdings"];
|
|
27
209
|
if (source?.[1]?.toUpperCase() === "PORT") {
|
|
28
210
|
const local = await ctx.resolvePortfolio?.(source[2]!);
|
|
29
|
-
if (!local) throw new Error(`Unknown local portfolio: ${source[2]}`);
|
|
211
|
+
if (!local) throw new Error(`Unknown local portfolio: ${source[2]}.${ctx.config.portfolios.length ? ` Portfolios: ${describeCollections(ctx.config.portfolios)}.` : ""}`);
|
|
30
212
|
holdings = await portfolioHoldings(local.portfolio, local.tickers, Number(args.options.nav), ctx.marketData);
|
|
31
213
|
} else if (source) {
|
|
32
214
|
const tickers = await ctx.resolveWatchlist?.(source[2]!);
|
|
33
|
-
if (!tickers) throw new Error(`Unknown local watchlist: ${source[2]}`);
|
|
34
|
-
|
|
215
|
+
if (!tickers) throw new Error(`Unknown local watchlist: ${source[2]}.${ctx.config.watchlists.length ? ` Watchlists: ${describeCollections(ctx.config.watchlists)}.` : ""}`);
|
|
216
|
+
// The host reads the name in any case; membership is kept under the configured id.
|
|
217
|
+
holdings = watchlistHoldings(tickers, findNamedCollection(ctx.config.watchlists, source[2])?.id ?? source[2]!);
|
|
35
218
|
} else holdings = parseHoldings(input);
|
|
36
219
|
const id = String(args.options.scenario ?? DEFAULT_SCENARIO.id);
|
|
37
220
|
const scenario = args.options.custom ? parseCustomScenario(String(args.options.custom)) : (await fetchScenarios(ctx.apiClient)).find(s => s.id === id);
|
|
@@ -40,14 +223,23 @@ export const exposureHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
40
223
|
if (cashWeight !== undefined && (!Number.isFinite(cashWeight) || Math.abs(cashWeight) > 10)) throw new Error("Cash must be a NAV fraction between -10 and 10.");
|
|
41
224
|
if (ctx.signal.aborted) throw new Error("Exposure analysis cancelled.");
|
|
42
225
|
const data = await fetchExposure({ holdings, scenario, depth: Number(args.options.depth ?? 2), ...(cashWeight === undefined ? {} : { cashWeight }) }, ctx.apiClient);
|
|
226
|
+
const drivers = withNotes(driverRows(data), row => row.driver?.notes ?? []);
|
|
227
|
+
const stress = withNotes(portfolioRows(data, "stress"), row => row.incomplete ? row.unknowns : []);
|
|
228
|
+
const concentrations = CONCENTRATION_VIEWS.flatMap(kind => portfolioRows(data, kind).map(row => ({ ...row, kind })));
|
|
229
|
+
const holdingRows = tableRows(data), paths = pathRows(data);
|
|
43
230
|
return { complete: data.lockedHoldings === 0 && data.holdings.every(h => h.status === "quantified") && data.unknowns.length === 0,
|
|
44
231
|
errors: [...data.unknowns, ...(data.access === "preview" ? ["Free preview: one holding, depth one. Full analysis requires Pro."] : [])],
|
|
45
232
|
sections: [
|
|
46
|
-
{ title: "Holdings", rows: data.holdings.map(h => ({ symbol: h.symbol, weight: h.weight, status: h.status, measures: h.measures, coverage: h.coverage, unknowns: h.unknowns }))
|
|
47
|
-
|
|
48
|
-
{ title: "
|
|
49
|
-
|
|
50
|
-
{ title: "
|
|
233
|
+
{ title: "Holdings", rows: data.holdings.map(h => ({ symbol: h.symbol, weight: h.weight, status: h.status, measures: h.measures, coverage: h.coverage, unknowns: h.unknowns })),
|
|
234
|
+
textColumns: fitted(holdingColumns(data), holdingRows), textRows: textRows(holdingRows) },
|
|
235
|
+
{ title: "Company drivers", rows: data.holdings.flatMap(h => (h.extensions ?? []).map(row => ({ symbol: h.symbol, ...row }))),
|
|
236
|
+
textColumns: fitted(DRIVER_COLUMNS, drivers, [note]), textRows: textRows(drivers) },
|
|
237
|
+
{ title: "Paths and evidence", rows: data.holdings.flatMap(h => h.components.map(c => ({ symbol: h.symbol, ...c }))),
|
|
238
|
+
textColumns: fitted(PATH_COLUMNS, paths), textRows: textRows(paths) },
|
|
239
|
+
{ title: "Portfolio operating stress", rows: data.portfolio.measures.map(m => ({ ...m })),
|
|
240
|
+
textColumns: fitted(portfolioColumns(data, false, stress), stress, [note]), textRows: textRows(stress) },
|
|
241
|
+
{ title: "Concentrations", rows: data.portfolio.concentrations.map(c => ({ ...c })),
|
|
242
|
+
textColumns: fitted(portfolioColumns(data, true, concentrations), concentrations), textRows: textRows(concentrations) },
|
|
51
243
|
], metadata: { payload: data } };
|
|
52
244
|
},
|
|
53
245
|
};
|
|
@@ -4,7 +4,7 @@ import type { Portfolio, TickerRecord } from "../../../types/ticker";
|
|
|
4
4
|
import { publicTickerKey } from "../../../utils/exchanges";
|
|
5
5
|
import { resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
6
6
|
import { convertCurrency } from "../../../utils/format";
|
|
7
|
-
import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue } from "
|
|
7
|
+
import { getPortfolioPositionMetrics, getPortfolioQuoteDisplay, resolvePortfolioMarketValue } from "../../../portfolio/position-metrics";
|
|
8
8
|
|
|
9
9
|
/** Explicit NAV preserves cash, shorts and leverage; net holdings are never used as NAV. */
|
|
10
10
|
export async function portfolioHoldings(portfolio: Portfolio, tickers: TickerRecord[], nav: number, market: Pick<DataProvider, "getQuote" | "getExchangeRate">): Promise<ExposureHolding[]> {
|
|
@@ -4,6 +4,7 @@ import { ExposurePane } from "./pane";
|
|
|
4
4
|
|
|
5
5
|
export const exposureModule: PluginModule = {
|
|
6
6
|
panes: [{ id: "exposure", name: "Exposure", icon: "E", component: ExposurePane, defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 138, height: 31 }, tableExport: true, headless: exposureHeadless,
|
|
7
|
+
personalData: "holdings",
|
|
7
8
|
portableShare: { private: { params: true, settings: true, state: true } } }],
|
|
8
9
|
paneTemplates: [{ id: "exposure-pane", paneId: "exposure", label: "Exposure", description: "Pro scenario exposures across holdings, geographic revenue and supply chains, with source evidence and portfolio concentrations.",
|
|
9
10
|
keywords: ["exposure", "scenario", "stress", "portfolio", "supply", "country", "tariff", "pro"],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExposureBasis, ExposureComponent, ExposureExtensionObservation, ExposureHolding, ExposurePayload, ExposureRange, ExposureScenario } from "../../../api-client/exposure";
|
|
2
|
+
import { humanLabel } from "../shared/research-cells";
|
|
2
3
|
|
|
3
4
|
export const TABS = [{ value: "table", label: "Table" }, { value: "drivers", label: "Drivers" }, { value: "paths", label: "Paths" }, { value: "portfolio", label: "Portfolio" }];
|
|
4
5
|
export const DEFAULT_SCENARIO: ExposureScenario = { id: "taiwan-disruption", label: "Taiwan disruption", shocks: [{ id: "taiwan", kind: "country", target: "TW", changePct: -100 }] };
|
|
@@ -61,12 +62,31 @@ export interface ExposureRow {
|
|
|
61
62
|
exposure: ExposureRange | null; impact: ExposureRange | null; weight: number | null;
|
|
62
63
|
classification: string; incomplete: boolean; concentration?: boolean; components: ExposureComponent[]; unknowns: string[];
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
/** A table cell's text as the pane draws it; `fn EXPO` prints the same words. */
|
|
66
|
+
export function exposureCellText(row: ExposureRow, column: "label" | "basis" | "exposure" | "classification" | "impact" | "period" | "weight"): string {
|
|
67
|
+
switch (column) {
|
|
68
|
+
case "label": return row.label;
|
|
69
|
+
case "basis": return row.basis ? humanLabel(basisLabel(row.basis)) : "--";
|
|
70
|
+
case "exposure": return row.exposure ? rangeText(row.exposure) : "Unknown";
|
|
71
|
+
// What the number rests on, in words: disclosed by the company, estimated by a chain, or not visible.
|
|
72
|
+
case "classification": return `${humanLabel(row.classification)}${row.incomplete && row.classification !== "unknown" ? " · partial" : ""}`;
|
|
73
|
+
case "impact": return row.impact ? rangeText(row.impact) : "--";
|
|
74
|
+
case "period": return row.period ?? "--";
|
|
75
|
+
case "weight": return fraction(row.weight ?? 0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function shockText(shock: ExposureScenario["shocks"][number]): string {
|
|
67
79
|
const change = shock.changeBps ?? shock.changePct;
|
|
68
80
|
return `${shock.target}${change == null ? "" : ` ${change > 0 ? "+" : ""}${change}${shock.kind === "rate" ? "bp" : "%"}`}`;
|
|
69
81
|
}
|
|
82
|
+
function shockLabel(data: ExposurePayload, id: string): string {
|
|
83
|
+
const shock = data.scenario.shocks.find(s => s.id === id);
|
|
84
|
+
return shock ? shockText(shock) : id;
|
|
85
|
+
}
|
|
86
|
+
/** The scenario as the pane's scenario picker names it, with its one shock when it has one: Taiwan disruption (TW -100%). */
|
|
87
|
+
export function scenarioTitle(scenario: ExposureScenario): string {
|
|
88
|
+
return `${scenario.label}${scenario.shocks.length === 1 ? ` (${shockText(scenario.shocks[0]!)})` : ""}`;
|
|
89
|
+
}
|
|
70
90
|
export function tableRows(data: ExposurePayload): ExposureRow[] {
|
|
71
91
|
return data.holdings.flatMap<ExposureRow>(holding => [
|
|
72
92
|
...holding.measures.map((measure, i): ExposureRow => ({
|
|
@@ -10,6 +10,7 @@ import type { PaneProps } from "../../../types/plugin";
|
|
|
10
10
|
import { Box, ScrollBox, Text, useRendererHost, useUiCapabilities } from "../../../ui";
|
|
11
11
|
import { SignInWall } from "../cloud/auth-actions";
|
|
12
12
|
import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
|
|
13
|
+
import { useOpenDescription } from "../shared/open-description";
|
|
13
14
|
import { Blurred, LockedOverlay, UpgradeLabel } from "../shared/locked-rows";
|
|
14
15
|
import { humanLabel, listingCell, missingCell } from "../shared/research-cells";
|
|
15
16
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
@@ -17,9 +18,9 @@ import { ExposurePathDiagram, ExposureRanges } from "./charts";
|
|
|
17
18
|
import { fetchExposure, fetchScenarios, validateExposure } from "./client";
|
|
18
19
|
import { ExposureEditor, type ExposureInputs } from "./editor";
|
|
19
20
|
import { portfolioHoldings, watchlistHoldings } from "./holdings";
|
|
20
|
-
import { basisLabel, DEFAULT_SCENARIO, fraction, parseCustomScenario, parseHoldings, pathRows, portfolioRows, rangeText, tableRows, TABS, type ExposureRow } from "./model";
|
|
21
|
+
import { basisLabel, DEFAULT_SCENARIO, exposureCellText, fraction, parseCustomScenario, parseHoldings, pathRows, portfolioRows, rangeText, tableRows, TABS, type ExposureRow } from "./model";
|
|
21
22
|
import { useExposureEvidence } from "./evidence";
|
|
22
|
-
import { driverRows, driverUnits, driverValue } from "./drivers";
|
|
23
|
+
import { driverRows, driverScope, driverUnits, driverValue } from "./drivers";
|
|
23
24
|
|
|
24
25
|
function EvidenceItem({ item }: { item: ExposureEvidence }) {
|
|
25
26
|
const colors = useThemeColors();
|
|
@@ -79,6 +80,7 @@ export function ExposurePane({ width, height, focused }: PaneProps) {
|
|
|
79
80
|
const accessKey = `${session.requestKey}:${access.hasProAccess ? "full" : "preview"}`;
|
|
80
81
|
const upgrade = useCloudUpgradeAction("expo");
|
|
81
82
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
83
|
+
const openDescription = useOpenDescription();
|
|
82
84
|
const [openingScenario] = usePaneSettingValue("scenario", "taiwan-disruption");
|
|
83
85
|
const [customSetting] = usePaneSettingValue("customScenario", "");
|
|
84
86
|
const [openingDepth] = usePaneSettingValue("depth", 2);
|
|
@@ -130,7 +132,7 @@ export function ExposurePane({ width, height, focused }: PaneProps) {
|
|
|
130
132
|
usePaneRefreshKey(() => void resource.reload(), { focused: focused && !editing });
|
|
131
133
|
const rawRows = useMemo(() => !data ? [] : tab === "drivers" ? driverRows(data) : tab === "paths" ? pathRows(data) : tab === "portfolio" ? portfolioRows(data, view) : tableRows(data), [data, tab, view]);
|
|
132
134
|
const rows = useMemo(() => [...rawRows].sort((a, b) => {
|
|
133
|
-
const get = (r: ExposureRow) => sort.column === "value" ? r.driver?.value ?? r.driver?.range?.high ?? -Infinity : sort.column === "units" ? r.driver ? driverUnits(r.driver) : "" : sort.column === "scope" ?
|
|
135
|
+
const get = (r: ExposureRow) => sort.column === "value" ? r.driver?.value ?? r.driver?.range?.high ?? -Infinity : sort.column === "units" ? r.driver ? driverUnits(r.driver) : "" : sort.column === "scope" ? r.driver ? driverScope(r.driver) : "" : sort.column === "classification" ? r.classification : sort.column === "label" ? r.label : sort.column === "period" ? r.period ?? "" : sort.column === "exposure" ? r.exposure?.high ?? -Infinity : sort.column === "impact" ? r.impact?.low ?? -Infinity : sort.column === "weight" ? r.weight ?? -Infinity : sort.column === "basis" ? r.basis ?? "" : r.symbol;
|
|
134
136
|
const av = get(a), bv = get(b); const delta = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv));
|
|
135
137
|
return (sort.direction === "asc" ? delta : -delta) || a.id.localeCompare(b.id);
|
|
136
138
|
}), [rawRows, sort]);
|
|
@@ -150,7 +152,7 @@ export function ExposurePane({ width, height, focused }: PaneProps) {
|
|
|
150
152
|
...(symbol ? [
|
|
151
153
|
...(activeRow?.driver ? [{ id: "dataset", key: "a", label: "ll disclosures", title: "Open company disclosures", onPress: () => createPaneFromTemplate(activeRow.driver!.kind === "kpi" ? "company-kpis-pane" : activeRow.driver!.kind === "guidance" ? "company-guidance-pane" : "credit-documents-pane", { symbol }) }] : []),
|
|
152
154
|
{ id: "supply", key: "c", label: "hain", title: "Supply chain", onPress: () => createPaneFromTemplate("supply-chain-pane", { symbol }) },
|
|
153
|
-
{ id: "des", key: "d", label: "es", onPress: () =>
|
|
155
|
+
{ id: "des", key: "d", label: "es", onPress: () => openDescription(symbol) },
|
|
154
156
|
{ id: "fa", key: "f", label: "a", onPress: () => createPaneFromTemplate("financial-analysis-pane", { symbol }) },
|
|
155
157
|
{ id: "chart", key: "g", label: "raph", onPress: () => createPaneFromTemplate("chart-composer-pane", { arg: symbol }) },
|
|
156
158
|
] : []),
|
|
@@ -190,17 +192,17 @@ export function ExposurePane({ width, height, focused }: PaneProps) {
|
|
|
190
192
|
case "symbol": return row.symbol.includes(",") ? { text: row.symbol, color: colors.textBright } : listingCell(row.symbol, colors, state.selected);
|
|
191
193
|
case "value": return { text: row.driver ? driverValue(row.driver) : "--", value: row.driver?.value ?? null, color: colors.textBright };
|
|
192
194
|
case "units": return { text: row.driver ? driverUnits(row.driver) : "", color: colors.textDim };
|
|
193
|
-
case "scope": return { text:
|
|
194
|
-
case "label": return { text: row
|
|
195
|
-
case "basis": return row.basis ? { text:
|
|
196
|
-
case "exposure": return row.exposure ? { text:
|
|
195
|
+
case "scope": return { text: row.driver ? driverScope(row.driver) : "", color: colors.textDim };
|
|
196
|
+
case "label": return { text: exposureCellText(row, "label"), color: unknown ? colors.textDim : colors.text };
|
|
197
|
+
case "basis": return row.basis ? { text: exposureCellText(row, "basis"), color: colors.textDim } : missingCell(colors);
|
|
198
|
+
case "exposure": return row.exposure ? { text: exposureCellText(row, "exposure"), value: row.exposure.high, color: colors.textBright } : { text: exposureCellText(row, "exposure"), value: null, color: colors.textMuted };
|
|
197
199
|
// Disclosed figures stand out; estimates read plainly, and the word says which is which.
|
|
198
|
-
case "classification": return { text:
|
|
200
|
+
case "classification": return { text: exposureCellText(row, "classification"),
|
|
199
201
|
color: row.classification === "disclosed" ? colors.borderFocused : unknown ? colors.textMuted : colors.textDim, keepColorWhenSelected: row.classification === "disclosed" };
|
|
200
|
-
case "impact": return !row.impact ? missingCell(colors) : { text:
|
|
202
|
+
case "impact": return !row.impact ? missingCell(colors) : { text: exposureCellText(row, "impact"), value: row.impact.low,
|
|
201
203
|
color: row.concentration ? colors.text : row.impact.high < 0 ? colors.negative : row.impact.low > 0 ? colors.positive : colors.text };
|
|
202
|
-
case "period": return row.period ? { text: row
|
|
203
|
-
default: return { text:
|
|
204
|
+
case "period": return row.period ? { text: exposureCellText(row, "period"), color: colors.textDim } : missingCell(colors);
|
|
205
|
+
default: return { text: exposureCellText(row, "weight"), value: row.weight === null ? null : row.weight * 100, color: colors.text };
|
|
204
206
|
}
|
|
205
207
|
};
|
|
206
208
|
const lockedRows = data?.access === "preview" ? Math.min(3, data.lockedHoldings) : 0;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CLI_COMMAND_GROUPS } from "../../../cli/help";
|
|
2
|
+
import { rowsFreshness } from "../../../cli/freshness";
|
|
2
3
|
import type { GloomPlugin } from "../../../types/plugin";
|
|
3
4
|
import {
|
|
4
5
|
attachFearGreedPersistence,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
} from "./cache";
|
|
8
9
|
import { FearGreedPane } from "./pane";
|
|
9
10
|
import { fearGreedHeadless } from "./headless";
|
|
11
|
+
import { FEAR_GREED_SERIES } from "./series";
|
|
10
12
|
|
|
11
13
|
export const fearGreedPlugin: GloomPlugin = {
|
|
12
14
|
id: "fear-greed",
|
|
@@ -29,6 +31,9 @@ export const fearGreedPlugin: GloomPlugin = {
|
|
|
29
31
|
resetFearGreedPersistence();
|
|
30
32
|
},
|
|
31
33
|
|
|
34
|
+
// The index and its seven readings, chartable against anything else.
|
|
35
|
+
series: FEAR_GREED_SERIES,
|
|
36
|
+
|
|
32
37
|
panes: [
|
|
33
38
|
{
|
|
34
39
|
id: "fear-greed",
|
|
@@ -62,17 +67,20 @@ export const fearGreedPlugin: GloomPlugin = {
|
|
|
62
67
|
description: "Fetch CNN Fear & Greed gauge data",
|
|
63
68
|
help: { group: CLI_COMMAND_GROUPS.markets },
|
|
64
69
|
async execute(_args, ctx) {
|
|
65
|
-
const { data } = await loadFearGreed(ctx.cliOptions.refresh);
|
|
70
|
+
const { data, stale } = await loadFearGreed(ctx.cliOptions.refresh);
|
|
71
|
+
const rows = [{
|
|
72
|
+
score: data.overall.score,
|
|
73
|
+
rating: data.overall.rating,
|
|
74
|
+
updatedAt: data.overall.updatedAt?.toISOString() ?? "",
|
|
75
|
+
previousClose: data.overall.previousClose,
|
|
76
|
+
previousWeek: data.overall.previousWeek,
|
|
77
|
+
previousMonth: data.overall.previousMonth,
|
|
78
|
+
previousYear: data.overall.previousYear,
|
|
79
|
+
}];
|
|
66
80
|
ctx.printResult({
|
|
67
|
-
data:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
updatedAt: data.overall.updatedAt?.toISOString() ?? "",
|
|
71
|
-
previousClose: data.overall.previousClose,
|
|
72
|
-
previousWeek: data.overall.previousWeek,
|
|
73
|
-
previousMonth: data.overall.previousMonth,
|
|
74
|
-
previousYear: data.overall.previousYear,
|
|
75
|
-
}],
|
|
81
|
+
data: rows,
|
|
82
|
+
// Exports cite CNN's reading as `fn FNG` does; the text stays the row alone.
|
|
83
|
+
exportFreshness: rowsFreshness(rows, { source: "CNN", status: stale ? "stale" : "not-a-feed", basis: "published index" }),
|
|
76
84
|
metadata: {
|
|
77
85
|
indicators: data.indicators.map((indicator) => ({
|
|
78
86
|
id: indicator.definition.id,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { SeriesDefinition, SeriesLoadResult, SeriesPoint } from "../../../series/types";
|
|
2
|
+
import { cachedSeriesWarning } from "../shared/series-warning";
|
|
3
|
+
import { loadFearGreed } from "./cache";
|
|
4
|
+
import type { FearGreedData } from "./data";
|
|
5
|
+
|
|
6
|
+
/** The index refreshes through the session; the pane's cache keeps a copy for five minutes. */
|
|
7
|
+
const REFRESH_MS = 5 * 60_000;
|
|
8
|
+
|
|
9
|
+
type Reading = (data: FearGreedData) => SeriesPoint[];
|
|
10
|
+
|
|
11
|
+
function component(id: string, pick: "primary" | "secondary"): Reading {
|
|
12
|
+
return (data) => {
|
|
13
|
+
const indicator = data.indicators.find((entry) => entry.definition.id === id);
|
|
14
|
+
if (!indicator) throw new Error("This component is missing from the latest reading.");
|
|
15
|
+
if (pick === "primary") return indicator.points.map((point) => ({ date: point.date, value: point.close }));
|
|
16
|
+
return indicator.secondaryPoints.flatMap((point) => {
|
|
17
|
+
const date = indicator.points[point.index]?.date;
|
|
18
|
+
return date ? [{ date, value: point.value }] : [];
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function series(id: string, name: string, unit: string, read: Reading, tags: readonly string[]): SeriesDefinition {
|
|
24
|
+
return {
|
|
25
|
+
id: `fear-greed:${id}`,
|
|
26
|
+
name,
|
|
27
|
+
unit,
|
|
28
|
+
frequency: "daily",
|
|
29
|
+
group: "Sentiment",
|
|
30
|
+
tags: ["fear and greed", ...tags],
|
|
31
|
+
refreshMs: REFRESH_MS,
|
|
32
|
+
load: async (): Promise<SeriesLoadResult> => {
|
|
33
|
+
const result = await loadFearGreed();
|
|
34
|
+
const warning = cachedSeriesWarning(result);
|
|
35
|
+
return { points: read(result.data), ...(warning ? { warning } : {}) };
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The index and the seven readings it is built from, as the pane charts them. */
|
|
41
|
+
export const FEAR_GREED_SERIES: readonly SeriesDefinition[] = [
|
|
42
|
+
series("index", "Fear & Greed index", "score", (data) => data.overall.history.map((point) => ({ date: point.date, value: point.close })), ["sentiment"]),
|
|
43
|
+
series("market-momentum", "Fear & Greed: S&P 500 momentum", "index", component("market-momentum", "primary"), ["momentum", "S&P 500"]),
|
|
44
|
+
series("market-momentum-average", "Fear & Greed: S&P 500 125-day average", "index", component("market-momentum", "secondary"), ["momentum", "moving average"]),
|
|
45
|
+
series("stock-price-strength", "Fear & Greed: net new 52-week highs", "%", component("stock-price-strength", "primary"), ["breadth", "highs and lows"]),
|
|
46
|
+
series("stock-price-breadth", "Fear & Greed: McClellan volume summation", "index", component("stock-price-breadth", "primary"), ["breadth", "McClellan"]),
|
|
47
|
+
series("put-call-options", "Fear & Greed: 5-day put/call ratio", "ratio", component("put-call-options", "primary"), ["options", "put call"]),
|
|
48
|
+
series("market-volatility", "Fear & Greed: VIX", "index", component("market-volatility", "primary"), ["volatility", "VIX"]),
|
|
49
|
+
series("market-volatility-average", "Fear & Greed: VIX 50-day average", "index", component("market-volatility", "secondary"), ["volatility", "moving average"]),
|
|
50
|
+
series("safe-haven-demand", "Fear & Greed: stocks minus bonds, 20-day return", "%", component("safe-haven-demand", "primary"), ["safe haven", "bonds"]),
|
|
51
|
+
series("junk-bond-demand", "Fear & Greed: junk bond spread", "%", component("junk-bond-demand", "primary"), ["credit", "high yield", "junk"]),
|
|
52
|
+
];
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type PaneFooterSegment,
|
|
14
14
|
} from "../../../components";
|
|
15
15
|
import { handleRefreshKey } from "../../../components/data-table/table-pane";
|
|
16
|
+
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
16
17
|
import { usePaneVisible } from "../../../state/app/activity";
|
|
17
18
|
import { usePaneInstance } from "../../../state/app/context";
|
|
18
19
|
import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
|
|
@@ -22,7 +23,9 @@ import { isPlainArrowUp, stopSearchFocusNavigation } from "../../../utils/search
|
|
|
22
23
|
import { cycleSortPreference, nextHeaderSort } from "../../../utils/sort-values";
|
|
23
24
|
import { useAssetData, usePluginTickerActions } from "../../runtime";
|
|
24
25
|
import type { PluginModule } from "../plugin-module";
|
|
26
|
+
import { FUTURES_SERIES } from "./series";
|
|
25
27
|
import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
|
|
28
|
+
import { quoteObservation } from "../shared/report-freshness";
|
|
26
29
|
import {
|
|
27
30
|
boardErrorMessage,
|
|
28
31
|
quoteBoardFooterInfo,
|
|
@@ -34,9 +37,10 @@ import {
|
|
|
34
37
|
import {
|
|
35
38
|
FUTURES_CONTRACTS,
|
|
36
39
|
FUTURES_SECTOR_LABELS,
|
|
40
|
+
futuresBoardContract,
|
|
37
41
|
getContractsBySector,
|
|
38
42
|
type FuturesSector,
|
|
39
|
-
} from "
|
|
43
|
+
} from "../../../market-data/futures-board";
|
|
40
44
|
import {
|
|
41
45
|
buildFuturesRows,
|
|
42
46
|
DEFAULT_FUTURES_SORT,
|
|
@@ -75,7 +79,8 @@ function FuturesPane({ focused, width, height }: PaneProps) {
|
|
|
75
79
|
const liveStreaming = useLiveStreamingSetting();
|
|
76
80
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
77
81
|
const [sortPreference, setSortPreference] = useState<FuturesSortPreference>(DEFAULT_FUTURES_SORT);
|
|
78
|
-
|
|
82
|
+
// `FUT TTF` opens the board searched for the contract it names.
|
|
83
|
+
const [searchQuery, setSearchQuery] = useState(() => paneInstance?.params?.query ?? "");
|
|
79
84
|
const { active: searchFocused, focus: focusSearch, searchProps } = useQueryBarSearch();
|
|
80
85
|
const [collapsedSectors, setCollapsedSectors] = useState<ReadonlySet<FuturesSector>>(new Set());
|
|
81
86
|
const [visibleRange, setVisibleRange] = useState<DataTableVisibleRange | null>(null);
|
|
@@ -196,6 +201,17 @@ function FuturesPane({ focused, width, height }: PaneProps) {
|
|
|
196
201
|
return handlePaneKey(event);
|
|
197
202
|
}, [focusSearch, handlePaneKey]);
|
|
198
203
|
|
|
204
|
+
// `fn FUT` and `shot FUT` are dated by the quotes the board shows, as `quote` dates them:
|
|
205
|
+
// the newest, its delay, and whether the markets are open.
|
|
206
|
+
useRemoteUiNode(quotes.size > 0 ? {
|
|
207
|
+
role: "report-freshness",
|
|
208
|
+
label: "Quote freshness",
|
|
209
|
+
getMetadata: () => {
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
return { observations: [...quotes.values()].flatMap((state) => (state.quote ? [quoteObservation(state.quote, now)] : [])) };
|
|
212
|
+
},
|
|
213
|
+
} : null);
|
|
214
|
+
|
|
199
215
|
const status = quoteBoardStatus(quotes);
|
|
200
216
|
const errorMessage = boardErrorMessage(quotes);
|
|
201
217
|
// Every load came back empty: a board of dashes reads as broken, so the
|
|
@@ -265,6 +281,7 @@ function FuturesPane({ focused, width, height }: PaneProps) {
|
|
|
265
281
|
renderSectionHeader={renderSectorHeader}
|
|
266
282
|
renderCell={renderCell}
|
|
267
283
|
selectedTextOverridesCellColor
|
|
284
|
+
reportEveryRow
|
|
268
285
|
emptyStateTitle={searchQuery.trim()
|
|
269
286
|
? "No matching contracts."
|
|
270
287
|
: "No market data provider connected."}
|
|
@@ -288,6 +305,7 @@ function FuturesPane({ focused, width, height }: PaneProps) {
|
|
|
288
305
|
}
|
|
289
306
|
|
|
290
307
|
export const futuresModule: PluginModule = {
|
|
308
|
+
series: FUTURES_SERIES,
|
|
291
309
|
panes: [
|
|
292
310
|
{
|
|
293
311
|
id: FUTURES_PANE_ID,
|
|
@@ -298,6 +316,7 @@ export const futuresModule: PluginModule = {
|
|
|
298
316
|
defaultMode: "floating",
|
|
299
317
|
defaultFloatingSize: { width: 76, height: 34 },
|
|
300
318
|
tableExport: true,
|
|
319
|
+
shotFitsRows: true,
|
|
301
320
|
// Resolved per open so the dialog shows the full default set until the
|
|
302
321
|
// user saves a narrower selection.
|
|
303
322
|
settings: (context) => ({
|
|
@@ -341,11 +360,32 @@ export const futuresModule: PluginModule = {
|
|
|
341
360
|
"lumber",
|
|
342
361
|
"aluminum",
|
|
343
362
|
"ttf",
|
|
363
|
+
"energy",
|
|
364
|
+
"natural gas",
|
|
365
|
+
"gas",
|
|
366
|
+
"lng",
|
|
367
|
+
"brent",
|
|
368
|
+
"wti",
|
|
369
|
+
"heating oil",
|
|
370
|
+
"gasoline",
|
|
344
371
|
"treasuries",
|
|
345
372
|
"contracts",
|
|
346
373
|
"cme",
|
|
347
374
|
],
|
|
348
|
-
shortcut: {
|
|
375
|
+
shortcut: {
|
|
376
|
+
prefix: "FUT",
|
|
377
|
+
aliases: ["GLCO"],
|
|
378
|
+
argKind: "text",
|
|
379
|
+
argPlaceholder: "code or name",
|
|
380
|
+
argOptional: true,
|
|
381
|
+
},
|
|
382
|
+
// A board code or symbol searches for that contract alone (NG=F: a bare NG also matches Orange Juice); other text searches as typed.
|
|
383
|
+
createInstance: (_context, options) => {
|
|
384
|
+
const input = options?.arg?.trim();
|
|
385
|
+
if (!input) return {};
|
|
386
|
+
const query = futuresBoardContract(input)?.symbol ?? input;
|
|
387
|
+
return { title: `FUT ${input.toUpperCase()}`, params: { query } };
|
|
388
|
+
},
|
|
349
389
|
},
|
|
350
390
|
],
|
|
351
391
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Quote } from "../../../types/financials";
|
|
2
2
|
import { compareSortValues, type SortDirection } from "../../../utils/sort-values";
|
|
3
3
|
import type { BoardQuoteMap } from "../shared/use-quote-board";
|
|
4
|
-
import { FUTURES_SECTOR_ORDER, type FuturesContract, type FuturesSector } from "
|
|
4
|
+
import { FUTURES_SECTOR_ORDER, type FuturesContract, type FuturesSector } from "../../../market-data/futures-board";
|
|
5
5
|
import type { FuturesReturnHorizon } from "./returns";
|
|
6
6
|
import type { FrontReturnsMap } from "./use-front-returns";
|
|
7
7
|
|