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,4 +1,6 @@
|
|
|
1
1
|
import { createBaseConverter } from "../../../../cli/base-converter";
|
|
2
|
+
import { savedListingName } from "../../../../cli/listing-arg";
|
|
3
|
+
import { currentListingName } from "../../../../tickers/listing-name";
|
|
2
4
|
import { withMarketData } from "../../../../cli/scoped-context";
|
|
3
5
|
import { countCollectionTickers } from "../../../../cli/helpers";
|
|
4
6
|
import {
|
|
@@ -8,9 +10,18 @@ import {
|
|
|
8
10
|
renderTable,
|
|
9
11
|
} from "../../../../utils/cli-output";
|
|
10
12
|
import { formatCompact, formatPercentRaw } from "../../../../utils/format";
|
|
11
|
-
import {
|
|
13
|
+
import { currencyPriceMinDigits, formatMarketCostWithCurrency, formatMarketPrice, formatMarketPriceWithCurrency, formatMarketQuantity, quoteFormatOptions } from "../../../../market-data/market/format";
|
|
12
14
|
import { resolvePriceBasis } from "../../../../market-data/market/price-basis";
|
|
13
|
-
import { getPortfolioPositionMetrics,
|
|
15
|
+
import { getPortfolioPositionMetrics, resolvePortfolioMarketValue, resolvePortfolioPositionPnl } from "../../../../portfolio/position-metrics";
|
|
16
|
+
import {
|
|
17
|
+
getPortfolioValuationDisplay,
|
|
18
|
+
getPortfolioValuationTime,
|
|
19
|
+
VALUATION_BASIS_LABELS,
|
|
20
|
+
valuationTimeIso,
|
|
21
|
+
type PortfolioValuationBasis,
|
|
22
|
+
} from "../../../../portfolio/valuation-basis";
|
|
23
|
+
import { formatUtcDayTime, parseReportTime } from "../../../../utils/utc-time";
|
|
24
|
+
import { quoteFreshnessFields } from "../../shared/report-freshness";
|
|
14
25
|
import { resolvePortfolioTotalsCurrency } from "../summary/totals";
|
|
15
26
|
import {
|
|
16
27
|
EXTENDED_SESSION_LABELS,
|
|
@@ -21,16 +32,25 @@ import {
|
|
|
21
32
|
type ExtendedSession,
|
|
22
33
|
type ExtendedSessionDisplay,
|
|
23
34
|
} from "../../../../market-data/market/status";
|
|
24
|
-
import { headlineQuoteDisplay } from "../column-values";
|
|
35
|
+
import { formatPerpetualRate, headlineQuoteDisplay } from "../column-values";
|
|
25
36
|
import type { AppConfig } from "../../../../types/config";
|
|
26
37
|
import type { DataProvider } from "../../../../types/data-provider";
|
|
27
38
|
import type { CliCommandContext } from "../../../../types/plugin";
|
|
28
39
|
import type { MarketContext } from "../../../../cli/types";
|
|
29
40
|
import type { Portfolio, TickerPosition, TickerRecord } from "../../../../types/ticker";
|
|
41
|
+
import type { TickerFinancials } from "../../../../types/financials";
|
|
42
|
+
import type { TickerInstrumentKind } from "../../../../types/instrument";
|
|
43
|
+
import { resolveTickerInstrumentKind } from "../../../../tickers/instrument-kind";
|
|
44
|
+
import { perpetualListing } from "../../../../tickers/perpetual-listing";
|
|
45
|
+
import { loadPerpetualMarket } from "../../../../sources/gloomberb-cloud/perpetuals";
|
|
46
|
+
import { listedMarketCapitalization, selectMarketCapitalization } from "../../../../utils/market-capitalization";
|
|
47
|
+
import { chunked, mapWithConcurrency } from "../../../../utils/concurrency";
|
|
48
|
+
import { liveMarketCapitalization } from "../live-valuation";
|
|
30
49
|
import { instrumentFromTicker } from "../../../../market-data/request-types";
|
|
31
50
|
import { toMarketDataContext } from "../../../../market-data/selectors";
|
|
32
51
|
import { cliFreshnessFooter } from "../../../../cli/result";
|
|
33
|
-
import {
|
|
52
|
+
import { renderReportGlossary, reportGlossary, type GlossaryEntry } from "../../../../cli/glossary";
|
|
53
|
+
import { quotesFreshness, rowsFreshness } from "../../../../cli/freshness";
|
|
34
54
|
import type { BrokerAccount } from "../../../../types/trading";
|
|
35
55
|
import {
|
|
36
56
|
BROKER_ACCOUNT_MISSING_NOTE,
|
|
@@ -47,20 +67,31 @@ import {
|
|
|
47
67
|
type PortfolioAllocation,
|
|
48
68
|
} from "../allocation";
|
|
49
69
|
import { findCachedPortfolioAccount } from "../cached-account";
|
|
70
|
+
import { importedFromText } from "../imported-from";
|
|
71
|
+
import { findNamedCollection } from "../../../../portfolio/collection-name";
|
|
50
72
|
|
|
51
73
|
/** Money prices pad to the currency's minor unit ($337.90, not $337.9), as the ticker view does. */
|
|
52
74
|
function priceFormatOptions(quote: Parameters<typeof quoteFormatOptions>[0] & { currency?: string }, assetCategory?: string) {
|
|
53
75
|
const options = quoteFormatOptions(quote, assetCategory);
|
|
54
76
|
return resolvePriceBasis(options.priceBasis, options.assetCategory) === "per-unit"
|
|
55
|
-
? { ...options, minimumFractionDigits:
|
|
77
|
+
? { ...options, minimumFractionDigits: currencyPriceMinDigits(quote?.currency) }
|
|
56
78
|
: options;
|
|
57
79
|
}
|
|
58
80
|
|
|
81
|
+
/** A Last as `quote` prints it: money in the quote's currency, and an index level in points, padded to two decimals. */
|
|
82
|
+
function formatListPrice(value: number, quote: Parameters<typeof quoteFormatOptions>[0] & { currency: string }, assetCategory?: string): string {
|
|
83
|
+
const options = priceFormatOptions(quote, assetCategory);
|
|
84
|
+
return options.assetCategory?.trim().toUpperCase() === "INDEX"
|
|
85
|
+
? formatMarketPrice(value, { ...options, minimumFractionDigits: 2 })
|
|
86
|
+
: formatMarketPriceWithCurrency(value, quote.currency, options);
|
|
87
|
+
}
|
|
88
|
+
|
|
59
89
|
/**
|
|
60
90
|
* What a collection's Last and Chg show, as the PF and watchlist panes do: the
|
|
61
91
|
* traded price until the regular close, then that close and its move, with a
|
|
62
92
|
* pre-market or after-hours print beside them, measured from that close.
|
|
63
|
-
* Market value and P&L keep the
|
|
93
|
+
* Market value and P&L keep the latest print unless the book is valued at the
|
|
94
|
+
* regular close (`portfolio show --close`), which is this Last.
|
|
64
95
|
*/
|
|
65
96
|
export function collectionQuoteHeadline(quote: CollectionQuote | undefined): {
|
|
66
97
|
headline: ActiveQuoteDisplay | null;
|
|
@@ -82,6 +113,20 @@ export function extendedQuoteFields(extended: ExtendedSessionDisplay | null) {
|
|
|
82
113
|
};
|
|
83
114
|
}
|
|
84
115
|
|
|
116
|
+
/**
|
|
117
|
+
* The listing a row is (SAN:EPA, as the Ticker column names it) and its company, as export fields:
|
|
118
|
+
* the name its quote gives now, as `quote` names it, else the one it was saved with.
|
|
119
|
+
*/
|
|
120
|
+
function listingColumns(ticker: TickerRecord, quote: CollectionQuote | undefined) {
|
|
121
|
+
return { listing: listingLabel(ticker), name: currentListingName(ticker, quote) };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The Chg column's move as export fields: from the close the headline price is measured against. */
|
|
125
|
+
function quoteMoveColumns(headline: ActiveQuoteDisplay | null) {
|
|
126
|
+
const known = (value: number | undefined) => value != null && Number.isFinite(value) ? value : null;
|
|
127
|
+
return { quoteChange: known(headline?.change), quoteChangePercent: known(headline?.changePercent) };
|
|
128
|
+
}
|
|
129
|
+
|
|
85
130
|
/** The extended sessions among the rows' prints, in the order their columns go. */
|
|
86
131
|
export function extendedSessionsOf(sessions: Iterable<ExtendedSession | null | undefined>): ExtendedSession[] {
|
|
87
132
|
const present = new Set(sessions);
|
|
@@ -129,8 +174,23 @@ export function renderCollectionOverview(config: AppConfig, tickers: TickerRecor
|
|
|
129
174
|
return blocks.join("\n");
|
|
130
175
|
}
|
|
131
176
|
|
|
132
|
-
export
|
|
133
|
-
|
|
177
|
+
export interface ShowCollectionOptions {
|
|
178
|
+
/** `--explain`: follow the report with what the terms it shows mean, as `fn <function> --explain` does. */
|
|
179
|
+
explain?: boolean;
|
|
180
|
+
/** What a portfolio's value is priced at; the latest print when absent. */
|
|
181
|
+
basis?: PortfolioValuationBasis;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export async function showCollection(name: string, ctx: CliCommandContext, options: ShowCollectionOptions = {}) {
|
|
185
|
+
await withMarketData(ctx, (context) => showCollectionWithMarketData(name, ctx, context, options));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** What `--explain` names a report by when it shows no terms. */
|
|
189
|
+
const EXPLAINED_REPORT = "This report";
|
|
190
|
+
|
|
191
|
+
/** The terms as `--json` lists them, in the shape `fn --explain` gives `data.glossary`. */
|
|
192
|
+
function glossaryExport(entries: readonly GlossaryEntry[]) {
|
|
193
|
+
return entries.map(({ term, short, definition }) => ({ term, ...(short ? { short } : {}), definition }));
|
|
134
194
|
}
|
|
135
195
|
|
|
136
196
|
/** A portfolio or watchlist named by its id or its name, either in any case. */
|
|
@@ -139,14 +199,9 @@ export type CollectionMatch =
|
|
|
139
199
|
| { kind: "watchlist"; id: string; name: string };
|
|
140
200
|
|
|
141
201
|
export function findCollection(config: AppConfig, name: string): CollectionMatch | null {
|
|
142
|
-
const
|
|
143
|
-
const portfolio = config.portfolios.find((entry) =>
|
|
144
|
-
entry.id.toLowerCase() === normalized || entry.name.toLowerCase() === normalized
|
|
145
|
-
);
|
|
202
|
+
const portfolio = findNamedCollection(config.portfolios, name);
|
|
146
203
|
if (portfolio) return { kind: "portfolio", id: portfolio.id, name: portfolio.name, portfolio };
|
|
147
|
-
const watchlist = config.watchlists
|
|
148
|
-
entry.id.toLowerCase() === normalized || entry.name.toLowerCase() === normalized
|
|
149
|
-
);
|
|
204
|
+
const watchlist = findNamedCollection(config.watchlists, name);
|
|
150
205
|
return watchlist ? { kind: "watchlist", id: watchlist.id, name: watchlist.name } : null;
|
|
151
206
|
}
|
|
152
207
|
|
|
@@ -184,7 +239,7 @@ export async function loadCollectionQuotes(
|
|
|
184
239
|
interface PortfolioPositionValue {
|
|
185
240
|
ticker: TickerRecord;
|
|
186
241
|
quote: CollectionQuote | undefined;
|
|
187
|
-
/** The live print,
|
|
242
|
+
/** The live print, extended hours included. */
|
|
188
243
|
activeQuote: ReturnType<typeof getActiveQuoteDisplay> | null;
|
|
189
244
|
/** Last and Chg as shown: the regular session's (collectionQuoteHeadline). */
|
|
190
245
|
headline: ActiveQuoteDisplay | null;
|
|
@@ -195,11 +250,28 @@ interface PortfolioPositionValue {
|
|
|
195
250
|
metrics: ReturnType<typeof getPortfolioPositionMetrics> | null;
|
|
196
251
|
/** Unrealized P&L in the portfolio's currency. */
|
|
197
252
|
pnl: number | null;
|
|
253
|
+
/** The price the position is valued at, on the valuation's basis, and when it is from. */
|
|
254
|
+
valuation: PortfolioValuationFields;
|
|
255
|
+
/**
|
|
256
|
+
* The position's move over the day of that price, in the portfolio's
|
|
257
|
+
* currency, and its value at the close before; null when either is unknown.
|
|
258
|
+
*/
|
|
259
|
+
dayMove: { change: number; previousValue: number } | null;
|
|
198
260
|
/** The `portfolio show --json` row. */
|
|
199
261
|
row: Record<string, unknown>;
|
|
200
262
|
}
|
|
201
263
|
|
|
264
|
+
/** What a row's value is priced at: exported after every other field, so existing columns keep their places. */
|
|
265
|
+
interface PortfolioValuationFields {
|
|
266
|
+
valuationBasis: PortfolioValuationBasis;
|
|
267
|
+
/** In the quote's currency. */
|
|
268
|
+
valuationPrice: number | null;
|
|
269
|
+
/** An ISO instant in UTC, or an ISO date for a close whose hour the venue's calendar does not give. */
|
|
270
|
+
valuationAsOf: string | null;
|
|
271
|
+
}
|
|
272
|
+
|
|
202
273
|
export interface PortfolioValuation {
|
|
274
|
+
basis: PortfolioValuationBasis;
|
|
203
275
|
positions: PortfolioPositionValue[];
|
|
204
276
|
/** One per ticker, for weights and targets. */
|
|
205
277
|
holdings: AllocationHolding[];
|
|
@@ -213,7 +285,8 @@ export interface PortfolioValuation {
|
|
|
213
285
|
/**
|
|
214
286
|
* Values every position of a portfolio in the portfolio's currency, as
|
|
215
287
|
* `portfolio show` prints it: cost basis, market value and unrealized P&L
|
|
216
|
-
* from the current quote
|
|
288
|
+
* from the current quote at `basis` (the latest print unless given), or the
|
|
289
|
+
* broker's snapshot where that is the basis.
|
|
217
290
|
*/
|
|
218
291
|
export async function valuePortfolioPositions({
|
|
219
292
|
tickers,
|
|
@@ -222,6 +295,7 @@ export async function valuePortfolioPositions({
|
|
|
222
295
|
currency,
|
|
223
296
|
baseCurrency,
|
|
224
297
|
toBase,
|
|
298
|
+
basis = "latest-print",
|
|
225
299
|
}: {
|
|
226
300
|
tickers: readonly TickerRecord[];
|
|
227
301
|
quotes: ReadonlyMap<string, CollectionQuote>;
|
|
@@ -229,6 +303,7 @@ export async function valuePortfolioPositions({
|
|
|
229
303
|
currency: string;
|
|
230
304
|
baseCurrency: string;
|
|
231
305
|
toBase: (value: number, fromCurrency: string) => Promise<number>;
|
|
306
|
+
basis?: PortfolioValuationBasis;
|
|
232
307
|
}): Promise<PortfolioValuation> {
|
|
233
308
|
const id = portfolioId;
|
|
234
309
|
let totalPnl = 0;
|
|
@@ -249,12 +324,15 @@ export async function valuePortfolioPositions({
|
|
|
249
324
|
|
|
250
325
|
if (positions.length === 0) {
|
|
251
326
|
const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
|
|
252
|
-
|
|
327
|
+
// What a holding would be priced at, for the trade that reaches its target.
|
|
328
|
+
const priced = basis === "regular-close" ? headline : activeQuote;
|
|
329
|
+
const unitPrice = priced ? known(await toBase(priced.price, quoteCurrency)) : null;
|
|
253
330
|
holdings.push({ symbol: ticker.metadata.ticker, held: false, marketValue: 0, units: 0, unitPrice });
|
|
254
|
-
values.push({ ticker, quote, activeQuote, headline, extended, position: null, metrics: null, pnl: null,
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
331
|
+
values.push({ ticker, quote, activeQuote, headline, extended, position: null, metrics: null, pnl: null, dayMove: null,
|
|
332
|
+
valuation: { valuationBasis: basis, valuationPrice: known(priced?.price), valuationAsOf: valuationTimeIso(getPortfolioValuationTime(quote, basis)) },
|
|
333
|
+
row: { symbol: ticker.metadata.ticker, ...listingColumns(ticker, quote), exchange: ticker.metadata.exchange, shares: null, avgCost: null,
|
|
334
|
+
positionCurrency: null, quotePrice: known(headline?.price), ...quoteMoveColumns(headline), ...extendedQuoteFields(extended), quoteCurrency: quote?.currency ?? null,
|
|
335
|
+
volume: known(quote?.volume), costBasis: null, marketValue: null, unrealizedPnl: null, baseCurrency: currency } });
|
|
258
336
|
continue;
|
|
259
337
|
}
|
|
260
338
|
|
|
@@ -263,7 +341,7 @@ export async function valuePortfolioPositions({
|
|
|
263
341
|
for (const position of positions) {
|
|
264
342
|
const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
|
|
265
343
|
const metrics = getPortfolioPositionMetrics({ ...ticker, metadata: { ...ticker.metadata, positions: [position] } }, id, quoteCurrency, undefined, quote);
|
|
266
|
-
const valuationQuote =
|
|
344
|
+
const valuationQuote = getPortfolioValuationDisplay(metrics, quote, basis);
|
|
267
345
|
const positionCurrency = metrics.positionCurrency;
|
|
268
346
|
const costBasisBase = positionCurrency ? await toBase(metrics.totalCost, positionCurrency) : Number.NaN;
|
|
269
347
|
|
|
@@ -285,19 +363,56 @@ export async function valuePortfolioPositions({
|
|
|
285
363
|
holding.marketValue = holding.marketValue == null || marketValue == null ? null : holding.marketValue + marketValue;
|
|
286
364
|
holding.units += metrics.totalShares;
|
|
287
365
|
holding.unitPrice ??= valuationQuote ? known(await toBase(valuationQuote.price, quoteCurrency)) : null;
|
|
366
|
+
const change = valuationQuote?.change;
|
|
367
|
+
const dayMove = valuationQuote && change != null && Number.isFinite(change)
|
|
368
|
+
? { change: await toBase(metrics.totalPriceUnits * change, quoteCurrency),
|
|
369
|
+
previousValue: await toBase(metrics.grossPriceUnits * (valuationQuote.price - change), quoteCurrency) }
|
|
370
|
+
: null;
|
|
288
371
|
values.push({ ticker, quote, activeQuote, headline, extended, position, metrics, pnl,
|
|
289
|
-
|
|
372
|
+
dayMove: dayMove && Number.isFinite(dayMove.change) && Number.isFinite(dayMove.previousValue) ? dayMove : null,
|
|
373
|
+
valuation: { valuationBasis: basis, valuationPrice: known(valuationQuote?.price),
|
|
374
|
+
valuationAsOf: valuationTimeIso(getPortfolioValuationTime(quote, basis, metrics)) },
|
|
375
|
+
row: { symbol: ticker.metadata.ticker, ...listingColumns(ticker, quote), exchange: ticker.metadata.exchange,
|
|
290
376
|
shares: metrics.totalShares, avgCost: known(position.avgCost), positionCurrency,
|
|
291
377
|
priceBasis: position.priceBasis ?? null, quantityUnit: metrics.priceBasis === "percent-of-par" ? "face" : null, quotePriceBasis: quote?.priceBasis ?? null,
|
|
292
|
-
// Last as shown, the regular close after it; market value and P&L are at
|
|
293
|
-
quotePrice: known(headline?.price), ...extendedQuoteFields(extended), quoteCurrency,
|
|
378
|
+
// Last as shown, the regular close after it; market value and P&L are at `valuationPrice`.
|
|
379
|
+
quotePrice: known(headline?.price), ...quoteMoveColumns(headline), ...extendedQuoteFields(extended), quoteCurrency,
|
|
380
|
+
volume: known(quote?.volume), quoteAsOf: quote?.lastUpdated ?? null,
|
|
294
381
|
costBasis: known(direction * costBasisBase), marketValue,
|
|
295
382
|
unrealizedPnl: known(pnl), baseCurrency: currency, dateAcquired: position.dateAcquired ?? null,
|
|
296
383
|
pnlBasis: selectedPnl.basis, brokerUnrealizedPnl: known(position.unrealizedPnl), brokerPnlCurrency: positionCurrency, brokerPnlAsOf: null } });
|
|
297
384
|
}
|
|
298
385
|
}
|
|
299
386
|
|
|
300
|
-
return { positions: values, holdings, totalPnl, unavailablePnl, unavailableCost, unavailableMarketValue, brokerPnlSymbols };
|
|
387
|
+
return { basis, positions: values, holdings, totalPnl, unavailablePnl, unavailableCost, unavailableMarketValue, brokerPnlSymbols };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The holdings' move over the day of the prices they are valued at, in the
|
|
392
|
+
* portfolio's currency, and that move against their value at the close
|
|
393
|
+
* before. Null when any priced holding's move is unknown, as the pane's Day is.
|
|
394
|
+
*/
|
|
395
|
+
export function portfolioDayMove(valuation: Pick<PortfolioValuation, "positions">): { change: number; changePercent: number | null } | null {
|
|
396
|
+
let change = 0;
|
|
397
|
+
let previousValue = 0;
|
|
398
|
+
for (const entry of valuation.positions) {
|
|
399
|
+
if (!entry.position) continue;
|
|
400
|
+
if (!entry.dayMove) return null;
|
|
401
|
+
change += entry.dayMove.change;
|
|
402
|
+
previousValue += entry.dayMove.previousValue;
|
|
403
|
+
}
|
|
404
|
+
return { change, changePercent: previousValue !== 0 ? (change / previousValue) * 100 : null };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** `Day's move: +$1,165.49 (+0.51%)`, the line a book valued at the close carries; `color` tints the figures by sign. */
|
|
408
|
+
export function dayMoveLine(
|
|
409
|
+
move: ReturnType<typeof portfolioDayMove>,
|
|
410
|
+
currency: string,
|
|
411
|
+
color: (text: string, value: number) => string = (text) => text,
|
|
412
|
+
): string {
|
|
413
|
+
if (!move) return "Day's move: unavailable";
|
|
414
|
+
const percent = move.changePercent != null ? ` (${formatPercentRaw(move.changePercent)})` : "";
|
|
415
|
+
return `Day's move: ${color(`${formatAllocationMoney(move.change, currency, { signed: true })}${percent}`, move.change)}`;
|
|
301
416
|
}
|
|
302
417
|
|
|
303
418
|
/** A portfolio's cash line, as `portfolio show` and `fn PF` report it. */
|
|
@@ -344,6 +459,16 @@ function signed(text: string, value: number | null | undefined): string {
|
|
|
344
459
|
|
|
345
460
|
const NOT_AVAILABLE = "n/a";
|
|
346
461
|
|
|
462
|
+
/**
|
|
463
|
+
* What a table row calls a listing: SAN:EPA or SAN:NYSE, as the add, remove and
|
|
464
|
+
* target messages name it, not the key it is stored under (SAN, SAN:XNYS),
|
|
465
|
+
* which leaves two listings of one symbol looking alike. JSON and CSV keep the
|
|
466
|
+
* stored key in `symbol`.
|
|
467
|
+
*/
|
|
468
|
+
export function listingLabel(ticker: TickerRecord): string {
|
|
469
|
+
return savedListingName(ticker).key;
|
|
470
|
+
}
|
|
471
|
+
|
|
347
472
|
/** One cell per extended column: the print and its move from the close, as `quote` prints it, or blank. */
|
|
348
473
|
function extendedCells(
|
|
349
474
|
sessions: readonly ExtendedSession[],
|
|
@@ -352,14 +477,64 @@ function extendedCells(
|
|
|
352
477
|
assetCategory: string | undefined,
|
|
353
478
|
): string[] {
|
|
354
479
|
return sessions.map((session) => extended?.session === session && quote
|
|
355
|
-
? `${
|
|
480
|
+
? `${formatListPrice(extended.price, quote, assetCategory)} ${colorBySign(formatPercentRaw(extended.changePercent), extended.change)}`
|
|
356
481
|
: "");
|
|
357
482
|
}
|
|
358
483
|
|
|
484
|
+
/**
|
|
485
|
+
* The observations a portfolio's value is dated by, one per quote: when the
|
|
486
|
+
* price it is valued at is from, with the quote's feed, delay and venue.
|
|
487
|
+
*/
|
|
488
|
+
function valuationObservations(positions: readonly PortfolioPositionValue[]): Record<string, unknown>[] {
|
|
489
|
+
const seen = new Set<string>();
|
|
490
|
+
return positions.flatMap(({ ticker, quote, valuation }) => {
|
|
491
|
+
if (!quote || seen.has(ticker.metadata.ticker)) return [];
|
|
492
|
+
seen.add(ticker.metadata.ticker);
|
|
493
|
+
return [{ ...quoteFreshnessFields(quote), valuationAsOf: valuation.valuationAsOf }];
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* How current a valued portfolio is: dated by the prices its value uses, the
|
|
499
|
+
* regular close when it is valued there, and in exports by the oldest of them
|
|
500
|
+
* too (`citeOldest`), so an audit file does not date an equity's Friday price
|
|
501
|
+
* by the newest row. `note` names the basis where no header line does.
|
|
502
|
+
*/
|
|
503
|
+
function portfolioValuationFreshness(
|
|
504
|
+
valuation: Pick<PortfolioValuation, "positions" | "basis">,
|
|
505
|
+
source: string,
|
|
506
|
+
{ note = false, now = Date.now() }: { note?: boolean; now?: number } = {},
|
|
507
|
+
) {
|
|
508
|
+
const observations = valuationObservations(valuation.positions);
|
|
509
|
+
if (observations.length === 0) return undefined;
|
|
510
|
+
return rowsFreshness(observations, {
|
|
511
|
+
source,
|
|
512
|
+
observedKey: "valuationAsOf",
|
|
513
|
+
citeOldest: true,
|
|
514
|
+
...(note ? { note: `priced at ${VALUATION_BASIS_LABELS[valuation.basis]}` } : {}),
|
|
515
|
+
}, undefined, now);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** `Priced at the latest print, extended hours included`: the line under a portfolio's title. */
|
|
519
|
+
function valuationBasisLine(basis: PortfolioValuationBasis): string {
|
|
520
|
+
return `Priced at ${VALUATION_BASIS_LABELS[basis]}`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* An As of cell: when the price a row's value uses is from (`valuationAsOf`),
|
|
525
|
+
* in UTC as its header says; a close dated only by its day says so.
|
|
526
|
+
*/
|
|
527
|
+
export function valuationTimeText(value: unknown, now = Date.now()): string {
|
|
528
|
+
const time = parseReportTime(value);
|
|
529
|
+
if (!time) return "—";
|
|
530
|
+
return time.dateOnly ? `${formatUtcDayTime(time, now)} close` : formatUtcDayTime(time, now);
|
|
531
|
+
}
|
|
532
|
+
|
|
359
533
|
async function showCollectionWithMarketData(
|
|
360
534
|
name: string,
|
|
361
535
|
ctx: CliCommandContext,
|
|
362
536
|
{ config, store, dataProvider, persistence }: MarketContext,
|
|
537
|
+
{ explain = false, basis = "latest-print" }: ShowCollectionOptions,
|
|
363
538
|
) {
|
|
364
539
|
const tickers = (await store.loadAllTickers()).sort((left, right) =>
|
|
365
540
|
left.metadata.ticker.localeCompare(right.metadata.ticker)
|
|
@@ -374,10 +549,13 @@ async function showCollectionWithMarketData(
|
|
|
374
549
|
}
|
|
375
550
|
|
|
376
551
|
const matchedPortfolio = collection.kind === "portfolio" ? collection.portfolio : undefined;
|
|
552
|
+
if (!matchedPortfolio && basis === "regular-close") {
|
|
553
|
+
ctx.fail(`--close values a portfolio; "${collection.name}" is a watchlist.`, "Usage: gloomberb portfolio show <portfolio> --close");
|
|
554
|
+
}
|
|
377
555
|
const isPortfolio = !!matchedPortfolio;
|
|
378
556
|
const id = collection.id;
|
|
379
557
|
const displayName = collection.name;
|
|
380
|
-
const structured =
|
|
558
|
+
const structured = ctx.cliOptions?.format != null && ctx.cliOptions.format !== "text";
|
|
381
559
|
const filtered = tickers.filter((ticker) =>
|
|
382
560
|
isPortfolio ? ticker.metadata.portfolios.includes(id) : ticker.metadata.watchlists.includes(id)
|
|
383
561
|
);
|
|
@@ -390,25 +568,32 @@ async function showCollectionWithMarketData(
|
|
|
390
568
|
if (filtered.length === 0 && !structured && !hasCash) {
|
|
391
569
|
console.log(cliStyles.bold(displayName));
|
|
392
570
|
console.log(cliStyles.muted("No tickers in this collection."));
|
|
571
|
+
if (explain) console.log(`\n${renderReportGlossary(EXPLAINED_REPORT, [])}`);
|
|
393
572
|
return;
|
|
394
573
|
}
|
|
574
|
+
// The terms the report shows, which --explain follows it with.
|
|
575
|
+
let explained: GlossaryEntry[] = [];
|
|
395
576
|
|
|
396
577
|
const quotes = await loadCollectionQuotes(filtered, collection, dataProvider);
|
|
397
578
|
// Worst of across the holdings' quotes, as the PORT and watchlist reports state it.
|
|
398
579
|
const broker = !!(matchedPortfolio?.brokerId || matchedPortfolio?.brokerInstanceId);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
});
|
|
580
|
+
const source = !isPortfolio ? "Local watchlist and Gloom Cloud"
|
|
581
|
+
: broker ? "Your broker account and Gloom Cloud" : "Local portfolio and Gloom Cloud";
|
|
582
|
+
let freshness = isPortfolio ? undefined : quotesFreshness(quotes.values(), { source });
|
|
403
583
|
|
|
404
584
|
if (!structured) {
|
|
405
585
|
console.log(cliStyles.bold(displayName + (isPortfolio ? ` (${currency})` : "")));
|
|
406
586
|
console.log(cliStyles.muted(`${filtered.length} ticker${filtered.length === 1 ? "" : "s"}`));
|
|
587
|
+
const imported = matchedPortfolio ? importedFromText(matchedPortfolio) : null;
|
|
588
|
+
if (imported) console.log(cliStyles.muted(imported));
|
|
589
|
+
if (isPortfolio) console.log(cliStyles.muted(valuationBasisLine(basis)));
|
|
407
590
|
console.log("");
|
|
408
591
|
}
|
|
409
592
|
|
|
410
593
|
if (matchedPortfolio) {
|
|
411
|
-
const valuation = await valuePortfolioPositions({ tickers: filtered, quotes, portfolioId: id, currency, baseCurrency: config.baseCurrency, toBase });
|
|
594
|
+
const valuation = await valuePortfolioPositions({ tickers: filtered, quotes, portfolioId: id, currency, baseCurrency: config.baseCurrency, toBase, basis });
|
|
595
|
+
// Exports carry no header line, so their source line names the basis.
|
|
596
|
+
freshness = portfolioValuationFreshness(valuation, source, { note: structured });
|
|
412
597
|
const {
|
|
413
598
|
positions: valued,
|
|
414
599
|
totalPnl,
|
|
@@ -420,6 +605,9 @@ async function showCollectionWithMarketData(
|
|
|
420
605
|
const { allocation, cash } = await valuePortfolioAllocation({ valuation, portfolio: matchedPortfolio, account, toBase });
|
|
421
606
|
const allocationBySymbol = new Map(allocation.rows.map((row) => [row.symbol, row]));
|
|
422
607
|
const showTargets = allocation.targetSum != null;
|
|
608
|
+
// Never dropped on a narrow terminal, so --explain reads the terms off them.
|
|
609
|
+
const allocationHeaders = showTargets ? ["Weight", "Target", "Drift", "Trade", "Trade Value"] : ["Weight"];
|
|
610
|
+
explained = reportGlossary("PF", allocationHeaders.join(" "));
|
|
423
611
|
// A ticker held in several lots carries its allocation on its first row.
|
|
424
612
|
const firstRows = new Set<string>();
|
|
425
613
|
const positionsExport = valued.map((entry): Record<string, unknown> & {
|
|
@@ -436,9 +624,12 @@ async function showCollectionWithMarketData(
|
|
|
436
624
|
drift: figures?.drift ?? null,
|
|
437
625
|
tradeShares: figures?.tradeUnits ?? null,
|
|
438
626
|
tradeValue: figures?.tradeValue ?? null,
|
|
627
|
+
...entry.valuation,
|
|
439
628
|
};
|
|
440
629
|
});
|
|
441
630
|
const totalPnlValue = unavailablePnl.size > 0 ? null : totalPnl;
|
|
631
|
+
// At the close, the value is Last times the shares, so the day's move from the close before is said beside it.
|
|
632
|
+
const dayMove = basis === "regular-close" ? portfolioDayMove(valuation) : null;
|
|
442
633
|
const cashExport = cash && allocation.cash ? {
|
|
443
634
|
...cash,
|
|
444
635
|
weight: allocation.cash.weight,
|
|
@@ -461,17 +652,21 @@ async function showCollectionWithMarketData(
|
|
|
461
652
|
...(cashExport ? [{ symbol: CASH_SYMBOL, marketValue: cashExport.value, baseCurrency: currency, weight: cashExport.weight,
|
|
462
653
|
targetWeight: cashExport.targetWeight, drift: cashExport.drift, tradeShares: null, tradeValue: cashExport.tradeValue }] : []),
|
|
463
654
|
{ symbol: "TOTAL", marketValue: allocation.total, unrealizedPnl: totalPnlValue, baseCurrency: currency,
|
|
464
|
-
weight: allocation.total != null && allocation.total > 0 ? 100 : null, targetWeight: allocation.targetSum },
|
|
655
|
+
weight: allocation.total != null && allocation.total > 0 ? 100 : null, targetWeight: allocation.targetSum, valuationBasis: basis },
|
|
465
656
|
];
|
|
466
657
|
ctx.printResult({ data: positionsExport, metadata: {
|
|
467
658
|
portfolioId: id, portfolioName: displayName, baseCurrency: currency, currency,
|
|
659
|
+
valuationBasis: basis,
|
|
468
660
|
total: allocation.total, cash: cashExport, targetSum: allocation.targetSum,
|
|
661
|
+
...(basis === "regular-close" ? { dayChange: dayMove?.change ?? null, dayChangePercent: dayMove?.changePercent ?? null } : {}),
|
|
469
662
|
unpricedCount: allocation.unpriced.length, unpricedSymbols: allocation.unpriced,
|
|
470
663
|
totalUnrealizedPnl: totalPnlValue,
|
|
471
664
|
complete: unavailablePnl.size === 0 && unavailableCost.size === 0 && unavailableMarketValue.size === 0,
|
|
472
665
|
unavailableSymbols: [...new Set([...unavailablePnl, ...unavailableCost, ...unavailableMarketValue])],
|
|
473
666
|
unavailableCostSymbols: [...unavailableCost], brokerPnlSymbols: [...brokerPnlSymbols],
|
|
474
667
|
accountingBasis, ...(manualAccounting ? { manualAccounting } : {}),
|
|
668
|
+
...(matchedPortfolio.importedFrom ? { importedFrom: matchedPortfolio.importedFrom } : {}),
|
|
669
|
+
...(explain ? { glossary: glossaryExport(explained) } : {}),
|
|
475
670
|
}, freshness }, { rows: (data) => [...(data as Record<string, unknown>[]), ...extraRows] });
|
|
476
671
|
return;
|
|
477
672
|
}
|
|
@@ -497,9 +692,11 @@ async function showCollectionWithMarketData(
|
|
|
497
692
|
};
|
|
498
693
|
|
|
499
694
|
const extendedSessions = extendedSessionsOf(valued.map(({ extended }) => extended?.session));
|
|
500
|
-
const
|
|
695
|
+
const now = Date.now();
|
|
696
|
+
const rows: string[][] = valued.map(({ ticker, quote, headline, extended, position, metrics, pnl, row, valuation: priced }, index) => {
|
|
697
|
+
const asOf = valuationTimeText(priced.valuationAsOf, now);
|
|
501
698
|
const priceText = quote && headline
|
|
502
|
-
? colorBySign(
|
|
699
|
+
? colorBySign(formatListPrice(headline.price, quote, ticker.metadata.assetCategory), headline.change)
|
|
503
700
|
: "—";
|
|
504
701
|
const changeText = headline ? colorBySign(formatPercentRaw(headline.changePercent), headline.change) : "—";
|
|
505
702
|
const quoteCells = [priceText, changeText, ...extendedCells(extendedSessions, extended, quote, ticker.metadata.assetCategory)];
|
|
@@ -509,26 +706,27 @@ async function showCollectionWithMarketData(
|
|
|
509
706
|
: undefined;
|
|
510
707
|
const symbolUnits = allocationBySymbol.get(ticker.metadata.ticker)?.units ?? 0;
|
|
511
708
|
if (!position || !metrics) {
|
|
512
|
-
return [ticker
|
|
513
|
-
...allocationCells(figures, { unpriced: false, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }), "—"];
|
|
709
|
+
return [listingLabel(ticker), ...quoteCells, "—", "—", "—",
|
|
710
|
+
...allocationCells(figures, { unpriced: false, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }), "—", asOf];
|
|
514
711
|
}
|
|
515
712
|
const positionCurrency = metrics.positionCurrency;
|
|
516
713
|
const marketValue = row.marketValue as number | null;
|
|
517
714
|
const unpriced = marketValue == null;
|
|
518
715
|
return [
|
|
519
|
-
ticker
|
|
716
|
+
listingLabel(ticker),
|
|
520
717
|
...quoteCells,
|
|
521
718
|
formatMarketQuantity(metrics.totalShares, { assetCategory: ticker.metadata.assetCategory, multiplier: position.multiplier, priceBasis: metrics.priceBasis, quantityCurrency: positionCurrency }),
|
|
522
719
|
formatMarketCostWithCurrency(position.avgCost, positionCurrency, { assetCategory: ticker.metadata.assetCategory, multiplier: position.multiplier, priceBasis: metrics.priceBasis }),
|
|
523
720
|
unpriced ? NOT_AVAILABLE : wholeMoney(marketValue),
|
|
524
721
|
...allocationCells(figures, { unpriced, units: symbolUnits, assetCategory: ticker.metadata.assetCategory }),
|
|
525
722
|
pnl == null || !Number.isFinite(pnl) ? "—" : colorBySign(money(pnl, { signed: true }), pnl),
|
|
723
|
+
asOf,
|
|
526
724
|
];
|
|
527
725
|
});
|
|
528
726
|
if (cashExport) {
|
|
529
727
|
const label = cashExport.currency && cashExport.currency !== currency ? `${CASH_SYMBOL} ${cashExport.currency}` : CASH_SYMBOL;
|
|
530
728
|
rows.push([label, "", "", ...extendedSessions.map(() => ""), "", "", cashExport.value == null ? NOT_AVAILABLE : wholeMoney(cashExport.value),
|
|
531
|
-
...allocationCells(cashExport, { unpriced: cashExport.value == null }), ""]);
|
|
729
|
+
...allocationCells(cashExport, { unpriced: cashExport.value == null }), "", ""]);
|
|
532
730
|
}
|
|
533
731
|
const total = allocation.total;
|
|
534
732
|
rows.push([
|
|
@@ -537,6 +735,7 @@ async function showCollectionWithMarketData(
|
|
|
537
735
|
total != null && total > 0 ? formatAllocationWeight(100) : "—",
|
|
538
736
|
...(showTargets ? [formatAllocationWeight(allocation.targetSum), "", "", ""] : []),
|
|
539
737
|
totalPnlValue == null ? "—" : colorBySign(money(totalPnlValue, { signed: true }), totalPnlValue),
|
|
738
|
+
"",
|
|
540
739
|
]);
|
|
541
740
|
|
|
542
741
|
const quantityHeader = filtered.some(ticker => ticker.metadata.assetCategory?.toUpperCase() === "BOND") || positionsExport.some(row => row.quantityUnit === "face") ? "Qty" : "Shares";
|
|
@@ -551,24 +750,22 @@ async function showCollectionWithMarketData(
|
|
|
551
750
|
{ header: quantityHeader, align: "right" },
|
|
552
751
|
{ header: "Avg Cost", align: "right", optional: true, dropPriority: 3 },
|
|
553
752
|
{ header: "Value", align: "right" },
|
|
554
|
-
{ header
|
|
555
|
-
...(showTargets
|
|
556
|
-
? [
|
|
557
|
-
{ header: "Target", align: "right" as const },
|
|
558
|
-
{ header: "Drift", align: "right" as const },
|
|
559
|
-
{ header: "Trade", align: "right" as const },
|
|
560
|
-
{ header: "Trade Value", align: "right" as const },
|
|
561
|
-
]
|
|
562
|
-
: []),
|
|
753
|
+
...allocationHeaders.map((header) => ({ header, align: "right" as const })),
|
|
563
754
|
{ header: "P&L", align: "right", optional: true },
|
|
755
|
+
// When each row's price is from, so a Friday close never hides behind a newer row.
|
|
756
|
+
{ header: "As of (UTC)" },
|
|
564
757
|
],
|
|
565
758
|
rows,
|
|
566
759
|
));
|
|
567
760
|
console.log("");
|
|
761
|
+
if (basis === "regular-close") console.log(dayMoveLine(dayMove, currency, colorBySign));
|
|
568
762
|
const notes: string[] = [];
|
|
763
|
+
// The notes name the rows as the table does.
|
|
764
|
+
const labels = new Map(filtered.map((ticker) => [ticker.metadata.ticker, listingLabel(ticker)]));
|
|
765
|
+
const named = (symbols: Iterable<string>) => [...symbols].map((symbol) => labels.get(symbol) ?? symbol).join(", ");
|
|
569
766
|
if (allocation.unpriced.length > 0) {
|
|
570
767
|
const count = allocation.unpriced.length;
|
|
571
|
-
notes.push(cliStyles.warning(`Total and weights exclude ${count} unpriced holding${count === 1 ? "" : "s"}: ${allocation.unpriced
|
|
768
|
+
notes.push(cliStyles.warning(`Total and weights exclude ${count} unpriced holding${count === 1 ? "" : "s"}: ${named(allocation.unpriced)}.`));
|
|
572
769
|
}
|
|
573
770
|
if (cashExport?.source === "broker") notes.push(cliStyles.muted("Cash is the broker account's balance as of its last sync."));
|
|
574
771
|
if (matchedPortfolio.brokerInstanceId && !account) notes.push(cliStyles.warning(BROKER_ACCOUNT_MISSING_NOTE));
|
|
@@ -579,34 +776,38 @@ async function showCollectionWithMarketData(
|
|
|
579
776
|
}
|
|
580
777
|
const targetNote = describeTargetSum(allocation.targetSum);
|
|
581
778
|
if (targetNote) notes.push(cliStyles.muted(targetNote));
|
|
582
|
-
if (brokerPnlSymbols.size > 0) notes.push(cliStyles.muted(`P&L for ${
|
|
583
|
-
if (unavailableCost.size > 0) notes.push(cliStyles.muted(`Cost unavailable for ${
|
|
584
|
-
if (unavailablePnl.size > 0) notes.push(cliStyles.muted(`P&L unavailable for ${
|
|
779
|
+
if (brokerPnlSymbols.size > 0) notes.push(cliStyles.muted(`P&L for ${named(brokerPnlSymbols)} is the broker's last snapshot.`));
|
|
780
|
+
if (unavailableCost.size > 0) notes.push(cliStyles.muted(`Cost unavailable for ${named(unavailableCost)}.`));
|
|
781
|
+
if (unavailablePnl.size > 0) notes.push(cliStyles.muted(`P&L unavailable for ${named(unavailablePnl)}.`));
|
|
585
782
|
for (const note of notes) console.log(note);
|
|
586
783
|
} else {
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
784
|
+
const watched = await valueWatchlistRows({ tickers: filtered, quotes, dataProvider, currency, toBase });
|
|
785
|
+
if (structured) {
|
|
786
|
+
ctx.printResult({
|
|
787
|
+
data: watched.map((entry) => entry.row),
|
|
788
|
+
metadata: { watchlistId: id, watchlistName: displayName, baseCurrency: currency, ...(explain ? { glossary: [] } : {}) },
|
|
789
|
+
freshness,
|
|
790
|
+
});
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const extendedSessions = extendedSessionsOf(watched.map(({ extended }) => extended?.session));
|
|
794
|
+
// Funding is the one column a list earns by holding perpetual markets; any other list's table is unchanged.
|
|
795
|
+
const showFunding = watched.some(({ funding }) => funding !== undefined);
|
|
796
|
+
const rows = watched.map(({ ticker, quote, headline, extended, venue, marketCap, funding }) => {
|
|
593
797
|
const priceText = quote && headline
|
|
594
|
-
? colorBySign(
|
|
798
|
+
? colorBySign(formatListPrice(headline.price, quote, ticker.metadata.assetCategory), headline.change)
|
|
595
799
|
: "—";
|
|
596
800
|
const changeText = headline ? colorBySign(formatPercentRaw(headline.changePercent), headline.change) : "—";
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
rows.push([
|
|
602
|
-
ticker.metadata.ticker,
|
|
603
|
-
exchangeShortName(quote?.exchangeName, quote?.fullExchangeName) || ticker.metadata.exchange || "—",
|
|
801
|
+
return [
|
|
802
|
+
listingLabel(ticker),
|
|
803
|
+
venue ?? "—",
|
|
604
804
|
priceText,
|
|
605
805
|
changeText,
|
|
606
806
|
...extendedCells(extendedSessions, extended, quote, ticker.metadata.assetCategory),
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
807
|
+
...showFunding ? [funding == null ? "—" : colorBySign(formatPerpetualRate(funding, 4), funding)] : [],
|
|
808
|
+
marketCap == null ? "—" : `${formatCompact(marketCap)} ${currency}`,
|
|
809
|
+
];
|
|
810
|
+
});
|
|
610
811
|
|
|
611
812
|
console.log(renderTable(
|
|
612
813
|
[
|
|
@@ -615,11 +816,176 @@ async function showCollectionWithMarketData(
|
|
|
615
816
|
{ header: "Last", align: "right" },
|
|
616
817
|
{ header: "Chg", align: "right" },
|
|
617
818
|
...extendedSessions.map((session) => ({ header: EXTENDED_SESSION_LABELS[session], align: "right" as const, optional: true, dropPriority: 1 })),
|
|
819
|
+
// As the TUI's FUND 8H: per eight hours whatever interval the venue pays on.
|
|
820
|
+
...showFunding ? [{ header: "Fund 8H", align: "right" as const, optional: true, dropPriority: 1 }] : [],
|
|
618
821
|
// A narrow terminal drops these first.
|
|
619
822
|
{ header: "Mkt Cap", align: "right", optional: true, dropPriority: 2 },
|
|
620
823
|
],
|
|
621
824
|
rows,
|
|
622
825
|
));
|
|
826
|
+
const notes = marketCapNotes(watched);
|
|
827
|
+
if (notes.length > 0) console.log(["", ...notes.map((note) => cliStyles.muted(note))].join("\n"));
|
|
623
828
|
}
|
|
624
829
|
if (freshness) console.log(`\n${cliFreshnessFooter(freshness)}`);
|
|
830
|
+
if (explain) console.log(`\n${renderReportGlossary(EXPLAINED_REPORT, explained)}`);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/** One watchlist row: the quote as the table shows it, the market cap in the base currency, and the `--json` and `--csv` row. */
|
|
834
|
+
interface WatchlistRowValue {
|
|
835
|
+
ticker: TickerRecord;
|
|
836
|
+
quote: CollectionQuote | undefined;
|
|
837
|
+
headline: ActiveQuoteDisplay | null;
|
|
838
|
+
extended: ExtendedSessionDisplay | null;
|
|
839
|
+
kind: TickerInstrumentKind;
|
|
840
|
+
/** The Exchange column: the quote's venue, else the saved one. */
|
|
841
|
+
venue: string | null;
|
|
842
|
+
marketCap: number | null;
|
|
843
|
+
/** A perpetual market's funding per eight hours, as a fraction; null when its market cannot be read, undefined for any other row. */
|
|
844
|
+
funding: number | null | undefined;
|
|
845
|
+
row: Record<string, unknown>;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** Financials requested at once, the most the app asks for at a time. */
|
|
849
|
+
const FINANCIALS_BATCH_SIZE = 64;
|
|
850
|
+
/** Financials asked for one at a time, at most this many at once, from a source without batches. */
|
|
851
|
+
const FINANCIALS_CONCURRENCY = 8;
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* The financials of the listings whose quote carries no market cap, through
|
|
855
|
+
* the same batched request the watchlist pane makes. A lightweight quote, as
|
|
856
|
+
* US listings are served, leaves the capitalization to the fundamentals.
|
|
857
|
+
*/
|
|
858
|
+
async function loadMarketCapFinancials(
|
|
859
|
+
tickers: readonly TickerRecord[],
|
|
860
|
+
quotes: ReadonlyMap<string, CollectionQuote>,
|
|
861
|
+
dataProvider: Pick<DataProvider, "getTickerFinancials" | "getTickerFinancialsBatch">,
|
|
862
|
+
): Promise<Map<string, TickerFinancials>> {
|
|
863
|
+
const needed = tickers.filter((ticker) => !selectMarketCapitalization(quotes.get(ticker.metadata.ticker), undefined));
|
|
864
|
+
const financials = new Map<string, TickerFinancials>();
|
|
865
|
+
if (needed.length === 0) return financials;
|
|
866
|
+
const targets = needed.map((ticker) => ({ symbol: ticker.metadata.ticker, exchange: ticker.metadata.exchange }));
|
|
867
|
+
if (dataProvider.getTickerFinancialsBatch) {
|
|
868
|
+
await Promise.all(chunked(targets, FINANCIALS_BATCH_SIZE).map(async (batch) => {
|
|
869
|
+
const results = await dataProvider.getTickerFinancialsBatch!(batch).catch(() => []);
|
|
870
|
+
batch.forEach((target, index) => {
|
|
871
|
+
const value = (results.find((result) => result.target === target) ?? results[index])?.financials;
|
|
872
|
+
if (value) financials.set(target.symbol, value);
|
|
873
|
+
});
|
|
874
|
+
}));
|
|
875
|
+
return financials;
|
|
876
|
+
}
|
|
877
|
+
await mapWithConcurrency(targets, FINANCIALS_CONCURRENCY, async (target) => {
|
|
878
|
+
const value = await dataProvider.getTickerFinancials(target.symbol, target.exchange).catch(() => null);
|
|
879
|
+
if (value) financials.set(target.symbol, value);
|
|
880
|
+
});
|
|
881
|
+
return financials;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/** Perpetual markets read at once, the most the app asks for at a time. */
|
|
885
|
+
const PERPETUAL_CONCURRENCY = 8;
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* The funding per eight hours of each perpetual market in a list, by ticker, as `quote` reads it: null for a market
|
|
889
|
+
* that cannot be read. A list without perpetual markets reads nothing.
|
|
890
|
+
*/
|
|
891
|
+
async function loadPerpetualFunding(tickers: readonly TickerRecord[]): Promise<Map<string, number | null>> {
|
|
892
|
+
const markets = tickers.flatMap((ticker) => {
|
|
893
|
+
const listing = perpetualListing(ticker.metadata.ticker, ticker.metadata.exchange);
|
|
894
|
+
return listing ? [{ symbol: ticker.metadata.ticker, listing }] : [];
|
|
895
|
+
});
|
|
896
|
+
const funding = new Map<string, number | null>();
|
|
897
|
+
await mapWithConcurrency(markets, PERPETUAL_CONCURRENCY, async ({ symbol, listing }) => {
|
|
898
|
+
funding.set(symbol, await loadPerpetualMarket(listing).then((market) => market?.row.fundingRate8h ?? null, () => null));
|
|
899
|
+
});
|
|
900
|
+
return funding;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** Watchlist rows as `watchlist show` prints them, with the market cap the watchlist pane shows. */
|
|
904
|
+
async function valueWatchlistRows({
|
|
905
|
+
tickers,
|
|
906
|
+
quotes,
|
|
907
|
+
dataProvider,
|
|
908
|
+
currency,
|
|
909
|
+
toBase,
|
|
910
|
+
}: {
|
|
911
|
+
tickers: readonly TickerRecord[];
|
|
912
|
+
quotes: ReadonlyMap<string, CollectionQuote>;
|
|
913
|
+
dataProvider: Pick<DataProvider, "getTickerFinancials" | "getTickerFinancialsBatch">;
|
|
914
|
+
currency: string;
|
|
915
|
+
toBase: (value: number, fromCurrency: string) => Promise<number>;
|
|
916
|
+
}): Promise<WatchlistRowValue[]> {
|
|
917
|
+
const [financials, perpetualFunding] = await Promise.all([
|
|
918
|
+
loadMarketCapFinancials(tickers, quotes, dataProvider),
|
|
919
|
+
loadPerpetualFunding(tickers),
|
|
920
|
+
]);
|
|
921
|
+
const known = (value: number | null | undefined): number | null => value != null && Number.isFinite(value) ? value : null;
|
|
922
|
+
// The export carries funding on the table's terms: only for a list that holds perpetual markets.
|
|
923
|
+
const hasPerpetuals = perpetualFunding.size > 0;
|
|
924
|
+
const values: WatchlistRowValue[] = [];
|
|
925
|
+
for (const ticker of tickers) {
|
|
926
|
+
const symbol = ticker.metadata.ticker;
|
|
927
|
+
const quote = quotes.get(symbol);
|
|
928
|
+
const snapshot = financials.get(symbol);
|
|
929
|
+
const { headline, extended } = collectionQuoteHeadline(quote);
|
|
930
|
+
const cap = liveMarketCapitalization(quote, snapshot?.fundamentals) ?? liveMarketCapitalization(snapshot?.quote, snapshot?.fundamentals);
|
|
931
|
+
const marketCap = cap ? listedMarketCapitalization(known(await toBase(cap.value, cap.currency))) : null;
|
|
932
|
+
const kind = resolveTickerInstrumentKind(ticker, { quote: quote ?? snapshot?.quote });
|
|
933
|
+
const venue = exchangeShortName(quote?.tradingExchangeName || quote?.exchangeName, quote?.fullExchangeName) || ticker.metadata.exchange || null;
|
|
934
|
+
const funding = perpetualFunding.get(symbol);
|
|
935
|
+
values.push({
|
|
936
|
+
ticker, quote, headline, extended, kind, venue, marketCap, funding,
|
|
937
|
+
row: {
|
|
938
|
+
symbol,
|
|
939
|
+
...listingColumns(ticker, quote),
|
|
940
|
+
exchange: venue,
|
|
941
|
+
kind,
|
|
942
|
+
currency: quote?.currency || ticker.metadata.currency || null,
|
|
943
|
+
price: known(headline?.price),
|
|
944
|
+
change: known(headline?.change),
|
|
945
|
+
changePercent: known(headline?.changePercent),
|
|
946
|
+
...extendedQuoteFields(extended),
|
|
947
|
+
volume: known(quote?.volume),
|
|
948
|
+
...hasPerpetuals ? { fundingRate8h: known(funding) } : {},
|
|
949
|
+
marketCap,
|
|
950
|
+
marketCapCurrency: currency,
|
|
951
|
+
quoteAsOf: known(quote?.lastUpdated),
|
|
952
|
+
},
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
return values;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Kinds of listing that have no market cap at all, as a note names them. */
|
|
959
|
+
const NO_MARKET_CAP: Partial<Record<TickerInstrumentKind, string>> = {
|
|
960
|
+
fund: "funds",
|
|
961
|
+
index: "indices",
|
|
962
|
+
currency: "currencies",
|
|
963
|
+
future: "futures",
|
|
964
|
+
perpetual: "perpetuals",
|
|
965
|
+
prediction: "prediction markets",
|
|
966
|
+
option: "options",
|
|
967
|
+
bond: "bonds",
|
|
968
|
+
};
|
|
969
|
+
const NOTE_NAMES = 10;
|
|
970
|
+
|
|
971
|
+
function namedList(labels: readonly string[]): string {
|
|
972
|
+
const more = labels.length - NOTE_NAMES;
|
|
973
|
+
return `${labels.slice(0, NOTE_NAMES).join(", ")}${more > 0 ? ` and ${more} more` : ""}`;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/** Why a Mkt Cap cell is blank: funds and indices have none; a company or coin whose figure did not come is unavailable. */
|
|
977
|
+
function marketCapNotes(rows: readonly WatchlistRowValue[]): string[] {
|
|
978
|
+
const blank = rows.filter((row) => row.marketCap == null);
|
|
979
|
+
const notes: string[] = [];
|
|
980
|
+
const byKind = new Map<string, string[]>();
|
|
981
|
+
for (const row of blank) {
|
|
982
|
+
const group = NO_MARKET_CAP[row.kind] ?? "";
|
|
983
|
+
byKind.set(group, [...byKind.get(group) ?? [], listingLabel(row.ticker)]);
|
|
984
|
+
}
|
|
985
|
+
for (const [group, labels] of byKind) {
|
|
986
|
+
if (group) notes.push(`No market cap for ${group}: ${namedList(labels)}.`);
|
|
987
|
+
}
|
|
988
|
+
const unavailable = byKind.get("");
|
|
989
|
+
if (unavailable?.length) notes.push(`Market cap unavailable for ${namedList(unavailable)}.`);
|
|
990
|
+
return notes;
|
|
625
991
|
}
|