gloomberb 0.18.3 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +84 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +22 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +21 -4
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1117 -264
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +192 -47
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +47 -10
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +198 -68
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +305 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +13 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +12 -6
- package/src/components/layout/header.tsx +7 -6
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +107 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +76 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +6 -3
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +6 -8
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +79 -23
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +5 -3
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +3 -2
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +18 -11
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +6 -1
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -4
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +10 -4
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +33 -19
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +29 -8
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +36 -15
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +4 -1
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +8 -1
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +2 -1
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/styles.css +76 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +32 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +17 -4
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/index.ts +10 -5
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +3 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- package/src/cli/pane-functions/screenshot.ts +0 -2122
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ChartTableHeader,
|
|
7
7
|
DetailScrollBody,
|
|
8
8
|
KeyValueRow,
|
|
9
|
+
QueryBar,
|
|
9
10
|
Prose,
|
|
10
11
|
SectionHeading,
|
|
11
12
|
usePaneNoticeFooter,
|
|
@@ -31,6 +32,8 @@ import { handleRefreshKey } from "../../../components/data-table/table-pane";
|
|
|
31
32
|
import { SignInWall } from "../cloud/auth-actions";
|
|
32
33
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
33
34
|
import { useBoundTicker as useSymbolBinding, useTickerRequest } from "../shared/ticker-request";
|
|
35
|
+
import { loadEarningsCalendar } from "../earnings/data/cache";
|
|
36
|
+
import { calendarEventOf, newYorkDay, withResolvedEarnings } from "../shared/earnings-calendar";
|
|
34
37
|
import {
|
|
35
38
|
documentContentKey,
|
|
36
39
|
formatCompactDocumentLabel,
|
|
@@ -50,7 +53,7 @@ import {
|
|
|
50
53
|
type EventStatus,
|
|
51
54
|
} from "./event-model";
|
|
52
55
|
|
|
53
|
-
type EventColumnId = "date" | "status" | "period" | "qEps" | "qRevenue" | "annualEps" | "annualRevenue" | "value" | "detail";
|
|
56
|
+
type EventColumnId = "date" | "status" | "period" | "qEps" | "qRevenue" | "annualEps" | "annualRevenue" | "value" | "detail" | "eps" | "revenue" | "surprise" | "growth";
|
|
54
57
|
type EventColumn = DataTableColumn & { id: EventColumnId };
|
|
55
58
|
|
|
56
59
|
const SEC_EVENT_FILING_LIMIT = 50;
|
|
@@ -138,15 +141,15 @@ export function matchEarningsSecFiling(row: { status: string; date: string; date
|
|
|
138
141
|
return best?.filing ?? null;
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
/**
|
|
142
|
-
function sharedMetricCurrency(rows: readonly EventRow[]): string | undefined {
|
|
144
|
+
/** Share the currency label; EE marks any unstated currencies separately. */
|
|
145
|
+
function sharedMetricCurrency(rows: readonly EventRow[], allowUnstated = false): string | undefined {
|
|
143
146
|
const currencies = new Set<string | undefined>();
|
|
144
147
|
for (const row of rows) {
|
|
145
148
|
for (const [value, currency] of [
|
|
146
149
|
[row.qEps, row.epsCurrency], [row.annualEps, row.epsCurrency],
|
|
147
150
|
[row.qRevenue, row.revenueCurrency], [row.annualRevenue, row.revenueCurrency],
|
|
148
151
|
] as const) {
|
|
149
|
-
if (value != null && Number.isFinite(value)) currencies.add(currency || undefined);
|
|
152
|
+
if (value != null && Number.isFinite(value) && (currency || !allowUnstated)) currencies.add(currency || undefined);
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
const [only] = currencies;
|
|
@@ -154,11 +157,23 @@ function sharedMetricCurrency(rows: readonly EventRow[]): string | undefined {
|
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
export function buildEventColumns(width: number, unit?: string, variant: "corporate-actions" | "earnings-estimates" = "corporate-actions"): EventColumn[] {
|
|
160
|
+
if (variant === "earnings-estimates") {
|
|
161
|
+
return fitChartTableColumns<EventColumn>([
|
|
162
|
+
{ id: "date", label: "DATE", width: 10, align: "left" },
|
|
163
|
+
{ id: "status", label: "EVENT", width: 8, align: "left" },
|
|
164
|
+
{ id: "period", label: "PERIOD", width: 9, align: "left" },
|
|
165
|
+
{ id: "eps", label: "EPS", width: unit ? 7 : 11, align: "right" },
|
|
166
|
+
{ id: "revenue", label: "REVENUE", width: unit ? 9 : 13, align: "right" },
|
|
167
|
+
{ id: "surprise", label: "EPS SURP", width: 9, align: "right" },
|
|
168
|
+
{ id: "growth", label: "YOY GROWTH", width: 14, align: "right" },
|
|
169
|
+
{ id: "detail", label: "DETAIL", width: 9, align: "left", flexGrow: 1 },
|
|
170
|
+
], width, ["detail", "growth", "revenue"]);
|
|
171
|
+
}
|
|
157
172
|
const withUnit = (label: string) => unit ? `${label} ${unit}` : label;
|
|
158
173
|
const columns: EventColumn[] = [
|
|
159
174
|
{ id: "date", label: "DATE", width: 10, align: "left" },
|
|
160
175
|
{ id: "status", label: "EVENT", width: 8, align: "left" },
|
|
161
|
-
{ id: "period", label: "PERIOD", width:
|
|
176
|
+
{ id: "period", label: "PERIOD", width: 12, align: "left" },
|
|
162
177
|
{ id: "qEps", label: withUnit("Q EPS"), width: 12, align: "right" },
|
|
163
178
|
{ id: "qRevenue", label: withUnit("Q REV"), width: 12, align: "right" },
|
|
164
179
|
{ id: "annualEps", label: withUnit("ANN EPS"), width: 12, align: "right" },
|
|
@@ -166,9 +181,7 @@ export function buildEventColumns(width: number, unit?: string, variant: "corpor
|
|
|
166
181
|
{ id: "value", label: "VALUE", width: 11, align: "right" },
|
|
167
182
|
{ id: "detail", label: "DETAIL", width: 9, align: "left", flexGrow: 1 },
|
|
168
183
|
];
|
|
169
|
-
|
|
170
|
-
return variant === "earnings-estimates" ? columns
|
|
171
|
-
: fitChartTableColumns(columns, width, ["annualRevenue", "annualEps", "qRevenue", "qEps", "detail"]);
|
|
184
|
+
return fitChartTableColumns(columns, width, ["annualRevenue", "annualEps", "qRevenue", "qEps", "detail"]);
|
|
172
185
|
}
|
|
173
186
|
|
|
174
187
|
function toneColor(tone: EventRow["tone"]): string {
|
|
@@ -434,13 +447,16 @@ export function CorporateActionsView({
|
|
|
434
447
|
const trailingEps = tickerFinancials?.fundamentals?.eps;
|
|
435
448
|
const trailingEpsCurrency = tickerFinancials?.fundamentals?.epsCurrency;
|
|
436
449
|
const fundamentalsCurrency = tickerFinancials?.fundamentals?.financialCurrency;
|
|
450
|
+
const marketCapCurrency = tickerFinancials?.fundamentals?.marketCapCurrency;
|
|
451
|
+
const quoteCurrency = tickerFinancials?.quote?.currency;
|
|
437
452
|
const financialsData = useMemo<EventFinancials | null>(
|
|
438
453
|
() => (quarterlyStatements || financialCurrency || trailingEps != null ? {
|
|
439
454
|
quarterlyStatements: quarterlyStatements ?? [],
|
|
440
455
|
financialCurrency,
|
|
441
|
-
fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency },
|
|
456
|
+
fundamentals: { eps: trailingEps, epsCurrency: trailingEpsCurrency, financialCurrency: fundamentalsCurrency, marketCapCurrency },
|
|
457
|
+
quote: quoteCurrency ? { currency: quoteCurrency } : null,
|
|
442
458
|
} : null),
|
|
443
|
-
[financialCurrency, fundamentalsCurrency, quarterlyStatements, trailingEps, trailingEpsCurrency],
|
|
459
|
+
[financialCurrency, fundamentalsCurrency, marketCapCurrency, quarterlyStatements, quoteCurrency, trailingEps, trailingEpsCurrency],
|
|
444
460
|
);
|
|
445
461
|
const actionsLoader = useCallback((nextSymbol: string, nextExchange: string, forceRefresh: boolean) => {
|
|
446
462
|
if (!dataProvider?.getCorporateActions) throw new Error("Corporate actions source unavailable");
|
|
@@ -450,12 +466,22 @@ export function CorporateActionsView({
|
|
|
450
466
|
if (!dataProvider?.getAnalystResearch) return null;
|
|
451
467
|
return dataProvider.getAnalystResearch(nextSymbol, nextExchange, forceRefresh ? { cacheMode: "refresh" } : undefined);
|
|
452
468
|
}, [dataProvider, cloudSession.requestKey]);
|
|
469
|
+
// The earnings calendar's next report, which the rows are resolved against as `earnings` and ERN read it.
|
|
470
|
+
const calendarLoader = useCallback((nextSymbol: string, _exchange: string, forceRefresh: boolean) => (
|
|
471
|
+
loadEarningsCalendar(dataProvider, [nextSymbol], { force: forceRefresh }).then((result) => calendarEventOf(result.events, nextSymbol))
|
|
472
|
+
), [dataProvider]);
|
|
453
473
|
const {
|
|
454
|
-
data:
|
|
474
|
+
data: recordedActions,
|
|
455
475
|
loading: actionsLoading,
|
|
456
476
|
error: actionsError,
|
|
457
477
|
reload: reloadActions,
|
|
458
478
|
} = useTickerRequest<CorporateActionsData>(actionsLoader, symbol, exchange);
|
|
479
|
+
const { data: calendarEvent, reload: reloadCalendar } = useTickerRequest(calendarLoader, symbol, exchange);
|
|
480
|
+
const today = newYorkDay();
|
|
481
|
+
const actionsData = useMemo(
|
|
482
|
+
() => (recordedActions ? withResolvedEarnings(recordedActions, calendarEvent, today) : recordedActions),
|
|
483
|
+
[calendarEvent, recordedActions, today],
|
|
484
|
+
);
|
|
459
485
|
const {
|
|
460
486
|
data: analystData,
|
|
461
487
|
loading: analystLoading,
|
|
@@ -471,7 +497,11 @@ export function CorporateActionsView({
|
|
|
471
497
|
? allRows.filter((row) => EARNINGS_STATUSES.has(row.status))
|
|
472
498
|
: allRows
|
|
473
499
|
), [allRows, variant]);
|
|
474
|
-
const unit = useMemo(() => sharedMetricCurrency(rows), [rows]);
|
|
500
|
+
const unit = useMemo(() => sharedMetricCurrency(rows, variant === "earnings-estimates"), [rows, variant]);
|
|
501
|
+
const hasUnstatedCurrency = rows.some((row) =>
|
|
502
|
+
((row.qEps != null || row.annualEps != null) && !row.epsCurrency)
|
|
503
|
+
|| ((row.qRevenue != null || row.annualRevenue != null) && !row.revenueCurrency));
|
|
504
|
+
const currencyMeta = [unit, hasUnstatedCurrency ? "? currency unstated" : null].filter(Boolean).join(" · ");
|
|
475
505
|
const columns = useMemo(() => buildEventColumns(width, unit, variant), [width, unit, variant]);
|
|
476
506
|
const sourceNotice = useMemo(() => (
|
|
477
507
|
actionsLoading || analystLoading
|
|
@@ -506,7 +536,8 @@ export function CorporateActionsView({
|
|
|
506
536
|
const reload = useCallback(() => {
|
|
507
537
|
reloadActions();
|
|
508
538
|
reloadAnalyst();
|
|
509
|
-
|
|
539
|
+
reloadCalendar();
|
|
540
|
+
}, [reloadActions, reloadAnalyst, reloadCalendar]);
|
|
510
541
|
const openRow = openRowId
|
|
511
542
|
? rows.find((row) => row.id === openRowId) ?? null
|
|
512
543
|
: null;
|
|
@@ -593,6 +624,19 @@ export function CorporateActionsView({
|
|
|
593
624
|
return { text: row.status, color: colors.textBright, attributes: TextAttributes.BOLD };
|
|
594
625
|
case "period":
|
|
595
626
|
return { text: row.period, color: colors.textDim };
|
|
627
|
+
case "eps":
|
|
628
|
+
case "revenue": {
|
|
629
|
+
const eps = column.id === "eps";
|
|
630
|
+
const value = eps ? row.qEps ?? row.annualEps : row.qRevenue ?? row.annualRevenue;
|
|
631
|
+
const currency = eps ? row.epsCurrency : row.revenueCurrency;
|
|
632
|
+
const text = formatEventMetric(value, unit ? undefined : currency, eps ? "eps" : "revenue");
|
|
633
|
+
return { text: value != null && !currency ? `${text} ?` : text,
|
|
634
|
+
value: unit && currency ? value : undefined, color: colors.textDim };
|
|
635
|
+
}
|
|
636
|
+
case "surprise":
|
|
637
|
+
return { text: row.status === "Earnings" ? row.value : "-", value: row.surprisePercent, color: row.status === "Earnings" ? toneColor(row.tone) : colors.textDim };
|
|
638
|
+
case "growth":
|
|
639
|
+
return { text: row.estimateGrowthMetric && row.value !== "-" ? `${row.value} ${row.estimateGrowthMetric === "eps" ? "EPS" : "rev"}` : "-", color: row.estimateGrowthMetric ? toneColor(row.tone) : colors.textDim };
|
|
596
640
|
case "qEps":
|
|
597
641
|
return { text: formatEventMetric(row.qEps, unit ? undefined : row.epsCurrency, "eps"), color: colors.textDim };
|
|
598
642
|
case "qRevenue":
|
|
@@ -648,7 +692,8 @@ export function CorporateActionsView({
|
|
|
648
692
|
detailScrollRef={detailScrollRef}
|
|
649
693
|
rootWidth={width}
|
|
650
694
|
rootHeight={height}
|
|
651
|
-
rootBefore={<ChartTableHeader width={width} height={height} tableRows={rows.length} tableColumns={columns}
|
|
695
|
+
rootBefore={<ChartTableHeader width={width} height={height} tableRows={rows.length} tableColumns={columns}
|
|
696
|
+
query={variant === "earnings-estimates" && currencyMeta ? <QueryBar width={width} meta={currencyMeta} /> : undefined} />}
|
|
652
697
|
onRootKeyDown={handleKeyDown}
|
|
653
698
|
columns={columns}
|
|
654
699
|
items={rows}
|
|
@@ -656,6 +701,10 @@ export function CorporateActionsView({
|
|
|
656
701
|
sortDirection="desc"
|
|
657
702
|
getItemKey={eventRowKey}
|
|
658
703
|
renderCell={renderCell}
|
|
704
|
+
getExportMetadata={variant === "earnings-estimates" ? () => [
|
|
705
|
+
...(unit ? [["Currency", unit]] : []),
|
|
706
|
+
...(hasUnstatedCurrency ? [["?", "Currency unstated"]] : []),
|
|
707
|
+
] : undefined}
|
|
659
708
|
selectedTextOverridesCellColor
|
|
660
709
|
getRowBackgroundColor={rowBackground}
|
|
661
710
|
emptyStateTitle={loading
|
|
@@ -179,7 +179,9 @@ function estimateValue(pair: EstimatePair): string {
|
|
|
179
179
|
* with the fundamentals, the figure `ticker` prints as EPS (TTM).
|
|
180
180
|
*/
|
|
181
181
|
export type EventFinancials = Pick<TickerFinancials, "quarterlyStatements" | "financialCurrency"> & {
|
|
182
|
-
fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency"> | null;
|
|
182
|
+
fundamentals?: Pick<NonNullable<TickerFinancials["fundamentals"]>, "eps" | "epsCurrency" | "financialCurrency" | "marketCapCurrency"> | null;
|
|
183
|
+
/** The listing's currency, which says whether an unlabelled EPS can be in the statements' (fundamentalsCurrency). */
|
|
184
|
+
quote?: Pick<NonNullable<TickerFinancials["quote"]>, "currency"> | null;
|
|
183
185
|
};
|
|
184
186
|
|
|
185
187
|
/**
|
|
@@ -5,6 +5,8 @@ import type {
|
|
|
5
5
|
TickerFinancials,
|
|
6
6
|
} from "../../../types/financials";
|
|
7
7
|
import { formatEventMetric } from "./event-model";
|
|
8
|
+
import { loadEarningsCalendar } from "../earnings/data/cache";
|
|
9
|
+
import { calendarEventOf, newYorkDay, withResolvedEarnings } from "../shared/earnings-calendar";
|
|
8
10
|
|
|
9
11
|
function metricFormat(currencyKey: "epsCurrency" | "revenueCurrency", kind: "eps" | "revenue") {
|
|
10
12
|
return (value: unknown, row: Record<string, unknown>) => {
|
|
@@ -42,14 +44,20 @@ function settledError(result: PromiseSettledResult<unknown>): string | null {
|
|
|
42
44
|
return result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Corporate actions, analyst research and financials together; one failing
|
|
49
|
+
* leaves the others. The earnings rows are resolved against the earnings
|
|
50
|
+
* calendar, so the next report reads as `earnings` and ERN give it.
|
|
51
|
+
*/
|
|
52
|
+
export async function loadEventSources(provider: DataProvider, symbol: string, today = newYorkDay()): Promise<EventSources> {
|
|
53
|
+
const [actions, estimates, financials, calendar] = await Promise.allSettled([
|
|
48
54
|
provider.getCorporateActions?.(symbol, "") ?? null,
|
|
49
55
|
provider.getAnalystResearch?.(symbol, "") ?? null,
|
|
50
56
|
provider.getTickerFinancials(symbol, ""),
|
|
57
|
+
loadEarningsCalendar(provider, [symbol]),
|
|
51
58
|
]);
|
|
52
|
-
const
|
|
59
|
+
const event = calendar.status === "fulfilled" ? calendarEventOf(calendar.value.events, symbol) : null;
|
|
60
|
+
const actionsData = actions.status === "fulfilled" && actions.value ? withResolvedEarnings(actions.value, event, today) : null;
|
|
53
61
|
const estimatesData = estimates.status === "fulfilled" ? estimates.value : null;
|
|
54
62
|
const financialsData = financials.status === "fulfilled" ? financials.value : null;
|
|
55
63
|
return {
|
|
@@ -159,7 +159,8 @@ export const researchModule: PluginModule = {
|
|
|
159
159
|
paneId: "relative-valuation",
|
|
160
160
|
label: "Relative Valuation",
|
|
161
161
|
description: "Compare valuation and operating metrics across peers.",
|
|
162
|
-
|
|
162
|
+
// The words an analyst asks for a comp table by.
|
|
163
|
+
keywords: ["relative", "valuation", "comps", "peers", "rv", "comp table", "comparable companies", "peer comparison", "competitors"],
|
|
163
164
|
shortcut: { prefix: "RV", argPlaceholder: "tickers", argKind: "ticker-list" },
|
|
164
165
|
canCreate: (context, options) => !!(options?.symbols?.length || options?.arg || context.activeTicker),
|
|
165
166
|
createInstance: (context, options) => {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What RV's figures run through, as its footer says it under the text table,
|
|
3
|
+
* in the pane and in a capture: the statement period the trailing figures
|
|
4
|
+
* reach, and the basis of each column that is not trailing twelve months.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface DatedPeer {
|
|
8
|
+
symbol: string;
|
|
9
|
+
date: string | null | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `2026-06-30`, or with peers dated apart, the date most of them share and
|
|
14
|
+
* each other peer's own: `2026-06-30 (LRN 2026-03-31)`. Null when none is dated.
|
|
15
|
+
*/
|
|
16
|
+
export function peerDatesText(peers: readonly DatedPeer[]): string | null {
|
|
17
|
+
const dated = peers.flatMap(({ symbol, date }) => (date ? [{ symbol, date: date.slice(0, 10) }] : []));
|
|
18
|
+
if (dated.length === 0) return null;
|
|
19
|
+
const counts = new Map<string, number>();
|
|
20
|
+
for (const { date } of dated) counts.set(date, (counts.get(date) ?? 0) + 1);
|
|
21
|
+
const [common] = [...counts].sort(([leftDate, left], [rightDate, right]) => right - left || rightDate.localeCompare(leftDate))[0]!;
|
|
22
|
+
const others = dated.filter(({ date }) => date !== common).map(({ symbol, date }) => `${symbol} ${date}`);
|
|
23
|
+
return others.length > 0 ? `${common} (${others.join(", ")})` : common;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface BasisRow {
|
|
27
|
+
symbol: string;
|
|
28
|
+
fundamentalsThrough?: string | null;
|
|
29
|
+
revenueGrowth?: number | null;
|
|
30
|
+
revenueGrowthQuarter?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** `fundamentals through 2026-06-30`, the newest statement period of each peer's trailing figures; null when none is dated. */
|
|
34
|
+
export function fundamentalsThroughText(rows: readonly BasisRow[]): string | null {
|
|
35
|
+
const through = peerDatesText(rows.map(({ symbol, fundamentalsThrough }) => ({ symbol, date: fundamentalsThrough })));
|
|
36
|
+
return through ? `fundamentals through ${through}` : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `fundamentals through 2026-06-30` and the quarter the revenue growth column
|
|
41
|
+
* compares with a year earlier, named by the column's header where the table
|
|
42
|
+
* is read: `, Q REV% for that quarter` when it is the one the fundamentals run
|
|
43
|
+
* through for every peer, else on its own (`Q REV%: quarter to 2026-03-31`).
|
|
44
|
+
* P/E, EV/S, FCF yield and the operating margin are trailing twelve months
|
|
45
|
+
* through that date; the forward P/E says forward in its own header.
|
|
46
|
+
*/
|
|
47
|
+
export function fundamentalsBasisParts(rows: readonly BasisRow[], revenueGrowthHeader: string): string[] {
|
|
48
|
+
const through = fundamentalsThroughText(rows);
|
|
49
|
+
const growing = rows.filter((row) => row.revenueGrowth != null);
|
|
50
|
+
const quarter = peerDatesText(growing.map(({ symbol, revenueGrowthQuarter }) => ({ symbol, date: revenueGrowthQuarter })));
|
|
51
|
+
if (through && quarter && growing.every((row) => row.revenueGrowthQuarter === row.fundamentalsThrough)) {
|
|
52
|
+
return [`${through}, ${revenueGrowthHeader} for that quarter`];
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
...(through ? [through] : []),
|
|
56
|
+
...(quarter ? [`${revenueGrowthHeader}: quarter to ${quarter}`] : []),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
@@ -4,12 +4,14 @@ import type { TickerFinancials } from "../../../types/financials";
|
|
|
4
4
|
import { isUsListingExchange, parsePublicTickerKey } from "../../../utils/exchanges";
|
|
5
5
|
import { errorMessage } from "../../../utils/errors";
|
|
6
6
|
import { formatNumber } from "../../../utils/format";
|
|
7
|
+
import { formatPriceEarnings, NOT_MEANINGFUL } from "../../../utils/price-earnings";
|
|
7
8
|
import { fetchScreen } from "../equity-screener/client";
|
|
8
9
|
import { refusedLock } from "../shared/cloud-denied";
|
|
9
10
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
10
11
|
import { loadShortInterest } from "../short-interest/client";
|
|
11
12
|
import { buildShortWatchRow } from "../short-interest/watch-model";
|
|
12
13
|
import { analystTargetCurrency, formatAnalystPrice, recommendationTotal } from "./analyst-model";
|
|
14
|
+
import { peerDatesText } from "./relative-valuation-basis";
|
|
13
15
|
|
|
14
16
|
/** The columns `--columns` adds to RV's report, each read from the path its own function uses. */
|
|
15
17
|
export const EXTRA_COLUMN_IDS = ["shortInterest", "nextEarnings", "target", "grossMargin", "evEbitda"] as const;
|
|
@@ -70,6 +72,12 @@ interface ColumnSpec {
|
|
|
70
72
|
sourceKey: string;
|
|
71
73
|
asOfKey: string;
|
|
72
74
|
label: string;
|
|
75
|
+
/**
|
|
76
|
+
* What the column's figures are as of, for the report's footer, from the
|
|
77
|
+
* dates of its cells (`fiscal year to 2025-12-31`). A column dated only by
|
|
78
|
+
* when it was read (targets, the earnings calendar) has none.
|
|
79
|
+
*/
|
|
80
|
+
basis?: (dates: string) => string;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
|
|
@@ -78,6 +86,7 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
|
|
|
78
86
|
columns: [{ key: "shortInterestPercentFloat", header: "Short % Float", align: "right", format: percent }],
|
|
79
87
|
sourceKey: "shortInterestSource",
|
|
80
88
|
asOfKey: "shortInterestAsOf",
|
|
89
|
+
basis: (dates) => `settlement ${dates}`,
|
|
81
90
|
},
|
|
82
91
|
nextEarnings: {
|
|
83
92
|
label: "Next Earnings",
|
|
@@ -93,7 +102,7 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
|
|
|
93
102
|
target: {
|
|
94
103
|
label: "Avg Target",
|
|
95
104
|
columns: [
|
|
96
|
-
{ key: "targetMean", header: "Avg Target", align: "right", unitKey: "targetCurrency",
|
|
105
|
+
{ key: "targetMean", header: "Avg Target", align: "right", unitKey: "targetCurrency", unitColumn: true,
|
|
97
106
|
format: (value, row) => (typeof value === "number" ? formatAnalystPrice(value, typeof row.targetCurrency === "string" ? row.targetCurrency : undefined) : "-") },
|
|
98
107
|
{ key: "analystCount", header: "Analysts", align: "right", format: (value) => (typeof value === "number" ? String(value) : "-") },
|
|
99
108
|
],
|
|
@@ -105,12 +114,17 @@ const COLUMN_SPECS: Record<ExtraColumnId, ColumnSpec> = {
|
|
|
105
114
|
columns: [{ key: "grossMarginPercent", header: "Gross Margin", align: "right", format: percent }],
|
|
106
115
|
sourceKey: "grossMarginSource",
|
|
107
116
|
asOfKey: "grossMarginAsOf",
|
|
117
|
+
// The screener stores the fiscal year's margin (its FY GROSS MGN%), dated by the year's end.
|
|
118
|
+
basis: (dates) => `fiscal year to ${dates}`,
|
|
108
119
|
},
|
|
109
120
|
evEbitda: {
|
|
110
121
|
label: "EV/EBITDA",
|
|
111
|
-
|
|
122
|
+
// N/M over an EV or EBITDA at or below zero, as EQS shows it.
|
|
123
|
+
columns: [{ key: "evEbitda", header: "EV/EBITDA", align: "right", format: (value, row) => (typeof value === "number" ? formatNumber(value, 1)
|
|
124
|
+
: row.evEbitdaNotMeaningful ? formatPriceEarnings(NOT_MEANINGFUL) : "-") }],
|
|
112
125
|
sourceKey: "evEbitdaSource",
|
|
113
126
|
asOfKey: "evEbitdaAsOf",
|
|
127
|
+
basis: (dates) => `statements to ${dates}`,
|
|
114
128
|
},
|
|
115
129
|
};
|
|
116
130
|
|
|
@@ -130,6 +144,20 @@ export function extraColumns(ids: readonly ExtraColumnId[]): { text: HeadlessPan
|
|
|
130
144
|
};
|
|
131
145
|
}
|
|
132
146
|
|
|
147
|
+
/**
|
|
148
|
+
* The footer's word on each added column dated by its figures: `Gross Margin:
|
|
149
|
+
* fiscal year to 2025-12-31`, beside the TTM columns' `fundamentals through`.
|
|
150
|
+
*/
|
|
151
|
+
export function extraColumnsBasis(ids: readonly ExtraColumnId[], rows: readonly HeadlessPaneRow[]): string[] {
|
|
152
|
+
return ids.flatMap((id) => {
|
|
153
|
+
const spec = COLUMN_SPECS[id];
|
|
154
|
+
const valueKey = spec.columns[0]!.key;
|
|
155
|
+
const dates = spec.basis && peerDatesText(rows.filter((row) => row[valueKey] != null)
|
|
156
|
+
.map((row) => ({ symbol: String(row.symbol), date: typeof row[spec.asOfKey] === "string" ? row[spec.asOfKey] as string : null })));
|
|
157
|
+
return dates ? [`${spec.label}: ${spec.basis!(dates)}`] : [];
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
133
161
|
/** Whether a symbol is a US listing, which FINRA short interest, the earnings calendar and the screener cover. */
|
|
134
162
|
function usListing(symbol: string): string | null {
|
|
135
163
|
const { symbol: bare, exchange } = parsePublicTickerKey(symbol);
|
|
@@ -273,6 +301,7 @@ async function loadScreenerFields(ids: readonly ExtraColumnId[], symbols: readon
|
|
|
273
301
|
if (wanted.includes("evEbitda")) {
|
|
274
302
|
const multiple = metricFields(row.metrics.evToEbitda);
|
|
275
303
|
if (multiple) Object.assign(fields, { evEbitda: multiple.value, evEbitdaSource: multiple.source, evEbitdaAsOf: multiple.asOf });
|
|
304
|
+
else if (row.metrics.evToEbitda?.notMeaningful) Object.assign(fields, { evEbitda: null, evEbitdaNotMeaningful: row.metrics.evToEbitda.reason ?? true });
|
|
276
305
|
}
|
|
277
306
|
}
|
|
278
307
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HeadlessPaneContext } from "../../../types/headless";
|
|
2
|
+
import type { QueryEntry } from "../../../market-data/result-types";
|
|
2
3
|
import { MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
|
|
3
4
|
import { fxLegForCurrency } from "../../../market-data/coordinator/fx-legs";
|
|
4
5
|
import { resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
@@ -33,6 +34,12 @@ export type ConvertedCapRow<Row extends CapRow> = Row & {
|
|
|
33
34
|
|
|
34
35
|
const usable = (rate: number | undefined): rate is number => rate != null && Number.isFinite(rate) && rate > 0;
|
|
35
36
|
|
|
37
|
+
/** A loaded rate as a `UsdRate`: with its source's observation time and when it was read, from the entry it came in. */
|
|
38
|
+
export function usdRateFromEntry(rate: number, entry: Pick<QueryEntry<number>, "asOf" | "fetchedAt"> | null | undefined): UsdRate {
|
|
39
|
+
const time = (value: number | null | undefined) => (value != null && Number.isFinite(value) ? new Date(value).toISOString() : null);
|
|
40
|
+
return { rate, asOf: time(entry?.asOf), retrievedAt: time(entry?.fetchedAt) };
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
function olderOf(left: string | null, right: string | null): string | null {
|
|
37
44
|
if (!left || !right) return left ?? right;
|
|
38
45
|
return Date.parse(left) <= Date.parse(right) ? left : right;
|
|
@@ -105,13 +112,14 @@ function rateTimeText(asOf: string): string {
|
|
|
105
112
|
|
|
106
113
|
/**
|
|
107
114
|
* The status line's note naming each rate and its date:
|
|
108
|
-
* `market caps in USD at EUR/USD 1.1612 (2026-10-13 21:00 UTC)
|
|
115
|
+
* `market caps in USD at EUR/USD 1.1612 (2026-10-13 21:00 UTC)`; the pane
|
|
116
|
+
* names its column instead, `MCAP USD at …`.
|
|
109
117
|
*/
|
|
110
|
-
export function conversionNote(target: string, conversions: readonly CapConversion[]): string | null {
|
|
118
|
+
export function conversionNote(target: string, conversions: readonly CapConversion[], subject = `market caps in ${target}`): string | null {
|
|
111
119
|
if (conversions.length === 0) return null;
|
|
112
120
|
const rates = conversions.map((conversion) => `${conversionPairText(conversion)} (${conversion.asOf ? rateTimeText(conversion.asOf)
|
|
113
121
|
: conversion.retrievedAt ? `retrieved ${rateTimeText(conversion.retrievedAt)}` : "time not stated"})`);
|
|
114
|
-
return
|
|
122
|
+
return `${subject} at ${rates.join(", ")}`;
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
/**
|
|
@@ -134,8 +142,7 @@ export async function loadFxLegs(
|
|
|
134
142
|
const entry = await coordinator.loadFxRate(currency);
|
|
135
143
|
const rate = resolveEntryValue(entry);
|
|
136
144
|
if (!usable(rate ?? undefined)) throw new Error(entry.error?.message ?? "no usable rate");
|
|
137
|
-
|
|
138
|
-
legs.set(currency, { rate: rate!, asOf: time(entry.asOf), retrievedAt: time(entry.fetchedAt) });
|
|
145
|
+
legs.set(currency, usdRateFromEntry(rate!, entry));
|
|
139
146
|
} catch (error) {
|
|
140
147
|
failed.push({ currency, reason: errorMessage(error) });
|
|
141
148
|
}
|
|
@@ -4,12 +4,17 @@ import { EXTENDED_SESSION_LABELS, type ExtendedSession } from "../../../market-d
|
|
|
4
4
|
import { formatCompact, formatCurrency, formatLevelPercent, formatNumber, formatPercent, formatPercentRaw } from "../../../utils/format";
|
|
5
5
|
import { loadHeadlessFinancials, loadHeadlessSymbols } from "../shared/headless-market-data";
|
|
6
6
|
import { quoteFreshnessFields } from "../shared/report-freshness";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
RELATIVE_VALUATION_STALE_FUNDAMENTALS_NOTICE, RELATIVE_VALUATION_STALE_QUOTE_NOTICE, relativeValuationValues, type RelativeValuationGapKey,
|
|
9
|
+
} from "./relative-valuation-model";
|
|
8
10
|
import { paneSchemas } from "./headless-schema";
|
|
9
11
|
import { isRecord } from "../../../utils/guards";
|
|
12
|
+
import { errorMessage } from "../../../utils/errors";
|
|
13
|
+
import { notATickerSymbol } from "../../../sources/provider-errors";
|
|
10
14
|
import { resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
11
15
|
import { pickPeers, peerMethodTitle, type PeerSelection } from "./relative-valuation-peers";
|
|
12
|
-
import { extraColumns, loadExtraColumns, parseExtraColumns } from "./relative-valuation-columns";
|
|
16
|
+
import { extraColumns, extraColumnsBasis, loadExtraColumns, parseExtraColumns } from "./relative-valuation-columns";
|
|
17
|
+
import { fundamentalsBasisParts } from "./relative-valuation-basis";
|
|
13
18
|
import { conversionNote, convertMarketCaps, loadFxLegs } from "./relative-valuation-currency";
|
|
14
19
|
|
|
15
20
|
const price = (value: unknown, row: Record<string, unknown>) => (
|
|
@@ -17,6 +22,12 @@ const price = (value: unknown, row: Record<string, unknown>) => (
|
|
|
17
22
|
);
|
|
18
23
|
const percent = (value: unknown) => value == null ? "-" : formatPercentRaw(Number(value));
|
|
19
24
|
|
|
25
|
+
/** The reason a row lacks the figure under `key` ("no TTM FCF"), which exports move to their status column. */
|
|
26
|
+
function gapText(row: Record<string, unknown>, key: RelativeValuationGapKey): string {
|
|
27
|
+
const gaps = row.gaps as Partial<Record<RelativeValuationGapKey, string>> | undefined;
|
|
28
|
+
return gaps?.[key] ?? "-";
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
/**
|
|
21
32
|
* The pre-market, after-hours or overnight print and its move from the regular close in Last, for the peers that have
|
|
22
33
|
* one; "mid" when it is a bid and ask midpoint.
|
|
@@ -32,32 +43,60 @@ function extendedColumn(session: ExtendedSession): HeadlessPaneColumn {
|
|
|
32
43
|
};
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
/**
|
|
47
|
+
* An export's extended print: the price alone, in the row's currency, then its move from the close in a
|
|
48
|
+
* `<session> Chg%` column, blank for a peer whose print is from another session or that has none.
|
|
49
|
+
*/
|
|
50
|
+
function extendedExportColumns(session: ExtendedSession): HeadlessPaneColumn[] {
|
|
51
|
+
const label = EXTENDED_SESSION_LABELS[session];
|
|
52
|
+
return [
|
|
53
|
+
{ key: "extendedPrice", header: label, align: "right", unitKey: "currency",
|
|
54
|
+
format: (value, row) => row.extendedSession === session && typeof value === "number" ? price(value, row) : "-" },
|
|
55
|
+
{ key: "extendedChangePercent", header: `${label} Chg%`, align: "right",
|
|
56
|
+
format: (value, row) => row.extendedSession === session && typeof value === "number" ? percent(value) : "-" },
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const EXTENDED_SESSIONS = ["PRE", "POST", "OVERNIGHT"] as const;
|
|
61
|
+
|
|
62
|
+
/** Every session's columns, whichever peers have a print, so the header is the same before the open and after the close. */
|
|
63
|
+
const EXTENDED_EXPORT_COLUMNS = EXTENDED_SESSIONS.flatMap(extendedExportColumns);
|
|
64
|
+
|
|
65
|
+
// Last is in each peer's own currency: an export writes the number, and the row's `currency_or_unit` names its currency.
|
|
35
66
|
const QUOTE_COLUMNS: HeadlessPaneColumn[] = [
|
|
36
67
|
{ key: "symbol", header: "Ticker" },
|
|
37
|
-
{ key: "price", header: "Last", align: "right",
|
|
38
|
-
|
|
68
|
+
{ key: "price", header: "Last", align: "right", unitKey: "currency",
|
|
69
|
+
format: (value, row) => value == null ? gapText(row, "price") : price(value, row) },
|
|
70
|
+
{ key: "changePercent", header: "Chg%", align: "right", format: (value, row) => value == null ? gapText(row, "changePercent") : percent(value) },
|
|
39
71
|
];
|
|
40
72
|
|
|
73
|
+
/** The latest quarter's revenue against the same quarter a year earlier; the footer names the quarter. */
|
|
74
|
+
const REVENUE_GROWTH_HEADER = "Quarter Revenue YoY";
|
|
75
|
+
|
|
41
76
|
const VALUATION_COLUMNS: HeadlessPaneColumn[] = [
|
|
42
77
|
// In the currency each peer's cap is in, which exports write in the header or beside the figure.
|
|
43
|
-
{ key: "marketCap", header: "Mkt Cap", align: "right", unitKey: "marketCapCurrency",
|
|
44
|
-
format: (value, row) => value == null ? "
|
|
78
|
+
{ key: "marketCap", header: "Mkt Cap", align: "right", unitKey: "marketCapCurrency", unitColumn: true,
|
|
79
|
+
format: (value, row) => value == null ? gapText(row, "marketCap") : `${formatCompact(Number(value), { fixedDecimals: true })}${row.marketCapCurrency ? ` ${row.marketCapCurrency}` : ""}` },
|
|
45
80
|
...([ ["trailingPE", "P/E"], ["forwardPE", "Fwd P/E"] ] as const).map(([key, header]) => ({
|
|
46
81
|
key, header, align: "right" as const,
|
|
47
|
-
|
|
82
|
+
// The multiple as reported, else the comparable value itself, so an export reads the column as one of figures.
|
|
83
|
+
format: (value: unknown, row: Record<string, unknown>) => {
|
|
48
84
|
const values = row as Partial<ReturnType<typeof relativeValuationValues>>;
|
|
49
|
-
|
|
85
|
+
const multiple = values.notMeaningful?.[key] ? NOT_MEANINGFUL : values.reportedMultiples?.[key] ?? (typeof value === "number" ? value : null);
|
|
86
|
+
return multiple == null ? gapText(row, key) : formatPriceEarnings(multiple);
|
|
50
87
|
},
|
|
51
88
|
})),
|
|
52
|
-
...[["evSales", "EV/S"]].map(([key, header]) => ({
|
|
53
|
-
key
|
|
54
|
-
// A withdrawn EV says so rather than leaving a bare dash; the note under the table says why.
|
|
89
|
+
...([["evSales", "EV/S"]] as const).map(([key, header]) => ({
|
|
90
|
+
key, header, align: "right" as const,
|
|
91
|
+
// A withdrawn EV says so rather than leaving a bare dash, one at or below zero reads N/M; the note under the table says why.
|
|
55
92
|
format: (value: unknown, row: Record<string, unknown>) => value != null ? formatNumber(Number(value), 1)
|
|
56
|
-
: row
|
|
93
|
+
: (row as Partial<ReturnType<typeof relativeValuationValues>>).notMeaningful?.evSales ? formatPriceEarnings(NOT_MEANINGFUL)
|
|
94
|
+
: gapText(row, key),
|
|
57
95
|
})),
|
|
58
|
-
...[["fcfYield", "FCF Yield"], ["revenueGrowth",
|
|
59
|
-
key
|
|
60
|
-
format: (value: unknown) => value == null ?
|
|
96
|
+
...([["fcfYield", "FCF Yield"], ["revenueGrowth", REVENUE_GROWTH_HEADER], ["operatingMargin", "TTM Op Margin"]] as const).map(([key, header]) => ({
|
|
97
|
+
key, header, align: "right" as const,
|
|
98
|
+
format: (value: unknown, row: Record<string, unknown>) => value == null ? gapText(row, key)
|
|
99
|
+
: key === "revenueGrowth" ? formatPercent(Number(value)) : formatLevelPercent(Number(value)),
|
|
61
100
|
})),
|
|
62
101
|
];
|
|
63
102
|
|
|
@@ -76,7 +115,14 @@ async function choosePeers(symbols: readonly string[], ctx: HeadlessPaneContext)
|
|
|
76
115
|
throw new Error(`--peers picks the peers of one symbol, such as RV ${symbols[0] ?? "DUOL"} --peers; with several symbols, leave it out.`);
|
|
77
116
|
}
|
|
78
117
|
const subject = symbols[0]!;
|
|
79
|
-
|
|
118
|
+
const financials = await loadHeadlessFinancials(ctx, subject).catch((error: unknown) => {
|
|
119
|
+
ctx.signal.throwIfAborted();
|
|
120
|
+
// A word that is no ticker is said as `fn RV duolingo lrn` says it, which the CLI follows with the search to run.
|
|
121
|
+
if (notATickerSymbol(errorMessage(error))) throw error;
|
|
122
|
+
// Any other failure leaves the peers to what the symbol alone places (a curated core's member).
|
|
123
|
+
return null;
|
|
124
|
+
});
|
|
125
|
+
return pickPeers(subject, financials, ctx);
|
|
80
126
|
}
|
|
81
127
|
|
|
82
128
|
const PEER_SELECTION_SETTING = "peerSelection";
|
|
@@ -93,7 +139,7 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
93
139
|
? `Relative Valuation | ${selection.subject} and ${peerMethodTitle(selection)}`
|
|
94
140
|
: `Relative Valuation | ${(result?.symbols ?? symbols).join(", ")}`;
|
|
95
141
|
},
|
|
96
|
-
columns: [...QUOTE_COLUMNS, ...VALUATION_COLUMNS],
|
|
142
|
+
columns: [...QUOTE_COLUMNS, ...EXTENDED_EXPORT_COLUMNS, ...VALUATION_COLUMNS],
|
|
97
143
|
async expandArgument(args, ctx) {
|
|
98
144
|
if (args.options.peers !== true) return null;
|
|
99
145
|
const selection = await choosePeers(args.symbols, ctx);
|
|
@@ -137,23 +183,33 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
137
183
|
const reason = fx?.failed.find((failure) => failure.currency === resolveCurrencyUnit(currency).currency)?.reason;
|
|
138
184
|
return `No ${currency} rate${reason ? ` (${reason})` : ""}: ${left.join(", ")} ${left.length === 1 ? "keeps its" : "keep their"} market cap in ${currency}.`;
|
|
139
185
|
}) ?? [];
|
|
140
|
-
|
|
186
|
+
// The footer: the rates the caps were converted at, the statement period the figures run through, and the
|
|
187
|
+
// basis of each column that is not trailing twelve months.
|
|
188
|
+
const footnote = [
|
|
189
|
+
...capCurrency && converted ? [conversionNote(capCurrency, converted.conversions)] : [],
|
|
190
|
+
...fundamentalsBasisParts(rows, REVENUE_GROWTH_HEADER),
|
|
191
|
+
...extraColumnsBasis(extraIds, rows),
|
|
192
|
+
].filter(Boolean).join(" · ");
|
|
141
193
|
const notes = [
|
|
142
|
-
...selection ? [`Peers chosen automatically: ${selection.basis}. To choose your own:
|
|
194
|
+
...selection ? [`Peers chosen automatically: ${selection.basis}. To choose your own: gloom fn RV ${selection.subject} <tickers>.`] : [],
|
|
143
195
|
...extra.notes,
|
|
144
196
|
];
|
|
145
197
|
// Last and Chg% are the regular session; an extended print gets a column only when a peer has one.
|
|
146
|
-
|
|
147
|
-
const
|
|
198
|
+
// Exports keep every session's price and move, so their header is the same in every market state.
|
|
199
|
+
const extendedSessions = EXTENDED_SESSIONS.filter((session) => rows.some((row) => row.extendedSession === session));
|
|
200
|
+
const textColumns = [...QUOTE_COLUMNS, ...extendedSessions.map(extendedColumn), ...VALUATION_COLUMNS];
|
|
148
201
|
const added = extraIds.length > 0 ? extraColumns(extraIds) : null;
|
|
149
202
|
return {
|
|
150
203
|
rows, unavailableSymbols, errors, complete: unavailableSymbols.length === 0 && errors.length === 0,
|
|
151
204
|
...(selection ? { symbols } : {}),
|
|
152
|
-
|
|
153
|
-
|
|
205
|
+
columns: [...QUOTE_COLUMNS, ...EXTENDED_EXPORT_COLUMNS, ...VALUATION_COLUMNS, ...added?.export ?? []],
|
|
206
|
+
textColumns: [...textColumns, ...added?.text ?? []],
|
|
207
|
+
// Each peer's row names its ticker and the currency its prices are in.
|
|
208
|
+
rowSymbolKey: "symbol",
|
|
209
|
+
rowUnitKey: "currency",
|
|
154
210
|
...(notes.length > 0 ? { notes } : {}),
|
|
155
211
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
156
|
-
...(
|
|
212
|
+
...(footnote ? { freshness: { note: footnote } } : {}),
|
|
157
213
|
metadata: {
|
|
158
214
|
staleSymbols,
|
|
159
215
|
staleFundamentalsSymbols,
|
|
@@ -165,11 +221,13 @@ export const relativeValuationHeadless: HeadlessPaneDefinition<"rows"> = {
|
|
|
165
221
|
priceBasis: "price, change and changePercent are the regular session: after the close, its close and that close's move. A pre-market, after-hours or overnight print is in extendedPrice, extendedChange and extendedChangePercent, measured from that close (extendedMark when it is a bid and ask midpoint). marketCap, the P/E multiples, evSales (through the equity part of the enterprise value) and fcfYield are measured at that regular session price.",
|
|
166
222
|
fundamentalsBasis: "Provider multiples and operating metrics retain independent fundamentalsProvenance. Retrieval time does not establish the valuation date.",
|
|
167
223
|
notices: [
|
|
168
|
-
...rows.some((row) =>
|
|
224
|
+
...rows.some((row) => row.notMeaningful.trailingPE || row.notMeaningful.forwardPE
|
|
169
225
|
|| Object.values(row.reportedMultiples).some((value) => value != null && value <= 0)) ? [PRICE_EARNINGS_NOTICE] : [],
|
|
170
226
|
...rows.flatMap((row) => row.evSalesUnavailable ? [`${row.symbol}: ${row.evSalesUnavailable}`] : []),
|
|
227
|
+
...rows.flatMap((row) => row.evSalesNote ? [`${row.symbol}: ${row.evSalesNote}`] : []),
|
|
228
|
+
...rows.flatMap((row) => row.marketCapNote ? [`${row.symbol}: ${row.marketCapNote}`] : []),
|
|
171
229
|
],
|
|
172
|
-
multipleBasis: "Comparable P/E fields require a positive finite multiple over positive EPS; notMeaningful says why one is null, and reportedMultiples preserves the finite provider values.",
|
|
230
|
+
multipleBasis: "Comparable P/E fields require a positive finite multiple over positive EPS, and evSales an enterprise value above zero; notMeaningful says why one is null, and reportedMultiples preserves the finite provider P/E values.",
|
|
173
231
|
marketCapBasis: "Market caps retain their own currency and source. Fundamentals retrieval time is not a valuation date; quote timestamps do not date fallback caps.",
|
|
174
232
|
},
|
|
175
233
|
};
|