@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { HeadlessBundleResult, HeadlessPaneColumn } from "../../../types/headless";
|
|
2
2
|
import { formatPercent } from "../../../utils/format";
|
|
3
3
|
import type { DividendData } from "./client";
|
|
4
|
-
import { formatDividendYield } from "./view";
|
|
4
|
+
import { formatDividendYield, growthPaymentsDetail, PAYOUT_NOT_MEANINGFUL, trailingPaymentsText } from "./view";
|
|
5
5
|
import type { FundBondFacts } from "../../../api-client/fund-facts";
|
|
6
6
|
import { NOT_SEC_YIELD } from "../../../market-data/bond-funds";
|
|
7
7
|
|
|
@@ -38,10 +38,19 @@ function declaredPayDate(data: Pick<DividendData, "declared" | "metrics">, now =
|
|
|
38
38
|
*/
|
|
39
39
|
function incompleteNote(entry: DividendRankingEntry): string | null {
|
|
40
40
|
if ("error" in entry) return "not read";
|
|
41
|
-
const { historyError, summaryError } = entry.data;
|
|
42
|
-
|
|
41
|
+
const { historyError, summaryError, forwardFromFundamentals, metrics } = entry.data;
|
|
42
|
+
// A forward yield read from fundamentals in place of the summary is dated by the ex-date it annualizes.
|
|
43
|
+
const forward = summaryError && forwardFromFundamentals && metrics.forwardRateAsOf
|
|
44
|
+
? `; fwd yield from fundamentals, ex ${metrics.forwardRateAsOf}` : "";
|
|
45
|
+
if (historyError && summaryError) return `no cash history or summary${forward}`;
|
|
43
46
|
if (historyError) return "no cash history";
|
|
44
|
-
return summaryError ?
|
|
47
|
+
return summaryError ? `no summary${forward}` : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** How a row's trailing yield was counted, when its year holds fewer payments than its cadence pays; null otherwise. */
|
|
51
|
+
function yieldBasis(entry: DividendRankingEntry): string | null {
|
|
52
|
+
const counted = "data" in entry ? trailingPaymentsText(entry.data.metrics.trailingPayments, { cadence: true }) : null;
|
|
53
|
+
return counted ? `counts ${counted}` : null;
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
/** What a row is missing, in full, for the report's errors; null for a whole read. */
|
|
@@ -90,12 +99,17 @@ function rankDividendEntries(entries: readonly DividendRankingEntry[], now = new
|
|
|
90
99
|
forwardYield: roundedRatio(metrics.forwardYield),
|
|
91
100
|
...bonds ? secYieldFields(entry) : {},
|
|
92
101
|
payoutRatio: roundedRatio(metrics.payoutRatio),
|
|
102
|
+
payoutNotMeaningful: metrics.payoutNotMeaningful === true,
|
|
93
103
|
growth1Y: roundedRatio(metrics.growth1Y),
|
|
94
104
|
growth3Y: roundedRatio(metrics.growth3Y),
|
|
105
|
+
// Set when a year the figure compares is short of the cadence's payments: the figure is then n/a.
|
|
106
|
+
...metrics.growth1YPayments ? { growth1YPayments: growthPaymentsDetail(metrics.growth1YPayments) } : {},
|
|
107
|
+
...metrics.growth3YPayments ? { growth3YPayments: growthPaymentsDetail(metrics.growth3YPayments) } : {},
|
|
95
108
|
lastExDate: isoDay(metrics.lastExDividendDate),
|
|
96
109
|
payDate: declaredPayDate(data, now),
|
|
97
110
|
frequency: metrics.paymentFrequency,
|
|
98
111
|
note: incompleteNote(entry),
|
|
112
|
+
yieldBasis: yieldBasis(entry),
|
|
99
113
|
error: incompleteReason(entry),
|
|
100
114
|
},
|
|
101
115
|
};
|
|
@@ -111,7 +125,9 @@ function rankDividendEntries(entries: readonly DividendRankingEntry[], now = new
|
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
const ratio = (value: unknown) => formatDividendYield(typeof value === "number" ? value : null);
|
|
114
|
-
|
|
128
|
+
/** A growth figure, or n/a when one of its years is short (the row's `<key>Payments`). */
|
|
129
|
+
const growth = (key: "growth1Y" | "growth3Y") => (value: unknown, row: Record<string, unknown>) =>
|
|
130
|
+
typeof value !== "number" && typeof row[`${key}Payments`] === "string" ? "n/a" : formatPercent(typeof value === "number" ? value : undefined);
|
|
115
131
|
const day = (value: unknown) => typeof value === "string" && value ? value : "—";
|
|
116
132
|
|
|
117
133
|
const RANKING_COLUMNS: HeadlessPaneColumn[] = [
|
|
@@ -120,12 +136,15 @@ const RANKING_COLUMNS: HeadlessPaneColumn[] = [
|
|
|
120
136
|
description: "Cash distributions with ex-dates in the past twelve months over the reference price." },
|
|
121
137
|
{ key: "forwardYield", header: "Fwd yield", align: "right", format: ratio, stackPriority: 1,
|
|
122
138
|
description: "Indicated annual rate over the reference price; none once the dividend has stopped." },
|
|
123
|
-
{ key: "payoutRatio", header: "Payout", align: "right",
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
{ key: "payoutRatio", header: "Payout", align: "right",
|
|
140
|
+
// CSV and NDJSON say `n/m` where JSON has null; the text report draws `N/M` as P/E does.
|
|
141
|
+
format: (value, row) => row.payoutNotMeaningful === true ? PAYOUT_NOT_MEANINGFUL.toLowerCase() : ratio(value),
|
|
142
|
+
textFormat: (value, row) => row.payoutNotMeaningful === true ? PAYOUT_NOT_MEANINGFUL : ratio(value),
|
|
143
|
+
description: "Trailing twelve-month dividends over trailing twelve-month diluted EPS; N/M when that EPS is zero or negative." },
|
|
144
|
+
{ key: "growth1Y", header: "1Y growth", align: "right", format: growth("growth1Y"),
|
|
145
|
+
description: "Trailing twelve-month cash per share against a year earlier; n/a when either year holds fewer payments than the cadence pays." },
|
|
146
|
+
{ key: "growth3Y", header: "3Y CAGR", align: "right", format: growth("growth3Y"),
|
|
147
|
+
description: "Compound annual growth of trailing twelve-month cash per share over three years; n/a when either year holds fewer payments than the cadence pays." },
|
|
129
148
|
{ key: "lastExDate", header: "Last ex", format: day, shrink: false },
|
|
130
149
|
{ key: "payDate", header: "Pay date", format: day, shrink: false, stackPriority: 2,
|
|
131
150
|
description: "Pay date of the next declared distribution; past distributions carry ex-dates only." },
|
|
@@ -148,7 +167,11 @@ function rankingColumns(bonds: boolean): HeadlessPaneColumn[] {
|
|
|
148
167
|
|
|
149
168
|
/** Shown once a row is incomplete, so a missing figure never reads as a non-payer's. */
|
|
150
169
|
const NOTE_COLUMN: HeadlessPaneColumn = { key: "note", header: "Note", format: (value) => typeof value === "string" ? value : "",
|
|
151
|
-
description: "What the read of this ticker is missing: not read, no cash history (yields and growth), or no summary (forward yield, payout, pay date)." };
|
|
170
|
+
description: "What the read of this ticker is missing: not read, no cash history (yields and growth), or no summary (forward yield, payout, pay date); a forward yield read from fundamentals instead is dated by its ex-date." };
|
|
171
|
+
|
|
172
|
+
/** Shown once a regular payer's trailing year holds fewer payments than its cadence pays, which says nothing is missing from the read itself. */
|
|
173
|
+
const YIELD_BASIS_COLUMN: HeadlessPaneColumn = { key: "yieldBasis", header: "Yield basis", format: (value) => typeof value === "string" ? value : "",
|
|
174
|
+
description: "How many payments the trailing yield counts when a regular payer's year holds fewer than its cadence pays (a gap in its ex-dates)." };
|
|
152
175
|
|
|
153
176
|
/** `fn DVD A B C`: one table of every symbol, ranked by trailing yield. */
|
|
154
177
|
export function projectDividendRanking(entries: readonly DividendRankingEntry[], now = new Date()): HeadlessBundleResult {
|
|
@@ -167,7 +190,7 @@ export function projectDividendRanking(entries: readonly DividendRankingEntry[],
|
|
|
167
190
|
} : {}),
|
|
168
191
|
sections: [{
|
|
169
192
|
title: `Ranked by trailing yield; payout is over TTM diluted EPS${bonds ? `; a bond fund's yields are distribution yields, ${NOT_SEC_YIELD}` : ""}`,
|
|
170
|
-
columns: incomplete.length > 0 ? [...
|
|
193
|
+
columns: [...rankingColumns(bonds), ...incomplete.length > 0 ? [NOTE_COLUMN] : [], ...rows.some((row) => row.yieldBasis) ? [YIELD_BASIS_COLUMN] : []],
|
|
171
194
|
rows,
|
|
172
195
|
// Every figure is a percent; each exported row names its ticker and the time of the price its yields are over.
|
|
173
196
|
unit: "pct",
|
|
@@ -178,7 +201,7 @@ export function projectDividendRanking(entries: readonly DividendRankingEntry[],
|
|
|
178
201
|
rankedBy: "trailingYield",
|
|
179
202
|
symbols: entries.map((entry) => entry.symbol),
|
|
180
203
|
yieldMethod: "Cash distributions with ex-dates in the preceding 12 months, one payment per period for a regular cadence, divided by the reference share price, in the listing's own currency.",
|
|
181
|
-
payoutMethod: "Trailing twelve-month dividends over trailing twelve-month diluted EPS, as the source states it.",
|
|
204
|
+
payoutMethod: "Trailing twelve-month dividends over trailing twelve-month diluted EPS, as the source states it; N/M (not meaningful) when that EPS is zero or negative.",
|
|
182
205
|
growthMethod: "Trailing twelve-month cash per share now against one and three years before; 3Y is a compound annual rate.",
|
|
183
206
|
payDateMethod: "Pay date of the next declared distribution; past distributions carry ex-dates only.",
|
|
184
207
|
...bonds ? { secYieldMethod: `A bond fund's 30-day SEC yield as its sponsor publishes it, dated by secYieldAsOf, as a ratio like the other yields; its trailingYield and forwardYield are distribution yields, ${NOT_SEC_YIELD}.` } : {},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DividendLapse } from "../../../utils/forward-dividend";
|
|
2
|
-
import type { DistributionCadence } from "../../../utils/trailing-dividend";
|
|
2
|
+
import type { DistributionCadence, TrailingPayments } from "../../../utils/trailing-dividend";
|
|
3
3
|
|
|
4
4
|
export interface DividendPayment {
|
|
5
5
|
exDate: Date;
|
|
@@ -9,6 +9,14 @@ export interface DividendPayment {
|
|
|
9
9
|
amount: number;
|
|
10
10
|
currency: string;
|
|
11
11
|
type: "cash" | "special" | "stock" | "unknown";
|
|
12
|
+
/** The amount as the company declared it, in another currency than `currency` (BP's dollars beside the pence its London line paid). */
|
|
13
|
+
declared?: { amount: number; currency: string };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A short year a growth figure would compare: its payments, and the day it ends (absent for the trailing year). */
|
|
17
|
+
export interface GrowthPayments extends TrailingPayments {
|
|
18
|
+
/** YYYY-MM-DD; absent when the short year is the trailing one. */
|
|
19
|
+
yearTo?: string;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
export interface DividendMetrics {
|
|
@@ -16,13 +24,23 @@ export interface DividendMetrics {
|
|
|
16
24
|
forwardYield: number | null;
|
|
17
25
|
trailingRate: number | null;
|
|
18
26
|
forwardRate: number | null;
|
|
19
|
-
/** The ex-date of the distribution the forward rate annualizes, YYYY-MM-DD. */
|
|
27
|
+
/** The ex-date of the distribution the forward rate annualizes, YYYY-MM-DD; with `forwardBasis`, the latest of the year's payments. */
|
|
20
28
|
forwardRateAsOf: string | null;
|
|
29
|
+
/** `uneven`: the year's regular payments differ widely, so the forward rate is their total rather than one annualized (unevenRegularPayments). */
|
|
30
|
+
forwardBasis?: "uneven" | null;
|
|
21
31
|
/** Set once the dividend has stopped (lapsedDividend), when there is no forward rate or yield. */
|
|
22
32
|
lapse?: DividendLapse | null;
|
|
23
33
|
payoutRatio: number | null;
|
|
34
|
+
/** Trailing EPS is zero or negative, so a payout ratio says nothing: shown "N/M", never a ratio. */
|
|
35
|
+
payoutNotMeaningful?: boolean;
|
|
36
|
+
/** A regular payer's trailing year holds fewer payments than its cadence: the yield counts only these. */
|
|
37
|
+
trailingPayments?: TrailingPayments | null;
|
|
24
38
|
growth1Y: number | null;
|
|
25
39
|
growth3Y: number | null;
|
|
40
|
+
/** Set when one of the two years 1Y growth compares holds fewer payments than the cadence pays: no growth, and how many it holds. */
|
|
41
|
+
growth1YPayments?: GrowthPayments | null;
|
|
42
|
+
/** The same for the two years the 3Y rate compares. */
|
|
43
|
+
growth3YPayments?: GrowthPayments | null;
|
|
26
44
|
paymentFrequency: DistributionCadence | null;
|
|
27
45
|
lastExDividendDate: Date | null;
|
|
28
46
|
/** Announced ex-date after now, from the summary or a future-dated history record. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { DividendPayment } from "./types";
|
|
1
|
+
import type { DividendPayment, GrowthPayments } from "./types";
|
|
2
2
|
import type { ProjectedChartPoint } from "../../../components/chart/core/data";
|
|
3
3
|
import { calendarMonthsBefore } from "../../../utils/calendar-date";
|
|
4
|
-
import {
|
|
4
|
+
import { NOT_MEANINGFUL, formatPriceEarnings } from "../../../utils/price-earnings";
|
|
5
|
+
import { LATE_GRACE_DAYS, trailingCashAt, type TrailingPayments } from "../../../utils/trailing-dividend";
|
|
5
6
|
|
|
6
7
|
const DAY = 24 * 60 * 60 * 1000;
|
|
7
8
|
|
|
@@ -9,6 +10,30 @@ export function formatDividendYield(value: number | null): string {
|
|
|
9
10
|
return value != null && Number.isFinite(value) ? `${(value * 100).toFixed(2)}%` : "—";
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/** A payout over zero or negative earnings, written as a P/E over a loss is. */
|
|
14
|
+
export const PAYOUT_NOT_MEANINGFUL = formatPriceEarnings(NOT_MEANINGFUL);
|
|
15
|
+
|
|
16
|
+
/** How many payments a trailing yield counts, for a regular payer whose year holds fewer than its cadence pays: "9 of 12 payments". */
|
|
17
|
+
export function trailingPaymentsText(payments: TrailingPayments | null | undefined, { cadence = false }: { cadence?: boolean } = {}): string | null {
|
|
18
|
+
return payments ? `${payments.counted} of ${payments.expected} ${cadence ? `${payments.cadence} ` : ""}payments` : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** How many payments a growth figure's short year holds: "1 of 2 payments", naming an earlier year by its end. */
|
|
22
|
+
export function growthPaymentsDetail(payments: GrowthPayments): string {
|
|
23
|
+
return `${payments.counted} of ${payments.expected} payments${payments.yearTo ? ` in the year to ${payments.yearTo}` : ""}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** What a growth figure reads when one of its years is short: "n/a: 1 of 2 payments". */
|
|
27
|
+
export function growthPaymentsText(payments: GrowthPayments): string {
|
|
28
|
+
return `n/a: ${growthPaymentsDetail(payments)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A history served stale, dated by its read when the source gave it. */
|
|
32
|
+
export function staleHistoryText(fetchedAt: string | undefined): string {
|
|
33
|
+
const day = fetchedAt && /^\d{4}-\d{2}-\d{2}/.test(fetchedAt) ? fetchedAt.slice(0, 10) : null;
|
|
34
|
+
return `Stale cash history${day ? ` as of ${day}` : ""}; recent distributions may be missing.`;
|
|
35
|
+
}
|
|
36
|
+
|
|
12
37
|
/** Cash history does not contain historical share prices, so cannot establish historical yields. */
|
|
13
38
|
export function buildTrailingCashChartPoints(payments: DividendPayment[], now = new Date()): ProjectedChartPoint[] {
|
|
14
39
|
const sorted = payments.filter((payment) => payment.exDate <= now && Number.isFinite(payment.amount) && payment.amount > 0)
|
|
@@ -52,6 +77,8 @@ export interface DividendRow {
|
|
|
52
77
|
exDate: string;
|
|
53
78
|
amount: number;
|
|
54
79
|
currency: string;
|
|
80
|
+
/** The amount as the company declared it, in another currency (DividendPayment.declared). */
|
|
81
|
+
declared?: { amount: number; currency: string };
|
|
55
82
|
}
|
|
56
83
|
|
|
57
84
|
export function toDividendRows(payments: DividendPayment[]): DividendRow[] {
|
|
@@ -60,5 +87,6 @@ export function toDividendRows(payments: DividendPayment[]): DividendRow[] {
|
|
|
60
87
|
exDate: payment.exDate.toISOString().slice(0, 10),
|
|
61
88
|
amount: payment.amount,
|
|
62
89
|
currency: payment.currency,
|
|
90
|
+
...(payment.declared ? { declared: payment.declared } : {}),
|
|
63
91
|
}));
|
|
64
92
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* entry in the docs the app ships.
|
|
11
11
|
*/
|
|
12
12
|
import { DOCS_SITE } from "../../../docs/content";
|
|
13
|
+
import { ATTENTION_LISTED } from "../attention/listing";
|
|
13
14
|
import type { DocsTopic } from "../../../docs/topics";
|
|
14
15
|
import {
|
|
15
16
|
helpFunctionForPane,
|
|
@@ -25,7 +26,10 @@ const PANE_DOCS: Readonly<Record<string, DocsTopic>> = {
|
|
|
25
26
|
"twitter-feed": { kind: "function", code: "TWIT" },
|
|
26
27
|
// One article opened from a news list.
|
|
27
28
|
"news-story": { kind: "function", code: "N" },
|
|
28
|
-
|
|
29
|
+
// The ATTN page while it is listed. Until then the docs have none, so both
|
|
30
|
+
// panes open the page on Attention Counts, where their numbers come from.
|
|
31
|
+
"attention-trending": ATTENTION_LISTED ? { kind: "page", slug: "attention" } : { kind: "page", slug: "cloud-and-data" },
|
|
32
|
+
...(ATTENTION_LISTED ? {} : { attention: { kind: "page", slug: "cloud-and-data" } as const }),
|
|
29
33
|
// The reader itself: its ? goes back to the index.
|
|
30
34
|
docs: { kind: "home" },
|
|
31
35
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DoeSeriesRow, DoeTab } from "../../../api-client/doe";
|
|
2
2
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
3
|
-
import {
|
|
3
|
+
import { loadDoeBoard } from "./client";
|
|
4
4
|
import {
|
|
5
5
|
DOE_TABS,
|
|
6
6
|
DOE_UNIT_LABEL,
|
|
@@ -61,7 +61,8 @@ export function doeHeadless(defaultTab: DoeTab | "all"): HeadlessPaneDefinition<
|
|
|
61
61
|
"Five-year ranges read earlier years on the same calendar day.", "A range holds fewer than five years where a series is younger."] },
|
|
62
62
|
describe: "EIA weekly petroleum and natural gas storage",
|
|
63
63
|
async load(args) {
|
|
64
|
-
|
|
64
|
+
// Read afresh; a failed read answers from the board an earlier one saved, which the report says it is.
|
|
65
|
+
const data = (await loadDoeBoard(true)).payload;
|
|
65
66
|
// Each tab is dated by its own EIA report (petroleum Wednesday, gas Thursday);
|
|
66
67
|
// once the next one is a day overdue the tables have missed it. The basis
|
|
67
68
|
// names what Vs 1Y and Vs 5Y avg are measured against.
|
|
@@ -13,8 +13,10 @@ import {
|
|
|
13
13
|
usePaneTabs,
|
|
14
14
|
type ChartTableChart,
|
|
15
15
|
type DataTableColumn,
|
|
16
|
+
type PaneFooterSegment,
|
|
16
17
|
} from "../../../components";
|
|
17
18
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
19
|
+
import { savedCopyFooterSegment } from "../../../components/layout/pane/footer";
|
|
18
20
|
import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginPaneState } from "../../../public/react";
|
|
19
21
|
import { usePaneInstance } from "../../../state/app/context";
|
|
20
22
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
@@ -136,13 +138,21 @@ export function DoePane({ width, height, focused }: PaneProps) {
|
|
|
136
138
|
const { strip, rows: tabRows } = usePaneTabs({ tabs: [...DOE_TABS], activeValue: tab, onSelect: setTab, focused, dense: true });
|
|
137
139
|
useAutoRefresh(resource.updatedAt, resource.load);
|
|
138
140
|
usePaneRefreshKey(() => void resource.reload(), { focused });
|
|
141
|
+
// A refresh that failed shows the board an earlier read saved, and says when that copy is from.
|
|
142
|
+
const savedAt = resource.data?.savedAt ?? null;
|
|
139
143
|
usePaneNoticeFooter({ registrationId: "doe:notices", focused,
|
|
140
|
-
notices: [...(data?.gaps ?? []), ...(resource.data?.refreshError ? [resource.data.refreshError] : [])] });
|
|
144
|
+
notices: [...(data?.gaps ?? []), ...(resource.data?.refreshError && savedAt == null ? [resource.data.refreshError] : [])] });
|
|
141
145
|
// What Vs 1Y and Vs 5Y are measured against on this tab: the EIA's published figures for gas storage.
|
|
142
146
|
const basis = useMemo(() => data ? doeComparisonBasis(doeRows(data, tab)) : null, [data, tab]);
|
|
143
|
-
const info = useMemo(() =>
|
|
147
|
+
const info = useMemo(() => {
|
|
148
|
+
const segments: PaneFooterSegment[] = [
|
|
149
|
+
...(basis ? [{ id: "basis", parts: [{ text: basis, tone: "muted" as const }] }] : []),
|
|
150
|
+
...(savedAt != null ? [savedCopyFooterSegment("saved-copy", savedAt)] : []),
|
|
151
|
+
];
|
|
152
|
+
return segments.length ? segments : undefined;
|
|
153
|
+
}, [basis, savedAt]);
|
|
144
154
|
usePaneStatusFooter({ registrationId: "doe", loading: resource.loading, error: notAvailable ? null : resource.error,
|
|
145
|
-
stale: !!data && resource.data?.stale, info });
|
|
155
|
+
stale: !!data && !!resource.data?.stale && savedAt == null, info });
|
|
146
156
|
return (
|
|
147
157
|
<Box width={width} height={height} flexDirection="column">
|
|
148
158
|
{strip}
|
|
@@ -5,8 +5,7 @@ import type { EarningsEvent } from "../../../types/data-provider";
|
|
|
5
5
|
import type { EarningsDateStatus, EarningsEpsBasis, Quote } from "../../../types/financials";
|
|
6
6
|
import type { TickerRecord } from "../../../types/ticker";
|
|
7
7
|
import { earningsDateStatus, earningsEpsBasis, isProvisionalActual } from "../shared/earnings-report";
|
|
8
|
-
import {
|
|
9
|
-
import { coherentEarningsValue } from "../shared/earnings-estimate-basis";
|
|
8
|
+
import { calendarReport } from "../shared/earnings-calendar";
|
|
10
9
|
import { shortDate, surprise, weekday } from "./format";
|
|
11
10
|
|
|
12
11
|
/** A held name is in a portfolio; a watched one only in watchlists. */
|
|
@@ -149,33 +148,35 @@ export function boardReport(report: EarningsCalendarReport, owned: ReadonlyMap<s
|
|
|
149
148
|
};
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* A company the stored calendar does not carry (a listing abroad, a date past
|
|
153
|
+
* its horizon) from the per-ticker calendar, read as every earnings surface
|
|
154
|
+
* reads its report (shared/earnings-calendar): a date gone by is reported, and
|
|
155
|
+
* keeps a consensus only when it is that report's, not the next quarter's.
|
|
156
|
+
*/
|
|
153
157
|
export function fallbackReport(event: EarningsEvent, owned: ReadonlyMap<string, Ownership>, today: string): BoardReport {
|
|
154
|
-
const
|
|
155
|
-
const timing = event.timing === "BMO" ? "bmo" : event.timing === "AMC" ? "amc" : null;
|
|
158
|
+
const report = calendarReport(event, today);
|
|
156
159
|
// Estimates in another currency would read as dollars beside the rest.
|
|
157
160
|
const dollars = (field: "epsEstimate" | "revenueEstimate") => {
|
|
158
161
|
const currency = event.estimateBasis?.[field]?.currency;
|
|
159
|
-
return currency && currency !== "USD" ? null :
|
|
162
|
+
return currency && currency !== "USD" ? null : report[field];
|
|
160
163
|
};
|
|
161
164
|
const epsEstimate = dollars("epsEstimate");
|
|
162
165
|
return {
|
|
163
|
-
key: `${event.symbol}:${date}`,
|
|
166
|
+
key: `${event.symbol}:${report.date}`,
|
|
164
167
|
symbol: event.symbol,
|
|
165
168
|
name: event.name || event.symbol,
|
|
166
|
-
date,
|
|
167
|
-
timing,
|
|
169
|
+
date: report.date,
|
|
170
|
+
timing: report.timing,
|
|
168
171
|
expectedTiming: false,
|
|
169
|
-
|
|
170
|
-
dateStatus: calendarStatus(event, today),
|
|
172
|
+
dateStatus: report.dateStatus,
|
|
171
173
|
marketCap: null,
|
|
172
174
|
epsEstimate,
|
|
173
|
-
// The count
|
|
174
|
-
epsAnalysts: epsEstimate == null ? null :
|
|
175
|
+
// The count and the basis belong to the estimate beside them.
|
|
176
|
+
epsAnalysts: epsEstimate == null ? null : report.epsAnalysts,
|
|
175
177
|
epsActual: null,
|
|
176
178
|
provisional: false,
|
|
177
|
-
|
|
178
|
-
epsBasis: epsEstimate == null ? null : earningsEpsBasis(event.epsBasis),
|
|
179
|
+
epsBasis: epsEstimate == null ? null : report.epsBasis,
|
|
179
180
|
epsSurprise: null,
|
|
180
181
|
revenueEstimate: dollars("revenueEstimate"),
|
|
181
182
|
impliedMove: null,
|
|
@@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
2
2
|
import {
|
|
3
3
|
DataTableStackView,
|
|
4
4
|
PageStackView,
|
|
5
|
-
PaneStatusBody,
|
|
6
5
|
QueryBar,
|
|
7
6
|
usePaneFooter,
|
|
8
7
|
usePaneMenuItems,
|
|
@@ -62,8 +61,7 @@ import {
|
|
|
62
61
|
earningsScopeOptions,
|
|
63
62
|
MINE_SCOPE,
|
|
64
63
|
readEarningsScope,
|
|
65
|
-
|
|
66
|
-
sectorSymbols,
|
|
64
|
+
fundSymbols,
|
|
67
65
|
type EarningsScope,
|
|
68
66
|
} from "./scope";
|
|
69
67
|
|
|
@@ -78,9 +76,8 @@ const MARKET_PER_DAY = 200;
|
|
|
78
76
|
const MAX_SYMBOLS = 200;
|
|
79
77
|
|
|
80
78
|
const MARKET_SCOPE: EarningsScope = { kind: "all" };
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const sectorNames = (...args: Parameters<typeof sectorSymbols>) => sectorSymbols(...args).slice(0, MAX_SYMBOLS);
|
|
79
|
+
/** A fund's companies the board reads, its largest first. */
|
|
80
|
+
const fundNames = (members: Parameters<typeof fundSymbols>[0]) => fundSymbols(members).slice(0, MAX_SYMBOLS);
|
|
84
81
|
|
|
85
82
|
type Order = "cap" | "mine";
|
|
86
83
|
type BoardView = "table" | "calendar";
|
|
@@ -100,7 +97,7 @@ interface BoardData {
|
|
|
100
97
|
/**
|
|
101
98
|
* ERN without a ticker: the market's report days, largest companies first,
|
|
102
99
|
* with the implied move and each company's average move. With tickers, or the
|
|
103
|
-
* Names filter (Mine, a portfolio, a watchlist or a
|
|
100
|
+
* Names filter (Mine, a portfolio, a watchlist or a fund's holdings),
|
|
104
101
|
* the upcoming reports of those companies instead.
|
|
105
102
|
*/
|
|
106
103
|
export function EarningsBoard({ focused, width, height, scopedSymbols, highlight = null }: {
|
|
@@ -152,9 +149,7 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
152
149
|
() => earningsScopeOptions({ portfolios: config.portfolios, watchlists: config.watchlists }),
|
|
153
150
|
[config.portfolios, config.watchlists],
|
|
154
151
|
);
|
|
155
|
-
const
|
|
156
|
-
// The terminal keeps its Mine toggle, so a choice only the desktop offers reads as the market there.
|
|
157
|
-
const scopeOption = desktop || chosenScope.value === MINE_SCOPE ? chosenScope : scopeOptions[0]!;
|
|
152
|
+
const scopeOption = readEarningsScope(savedScope ?? (mineOnly ? MINE_SCOPE : ALL_SCOPE), scopeOptions);
|
|
158
153
|
const scoped = scopedSymbols.length > 0;
|
|
159
154
|
const scope = scoped ? MARKET_SCOPE : scopeOption.scope;
|
|
160
155
|
const view: BoardView = !desktop ? "table"
|
|
@@ -174,17 +169,17 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
174
169
|
.sort()
|
|
175
170
|
.join(",");
|
|
176
171
|
}, [scope, tickers]);
|
|
177
|
-
const
|
|
178
|
-
// A
|
|
172
|
+
const fund = scope.kind === "fund" ? scope : null;
|
|
173
|
+
// A fund's companies come with its holdings, which the loader reads; the others are known here.
|
|
179
174
|
const named = useMemo(() => {
|
|
180
175
|
const names = scoped ? [...scopedSymbols]
|
|
181
176
|
: scope.kind === "portfolio" || scope.kind === "watchlist" ? (collectionKey ? collectionKey.split(",") : [])
|
|
182
|
-
: scope.kind === "
|
|
177
|
+
: scope.kind === "fund" ? [] : [...owned.keys()];
|
|
183
178
|
return names.slice(0, MAX_SYMBOLS);
|
|
184
179
|
}, [collectionKey, owned, scope.kind, scoped, scopedSymbols]);
|
|
185
180
|
// Named companies also go to the per-ticker calendar, which covers listings the stored one does not;
|
|
186
|
-
//
|
|
187
|
-
const perTicker = !market && !
|
|
181
|
+
// fund scopes use the stored calendar, including members outside the S&P 500.
|
|
182
|
+
const perTicker = !market && !fund;
|
|
188
183
|
// The days ahead by default; a chosen week or day, past or ahead, for the market and for named companies alike.
|
|
189
184
|
const days = useMemo(() => (market || range ? boardRangeDays(range, today) : relativeDays(today)), [market, range, today]);
|
|
190
185
|
const weekOffset = boardRangeWeek(range, today);
|
|
@@ -196,13 +191,14 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
196
191
|
? { from: days[0]!.from, to: days.at(-1)!.to, perDay: market ? MARKET_PER_DAY : 0, symbols: named }
|
|
197
192
|
: { from: today, to: addDays(today, HORIZON_DAYS), perDay: 0, symbols: named },
|
|
198
193
|
[calendar, days, market, named, range, today, week]);
|
|
199
|
-
// A
|
|
200
|
-
const queryKey =
|
|
194
|
+
// A fund's read is named by the fund: its companies are known once its holdings are.
|
|
195
|
+
const queryKey = fund ? `${fund.etf}|${earningsCalendarKey(query)}` : earningsCalendarKey(query);
|
|
201
196
|
|
|
202
197
|
const loader = useCallback(async (force: boolean): Promise<BoardData> => {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
const holdings = fund ? await loadMembers(fund.etf, force) : null;
|
|
199
|
+
const names = holdings ? fundNames(holdings.payload.members) : named;
|
|
200
|
+
if (!market && names.length === 0) return { queryKey, reports: [], stale: !!holdings && (holdings.stale || holdings.payload.stale), refreshError: holdings?.refreshError ?? null };
|
|
201
|
+
const read = fund ? { ...query, symbols: names } : query;
|
|
206
202
|
const [cloud, fallback] = await Promise.allSettled([
|
|
207
203
|
loadEarningsBoard(read, force),
|
|
208
204
|
!perTicker ? Promise.resolve([] as EarningsEvent[]) : loadEarningsCalendar(dataProvider, names, { force }).then((result) => result.events
|
|
@@ -218,24 +214,24 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
218
214
|
return {
|
|
219
215
|
queryKey,
|
|
220
216
|
reports: [...reports, ...extra],
|
|
221
|
-
stale: cloud.status === "fulfilled" && cloud.value.stale,
|
|
217
|
+
stale: (!!holdings && (holdings.stale || holdings.payload.stale)) || (cloud.status === "fulfilled" && cloud.value.stale),
|
|
222
218
|
refreshError: cloud.status === "rejected"
|
|
223
219
|
? (cloud.reason instanceof Error ? cloud.reason.message : String(cloud.reason))
|
|
224
|
-
: cloud.value.refreshError,
|
|
220
|
+
: holdings?.refreshError ?? cloud.value.refreshError,
|
|
225
221
|
};
|
|
226
|
-
}, [calendar, dataProvider, market, named, owned, perTicker, query, queryKey, range,
|
|
222
|
+
}, [calendar, dataProvider, market, named, owned, perTicker, query, queryKey, range, fund, today]);
|
|
227
223
|
const resource = useAsyncResource(loader, {
|
|
228
224
|
keepPreviousData: true,
|
|
229
225
|
initialData: () => {
|
|
230
|
-
const holdings =
|
|
231
|
-
if (
|
|
232
|
-
const cached = cachedEarningsCalendar(
|
|
233
|
-
return cached ? { queryKey, reports: cached.payload.reports.map((report) => boardReport(report, owned)), stale:
|
|
226
|
+
const holdings = fund ? cachedMembers(fund.etf) : null;
|
|
227
|
+
if (fund && !holdings) return null;
|
|
228
|
+
const cached = cachedEarningsCalendar(holdings ? { ...query, symbols: fundNames(holdings.payload.members) } : query);
|
|
229
|
+
return cached ? { queryKey, reports: cached.payload.reports.map((report) => boardReport(report, owned)), stale: !!holdings?.payload.stale, refreshError: null } : null;
|
|
234
230
|
},
|
|
235
231
|
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const weekData = data
|
|
232
|
+
// A new fund or range never shows the previous scope while its holdings load or fail.
|
|
233
|
+
const data = resource.data?.queryKey === queryKey ? resource.data : null;
|
|
234
|
+
const weekData = data;
|
|
239
235
|
const rows = useMemo(() => boardRows(data?.reports ?? [], days, sort, order === "mine"), [data, days, order, sort]);
|
|
240
236
|
const reportRows = useMemo(() => rows.filter((row): row is BoardRow & { kind: "report" } => row.kind === "report"), [rows]);
|
|
241
237
|
const slotsPerRow = calendarSlotsPerRow(width);
|
|
@@ -336,16 +332,12 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
336
332
|
usePaneRefreshKey(() => void resource.reload(), { focused, enabled: calendar && !open });
|
|
337
333
|
|
|
338
334
|
const sortColumn = (Object.entries(SORTABLE).find(([, value]) => value === sort.column)?.[0]) ?? null;
|
|
339
|
-
const mineFilter: QueryBarFilter[] = scoped ? [] : [
|
|
340
|
-
{ id: "mine", kind: "toggle", label: "Mine", value: scope.kind === "mine", defaultValue: false, onChange: (mine: boolean) => setScope(mine ? MINE_SCOPE : ALL_SCOPE) },
|
|
341
|
-
];
|
|
342
|
-
// The desktop's Mine is one choice of the Names filter, beside each portfolio, watchlist and sector fund.
|
|
343
335
|
const namesFilter: QueryBarFilter[] = scoped ? [] : [{
|
|
344
336
|
id: "names",
|
|
345
337
|
label: "Names",
|
|
346
338
|
value: scopeOption.value,
|
|
347
339
|
defaultValue: ALL_SCOPE,
|
|
348
|
-
options: scopeOptions.map(({ value, label, description }) => ({ value, label, description })),
|
|
340
|
+
options: scopeOptions.map(({ value, label, description, group, scope }) => ({ value, label, description, group, short: !desktop && scope.kind === "fund" ? scope.etf : undefined })),
|
|
349
341
|
onChange: (value: string) => setScope(value),
|
|
350
342
|
}];
|
|
351
343
|
const orderView: QueryBarView<Order> = { value: order, options: ORDER_OPTIONS, onChange: setOrder };
|
|
@@ -371,7 +363,7 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
371
363
|
onChange: (value: string) => { setRange(value); setSelectedKey(null); },
|
|
372
364
|
};
|
|
373
365
|
// On the desktop the view picks the table or the week, and the order is a filter beside the names and the Range.
|
|
374
|
-
const queryBar = !desktop ? <QueryBar width={width} filters={[...
|
|
366
|
+
const queryBar = !desktop ? <QueryBar width={width} filters={[...namesFilter, rangeFilter]} view={orderView} /> : (
|
|
375
367
|
<QueryBar
|
|
376
368
|
width={width}
|
|
377
369
|
filters={[
|
|
@@ -431,41 +423,40 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
|
|
|
431
423
|
}
|
|
432
424
|
|
|
433
425
|
return (
|
|
434
|
-
<
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
</PaneStatusBody>
|
|
426
|
+
<DataTableStackView<BoardRow, BoardColumn>
|
|
427
|
+
focused={focused}
|
|
428
|
+
rootWidth={width}
|
|
429
|
+
rootHeight={height}
|
|
430
|
+
rootBefore={queryBar}
|
|
431
|
+
status={{ loading: resource.loading && !data, error: !data ? resource.error : null, subject: "the earnings calendar" }}
|
|
432
|
+
columns={columns}
|
|
433
|
+
items={rows}
|
|
434
|
+
getItemKey={(row) => row.key}
|
|
435
|
+
isNavigable={(row) => row.kind === "report"}
|
|
436
|
+
renderSectionHeader={renderBoardSection}
|
|
437
|
+
renderCell={renderCell}
|
|
438
|
+
getRowVersion={rowVersion}
|
|
439
|
+
selectedTextOverridesCellColor
|
|
440
|
+
sortColumnId={sortColumn}
|
|
441
|
+
sortDirection={sort.direction}
|
|
442
|
+
onHeaderClick={(columnId) => {
|
|
443
|
+
const column = SORTABLE[columnId as BoardColumn["id"]];
|
|
444
|
+
if (!column) return;
|
|
445
|
+
setSort((current) => ({ column, direction: current.column === column && current.direction === "desc" ? "asc" : column === "symbol" ? "asc" : "desc" }));
|
|
446
|
+
}}
|
|
447
|
+
selection={{
|
|
448
|
+
kind: "index",
|
|
449
|
+
selectedIndex: selected ? rows.indexOf(selected) : -1,
|
|
450
|
+
onChange: (_index, row) => { if (row.kind === "report") setSelectedKey(row.key); },
|
|
451
|
+
}}
|
|
452
|
+
onActivate={(row) => { if (row.kind === "report") setOpenKey(row.key); }}
|
|
453
|
+
onRootKeyDown={handleKeyDown}
|
|
454
|
+
detailOpen={!!open}
|
|
455
|
+
onBack={() => setOpenKey(null)}
|
|
456
|
+
detailTitle={open ? `${open.symbol} · ${open.name}` : undefined}
|
|
457
|
+
detailContent={history}
|
|
458
|
+
emptyStateTitle={emptyStateTitle}
|
|
459
|
+
/>
|
|
469
460
|
);
|
|
470
461
|
}
|
|
471
462
|
|