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
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
Quote,
|
|
11
11
|
TickerFinancials,
|
|
12
12
|
} from "../../types/financials";
|
|
13
|
-
import {
|
|
13
|
+
import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, quoteFormatOptions } from "../../market-data/market/format";
|
|
14
14
|
import {
|
|
15
15
|
EXTENDED_SESSION_LABELS,
|
|
16
16
|
getExtendedSessionDisplay,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from "../../market-data/market/status";
|
|
22
22
|
import { formatCompact, formatDistributionAmount, formatPercent, formatPercentRaw } from "../../utils/format";
|
|
23
23
|
import { withCliServices, withMarketData } from "../context";
|
|
24
|
-
import { isoDate, parsePositiveInt,
|
|
24
|
+
import { isoDate, parsePositiveInt, requireOneArg, takeFlag, takeOption } from "./command-utils";
|
|
25
25
|
import type { BuiltinCliCommandDef } from "../command-options";
|
|
26
26
|
import { CLI_COMMAND_GROUPS, TABLE_SECTION_OPTION } from "../help";
|
|
27
27
|
import {
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
formatFractionPercentCell,
|
|
31
31
|
formatPriceRange,
|
|
32
32
|
} from "../helpers";
|
|
33
|
-
import { cliStyles, renderStats } from "../../utils/cli-output";
|
|
33
|
+
import { cliStyles, colorBySign, renderSection, renderStats } from "../../utils/cli-output";
|
|
34
34
|
import { formatPerShareNumber } from "../../utils/reported-money";
|
|
35
35
|
import {
|
|
36
36
|
analystTargetCurrency,
|
|
@@ -43,7 +43,9 @@ import {
|
|
|
43
43
|
import { getPublishedUsEquityCalendarYears, getPublishedUsEquitySession } from "../../market-data/published-us-sessions";
|
|
44
44
|
import { isQuoteStaleForCurrentSession } from "../../market-data/quotes/freshness";
|
|
45
45
|
import { isOptionsUnavailableError, listedOptionsAlternative, optionsUnavailableTitle } from "../../market-data/options-alternatives";
|
|
46
|
-
import { fundamentalsReportTables, renderFundamentalsReport } from "./ticker";
|
|
46
|
+
import { cliFuturesContract, futuresContractMetrics, fundamentalsReportTables, renderFundamentalsReport } from "./ticker";
|
|
47
|
+
import type { FuturesContractTermsPayload } from "../../api-client/futures-curve";
|
|
48
|
+
import { futuresSettleText, isFuturesInstrument } from "../../market-data/futures-contract";
|
|
47
49
|
import {
|
|
48
50
|
chainHasExpiry,
|
|
49
51
|
chainWithModelFigures,
|
|
@@ -67,20 +69,27 @@ import {
|
|
|
67
69
|
type CliReportTables,
|
|
68
70
|
} from "../report-tables";
|
|
69
71
|
import {
|
|
72
|
+
historyBarStatusNote,
|
|
70
73
|
historyFacts,
|
|
71
74
|
historyFlagNote,
|
|
75
|
+
historyGapNote,
|
|
72
76
|
historyAllRangeYears,
|
|
73
77
|
historyIntervalsByRange,
|
|
74
78
|
historyNotes,
|
|
75
79
|
historyPriceDecimals,
|
|
76
80
|
historyRows,
|
|
77
81
|
historyUnit,
|
|
82
|
+
historyVolume,
|
|
83
|
+
historyVolumeText,
|
|
78
84
|
} from "../history-rows";
|
|
79
85
|
import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
|
|
86
|
+
import { bareFuturesCode } from "../../market-data/futures-board";
|
|
87
|
+
import { newsEmptyMessage } from "./news-hints";
|
|
88
|
+
import { newsFallbackText, newsSearchHeading, newsSearchText, newsSearchTextProblem, searchNewsStories } from "./news-search";
|
|
80
89
|
import { formatUtcTime } from "../../utils/utc-time";
|
|
81
90
|
import { isFiniteNumber } from "../../utils/guards";
|
|
82
91
|
import { exportedFundamentals } from "../../utils/price-earnings";
|
|
83
|
-
import { fundamentalsFreshness, quotesFreshness, rowsFreshness } from "../freshness";
|
|
92
|
+
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness } from "../freshness";
|
|
84
93
|
import {
|
|
85
94
|
barHistoryFreshness,
|
|
86
95
|
barResolutionFromDates,
|
|
@@ -91,33 +100,50 @@ import {
|
|
|
91
100
|
} from "../../plugins/builtin/shared/report-freshness";
|
|
92
101
|
import {
|
|
93
102
|
bareListingNote,
|
|
103
|
+
tradingBoardNote,
|
|
94
104
|
EXCHANGE_OPTION,
|
|
105
|
+
futuresTryLine,
|
|
95
106
|
isNoProviderError,
|
|
96
107
|
listingHeading,
|
|
97
108
|
listingIdentity,
|
|
98
109
|
listingTitle,
|
|
99
110
|
listingVenues,
|
|
111
|
+
listingChoiceNotes,
|
|
112
|
+
listingReadNotes,
|
|
113
|
+
loadBareVenues,
|
|
114
|
+
type ListingVenue,
|
|
100
115
|
loadForListing,
|
|
101
116
|
loadListingQuote,
|
|
117
|
+
listingFateLoader,
|
|
118
|
+
listingFateNotes,
|
|
119
|
+
noLongerTradingNote,
|
|
102
120
|
notTradedMessage,
|
|
103
121
|
quoteBareListing,
|
|
104
122
|
requireCliListing,
|
|
123
|
+
tradingListing,
|
|
105
124
|
type CliListing,
|
|
106
125
|
type ListingDataRequest,
|
|
107
126
|
type ListingIdentity,
|
|
108
127
|
} from "../listing-arg";
|
|
109
|
-
import { indexRootAlsoLine, indexRootFor, indexRootTryLine } from "../index-roots";
|
|
128
|
+
import { indexRootAlsoLine, indexRootFor, indexRootTryLine, isYieldIndexSymbol } from "../index-roots";
|
|
129
|
+
import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote } from "../earnings-calendar";
|
|
130
|
+
import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
|
|
110
131
|
import { providerMissReason } from "../../sources/provider-errors";
|
|
111
132
|
import { isNotATickerMessage } from "../not-a-ticker";
|
|
133
|
+
import { splitSymbolArgs } from "../symbol-args";
|
|
134
|
+
import { cliPerpetualContract, perpetualContractStats, type PerpetualContractPayload } from "../perpetual-contract";
|
|
135
|
+
import { subMinuteDataMessage } from "../known-gaps";
|
|
112
136
|
import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
|
|
113
137
|
import { isCryptoPairSymbol } from "../../utils/crypto-pair";
|
|
114
138
|
import { isUsListingExchange } from "../../utils/exchanges";
|
|
115
|
-
import {
|
|
139
|
+
import { describeTickerChange, startTickerChangeLookup, tickerChangeLine, type TickerChange } from "../../tickers/ticker-changes";
|
|
140
|
+
import { noSecReportsNotice, nonUsSecListingVenue } from "../../utils/sec";
|
|
116
141
|
import type { MarketContext } from "../types";
|
|
117
142
|
import { windowRows } from "../row-window";
|
|
118
143
|
import { crossRate, describeFxRate, isUsableRate, parseFxRequest } from "../fx-pair";
|
|
119
144
|
import {
|
|
120
145
|
holderListFacts,
|
|
146
|
+
holderListPeriod,
|
|
121
147
|
holderShareBasisMarker,
|
|
122
148
|
holderShareBasisNote,
|
|
123
149
|
holderValueBasis,
|
|
@@ -125,15 +151,28 @@ import {
|
|
|
125
151
|
nonUsHolderCaveat,
|
|
126
152
|
sharedReportDate,
|
|
127
153
|
} from "../../plugins/builtin/holders/report-header";
|
|
128
|
-
import {
|
|
154
|
+
import { formatPercentHeld, holderStakeMarketCap, holderValueCurrency } from "../../plugins/builtin/holders/format";
|
|
155
|
+
import {
|
|
156
|
+
holderReportColumns,
|
|
157
|
+
holderReportDates,
|
|
158
|
+
holderReportNotices,
|
|
159
|
+
holderReportRows,
|
|
160
|
+
holderReportUnits,
|
|
161
|
+
unvaluedHoldersNote,
|
|
162
|
+
} from "../../plugins/builtin/holders/report-rows";
|
|
129
163
|
import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-client";
|
|
164
|
+
import { centralBankEventsMessage } from "../central-bank-events";
|
|
165
|
+
import { classSharesToday } from "../../plugins/builtin/holders/client";
|
|
130
166
|
import { filingFormMatches, SEC_FILING_FETCH_LIMIT } from "../../plugins/builtin/sec/forms";
|
|
167
|
+
import { loadParsedInsiderFilings } from "../../plugins/builtin/insider/client";
|
|
168
|
+
import { insiderEmptyNotice } from "../../plugins/builtin/insider/issuer";
|
|
131
169
|
import {
|
|
132
170
|
BENEFICIAL_REPORT_COLUMNS,
|
|
133
171
|
beneficialCoverageNotices,
|
|
134
172
|
beneficialListComplete,
|
|
135
173
|
beneficialListFacts,
|
|
136
174
|
beneficialListUnreadable,
|
|
175
|
+
beneficialReportLegend,
|
|
137
176
|
beneficialRouteForm,
|
|
138
177
|
buildBeneficialReportRows,
|
|
139
178
|
HOLDER_FORMS,
|
|
@@ -144,7 +183,17 @@ import {
|
|
|
144
183
|
const VALID_RANGES = new Set<TimeRange>(TIME_RANGES);
|
|
145
184
|
const VALID_NEWS_FEEDS = new Set<NewsFeed>(["latest", "top", "breaking", "ticker", "sector", "topic"]);
|
|
146
185
|
|
|
147
|
-
type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & {
|
|
186
|
+
type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & {
|
|
187
|
+
error: string | null;
|
|
188
|
+
/** The listing the row is, named from its saved ticker when the quote names nothing. */
|
|
189
|
+
listing?: { name: string | null };
|
|
190
|
+
/** A futures row's contract, terms and latest confirmed settlement, as `ticker` reads them. */
|
|
191
|
+
futuresContract?: FuturesContractTermsPayload;
|
|
192
|
+
/** A perpetual row's market, funding per eight hours and open interest. */
|
|
193
|
+
perpetualContract?: PerpetualContractPayload;
|
|
194
|
+
/** The symbol changed: its issuer moved to another symbol, or its holder had another before. */
|
|
195
|
+
tickerChange?: TickerChange;
|
|
196
|
+
};
|
|
148
197
|
type FinancialsCliData = TickerFinancials & {
|
|
149
198
|
symbol: string;
|
|
150
199
|
exchange: string;
|
|
@@ -161,11 +210,9 @@ function parseNewsFeed(value: string | undefined): NewsQuery["feed"] | undefined
|
|
|
161
210
|
return value && VALID_NEWS_FEEDS.has(value as NewsFeed) ? value as NewsQuery["feed"] : undefined;
|
|
162
211
|
}
|
|
163
212
|
|
|
213
|
+
/** The symbols of a command's arguments, one each however they were separated: spaces, commas or pasted lines. */
|
|
164
214
|
function normalizeSymbols(args: string[]): string[] {
|
|
165
|
-
return args
|
|
166
|
-
.flatMap((arg) => arg.split(","))
|
|
167
|
-
.map((symbol) => symbol.trim().toUpperCase())
|
|
168
|
-
.filter(Boolean);
|
|
215
|
+
return splitSymbolArgs(args).map((symbol) => symbol.toUpperCase());
|
|
169
216
|
}
|
|
170
217
|
|
|
171
218
|
/** What JSON metadata says about the listing a command resolved. */
|
|
@@ -186,10 +233,15 @@ const QUOTE_LEAD_COLUMNS = [
|
|
|
186
233
|
row.error && !value
|
|
187
234
|
// A typo is not an outage: say what is wrong with the symbol itself.
|
|
188
235
|
? isNotATickerMessage(row.error) ? cliStyles.warning("not a ticker") : cliStyles.danger("unavailable")
|
|
189
|
-
:
|
|
236
|
+
: yieldLevelCell(value, row)
|
|
190
237
|
),
|
|
191
238
|
},
|
|
192
|
-
{
|
|
239
|
+
{
|
|
240
|
+
key: "changePercent",
|
|
241
|
+
header: "Chg%",
|
|
242
|
+
align: "right" as const,
|
|
243
|
+
format: (value: unknown, row: QuoteRow) => row.yieldIndex ? yieldChangeCell(row) : formatChangePercentCell(value),
|
|
244
|
+
},
|
|
193
245
|
// Text drops a column no row fills, so a table without an extended print stays as narrow as before.
|
|
194
246
|
extendedColumn("PRE"),
|
|
195
247
|
extendedColumn("POST"),
|
|
@@ -198,6 +250,17 @@ const QUOTE_LEAD_COLUMNS = [
|
|
|
198
250
|
|
|
199
251
|
type QuoteRow = ReturnType<typeof quoteRows>[number];
|
|
200
252
|
|
|
253
|
+
/** A Cboe yield index level is a yield: `5.24%`, not a price. */
|
|
254
|
+
function yieldLevelCell(value: unknown, row: QuoteRow): string {
|
|
255
|
+
const text = String(value ?? "");
|
|
256
|
+
return row.yieldIndex && text ? `${text}%` : text;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** A yield index moves in basis points: `+1.3bp`, from the real previous close, not a percent of a percent. */
|
|
260
|
+
function yieldChangeCell(row: QuoteRow): string {
|
|
261
|
+
return row.changeBasisPoints == null ? "" : colorBySign(formatBasisPoints(row.changeBasisPoints / 100, 1), row.changeBasisPoints);
|
|
262
|
+
}
|
|
263
|
+
|
|
201
264
|
/** The pre-market or after-hours print and its move from the regular close, in its own column. */
|
|
202
265
|
function extendedColumn(session: ExtendedSession) {
|
|
203
266
|
return {
|
|
@@ -240,7 +303,7 @@ function quoteColumns() {
|
|
|
240
303
|
function compareColumns() {
|
|
241
304
|
return [
|
|
242
305
|
...QUOTE_LEAD_COLUMNS,
|
|
243
|
-
{ key: "previousClose", header: "Prev Close", align: "right" as const },
|
|
306
|
+
{ key: "previousClose", header: "Prev Close", align: "right" as const, format: yieldLevelCell },
|
|
244
307
|
// A narrow terminal drops these before it cuts the names short.
|
|
245
308
|
{ key: "dayRange", header: "Day Range", align: "right" as const, optional: true, dropPriority: 1 },
|
|
246
309
|
{ key: "volume", header: "Volume", align: "right" as const, format: formatCountCell, optional: true, dropPriority: 2 },
|
|
@@ -267,13 +330,14 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
|
|
|
267
330
|
// pre-market or after-hours print is its own column, measured from that session's close.
|
|
268
331
|
const display = getRegularSessionDisplay(quote);
|
|
269
332
|
const extended = getExtendedSessionDisplay(quote);
|
|
333
|
+
const yieldIndex = isYieldIndexSymbol(result.target.symbol);
|
|
270
334
|
// An index level is in points, not in the currency its members trade in.
|
|
271
335
|
const indexPoints = quote?.instrumentType?.trim().toUpperCase() === "INDEX";
|
|
272
336
|
// Pad to two decimals so a column lines up, but never past the currency's minor unit (JPY has none).
|
|
273
337
|
// Points have no minor unit, so a yen-listed index still pads to two.
|
|
274
338
|
const options = {
|
|
275
339
|
...quoteFormatOptions(quote),
|
|
276
|
-
minimumFractionDigits: indexPoints ? 2 :
|
|
340
|
+
minimumFractionDigits: indexPoints ? 2 : currencyPriceMinDigits(quote?.currency),
|
|
277
341
|
};
|
|
278
342
|
const price = (value: number | undefined) => (
|
|
279
343
|
quote && value != null
|
|
@@ -282,13 +346,16 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
|
|
|
282
346
|
);
|
|
283
347
|
return {
|
|
284
348
|
symbol: result.target.symbol,
|
|
285
|
-
name: quote?.name
|
|
349
|
+
name: quote?.name || result.listing?.name || "",
|
|
286
350
|
price: price(display?.price),
|
|
287
351
|
rawPrice: display?.price ?? null,
|
|
288
352
|
priceBasis: quote?.priceBasis ?? null,
|
|
289
353
|
instrumentType: quote?.instrumentType ?? null,
|
|
290
354
|
change: display?.change ?? null,
|
|
291
355
|
changePercent: roundedPercent(display?.changePercent),
|
|
356
|
+
// A Cboe yield index is read as a yield in percent and moves in basis points.
|
|
357
|
+
yieldIndex,
|
|
358
|
+
changeBasisPoints: yieldIndex && display?.change != null ? toBasisPoints(display.change, 1) : null,
|
|
292
359
|
extendedSession: extended?.session ?? null,
|
|
293
360
|
extendedPrice: price(extended?.price),
|
|
294
361
|
rawExtendedPrice: extended?.price ?? null,
|
|
@@ -349,22 +416,6 @@ function filingRows(filings: SecFilingItem[]) {
|
|
|
349
416
|
}));
|
|
350
417
|
}
|
|
351
418
|
|
|
352
|
-
function holderRows(data: HolderData, ownerTypes?: Set<string>) {
|
|
353
|
-
const holders = ownerTypes
|
|
354
|
-
? data.holders.filter((holder) => ownerTypes.has(holder.ownerType))
|
|
355
|
-
: data.holders;
|
|
356
|
-
return holders.map((holder) => ({
|
|
357
|
-
type: holder.ownerType,
|
|
358
|
-
name: holder.name,
|
|
359
|
-
reportDate: holder.reportDate ?? "",
|
|
360
|
-
shares: holder.shares ?? null,
|
|
361
|
-
value: holder.value ?? null,
|
|
362
|
-
percentHeld: holder.percentHeld ?? null,
|
|
363
|
-
changeShares: holder.changeShares ?? null,
|
|
364
|
-
shareBasis: holder.shareBasis ?? null,
|
|
365
|
-
}));
|
|
366
|
-
}
|
|
367
|
-
|
|
368
419
|
function analystSummary(data: AnalystResearchData): string {
|
|
369
420
|
const target = data.priceTarget;
|
|
370
421
|
const currency = analystTargetCurrency(data);
|
|
@@ -459,6 +510,8 @@ function earningsRows(events: EarningsEvent[]) {
|
|
|
459
510
|
symbol: event.symbol,
|
|
460
511
|
name: event.name,
|
|
461
512
|
date: isoDate(event.earningsDate).slice(0, 10),
|
|
513
|
+
// Whether the company announced the date or it is projected from past reports.
|
|
514
|
+
status: earningsDateLabel(event),
|
|
462
515
|
timing: event.timing,
|
|
463
516
|
epsEstimate: event.epsEstimate,
|
|
464
517
|
epsActual: event.epsActual,
|
|
@@ -467,6 +520,9 @@ function earningsRows(events: EarningsEvent[]) {
|
|
|
467
520
|
}));
|
|
468
521
|
}
|
|
469
522
|
|
|
523
|
+
/** Symbols at most whose venues a command looks up to name the company each bare one went to. */
|
|
524
|
+
const MAX_CHOICE_LOOKUPS = 10;
|
|
525
|
+
|
|
470
526
|
async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], commandName: "quote" | "compare") {
|
|
471
527
|
const args = [...rawArgs];
|
|
472
528
|
const exchange = takeOption(args, "--exchange") ?? "";
|
|
@@ -479,38 +535,112 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
|
|
|
479
535
|
symbol, exchange, market, ctx, { ownExchangeWins: symbols.length > 1 },
|
|
480
536
|
)));
|
|
481
537
|
const targets: QuoteSubscriptionTarget[] = listings.map((listing) => ({ symbol: listing.request.symbol, exchange: listing.request.exchange }));
|
|
538
|
+
// A bare symbol's venues load beside its quote, to name the company it was read as when several list it.
|
|
539
|
+
// Each costs a search, so a long list (a portfolio check, not a lookup) names none.
|
|
540
|
+
const venues = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
541
|
+
: listings.map((listing) => [listing, loadBareVenues(listing, market)] as const));
|
|
542
|
+
// So is whether a symbol changed (FB is now META), for the same lists.
|
|
543
|
+
const changes = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
544
|
+
: listings.map((listing) => [listing, startTickerChangeLookup(market.dataProvider, listing.symbol, listing.exchange)] as const));
|
|
482
545
|
const results = await market.dataProvider.getQuotesBatch(targets, { forceRefresh: ctx.cliOptions.refresh });
|
|
483
546
|
const listingOf = (result: QuoteBatchResult, index: number) => listings[targets.indexOf(result.target)] ?? listings[index]!;
|
|
547
|
+
const asked = results.map(listingOf);
|
|
484
548
|
const request = { command: commandName, noun: "quote" };
|
|
485
|
-
const answers = await Promise.all(results.map((result, index) =>
|
|
549
|
+
const answers = await Promise.all(results.map((result, index) => {
|
|
550
|
+
const listing = asked[index]!;
|
|
551
|
+
return answerQuote(result, listing, market, request, ctx.cliOptions.refresh, venues.get(listing) ?? null);
|
|
552
|
+
}));
|
|
553
|
+
const now = Date.now();
|
|
554
|
+
const tickerChanges = await Promise.all(answers.map(async ({ result }, index) => {
|
|
555
|
+
const listing = asked[index]!;
|
|
556
|
+
// A bare symbol read on a listing abroad (SXR8 -> XETRA) is not the US symbol the changes are of.
|
|
557
|
+
if (result.quote && bareListingNote(listing, result.quote)) return null;
|
|
558
|
+
return describeTickerChange(listing.symbol, await changes.get(listing)?.settle() ?? null, tradingListing(result.quote, now));
|
|
559
|
+
}));
|
|
486
560
|
const failed = answers[0]?.failure;
|
|
487
|
-
if (listings.length === 1 && failed)
|
|
561
|
+
if (listings.length === 1 && failed) {
|
|
562
|
+
ctx.fail(failed.message, [tickerChangeLine(tickerChanges[0]), failed.details].filter(Boolean).join("\n") || undefined);
|
|
563
|
+
}
|
|
564
|
+
// A futures quote adds its dated settlement and contract terms, as `ticker` prints them; `compare` stays a table.
|
|
565
|
+
const contracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
|
|
566
|
+
? cliFuturesContract(result.quote.symbol || result.target.symbol, result.quote.instrumentType)
|
|
567
|
+
: null));
|
|
568
|
+
// A perpetual adds its funding per eight hours and open interest, the figures PERP lists beside its mark.
|
|
569
|
+
const perpetualContracts = await Promise.all(answers.map(({ result }) => commandName === "quote" && result.quote
|
|
570
|
+
? cliPerpetualContract(result.quote)
|
|
571
|
+
: null));
|
|
488
572
|
// Each row names its listing by key (SAN:EPA) and company, so the table needs no line above it.
|
|
489
|
-
const data = answers.map(({ result, listing, failure }) => ({
|
|
573
|
+
const data = answers.map(({ result, listing, failure }, index) => ({
|
|
490
574
|
target: result.target,
|
|
491
575
|
listing: listingMetadata(listingIdentity(listing, result.quote)),
|
|
492
576
|
// The figures the table shows, beside the quote as the source sent it.
|
|
493
577
|
...getQuoteSessionFields(result.quote),
|
|
494
578
|
quote: result.quote,
|
|
579
|
+
...contracts[index] ? { futuresContract: contracts[index] } : {},
|
|
580
|
+
...perpetualContracts[index] ? { perpetualContract: perpetualContracts[index] } : {},
|
|
495
581
|
error: failure?.oneLine ?? errorMessage(result.error),
|
|
582
|
+
...yieldIndexFields(result),
|
|
583
|
+
...tickerChanges[index] ? { tickerChange: tickerChanges[index] } : {},
|
|
496
584
|
}));
|
|
497
585
|
const notes = [
|
|
586
|
+
// FB now lists another security; the company that was FB trades as META. A symbol a
|
|
587
|
+
// company renamed into (META) is what was typed: its change is in the row, not here.
|
|
588
|
+
...tickerChanges.flatMap((change) => tickerChangeLine(change) ?? []),
|
|
498
589
|
// Which listing a bare symbol was read as, and the index a bare root is not, are in no row.
|
|
499
590
|
...answers.flatMap((answer) => answer.notes),
|
|
591
|
+
// A row that stopped trading long ago reads as a quote that is merely stale.
|
|
592
|
+
...data.flatMap((row) => row.quote
|
|
593
|
+
? noLongerTradingNote(`${row.target.symbol}${row.listing.name ? ` (${row.listing.name})` : ""}`, row.quote, now) ?? []
|
|
594
|
+
: []),
|
|
500
595
|
// Text mode shows only "unavailable" in the cell; the JSON rows already carry each reason.
|
|
501
596
|
...ctx.cliOptions.format === "text" ? quoteNotes(data, { exchange }) : [],
|
|
502
597
|
];
|
|
598
|
+
const columns = commandName === "compare" ? compareColumns() : quoteColumns();
|
|
599
|
+
// Only yield indices: their change is in bp, so the text header drops the percent sign (CSV keeps the percent it holds).
|
|
600
|
+
const yieldsOnly = data.length > 0 && data.every((row) => isYieldIndexSymbol(row.target.symbol));
|
|
503
601
|
ctx.printResult({
|
|
504
602
|
data,
|
|
505
603
|
warnings: notes.length > 0 ? notes : undefined,
|
|
506
604
|
freshness: quotesFreshness(data.map((row) => row.quote)),
|
|
507
605
|
}, {
|
|
508
606
|
rows: quoteRows,
|
|
509
|
-
|
|
607
|
+
details: quoteFuturesContractText,
|
|
608
|
+
columns,
|
|
609
|
+
...(yieldsOnly ? { textColumns: columns.map((column) => column.key === "changePercent" ? { ...column, header: "Chg" } : column) } : {}),
|
|
510
610
|
});
|
|
511
611
|
});
|
|
512
612
|
}
|
|
513
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Under the table, each futures row's Settle line and Contract block, worded
|
|
616
|
+
* as `ticker` words them. Several symbols name theirs in the block's title.
|
|
617
|
+
*/
|
|
618
|
+
function quoteFuturesContractText(data: QuoteCliRecord[]): string {
|
|
619
|
+
return data.flatMap((row) => {
|
|
620
|
+
if (row.perpetualContract) {
|
|
621
|
+
const stats = perpetualContractStats(row.perpetualContract);
|
|
622
|
+
return [data.length > 1 ? `${renderSection(`${row.target.symbol} Contract`)}\n${renderStats(stats)}` : `${renderSection("Contract")}\n${renderStats(stats)}`];
|
|
623
|
+
}
|
|
624
|
+
const terms = row.futuresContract;
|
|
625
|
+
if (!terms || !row.quote) return [];
|
|
626
|
+
const settle: [string, string] = ["Settle", futuresSettleText(terms, row.quote)];
|
|
627
|
+
const contract = futuresContractMetrics(terms, row.quote.symbol || row.target.symbol);
|
|
628
|
+
if (data.length > 1) return [`${renderSection(`${row.target.symbol} Contract`)}\n${renderStats([settle, ...contract])}`];
|
|
629
|
+
return [[renderStats([settle]), ...contract.length ? ["", renderSection("Contract"), renderStats(contract)] : []].join("\n")];
|
|
630
|
+
}).join("\n\n");
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* What a Cboe yield index adds to its JSON row: the level is a yield in percent
|
|
635
|
+
* and its change since the previous close is in basis points (the quote's own
|
|
636
|
+
* `change` is the same move in percentage points).
|
|
637
|
+
*/
|
|
638
|
+
function yieldIndexFields(result: QuoteBatchResult): { unit?: "percent"; changeBasisPoints?: number } {
|
|
639
|
+
if (!isYieldIndexSymbol(result.target.symbol)) return {};
|
|
640
|
+
const change = getRegularSessionDisplay(result.quote)?.change;
|
|
641
|
+
return { unit: "percent", ...(change == null ? {} : { changeBasisPoints: toBasisPoints(change, 1) }) };
|
|
642
|
+
}
|
|
643
|
+
|
|
514
644
|
interface QuoteAnswer {
|
|
515
645
|
result: QuoteBatchResult;
|
|
516
646
|
/** The listing the row is: the one asked for, or the venue a bare symbol was quoted on. */
|
|
@@ -524,8 +654,10 @@ interface QuoteAnswer {
|
|
|
524
654
|
* One symbol's row of `quote` and `compare`. A listing on an exchange its symbol
|
|
525
655
|
* is not listed on says so. A bare symbol the data service read on its home
|
|
526
656
|
* listing abroad names that venue (SXR8 -> XETRA). One no source quotes is
|
|
527
|
-
* quoted on its listings (`quoteBareListing`), else names them
|
|
528
|
-
*
|
|
657
|
+
* quoted on its listings (`quoteBareListing`), else names them, or asks for an
|
|
658
|
+
* exchange when several companies list it (2222); a bare index root (VIX) also
|
|
659
|
+
* names its index (^VIX), and a futures board code a security keeps (GC) its
|
|
660
|
+
* future (GC=F). Venues are only looked up for a
|
|
529
661
|
* symbol with no quote, so a request that succeeds costs no search.
|
|
530
662
|
*/
|
|
531
663
|
async function answerQuote(
|
|
@@ -534,12 +666,20 @@ async function answerQuote(
|
|
|
534
666
|
market: MarketContext,
|
|
535
667
|
request: ListingDataRequest,
|
|
536
668
|
refresh: boolean,
|
|
669
|
+
venues: Promise<ListingVenue[]> | null,
|
|
537
670
|
): Promise<QuoteAnswer> {
|
|
538
671
|
const root = listing.exchange ? null : indexRootFor(listing.symbol);
|
|
539
672
|
// A bare root that quoted something else, such as MOVE (Corvex), points at the index too.
|
|
540
673
|
const also = (quote: Quote) => root && quote.instrumentType?.trim().toUpperCase() !== "INDEX"
|
|
541
674
|
? [indexRootAlsoLine(root, request.command)] : [];
|
|
542
|
-
if (result.quote)
|
|
675
|
+
if (result.quote) {
|
|
676
|
+
// "CAR = Avis Budget Group, NASDAQ (also XETRA, ASX)" names the venue too, so it replaces "SXR8 -> XETRA".
|
|
677
|
+
const choice = venues ? await listingChoiceNotes(listing, market, result.quote, venues) : listingReadNotes(listing);
|
|
678
|
+
const venue = choice.length > 0 ? null : bareListingNote(listing, result.quote);
|
|
679
|
+
// A row saved as 247540:KRX is quoted from KOSDAQ, the board it trades on.
|
|
680
|
+
const board = tradingBoardNote(listing, result.quote);
|
|
681
|
+
return { result, listing, notes: [...choice, venue ?? [], board ?? [], also(result.quote)].flat() };
|
|
682
|
+
}
|
|
543
683
|
const notTraded = await notTradedMessage(listing, market);
|
|
544
684
|
if (notTraded) return { result, listing, failure: { message: notTraded, oneLine: notTraded }, notes: [] };
|
|
545
685
|
if (!isNoProviderError(result.error)) return { result, listing, notes: [] };
|
|
@@ -551,14 +691,18 @@ async function answerQuote(
|
|
|
551
691
|
notes: [other.note, ...also(other.quote)],
|
|
552
692
|
};
|
|
553
693
|
}
|
|
554
|
-
|
|
555
|
-
|
|
694
|
+
// Other listings already name a futures board code's future (GC=F); with none, it is named here.
|
|
695
|
+
const tryLines = [
|
|
696
|
+
root ? indexRootTryLine(root, request.command) : null,
|
|
697
|
+
other || listing.exchange ? null : futuresTryLine(listing.symbol, request),
|
|
698
|
+
].filter((line): line is string => !!line);
|
|
699
|
+
if (!other && tryLines.length === 0) return { result, listing, notes: [] };
|
|
556
700
|
const message = other?.message ?? `${listing.symbol} has no ${request.noun}.`;
|
|
557
|
-
const details = [other?.details,
|
|
701
|
+
const details = [other?.details, ...tryLines].filter(Boolean).join("\n");
|
|
558
702
|
const oneLine = [
|
|
559
703
|
message,
|
|
560
|
-
other ?
|
|
561
|
-
|
|
704
|
+
other ? `${other.heading}: ${other.listings.join(", ")}.` : "",
|
|
705
|
+
...tryLines,
|
|
562
706
|
].filter(Boolean).join(" ");
|
|
563
707
|
return { result, listing, failure: { message, details, oneLine }, notes: [] };
|
|
564
708
|
}
|
|
@@ -573,6 +717,20 @@ const HISTORY_USAGE = "history <symbol> [--range <range>]";
|
|
|
573
717
|
const HISTORY_ALL_RANGE = `ALL gives the full history the source has, up to ${historyAllRangeYears()} years`;
|
|
574
718
|
/** Options people reach for to ask for dates, which history answers with a range. */
|
|
575
719
|
const DATE_WINDOW_OPTIONS = new Set(["--from", "--to", "--start", "--end", "--since", "--until", "--date", "--start-date", "--end-date"]);
|
|
720
|
+
/** Options people reach for to pick a bar size, which history derives from the range. */
|
|
721
|
+
const BAR_SIZE_OPTIONS = new Set(["--interval", "--resolution", "--bar", "--bars", "--bar-size", "--granularity", "--timeframe", "--frequency", "--freq", "--tick", "--ticks"]);
|
|
722
|
+
|
|
723
|
+
/** The symbol `history` was given: the first word that is not an option or the value after one. */
|
|
724
|
+
function historySymbolArg(args: readonly string[]): string | undefined {
|
|
725
|
+
return args.find((arg, index) => !arg.startsWith("-")
|
|
726
|
+
&& !(index > 0 && args[index - 1]!.startsWith("--") && !args[index - 1]!.includes("=")));
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function historyOptionHint(flag: string, args: readonly string[]): string | null {
|
|
730
|
+
if (DATE_WINDOW_OPTIONS.has(flag)) return `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`;
|
|
731
|
+
if (BAR_SIZE_OPTIONS.has(flag)) return `history serves each --range in one bar size. ${subMinuteDataMessage(historySymbolArg(args))}`;
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
576
734
|
|
|
577
735
|
async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
578
736
|
const args = [...rawArgs];
|
|
@@ -581,23 +739,31 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
581
739
|
const raw = requireOneArg(args, HISTORY_USAGE, "symbol", ctx);
|
|
582
740
|
await withMarketData(ctx, async (market) => {
|
|
583
741
|
const listing = await requireCliListing(raw, requestedExchange, market, ctx);
|
|
742
|
+
const venues = loadBareVenues(listing, market);
|
|
584
743
|
const { symbol, exchange } = listing.request;
|
|
585
744
|
const context = { cacheMode: ctx.cliOptions.refresh ? "refresh" as const : "default" as const };
|
|
586
745
|
const load = () => market.dataProvider.getPriceHistoryWithMetadata
|
|
587
746
|
? market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, range, context)
|
|
588
747
|
: market.dataProvider.getPriceHistory(symbol, exchange, range, context).then((points) => ({ points, resolution: null }));
|
|
748
|
+
const quoted = loadListingQuote(market.dataProvider, listing);
|
|
749
|
+
const fate = listingFateLoader(listing, market, quoted);
|
|
589
750
|
const [{ points, resolution }, quote] = await Promise.all([
|
|
590
751
|
loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0, {
|
|
591
|
-
command: range === "1Y" ? "history" : `history --range ${range}`, noun: "history",
|
|
752
|
+
command: range === "1Y" ? "history" : `history --range ${range}`, noun: "history", fate,
|
|
592
753
|
})
|
|
593
754
|
.catch((error) => failHistory(error, listing.key, ctx)),
|
|
594
|
-
|
|
755
|
+
quoted,
|
|
595
756
|
]);
|
|
596
757
|
const identity = listingIdentity(listing, quote);
|
|
597
758
|
// Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
|
|
598
759
|
const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(() => null);
|
|
599
|
-
const unit = historyUnit(listing.
|
|
600
|
-
const
|
|
760
|
+
const unit = historyUnit(listing.key, listed ?? quote);
|
|
761
|
+
const traded = {
|
|
762
|
+
listing: listing.key, exchange: quote?.listingExchangeName || exchange || undefined, instrumentType: quote?.instrumentType,
|
|
763
|
+
};
|
|
764
|
+
// A coin's volume is traded value in its quote currency: said in every row and on the status line.
|
|
765
|
+
const volume = historyVolume(unit, traded);
|
|
766
|
+
const data = historyRows(points, resolution, unit.currency, unit.unit, volume);
|
|
601
767
|
const interval = data[0]?.interval ?? null;
|
|
602
768
|
const decimals = historyPriceDecimals(data, listing.saved?.metadata.assetCategory);
|
|
603
769
|
const price = (value: unknown) => typeof value === "number" ? value.toFixed(decimals) : "";
|
|
@@ -608,8 +774,17 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
608
774
|
...barHistoryFreshness(intraday ? null : barResolutionFromDates(data.map((row) => row.date))),
|
|
609
775
|
observedKey: "date",
|
|
610
776
|
oldest: null,
|
|
777
|
+
// Bars stop arriving on the listing's own trading days, not New York's.
|
|
778
|
+
cadenceExchange: quote?.listingExchangeName || exchange || undefined,
|
|
779
|
+
...(volume ? { note: historyVolumeText(volume, interval) } : {}),
|
|
611
780
|
});
|
|
612
|
-
const
|
|
781
|
+
const gaps = historyGapNote(data, traded);
|
|
782
|
+
const notes = [
|
|
783
|
+
// FB's bars are the fund listed there now; META's from 2021 are from when it was FB.
|
|
784
|
+
...listingFateNotes(await fate(), { served: data.length > 0, firstDate: data[0]?.date }),
|
|
785
|
+
...await listingChoiceNotes(listing, market, quote, venues),
|
|
786
|
+
...historyNotes(range, interval, data[0]?.date), gaps ?? [], historyFlagNote(data) ?? [], historyBarStatusNote(data) ?? [],
|
|
787
|
+
].flat();
|
|
613
788
|
const priceColumns: CliResultColumn[] = [
|
|
614
789
|
intraday
|
|
615
790
|
? { key: "date", header: "Time", format: (value) => typeof value === "string" ? formatUtcTime(value) : "" }
|
|
@@ -624,6 +799,16 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
624
799
|
const flagColumn: CliResultColumn = {
|
|
625
800
|
key: "flag", header: "Flag", format: (value) => typeof value === "string" ? cliStyles.warning(value) : "",
|
|
626
801
|
};
|
|
802
|
+
// Text also marks a bar still forming or rebuilt from hourly bars there; exports keep them as their own fields.
|
|
803
|
+
const flagTextColumn: CliResultColumn = {
|
|
804
|
+
...flagColumn,
|
|
805
|
+
format: (value, row) => [
|
|
806
|
+
typeof value === "string" ? cliStyles.warning(value) : "",
|
|
807
|
+
row.partial ? cliStyles.muted("partial") : "",
|
|
808
|
+
row.filled ? cliStyles.muted("filled") : "",
|
|
809
|
+
].filter(Boolean).join(" "),
|
|
810
|
+
};
|
|
811
|
+
const marked = data.some((row) => row.partial || row.filled);
|
|
627
812
|
ctx.printResult({
|
|
628
813
|
data,
|
|
629
814
|
metadata: {
|
|
@@ -638,19 +823,23 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
638
823
|
lastDate: data.at(-1)?.date ?? null,
|
|
639
824
|
bars: data.length,
|
|
640
825
|
asOf: freshness?.asOf ?? null,
|
|
826
|
+
...(volume ? { volumeUnit: volume.unit, volumePeriod: interval === "1d" ? "UTC day" : interval } : {}),
|
|
641
827
|
},
|
|
642
828
|
...(notes.length > 0 ? { warnings: notes } : {}),
|
|
643
829
|
freshness,
|
|
644
830
|
}, {
|
|
645
831
|
heading: `${listingHeading(identity)}\n${cliStyles.muted(historyFacts(unit, data).join(" · "))}`,
|
|
646
832
|
dateKey: "date",
|
|
647
|
-
textColumns: [...priceColumns,
|
|
648
|
-
// Every exported row says its currency and bar size, so it survives head and concatenation.
|
|
833
|
+
textColumns: [...priceColumns, flagTextColumn],
|
|
834
|
+
// Every exported row says its currency, what a price is in and its bar size, so it survives head and concatenation.
|
|
649
835
|
columns: [
|
|
650
836
|
...priceColumns,
|
|
651
837
|
{ key: "currency", header: "Currency" },
|
|
838
|
+
{ key: "unit", header: "Unit" },
|
|
652
839
|
{ key: "interval", header: "Interval" },
|
|
653
840
|
flagColumn,
|
|
841
|
+
...marked ? [{ key: "partial", header: "Partial" }, { key: "filled", header: "Filled" }] : [],
|
|
842
|
+
...volume ? [{ key: "volumeUnit", header: "Volume unit" }, { key: "volumeNote", header: "Volume note" }] : [],
|
|
654
843
|
],
|
|
655
844
|
});
|
|
656
845
|
});
|
|
@@ -687,10 +876,12 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
687
876
|
};
|
|
688
877
|
await withMarketData(ctx, async (market) => {
|
|
689
878
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
879
|
+
const venues = loadBareVenues(listing, market);
|
|
690
880
|
const financials = await loadForListing(listing, market, ctx, () => market.dataProvider.getTickerFinancials(
|
|
691
881
|
listing.request.symbol, listing.request.exchange, { cacheMode: ctx.cliOptions.refresh ? "refresh" : "default" },
|
|
692
882
|
), undefined, { command: commandName, noun: commandName === "valuation" ? "valuation data" : commandName });
|
|
693
883
|
const identity = listingIdentity(listing, financials.quote);
|
|
884
|
+
const choice = await listingChoiceNotes(listing, market, financials.quote, venues);
|
|
694
885
|
const data: FinancialsCliData = {
|
|
695
886
|
symbol: listing.key,
|
|
696
887
|
exchange: identity.exchange,
|
|
@@ -703,6 +894,7 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
703
894
|
// JSON reads a multiple over a loss as null with its reason, never as a number.
|
|
704
895
|
data: { ...data, fundamentals: exportedFundamentals(data.fundamentals) },
|
|
705
896
|
freshness,
|
|
897
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
706
898
|
}, {
|
|
707
899
|
text: () => renderFundamentalsReport(data, view),
|
|
708
900
|
...(tabular ? { tables: selectTables(fundamentalsReportTables(data, view, freshness)) } : {}),
|
|
@@ -731,6 +923,7 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
731
923
|
profile: financials.profile,
|
|
732
924
|
},
|
|
733
925
|
freshness,
|
|
926
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
734
927
|
}, {
|
|
735
928
|
heading: listingHeading(identity),
|
|
736
929
|
rows: () => rows,
|
|
@@ -740,16 +933,55 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
740
933
|
});
|
|
741
934
|
}
|
|
742
935
|
|
|
936
|
+
const NEWS_COLUMNS = [
|
|
937
|
+
// UTC with the zone named, as every CLI time prints, rather than the host zone unlabeled.
|
|
938
|
+
{
|
|
939
|
+
key: "publishedAt",
|
|
940
|
+
header: "Published",
|
|
941
|
+
format: (value: unknown) => typeof value === "string" ? formatUtcTime(value) : "",
|
|
942
|
+
},
|
|
943
|
+
{ key: "source", header: "Source", maxWidth: 20 },
|
|
944
|
+
{ key: "title", header: "Title" },
|
|
945
|
+
{ key: "tickers", header: "Tickers", maxWidth: 16 },
|
|
946
|
+
{ key: "url", header: "URL", optional: true },
|
|
947
|
+
];
|
|
948
|
+
|
|
949
|
+
/** Stories matching text, printed as a symbol's news is, under a line saying what they match. */
|
|
950
|
+
async function printNewsSearch(
|
|
951
|
+
text: string,
|
|
952
|
+
articles: NewsArticle[],
|
|
953
|
+
{ reason, empty }: { reason?: string; empty?: string },
|
|
954
|
+
ctx: Parameters<CliCommandDef["execute"]>[1],
|
|
955
|
+
) {
|
|
956
|
+
ctx.printResult({
|
|
957
|
+
data: articles,
|
|
958
|
+
metadata: { ticker: null, search: text, feed: null },
|
|
959
|
+
freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
960
|
+
}, {
|
|
961
|
+
heading: newsSearchHeading(text, reason),
|
|
962
|
+
rows: newsRows,
|
|
963
|
+
columns: NEWS_COLUMNS,
|
|
964
|
+
empty: empty ?? `No stories match "${text}".`,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
743
968
|
async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
744
969
|
const args = [...rawArgs];
|
|
745
970
|
const feed = parseNewsFeed(takeOption(args, "--feed"));
|
|
746
971
|
const exchangeOption = takeOption(args, "--exchange");
|
|
972
|
+
// Stories come newest first, so the newest n are the first n either way.
|
|
973
|
+
const limit = ctx.cliOptions.tail ?? ctx.cliOptions.limit ?? 20;
|
|
974
|
+
const text = newsSearchText(args);
|
|
975
|
+
if (text != null) {
|
|
976
|
+
if (exchangeOption || feed) ctx.fail(`${exchangeOption ? "--exchange" : "--feed"} goes with a symbol, not with text to search stories for.`, "Usage: gloomberb news <query>");
|
|
977
|
+
const problem = newsSearchTextProblem(text);
|
|
978
|
+
if (problem) ctx.fail(problem);
|
|
979
|
+
await printNewsSearch(text, await searchNewsStories(text, limit), {}, ctx);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
747
982
|
if (exchangeOption && !args[0]) ctx.fail("--exchange needs a symbol: gloomberb news <symbol> --exchange <code>");
|
|
748
|
-
rejectExtraArgs(args, 1, { usage: "news [symbol] [--feed <feed>]", takes: "one symbol at most", advice: "Run it once per symbol." }, ctx);
|
|
749
983
|
await withMarketData(ctx, async (market) => {
|
|
750
984
|
const listing = args[0] ? await requireCliListing(args[0], exchangeOption, market, ctx) : null;
|
|
751
|
-
// Stories come newest first, so the newest n are the first n either way.
|
|
752
|
-
const limit = ctx.cliOptions.tail ?? ctx.cliOptions.limit ?? 20;
|
|
753
985
|
const loadNews = () => market.dataProvider.getNews({
|
|
754
986
|
feed: feed ?? (listing ? "ticker" : "latest"),
|
|
755
987
|
// Still set for news plugins that read the deprecated scope.
|
|
@@ -763,25 +995,33 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
763
995
|
listing ? loadListingQuote(market.dataProvider, listing) : null,
|
|
764
996
|
]);
|
|
765
997
|
const identity = listing ? listingIdentity(listing, quote) : null;
|
|
998
|
+
const resolved = quote !== null || identity?.name != null;
|
|
999
|
+
// A bare word no listing answers to is text, and so is a futures board code (TTF), which resolves
|
|
1000
|
+
// to its future: news is filed under company tickers, so the stories that mention the word are the news.
|
|
1001
|
+
const word = listing && !listing.exchange && !feed && articles.length === 0 ? newsFallbackText(args[0]!) : null;
|
|
1002
|
+
const futuresCode = !!word && !!bareFuturesCode(word) && isFuturesInstrument(listing!.symbol, quote?.instrumentType);
|
|
1003
|
+
const reason = !resolved ? "no ticker by that name" : futuresCode ? "a futures code, not a ticker" : null;
|
|
1004
|
+
const emptyMessage = (searched: boolean) => listing && identity
|
|
1005
|
+
? newsEmptyMessage(searched && word ? word : listing.symbol, listingTitle(identity), quote, resolved, searched)
|
|
1006
|
+
: undefined;
|
|
1007
|
+
if (word && reason) {
|
|
1008
|
+
// The lookup that found no ticker already answered; a search that fails leaves that answer.
|
|
1009
|
+
const found = await searchNewsStories(word, limit).catch(() => []);
|
|
1010
|
+
await printNewsSearch(word, found, { reason, empty: emptyMessage(true) }, ctx);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const empty = emptyMessage(false);
|
|
1014
|
+
const read = listing ? listingReadNotes(listing) : [];
|
|
766
1015
|
ctx.printResult({
|
|
767
1016
|
data: articles,
|
|
1017
|
+
...(read.length > 0 ? { warnings: read } : {}),
|
|
768
1018
|
metadata: { ticker: listing?.key ?? null, ...(identity ? listingMetadata(identity) : {}), feed: feed ?? null },
|
|
769
1019
|
freshness: rowsFreshness(newsRows(articles), cloudNewsFreshness(await cloudRealtimeAccess())),
|
|
770
1020
|
}, {
|
|
771
1021
|
...(identity ? { heading: listingHeading(identity) } : {}),
|
|
772
1022
|
rows: newsRows,
|
|
773
|
-
columns:
|
|
774
|
-
|
|
775
|
-
{
|
|
776
|
-
key: "publishedAt",
|
|
777
|
-
header: "Published",
|
|
778
|
-
format: (value) => typeof value === "string" ? formatUtcTime(value) : "",
|
|
779
|
-
},
|
|
780
|
-
{ key: "source", header: "Source", maxWidth: 20 },
|
|
781
|
-
{ key: "title", header: "Title" },
|
|
782
|
-
{ key: "tickers", header: "Tickers", maxWidth: 16 },
|
|
783
|
-
{ key: "url", header: "URL", optional: true },
|
|
784
|
-
],
|
|
1023
|
+
columns: NEWS_COLUMNS,
|
|
1024
|
+
...(empty ? { empty } : {}),
|
|
785
1025
|
});
|
|
786
1026
|
});
|
|
787
1027
|
}
|
|
@@ -809,6 +1049,7 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
809
1049
|
const raw = requireOneArg(args, "filings <symbol> [--count <n>] [--form <form>]", "symbol", ctx);
|
|
810
1050
|
await withMarketData(ctx, async (market) => {
|
|
811
1051
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
1052
|
+
const venues = loadBareVenues(listing, market);
|
|
812
1053
|
const { symbol, exchange } = listing.request;
|
|
813
1054
|
// A listing outside the US sends its company to the lookup, which needs its quote first.
|
|
814
1055
|
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
@@ -825,15 +1066,20 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
825
1066
|
quotePromise,
|
|
826
1067
|
]);
|
|
827
1068
|
const resolved = identity ?? listingIdentity(listing, quote);
|
|
1069
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
828
1070
|
ctx.printResult({
|
|
829
1071
|
data: filings,
|
|
830
1072
|
metadata: { ...listingMetadata(resolved), ...(form ? { form } : {}) },
|
|
831
1073
|
freshness: rowsFreshness(filingRows(filings), { ...SEC_FILINGS, observedKey: "filingDate" }),
|
|
1074
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
832
1075
|
}, {
|
|
833
1076
|
heading: listingHeading(resolved),
|
|
834
1077
|
rows: filingRows,
|
|
835
1078
|
columns: FILING_COLUMNS,
|
|
836
|
-
|
|
1079
|
+
// A listing abroad is looked up by its company: none found means the company files nothing with the SEC.
|
|
1080
|
+
empty: form ? `No ${form} filings found for ${listingTitle(resolved)}.`
|
|
1081
|
+
: identity ? noSecReportsNotice(resolved)
|
|
1082
|
+
: `No SEC filings found for ${listingTitle(resolved)}.`,
|
|
837
1083
|
});
|
|
838
1084
|
} catch (error) {
|
|
839
1085
|
if (!(error instanceof SecRegistrantMismatchError)) throw error;
|
|
@@ -851,17 +1097,50 @@ async function runFilings(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
851
1097
|
});
|
|
852
1098
|
}
|
|
853
1099
|
|
|
1100
|
+
type Form4Insiders = "available" | "none" | "foreign-private-issuer" | "no-sec-reports";
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Whether the company has Form 4 filings of its own, read as `fn INS` reads
|
|
1104
|
+
* them; null when they could not be read. A listing abroad the SEC lists
|
|
1105
|
+
* nothing for files no SEC reports at all.
|
|
1106
|
+
*/
|
|
1107
|
+
async function loadForm4Insiders(market: MarketContext, listing: CliListing): Promise<Form4Insiders | null> {
|
|
1108
|
+
try {
|
|
1109
|
+
const { parsed, foreignPrivateIssuer, noFilings } = await loadParsedInsiderFilings(market.dataProvider, listing.request.symbol, {
|
|
1110
|
+
limit: 1,
|
|
1111
|
+
exchange: listing.request.exchange,
|
|
1112
|
+
});
|
|
1113
|
+
if (parsed.length > 0) return "available";
|
|
1114
|
+
if (noFilings && nonUsSecListingVenue(listing.request.symbol, listing.request.exchange)) return "no-sec-reports";
|
|
1115
|
+
return foreignPrivateIssuer ? "foreign-private-issuer" : "none";
|
|
1116
|
+
} catch {
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
854
1121
|
/**
|
|
855
1122
|
* Holder lists name institutions and funds; individual insiders rarely appear.
|
|
856
|
-
* The insider share of the company is reported either way, so say it.
|
|
1123
|
+
* The insider share of the company is reported either way, so say it. The
|
|
1124
|
+
* Form 4 trades are pointed to only when the company has some; otherwise the
|
|
1125
|
+
* line says why there are none, in the words `fn INS` uses.
|
|
857
1126
|
*/
|
|
858
|
-
function insiderSummary(
|
|
1127
|
+
function insiderSummary(
|
|
1128
|
+
data: HolderData,
|
|
1129
|
+
ownerTypes: Set<string>,
|
|
1130
|
+
listing: Pick<CliListing, "key">,
|
|
1131
|
+
identity: ListingIdentity,
|
|
1132
|
+
form4: Form4Insiders | null,
|
|
1133
|
+
): string {
|
|
859
1134
|
const held = data.summary?.insidersPercentHeld;
|
|
860
1135
|
const share = held == null || !Number.isFinite(held)
|
|
861
1136
|
? ""
|
|
862
1137
|
: `Insiders hold ${formatFractionPercentCell(held)} of shares outstanding.`;
|
|
863
1138
|
const listed = data.holders.some((holder) => ownerTypes.has(holder.ownerType));
|
|
864
|
-
|
|
1139
|
+
// The listing as the command read it (CAR:ASX), not the source's spelling of it (CAR.AX), which fn does not take.
|
|
1140
|
+
const transactions = form4 === "available"
|
|
1141
|
+
? listed ? "" : `Form 4 transactions: gloomberb fn INS ${listing.key}`
|
|
1142
|
+
: form4 === "no-sec-reports" ? noSecReportsNotice(identity)
|
|
1143
|
+
: form4 ? insiderEmptyNotice(form4 === "foreign-private-issuer") : "";
|
|
865
1144
|
return [share, transactions].filter(Boolean).join("\n");
|
|
866
1145
|
}
|
|
867
1146
|
|
|
@@ -878,14 +1157,18 @@ async function printBeneficialOwners(
|
|
|
878
1157
|
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
879
1158
|
const abroad = nonUsSecListingVenue(symbol, exchange) ? listingIdentity(listing, await quotePromise) : null;
|
|
880
1159
|
const request = { form: beneficialRouteForm(form), history, listing: { exchange, name: abroad?.name ?? undefined } };
|
|
881
|
-
const [payload, holders, quote] = await Promise.all([
|
|
1160
|
+
const [payload, holders, quote, financials] = await Promise.all([
|
|
882
1161
|
loadForListing(listing, market, ctx, () => fetchBeneficialOwners(symbol, request)),
|
|
883
1162
|
market.dataProvider.getHolders?.(symbol, exchange).catch(() => null) ?? null,
|
|
884
1163
|
quotePromise,
|
|
1164
|
+
// Today's share count, which the filed percents are set against.
|
|
1165
|
+
market.dataProvider.getTickerFinancials(symbol, exchange).catch(() => null),
|
|
885
1166
|
]);
|
|
886
1167
|
const identity = listingIdentity(listing, quote);
|
|
887
|
-
const
|
|
1168
|
+
const sharesOutstanding = classSharesToday(financials);
|
|
1169
|
+
const rows = buildBeneficialReportRows(payload, { history, holders, sharesOutstanding });
|
|
888
1170
|
const notices = beneficialCoverageNotices(payload.coverage);
|
|
1171
|
+
const legend = beneficialReportLegend(rows, sharesOutstanding);
|
|
889
1172
|
ctx.printResult({
|
|
890
1173
|
data: rows,
|
|
891
1174
|
...(notices.length ? { warnings: notices } : {}),
|
|
@@ -898,12 +1181,16 @@ async function printBeneficialOwners(
|
|
|
898
1181
|
asOf: payload.asOf,
|
|
899
1182
|
coverage: payload.coverage,
|
|
900
1183
|
complete: beneficialListComplete(payload),
|
|
1184
|
+
sharesOutstanding,
|
|
901
1185
|
},
|
|
902
1186
|
}, {
|
|
903
1187
|
heading: `${listingHeading(identity)}${cliStyles.muted(beneficialListFacts(payload, form, rows.length, history).map((fact) => ` · ${fact}`).join(""))}`,
|
|
904
|
-
|
|
1188
|
+
// Text draws each column as the HDS report does; CSV and JSON keep the raw values.
|
|
1189
|
+
textColumns: BENEFICIAL_REPORT_COLUMNS.map((column) => ({ ...column, format: column.textFormat ?? column.format })),
|
|
1190
|
+
...(legend ? { footnote: legend } : {}),
|
|
905
1191
|
columns: [
|
|
906
|
-
...BENEFICIAL_REPORT_COLUMNS,
|
|
1192
|
+
...BENEFICIAL_REPORT_COLUMNS.map(({ key, header, align }) => ({ key, header, ...(align ? { align } : {}) })),
|
|
1193
|
+
{ key: "otherPersons", header: "Other persons" },
|
|
907
1194
|
{ key: "status", header: "Status" },
|
|
908
1195
|
{ key: "filerCik", header: "Filer CIK" },
|
|
909
1196
|
{ key: "accessionNumber", header: "Accession" },
|
|
@@ -911,6 +1198,8 @@ async function printBeneficialOwners(
|
|
|
911
1198
|
],
|
|
912
1199
|
empty: beneficialListUnreadable(payload)
|
|
913
1200
|
? `13D/13G filings for ${listingTitle(identity)} are listed but could not be read.`
|
|
1201
|
+
// The SEC knows no company for a listing abroad that files there under no name of its own.
|
|
1202
|
+
: abroad && !payload.cik ? noSecReportsNotice(identity)
|
|
914
1203
|
: `No 13D/13G filings in the last 4 years for ${listingTitle(identity)}.`,
|
|
915
1204
|
});
|
|
916
1205
|
}
|
|
@@ -932,7 +1221,9 @@ async function runHolders(
|
|
|
932
1221
|
await withMarketData(ctx, async (market) => {
|
|
933
1222
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
934
1223
|
if (form !== "13f") return printBeneficialOwners(listing, market, form, history, ctx);
|
|
935
|
-
const
|
|
1224
|
+
const insiderCommand = commandName === "insider" && ownerTypes != null;
|
|
1225
|
+
const venues = loadBareVenues(listing, market);
|
|
1226
|
+
const [data, quote, form4] = await Promise.all([
|
|
936
1227
|
loadForListing(
|
|
937
1228
|
listing, market, ctx,
|
|
938
1229
|
() => market.dataProvider.getHolders(listing.request.symbol, listing.request.exchange),
|
|
@@ -940,43 +1231,70 @@ async function runHolders(
|
|
|
940
1231
|
{ command: commandName, noun: "holder data" },
|
|
941
1232
|
),
|
|
942
1233
|
loadListingQuote(market.dataProvider, listing),
|
|
1234
|
+
insiderCommand ? loadForm4Insiders(market, listing) : null,
|
|
943
1235
|
]);
|
|
944
1236
|
const identity = listingIdentity(listing, quote);
|
|
945
|
-
const
|
|
946
|
-
|
|
1237
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
1238
|
+
// A London line's values can be dollars from the 13F filings; the listing's currency is not their unit.
|
|
1239
|
+
const valueCurrency = holderValueCurrency(data, "USD")!;
|
|
1240
|
+
// The rows, their units and their order are HDS's, so `holders --csv` and `fn HDS --csv` agree.
|
|
1241
|
+
const rows = holderReportRows(data, {
|
|
1242
|
+
marketCap: holderStakeMarketCap(quote?.marketCap, quote?.currency, valueCurrency),
|
|
1243
|
+
ownerTypes,
|
|
1244
|
+
});
|
|
1245
|
+
const shownRows = windowRows(rows, ctx.cliOptions).rows;
|
|
1246
|
+
const shown = shownRows.length;
|
|
947
1247
|
const institutional = !ownerTypes?.has("insider");
|
|
948
1248
|
const total = institutional ? data.summary?.institutionsCount ?? null : null;
|
|
949
1249
|
const reportDate = sharedReportDate(rows);
|
|
950
|
-
// A London line's values can be dollars from the 13F filings; the listing's currency is not their unit.
|
|
951
|
-
const valueCurrency = holderValueCurrency(data);
|
|
952
1250
|
const valueBasis = rows.length > 0 ? holderValueBasis(reportDate, data.valueBasis, valueCurrency) : null;
|
|
953
1251
|
const positionsBasis = rows.length > 0 ? nonUsHolderCaveat(identity.exchange || data.exchange, data.currency) : null;
|
|
954
1252
|
const shareBasisNote = holderShareBasisNote(data, rows);
|
|
955
1253
|
const markedRows = rows.some((row) => holderShareBasisMarker(row));
|
|
956
1254
|
// The heading names the listing; its unit, date and how much of the list follows on the same line.
|
|
1255
|
+
// One "as of" only when every row shown is of that date; each row carries its own.
|
|
957
1256
|
const facts = holderListFacts({
|
|
958
|
-
currency: valueCurrency,
|
|
1257
|
+
currency: valueCurrency,
|
|
1258
|
+
listingCurrency: data.currency,
|
|
1259
|
+
period: holderListPeriod(shownRows, data.asOf),
|
|
1260
|
+
shown,
|
|
1261
|
+
reported: rows.length,
|
|
1262
|
+
total,
|
|
1263
|
+
ranked: !positionsBasis,
|
|
959
1264
|
});
|
|
1265
|
+
const unvaluedNote = unvaluedHoldersNote(shownRows);
|
|
1266
|
+
const columns = holderReportColumns(shownRows, valueCurrency);
|
|
1267
|
+
// Dated as HDS dates it: by the list's as-of.
|
|
1268
|
+
const freshness = rowsFreshness(shownRows, { ...SEC_FILINGS, basis: "13F filings" }, { asOf: data.asOf ?? null });
|
|
1269
|
+
// Named as fn HDS takes it, so the closing notes point to the same listing.
|
|
1270
|
+
const requested = exchangeOption && !raw.includes(":") ? `${raw}:${exchangeOption.toUpperCase()}` : raw;
|
|
1271
|
+
const { notices } = holderReportNotices({ data, requested, shown: shownRows, reported: rows.length, total });
|
|
960
1272
|
ctx.printResult({
|
|
961
|
-
data,
|
|
1273
|
+
data: rows,
|
|
1274
|
+
...(freshness ? { freshness } : {}),
|
|
962
1275
|
metadata: {
|
|
963
1276
|
...listingMetadata(identity),
|
|
964
|
-
summary: data.summary,
|
|
1277
|
+
summary: data.summary ?? null,
|
|
965
1278
|
currency: data.currency ?? null,
|
|
966
|
-
valueCurrency
|
|
1279
|
+
valueCurrency,
|
|
1280
|
+
units: holderReportUnits(valueCurrency),
|
|
967
1281
|
asOf: data.asOf ?? null,
|
|
1282
|
+
// Each holder's latest period; several when the list mixes quarters.
|
|
1283
|
+
reportDates: holderReportDates(shownRows),
|
|
968
1284
|
shown,
|
|
969
1285
|
reported: rows.length,
|
|
970
1286
|
total,
|
|
971
1287
|
truncated: shown < (total ?? rows.length),
|
|
972
1288
|
valueBasis,
|
|
973
1289
|
positionsBasis,
|
|
1290
|
+
...(insiderCommand ? { form4Insiders: form4 } : {}),
|
|
974
1291
|
},
|
|
1292
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
975
1293
|
}, {
|
|
976
1294
|
heading: `${listingHeading(identity)}${cliStyles.muted(facts.map((fact) => ` · ${fact}`).join(""))}`,
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
{ key: "
|
|
1295
|
+
columns,
|
|
1296
|
+
textColumns: [
|
|
1297
|
+
{ key: "ownerType", header: "Type" },
|
|
980
1298
|
{ key: "name", header: "Holder" },
|
|
981
1299
|
{ key: "reportDate", header: "Date" },
|
|
982
1300
|
{ key: "shares", header: "Shares", align: "right", format: formatCountCell },
|
|
@@ -992,12 +1310,17 @@ async function runHolders(
|
|
|
992
1310
|
key: "percentHeld",
|
|
993
1311
|
header: "% Held",
|
|
994
1312
|
align: "right",
|
|
995
|
-
format: (value) => isFiniteNumber(value) ?
|
|
1313
|
+
format: (value) => isFiniteNumber(value) ? formatPercentHeld(value) : "",
|
|
996
1314
|
},
|
|
997
1315
|
],
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1316
|
+
// CSV and NDJSON are the HDS report's table and closing lines.
|
|
1317
|
+
tables: {
|
|
1318
|
+
tables: [exportRowsTable("Holders", columns, shownRows)],
|
|
1319
|
+
footer: reportFooterLines({ freshness, notes: notices }),
|
|
1320
|
+
},
|
|
1321
|
+
summary: () => [
|
|
1322
|
+
[valueBasis ? `Value = ${valueBasis}.` : "", positionsBasis ?? "", shareBasisNote ?? "", unvaluedNote ?? ""].filter(Boolean).join(" "),
|
|
1323
|
+
insiderCommand ? insiderSummary(data, ownerTypes, listing, identity, form4) : "",
|
|
1001
1324
|
].filter(Boolean).join("\n"),
|
|
1002
1325
|
empty: `No holders reported for ${listingTitle(identity)}.`,
|
|
1003
1326
|
});
|
|
@@ -1010,6 +1333,7 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1010
1333
|
const raw = requireOneArg(args, "analyst <symbol>", "symbol", ctx);
|
|
1011
1334
|
await withMarketData(ctx, async (market) => {
|
|
1012
1335
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
1336
|
+
const venues = loadBareVenues(listing, market);
|
|
1013
1337
|
const [data, quote] = await Promise.all([
|
|
1014
1338
|
loadForListing(
|
|
1015
1339
|
listing, market, ctx, () => market.dataProvider.getAnalystResearch(listing.request.symbol, listing.request.exchange),
|
|
@@ -1018,6 +1342,7 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1018
1342
|
loadListingQuote(market.dataProvider, listing),
|
|
1019
1343
|
]);
|
|
1020
1344
|
const identity = listingIdentity(listing, quote);
|
|
1345
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
1021
1346
|
ctx.printResult({
|
|
1022
1347
|
data,
|
|
1023
1348
|
metadata: {
|
|
@@ -1029,6 +1354,7 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1029
1354
|
freshness: rowsFreshness(analystRows(data), {
|
|
1030
1355
|
...REPORTED_DATA, basis: "analyst ratings", observedKey: "date", oldest: null,
|
|
1031
1356
|
}, { stale: data.stale === true }),
|
|
1357
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
1032
1358
|
}, {
|
|
1033
1359
|
heading: listingHeading(identity),
|
|
1034
1360
|
rows: analystRows,
|
|
@@ -1047,9 +1373,13 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1047
1373
|
async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
1048
1374
|
const args = [...rawArgs];
|
|
1049
1375
|
const exchangeOption = takeOption(args, "--exchange");
|
|
1376
|
+
// A central bank's name means the bank, not a listing that shares it; a venue still reaches the listing.
|
|
1377
|
+
const bank = exchangeOption ? null : centralBankEventsMessage(args.join(" "));
|
|
1378
|
+
if (bank) ctx.fail(bank);
|
|
1050
1379
|
const raw = requireOneArg(args, "events <symbol>", "symbol", ctx);
|
|
1051
1380
|
await withMarketData(ctx, async (market) => {
|
|
1052
1381
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
1382
|
+
const venues = loadBareVenues(listing, market);
|
|
1053
1383
|
const [data, quote] = await Promise.all([
|
|
1054
1384
|
loadForListing(
|
|
1055
1385
|
listing, market, ctx, () => market.dataProvider.getCorporateActions(listing.request.symbol, listing.request.exchange),
|
|
@@ -1058,12 +1388,14 @@ async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execu
|
|
|
1058
1388
|
loadListingQuote(market.dataProvider, listing),
|
|
1059
1389
|
]);
|
|
1060
1390
|
const identity = listingIdentity(listing, quote);
|
|
1391
|
+
const choice = await listingChoiceNotes(listing, market, quote, venues);
|
|
1061
1392
|
ctx.printResult({
|
|
1062
1393
|
data,
|
|
1063
1394
|
metadata: listingMetadata(identity),
|
|
1064
1395
|
freshness: rowsFreshness(corporateActionRows(data), {
|
|
1065
1396
|
...REPORTED_DATA, basis: "corporate actions", observedKey: "date", oldest: null,
|
|
1066
1397
|
}),
|
|
1398
|
+
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
1067
1399
|
}, {
|
|
1068
1400
|
heading: listingHeading(identity),
|
|
1069
1401
|
rows: corporateActionRows,
|
|
@@ -1111,7 +1443,9 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1111
1443
|
const listing = await requireCliListing(raw, exchangeOption, market, ctx);
|
|
1112
1444
|
const { symbol, exchange } = listing.request;
|
|
1113
1445
|
const refresh = ctx.cliOptions.refresh;
|
|
1446
|
+
const venues = loadBareVenues(listing, market);
|
|
1114
1447
|
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
1448
|
+
const fate = listingFateLoader(listing, market, quotePromise);
|
|
1115
1449
|
const inputsPromise = loadOptionModelInputs(market.dataProvider, listing, quotePromise, refresh);
|
|
1116
1450
|
const { result, chain: loaded } = await loadForListing(listing, market, ctx, async () => {
|
|
1117
1451
|
const cached = await market.dataProvider.getCachedQuery?.("getOptionsChain", [symbol, exchange, expirationDate, undefined])
|
|
@@ -1122,7 +1456,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1122
1456
|
cacheMode: refresh ? "refresh" : "default",
|
|
1123
1457
|
}),
|
|
1124
1458
|
};
|
|
1125
|
-
}, undefined, { command: "options", noun: "options chain" }).catch((error: unknown) => {
|
|
1459
|
+
}, undefined, { command: "options", noun: "options chain", fate }).catch((error: unknown) => {
|
|
1126
1460
|
if (!isOptionsUnavailableError(error)) throw error;
|
|
1127
1461
|
// A future or commodity with no chain of its own: name the listed fund whose options stand in.
|
|
1128
1462
|
const alternative = listedOptionsAlternative(listing.key);
|
|
@@ -1140,7 +1474,8 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1140
1474
|
const miss = missingExpiryMessage(listing.key, expirationDate, listed);
|
|
1141
1475
|
ctx.fail(miss.message, miss.details);
|
|
1142
1476
|
}
|
|
1143
|
-
const
|
|
1477
|
+
const quote = await quotePromise;
|
|
1478
|
+
const identity = listingIdentity(listing, quote);
|
|
1144
1479
|
const chain = chainWithModelFigures(loaded, inputs);
|
|
1145
1480
|
// A failed refresh falls back to the stored chain, which can be days old.
|
|
1146
1481
|
const refreshWarning = result?.refreshError == null ? null
|
|
@@ -1150,7 +1485,13 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1150
1485
|
const modelWarning = inputs.spot == null && (chain.calls.length > 0 || chain.puts.length > 0)
|
|
1151
1486
|
? `IV and delta need a current ${listing.key} quote; those columns are blank`
|
|
1152
1487
|
: null;
|
|
1153
|
-
const
|
|
1488
|
+
const listed = chain.expirationDates.length > 0 || chain.calls.length > 0 || chain.puts.length > 0;
|
|
1489
|
+
const warnings = [
|
|
1490
|
+
// FB's chain is the fund listed there now; a listing that stopped trading has none.
|
|
1491
|
+
...listingFateNotes(await fate(), { served: listed }),
|
|
1492
|
+
...await listingChoiceNotes(listing, market, quote, venues),
|
|
1493
|
+
...[refreshWarning ?? sessionWarning, modelWarning].filter((warning): warning is string => warning != null),
|
|
1494
|
+
];
|
|
1154
1495
|
// Dated by the chain's last trade; a chain that failed to refresh is the stored one, stale.
|
|
1155
1496
|
const freshness = rowsFreshness([], { asOf: chain.asOf ?? null }, {
|
|
1156
1497
|
...(chain.dataSource ? { dataSource: chain.dataSource } : {}),
|
|
@@ -1226,12 +1567,16 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
|
|
|
1226
1567
|
const asOf = observed.length > 0 ? new Date(Math.min(...observed)).toISOString() : null;
|
|
1227
1568
|
const stale = legs.some((leg) => leg != null && (leg.staleAt <= Date.now() || leg.refreshError != null));
|
|
1228
1569
|
const row = { currency, baseCurrency, rate, asOf, stale, pair, inverse: 1 / rate };
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1570
|
+
// The closing line dates the rate, and says when it is stale, as `fn FXC` does.
|
|
1571
|
+
const freshness = fxPairFreshness(new Map(codes.flatMap((code, index) => {
|
|
1572
|
+
const leg = legs[index];
|
|
1573
|
+
return leg ? [[code, {
|
|
1574
|
+
rate: leg.value,
|
|
1575
|
+
timing: { asOf: leg.asOf, staleAt: leg.staleAt, error: leg.refreshError, delayMinutes: leg.delayMinutes },
|
|
1576
|
+
}] as const] : [];
|
|
1577
|
+
})));
|
|
1578
|
+
ctx.printResult({ data: [row], freshness }, {
|
|
1579
|
+
text: () => describeFxRate(request, rate),
|
|
1235
1580
|
columns: [
|
|
1236
1581
|
{ key: "currency", header: "Currency" },
|
|
1237
1582
|
{ key: "baseCurrency", header: "Base" },
|
|
@@ -1254,24 +1599,55 @@ async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["exe
|
|
|
1254
1599
|
const listings = await Promise.all(symbols.map((symbol) => requireCliListing(
|
|
1255
1600
|
symbol, exchangeOption, services, ctx, { ownExchangeWins: symbols.length > 1 },
|
|
1256
1601
|
)));
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
(
|
|
1261
|
-
|
|
1602
|
+
const keys = listings.map((listing) => listing.key);
|
|
1603
|
+
// Each bare symbol's venues cost a search, so a long list (a portfolio check, not a lookup) names none.
|
|
1604
|
+
const venues = new Map(listings.length > MAX_CHOICE_LOOKUPS ? []
|
|
1605
|
+
: listings.map((listing) => [listing, loadBareVenues(listing, services)] as const));
|
|
1606
|
+
let events: EarningsEvent[];
|
|
1607
|
+
try {
|
|
1608
|
+
events = await loadForListing(
|
|
1609
|
+
listings, services, ctx,
|
|
1610
|
+
() => services.dataProvider.getEarningsCalendar(keys),
|
|
1611
|
+
(found) => found.length === 0,
|
|
1612
|
+
);
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
const failure = earningsCalendarFailure(error, keys);
|
|
1615
|
+
if (failure) ctx.fail(failure);
|
|
1616
|
+
throw error;
|
|
1617
|
+
}
|
|
1618
|
+
const eventOf = (listing: CliListing) => events.find((event) => event.symbol.toUpperCase() === listing.key.toUpperCase());
|
|
1619
|
+
const [abroadNotes, choiceNotes] = await Promise.all([
|
|
1620
|
+
// A listing abroad with no date of its own says so, with its US line's date when that line is the same company.
|
|
1621
|
+
Promise.all(listings.filter((listing) => isListingAbroad(listing) && !eventOf(listing))
|
|
1622
|
+
.map((listing) => noListingCalendarNote(listing, services))),
|
|
1623
|
+
Promise.all(listings.map(async (listing) => {
|
|
1624
|
+
const listed = venues.get(listing);
|
|
1625
|
+
return listed ? earningsChoiceNote(listing, eventOf(listing), await listed) : null;
|
|
1626
|
+
})),
|
|
1627
|
+
]);
|
|
1628
|
+
const noCalendar = abroadNotes.join("\n");
|
|
1629
|
+
// Text prints the listings without a calendar under the table; other formats carry them as warnings.
|
|
1630
|
+
const warnings = [
|
|
1631
|
+
...listings.flatMap(listingReadNotes),
|
|
1632
|
+
...choiceNotes.filter((note): note is string => !!note),
|
|
1633
|
+
...ctx.cliOptions.format === "text" ? [] : abroadNotes,
|
|
1634
|
+
];
|
|
1262
1635
|
ctx.printResult({
|
|
1263
1636
|
data: events,
|
|
1264
1637
|
freshness: rowsFreshness(earningsRows(events), { status: "not-a-feed", basis: "calendar", observedKey: "date", oldest: null }),
|
|
1638
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
1265
1639
|
}, {
|
|
1266
1640
|
rows: earningsRows,
|
|
1267
1641
|
columns: [
|
|
1268
1642
|
{ key: "date", header: "Date" },
|
|
1643
|
+
{ key: "status", header: "Status" },
|
|
1269
1644
|
{ key: "symbol", header: "Symbol" },
|
|
1270
1645
|
{ key: "name", header: "Name" },
|
|
1271
1646
|
{ key: "timing", header: "Timing" },
|
|
1272
1647
|
{ key: "epsEstimate", header: "EPS Est", align: "right", format: formatEpsCell },
|
|
1273
1648
|
{ key: "epsActual", header: "EPS", align: "right", format: formatEpsCell },
|
|
1274
1649
|
],
|
|
1650
|
+
...(noCalendar ? { empty: noCalendar, footnote: noCalendar } : {}),
|
|
1275
1651
|
});
|
|
1276
1652
|
});
|
|
1277
1653
|
}
|
|
@@ -1284,7 +1660,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
1284
1660
|
group: CLI_COMMAND_GROUPS.research,
|
|
1285
1661
|
usage: ["quote <symbol...>"],
|
|
1286
1662
|
options: [EXCHANGE_OPTION],
|
|
1287
|
-
examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote AAPL --json"],
|
|
1663
|
+
examples: ["quote AAPL MSFT NVDA", "quote SAN:EPA BHP:ASX", "quote BTC-USD EURUSD=X", "quote binance:BZUSDT", "quote AAPL --json"],
|
|
1288
1664
|
},
|
|
1289
1665
|
execute: (args, ctx) => runQuote(args, ctx, "quote"),
|
|
1290
1666
|
},
|
|
@@ -1312,15 +1688,13 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
1312
1688
|
sections: [{
|
|
1313
1689
|
title: "Bars",
|
|
1314
1690
|
lines: [
|
|
1315
|
-
`Each range comes in one bar size: ${historyIntervalsByRange()}. The line under the heading, and the --json metadata, give the currency, the bar size actually served, the first and last bar and the bar count. A warning says when the bars are coarser, or start later, than the range asked for.`,
|
|
1691
|
+
`Each range comes in one bar size: ${historyIntervalsByRange()}. The line under the heading, and the --json metadata, give the currency, the bar size actually served, the first and last bar and the bar count. A warning says when the bars are coarser, or start later, than the range asked for, and for a coin, which trades every day, names each day it has no bar for.`,
|
|
1316
1692
|
"A bar whose prices contradict each other (high below the open or close, or low above them) is left blank and flagged, such as high<open, with the count in a warning. Every exported row (--csv, --ndjson, --json) carries currency, interval and flag.",
|
|
1317
1693
|
],
|
|
1318
1694
|
}],
|
|
1319
1695
|
examples: ["history AAPL", "history BHP:ASX --range 5Y", "history ZAR=X --range ALL", "history ZAR=X --tail 5", "history AAPL --range 5Y --csv > aapl.csv"],
|
|
1320
1696
|
},
|
|
1321
|
-
unknownOptionHint:
|
|
1322
|
-
? `history takes a --range instead of dates: ${TIME_RANGES.join(", ")}. ${HISTORY_ALL_RANGE}.`
|
|
1323
|
-
: null,
|
|
1697
|
+
unknownOptionHint: historyOptionHint,
|
|
1324
1698
|
execute: runHistory,
|
|
1325
1699
|
},
|
|
1326
1700
|
{
|
|
@@ -1472,7 +1846,13 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
1472
1846
|
{ flags: "--history", description: "With --form 13d, 13g or all, every report newest first instead of the latest per filer" },
|
|
1473
1847
|
EXCHANGE_OPTION,
|
|
1474
1848
|
],
|
|
1475
|
-
|
|
1849
|
+
sections: [{
|
|
1850
|
+
title: "Output",
|
|
1851
|
+
lines: [
|
|
1852
|
+
"Holders run largest value first, as in fn HDS. --csv and --json carry the HDS report's columns: values in the currency the header names, shares, and the quarter's change and the stake in percent (0.59 is 0.59%).",
|
|
1853
|
+
],
|
|
1854
|
+
}],
|
|
1855
|
+
examples: ["holders AAPL", "holders SAN:EPA", "holders SAN --csv", "holders CAR --form all", "holders CAR --form 13g --history --json"],
|
|
1476
1856
|
},
|
|
1477
1857
|
execute: (args, ctx) => runHolders(args, ctx, "holders"),
|
|
1478
1858
|
},
|
|
@@ -1519,15 +1899,21 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
1519
1899
|
},
|
|
1520
1900
|
{
|
|
1521
1901
|
name: "news",
|
|
1522
|
-
description: "Fetch market headlines,
|
|
1902
|
+
description: "Fetch market headlines, news for one symbol, or stories matching text",
|
|
1523
1903
|
help: {
|
|
1524
1904
|
group: CLI_COMMAND_GROUPS.companyData,
|
|
1525
|
-
usage: ["news [symbol] [--feed <feed>]"],
|
|
1905
|
+
usage: ["news [symbol] [--feed <feed>]", "news <query>"],
|
|
1526
1906
|
options: [
|
|
1527
1907
|
{ flags: "--feed <feed>", description: "latest, top, or breaking for market news (default latest)" },
|
|
1528
1908
|
EXCHANGE_OPTION,
|
|
1529
1909
|
],
|
|
1530
|
-
|
|
1910
|
+
sections: [{
|
|
1911
|
+
title: "Text",
|
|
1912
|
+
lines: [
|
|
1913
|
+
"Several words, in quotes or not, search the stories that mention them. So does a single word of three letters or more that no listing answers to, and a futures board code such as TTF, a gas benchmark rather than a ticker. A real ticker always gives its own news. The best matches print newest first, 20 unless --limit says otherwise; a search takes neither --feed nor --exchange.",
|
|
1914
|
+
],
|
|
1915
|
+
}],
|
|
1916
|
+
examples: ["news", "news TSLA", "news SAN:EPA", "news TTF", "news \"european gas\"", "news --feed top --limit 10"],
|
|
1531
1917
|
},
|
|
1532
1918
|
execute: runNews,
|
|
1533
1919
|
},
|