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,6 +1,6 @@
|
|
|
1
1
|
import type { PricePoint, Quote } from "../../../types/financials";
|
|
2
2
|
import { calendarMonthsBefore } from "../../../utils/calendar-date";
|
|
3
|
-
import type { FuturesContract } from "
|
|
3
|
+
import type { FuturesContract } from "../../../market-data/futures-board";
|
|
4
4
|
import { quotedContractMonth } from "./model";
|
|
5
5
|
|
|
6
6
|
export type FuturesReturnHorizon = "1W" | "1M" | "YTD";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SeriesDefinition } from "../../../series/types";
|
|
2
|
+
import { FUTURES_CONTRACTS, FUTURES_SECTOR_LABELS } from "../../../market-data/futures-board";
|
|
3
|
+
|
|
4
|
+
/** Each continuous front-month contract on the futures board, as `FUT:ES`. */
|
|
5
|
+
export const FUTURES_SERIES: readonly SeriesDefinition[] = FUTURES_CONTRACTS.map((contract) => ({
|
|
6
|
+
id: `fut:${contract.code}`,
|
|
7
|
+
name: contract.name,
|
|
8
|
+
unit: "USD",
|
|
9
|
+
frequency: "daily",
|
|
10
|
+
group: "Futures",
|
|
11
|
+
tags: [contract.code, FUTURES_SECTOR_LABELS[contract.sector], "futures", "front month"],
|
|
12
|
+
expression: `FUT:${contract.code}`,
|
|
13
|
+
}));
|
|
@@ -4,7 +4,7 @@ import type { Quote } from "../../../types/financials";
|
|
|
4
4
|
import { TextAttributes } from "../../../ui";
|
|
5
5
|
import { formatCompact, formatNumber, formatPercentRaw } from "../../../utils/format";
|
|
6
6
|
import { isBoardRowLoading, renderQuoteBoardCell, type BoardQuoteMap } from "../shared/use-quote-board";
|
|
7
|
-
import { tickDecimals, type FuturesContract } from "
|
|
7
|
+
import { tickDecimals, type FuturesContract } from "../../../market-data/futures-board";
|
|
8
8
|
import { FUTURES_RETURN_COLUMNS, futuresContractName, type FuturesColumnId, type FuturesTableRow } from "./model";
|
|
9
9
|
import type { FrontReturnsMap } from "./use-front-returns";
|
|
10
10
|
|
|
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import type { DataProvider } from "../../../types/data-provider";
|
|
3
3
|
import type { PricePoint } from "../../../types/financials";
|
|
4
4
|
import type { BoardQuoteMap } from "../shared/use-quote-board";
|
|
5
|
-
import type { FuturesContract } from "
|
|
5
|
+
import type { FuturesContract } from "../../../market-data/futures-board";
|
|
6
6
|
import {
|
|
7
7
|
frontContractCandidates,
|
|
8
8
|
frontContractReturns,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
2
|
import type { FuturesCurvePayload } from "../../../api-client/futures-curve";
|
|
3
3
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
4
|
+
import { isFuturesContractSpec } from "../../../market-data/futures-contract";
|
|
4
5
|
import { loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
5
6
|
import type { DataProvider } from "../../../types/data-provider";
|
|
6
7
|
import { archivedFuturesCurve, curveLookbackDate, curveSpot, normalizeCurveRoot, unsupportedCurveRootMessage, type CurveSpot } from "./model";
|
|
@@ -13,13 +14,17 @@ const finiteOrNull = (value: unknown) => value === null || typeof value === "num
|
|
|
13
14
|
const timestamp = (value: unknown) => typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
14
15
|
const date = (value: unknown) => timestamp(value) && new Date(value as string).toISOString().slice(0, 10) === value;
|
|
15
16
|
const rank = (value: unknown) => value === null || typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100;
|
|
17
|
+
const optionalDate = (value: unknown) => value === undefined || value === null || date(value);
|
|
18
|
+
/** Contract terms the pane computes with; a curve from before the server sent them has none. */
|
|
19
|
+
const validSpec = (spec: unknown) => spec === undefined || spec === null || isFuturesContractSpec(spec);
|
|
16
20
|
|
|
17
21
|
export function validateFuturesCurve(data: FuturesCurvePayload, root: string): FuturesCurvePayload {
|
|
18
22
|
if (!data || data.root !== root || !Array.isArray(data.contracts) || !Array.isArray(data.ghosts)
|
|
19
23
|
|| !Array.isArray(data.gaps) || !data.gaps.every((gap) => typeof gap === "string")
|
|
20
24
|
|| !data.catalogue || !data.slope || !timestamp(data.fetchedAt)
|
|
21
25
|
|| data.asOf !== null && !timestamp(data.asOf)
|
|
22
|
-
|| !["gloom", "cboe"].includes(data.source) || !["available", "partial", "unavailable"].includes(data.status)
|
|
26
|
+
|| !["gloom", "cboe"].includes(data.source) || !["available", "partial", "unavailable"].includes(data.status)
|
|
27
|
+
|| !optionalDate(data.settlementDate) || !validSpec(data.spec)) {
|
|
23
28
|
throw new Error("The server returned an invalid futures curve");
|
|
24
29
|
}
|
|
25
30
|
const symbols = new Set<string>();
|
|
@@ -30,7 +35,9 @@ export function validateFuturesCurve(data: FuturesCurvePayload, root: string): F
|
|
|
30
35
|
|| typeof row.currency !== "string"
|
|
31
36
|
|| !rank(row.percentile) || !Number.isInteger(row.samples) || row.samples < 0
|
|
32
37
|
|| row.asOf !== null && !timestamp(row.asOf)
|
|
33
|
-
|| ![row.volume, row.openInterest, row.delayMinutes].every((value) => finiteOrNull(value) && (value === null || value >= 0))
|
|
38
|
+
|| ![row.volume, row.openInterest, row.delayMinutes].every((value) => finiteOrNull(value) && (value === null || value >= 0))
|
|
39
|
+
|| row.settlement !== undefined && !finiteOrNull(row.settlement) || !optionalDate(row.lastTrade) || !optionalDate(row.firstNotice)
|
|
40
|
+
|| row.inDelivery !== undefined && typeof row.inDelivery !== "boolean") {
|
|
34
41
|
throw new Error("The server returned an invalid futures contract");
|
|
35
42
|
}
|
|
36
43
|
symbols.add(row.symbol);
|
|
@@ -80,7 +87,9 @@ export async function loadFuturesCurveAsOf(root: string, date: string,
|
|
|
80
87
|
client.getCloudFuturesCurveAsOf(normalized, curveLookbackDate(date, 30)).catch(() => null),
|
|
81
88
|
]);
|
|
82
89
|
if (!curve || curve.root !== normalized || !Array.isArray(curve.contracts) || !Array.isArray(curve.gaps)
|
|
83
|
-
|| curve.contracts.some((row) => !row || typeof row.symbol !== "string" || !Number.isFinite(row.price)
|
|
90
|
+
|| curve.contracts.some((row) => !row || typeof row.symbol !== "string" || !Number.isFinite(row.price)
|
|
91
|
+
|| row.settled !== undefined && typeof row.settled !== "boolean" || !optionalDate(row.firstNotice) || !optionalDate(row.lastTrade))
|
|
92
|
+
|| !validSpec(curve.spec)) {
|
|
84
93
|
throw new Error("The server returned an invalid past futures curve");
|
|
85
94
|
}
|
|
86
95
|
return archivedFuturesCurve(normalized, curve, { "1W": week, "1M": month }, new Date().toISOString());
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FuturesContract, FuturesCurvePayload } from "../../../api-client/futures-curve";
|
|
2
|
+
import { futuresSpecLines } from "../../../market-data/futures-contract";
|
|
3
|
+
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneEntry, HeadlessPaneFreshness } from "../../../types/plugin";
|
|
2
4
|
import { newestReportTime, oldestReportTime } from "../../../utils/utc-time";
|
|
3
5
|
import { fetchFuturesCurve, loadCurveSpot, loadFuturesCurveAsOf } from "./client";
|
|
4
|
-
import { basisSpotSymbol, contractBasis, curveAsOfDate, curveSpotLabel, normalizeCurveRoot, thinContractCount, unsupportedCurveRootMessage } from "./model";
|
|
6
|
+
import { basisSpotSymbol, catalogueCurveUnit, contractBasis, curveAsOfDate, curveBasisPercent, curveChangeText, curveContractCode, curvePrice, curveRoundedPrice, curveSpotLabel, curveSpreadLabel, curveTimestamp, curveUnitLabel, normalizeCurveRoot, thinContractCount, unsupportedCurveRootMessage, type ContractBasis } from "./model";
|
|
5
7
|
|
|
6
8
|
/** The root a report shows when none is given. */
|
|
7
9
|
const DEFAULT_ROOT = "ES";
|
|
@@ -12,9 +14,66 @@ const rank = (value: number | null) => value == null ? null : Math.round(value);
|
|
|
12
14
|
/** The basis fields are read to four decimals, finer than any cell shows. */
|
|
13
15
|
const basisValue = (value: number | null) => value == null ? null : Number(value.toFixed(4));
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* One contract as the report reads it, the same fields in the same order for
|
|
19
|
+
* every root. Prices and changes are at the root's tick precision. `price` is
|
|
20
|
+
* the latest trade and `settlement` the exchange settlement for the curve's
|
|
21
|
+
* settlement session (a past curve's settled rows have no separate trade).
|
|
22
|
+
*/
|
|
23
|
+
function contractRow(row: FuturesContract, data: FuturesCurvePayload, root: string, past: boolean, basis?: ContractBasis) {
|
|
24
|
+
const price = (value: number | null | undefined) => curveRoundedPrice(value, root);
|
|
25
|
+
return {
|
|
26
|
+
symbol: row.symbol, label: row.label, expiration: row.expiration, lastTrade: row.lastTrade ?? row.expiration,
|
|
27
|
+
firstNotice: row.firstNotice ?? null, inDelivery: row.inDelivery ?? false,
|
|
28
|
+
settlement: price(row.settlement), settlementDate: row.settlement != null ? data.settlementDate ?? null : null,
|
|
29
|
+
price: past && row.settlement != null ? null : price(row.price), change: price(row.change),
|
|
30
|
+
asOf: row.asOf, stale: row.stale, volume: row.volume, openInterest: row.openInterest, percentile: rank(row.percentile),
|
|
31
|
+
samples: row.samples, historyStart: row.historyStart, historyEnd: row.historyEnd,
|
|
32
|
+
currency: row.currency, quoteUnit: curveUnitLabel(data) ?? row.quoteUnit, delayMinutes: row.delayMinutes,
|
|
33
|
+
...(basis ? { vsSpotPct: basisValue(basis.vsSpotPct), annualisedBasisPct: basisValue(basis.annualisedBasisPct) } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The text table: what a hedge reads, in one order for every root; JSON keeps every field. */
|
|
38
|
+
function contractColumns(root: string, settlementDate: string | null, options: { basis: boolean; last: boolean; notice: boolean }): HeadlessPaneColumn[] {
|
|
39
|
+
const price = (value: unknown) => typeof value === "number" ? curvePrice(value, root) : "--";
|
|
40
|
+
const date = (value: unknown) => typeof value === "string" ? value : "--";
|
|
41
|
+
return [
|
|
42
|
+
{ key: "symbol", header: "Contract", shrink: false, format: (value) => curveContractCode(String(value)) },
|
|
43
|
+
{ key: "lastTrade", header: "Last Trade", shrink: false, format: date },
|
|
44
|
+
// Cash-settled roots have no first notice day.
|
|
45
|
+
...options.notice ? [{ key: "firstNotice", header: "1st Notice", shrink: false,
|
|
46
|
+
format: (value: unknown, row: Record<string, unknown>) => typeof value === "string" ? `${value}${row.inDelivery ? " passed" : ""}` : "--" }] : [],
|
|
47
|
+
{ key: "settlement", header: settlementDate ? `Settle ${settlementDate}` : "Settle", align: "right", format: price },
|
|
48
|
+
// A Cboe price is the settlement itself, with no separate last trade.
|
|
49
|
+
...options.last ? [{ key: "price", header: "Last", align: "right" as const, format: price }] : [],
|
|
50
|
+
{ key: "change", header: "Change", align: "right", format: (value) => typeof value === "number" ? curveChangeText(value, root) : "--" },
|
|
51
|
+
...options.basis ? [{ key: "vsSpotPct", header: "Vs Spot", align: "right" as const, format: (value: unknown) => curveBasisPercent(typeof value === "number" ? value : null, 2) },
|
|
52
|
+
{ key: "annualisedBasisPct", header: "Ann Basis", align: "right" as const, format: (value: unknown) => curveBasisPercent(typeof value === "number" ? value : null, 1) }] : [],
|
|
53
|
+
{ key: "asOf", header: "As Of UTC", shrink: false, format: (value) => typeof value === "string" ? curveTimestamp(value) : "--" },
|
|
54
|
+
{ key: "openInterest", header: "Open Int", align: "right", format: (value) => typeof value === "number" ? value.toLocaleString("en-US") : "--" },
|
|
55
|
+
{ key: "volume", header: "Volume", align: "right", format: (value) => typeof value === "number" ? value.toLocaleString("en-US") : "--" },
|
|
56
|
+
{ key: "percentile", header: "Pctl", align: "right", format: (value) => typeof value === "number" ? String(value) : "--" },
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** What one contract is: the terms a hedge is sized on, once rather than on every row, worded as `ticker` words them. */
|
|
61
|
+
function specEntries(data: FuturesCurvePayload): HeadlessPaneEntry[] {
|
|
62
|
+
const spec = data.spec;
|
|
63
|
+
// A root without terms still says what its prices count: Dutch TTF gas is euros per MWh.
|
|
64
|
+
if (!spec) {
|
|
65
|
+
const unit = catalogueCurveUnit(data.root);
|
|
66
|
+
return unit ? [{ key: "unit", label: "Unit", value: unit }] : [];
|
|
67
|
+
}
|
|
68
|
+
return futuresSpecLines(spec).map((line) => {
|
|
69
|
+
const value = spec[line.key as keyof typeof spec];
|
|
70
|
+
return { key: line.key, label: line.label, value, ...line.value === String(value) ? {} : { formatted: line.value } };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
15
74
|
export const futuresCurveHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
16
75
|
discovery: { aliases: ["CTM"], dataRequirements: ["Gloom Cloud futures curve endpoint"],
|
|
17
|
-
limitations: ["Listed-contract catalogues can be incomplete", "Ghosts and percentiles use the same listed contracts", "Cboe VIX is daily settlement"] },
|
|
76
|
+
limitations: ["Listed-contract catalogues can be incomplete.", "Ghosts and percentiles use the same listed contracts.", "Cboe VIX is daily settlement."] },
|
|
18
77
|
shape: "bundle", argument: { kind: "free-text", optional: true, placeholder: "root", description: "FUT root such as CL, ES, ZN or VX, or a CME crypto root (BTC, ETH, SOL, XRP). Defaults to ES." },
|
|
19
78
|
// Only the pane reads the tab; the report always carries both.
|
|
20
79
|
options: [{ key: "tab", description: "Curve or the full contract table.", type: "enum",
|
|
@@ -35,6 +94,8 @@ export const futuresCurveHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
35
94
|
spotSymbol ? loadCurveSpot(spotSymbol, ctx.marketData, now) : null,
|
|
36
95
|
]);
|
|
37
96
|
const bases = spot ? data.contracts.map((row) => contractBasis(row, spot)) : [];
|
|
97
|
+
const unit = curveUnitLabel(data);
|
|
98
|
+
const terms = specEntries(data);
|
|
38
99
|
const notices = [...args.argument ? [] : [`Showing ${DEFAULT_ROOT}. Try fn CTM ${EXAMPLE_ROOT}.`],
|
|
39
100
|
...spot ? [spot.status === "ok" ? `Basis against ${curveSpotLabel(spot, root, now)}.` : `Basis blank: ${spot.reason}.`] : []];
|
|
40
101
|
// Dated by the contracts' own quotes; the same-contract history rows are a year of context, not observations.
|
|
@@ -46,17 +107,17 @@ export const futuresCurveHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
46
107
|
return {
|
|
47
108
|
freshness,
|
|
48
109
|
sections: [
|
|
49
|
-
{ title: "Contracts",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{ title: "Front spread", rows: [{ ...data.slope, annualizedRollYield: data.slope.annualizedRollYield == null ? null : Number(data.slope.annualizedRollYield.toFixed(2)),
|
|
110
|
+
{ title: "Contracts", columns: contractColumns(root, data.settlementDate ?? null,
|
|
111
|
+
{ basis: !!spot, last: data.source !== "cboe", notice: data.contracts.some((row) => row.firstNotice) }),
|
|
112
|
+
rows: data.contracts.map((row, index) => contractRow(row, data, root, !!date, bases[index])) },
|
|
113
|
+
{ title: "Front spread", rows: [{ label: curveSpreadLabel(data.slope), ...data.slope, value: curveRoundedPrice(data.slope.value, root),
|
|
114
|
+
annualizedRollYield: data.slope.annualizedRollYield == null ? null : Number(data.slope.annualizedRollYield.toFixed(2)),
|
|
55
115
|
percentile: rank(data.slope.percentile), rollPercentile: rank(data.slope.rollPercentile) }] },
|
|
116
|
+
...terms.length ? [{ title: "Contract terms", entries: terms }] : [],
|
|
56
117
|
...data.ghosts.map((ghost) => ({ title: `${ghost.label} same-contract history`, rows: ghost.points.map((point) => ({ ...point })) })),
|
|
57
118
|
],
|
|
58
119
|
errors: data.gaps,
|
|
59
|
-
metadata: { ...data, ...(spot ? { spot, basisBlankedThin: thinContractCount(bases) } : {}), ...(args.argument ? {} : { defaultArgument: DEFAULT_ROOT }),
|
|
120
|
+
metadata: { ...data, quoteUnit: unit, ...(spot ? { spot, basisBlankedThin: thinContractCount(bases) } : {}), ...(args.argument ? {} : { defaultArgument: DEFAULT_ROOT }),
|
|
60
121
|
...(notices.length ? { notices } : {}),
|
|
61
122
|
complete: data.status === "available", percentileBasis: "Same-contract observations within one year; actual sample start/end retained" },
|
|
62
123
|
};
|
|
@@ -18,7 +18,7 @@ export const futuresCurveModule: PluginModule = {
|
|
|
18
18
|
}],
|
|
19
19
|
paneTemplates: [{ id: "futures-curve-pane", paneId: "futures-curve", label: "Futures Curve",
|
|
20
20
|
description: "Listed futures contracts, historical curves, roll yield and open interest including VIX and CME crypto futures.",
|
|
21
|
-
keywords: ["ctm", "futures", "curve", "contango", "backwardation", "roll", "vix"],
|
|
21
|
+
keywords: ["ctm", "futures", "curve", "term structure", "contango", "backwardation", "roll", "vix", "crude oil", "natural gas"],
|
|
22
22
|
shortcut: { prefix: "CTM", aliases: ["CT"], argKind: "text", argPlaceholder: "root", argOptional: true,
|
|
23
23
|
argOptions: () => CURVE_ROOTS },
|
|
24
24
|
headless: futuresCurveHeadless,
|
|
@@ -3,7 +3,7 @@ import type { CurvePalette, CurveSeries } from "../../../components/chart/curve/
|
|
|
3
3
|
import { spanDigits } from "../../../components/chart-table";
|
|
4
4
|
import { compositeAxisTicks } from "../../../components/chart/composite/format";
|
|
5
5
|
import type { CompositeAxisDomain } from "../../../components/chart/composite/types";
|
|
6
|
-
import { FUTURES_CONTRACTS, tickDecimals } from "
|
|
6
|
+
import { FUTURES_CONTRACTS, futuresBoardContractForTicker, futuresContractByCode, tickDecimals } from "../../../market-data/futures-board";
|
|
7
7
|
import { formatPercentileRank } from "../../../utils/format";
|
|
8
8
|
import { cryptoPairCoin, isFuturesSymbol } from "../shared/crypto-pair";
|
|
9
9
|
import { parseReportTime } from "../../../utils/utc-time";
|
|
@@ -21,7 +21,7 @@ const CRYPTO_CURVE_ROOTS: readonly { code: string; name: string; tick: number }[
|
|
|
21
21
|
const rootLabel = (code: string, name: string) => name === code ? code : `${code} ${name}`;
|
|
22
22
|
|
|
23
23
|
export const CURVE_ROOTS = [
|
|
24
|
-
...FUTURES_CONTRACTS.
|
|
24
|
+
...FUTURES_CONTRACTS.map((row) => ({ value: row.code, label: rootLabel(row.code, row.name) })),
|
|
25
25
|
{ value: "VX", label: "VX VIX Futures" },
|
|
26
26
|
...CRYPTO_CURVE_ROOTS.map((row) => ({ value: row.code, label: rootLabel(row.code, row.name) })),
|
|
27
27
|
];
|
|
@@ -34,24 +34,40 @@ export function normalizeCurveRoot(value: unknown): string | null {
|
|
|
34
34
|
return CURVE_ROOTS.some((row) => row.value === root) ? root : null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** What a root
|
|
37
|
+
/** What a root CTM does not list is told: the roots that work, with the FUT board's, VIX and CME crypto named by group. */
|
|
38
38
|
export function unsupportedCurveRootMessage(value: unknown): string {
|
|
39
|
+
const typed = String(value).trim().toUpperCase().replace(/=F$/, "");
|
|
39
40
|
const examples = ["ES", "CL", "GC", "ZN"].filter((root) => CURVE_ROOTS.some((row) => row.value === root));
|
|
40
|
-
return `
|
|
41
|
+
return `No futures curve for ${typed || "that root"}: CTM covers the FUT board's roots, VIX and CME crypto, such as ${examples.join(", ")}.`;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Board roots whose bare code opens their curve from the ticker under the
|
|
46
|
+
* cursor. Several are also listed securities (ES Eversource, NG NovaGold, PL
|
|
47
|
+
* Planet Labs), but they followed the cursor before the board was checked
|
|
48
|
+
* against listed securities and keep doing so. A root added after that (ZM is
|
|
49
|
+
* Zoom, KE, LE, HE and ALI are tickers too) follows the cursor only as its
|
|
50
|
+
* `=F` symbol or a code no security answers to (`bareCode`: ZL, OJ, LBR), the
|
|
51
|
+
* rule FUT and COT apply, so a new board row defaults to the security.
|
|
52
|
+
*/
|
|
53
|
+
const CURSOR_BARE_ROOTS: ReadonlySet<string> = new Set(["ES", "CL", "BZ", "NG", "RB", "HO", "GC", "SI", "HG", "PL", "PA", "ZC", "ZS", "KC", "SB", "CC"]);
|
|
54
|
+
|
|
43
55
|
/**
|
|
44
56
|
* The root for the ticker under the cursor, when CTM is opened without an
|
|
45
57
|
* argument. The crypto roots are also US ticker symbols (Grayscale's mini
|
|
46
58
|
* trusts are BTC and ETH), so a bare one is the equity's, never the future's:
|
|
47
59
|
* only a futures symbol (BTC=F) or a coin pair (BTC-USD) names the crypto curve.
|
|
60
|
+
* A typed argument is different: `CTM ZM` is soybean meal even though ZM is also Zoom.
|
|
48
61
|
*/
|
|
49
62
|
export function curveRootForTicker(ticker: unknown): string | null {
|
|
50
63
|
if (typeof ticker !== "string") return null;
|
|
51
64
|
const coin = cryptoPairCoin(ticker);
|
|
52
65
|
if (coin) return CRYPTO_CURVE_ROOTS.some((row) => row.code === coin) ? coin : null;
|
|
53
66
|
const root = normalizeCurveRoot(ticker);
|
|
54
|
-
|
|
67
|
+
if (!root) return null;
|
|
68
|
+
if (CRYPTO_CURVE_ROOTS.some((row) => row.code === root)) return isFuturesSymbol(ticker) ? root : null;
|
|
69
|
+
const isBoardRoot = futuresContractByCode(root) != null;
|
|
70
|
+
return isBoardRoot && !CURSOR_BARE_ROOTS.has(root) ? futuresBoardContractForTicker(ticker)?.code ?? null : root;
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
/**
|
|
@@ -67,7 +83,7 @@ function curvePriceDecimals(root: string): number {
|
|
|
67
83
|
const rateTick = RATE_TICKS[root];
|
|
68
84
|
if (rateTick != null) return tickDecimals(rateTick);
|
|
69
85
|
if (root === "VX") return 4;
|
|
70
|
-
const tick =
|
|
86
|
+
const tick = futuresContractByCode(root)?.tick ?? CRYPTO_CURVE_ROOTS.find((row) => row.code === root)?.tick;
|
|
71
87
|
return tick == null ? 5 : Math.max(2, tickDecimals(tick));
|
|
72
88
|
}
|
|
73
89
|
|
|
@@ -84,6 +100,39 @@ export function curveChangeText(value: number | null, root: string): string {
|
|
|
84
100
|
return value != null && value > 0 && /[1-9]/.test(text) ? `+${text}` : text;
|
|
85
101
|
}
|
|
86
102
|
|
|
103
|
+
/** A price as a number at the root's display precision, for reports: 59.299805 reads 59.3, never float noise. */
|
|
104
|
+
export function curveRoundedPrice(value: number | null | undefined, root: string): number | null {
|
|
105
|
+
return value == null || !Number.isFinite(value) ? null : Number(value.toFixed(curvePriceDecimals(root)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The unit the catalogue gives a root whose contract terms the server does not send: "EUR / MWh" for Dutch TTF gas. */
|
|
109
|
+
export function catalogueCurveUnit(root: string): string | null {
|
|
110
|
+
return futuresContractByCode(root)?.unit ?? null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* What a price counts: the exchange's unit ("USD / troy oz") where the root's
|
|
115
|
+
* terms are known, the catalogue's where they are not, else the quote unit.
|
|
116
|
+
*/
|
|
117
|
+
export function curveUnitLabel(data: Pick<FuturesCurvePayload, "root" | "spec" | "quoteUnit" | "currency">): string | null {
|
|
118
|
+
return data.spec?.unit ?? catalogueCurveUnit(data.root) ?? data.quoteUnit ?? data.currency ?? null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A contract as traders write it: GCZ26.CMX is GCZ26; a VX symbol stays as Cboe lists it. */
|
|
122
|
+
export function curveContractCode(symbol: string): string {
|
|
123
|
+
return symbol.replace(/\.[A-Z]+$/, "");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The front spread named by the contracts it subtracts, "GCF27-GCZ26": the
|
|
128
|
+
* next contract less the front one. The front is the first contract the
|
|
129
|
+
* market trades, which can be past a delivery month nobody holds, so a bare
|
|
130
|
+
* "M2-M1" would not say which rows it means.
|
|
131
|
+
*/
|
|
132
|
+
export function curveSpreadLabel(slope: Pick<FuturesCurvePayload["slope"], "frontSymbol" | "nextSymbol">): string {
|
|
133
|
+
return slope.frontSymbol && slope.nextSymbol ? `${curveContractCode(slope.nextSymbol)}-${curveContractCode(slope.frontSymbol)}` : "M2-M1";
|
|
134
|
+
}
|
|
135
|
+
|
|
87
136
|
/**
|
|
88
137
|
* Axis gridlines sit on round values, so they need no tick precision: one
|
|
89
138
|
* decimal count across the gutter, as many as the plotted range asks for and
|
|
@@ -188,13 +237,14 @@ export function curveContractChanges(data: FuturesCurvePayload): CurveContractCh
|
|
|
188
237
|
|
|
189
238
|
const CHANGE_COLUMNS: Readonly<Record<string, CurveLookback>> = { change1w: "1W", change1m: "1M" };
|
|
190
239
|
|
|
191
|
-
type CurveSortKey = "symbol" | "expiration" | "price" | "change" | "openInterest" | "volume" | "percentile" | "asOf";
|
|
240
|
+
type CurveSortKey = "symbol" | "expiration" | "price" | "settlement" | "firstNotice" | "change" | "openInterest" | "volume" | "percentile" | "asOf";
|
|
192
241
|
|
|
193
242
|
const BASIS_COLUMNS: Readonly<Record<string, "vsSpotPct" | "annualisedBasisPct">> = { vsSpot: "vsSpotPct", annBasis: "annualisedBasisPct" };
|
|
194
243
|
|
|
195
244
|
export function sortCurveContracts(rows: readonly FuturesContract[], id: string, direction: SortDirection,
|
|
196
245
|
changes?: CurveContractChanges, basis?: CurveBasisRows): FuturesContract[] {
|
|
197
|
-
const keys: Record<string, CurveSortKey> = { symbol: "symbol", expiry: "expiration", price: "price",
|
|
246
|
+
const keys: Record<string, CurveSortKey> = { symbol: "symbol", expiry: "expiration", price: "price", settle: "settlement", notice: "firstNotice",
|
|
247
|
+
change: "change", oi: "openInterest", volume: "volume", percentile: "percentile", asOf: "asOf" };
|
|
198
248
|
const key = keys[id] ?? "expiration";
|
|
199
249
|
const lookback = CHANGE_COLUMNS[id];
|
|
200
250
|
const basisKey = BASIS_COLUMNS[id];
|
|
@@ -399,6 +449,9 @@ export function archivedFuturesCurve(root: string, curve: FuturesCurveAsOfPayloa
|
|
|
399
449
|
symbol: row.symbol, label: row.label, expiration: row.expiration, price: row.price, change: null, asOf: rowDate(row, stale),
|
|
400
450
|
currency: curve.currency ?? "USD", quoteUnit: curve.quoteUnit ?? curve.currency ?? "USD", volume: row.volume,
|
|
401
451
|
openInterest: row.openInterest, delayMinutes: null, stale, percentile: null, samples: 0, historyStart: null, historyEnd: null,
|
|
452
|
+
// Only a row the archive confirms is a settlement; an unconfirmed one is that session's last trade.
|
|
453
|
+
settlement: !stale && row.settled === true ? row.price : null,
|
|
454
|
+
lastTrade: row.lastTrade ?? row.expiration, firstNotice: row.firstNotice ?? null, inDelivery: row.inDelivery ?? false,
|
|
402
455
|
}];
|
|
403
456
|
}).sort((a, b) => a.expiration.localeCompare(b.expiration));
|
|
404
457
|
const ghosts = (["1W", "1M"] as const).map((label) => {
|
|
@@ -407,8 +460,10 @@ export function archivedFuturesCurve(root: string, curve: FuturesCurveAsOfPayloa
|
|
|
407
460
|
points: past ? past.contracts.flatMap((row) => row.expiration
|
|
408
461
|
? [{ symbol: row.symbol, expiration: row.expiration, price: row.price, asOf: rowDate(row, archivedRowStale(row, past)) }] : []) : [] };
|
|
409
462
|
});
|
|
410
|
-
// The front pair on the curve's own session
|
|
411
|
-
|
|
463
|
+
// The front pair on the curve's own session: settlements where the archive has them, else that session's
|
|
464
|
+
// last trades. Carried prices, and a last trade beside a settlement, do not make a spread.
|
|
465
|
+
const settled = contracts.filter((row) => row.settlement != null);
|
|
466
|
+
const [front, next] = settled.length >= 2 ? settled : settled.length ? [] : contracts.filter((row) => !row.stale);
|
|
412
467
|
const value = front && next ? next.price! - front.price! : null;
|
|
413
468
|
const days = front && next ? (Date.parse(next.expiration) - Date.parse(front.expiration)) / DAY_MS : 0;
|
|
414
469
|
const roll = front && next && front.price! > 0 && next.price! > 0 && days > 0 ? (front.price! / next.price! - 1) * 365 / days * 100 : null;
|
|
@@ -419,6 +474,6 @@ export function archivedFuturesCurve(root: string, curve: FuturesCurveAsOfPayloa
|
|
|
419
474
|
slope: { frontSymbol: front?.symbol ?? null, nextSymbol: next?.symbol ?? null, value, annualizedRollYield: roll, percentile: null,
|
|
420
475
|
rollPercentile: null, samples: 0, historyStart: null, historyEnd: null, asOf: front && next ? curve.asOf : null,
|
|
421
476
|
state: value == null ? "unavailable" : Math.abs(value) < 1e-10 ? "flat" : value > 0 ? "contango" : "backwardation" },
|
|
422
|
-
gaps: curve.gaps,
|
|
477
|
+
gaps: curve.gaps, spec: curve.spec ?? null, settlementDate: settled.length ? curve.asOf : null,
|
|
423
478
|
};
|
|
424
479
|
}
|
|
@@ -4,6 +4,7 @@ import { fitChartTableColumns } from "../../../components/chart-table";
|
|
|
4
4
|
import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve";
|
|
5
5
|
import { isAccessDenied } from "../../../api-client/errors";
|
|
6
6
|
import type { FuturesContract } from "../../../api-client/futures-curve";
|
|
7
|
+
import { futuresTickText } from "../../../market-data/futures-contract";
|
|
7
8
|
import { useAsyncResource, usePaneSettingValue, usePluginPaneState, useShortcut } from "../../../public/react";
|
|
8
9
|
import { usePaneInstance, usePaneTitle } from "../../../state/app/context";
|
|
9
10
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
@@ -16,14 +17,19 @@ import { futuresSessionRefreshInterval } from "../shared/futures-session";
|
|
|
16
17
|
import { useResearchCloudSession } from "../shared/research-cloud-session";
|
|
17
18
|
import { useQuoteBoard } from "../shared/use-quote-board";
|
|
18
19
|
import { getCachedFuturesCurve, loadFuturesCurve, loadFuturesCurveAsOf } from "./client";
|
|
19
|
-
import { basisSpotSymbol, curveAsOfDate, curveAxisPrice, curveBasisPercent, curveBasisRows, curveChangeText, curveContractChanges, curvePrice, curveRank, curveSpot, curveSpotLabel, curveTimestamp, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, normalizeCurveRoot, sortCurveContracts, thinContractCount, thinContractsNotice, unsupportedCurveRootMessage, type CurveContractChanges } from "./model";
|
|
20
|
+
import { basisSpotSymbol, curveAsOfDate, curveAxisPrice, curveBasisPercent, curveBasisRows, curveChangeText, curveContractChanges, curveContractCode, curvePrice, curveRank, curveSpot, curveSpotLabel, curveSpreadLabel, curveTimestamp, curveUnitLabel, DEFAULT_CURVE_HORIZON, futuresCurveSeries, newestQuote, normalizeCurveRoot, sortCurveContracts, thinContractCount, thinContractsNotice, unsupportedCurveRootMessage, type CurveContractChanges } from "./model";
|
|
20
21
|
|
|
21
22
|
const TABS = [{ value: "curve", label: "Curve" }, { value: "contracts", label: "Contracts" }];
|
|
23
|
+
// One column order for every root. SETTLE is the exchange settlement for the
|
|
24
|
+
// curve's settlement session, LAST the latest trade; the header dates SETTLE.
|
|
22
25
|
const COLUMNS: DataTableColumn[] = [
|
|
23
26
|
// The longest symbol is a three-letter root with its month and venue: RTYH27.CME.
|
|
24
27
|
{ id: "symbol", label: "CONTRACT", width: 11, align: "left" },
|
|
25
28
|
{ id: "expiry", label: "EXPIRY", width: 10, align: "left" },
|
|
26
|
-
{ id: "
|
|
29
|
+
{ id: "notice", label: "1ST NOTICE", width: 10, align: "left" },
|
|
30
|
+
// A Treasury price keeps its 1/256 tick: 103.50000000.
|
|
31
|
+
{ id: "settle", label: "SETTLE", width: 12, align: "right" },
|
|
32
|
+
{ id: "price", label: "LAST", width: 12, align: "right" },
|
|
27
33
|
// A Treasury change keeps its 1/256 tick: +0.11718750.
|
|
28
34
|
{ id: "change", label: "CHG", width: 11, align: "right" },
|
|
29
35
|
{ id: "percentile", label: "PCTL", width: 5, align: "right" },
|
|
@@ -31,25 +37,28 @@ const COLUMNS: DataTableColumn[] = [
|
|
|
31
37
|
{ id: "volume", label: "VOLUME", width: 10, align: "right" },
|
|
32
38
|
{ id: "asOf", label: "AS OF UTC", width: 16, align: "left" },
|
|
33
39
|
];
|
|
40
|
+
const CONTRACTS_DROP_ORDER = ["percentile", "volume", "oi", "notice"];
|
|
41
|
+
const column = (id: string) => COLUMNS.find((entry) => entry.id === id)!;
|
|
34
42
|
// The curve's rows move with the look-back curves drawn above them; the
|
|
35
43
|
// footer carries the quote time every row would otherwise repeat. The session
|
|
36
44
|
// change stays on Contracts, where the full table has the width for it.
|
|
37
45
|
const CURVE_COLUMNS: DataTableColumn[] = [
|
|
38
|
-
|
|
46
|
+
column("symbol"), column("expiry"), column("settle"), column("price"),
|
|
39
47
|
{ id: "change1w", label: "VS 1W", width: 10, align: "right" },
|
|
40
48
|
{ id: "change1m", label: "VS 1M", width: 10, align: "right" },
|
|
41
|
-
|
|
49
|
+
column("percentile"), column("oi"), column("volume"),
|
|
42
50
|
];
|
|
51
|
+
const CURVE_DROP_ORDER = ["percentile", "volume", "change1m", "oi"];
|
|
43
52
|
// A crypto curve also reads against spot. Its widths are the tightest each column
|
|
44
53
|
// holds (BTCV26.CME, 128000.00), so the two new columns fit beside the rest, and
|
|
45
54
|
// a narrow pane drops the least useful ones before a number would be clipped.
|
|
46
55
|
const BASIS_CURVE_COLUMNS: DataTableColumn[] = [
|
|
47
|
-
{ ...
|
|
56
|
+
{ ...column("symbol"), width: 10 }, column("expiry"), { ...column("settle"), width: 10 }, { ...column("price"), width: 10 },
|
|
48
57
|
{ id: "vsSpot", label: "VS SPOT", width: 9, align: "right" },
|
|
49
58
|
{ id: "annBasis", label: "ANN BASIS", width: 11, align: "right" },
|
|
50
|
-
...CURVE_COLUMNS.slice(
|
|
59
|
+
...CURVE_COLUMNS.slice(4),
|
|
51
60
|
];
|
|
52
|
-
const BASIS_DROP_ORDER = ["percentile", "volume", "change1m", "oi"];
|
|
61
|
+
const BASIS_DROP_ORDER = ["percentile", "volume", "change1m", "oi", "settle"];
|
|
53
62
|
const NO_SYMBOLS: string[] = [];
|
|
54
63
|
/** The spot's age is judged against the clock, so a quote that stops moving still goes stale on screen. */
|
|
55
64
|
const SPOT_CLOCK_MS = 30_000;
|
|
@@ -135,17 +144,31 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
|
|
|
135
144
|
const statItems: StatItem[] = data ? [
|
|
136
145
|
{ id: "roll", label: "Ann. roll yield", value: signedPercent(data.slope.annualizedRollYield),
|
|
137
146
|
detail: curveRank(data.slope.rollPercentile, data.slope.samples) },
|
|
138
|
-
|
|
147
|
+
// Named by the contracts it subtracts: the front is the first month the market trades, not always the first row.
|
|
148
|
+
{ id: "spread", label: curveSpreadLabel(data.slope), value: data.slope.value == null ? "--" : curvePrice(data.slope.value, root),
|
|
139
149
|
detail: [data.slope.state, curveRank(data.slope.percentile, data.slope.samples), slopeDate].filter(Boolean).join(" · ") },
|
|
150
|
+
...data.spec ? [{ id: "contract", label: "Contract", value: data.spec.size, detail: `tick ${futuresTickText(data.spec)}` }] : [],
|
|
140
151
|
] : [];
|
|
141
152
|
const { strip: tabStrip, rows: tabRows } = usePaneTabs({ tabs: TABS, activeValue: tab, onSelect: setTab, focused, dense: true });
|
|
142
153
|
// The query bar takes one row below the tabs, and a refused date one more.
|
|
143
154
|
const bodyHeight = Math.max(1, height - tabRows - 1 - (dateError ? 1 : 0));
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
// A Cboe price is the settlement itself, so its curve has no separate last trade.
|
|
156
|
+
const settlesOnly = data?.source === "cboe";
|
|
157
|
+
const settleLabel = data?.settlementDate ? `SETTLE ${data.settlementDate.slice(5)}` : "SETTLE";
|
|
158
|
+
// Cash-settled roots (ES, CL's Brent, VX) have no first notice day to show.
|
|
159
|
+
const noNotice = !data?.contracts.some((row) => row.firstNotice);
|
|
160
|
+
const columns = useMemo(() => {
|
|
161
|
+
// Labelled before fitting: a dated header is wider than the column's numbers.
|
|
162
|
+
const shown = (list: DataTableColumn[]) => list.filter((entry) => !(settlesOnly && entry.id === "price") && !(noNotice && entry.id === "notice"))
|
|
163
|
+
.map((entry) => entry.id === "settle" ? { ...entry, label: settleLabel } : entry);
|
|
164
|
+
return !curveTab ? fitChartTableColumns(shown(COLUMNS), width, CONTRACTS_DROP_ORDER)
|
|
165
|
+
: basisActive ? fitChartTableColumns(shown(BASIS_CURVE_COLUMNS), width, BASIS_DROP_ORDER)
|
|
166
|
+
: fitChartTableColumns(shown(CURVE_COLUMNS), width, CURVE_DROP_ORDER);
|
|
167
|
+
}, [basisActive, curveTab, noNotice, settleLabel, settlesOnly, width]);
|
|
146
168
|
const formatValue = useCallback((value: number) => curvePrice(value, root), [root]);
|
|
147
169
|
const formatChange = useCallback((value: number) => curveChangeText(value, root), [root]);
|
|
148
|
-
const
|
|
170
|
+
const unit = data ? curveUnitLabel(data) : null;
|
|
171
|
+
const caption = `${sentenceCase(unit ?? "price")} by contract month`;
|
|
149
172
|
// The strip keeps the curve's shape and the selected contract in one row.
|
|
150
173
|
const strip = curveTab ? curveStrip(curves, formatValue, { caption: "Price", selectedPointId: selectedId }) : null;
|
|
151
174
|
// Delayed contract quotes move all session; the curve follows them once a
|
|
@@ -177,8 +200,11 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
|
|
|
177
200
|
// A past date with no archived curve says why in the body, not behind the warning.
|
|
178
201
|
const emptyPast = !!requestedDate && !!data && !data.contracts.length;
|
|
179
202
|
// Thin contracts join the data warnings: the info row has no room for them beside the quote times.
|
|
203
|
+
// A long in these can be assigned delivery: the hedge a trader rolls before first notice.
|
|
204
|
+
const delivering = data?.contracts.filter((row) => row.inDelivery).map((row) => curveContractCode(row.symbol)) ?? [];
|
|
180
205
|
usePaneNoticeFooter({ registrationId: "futures-curve:notices", focused,
|
|
181
|
-
notices: emptyPast ? [] : [...data?.gaps ?? [], ...thinCount ? [thinContractsNotice(thinCount)] : []
|
|
206
|
+
notices: emptyPast ? [] : [...data?.gaps ?? [], ...thinCount ? [thinContractsNotice(thinCount)] : [],
|
|
207
|
+
...delivering.length ? [`${delivering.join(", ")} ${delivering.length === 1 ? "is" : "are"} past first notice.`] : []] });
|
|
182
208
|
const delay = Math.max(0, ...(data?.contracts.map((row) => row.delayMinutes ?? 0) ?? []));
|
|
183
209
|
usePaneStatusFooter({ registrationId: "futures-curve", loading: resource.loading, error: resource.error,
|
|
184
210
|
hints: [
|
|
@@ -186,7 +212,7 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
|
|
|
186
212
|
...requestedDate ? [{ id: "latest", key: "c", label: "urrent", title: "Current Curve", onPress: () => selectDate("") }] : [],
|
|
187
213
|
],
|
|
188
214
|
info: data ? [
|
|
189
|
-
{ id: "source", parts: [{ text: `${requestedDate ? "daily archive" : data.source === "cboe" ? "settlement" : delay > 0 ? `${delay}m delayed` : "dated quotes"} · ${
|
|
215
|
+
{ id: "source", parts: [{ text: `${requestedDate ? "daily archive" : data.source === "cboe" ? "settlement" : delay > 0 ? `${delay}m delayed` : "dated quotes"} · ${unit ?? "units unavailable"} · ${curveTimestamp(newest)}${newest?.includes("T") ? " UTC" : ""}`, tone: "muted" }] },
|
|
190
216
|
...(staleCount ? [{ id: "stale", parts: [{ text: `${staleCount} of ${data.contracts.length} stale`, tone: "warning" as const }] }] : []),
|
|
191
217
|
...(spot ? [{ id: "spot", parts: [spot.status === "ok" ? { text: curveSpotLabel(spot, root, clock), tone: "muted" as const }
|
|
192
218
|
: { text: `Basis blank: ${spot.reason}`, tone: "warning" as const }] }] : []),
|
|
@@ -202,10 +228,18 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
|
|
|
202
228
|
strip,
|
|
203
229
|
} : null;
|
|
204
230
|
const renderCell = useCallback((row: FuturesContract, column: DataTableColumn) => {
|
|
205
|
-
|
|
206
|
-
if (column.id === "
|
|
207
|
-
|
|
208
|
-
if (column.id === "
|
|
231
|
+
// A contract past first notice can be assigned delivery.
|
|
232
|
+
if (column.id === "symbol") return { text: row.symbol, color: row.inDelivery ? colors.warning : undefined };
|
|
233
|
+
if (column.id === "expiry") return { text: row.lastTrade ?? row.expiration, color: colors.textMuted };
|
|
234
|
+
if (column.id === "notice") return { text: row.firstNotice ?? "--", color: row.inDelivery ? colors.warning : colors.textMuted };
|
|
235
|
+
if (column.id === "settle") return { text: curvePrice(row.settlement ?? null, root) };
|
|
236
|
+
// A past curve's settled row has no separate last trade: its price is the settlement.
|
|
237
|
+
if (column.id === "price") {
|
|
238
|
+
const lastTrade = requestedDate && row.settlement != null ? null : row.price;
|
|
239
|
+
// Beside its settlement the last trade is the secondary price; without one it is the row's price.
|
|
240
|
+
return { text: curvePrice(lastTrade, root), color: curveTab && (row.stale || basis.get(row.symbol)?.thin) ? colors.warning
|
|
241
|
+
: row.settlement != null ? colors.textMuted : undefined };
|
|
242
|
+
}
|
|
209
243
|
if (column.id === "change") {
|
|
210
244
|
const text = curveChangeText(row.change ?? null, root);
|
|
211
245
|
return { text, color: text.startsWith("+") ? colors.positive : text.startsWith("-") && /[1-9]/.test(text) ? colors.negative : colors.textMuted };
|
|
@@ -222,7 +256,7 @@ function FuturesCurveView({ width, height, focused, root }: PaneProps & { root:
|
|
|
222
256
|
if (column.id === "oi") return { text: integer(row.openInterest) };
|
|
223
257
|
if (column.id === "volume") return { text: integer(row.volume) };
|
|
224
258
|
return { text: curveTimestamp(row.asOf), color: row.stale ? colors.warning : colors.textMuted };
|
|
225
|
-
}, [basis, changes, colors, curveTab, root]);
|
|
259
|
+
}, [basis, changes, colors, curveTab, requestedDate, root]);
|
|
226
260
|
return <Box width={width} height={height} flexDirection="column">
|
|
227
261
|
{tabStrip}
|
|
228
262
|
<QueryBar width={width} filters={[{
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { MarketDataCoordinator } from "../../../market-data/coordinator";
|
|
2
|
+
import type { QueryEntry } from "../../../market-data/result-types";
|
|
3
|
+
import { resolveEntryData } from "../../../market-data/selectors";
|
|
4
|
+
import type { DataProvider } from "../../../types/data-provider";
|
|
5
|
+
import type { Quote } from "../../../types/financials";
|
|
6
|
+
import { fxLegReferenceRate, fxLegs, liveFxLegEntry, type FxLeg } from "./live-legs";
|
|
7
|
+
|
|
8
|
+
/** One USD leg as the board reads it: the rate it draws and where that rate came from. */
|
|
9
|
+
export interface FxLegReading {
|
|
10
|
+
leg: FxLeg;
|
|
11
|
+
/** USD per unit, from the pair quote or else the loaded rate; null when neither loaded. */
|
|
12
|
+
entry: QueryEntry<number> | null;
|
|
13
|
+
/** The pair quote, only when the rate is its own: it then says what the rate is (live or delayed, its session). */
|
|
14
|
+
quote: Quote | null;
|
|
15
|
+
/**
|
|
16
|
+
* USD per unit at the pair's previous close, only when the rate is the pair
|
|
17
|
+
* quote's: a loaded rate has no previous close to read against.
|
|
18
|
+
*/
|
|
19
|
+
reference: number | null;
|
|
20
|
+
/** Why the leg has no rate, when it has none. */
|
|
21
|
+
error: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The board's USD legs, read once the way the pane reads them, through the
|
|
26
|
+
* same market data coordinator over the same service: each leg's loaded rate
|
|
27
|
+
* (`loadFxRate`, dated by the shared FX cache), replaced by its pair quote when
|
|
28
|
+
* that quote is current and at least as new. Only a pair quote brings the
|
|
29
|
+
* previous close a cross's move is read against. A leg with neither has no
|
|
30
|
+
* rate, never parity.
|
|
31
|
+
*/
|
|
32
|
+
export async function loadFxBoard(
|
|
33
|
+
currencies: readonly string[],
|
|
34
|
+
marketData: DataProvider,
|
|
35
|
+
options: { forceRefresh?: boolean } = {},
|
|
36
|
+
): Promise<Map<string, FxLegReading>> {
|
|
37
|
+
const coordinator = new MarketDataCoordinator(marketData);
|
|
38
|
+
try {
|
|
39
|
+
const legs = fxLegs(currencies);
|
|
40
|
+
const forceRefresh = options.forceRefresh === true;
|
|
41
|
+
const [rates, quotes] = await Promise.all([
|
|
42
|
+
Promise.all(legs.map((leg) => coordinator.loadFxRate(leg.currency, { forceRefresh }))),
|
|
43
|
+
// A pair quote only refines a loaded rate, so a failed batch leaves the loaded rates.
|
|
44
|
+
coordinator.loadQuotesBatch(legs.map((leg) => ({ symbol: leg.symbol, exchange: "" })), { forceRefresh })
|
|
45
|
+
.catch((): QueryEntry<Quote>[] => []),
|
|
46
|
+
]);
|
|
47
|
+
return new Map(legs.map((leg, index) => {
|
|
48
|
+
const rate = rates[index]!;
|
|
49
|
+
const quote = quotes[index];
|
|
50
|
+
const live = liveFxLegEntry(leg, quote, rate);
|
|
51
|
+
const entry = live ?? (resolveEntryData(rate) != null ? rate : null);
|
|
52
|
+
return [leg.currency, {
|
|
53
|
+
leg,
|
|
54
|
+
entry,
|
|
55
|
+
quote: live ? resolveEntryData(quote) : null,
|
|
56
|
+
reference: live ? fxLegReferenceRate(leg, quote) : null,
|
|
57
|
+
error: entry ? null : rate.error?.message ?? "No rate came back.",
|
|
58
|
+
}] as const;
|
|
59
|
+
}));
|
|
60
|
+
} finally {
|
|
61
|
+
coordinator.destroy();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QueryEntry } from "../../../market-data/result-types";
|
|
2
|
+
import { isFxRateStale } from "../../../utils/fx-status";
|
|
2
3
|
|
|
3
4
|
const iso = (time: number | null | undefined): string => {
|
|
4
5
|
const date = new Date(time ?? NaN);
|
|
@@ -23,7 +24,7 @@ export function createFxExportMetadata(
|
|
|
23
24
|
const status: string[] = [];
|
|
24
25
|
if (!available) status.push("unavailable");
|
|
25
26
|
if (entry?.phase === "loading" || entry?.phase === "refreshing") status.push("loading");
|
|
26
|
-
if (available && (entry
|
|
27
|
+
if (available && isFxRateStale(entry, now)) status.push("stale");
|
|
27
28
|
if (available && !iso(entry?.asOf)) status.push("time unknown");
|
|
28
29
|
return [currency, available ? rate : "", iso(entry?.asOf), iso(entry?.fetchedAt),
|
|
29
30
|
status.join("; ") || "current", entry?.error?.message ?? ""];
|