@gloom-sh/terminal 0.18.5 → 0.19.1
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
HeadlessRowsResult,
|
|
10
10
|
} from "../../../types/plugin";
|
|
11
11
|
import type { EarningsEvent } from "../../../types/data-provider";
|
|
12
|
-
import { addDays, boardReport, fallbackReport, MIN_AVERAGE_REPORTS, newYorkToday } from "./board-model";
|
|
12
|
+
import { addDays, boardReport, fallbackReport, MIN_AVERAGE_REPORTS, newYorkToday, type BoardReport } from "./board-model";
|
|
13
13
|
import { BOARD_WEEKS, boardRangeDays, boardRangeLabel, isBoardWeek, requestedBoardRange, type BoardRange } from "./calendar-model";
|
|
14
14
|
import { fetchEarningsCalendar, fetchEarningsHistory } from "./client";
|
|
15
15
|
import { loadEarningsCalendar } from "./data/cache";
|
|
@@ -104,6 +104,41 @@ function boardTickerNotice(symbol: string, board: ReadonlyArray<{ symbol: string
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/** Two dates this close are one report under the per-ticker calendar and the stored board. */
|
|
108
|
+
const SAME_REPORT_DAYS = 3;
|
|
109
|
+
const eventDay = (event: EarningsEvent) => event.earningsDate.toISOString().slice(0, 10);
|
|
110
|
+
const dayGap = (left: string, right: string) => Math.abs(Date.parse(`${left}T00:00:00Z`) - Date.parse(`${right}T00:00:00Z`)) / 86_400_000;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The stored board's own report for each named company whose per-ticker
|
|
114
|
+
* calendar still names a report gone by (META the week after it reported),
|
|
115
|
+
* read over those days as `ERN --date` reads them: the estimate it was
|
|
116
|
+
* measured against, its analysts, the actual, its basis and the cap, as the
|
|
117
|
+
* date board and `ERN <ticker>` show them. One the board does not have is
|
|
118
|
+
* left to the per-ticker calendar.
|
|
119
|
+
*/
|
|
120
|
+
async function storedPastReports(
|
|
121
|
+
events: readonly EarningsEvent[],
|
|
122
|
+
today: string,
|
|
123
|
+
context: HeadlessPaneContext,
|
|
124
|
+
deps: EarningsHeadlessDependencies,
|
|
125
|
+
): Promise<Map<EarningsEvent, BoardReport>> {
|
|
126
|
+
const past = events.filter((event) => eventDay(event) < today);
|
|
127
|
+
const found = new Map<EarningsEvent, BoardReport>();
|
|
128
|
+
if (!past.length) return found;
|
|
129
|
+
const from = addDays(past.map(eventDay).sort()[0]!, -SAME_REPORT_DAYS);
|
|
130
|
+
const symbols = [...new Set(past.map((event) => event.symbol))];
|
|
131
|
+
const payload = await deps.calendar({ from, to: addDays(today, -1), perDay: 0, symbols }, context)
|
|
132
|
+
.catch(logFailure("earnings.headless", "Read the board's past reports for named companies failed", null));
|
|
133
|
+
for (const event of past) {
|
|
134
|
+
const nearest = (payload?.reports ?? [])
|
|
135
|
+
.filter((report) => report.symbol === event.symbol && dayGap(report.date, eventDay(event)) <= SAME_REPORT_DAYS)
|
|
136
|
+
.sort((left, right) => dayGap(left.date, eventDay(event)) - dayGap(right.date, eventDay(event)))[0];
|
|
137
|
+
if (nearest) found.set(event, boardReport(nearest, NO_OWNERSHIP));
|
|
138
|
+
}
|
|
139
|
+
return found;
|
|
140
|
+
}
|
|
141
|
+
|
|
107
142
|
async function boardRows(args: HeadlessPaneLoadArgs, context: HeadlessPaneContext, deps: EarningsHeadlessDependencies, highlight?: string, range: BoardRange = ""): Promise<HeadlessRowsResult> {
|
|
108
143
|
const today = newYorkToday();
|
|
109
144
|
const named = args.symbols;
|
|
@@ -124,9 +159,10 @@ async function boardRows(args: HeadlessPaneLoadArgs, context: HeadlessPaneContex
|
|
|
124
159
|
const payload = cloud.status === "fulfilled" ? cloud.value : null;
|
|
125
160
|
const reports = (payload?.reports ?? []).map((report) => boardReport(report, NO_OWNERSHIP));
|
|
126
161
|
const covered = new Set(reports.map((report) => report.symbol));
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
162
|
+
const uncovered = fallback.status === "fulfilled" ? fallback.value.filter((event) => !covered.has(event.symbol)) : [];
|
|
163
|
+
// The days ahead leave out a report just gone by, which the stored board has as the date board shows it.
|
|
164
|
+
const stored = named.length && !range ? await storedPastReports(uncovered, today, context, deps) : new Map<EarningsEvent, BoardReport>();
|
|
165
|
+
const extra = uncovered.map((event) => stored.get(event) ?? fallbackReport(event, NO_OWNERSHIP, today));
|
|
130
166
|
const limit = Number(args.options.limit ?? 200);
|
|
131
167
|
const all = [...reports, ...extra].sort((left, right) => left.date.localeCompare(right.date) || (right.marketCap ?? 0) - (left.marketCap ?? 0));
|
|
132
168
|
const rows = all.slice(0, limit).map((report) => ({
|
|
@@ -2,40 +2,31 @@ import type { FundMember } from "../../../api-client/members";
|
|
|
2
2
|
import type { Portfolio, Watchlist } from "../../../types/ticker";
|
|
3
3
|
import { getSectorCollection } from "../sectors/sector-data";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/** Focused industries, named by the fund whose published holdings define the group. */
|
|
6
|
+
const INDUSTRIES = [
|
|
7
|
+
{ etf: "SOXX", name: "Semiconductors" },
|
|
8
|
+
{ etf: "XPH", name: "Pharmaceuticals" },
|
|
9
|
+
{ etf: "XBI", name: "Biotech" },
|
|
10
|
+
{ etf: "ITA", name: "Aerospace & defense" },
|
|
11
|
+
{ etf: "KRE", name: "Regional banks" },
|
|
12
|
+
{ etf: "IGV", name: "Software" },
|
|
13
|
+
{ etf: "XRT", name: "Retail" },
|
|
14
|
+
{ etf: "XHB", name: "Homebuilders" },
|
|
15
|
+
{ etf: "XOP", name: "Oil & gas exploration" },
|
|
16
|
+
] as const;
|
|
11
17
|
|
|
12
|
-
/**
|
|
13
|
-
const SECTOR_OF_FUND: Readonly<Record<string, readonly string[]>> = {
|
|
14
|
-
XLK: ["Information Technology"],
|
|
15
|
-
XLV: ["Health Care"],
|
|
16
|
-
XLF: ["Financials"],
|
|
17
|
-
XLY: ["Consumer Discretionary"],
|
|
18
|
-
// The holdings file says "Communication"; GICS's own name is the longer one.
|
|
19
|
-
XLC: ["Communication", "Communication Services"],
|
|
20
|
-
XLI: ["Industrials"],
|
|
21
|
-
XLP: ["Consumer Staples"],
|
|
22
|
-
XLE: ["Energy"],
|
|
23
|
-
XLU: ["Utilities"],
|
|
24
|
-
XLRE: ["Real Estate"],
|
|
25
|
-
XLB: ["Materials"],
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/** Which companies ERN lists: the market's, the user's, one portfolio or watchlist, or one sector fund's. */
|
|
18
|
+
/** Which companies ERN lists: the market, the user's lists, or a fund's holdings. */
|
|
29
19
|
export type EarningsScope =
|
|
30
20
|
| { kind: "all" }
|
|
31
21
|
| { kind: "mine" }
|
|
32
22
|
| { kind: "portfolio" | "watchlist"; id: string; name: string }
|
|
33
|
-
| { kind: "
|
|
23
|
+
| { kind: "fund"; etf: string; name: string };
|
|
34
24
|
|
|
35
25
|
export interface EarningsScopeOption {
|
|
36
26
|
value: string;
|
|
37
27
|
label: string;
|
|
38
28
|
description?: string;
|
|
29
|
+
group?: string;
|
|
39
30
|
scope: EarningsScope;
|
|
40
31
|
}
|
|
41
32
|
|
|
@@ -47,30 +38,28 @@ export const MINE_SCOPE = "mine";
|
|
|
47
38
|
const kindNote = (name: string, kind: "Portfolio" | "Watchlist") =>
|
|
48
39
|
name.toLowerCase().includes(kind.toLowerCase()) ? undefined : kind;
|
|
49
40
|
|
|
50
|
-
/**
|
|
51
|
-
* The choices of ERN's Names filter, in menu order: the market, every held or
|
|
52
|
-
* watched name, each portfolio and watchlist, then SECT's sector funds, each
|
|
53
|
-
* named with its fund ("Technology (XLK)").
|
|
54
|
-
*/
|
|
41
|
+
/** Personal lists, broad sectors and focused industries stay separate in the Names menu. */
|
|
55
42
|
export function earningsScopeOptions(collections: { portfolios: readonly Portfolio[]; watchlists: readonly Watchlist[] }): EarningsScopeOption[] {
|
|
56
43
|
return [
|
|
57
44
|
{ value: ALL_SCOPE, label: "All", scope: { kind: "all" } },
|
|
58
45
|
{ value: MINE_SCOPE, label: "Mine", description: "Portfolios and watchlists", scope: { kind: "mine" } },
|
|
59
46
|
...collections.portfolios.map((portfolio): EarningsScopeOption => ({
|
|
60
|
-
value: `portfolio:${portfolio.id}`, label: portfolio.name, description: kindNote(portfolio.name, "Portfolio"),
|
|
47
|
+
value: `portfolio:${portfolio.id}`, label: portfolio.name, description: kindNote(portfolio.name, "Portfolio"), group: "Your lists",
|
|
61
48
|
scope: { kind: "portfolio", id: portfolio.id, name: portfolio.name },
|
|
62
49
|
})),
|
|
63
50
|
...collections.watchlists.map((watchlist): EarningsScopeOption => ({
|
|
64
|
-
value: `watchlist:${watchlist.id}`, label: watchlist.name, description: kindNote(watchlist.name, "Watchlist"),
|
|
51
|
+
value: `watchlist:${watchlist.id}`, label: watchlist.name, description: kindNote(watchlist.name, "Watchlist"), group: "Your lists",
|
|
65
52
|
scope: { kind: "watchlist", id: watchlist.id, name: watchlist.name },
|
|
66
53
|
})),
|
|
67
|
-
...getSectorCollection("sectors").items.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
54
|
+
...getSectorCollection("sectors").items.map((sector): EarningsScopeOption => ({
|
|
55
|
+
// Keep the saved sector choice when switching to that fund's own holdings.
|
|
56
|
+
value: `sector:${sector.etf}`, label: `${sector.name} (${sector.etf})`, group: "Sectors",
|
|
57
|
+
scope: { kind: "fund", etf: sector.etf, name: sector.name },
|
|
58
|
+
})),
|
|
59
|
+
...INDUSTRIES.map((industry): EarningsScopeOption => ({
|
|
60
|
+
value: `industry:${industry.etf}`, label: `${industry.name} (${industry.etf})`, group: "Industries",
|
|
61
|
+
scope: { kind: "fund", etf: industry.etf, name: industry.name },
|
|
62
|
+
})),
|
|
74
63
|
];
|
|
75
64
|
}
|
|
76
65
|
|
|
@@ -79,11 +68,10 @@ export function readEarningsScope(value: string, options: readonly EarningsScope
|
|
|
79
68
|
return options.find((option) => option.value === value) ?? options[0]!;
|
|
80
69
|
}
|
|
81
70
|
|
|
82
|
-
/** A
|
|
83
|
-
export function
|
|
84
|
-
const symbols = members
|
|
85
|
-
.filter((member) => member.symbol && member.sector && sectors.includes(member.sector))
|
|
71
|
+
/** A fund's companies, largest weight first; cash and other unlisted holdings have no symbol. */
|
|
72
|
+
export function fundSymbols(members: readonly FundMember[]): string[] {
|
|
73
|
+
const symbols = [...members]
|
|
86
74
|
.sort((left, right) => (right.weight ?? 0) - (left.weight ?? 0))
|
|
87
|
-
.
|
|
75
|
+
.flatMap((member) => member.symbol?.trim() ? [member.symbol.trim().toUpperCase()] : []);
|
|
88
76
|
return [...new Set(symbols)];
|
|
89
77
|
}
|
|
@@ -29,7 +29,7 @@ import { usePluginPaneState } from "../../runtime";
|
|
|
29
29
|
import { newYorkToday } from "../earnings/board-model";
|
|
30
30
|
import { ProWall, SignInWall } from "../cloud/auth-actions";
|
|
31
31
|
import { cachedUpcomingCalls, calendarAway, loadUpcomingCalls, statusOf, upcomingCutNotice, type UpcomingCallsResult } from "./data";
|
|
32
|
-
import {
|
|
32
|
+
import { callPeriod } from "./format";
|
|
33
33
|
import {
|
|
34
34
|
calendarDays,
|
|
35
35
|
calendarSections,
|
|
@@ -106,7 +106,7 @@ function renderCalendarCell(row: CalendarRow, column: CalendarColumn, now: numbe
|
|
|
106
106
|
return clock ? { text: clock, color: tone(colors.textDim) } : { text: MISSING, color: colors.textMuted };
|
|
107
107
|
}
|
|
108
108
|
case "period":
|
|
109
|
-
return { text:
|
|
109
|
+
return { text: callPeriod(call), color: tone(colors.textDim) };
|
|
110
110
|
case "away":
|
|
111
111
|
return { text: calendarAway(call, now), color: over ? colors.textMuted : colors.textBright };
|
|
112
112
|
}
|
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
} from "../../../api-client";
|
|
8
8
|
import { ApiRequestError } from "../../../api-client/errors";
|
|
9
9
|
import { listingAbroad, type IssuerListingParams, type ListingAbroad } from "../../../api-client/paths";
|
|
10
|
+
import { servedFromSavedCopy } from "../../../data/saved-copies";
|
|
10
11
|
import { areDifferentCompanies } from "../../../sources/sec-registrant";
|
|
11
12
|
import type { HeadlessPaneApiClient, PluginPersistence } from "../../../types/plugin";
|
|
12
13
|
import { shortDate } from "../earnings/format";
|
|
13
14
|
import { callCountdown, callDay, callTimeKnown, isUpcomingCall } from "./upcoming";
|
|
15
|
+
import { type CompanyFrom, readCompanyFrom } from "../../../types/company-from";
|
|
14
16
|
|
|
15
17
|
type EarningsCallsApiClient = Pick<
|
|
16
18
|
HeadlessPaneApiClient,
|
|
@@ -50,6 +52,8 @@ export interface EarningsCallsResult {
|
|
|
50
52
|
pending?: boolean;
|
|
51
53
|
/** The symbol is not one the SEC knows, so there is nothing to look for. */
|
|
52
54
|
unknownTicker?: boolean;
|
|
55
|
+
/** The company's home line the calls are read from, when not the listing asked. */
|
|
56
|
+
companyFrom?: CompanyFrom | null;
|
|
53
57
|
/** Maximum rows requested from the server, which supplies no total/hasMore. */
|
|
54
58
|
sourceLimit?: number;
|
|
55
59
|
/** The response filled its request; additional calls may exist. */
|
|
@@ -216,8 +220,15 @@ export async function loadEarningsCallsWithClient(
|
|
|
216
220
|
...(offset > 0 ? { offset } : {}),
|
|
217
221
|
})
|
|
218
222
|
.then((payload) => {
|
|
219
|
-
|
|
220
|
-
const
|
|
223
|
+
// Calls the service read from the company's home line are the company's, whatever the line's own name.
|
|
224
|
+
const companyFrom = readCompanyFrom(payload.companyFrom);
|
|
225
|
+
const calls = companyFrom ? payload.calls ?? [] : listingCalls(payload.calls ?? [], abroad);
|
|
226
|
+
const value = {
|
|
227
|
+
calls,
|
|
228
|
+
pending: payload.pending === true,
|
|
229
|
+
unknownTicker: payload.unknownTicker === true,
|
|
230
|
+
...(companyFrom ? { companyFrom } : {}),
|
|
231
|
+
};
|
|
221
232
|
// A list with calls still being produced changes by the minute, so it
|
|
222
233
|
// is not worth keeping; a list of finished transcripts is.
|
|
223
234
|
const settled = !value.pending && calls.every((call) => call.hasTranscript);
|
|
@@ -250,14 +261,15 @@ export async function loadEarningsCallsWithClient(
|
|
|
250
261
|
allowExpired: true,
|
|
251
262
|
});
|
|
252
263
|
if (expired && errorStatus !== 401 && errorStatus !== 402 && errorStatus !== 403) {
|
|
253
|
-
|
|
264
|
+
// A command that shows it ends with when the copy was saved, not with the failure.
|
|
265
|
+
return servedFromSavedCopy({
|
|
254
266
|
...expired.value,
|
|
255
267
|
...sourceScope(expired.value.calls),
|
|
256
268
|
fetchedAt: expired.fetchedAt,
|
|
257
269
|
stale: true,
|
|
258
270
|
refreshError,
|
|
259
271
|
errorStatus,
|
|
260
|
-
};
|
|
272
|
+
}, expired.fetchedAt);
|
|
261
273
|
}
|
|
262
274
|
throw error;
|
|
263
275
|
})
|
|
@@ -356,11 +368,11 @@ export async function loadUpcomingCallsWithClient(
|
|
|
356
368
|
// Losing Pro access re-gates the calendar, as it does the list.
|
|
357
369
|
const expired = read(true);
|
|
358
370
|
if (expired && errorStatus !== 401 && errorStatus !== 402 && errorStatus !== 403) {
|
|
359
|
-
return {
|
|
371
|
+
return servedFromSavedCopy({
|
|
360
372
|
...upcomingFromCache(expired.value, expired.fetchedAt, true),
|
|
361
373
|
refreshError: error instanceof Error ? error.message : String(error),
|
|
362
374
|
errorStatus,
|
|
363
|
-
};
|
|
375
|
+
}, expired.fetchedAt);
|
|
364
376
|
}
|
|
365
377
|
throw error;
|
|
366
378
|
})
|
|
@@ -2,7 +2,7 @@ import type { CloudEarningsCallPayload } from "../../../api-client";
|
|
|
2
2
|
import { formatShortDate } from "../../../utils/datetime-format";
|
|
3
3
|
|
|
4
4
|
/** "FQ4 26" — fiscal period, compact enough for a table column. */
|
|
5
|
-
|
|
5
|
+
function formatPeriod(
|
|
6
6
|
fiscalYear: number | null,
|
|
7
7
|
fiscalQuarter: number | null,
|
|
8
8
|
): string {
|
|
@@ -36,8 +36,37 @@ export function formatTimestamp(seconds: number): string {
|
|
|
36
36
|
return `${minutes}:${String(remainder).padStart(2, "0")}`;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
type NamedCall = Pick<
|
|
40
|
+
CloudEarningsCallPayload,
|
|
41
|
+
"ticker" | "fiscalYear" | "fiscalQuarter" | "eventKind" | "eventLabel"
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
/** An event that is not an earnings call (a deal call, an investor day), shown by its label and never as a quarter. */
|
|
45
|
+
export function eventLabelOf(call: Omit<NamedCall, "ticker">): string | null {
|
|
46
|
+
return call.eventKind === "other" && call.eventLabel ? call.eventLabel : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fits the period column; the full label is in the detail title.
|
|
50
|
+
const SHORT_EVENT_LABELS: Record<string, string> = {
|
|
51
|
+
"M&A call": "M&A",
|
|
52
|
+
conference: "Conf",
|
|
53
|
+
"investor day": "Inv day",
|
|
54
|
+
"business update": "Update",
|
|
55
|
+
"trial results": "Trial",
|
|
56
|
+
"shareholder call": "Holders",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** The period column: "FQ4 26", or a short event label ("M&A", "Inv day"). */
|
|
60
|
+
export function callPeriod(call: Omit<NamedCall, "ticker">): string {
|
|
61
|
+
const label = eventLabelOf(call);
|
|
62
|
+
if (label) return SHORT_EVENT_LABELS[label] ?? label;
|
|
63
|
+
return formatPeriod(call.fiscalYear, call.fiscalQuarter);
|
|
64
|
+
}
|
|
65
|
+
|
|
39
66
|
/** Title for the detail stack: names the call so the body need not repeat it. */
|
|
40
|
-
export function callTitle(call:
|
|
67
|
+
export function callTitle(call: NamedCall): string {
|
|
68
|
+
const label = eventLabelOf(call);
|
|
69
|
+
if (label) return `${call.ticker} ${label}`;
|
|
41
70
|
const period = formatPeriod(call.fiscalYear, call.fiscalQuarter);
|
|
42
71
|
return period === "—" ? call.ticker : `${call.ticker} ${period}`;
|
|
43
72
|
}
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
type UpcomingCallsResult,
|
|
24
24
|
type UpcomingDays,
|
|
25
25
|
} from "./data";
|
|
26
|
-
import {
|
|
26
|
+
import { callPeriod, eventLabelOf } from "./format";
|
|
27
27
|
import type { MarketDay } from "../earnings/board-model";
|
|
28
28
|
import {
|
|
29
29
|
calendarDays,
|
|
@@ -46,6 +46,10 @@ import {
|
|
|
46
46
|
type TranscriptSection,
|
|
47
47
|
} from "./model";
|
|
48
48
|
import { REPORT_FILTER, REPORT_PAGE, REPORT_ROWS } from "../shared/headless-options";
|
|
49
|
+
import { companyDataNotes } from "../../../types/company-from";
|
|
50
|
+
import { savedCopyMark, unansweredRefreshError } from "../../../data/saved-copies";
|
|
51
|
+
|
|
52
|
+
const optionalErrors = (error: string | null) => error ? [error] : undefined;
|
|
49
53
|
|
|
50
54
|
const CALL_COLUMNS = [
|
|
51
55
|
{ key: "ticker", header: "Ticker" },
|
|
@@ -53,6 +57,7 @@ const CALL_COLUMNS = [
|
|
|
53
57
|
{ key: "callAt", header: "Call date" },
|
|
54
58
|
{ key: "fiscalYear", header: "FY", align: "right" as const },
|
|
55
59
|
{ key: "fiscalQuarter", header: "FQ", align: "right" as const },
|
|
60
|
+
{ key: "event", header: "Event" },
|
|
56
61
|
{ key: "status", header: "Status" },
|
|
57
62
|
{ key: "durationSeconds", header: "Seconds", align: "right" as const },
|
|
58
63
|
{ key: "wordCount", header: "Words", align: "right" as const },
|
|
@@ -124,6 +129,8 @@ function callRow(call: CloudEarningsCallPayload, now: number) {
|
|
|
124
129
|
callAt: call.callAt,
|
|
125
130
|
fiscalYear: call.fiscalYear,
|
|
126
131
|
fiscalQuarter: call.fiscalQuarter,
|
|
132
|
+
// A deal call, a conference, an investor day: not a quarter's earnings call.
|
|
133
|
+
event: eventLabelOf(call),
|
|
127
134
|
status: call.hasTranscript ? "transcribed" : callStatusLabel(call, now) || call.status,
|
|
128
135
|
upcoming,
|
|
129
136
|
in: upcoming ? callCountdown(call, now) : null,
|
|
@@ -167,9 +174,12 @@ function projectEarningsCallsHeadless(
|
|
|
167
174
|
columns: CALL_COLUMNS,
|
|
168
175
|
rows,
|
|
169
176
|
...(next ? { textRows: rows.filter((row) => row.id !== next.id) } : {}),
|
|
170
|
-
errors: result
|
|
177
|
+
errors: optionalErrors(unansweredRefreshError(result)),
|
|
171
178
|
...(sourceLimitReached || result.pending ? { complete: false } : {}),
|
|
179
|
+
...(result.companyFrom ? { notes: companyDataNotes(result.companyFrom) } : {}),
|
|
172
180
|
metadata: {
|
|
181
|
+
// A list served from a saved copy ends with when it was saved, instead of its refresh failure.
|
|
182
|
+
...savedCopyMark(result),
|
|
173
183
|
...(next ? { notices: [nextCallNotice(next, now)], nextCall: { id: next.id, callAt: next.callAt, timeKnown: callTimeKnown(next), in: callCountdown(next, now) } } : {}),
|
|
174
184
|
fetchedAt: result.fetchedAt,
|
|
175
185
|
stale: result.stale,
|
|
@@ -208,9 +218,11 @@ function transcriptResult(
|
|
|
208
218
|
return {
|
|
209
219
|
columns: TRANSCRIPT_COLUMNS,
|
|
210
220
|
rows,
|
|
211
|
-
errors: calls
|
|
221
|
+
errors: optionalErrors(unansweredRefreshError(calls)),
|
|
212
222
|
...(calls.stale || calls.pending ? { complete: false } : {}),
|
|
223
|
+
...(calls.companyFrom ? { notes: companyDataNotes(calls.companyFrom) } : {}),
|
|
213
224
|
metadata: {
|
|
225
|
+
...savedCopyMark(calls),
|
|
214
226
|
stale: calls.stale,
|
|
215
227
|
callListFetchedAt: calls.fetchedAt,
|
|
216
228
|
callListPending: calls.pending ?? false,
|
|
@@ -219,6 +231,7 @@ function transcriptResult(
|
|
|
219
231
|
company: transcript.companyName,
|
|
220
232
|
fiscalYear: transcript.fiscalYear,
|
|
221
233
|
fiscalQuarter: transcript.fiscalQuarter,
|
|
234
|
+
event: eventLabelOf(transcript),
|
|
222
235
|
callAt: transcript.callAt,
|
|
223
236
|
durationSeconds: transcript.durationSeconds,
|
|
224
237
|
wordCount: transcript.wordCount,
|
|
@@ -259,7 +272,8 @@ function projectCalendar(
|
|
|
259
272
|
timeKnown: callTimeKnown(call),
|
|
260
273
|
local: callLocalClock(call),
|
|
261
274
|
newYork: callNewYorkClock(call),
|
|
262
|
-
period:
|
|
275
|
+
period: callPeriod(call),
|
|
276
|
+
event: eventLabelOf(call),
|
|
263
277
|
fiscalYear: call.fiscalYear,
|
|
264
278
|
fiscalQuarter: call.fiscalQuarter,
|
|
265
279
|
in: calendarAway(call, now),
|
|
@@ -278,10 +292,11 @@ function projectCalendar(
|
|
|
278
292
|
columns: CALENDAR_COLUMNS,
|
|
279
293
|
textColumns: sameClock ? CALENDAR_TEXT_COLUMNS.filter((column) => column.key !== "local") : CALENDAR_TEXT_COLUMNS,
|
|
280
294
|
rows,
|
|
281
|
-
errors: result
|
|
295
|
+
errors: optionalErrors(unansweredRefreshError(result)),
|
|
282
296
|
...(result.stale || result.truncated ? { complete: false } : {}),
|
|
283
297
|
empty: "No calls on the calendar these days.",
|
|
284
298
|
metadata: {
|
|
299
|
+
...savedCopyMark(result),
|
|
285
300
|
view: "upcoming",
|
|
286
301
|
fetchedAt: result.fetchedAt,
|
|
287
302
|
stale: result.stale,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
CloudEarningsTranscriptPayload,
|
|
4
4
|
CloudTranscriptTurnPayload,
|
|
5
5
|
} from "../../../api-client";
|
|
6
|
+
import { eventLabelOf } from "./format";
|
|
6
7
|
import { splitParagraphs } from "./prose";
|
|
7
8
|
|
|
8
9
|
export type TranscriptSection =
|
|
@@ -210,7 +211,11 @@ export function findCallForQuarter(
|
|
|
210
211
|
): CloudEarningsCallPayload | null {
|
|
211
212
|
const sorted = sortCallsNewest(calls);
|
|
212
213
|
const token = requestedQuarter.trim().toUpperCase().replace(/[\s_-]+/g, "");
|
|
213
|
-
|
|
214
|
+
// The latest earnings call: a deal call or an investor day held after it is
|
|
215
|
+
// not the quarter's call, unless the company has nothing else.
|
|
216
|
+
if (!token || token === "LATEST") {
|
|
217
|
+
return sorted.find((call) => !eventLabelOf(call)) ?? sorted[0] ?? null;
|
|
218
|
+
}
|
|
214
219
|
|
|
215
220
|
const quarterFirst = token.match(/^(?:F?Q)?([1-4])(?:\/?(\d{2}|\d{4}))?$/);
|
|
216
221
|
if (quarterFirst) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type QueryBarView,
|
|
20
20
|
type StatItem,
|
|
21
21
|
} from "../../../components";
|
|
22
|
+
import { useCompanyDataFooter } from "../../../components/layout/pane/status-footer";
|
|
22
23
|
import { handleRefreshKey } from "../../../components/data-table/table-pane";
|
|
23
24
|
import { AudioPlayer, useCanPlayAudio } from "../../../components/ui";
|
|
24
25
|
import { useShortcut } from "../../../react/input";
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
loadTranscript,
|
|
50
51
|
statusOf,
|
|
51
52
|
} from "./data";
|
|
52
|
-
import { callTitle, formatCallDate, formatDuration,
|
|
53
|
+
import { callPeriod, callTitle, eventLabelOf, formatCallDate, formatDuration, formatSentiment } from "./format";
|
|
53
54
|
import { findCallForQuarter, findInLines, MIN_FIND_LENGTH, turnParagraphs } from "./model";
|
|
54
55
|
import { READER_TABS, TranscriptView, type ReaderTab, type TranscriptPlayback } from "./transcript-view";
|
|
55
56
|
import { PLAYBACK_RATES, SEEK_STEP_SECONDS, useCallPlayback } from "./use-call-playback";
|
|
@@ -101,7 +102,7 @@ function renderCell(call: CloudEarningsCallPayload, column: CallColumn, now: num
|
|
|
101
102
|
case "date":
|
|
102
103
|
return { text: formatCallDate(call.callAt), color: colors.textDim };
|
|
103
104
|
case "period":
|
|
104
|
-
return { text:
|
|
105
|
+
return { text: callPeriod(call) };
|
|
105
106
|
case "length":
|
|
106
107
|
return { text: formatDuration(call.durationSeconds), color: colors.textDim };
|
|
107
108
|
case "sentiment": {
|
|
@@ -277,6 +278,8 @@ function CallsListView({
|
|
|
277
278
|
const [listStatus, setListStatus] = useState<"idle" | "loading" | "loaded" | "error">(initialShelf ? "loaded" : "idle");
|
|
278
279
|
const [listError, setListError] = useState<{ message: string; status?: number } | null>(null);
|
|
279
280
|
const [stale, setStale] = useState(initialShelf?.stale === true);
|
|
281
|
+
// The company's home line the shelf is read from, for a receipt or a foreign line.
|
|
282
|
+
const [companyFrom, setCompanyFrom] = useState(initialShelf?.companyFrom ?? null);
|
|
280
283
|
const [listFetchedAt, setListFetchedAt] = useState(initialShelf?.fetchedAt ?? 0);
|
|
281
284
|
const listRequestVersion = useRef(0);
|
|
282
285
|
const [listCompletedRequest, setListCompletedRequest] = useState(0);
|
|
@@ -340,6 +343,7 @@ function CallsListView({
|
|
|
340
343
|
setNextOffset(result.calls.length);
|
|
341
344
|
setHasMore(result.sourceLimitReached === true);
|
|
342
345
|
setStale(result.stale);
|
|
346
|
+
setCompanyFrom(result.companyFrom ?? null);
|
|
343
347
|
setListFetchedAt(result.fetchedAt);
|
|
344
348
|
setListPending(result.pending === true && result.calls.length === 0);
|
|
345
349
|
setListError(
|
|
@@ -410,6 +414,7 @@ function CallsListView({
|
|
|
410
414
|
setListStatus(held ? "loaded" : "idle");
|
|
411
415
|
setListPending(held?.pending === true && held.calls.length === 0);
|
|
412
416
|
setStale(held?.stale === true);
|
|
417
|
+
setCompanyFrom(held?.companyFrom ?? null);
|
|
413
418
|
setListFetchedAt(held?.fetchedAt ?? 0);
|
|
414
419
|
setListError(null);
|
|
415
420
|
setLookup(null);
|
|
@@ -525,7 +530,8 @@ function CallsListView({
|
|
|
525
530
|
[
|
|
526
531
|
call.ticker,
|
|
527
532
|
call.companyName ?? "",
|
|
528
|
-
|
|
533
|
+
callPeriod(call),
|
|
534
|
+
eventLabelOf(call) ?? "",
|
|
529
535
|
formatCallDate(call.callAt),
|
|
530
536
|
]
|
|
531
537
|
.join(" ")
|
|
@@ -816,6 +822,8 @@ function CallsListView({
|
|
|
816
822
|
[focusSearch, scrollTranscriptBy, openSource, playerShown, playback.toggle, playback.seekBy],
|
|
817
823
|
);
|
|
818
824
|
|
|
825
|
+
// A receipt's or foreign line's calls are its company's, read from the home line.
|
|
826
|
+
useCompanyDataFooter(companyFrom, !readerOpen);
|
|
819
827
|
usePaneFooter(
|
|
820
828
|
footerId,
|
|
821
829
|
() => {
|
|
@@ -1020,7 +1028,7 @@ function CallsListView({
|
|
|
1020
1028
|
<AudioPlayer
|
|
1021
1029
|
src={playback.audio.url}
|
|
1022
1030
|
sourceKey={selected.id}
|
|
1023
|
-
label={`${callTitle(selected)} call recording`}
|
|
1031
|
+
label={`${callTitle(selected)}${eventLabelOf(selected) ? "" : " call"} recording`}
|
|
1024
1032
|
durationSeconds={playback.audio.durationSeconds}
|
|
1025
1033
|
rates={PLAYBACK_RATES}
|
|
1026
1034
|
rate={playback.rate}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
2
2
|
import type { DataTableColumn } from "../../../components";
|
|
3
3
|
import { colors } from "../../../theme/colors";
|
|
4
|
-
import { displayWidth } from "../../../utils/format";
|
|
4
|
+
import { displayWidth, MISSING_VALUE } from "../../../utils/format";
|
|
5
5
|
import { fetchEconCalendar } from "./calendar-source";
|
|
6
6
|
import { parseEconFigure } from "./figures";
|
|
7
7
|
import { isAllDayEvent, type EconEvent, type EconImpact } from "./types";
|
|
@@ -235,6 +235,51 @@ export function eventCountry(event: Pick<EconEvent, "country" | "event">): strin
|
|
|
235
235
|
return euroAreaMember(event) ?? event.country;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
/** What the feeds call the Fed's rate decision. */
|
|
239
|
+
const FED_RATE_DECISION_NAMES = new Set(["federal funds rate", "fed interest rate decision"]);
|
|
240
|
+
/** A figure that is a range (3.75-4.00%) rather than one level. */
|
|
241
|
+
const RANGE_FIGURE = /\d\s*[-\u2013]\s*\d/;
|
|
242
|
+
const FED_FUNDS_UPPER_BOUND = "Federal Funds Rate (upper bound)";
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A row's name as the calendar shows it. The Fed's decision is figured as one
|
|
246
|
+
* level, the top of the target range (4.00% for 3.75-4.00%), so it is named
|
|
247
|
+
* for that bound rather than read as the range or the effective rate; a row
|
|
248
|
+
* whose figures are the range keeps the feed's name. Whatever matches rows by
|
|
249
|
+
* name (alerts, the FRED history) reads the feed's own, `event.event`.
|
|
250
|
+
*/
|
|
251
|
+
export function econEventName(event: Pick<EconEvent, "country" | "event" | "actual" | "forecast" | "prior">): string {
|
|
252
|
+
if ((event.country !== "US" && event.country !== "USD") || !FED_RATE_DECISION_NAMES.has(event.event.trim().toLowerCase())) return event.event;
|
|
253
|
+
return [event.actual, event.forecast, event.prior].some((figure) => !!figure && RANGE_FIGURE.test(figure)) ? event.event : FED_FUNDS_UPPER_BOUND;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Letters and digits only, lower case: "Non-Farm" and "nonfarm" read alike. */
|
|
257
|
+
const compactName = (text: string) => text.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Whether a row's name carries every word of `query`, case, spaces and
|
|
261
|
+
* punctuation aside: `nonfarm` finds Non Farm Payrolls and Non-Farm
|
|
262
|
+
* Employment Change, `fed funds` the Federal Funds Rate. The name shown and the
|
|
263
|
+
* feed's own are both read.
|
|
264
|
+
*/
|
|
265
|
+
export function matchesEventSearch(event: Pick<EconEvent, "country" | "event" | "actual" | "forecast" | "prior">, query: string): boolean {
|
|
266
|
+
const words = query.split(/\s+/).map(compactName).filter(Boolean);
|
|
267
|
+
if (words.length === 0) return true;
|
|
268
|
+
const names = [event.event, econEventName(event)].map(compactName);
|
|
269
|
+
return names.some((name) => words.every((word) => name.includes(word)));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* A release still to come that has a prior and no forecast: the consensus is
|
|
274
|
+
* not in yet, which the calendar draws as a dash.
|
|
275
|
+
*/
|
|
276
|
+
export function awaitsConsensus(event: Pick<EconEvent, "actual" | "forecast" | "prior">): boolean {
|
|
277
|
+
return !event.actual?.trim() && !event.forecast?.trim() && !!event.prior?.trim();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** What a dash under Forecast means on a row still to come. */
|
|
281
|
+
export const NO_CONSENSUS_NOTE = `${MISSING_VALUE} forecast = no consensus yet`;
|
|
282
|
+
|
|
238
283
|
/** A national euro-area release matches EU (and G7, as EU) and its own member's code. */
|
|
239
284
|
export function matchesCountry(event: Pick<EconEvent, "country" | "event">, filter: CountryFilter): boolean {
|
|
240
285
|
const member = euroAreaMember(event);
|
|
@@ -376,6 +421,8 @@ export function subscribeCalendar(listener: () => void): () => void {
|
|
|
376
421
|
}
|
|
377
422
|
|
|
378
423
|
export interface EconCalendarLoadResult extends EconCalendarCacheEntry {
|
|
424
|
+
/** `stale-fallback`: the copy an earlier read saved, answering because its refresh failed. */
|
|
425
|
+
source?: "cache" | "network" | "stale-fallback";
|
|
379
426
|
refreshError?: string;
|
|
380
427
|
}
|
|
381
428
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { apiClient, type CloudEconEventPayload } from "../../../api-client";
|
|
2
2
|
import { ApiRequestError, isPermanentClientError } from "../../../api-client/errors";
|
|
3
|
+
import { withPercentUnits } from "./figures";
|
|
3
4
|
import type { EconEvent } from "./types";
|
|
4
5
|
|
|
5
6
|
function toEconEvent(event: CloudEconEventPayload): EconEvent {
|
|
@@ -9,9 +10,10 @@ function toEconEvent(event: CloudEconEventPayload): EconEvent {
|
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/** The calendar, a percent indicator's figures in percent on every row (`withPercentUnits`). */
|
|
12
14
|
export async function fetchEconCalendar(): Promise<EconEvent[]> {
|
|
13
15
|
const events = await apiClient.getCloudEconomicCalendar();
|
|
14
|
-
return events.map(toEconEvent);
|
|
16
|
+
return withPercentUnits(events.map(toEconEvent));
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/** The stored calendar from a day, or why it was not read so the caller reads the current calendar instead. */
|
|
@@ -40,7 +42,7 @@ export async function fetchEconCalendarFrom(from: string): Promise<EconCalendarF
|
|
|
40
42
|
if (!payload || !Array.isArray(payload.events) || !(payload.storedFrom === null || /^\d{4}-\d{2}-\d{2}$/.test(String(payload.storedFrom)))) {
|
|
41
43
|
return { events: null, failure: "the server returned an unreadable calendar" };
|
|
42
44
|
}
|
|
43
|
-
return { events: payload.events.map(toEconEvent), storedFrom: payload.storedFrom };
|
|
45
|
+
return { events: withPercentUnits(payload.events.map(toEconEvent)), storedFrom: payload.storedFrom };
|
|
44
46
|
} catch (error) {
|
|
45
47
|
if (error instanceof ApiRequestError && error.status === 404) return { events: null, failure: null };
|
|
46
48
|
if (isPermanentClientError(error)) throw error;
|
|
@@ -31,7 +31,7 @@ import { Box, Text } from "../../../ui";
|
|
|
31
31
|
import { displayWidth, truncateToDisplayWidth } from "../../../utils/format";
|
|
32
32
|
import { useOpenTickerChoice } from "../shared/ticker-choice";
|
|
33
33
|
import { resolveFredMapping, projectFredHistory, fredHistoryUnits } from "./fred-series-map";
|
|
34
|
-
import { actualColor, CALENDAR_TIME_ZONE_LABEL, timeLabel } from "./calendar-model";
|
|
34
|
+
import { actualColor, CALENDAR_TIME_ZONE_LABEL, econEventName, timeLabel } from "./calendar-model";
|
|
35
35
|
import { hasNoStatedTime, isAllDayEvent, type EconEvent } from "./types";
|
|
36
36
|
|
|
37
37
|
interface EconDetailViewProps {
|
|
@@ -238,7 +238,7 @@ export function EconDetailView({ event, width, height, focused }: EconDetailView
|
|
|
238
238
|
const rows = useMemo<HistoryRow[]>(() => projected
|
|
239
239
|
.map((obs, index) => ({ date: obs.date, value: obs.value, change: index > 0 ? obs.value - projected[index - 1]!.value : null }))
|
|
240
240
|
.reverse(), [projected]);
|
|
241
|
-
const label = event
|
|
241
|
+
const label = econEventName(event);
|
|
242
242
|
const series = useMemo(() => [staticSeries(
|
|
243
243
|
projected.map((obs) => scalarPoint(new Date(`${obs.date}T00:00:00Z`), obs.value)),
|
|
244
244
|
{ id: "fred-history", label, color: colors.positive, calendarSpaced: true },
|