gloomberb 0.16.6 → 0.17.0
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/package.json +2 -1
- package/src/api-client/chat-pane-link.ts +47 -0
- package/src/api-client/chat.ts +9 -1
- package/src/api-client/data.ts +37 -1
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/europe-energy.ts +91 -0
- package/src/api-client/index.ts +6 -0
- package/src/api-client/letters.ts +41 -0
- package/src/api-client/normalizers.ts +36 -6
- package/src/api-client/organization-tickers.ts +44 -0
- package/src/api-client/request.ts +3 -1
- package/src/api-client/types.ts +27 -1
- package/src/app/runtime/desktop-deeplink.ts +48 -15
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/dom-cell-metrics-allowlist.json +28 -0
- package/src/ask-gloom/pane-context.ts +23 -19
- package/src/ask-gloom/pane-request.ts +2 -2
- package/src/cli/commands/fred-series.ts +77 -11
- package/src/cli/commands/market.ts +77 -20
- package/src/cli/commands/overview.ts +29 -20
- package/src/cli/commands/ticker.ts +64 -11
- package/src/cli/desktop-pane-shot-view.ts +114 -0
- package/src/cli/desktop-pane-shot.ts +30 -31
- package/src/cli/entry.ts +7 -0
- package/src/cli/export-source.ts +43 -0
- package/src/cli/freshness.ts +18 -3
- package/src/cli/glossary.ts +17 -0
- package/src/cli/history-rows.ts +174 -13
- package/src/cli/known-gaps.ts +14 -3
- package/src/cli/listing-arg.ts +20 -2
- package/src/cli/listing-suggestion.ts +159 -0
- package/src/cli/native-smoke.ts +13 -0
- package/src/cli/not-a-ticker.ts +13 -1
- package/src/cli/pane-functions/capabilities.ts +16 -8
- package/src/cli/pane-functions/dom.ts +28 -13
- package/src/cli/pane-functions/function-help.ts +51 -6
- package/src/cli/pane-functions/headless.ts +26 -7
- package/src/cli/pane-functions/index.ts +46 -3
- package/src/cli/pane-functions/report-notices.ts +6 -3
- package/src/cli/pane-functions/report.ts +16 -2
- package/src/cli/pane-functions/resolver.ts +7 -4
- package/src/cli/report-tables.ts +31 -7
- package/src/cli/result.ts +54 -11
- package/src/cli/symbol-rows.ts +18 -1
- package/src/components/chart/composite/axis-overlays.tsx +2 -0
- package/src/components/chart/composite/composite-chart.tsx +4 -21
- package/src/components/chart/composite/legend.tsx +10 -6
- package/src/components/chart/composite/price-axis-labels.tsx +1 -0
- package/src/components/command-bar/list/model.ts +2 -0
- package/src/components/command-bar/pane-templates/items.ts +1 -1
- package/src/components/command-bar/routes/ticker-search/actions.ts +1 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +3 -1
- package/src/components/command-bar/workflow/ops.ts +8 -97
- package/src/components/command-bar/workflow/pane-reuse.ts +177 -0
- package/src/components/data-table/view.tsx +1 -0
- package/src/components/feed-data-table/stack-view.tsx +18 -42
- package/src/components/layout/missing-pane.tsx +2 -0
- package/src/components/layout/shell/index.tsx +1 -0
- package/src/components/layout/shell/pane/ask.ts +12 -7
- package/src/components/layout/shell/pane/capture.ts +8 -34
- package/src/components/markdown-text.tsx +37 -6
- package/src/components/reader/article-reader.tsx +91 -266
- package/src/components/reader/detail-reader.tsx +210 -0
- package/src/components/reader/reader-text.tsx +239 -0
- package/src/components/table-view-shared.tsx +8 -4
- package/src/components/ticker/badge/list.tsx +15 -4
- package/src/components/ui/choice-dialog.tsx +3 -0
- package/src/components/ui/data-table/types.ts +7 -0
- package/src/components/ui/detail-scroll-body.tsx +31 -4
- package/src/components/ui/display.tsx +46 -1
- package/src/components/ui/icon.tsx +11 -1
- package/src/components/ui/list-view.tsx +3 -1
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +9 -1
- package/src/components/ui/select-button.tsx +3 -0
- package/src/data/config/store/fold-panes.ts +202 -11
- package/src/data/config/store/migrations.ts +13 -1
- package/src/docs/snapshot.json +29 -0
- package/src/i18n/es.ts +14 -0
- package/src/i18n/ja.ts +14 -0
- package/src/i18n/ko.ts +14 -0
- package/src/i18n/zh-cn.ts +14 -0
- package/src/i18n/zh-tw.ts +14 -0
- package/src/layout/desk-keys.ts +7 -0
- package/src/layout/desks.ts +2 -2
- package/src/market-data/market/status.ts +67 -6
- package/src/market-data/quotes/contributions.ts +15 -0
- package/src/market-data/quotes/regular-session-valuation.ts +131 -0
- package/src/market-data/quotes/resolution.ts +4 -0
- package/src/market-data/snapshot-provider.ts +6 -3
- package/src/notifications/app-badge.ts +30 -0
- package/src/plugins/builtin/account-management/calendar-tab.tsx +1 -1
- package/src/plugins/builtin/account-management/form-components.tsx +53 -1
- package/src/plugins/builtin/account-management/keyboard.ts +5 -0
- package/src/plugins/builtin/account-management/model.ts +27 -0
- package/src/plugins/builtin/account-management/pane.tsx +62 -2
- package/src/plugins/builtin/alerts/cli.ts +6 -4
- package/src/plugins/builtin/analytics/index.tsx +1 -1
- package/src/plugins/builtin/awards/pane.tsx +29 -18
- package/src/plugins/builtin/backtest/pane.tsx +2 -1
- package/src/plugins/builtin/bond-calculator/index.tsx +3 -5
- package/src/plugins/builtin/buildout/detail/company.tsx +7 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +5 -4
- package/src/plugins/builtin/catalysts/model.ts +92 -10
- package/src/plugins/builtin/catalysts/pane.tsx +6 -4
- package/src/plugins/builtin/cds/index.tsx +21 -30
- package/src/plugins/builtin/changelog/index.tsx +11 -12
- package/src/plugins/builtin/chart-composer/headless-schema.ts +2 -0
- package/src/plugins/builtin/chart-composer/headless.ts +87 -5
- package/src/plugins/builtin/chart-composer/pane.tsx +3 -2
- package/src/plugins/builtin/chart-composer/session-history.ts +20 -5
- package/src/plugins/builtin/chat/command-bar.ts +6 -8
- package/src/plugins/builtin/chat/content/composer-runtime.ts +2 -2
- package/src/plugins/builtin/chat/content/composer.tsx +13 -4
- package/src/plugins/builtin/chat/content/footer.tsx +15 -5
- package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
- package/src/plugins/builtin/chat/content/index.tsx +113 -9
- package/src/plugins/builtin/chat/content/layout-metrics.ts +4 -0
- package/src/plugins/builtin/chat/content/open-targets.ts +20 -4
- package/src/plugins/builtin/chat/content/scroll.ts +6 -1
- package/src/plugins/builtin/chat/content/transcript.tsx +11 -2
- package/src/plugins/builtin/chat/ghost-user.ts +1 -1
- package/src/plugins/builtin/chat/handle-prompt.ts +128 -0
- package/src/plugins/builtin/chat/index.tsx +44 -21
- package/src/plugins/builtin/chat/layout.ts +7 -14
- package/src/plugins/builtin/chat/message/desktop.tsx +59 -11
- package/src/plugins/builtin/chat/message/header.tsx +69 -21
- package/src/plugins/builtin/chat/message/pane-chip.tsx +223 -0
- package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +73 -9
- package/src/plugins/builtin/chat/message/render-state.ts +15 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +33 -8
- package/src/plugins/builtin/chat/message/types.ts +3 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +149 -0
- package/src/plugins/builtin/chat/pane-links/open.ts +29 -0
- package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
- package/src/plugins/builtin/chat/pane-state.ts +5 -8
- package/src/plugins/builtin/chat/people.ts +202 -0
- package/src/plugins/builtin/chat/profile-popover.ts +18 -2
- package/src/plugins/builtin/chat/status-widget.tsx +3 -8
- package/src/plugins/builtin/chat/unread-badge.ts +65 -0
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +9 -6
- package/src/plugins/builtin/clinical-trials/client.ts +26 -1
- package/src/plugins/builtin/clinical-trials/index.tsx +10 -2
- package/src/plugins/builtin/clinical-trials/model.ts +269 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +247 -62
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +156 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +3 -3
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +3 -3
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +1 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +30 -11
- package/src/plugins/builtin/commodities-energy/index.tsx +179 -0
- package/src/plugins/builtin/commodity-balances/client.ts +208 -0
- package/src/plugins/builtin/commodity-balances/headless.ts +66 -0
- package/src/plugins/builtin/commodity-balances/index.tsx +32 -0
- package/src/plugins/builtin/commodity-balances/model.ts +261 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +227 -0
- package/src/plugins/builtin/composite-plugins.ts +26 -1
- package/src/plugins/builtin/credit-boards/index.tsx +21 -42
- package/src/plugins/builtin/credit-boards/model.ts +0 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +10 -19
- package/src/plugins/builtin/credit-documents/index.tsx +27 -10
- package/src/plugins/builtin/debt-maturities/index.tsx +3 -17
- package/src/plugins/builtin/debug/index.tsx +2 -2
- package/src/plugins/builtin/dividend-yield/client.ts +3 -1
- package/src/plugins/builtin/dividend-yield/headless.ts +3 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
- package/src/plugins/builtin/docs/blocks.tsx +4 -3
- package/src/plugins/builtin/docs/pane.tsx +16 -8
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +2 -12
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +36 -2
- package/src/plugins/builtin/equity-screener/model.ts +121 -68
- package/src/plugins/builtin/executives/pane.tsx +3 -1
- package/src/plugins/builtin/filing-events/index.tsx +20 -0
- package/src/plugins/builtin/fixed-income/index.tsx +46 -0
- package/src/plugins/builtin/fixed-income/model.ts +27 -0
- package/src/plugins/builtin/fixed-income/pane.tsx +162 -0
- package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +24 -7
- package/src/plugins/builtin/futures/series.ts +23 -2
- package/src/plugins/builtin/futures/use-front-returns.ts +3 -2
- package/src/plugins/builtin/futures-curve/pane.tsx +6 -4
- package/src/plugins/builtin/help/shortcut-table.tsx +4 -2
- package/src/plugins/builtin/insider/detail.tsx +20 -12
- package/src/plugins/builtin/insider/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/index.tsx +3 -0
- package/src/plugins/builtin/iv-history/screen-pane.tsx +13 -5
- package/src/plugins/builtin/jodi/client.ts +220 -0
- package/src/plugins/builtin/jodi/headless.ts +63 -0
- package/src/plugins/builtin/jodi/index.tsx +32 -0
- package/src/plugins/builtin/jodi/model.ts +75 -0
- package/src/plugins/builtin/jodi/pane.tsx +141 -0
- package/src/plugins/builtin/market-heatmap/evidence.ts +13 -4
- package/src/plugins/builtin/market-heatmap/headless.ts +3 -3
- package/src/plugins/builtin/market-heatmap/index.tsx +41 -23
- package/src/plugins/builtin/market-heatmap/live.ts +2 -1
- package/src/plugins/builtin/market-heatmap/portfolio.ts +67 -21
- package/src/plugins/builtin/market-movers/table.ts +2 -2
- package/src/plugins/builtin/market-valuation/align.ts +10 -9
- package/src/plugins/builtin/mna/distress/model.ts +52 -1
- package/src/plugins/builtin/mna/distress/pane.tsx +17 -12
- package/src/plugins/builtin/mna/index.tsx +2 -0
- package/src/plugins/builtin/news/wire/index.tsx +2 -2
- package/src/plugins/builtin/news/wire/industry-pane.tsx +28 -14
- package/src/plugins/builtin/news/wire/letters/client.ts +108 -0
- package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
- package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
- package/src/plugins/builtin/news/wire/mutes.ts +2 -1
- package/src/plugins/builtin/news/wire/news/query-presets.ts +4 -1
- package/src/plugins/builtin/openfda/client.ts +123 -15
- package/src/plugins/builtin/openfda/model.ts +163 -0
- package/src/plugins/builtin/openfda/pane.tsx +109 -65
- package/src/plugins/builtin/openfda/types.ts +25 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/pe-band/model.ts +3 -2
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +1 -1
- package/src/plugins/builtin/plugin-module.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +19 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +2 -2
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +39 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +18 -5
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +25 -13
- package/src/plugins/builtin/power/evidence.ts +32 -3
- package/src/plugins/builtin/power/gas-client.ts +110 -0
- package/src/plugins/builtin/power/gas-model.ts +194 -0
- package/src/plugins/builtin/power/gas.tsx +145 -0
- package/src/plugins/builtin/power/headless.ts +4 -1
- package/src/plugins/builtin/power/index.tsx +19 -9
- package/src/plugins/builtin/power/pane.tsx +2 -3
- package/src/plugins/builtin/power/prices-headless.ts +75 -7
- package/src/plugins/builtin/power/prices.tsx +36 -13
- package/src/plugins/builtin/primary-dealers/client.ts +108 -0
- package/src/plugins/builtin/primary-dealers/headless.ts +53 -0
- package/src/plugins/builtin/primary-dealers/index.tsx +17 -0
- package/src/plugins/builtin/primary-dealers/model.ts +206 -0
- package/src/plugins/builtin/primary-dealers/pane.tsx +149 -0
- package/src/plugins/builtin/relative-rotation/index.tsx +3 -0
- package/src/plugins/builtin/research/analyst-model.ts +9 -5
- package/src/plugins/builtin/research/headless-schema.ts +2 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +68 -32
- package/src/plugins/builtin/research/relative-valuation-model.ts +45 -24
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +39 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +8 -1
- package/src/plugins/builtin/sec/filing-display.tsx +3 -36
- package/src/plugins/builtin/sec/index.tsx +131 -43
- package/src/plugins/builtin/sec/model.ts +148 -2
- package/src/plugins/builtin/sec/row-summaries.ts +105 -0
- package/src/plugins/builtin/sectors/index.tsx +2 -0
- package/src/plugins/builtin/sectors/sector-model.ts +2 -2
- package/src/plugins/builtin/series-inventory.ts +1 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +179 -43
- package/src/plugins/builtin/shared/wall-teaser.tsx +2 -2
- package/src/plugins/builtin/shared/zip-text.ts +46 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +11 -2
- package/src/plugins/builtin/themes/model.ts +2 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +11 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +81 -19
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +2 -2
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +7 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -6
- package/src/plugins/builtin/time-sales/headless.ts +22 -0
- package/src/plugins/builtin/trace-bonds/client.ts +196 -0
- package/src/plugins/builtin/trace-bonds/headless.ts +69 -0
- package/src/plugins/builtin/trace-bonds/index.tsx +33 -0
- package/src/plugins/builtin/trace-bonds/model.ts +115 -0
- package/src/plugins/builtin/trace-bonds/pane.tsx +191 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +33 -47
- package/src/plugins/builtin/treasury-daily/client.ts +96 -0
- package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
- package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
- package/src/plugins/builtin/treasury-daily/model.ts +69 -0
- package/src/plugins/builtin/treasury-daily/pane.tsx +140 -0
- package/src/plugins/builtin/world-venue-map/entity-detail.tsx +15 -2
- package/src/plugins/builtin/world-venue-map/map.tsx +1 -0
- package/src/plugins/builtin-editorial.ts +1 -1
- package/src/plugins/catalog-browser.ts +25 -2
- package/src/plugins/marketplace/model.ts +57 -21
- package/src/renderers/dom/build-assets.ts +14 -5
- package/src/renderers/dom/data-table/index.tsx +5 -3
- package/src/renderers/dom/desktop/controls.tsx +2 -1
- package/src/renderers/dom/desktop/icons.tsx +25 -0
- package/src/renderers/dom/desktop/list-view.tsx +1 -1
- package/src/renderers/dom/desktop/menu.tsx +1 -1
- package/src/renderers/dom/desktop/query-bar.tsx +3 -1
- package/src/renderers/dom/host/chart-surface.tsx +7 -2
- package/src/renderers/dom/host/resize-rows.ts +111 -0
- package/src/renderers/dom/host/sticky-scroll.ts +22 -3
- package/src/renderers/dom/host/style.ts +14 -0
- package/src/renderers/dom/host/surface-3d.tsx +2 -1
- package/src/renderers/dom/host/surface-fit.ts +10 -6
- package/src/renderers/dom/host/text.tsx +2 -2
- package/src/renderers/dom/styles.css +63 -6
- package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +2 -40
- package/src/renderers/electrobun/bun/desktop/macos-objc.ts +41 -0
- package/src/renderers/electrobun/bun/tui-entry.ts +18 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +8 -3
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +5 -1
- package/src/series/validate.ts +1 -1
- package/src/shares/api.ts +5 -1
- package/src/shares/location.ts +31 -0
- package/src/shares/open.ts +26 -0
- package/src/shares/pane-image.ts +18 -6
- package/src/shares/personal-pane.ts +42 -54
- package/src/sources/gloomberb-cloud/index.ts +7 -2
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -0
- package/src/sources/history-coverage.ts +22 -4
- package/src/sources/history-result.ts +3 -1
- package/src/sources/provider-errors.ts +2 -1
- package/src/sources/provider-router/batches.ts +18 -2
- package/src/sources/provider-router/index.ts +2 -1
- package/src/state/app/context/index.tsx +3 -2
- package/src/test-support/builtin-panes.ts +31 -0
- package/src/tickers/listing-venues.ts +22 -0
- package/src/tickers/search/index.ts +7 -2
- package/src/tickers/search/ranking.ts +19 -0
- package/src/time-series/chart-data.ts +2 -0
- package/src/time-series/parsed-history-cache.ts +41 -11
- package/src/time-series/resolve.ts +47 -15
- package/src/types/config.ts +1 -1
- package/src/types/data-provider.ts +13 -1
- package/src/types/financials.ts +13 -0
- package/src/types/headless.ts +22 -0
- package/src/types/plugin.ts +33 -6
- package/src/types/price-history.ts +2 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/crypto-pair.ts +1 -1
- package/src/utils/currency-pair.ts +66 -0
- package/src/utils/decimal-comma.ts +57 -0
- package/src/utils/guards.ts +25 -0
- package/src/utils/local-utc-offset.ts +37 -0
- package/src/utils/plugin-proxy-hosts.json +8 -1
- package/src/components/chart/composite/crosshair-readout.tsx +0 -150
- package/src/plugins/builtin/chat/message/ai-badge.tsx +0 -18
- package/src/plugins/builtin/credit-boards/pane.tsx +0 -30
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"src/layout-marketplace/gallery.tsx": ["cloud"],
|
|
41
41
|
"src/layout-marketplace/linked-sync.ts": ["cloud"],
|
|
42
42
|
"src/layout/desks.ts": ["chart-composer"],
|
|
43
|
-
"src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","debt-maturities","dividend-yield","docs","doe","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","futures","futures-curve","fx-matrix","gpu","help","insider","ipo-calendar","iv-history","jobs","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","ticker-detail","time-sales","treasury-auctions","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
|
|
43
|
+
"src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","commodities-energy","commodity-balances","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","debt-maturities","dividend-yield","docs","doe","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","fixed-income","futures","futures-curve","fx-matrix","gpu","help","insider","ipo-calendar","iv-history","jobs","jodi","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","primary-dealers","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","ticker-detail","time-sales","trace-bonds","treasury-auctions","treasury-daily","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
|
|
44
44
|
"src/plugins/catalog-ui.ts": ["alerts","clinical-trials","cloud","comment-letters","custom-view","fear-greed","ipo-calendar","market-halts","market-heatmap","members","news","notes","openfda","research-search"],
|
|
45
45
|
"src/plugins/catalog.ts": ["debug"],
|
|
46
46
|
"src/remote/controller.ts": ["custom-view"],
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"src/components/layout/pane/header.tsx": 2,
|
|
3
|
+
"src/components/layout/pane/sizing.ts": 3,
|
|
4
|
+
"src/components/layout/shell/drag/slide.ts": 4,
|
|
5
|
+
"src/components/layout/shell/pane/layers.tsx": 6,
|
|
6
|
+
"src/components/ui/select-button.tsx": 2,
|
|
7
|
+
"src/renderers/dom/data-table/dom.ts": 26,
|
|
8
|
+
"src/renderers/dom/data-table/index.tsx": 21,
|
|
9
|
+
"src/renderers/dom/data-table/row.tsx": 11,
|
|
10
|
+
"src/renderers/dom/desktop/controls.tsx": 6,
|
|
11
|
+
"src/renderers/dom/desktop/list-view.tsx": 2,
|
|
12
|
+
"src/renderers/dom/desktop/popover.tsx": 6,
|
|
13
|
+
"src/renderers/dom/desktop/query-bar.tsx": 1,
|
|
14
|
+
"src/renderers/dom/desktop/select-field.tsx": 2,
|
|
15
|
+
"src/renderers/dom/dom-ui-host.tsx": 6,
|
|
16
|
+
"src/renderers/dom/host/input.tsx": 5,
|
|
17
|
+
"src/renderers/dom/host/live-frame.ts": 8,
|
|
18
|
+
"src/renderers/dom/host/mouse.ts": 10,
|
|
19
|
+
"src/renderers/dom/host/scroll-box.tsx": 22,
|
|
20
|
+
"src/renderers/dom/host/size-observer.ts": 6,
|
|
21
|
+
"src/renderers/dom/host/style.ts": 27,
|
|
22
|
+
"src/renderers/dom/host/surface-fit.ts": 4,
|
|
23
|
+
"src/renderers/dom/host/tabs.tsx": 2,
|
|
24
|
+
"src/renderers/dom/host/text.tsx": 2,
|
|
25
|
+
"src/renderers/dom/input-host.tsx": 4,
|
|
26
|
+
"src/renderers/dom/native-renderer.ts": 4,
|
|
27
|
+
"src/renderers/dom/styles.css": 17
|
|
28
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { ResolvedPane } from "../layout/pane-manager";
|
|
1
2
|
import type { PluginRegistry } from "../plugins/registry";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
3
|
+
import { panePersonalData } from "../shares/personal-pane";
|
|
4
|
+
import { resolveTickerForPane, type AppState } from "../state/app/context";
|
|
5
|
+
import type { LayoutConfig } from "../types/config";
|
|
4
6
|
import type { PaneSharePrivateFields } from "../types/plugin";
|
|
5
|
-
import type { TickerRecord } from "../types/ticker";
|
|
6
7
|
import type { AskGloomPaneAttachment } from "./pane-request";
|
|
7
8
|
|
|
8
9
|
/** A handful of the pane's own settings is enough to say how it is set up. */
|
|
@@ -50,14 +51,13 @@ function functionCode(registry: Pick<PluginRegistry, "paneTemplates">, paneType:
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export interface PaneAttachmentSource {
|
|
53
|
-
registry: Pick<PluginRegistry, "
|
|
54
|
-
|
|
54
|
+
registry: Pick<PluginRegistry, "paneTemplates" | "brokers" | "getPanePluginId" | "getBrokerPluginId">;
|
|
55
|
+
pane: Pick<ResolvedPane, "instance" | "def">;
|
|
56
|
+
state: AppState;
|
|
57
|
+
/** The layout the pane is in, as the share path reads it for `personalData`. */
|
|
58
|
+
layout: LayoutConfig;
|
|
55
59
|
/** What the header calls it. */
|
|
56
60
|
title: string;
|
|
57
|
-
symbol: string | null;
|
|
58
|
-
paneState?: Readonly<Record<string, unknown>>;
|
|
59
|
-
config: Pick<AppConfig, "portfolios" | "brokerInstances">;
|
|
60
|
-
tickers?: ReadonlyMap<string, TickerRecord>;
|
|
61
61
|
/** The tab each of its tab strips has open, outermost first. */
|
|
62
62
|
activeTabs?: readonly string[];
|
|
63
63
|
visibleText: string | null;
|
|
@@ -70,16 +70,17 @@ export interface PaneAttachmentSource {
|
|
|
70
70
|
*/
|
|
71
71
|
export function buildAskGloomPaneAttachment({
|
|
72
72
|
registry,
|
|
73
|
-
|
|
73
|
+
pane,
|
|
74
|
+
state,
|
|
75
|
+
layout,
|
|
74
76
|
title,
|
|
75
|
-
symbol,
|
|
76
|
-
paneState,
|
|
77
|
-
config,
|
|
78
|
-
tickers,
|
|
79
77
|
activeTabs = [],
|
|
80
78
|
visibleText,
|
|
81
79
|
}: PaneAttachmentSource): AskGloomPaneAttachment {
|
|
82
|
-
const
|
|
80
|
+
const { instance } = pane;
|
|
81
|
+
const symbol = resolveTickerForPane(state, instance.instanceId);
|
|
82
|
+
const paneState = state.paneState[instance.instanceId];
|
|
83
|
+
const privateFields = pane.def.portableShare?.private;
|
|
83
84
|
const args: Record<string, string> = {};
|
|
84
85
|
const tabs = activeTabs.map((tab) => tab.trim()).filter(Boolean);
|
|
85
86
|
if (tabs.length > 0) args.tab = tabs.join(" > ").slice(0, MAX_ARG_VALUE_LENGTH);
|
|
@@ -100,14 +101,17 @@ export function buildAskGloomPaneAttachment({
|
|
|
100
101
|
symbol,
|
|
101
102
|
args,
|
|
102
103
|
visibleText,
|
|
103
|
-
personal: showsPersonalData({ registry,
|
|
104
|
+
personal: showsPersonalData({ registry, state, layout, pane }),
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Whatever the pane shows as personal data, as sharing its picture reads it.
|
|
110
|
+
* A pane that cannot be read for it counts as personal: its contents then wait for a yes.
|
|
111
|
+
*/
|
|
112
|
+
function showsPersonalData(source: Parameters<typeof panePersonalData>[0]): boolean {
|
|
109
113
|
try {
|
|
110
|
-
return
|
|
114
|
+
return panePersonalData(source) !== null;
|
|
111
115
|
} catch {
|
|
112
116
|
return true;
|
|
113
117
|
}
|
|
@@ -23,8 +23,8 @@ export interface AskGloomPaneAttachment {
|
|
|
23
23
|
/** Its text as drawn when asked, bounded; null when none could be read. */
|
|
24
24
|
visibleText: string | null;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
* anything it shows leaves the device.
|
|
26
|
+
* The pane shows personal data (`PaneDef.personalData`), so Ask Gloom
|
|
27
|
+
* asks before anything it shows leaves the device.
|
|
28
28
|
*/
|
|
29
29
|
personal: boolean;
|
|
30
30
|
}
|
|
@@ -19,18 +19,44 @@ export function isSpreadSeries(seriesId: string): boolean {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* The
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* The unit a series' observations export in, for the `currency_or_unit`
|
|
23
|
+
* column beside its bare `value`: `bp` for a spread (exported in basis
|
|
24
|
+
* points), `pct` for a percent, otherwise FRED's own units, with "annual
|
|
25
|
+
* rate" as the text header says it. Null when FRED's metadata did not come
|
|
26
|
+
* back.
|
|
27
27
|
*/
|
|
28
|
-
export function
|
|
29
|
-
if (isSpreadSeries(seriesId)) return "
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
export function fredUnit(seriesId: string, info: { units?: string | null; seasonalAdjustment?: string | null } | null | undefined): string | null {
|
|
29
|
+
if (isSpreadSeries(seriesId)) return "bp";
|
|
30
|
+
const units = info?.units?.trim();
|
|
31
|
+
if (!units) return null;
|
|
32
|
+
if (/^percent$/i.test(units)) return "pct";
|
|
33
|
+
const annualRate = /annual rate/i.test(info?.seasonalAdjustment ?? "") && !/annual rate/i.test(units);
|
|
34
|
+
return `${units}${annualRate ? ", annual rate" : ""}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** How often a series observes, in FRED's word lower-cased: `Weekly, Ending Friday` is `weekly`. Null when FRED does not say. */
|
|
38
|
+
export function fredInterval(frequency: string | null | undefined): string | null {
|
|
39
|
+
const word = frequency?.split(",")[0]?.trim().toLowerCase();
|
|
40
|
+
return word || null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The observations from the series' first one on: Gloom Cloud answers a
|
|
45
|
+
* `--start` before a series begins with a blank row for every day in between
|
|
46
|
+
* (DEXSFUS from 1975 has five years of them before 1980-01-02). FRED's
|
|
47
|
+
* `observationStart` says where it begins; without it the blank rows before
|
|
48
|
+
* the first value go. Blank rows after it (a holiday's ".") stay.
|
|
49
|
+
*/
|
|
50
|
+
export function fredObservationsFromStart<T extends { date: string; value: number | null }>(
|
|
51
|
+
observations: readonly T[],
|
|
52
|
+
seriesStart: string | null | undefined,
|
|
53
|
+
): T[] {
|
|
54
|
+
const first = seriesStart && /^\d{4}-\d{2}-\d{2}$/.test(seriesStart)
|
|
55
|
+
? seriesStart
|
|
56
|
+
: observations.reduce<string | null>((earliest, observation) => (
|
|
57
|
+
observation.value != null && Number.isFinite(observation.value) && (!earliest || observation.date < earliest) ? observation.date : earliest
|
|
58
|
+
), null);
|
|
59
|
+
return first ? observations.filter((observation) => observation.date >= first) : [...observations];
|
|
34
60
|
}
|
|
35
61
|
|
|
36
62
|
/**
|
|
@@ -144,3 +170,43 @@ export function fredEmptyMessage(startDate: string, latest: { date: string; valu
|
|
|
144
170
|
? `Latest observation: ${latest.date}${latest.value == null ? "" : ` (${latest.value})`}. Nothing on or after ${startDate}.`
|
|
145
171
|
: "No results.";
|
|
146
172
|
}
|
|
173
|
+
|
|
174
|
+
/** What an export of nothing on or after `--start` says to try instead: a start that reaches the latest observation. */
|
|
175
|
+
export function fredEmptyAdvice(seriesId: string, latest: { date: string } | null): string {
|
|
176
|
+
return latest
|
|
177
|
+
? `Latest observation: ${latest.date}. Try: gloomberb fred ${seriesId} --start ${latest.date.slice(0, 4)}`
|
|
178
|
+
: `No observations came back for ${seriesId}. Try an earlier --start, or gloomberb fred --list for the series served.`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** A series as `fred --list` shows it. */
|
|
182
|
+
export interface FredListRow {
|
|
183
|
+
id: string;
|
|
184
|
+
title: string;
|
|
185
|
+
group: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** How well a word of the filter matches a series, best first: its id or group name, a word of its group or title, the start of its id, then any text. */
|
|
189
|
+
function matchTier(term: string, row: FredListRow, words: readonly string[]): number {
|
|
190
|
+
if (row.id.toLowerCase() === term || row.group.toLowerCase() === term) return 0;
|
|
191
|
+
if (words.some((word) => word.startsWith(term))) return 1;
|
|
192
|
+
if (row.id.toLowerCase().startsWith(term)) return 2;
|
|
193
|
+
return 3;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The series that carry every word of the filter in their id, title or group,
|
|
198
|
+
* the ones it names first: a group or a word of the title ahead of the id that
|
|
199
|
+
* only has it inside (`fx` is the FX group, not FDEFX, whose id spells it).
|
|
200
|
+
* Catalog order breaks ties; an empty filter keeps the catalog as it is.
|
|
201
|
+
*/
|
|
202
|
+
export function filterFredSeries(rows: readonly FredListRow[], filter: string): FredListRow[] {
|
|
203
|
+
const terms = filter.toLowerCase().split(/\s+/).filter(Boolean);
|
|
204
|
+
const ranked = rows.flatMap((row, index) => {
|
|
205
|
+
const text = `${row.id} ${row.title} ${row.group}`.toLowerCase();
|
|
206
|
+
if (!terms.every((term) => text.includes(term))) return [];
|
|
207
|
+
const words = `${row.title} ${row.group}`.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
208
|
+
const tiers = terms.map((term) => matchTier(term, row, words));
|
|
209
|
+
return [{ row, index, worst: Math.max(0, ...tiers), total: tiers.reduce((sum, tier) => sum + tier, 0) }];
|
|
210
|
+
});
|
|
211
|
+
return ranked.sort((a, b) => a.worst - b.worst || a.total - b.total || a.index - b.index).map((entry) => entry.row);
|
|
212
|
+
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
getExtendedSessionDisplay,
|
|
17
17
|
getQuoteSessionFields,
|
|
18
18
|
getRegularSessionDisplay,
|
|
19
|
-
|
|
19
|
+
quoteSessionLabel,
|
|
20
20
|
type ExtendedSession,
|
|
21
21
|
} from "../../market-data/market/status";
|
|
22
22
|
import { formatCompact, formatDistributionAmount, formatPercent, formatPercentRaw } from "../../utils/format";
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "./options-chain";
|
|
63
63
|
import { DEFAULT_LEAPS_CRITERIA, LEAPS_USAGE, parseLeapsCriteria, runLeapsScreen, takeLeapsOptions } from "./options-leaps";
|
|
64
64
|
import type { CliResultColumn } from "../result";
|
|
65
|
+
import type { PriceHistoryResult } from "../../types/price-history";
|
|
65
66
|
import {
|
|
66
67
|
exportRowsTable,
|
|
67
68
|
reportFooterLines,
|
|
@@ -70,9 +71,12 @@ import {
|
|
|
70
71
|
} from "../report-tables";
|
|
71
72
|
import {
|
|
72
73
|
historyBarStatusNote,
|
|
74
|
+
historyCoverageWarning,
|
|
73
75
|
historyFacts,
|
|
74
76
|
historyFlagNote,
|
|
75
77
|
historyGapNote,
|
|
78
|
+
historyJumpNote,
|
|
79
|
+
historyUnbrokenFrom,
|
|
76
80
|
historyAllRangeYears,
|
|
77
81
|
historyIntervalsByRange,
|
|
78
82
|
historyNotes,
|
|
@@ -89,7 +93,9 @@ import { newsFallbackText, newsSearchHeading, newsSearchText, newsSearchTextProb
|
|
|
89
93
|
import { formatUtcTime } from "../../utils/utc-time";
|
|
90
94
|
import { isFiniteNumber } from "../../utils/guards";
|
|
91
95
|
import { exportedFundamentals } from "../../utils/price-earnings";
|
|
92
|
-
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness } from "../freshness";
|
|
96
|
+
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness, valuationFreshness } from "../freshness";
|
|
97
|
+
import { atRegularSessionPrice } from "../../market-data/quotes/regular-session-valuation";
|
|
98
|
+
import { selectMarketCapitalization } from "../../utils/market-capitalization";
|
|
93
99
|
import {
|
|
94
100
|
barHistoryFreshness,
|
|
95
101
|
barResolutionFromDates,
|
|
@@ -128,13 +134,15 @@ import {
|
|
|
128
134
|
import { indexRootAlsoLine, indexRootFor, indexRootTryLine, isYieldIndexSymbol } from "../index-roots";
|
|
129
135
|
import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote } from "../earnings-calendar";
|
|
130
136
|
import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
|
|
131
|
-
import { providerMissReason } from "../../sources/provider-errors";
|
|
132
|
-
import { isNotATickerMessage } from "../not-a-ticker";
|
|
137
|
+
import { notATickerSymbol, providerMissReason } from "../../sources/provider-errors";
|
|
138
|
+
import { isNotATickerMessage, withListingSuggestion } from "../not-a-ticker";
|
|
139
|
+
import { describeSuggestion, suggestListing } from "../listing-suggestion";
|
|
133
140
|
import { splitSymbolArgs } from "../symbol-args";
|
|
134
141
|
import { cliPerpetualContract, perpetualContractStats, type PerpetualContractPayload } from "../perpetual-contract";
|
|
135
142
|
import { subMinuteDataMessage } from "../known-gaps";
|
|
136
143
|
import { secRegistrantMismatchMessage, SecRegistrantMismatchError, areDifferentCompanies } from "../../sources/sec-registrant";
|
|
137
144
|
import { isCryptoPairSymbol } from "../../utils/crypto-pair";
|
|
145
|
+
import { looksLikeCurrencyPair } from "../../utils/currency-pair";
|
|
138
146
|
import { isUsListingExchange } from "../../utils/exchanges";
|
|
139
147
|
import { describeTickerChange, startTickerChangeLookup, tickerChangeLine, type TickerChange } from "../../tickers/ticker-changes";
|
|
140
148
|
import { noSecReportsNotice, nonUsSecListingVenue } from "../../utils/sec";
|
|
@@ -245,6 +253,7 @@ const QUOTE_LEAD_COLUMNS = [
|
|
|
245
253
|
// Text drops a column no row fills, so a table without an extended print stays as narrow as before.
|
|
246
254
|
extendedColumn("PRE"),
|
|
247
255
|
extendedColumn("POST"),
|
|
256
|
+
extendedColumn("OVERNIGHT"),
|
|
248
257
|
{ key: "session", header: "Session", shrink: false },
|
|
249
258
|
];
|
|
250
259
|
|
|
@@ -261,17 +270,21 @@ function yieldChangeCell(row: QuoteRow): string {
|
|
|
261
270
|
return row.changeBasisPoints == null ? "" : colorBySign(formatBasisPoints(row.changeBasisPoints / 100, 1), row.changeBasisPoints);
|
|
262
271
|
}
|
|
263
272
|
|
|
264
|
-
/**
|
|
273
|
+
/**
|
|
274
|
+
* The pre-market, after-hours or overnight print and its move from the
|
|
275
|
+
* regular close, in its own column; "mid" when it is a bid and ask midpoint.
|
|
276
|
+
*/
|
|
265
277
|
function extendedColumn(session: ExtendedSession) {
|
|
278
|
+
const price = (row: QuoteRow) => row.extendedMark ? `${row.extendedPrice} mid` : row.extendedPrice;
|
|
266
279
|
return {
|
|
267
|
-
key: session === "PRE" ? "preMarket" : "afterHours",
|
|
280
|
+
key: session === "PRE" ? "preMarket" : session === "POST" ? "afterHours" : "overnight",
|
|
268
281
|
header: EXTENDED_SESSION_LABELS[session],
|
|
269
282
|
align: "right" as const,
|
|
270
283
|
value: (row: QuoteRow) => row.extendedSession === session && row.extendedPrice
|
|
271
|
-
? [row
|
|
284
|
+
? [price(row), row.extendedChangePercent == null ? "" : formatPercentRaw(row.extendedChangePercent)].join(" ").trim()
|
|
272
285
|
: "",
|
|
273
286
|
format: (value: unknown, row: QuoteRow) => (
|
|
274
|
-
value ? [row
|
|
287
|
+
value ? [price(row), formatChangePercentCell(row.extendedChangePercent)].join(" ").trim() : ""
|
|
275
288
|
),
|
|
276
289
|
};
|
|
277
290
|
}
|
|
@@ -361,7 +374,9 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
|
|
|
361
374
|
rawExtendedPrice: extended?.price ?? null,
|
|
362
375
|
extendedChange: extended?.change ?? null,
|
|
363
376
|
extendedChangePercent: roundedPercent(extended?.changePercent),
|
|
364
|
-
session
|
|
377
|
+
// The extended price is the session's bid and ask midpoint, nothing having traded in it.
|
|
378
|
+
extendedMark: extended?.mark === true,
|
|
379
|
+
session: quoteSessionLabel(quote) ?? "",
|
|
365
380
|
// The close the shown move is measured from: the one before the session Last is.
|
|
366
381
|
previousClose: price(display?.change != null ? display.price - display.change : quote?.previousClose),
|
|
367
382
|
dayRange: quote?.low != null && quote.high != null
|
|
@@ -696,7 +711,7 @@ async function answerQuote(
|
|
|
696
711
|
root ? indexRootTryLine(root, request.command) : null,
|
|
697
712
|
other || listing.exchange ? null : futuresTryLine(listing.symbol, request),
|
|
698
713
|
].filter((line): line is string => !!line);
|
|
699
|
-
if (!other && tryLines.length === 0) return { result, listing, notes: [] };
|
|
714
|
+
if (!other && tryLines.length === 0) return { result: await withCurrencyPairHint(result, market), listing, notes: [] };
|
|
700
715
|
const message = other?.message ?? `${listing.symbol} has no ${request.noun}.`;
|
|
701
716
|
const details = [other?.details, ...tryLines].filter(Boolean).join("\n");
|
|
702
717
|
const oneLine = [
|
|
@@ -707,6 +722,21 @@ async function answerQuote(
|
|
|
707
722
|
return { result, listing, failure: { message, details, oneLine }, notes: [] };
|
|
708
723
|
}
|
|
709
724
|
|
|
725
|
+
/**
|
|
726
|
+
* A symbol written like a currency pair that is not a ticker (USD_NGN, as some
|
|
727
|
+
* FX feeds write a pair) names the listing `gloomberb search` finds nearest to
|
|
728
|
+
* it, as `watchlist add` does: "Did you mean USDNGN=X (US Dollar/Nigerian
|
|
729
|
+
* Naira)?". When search finds nothing near, the row keeps its own error and its
|
|
730
|
+
* search hint.
|
|
731
|
+
*/
|
|
732
|
+
async function withCurrencyPairHint(result: QuoteBatchResult, market: MarketContext): Promise<QuoteBatchResult> {
|
|
733
|
+
const message = errorMessage(result.error);
|
|
734
|
+
const symbol = message ? notATickerSymbol(message) : null;
|
|
735
|
+
if (!message || !symbol || !looksLikeCurrencyPair(symbol)) return result;
|
|
736
|
+
const suggestion = await suggestListing(symbol, market);
|
|
737
|
+
return suggestion ? { ...result, error: withListingSuggestion(message, describeSuggestion(suggestion)) } : result;
|
|
738
|
+
}
|
|
739
|
+
|
|
710
740
|
/** A crypto pair that will not load points at the crypto board. Structured formats keep the error as thrown. */
|
|
711
741
|
function failHistory(error: unknown, symbol: string, ctx: Parameters<CliCommandDef["execute"]>[1]): never {
|
|
712
742
|
if (ctx.cliOptions.format !== "text" || !isCryptoPairSymbol(symbol)) throw error;
|
|
@@ -744,16 +774,17 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
744
774
|
const context = { cacheMode: ctx.cliOptions.refresh ? "refresh" as const : "default" as const };
|
|
745
775
|
const load = () => market.dataProvider.getPriceHistoryWithMetadata
|
|
746
776
|
? market.dataProvider.getPriceHistoryWithMetadata(symbol, exchange, range, context)
|
|
747
|
-
: market.dataProvider.getPriceHistory(symbol, exchange, range, context).then((points) => ({ points, resolution: null }));
|
|
777
|
+
: market.dataProvider.getPriceHistory(symbol, exchange, range, context).then((points): PriceHistoryResult => ({ points, resolution: null }));
|
|
748
778
|
const quoted = loadListingQuote(market.dataProvider, listing);
|
|
749
779
|
const fate = listingFateLoader(listing, market, quoted);
|
|
750
|
-
const [
|
|
780
|
+
const [loaded, quote] = await Promise.all([
|
|
751
781
|
loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0, {
|
|
752
782
|
command: range === "1Y" ? "history" : `history --range ${range}`, noun: "history", fate,
|
|
753
783
|
})
|
|
754
784
|
.catch((error) => failHistory(error, listing.key, ctx)),
|
|
755
785
|
quoted,
|
|
756
786
|
]);
|
|
787
|
+
const { points, resolution } = loaded;
|
|
757
788
|
const identity = listingIdentity(listing, quote);
|
|
758
789
|
// Without a quote currency, the listing metadata research reads (as `ticker` does) may still state it.
|
|
759
790
|
const listed = quote?.currency ? quote : await market.dataProvider.getQuoteMetadata?.(symbol, exchange).catch(() => null);
|
|
@@ -783,7 +814,8 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
783
814
|
// FB's bars are the fund listed there now; META's from 2021 are from when it was FB.
|
|
784
815
|
...listingFateNotes(await fate(), { served: data.length > 0, firstDate: data[0]?.date }),
|
|
785
816
|
...await listingChoiceNotes(listing, market, quote, venues),
|
|
786
|
-
...historyNotes(range, interval, data[0]?.date),
|
|
817
|
+
...historyNotes(range, interval, data[0]?.date), historyCoverageWarning(range, loaded) ?? [],
|
|
818
|
+
gaps ?? [], historyJumpNote(data) ?? [], historyFlagNote(data) ?? [], historyBarStatusNote(data) ?? [],
|
|
787
819
|
].flat();
|
|
788
820
|
const priceColumns: CliResultColumn[] = [
|
|
789
821
|
intraday
|
|
@@ -821,6 +853,8 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
821
853
|
interval,
|
|
822
854
|
firstDate: data[0]?.date ?? null,
|
|
823
855
|
lastDate: data.at(-1)?.date ?? null,
|
|
856
|
+
// The first bar after the last gap or jump; the first bar when there is none.
|
|
857
|
+
unbrokenFrom: historyUnbrokenFrom(data) ?? data[0]?.date ?? null,
|
|
824
858
|
bars: data.length,
|
|
825
859
|
asOf: freshness?.asOf ?? null,
|
|
826
860
|
...(volume ? { volumeUnit: volume.unit, volumePeriod: interval === "1d" ? "UTC day" : interval } : {}),
|
|
@@ -831,16 +865,14 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
831
865
|
heading: `${listingHeading(identity)}\n${cliStyles.muted(historyFacts(unit, data).join(" · "))}`,
|
|
832
866
|
dateKey: "date",
|
|
833
867
|
textColumns: [...priceColumns, flagTextColumn],
|
|
834
|
-
// Every exported row says its currency, what a price is in and its bar size, so it survives head and concatenation.
|
|
835
868
|
columns: [
|
|
836
869
|
...priceColumns,
|
|
837
|
-
{ key: "currency", header: "Currency" },
|
|
838
|
-
{ key: "unit", header: "Unit" },
|
|
839
|
-
{ key: "interval", header: "Interval" },
|
|
840
870
|
flagColumn,
|
|
841
871
|
...marked ? [{ key: "partial", header: "Partial" }, { key: "filled", header: "Filled" }] : [],
|
|
842
872
|
...volume ? [{ key: "volumeUnit", header: "Volume unit" }, { key: "volumeNote", header: "Volume note" }] : [],
|
|
843
873
|
],
|
|
874
|
+
// Every exported row says what it is a bar of, the currency a price is in and its bar size, so it survives head and concatenation.
|
|
875
|
+
exportIdentity: { symbolOrSeries: listing.key, currencyOrUnit: unit.currency ?? unit.unit, interval },
|
|
844
876
|
});
|
|
845
877
|
});
|
|
846
878
|
}
|
|
@@ -889,15 +921,28 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
889
921
|
...financials,
|
|
890
922
|
};
|
|
891
923
|
if (view !== "statements") {
|
|
892
|
-
|
|
924
|
+
// A valuation is measured at the quote's regular session price, so the quote dates it.
|
|
925
|
+
const freshness = view === "valuation" ? valuationFreshness(financials) : fundamentalsFreshness(financials);
|
|
926
|
+
// Market cap, the multiples and the yield at the regular session price, as the text gives them; the quote stays as sent.
|
|
927
|
+
const priced = atRegularSessionPrice(financials);
|
|
928
|
+
const capitalization = selectMarketCapitalization(priced.quote, priced.fundamentals);
|
|
893
929
|
ctx.printResult({
|
|
894
930
|
// JSON reads a multiple over a loss as null with its reason, never as a number.
|
|
895
|
-
data: {
|
|
931
|
+
data: {
|
|
932
|
+
...data,
|
|
933
|
+
...view === "valuation" ? {
|
|
934
|
+
...getQuoteSessionFields(financials.quote),
|
|
935
|
+
marketCap: capitalization?.value ?? null,
|
|
936
|
+
marketCapCurrency: capitalization?.currency ?? null,
|
|
937
|
+
} : {},
|
|
938
|
+
fundamentals: exportedFundamentals(priced.fundamentals),
|
|
939
|
+
},
|
|
896
940
|
freshness,
|
|
897
941
|
...(choice.length > 0 ? { warnings: choice } : {}),
|
|
898
942
|
}, {
|
|
899
943
|
text: () => renderFundamentalsReport(data, view),
|
|
900
944
|
...(tabular ? { tables: selectTables(fundamentalsReportTables(data, view, freshness)) } : {}),
|
|
945
|
+
exportIdentity: { symbolOrSeries: listing.key },
|
|
901
946
|
});
|
|
902
947
|
return;
|
|
903
948
|
}
|
|
@@ -929,6 +974,8 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
929
974
|
rows: () => rows,
|
|
930
975
|
columns: STATEMENT_COLUMNS,
|
|
931
976
|
...(tables && ctx.cliOptions.format === "csv" ? { tables } : {}),
|
|
977
|
+
// Annual statements in the company's reporting currency.
|
|
978
|
+
exportIdentity: { symbolOrSeries: listing.key, currencyOrUnit: rows.find((row) => row.currency)?.currency ?? null, interval: "annual" },
|
|
932
979
|
});
|
|
933
980
|
});
|
|
934
981
|
}
|
|
@@ -1318,6 +1365,7 @@ async function runHolders(
|
|
|
1318
1365
|
tables: [exportRowsTable("Holders", columns, shownRows)],
|
|
1319
1366
|
footer: reportFooterLines({ freshness, notes: notices }),
|
|
1320
1367
|
},
|
|
1368
|
+
exportIdentity: { symbolOrSeries: listing.key },
|
|
1321
1369
|
summary: () => [
|
|
1322
1370
|
[valueBasis ? `Value = ${valueBasis}.` : "", positionsBasis ?? "", shareBasisNote ?? "", unvaluedNote ?? ""].filter(Boolean).join(" "),
|
|
1323
1371
|
insiderCommand ? insiderSummary(data, ownerTypes, listing, identity, form4) : "",
|
|
@@ -1577,6 +1625,8 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
|
|
|
1577
1625
|
})));
|
|
1578
1626
|
ctx.printResult({ data: [row], freshness }, {
|
|
1579
1627
|
text: () => describeFxRate(request, rate),
|
|
1628
|
+
// The rate is the base currency's price of one unit of the other, at the latest quote of each leg.
|
|
1629
|
+
exportIdentity: { symbolOrSeries: pair, currencyOrUnit: baseCurrency, interval: "spot" },
|
|
1580
1630
|
columns: [
|
|
1581
1631
|
{ key: "currency", header: "Currency" },
|
|
1582
1632
|
{ key: "baseCurrency", header: "Base" },
|
|
@@ -1689,7 +1739,14 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
1689
1739
|
title: "Bars",
|
|
1690
1740
|
lines: [
|
|
1691
1741
|
`Each range comes in one bar size: ${historyIntervalsByRange()}. The line under the heading, and the --json metadata, give the currency, the bar size actually served, the first and last bar and the bar count. A warning says when the bars are coarser, or start later, than the range asked for, and for a coin, which trades every day, names each day it has no bar for.`,
|
|
1692
|
-
"A bar whose prices contradict each other (high below the open or close, or low above them) is left blank and flagged, such as high<open, with the count in a warning. Every
|
|
1742
|
+
"A bar whose prices contradict each other (high below the open or close, or low above them) is left blank and flagged, such as high<open, with the count in a warning. Every --csv and --ndjson row carries its flag and ends with symbol_or_series, currency_or_unit, interval, source and as_of; --json rows carry currency, unit, interval and flag.",
|
|
1743
|
+
"A break in the series flags the bar after it, which keeps its prices: gap after more than three months with no bar (weeks for weekly, daily and intraday bars), jump when the close is over 100 times the one before or under a hundredth of it. A warning names each break, and the heading says the date the series runs unbroken from.",
|
|
1744
|
+
],
|
|
1745
|
+
}, {
|
|
1746
|
+
title: "Long history",
|
|
1747
|
+
lines: [
|
|
1748
|
+
"ZAR has daily FRED data from 1980: gloomberb fred DEXSFUS --start 1980 --sort asc --csv",
|
|
1749
|
+
"NGN, SAR and ILS have no FRED series. Their daily bars cover the last five years (gloomberb fn G USDNGN=X --rangePreset 5Y --chartResolution 1d --csv); before that there are monthly bars only (gloomberb history USDNGN=X --range ALL).",
|
|
1693
1750
|
],
|
|
1694
1751
|
}],
|
|
1695
1752
|
examples: ["history AAPL", "history BHP:ASX --range 5Y", "history ZAR=X --range ALL", "history ZAR=X --tail 5", "history AAPL --range 5Y --csv > aapl.csv"],
|
|
@@ -40,13 +40,17 @@ import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
|
|
|
40
40
|
import {
|
|
41
41
|
basisPointsCell,
|
|
42
42
|
DEFAULT_FRED_START,
|
|
43
|
+
filterFredSeries,
|
|
44
|
+
fredEmptyAdvice,
|
|
43
45
|
fredEmptyMessage,
|
|
44
46
|
fredExportValue,
|
|
45
47
|
fredHistoryNotes,
|
|
48
|
+
fredInterval,
|
|
49
|
+
fredObservationsFromStart,
|
|
46
50
|
fredProvenance,
|
|
47
51
|
fredPublicationTime,
|
|
52
|
+
fredUnit,
|
|
48
53
|
fredValueHeader,
|
|
49
|
-
fredValueKey,
|
|
50
54
|
isSpreadSeries,
|
|
51
55
|
laggingSeriesNote,
|
|
52
56
|
} from "./fred-series";
|
|
@@ -346,13 +350,10 @@ async function runFredList(filter: string, ctx: Parameters<CliCommandDef["execut
|
|
|
346
350
|
if (error instanceof ApiRequestError && error.status === 404) ctx.fail("The list of supported FRED series is not available yet.");
|
|
347
351
|
throw error;
|
|
348
352
|
}
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const text = `${series.id} ${series.title} ${series.group}`.toLowerCase();
|
|
354
|
-
return terms.every((term) => text.includes(term));
|
|
355
|
-
});
|
|
353
|
+
const rows = filterFredSeries(
|
|
354
|
+
catalog.series.map((series) => ({ id: series.id, title: series.title?.trim() ?? "", group: series.group?.trim() ?? "" })),
|
|
355
|
+
filter,
|
|
356
|
+
);
|
|
356
357
|
ctx.printResult({ data: rows, metadata: { filter: filter || null, total: catalog.series.length } }, {
|
|
357
358
|
columns: [
|
|
358
359
|
{ key: "id", header: "ID", shrink: false },
|
|
@@ -381,15 +382,17 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
381
382
|
if (isUnsupportedSeries(error)) ctx.fail(`Unsupported FRED series ${seriesId}. List supported series with: gloomberb fred --list`);
|
|
382
383
|
throw error;
|
|
383
384
|
});
|
|
384
|
-
//
|
|
385
|
-
const
|
|
385
|
+
// Every observation exports as `value`, a spread in basis points; its unit goes in the `currency_or_unit` column.
|
|
386
|
+
const unit = fredUnit(seriesId, data.info);
|
|
387
|
+
const interval = fredInterval(data.info?.frequency);
|
|
386
388
|
// A value FRED does not list yet carries its publisher and when it published, in every export.
|
|
387
389
|
const exportRow = (observation: { date: string; value: number | null; publisher?: string; publishedAt?: string }) => ({
|
|
388
390
|
date: observation.date,
|
|
389
|
-
|
|
391
|
+
value: fredExportValue(seriesId, observation.value),
|
|
390
392
|
...(observation.publisher?.trim() ? { publisher: observation.publisher.trim(), ...(observation.publishedAt ? { publishedAt: observation.publishedAt } : {}) } : {}),
|
|
391
393
|
});
|
|
392
|
-
|
|
394
|
+
// A --start before the series begins comes back padded with blank days: the rows start at its first observation.
|
|
395
|
+
const rows = fredObservationsFromStart(data.observations, data.info?.observationStart).map(exportRow);
|
|
393
396
|
// The default start hides older history and a start before the series begins is moved up silently: say where it really starts.
|
|
394
397
|
const notes = [
|
|
395
398
|
...fredHistoryNotes({ startDate, startGiven: start != null, seriesStart: data.info?.observationStart }),
|
|
@@ -398,6 +401,10 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
398
401
|
// Nothing on or after the start: name where the series ends, so an empty answer is not a dead end.
|
|
399
402
|
const latest = rows.length > 0 ? null : await apiClient.getCloudFredSeries(seriesId, { limit: 1, sortOrder: "desc" })
|
|
400
403
|
.then((payload) => payload.observations[0] ?? null).catch(() => null);
|
|
404
|
+
// CSV and NDJSON are rows alone: an empty one would read as a series with no data, so it fails and says where the series ends.
|
|
405
|
+
if (rows.length === 0 && (ctx.cliOptions.format === "csv" || ctx.cliOptions.format === "ndjson")) {
|
|
406
|
+
ctx.fail(`No ${seriesId} observations on or after ${startDate}.`, fredEmptyAdvice(seriesId, latest));
|
|
407
|
+
}
|
|
401
408
|
const periodicity = periodicityOf(data.info?.frequency);
|
|
402
409
|
const newestDate = rows.reduce<string | null>((newest, row) => (!newest || row.date > newest ? row.date : newest), null);
|
|
403
410
|
// A newest value FRED does not list yet names its publisher, Treasury for a yield; FRED's own stay "FRED".
|
|
@@ -408,7 +415,7 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
408
415
|
ctx.printResult({
|
|
409
416
|
data: rows,
|
|
410
417
|
metadata: {
|
|
411
|
-
info: data.info, seriesId, startDate, sortOrder,
|
|
418
|
+
info: data.info, seriesId, startDate, sortOrder, unit, interval,
|
|
412
419
|
...(notes.length ? { notes } : {}),
|
|
413
420
|
...(latest ? { latestObservation: exportRow(latest) } : {}),
|
|
414
421
|
},
|
|
@@ -422,12 +429,13 @@ async function runFred(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
422
429
|
}, {
|
|
423
430
|
dateKey: "date",
|
|
424
431
|
empty: fredEmptyMessage(startDate, latest),
|
|
425
|
-
...(notes.length ? { footnote: notes.join("\n") } : {}),
|
|
432
|
+
...(notes.length ? { footnote: notes.join("\n"), exportNotes: notes } : {}),
|
|
433
|
+
exportIdentity: { symbolOrSeries: seriesId, currencyOrUnit: unit, interval },
|
|
426
434
|
textColumns: [
|
|
427
435
|
{ key: "date", header: "Date" },
|
|
428
436
|
isSpreadSeries(seriesId)
|
|
429
|
-
? { key:
|
|
430
|
-
: { key:
|
|
437
|
+
? { key: "value", header: "Value (bp)", align: "right", format: basisPointsCell }
|
|
438
|
+
: { key: "value", header: fredValueHeader(data.info), align: "right" },
|
|
431
439
|
],
|
|
432
440
|
});
|
|
433
441
|
}
|
|
@@ -445,12 +453,12 @@ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
445
453
|
return {
|
|
446
454
|
seriesId,
|
|
447
455
|
date: latest?.date ?? "",
|
|
448
|
-
// Yields in percent,
|
|
449
|
-
|
|
456
|
+
// Yields in percent, which the exports' `currency_or_unit` column says (`pct`), as `fred` does.
|
|
457
|
+
value: latest?.value ?? null,
|
|
450
458
|
title: data.info?.title ?? "",
|
|
451
459
|
// The move since the previous observation FRED has, not calendar yesterday: a Monday reads against Friday.
|
|
452
460
|
previousDate: previous?.date ?? null,
|
|
453
|
-
|
|
461
|
+
previousValue: previous?.value ?? null,
|
|
454
462
|
changeBasisPoints: latest?.value != null && previous?.value != null ? toBasisPoints(latest.value - previous.value, 1) : null,
|
|
455
463
|
latest,
|
|
456
464
|
publishedAt: latest?.publisher ? null : fredPublicationTime(data.info, latest?.date),
|
|
@@ -470,9 +478,10 @@ async function runYieldCurve(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
470
478
|
...(publishedAt ? { publishedAt } : {}),
|
|
471
479
|
}),
|
|
472
480
|
}, {
|
|
481
|
+
exportIdentity: (row) => ({ symbolOrSeries: String(row.seriesId), currencyOrUnit: "pct", interval: "daily" }),
|
|
473
482
|
textColumns: [
|
|
474
483
|
{ key: "tenor", header: "Tenor", value: (row) => YIELD_TENORS[String(row.seriesId)] ?? row.seriesId },
|
|
475
|
-
{ key: "
|
|
484
|
+
{ key: "value", header: "Yield %", align: "right", format: (value) => typeof value === "number" ? value.toFixed(2) : "-" },
|
|
476
485
|
{
|
|
477
486
|
key: "changeBasisPoints",
|
|
478
487
|
header: "1D bp",
|