gloomberb 0.19.2 → 0.19.3
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 +7 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +20 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +92 -23
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +9 -4
- package/src/cli/commands/search.ts +71 -12
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +5 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +154 -4
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +40 -5
- package/src/cli/listing-arg.ts +46 -9
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +20 -0
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +24 -1
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/header.tsx +8 -3
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +7 -2
- package/src/components/layout/pane/footer/model.ts +40 -5
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +2 -1
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +54 -23
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +8 -1
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +34 -31
- package/src/data/sqlite/access.ts +19 -8
- package/src/data/sqlite/checkpointer.ts +95 -8
- package/src/data/sqlite/database.ts +40 -7
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +26 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/index.tsx +1 -2
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +2 -0
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +15 -2
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/index.tsx +7 -1
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +60 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +116 -13
- package/src/plugins/builtin/thirteenf/data.ts +132 -30
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
- package/src/plugins/builtin/thirteenf/types.ts +12 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/pane.tsx +2 -2
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +15 -4
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +2 -0
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +7 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +12 -7
- package/src/telemetry/stall-reports.ts +34 -5
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name.ts +19 -0
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +13 -2
- package/src/tickers/search/index.ts +4 -1
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +55 -6
- package/src/time-series/resolve.ts +12 -3
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +22 -0
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +11 -0
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/main-thread-stall.ts +75 -3
- package/src/utils/perf-marks.ts +36 -2
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/renderers/electrobun/launcher/preload.ts +0 -13
- package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
- package/src/time-series/studies.ts +0 -1628
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { logFailure } from "../../utils/log-failure";
|
|
2
2
|
import type { CliCommandDef } from "../../types/plugin";
|
|
3
3
|
import { TIME_RANGES, type TimeRange } from "../../time-series/range";
|
|
4
|
+
import { CHART_RESOLUTION_STEP_MS } from "../../time-series/resolution";
|
|
4
5
|
import { yearToDateStartIndex } from "../../market-data/market/year-to-date";
|
|
5
6
|
import type { EarningsEvent, QuoteBatchResult, QuoteSubscriptionTarget, SecFilingItem } from "../../types/data-provider";
|
|
6
7
|
import { companyDataNotes, type CompanyFrom } from "../../types/company-from";
|
|
7
8
|
import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
|
|
9
|
+
import { newsSummaryLine } from "../../news/news-model";
|
|
10
|
+
import { shortStoryId } from "../../news/story-id";
|
|
8
11
|
import type {
|
|
9
12
|
AnalystResearchData,
|
|
10
13
|
CorporateActionsData,
|
|
@@ -52,6 +55,7 @@ import {
|
|
|
52
55
|
analystReferenceMetadata,
|
|
53
56
|
analystUpside,
|
|
54
57
|
formatAnalystReference,
|
|
58
|
+
ratingReason,
|
|
55
59
|
} from "../../plugins/builtin/research/analyst-model";
|
|
56
60
|
import { getPublishedUsEquityCalendarYears, getPublishedUsEquitySession } from "../../market-data/published-us-sessions";
|
|
57
61
|
import { isQuoteStaleForCurrentSession, noPrintsTodayText } from "../../market-data/quotes/freshness";
|
|
@@ -103,6 +107,7 @@ import {
|
|
|
103
107
|
} from "../../utils/option-expiry";
|
|
104
108
|
import { DATE_OPTION_EXAMPLES, isoDateOption, isoDateToday } from "../../utils/calendar-date";
|
|
105
109
|
import { apiClient } from "../../api-client";
|
|
110
|
+
import { CLOUD_QUOTE_DELAY_MINUTES } from "../../api-client/plan-rules";
|
|
106
111
|
import type { ExchangeAnnouncementsPayload } from "../../api-client/exchange-announcements";
|
|
107
112
|
import { exchangeAnnouncementsPage, listsExchangeAnnouncements, type ExchangeAnnouncementsPage } from "../../utils/exchange-announcements";
|
|
108
113
|
import { dailyFxLeg, fxCloseOnDate, type FxCloseReading } from "../../market-data/fx-closes";
|
|
@@ -120,7 +125,7 @@ import {
|
|
|
120
125
|
type CliReportTables,
|
|
121
126
|
} from "../report-tables";
|
|
122
127
|
import { historyCoverageNotice } from "../../sources/history-coverage";
|
|
123
|
-
import {
|
|
128
|
+
import { maxDrawdownText } from "../../utils/max-drawdown";
|
|
124
129
|
import { isPastPriceWindow, loadPriceWindowHistory, PRICE_WINDOW_FROM_HELP, PRICE_WINDOW_TO_HELP, priceWindowText, readPriceWindow, type PriceWindow } from "../../utils/price-window";
|
|
125
130
|
import {
|
|
126
131
|
BAR_INTERVAL_LABELS,
|
|
@@ -132,6 +137,9 @@ import {
|
|
|
132
137
|
historyFacts,
|
|
133
138
|
historyFlagNote,
|
|
134
139
|
historyWickNote,
|
|
140
|
+
historyContractDrawdown,
|
|
141
|
+
historyRollNote,
|
|
142
|
+
historyThinNote,
|
|
135
143
|
historyGapNote,
|
|
136
144
|
historyJumpNote,
|
|
137
145
|
historyUnbrokenFrom,
|
|
@@ -146,13 +154,14 @@ import {
|
|
|
146
154
|
historyUnit,
|
|
147
155
|
quotedPriceDivisor,
|
|
148
156
|
historyVolume,
|
|
157
|
+
historyVolumeBasis,
|
|
149
158
|
historyVolumeText,
|
|
150
159
|
type HistoryRow,
|
|
151
160
|
type HistoryUnit,
|
|
152
161
|
type HistoryVolume,
|
|
153
162
|
} from "../history-rows";
|
|
154
163
|
import {
|
|
155
|
-
|
|
164
|
+
historyExportColumns,
|
|
156
165
|
historyExportStatus,
|
|
157
166
|
historyFailureRow,
|
|
158
167
|
oneLineFailure,
|
|
@@ -191,7 +200,7 @@ import { DAY_SO_FAR_TEXT, withSessionBar } from "../../time-series/session-bar";
|
|
|
191
200
|
import { isFiniteNumber } from "../../utils/guards";
|
|
192
201
|
import { exportedFundamentals } from "../../utils/price-earnings";
|
|
193
202
|
import { fundamentalsFreshness, fxPairFreshness, quotesFreshness, rowsFreshness, valuationFreshness } from "../freshness";
|
|
194
|
-
import { fxRateNotes } from "../../plugins/builtin/shared/fx-rate-freshness";
|
|
203
|
+
import { fxRateNotes, refreshedFxRateNotes } from "../../plugins/builtin/shared/fx-rate-freshness";
|
|
195
204
|
import { quoteBarNotes } from "../../market-data/quotes/intraday-bar";
|
|
196
205
|
import { cedearRatioText, depositaryPairText, isReceiptSide, listingCedearRatio, listingDepositaryPair } from "../../market-data/depositary-pair";
|
|
197
206
|
import { isPesoDollarPair, loadPesoDollarRates, pesoDollarRateLines, pesoDollarRatesJson } from "../peso-dollar-rates";
|
|
@@ -246,13 +255,13 @@ import {
|
|
|
246
255
|
type CliListing,
|
|
247
256
|
type ListingIdentity,
|
|
248
257
|
} from "../listing-arg";
|
|
249
|
-
import { bareListingNote, listingFateNotes, noLongerTradingNote, tradingListing } from "../../tickers/listing-fate";
|
|
258
|
+
import { bareListingNote, listingFateNotes, noLongerTradingNote, SUCCESSOR_CHANGE_WAIT_MS, tradingListing } from "../../tickers/listing-fate";
|
|
250
259
|
import { isYieldIndexSymbol } from "../../tickers/index-roots";
|
|
251
260
|
import {
|
|
252
261
|
analyzeTaxEquivalent, distributionBasis, loadTreasuryTenors, parseTaxRates, TAX_RATE_HELP_OPTIONS, taxEquivalentJson, type TaxRates,
|
|
253
262
|
} from "../../market-data/tax-equivalent";
|
|
254
263
|
import { CBOE_YIELD_INDEX_CLOSE } from "../../plugins/builtin/shared/treasury-cutoffs";
|
|
255
|
-
import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote } from "../earnings-calendar";
|
|
264
|
+
import { earningsCalendarFailure, earningsChoiceNote, earningsDateLabel, isListingAbroad, noListingCalendarNote, undatedListingsNote } from "../earnings-calendar";
|
|
256
265
|
import { formatBasisPoints, toBasisPoints } from "../../utils/basis-points";
|
|
257
266
|
import { providerMissReason } from "../../sources/provider-errors";
|
|
258
267
|
import { isNetworkFailure } from "../../utils/network-failure";
|
|
@@ -805,7 +814,10 @@ function financialStatementRows(financials: FinancialsCliData) {
|
|
|
805
814
|
|
|
806
815
|
function newsRows(articles: NewsArticle[]) {
|
|
807
816
|
return articles.map((article) => ({
|
|
817
|
+
id: article.id,
|
|
808
818
|
title: article.title,
|
|
819
|
+
// Whole, as exports write it; the text table's line under the row (newsRowNote) runs it together.
|
|
820
|
+
summary: article.summary ?? null,
|
|
809
821
|
source: article.source,
|
|
810
822
|
publishedAt: isoDate(article.publishedAt),
|
|
811
823
|
topic: article.topic,
|
|
@@ -823,6 +835,8 @@ function filingRows(filings: SecFilingItem[]) {
|
|
|
823
835
|
companyName: filing.companyName ?? "",
|
|
824
836
|
accessionNumber: filing.accessionNumber,
|
|
825
837
|
url: filing.primaryDocumentUrl ?? filing.filingUrl,
|
|
838
|
+
// A current report's press release exhibit, where its index names one: the figures, not the cover page.
|
|
839
|
+
pressReleaseUrl: filing.pressReleaseUrl ?? "",
|
|
826
840
|
items: secFilingItemCodes(filing.items) ?? "",
|
|
827
841
|
// A current report's items by number and name: the results 8-K reads apart from another filed that day.
|
|
828
842
|
itemsLabel: currentReportItemsLabel(filing.items) ?? "",
|
|
@@ -867,9 +881,16 @@ function analystRows(data: AnalystResearchData) {
|
|
|
867
881
|
current: rating.current ?? "",
|
|
868
882
|
prior: rating.prior ?? "",
|
|
869
883
|
target: rating.currentPriceTarget ?? null,
|
|
884
|
+
...(ratingReason(rating) ? { reason: rating.reason } : {}),
|
|
870
885
|
}));
|
|
871
886
|
}
|
|
872
887
|
|
|
888
|
+
/** The reason coverage of an action gave, under its row with the article. */
|
|
889
|
+
function analystRowNote(row: { reason?: AnalystResearchData["ratings"][number]["reason"] }): string | null {
|
|
890
|
+
const reason = row.reason;
|
|
891
|
+
return reason ? `${reason.text} · ${reason.url}` : null;
|
|
892
|
+
}
|
|
893
|
+
|
|
873
894
|
/** Provider values carry binary floating-point noise such as 0.26940000000000003. */
|
|
874
895
|
function cleanDecimal(value: number | undefined): string {
|
|
875
896
|
return value == null || !Number.isFinite(value) ? "" : String(Number(value.toFixed(6)));
|
|
@@ -1044,7 +1065,7 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
|
|
|
1044
1065
|
const listing = asked[index]!;
|
|
1045
1066
|
// A bare symbol read on a listing abroad (SXR8 -> XETRA) is not the US symbol the changes are of.
|
|
1046
1067
|
if (result.quote && bareListingNote(listing, result.quote)) return { change: null, formerIssuer: null };
|
|
1047
|
-
const payload = await changes.get(listing)?.settle() ?? null;
|
|
1068
|
+
const payload = await changes.get(listing)?.settle(SUCCESSOR_CHANGE_WAIT_MS) ?? null;
|
|
1048
1069
|
const current = tradingListing(result.quote, now);
|
|
1049
1070
|
return { change: describeTickerChange(listing.symbol, payload, current), formerIssuer: describeFormerIssuer(listing.symbol, payload, current) };
|
|
1050
1071
|
}));
|
|
@@ -1391,7 +1412,7 @@ async function loadListingHistory(
|
|
|
1391
1412
|
: market.dataProvider.getPriceHistory(symbol, exchange, loadRange, context).then((points): PriceHistoryResult => ({ points, resolution: null }));
|
|
1392
1413
|
const quoteAnswer = loadListingQuoteAnswer(market.dataProvider, listing);
|
|
1393
1414
|
const quoted = quoteAnswer.then((answer) => answer.quote);
|
|
1394
|
-
const fate = lookups ? listingFateLoader(listing, market, quoted) : null;
|
|
1415
|
+
const fate = lookups ? listingFateLoader(listing, market, quoted, { successor: true }) : null;
|
|
1395
1416
|
const request = { command: historyCommand(ask), noun: "history", ...(fate ? { fate } : {}) };
|
|
1396
1417
|
const [loaded, quote] = await Promise.all([
|
|
1397
1418
|
loadForListing(listing, market, ctx, load, (loaded) => loaded.points.length === 0, request)
|
|
@@ -1435,21 +1456,28 @@ async function loadListingHistory(
|
|
|
1435
1456
|
// A bar history: dated by its last bar, stale once bars of its size stop arriving.
|
|
1436
1457
|
// The day's own bar, while its session trades, is not a close: dated by the quote it was read from.
|
|
1437
1458
|
const dayToDate = fromQuote && interval === "1d" && data.at(-1)?.partial === true;
|
|
1459
|
+
// Signed out or free, the bars of a session in progress run as late as the delayed quote they end on, and say how late.
|
|
1460
|
+
const sessionTrading = quote?.marketState === "REGULAR" || quote?.marketState === "PRE" || quote?.marketState === "POST";
|
|
1461
|
+
const delayedTail = quote?.dataSource === "delayed" && !quote.delisting && (dayToDate || (intraday && sessionTrading));
|
|
1438
1462
|
// The source that served the bars, a broker or another source when Gloom Cloud could not.
|
|
1439
1463
|
const source = loaded.sourceName ? { source: loaded.sourceName } : {};
|
|
1440
1464
|
// A window that closed before today is the history asked for: no later bar makes it stale.
|
|
1441
1465
|
const freshness = withSavedCopy(window && isPastPriceWindow(window) ? rowsFreshness(data, {
|
|
1442
1466
|
...source,
|
|
1443
1467
|
status: "not-a-feed", basis: `${interval ? BAR_INTERVAL_LABELS[interval] : "price"} bars, ${priceWindowText(window)}`, observedKey: "date", oldest: null,
|
|
1468
|
+
...(volume ? { note: historyVolumeText(volume, interval, historyVolumeBasis(data)) } : {}),
|
|
1444
1469
|
}) : rowsFreshness(data, {
|
|
1445
1470
|
...source,
|
|
1446
1471
|
...barHistoryFreshness(intraday ? null : barResolutionFromDates(data.map((row) => row.date))),
|
|
1447
1472
|
observedKey: "date",
|
|
1448
1473
|
oldest: null,
|
|
1449
1474
|
cadenceExchange: venue,
|
|
1475
|
+
// An intraday bar is dated by its open, so the line names its span: `last bar 15:00–16:00 UTC`.
|
|
1476
|
+
...(intraday && interval && interval in CHART_RESOLUTION_STEP_MS ? { barMinutes: CHART_RESOLUTION_STEP_MS[interval] / 60_000 } : {}),
|
|
1450
1477
|
...(forming ? { periodToDate: forming.periodToDate } : closedDay ? { asOf: closedDay } : {}),
|
|
1451
1478
|
...(dayToDate ? { basis: "daily bars", ...(quote && Number.isFinite(quote.lastUpdated) ? { asOf: new Date(quote.lastUpdated).toISOString() } : {}) } : {}),
|
|
1452
|
-
...(
|
|
1479
|
+
...(delayedTail ? { status: "delayed" as const, delayMinutes: CLOUD_QUOTE_DELAY_MINUTES } : {}),
|
|
1480
|
+
...(volume || dayToDate ? { note: [volume ? historyVolumeText(volume, interval, historyVolumeBasis(data)) : null, dayToDate ? `the newest bar is ${DAY_SO_FAR_TEXT}` : null].filter(Boolean).join("; ") } : {}),
|
|
1453
1481
|
// The rows are drawn from the bars served, which say whether they are the saved copy.
|
|
1454
1482
|
}), loaded);
|
|
1455
1483
|
const gaps = historyGapNote(data, traded);
|
|
@@ -1467,6 +1495,8 @@ async function loadListingHistory(
|
|
|
1467
1495
|
// The status line says how far a bar still forming runs; the note is for the bars it does not name.
|
|
1468
1496
|
gaps ?? [], historyJumpNote(data) ?? [], historyFlagNote(data) ?? [], historyWickNote(data) ?? [], historyMissingVolumeNote(data) ?? [],
|
|
1469
1497
|
historyVenueVolumeNote(data) ?? [],
|
|
1498
|
+
// NG=F's rows are November's to the 23rd and December's from the 26th: each roll named, and thin bars said.
|
|
1499
|
+
historyRollNote(listing.symbol, data) ?? [], historyThinNote(data) ?? [],
|
|
1470
1500
|
historyBarStatusNote(forming ? served : data, { tradingDay: venueTradingDay(venue, Date.now()) }) ?? [],
|
|
1471
1501
|
].flat();
|
|
1472
1502
|
return {
|
|
@@ -1475,6 +1505,19 @@ async function loadListingHistory(
|
|
|
1475
1505
|
};
|
|
1476
1506
|
}
|
|
1477
1507
|
|
|
1508
|
+
/**
|
|
1509
|
+
* A continuous future's history is the front month's, not the contract its
|
|
1510
|
+
* quote names now: NG=F's rows from before the roll are November's under a
|
|
1511
|
+
* quote of "Natural Gas Dec 2026". The heading says "Natural Gas front month"
|
|
1512
|
+
* once the bars name their contracts.
|
|
1513
|
+
*/
|
|
1514
|
+
function frontMonthIdentity(identity: ListingIdentity, symbol: string, rows: readonly HistoryRow[]): ListingIdentity {
|
|
1515
|
+
if (!/=F$/i.test(symbol) || !rows.some((row) => row.contract)) return identity;
|
|
1516
|
+
const board = futuresBoardContract(symbol);
|
|
1517
|
+
const name = board?.name ?? identity.name?.replace(/\s+[A-Z][a-z]{2}\s+\d{4}$/, "");
|
|
1518
|
+
return name ? { ...identity, name: `${name} front month` } : identity;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1478
1521
|
/** What a listing with no bar for what was asked says, in a warning and in a list's status column. */
|
|
1479
1522
|
function emptyHistoryNote(symbol: string, { range, window }: HistoryAsk): string {
|
|
1480
1523
|
return window ? `No bars for ${symbol} from ${priceWindowText(window)}.` : `No bars for ${symbol} in ${range}.`;
|
|
@@ -1520,6 +1563,8 @@ const HISTORY_FLAG_TEXT_COLUMN: CliResultColumn = {
|
|
|
1520
1563
|
row.partial ? cliStyles.muted("partial") : "",
|
|
1521
1564
|
row.filled ? cliStyles.muted("filled") : "",
|
|
1522
1565
|
row.unsettled ? cliStyles.muted("unsettled") : "",
|
|
1566
|
+
row.roll && typeof row.contract === "string" ? cliStyles.muted(`roll to ${row.contract}`) : "",
|
|
1567
|
+
row.thin ? cliStyles.muted("thin") : "",
|
|
1523
1568
|
typeof row.status === "string" ? cliStyles.warning(row.status) : "",
|
|
1524
1569
|
].filter(Boolean).join(" "),
|
|
1525
1570
|
};
|
|
@@ -1573,8 +1618,10 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1573
1618
|
const missingVolume = historyReportsVolume(data) ? MISSING_VOLUME_TEXT : "";
|
|
1574
1619
|
const textColumns = historyTextColumns(intraday, price).map((column) => column.key !== "volume" ? column
|
|
1575
1620
|
: { ...column, format: historyVolumeCell(() => missingVolume) });
|
|
1576
|
-
// The largest fall from a close to a later one in the bars shown, said under the heading
|
|
1577
|
-
|
|
1621
|
+
// The largest fall from a close to a later one in the bars shown, said under the heading: within one contract
|
|
1622
|
+
// of a continuous future, never from one month's close to another's across a roll.
|
|
1623
|
+
const contractDrawdown = historyContractDrawdown(data);
|
|
1624
|
+
const drawdown = contractDrawdown?.drawdown ?? null;
|
|
1578
1625
|
ctx.printResult({
|
|
1579
1626
|
data,
|
|
1580
1627
|
metadata: {
|
|
@@ -1591,20 +1638,21 @@ async function runHistory(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
1591
1638
|
// The first bar after the last gap or jump; the first bar when there is none.
|
|
1592
1639
|
unbrokenFrom: historyUnbrokenFrom(data) ?? data[0]?.date ?? null,
|
|
1593
1640
|
bars: data.length,
|
|
1594
|
-
maxDrawdown: drawdown,
|
|
1641
|
+
maxDrawdown: drawdown && contractDrawdown?.contract ? { ...drawdown, contract: contractDrawdown.contract } : drawdown,
|
|
1595
1642
|
asOf: freshness?.asOf ?? null,
|
|
1596
1643
|
...(volume ? { volumeUnit: volume.unit, volumePeriod: interval === "1d" ? "UTC day" : interval } : {}),
|
|
1597
1644
|
},
|
|
1598
1645
|
...(notes.length > 0 ? { warnings: notes } : {}),
|
|
1599
1646
|
freshness,
|
|
1600
1647
|
}, {
|
|
1601
|
-
heading: [listingHeading(identity), cliStyles.muted(historyFacts(unit, data).join(" · ")),
|
|
1602
|
-
...drawdown && interval ? [cliStyles.muted(maxDrawdownText(drawdown, {
|
|
1648
|
+
heading: [listingHeading(frontMonthIdentity(identity, history.listing.symbol, data)), cliStyles.muted(historyFacts(unit, data).join(" · ")),
|
|
1649
|
+
...drawdown && interval ? [cliStyles.muted(maxDrawdownText(drawdown, {
|
|
1650
|
+
basis: `${BAR_INTERVAL_LABELS[interval]} closes${contractDrawdown?.contract ? `, ${contractDrawdown.contract}` : ""}`, price,
|
|
1603
1651
|
date: (text) => intraday ? formatUtcTime(text) : text.slice(0, 10) }))] : []].join("\n"),
|
|
1604
1652
|
dateKey: "date",
|
|
1605
1653
|
textColumns: [...textColumns, HISTORY_FLAG_TEXT_COLUMN],
|
|
1606
|
-
// One header whatever the market is doing, for one symbol as for a list (
|
|
1607
|
-
columns:
|
|
1654
|
+
// One header whatever the market is doing, for one symbol as for a list (historyExportColumns).
|
|
1655
|
+
columns: historyExportColumns(data),
|
|
1608
1656
|
exportStatus: historyExportStatus,
|
|
1609
1657
|
// 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.
|
|
1610
1658
|
exportIdentity: { symbolOrSeries: history.listing.key, currencyOrUnit: unit.currency ?? unit.unit, interval },
|
|
@@ -1710,7 +1758,7 @@ async function runHistoryList(
|
|
|
1710
1758
|
dateKey: "date",
|
|
1711
1759
|
textColumns: [{ key: "symbol", header: "Symbol", shrink: false }, ...textColumns,
|
|
1712
1760
|
{ key: "currency", header: "Cur", shrink: false, format: currencyCell }, HISTORY_FLAG_TEXT_COLUMN],
|
|
1713
|
-
columns:
|
|
1761
|
+
columns: historyExportColumns(rows),
|
|
1714
1762
|
exportStatus: historyExportStatus,
|
|
1715
1763
|
// Every row names its symbol, the currency its prices are in, its bar size and the as-of of its own symbol's bars.
|
|
1716
1764
|
exportIdentity: (row) => {
|
|
@@ -1929,9 +1977,18 @@ const NEWS_COLUMNS = [
|
|
|
1929
1977
|
{ key: "source", header: "Source" },
|
|
1930
1978
|
{ key: "title", header: "Title" },
|
|
1931
1979
|
{ key: "tickers", header: "Tickers" },
|
|
1980
|
+
// The short id `fn news-story-pane` opens the story by; exports keep the whole id.
|
|
1981
|
+
{ key: "id", header: "ID", format: (value: unknown) => typeof value === "string" ? shortStoryId(value) : "" },
|
|
1932
1982
|
{ key: "url", header: "URL", optional: true },
|
|
1933
1983
|
];
|
|
1934
1984
|
|
|
1985
|
+
/**
|
|
1986
|
+
* A story's summary under its row, on one line, cut to the width (`rowNote`): the figure a headline leaves out.
|
|
1987
|
+
* None for a summary that only repeats the headline; exports keep the summary whole.
|
|
1988
|
+
*/
|
|
1989
|
+
const newsRowNote = (row: Record<string, unknown>) => typeof row.summary === "string"
|
|
1990
|
+
? newsSummaryLine({ title: String(row.title ?? ""), summary: row.summary }) : null;
|
|
1991
|
+
|
|
1935
1992
|
/**
|
|
1936
1993
|
* Stories matching text, printed as a symbol's news is, under a line saying
|
|
1937
1994
|
* what they match. A text naming a commodity that matched nothing says so and
|
|
@@ -1968,6 +2025,7 @@ async function printNewsSearch(
|
|
|
1968
2025
|
heading: newsSearchHeading(text, [reason, closestReason].filter(Boolean).join(" · ") || undefined),
|
|
1969
2026
|
rows: newsRows,
|
|
1970
2027
|
columns: NEWS_COLUMNS,
|
|
2028
|
+
rowNote: newsRowNote,
|
|
1971
2029
|
empty: named ? noStoriesMessage(text, route) : empty ?? (searched ? noStoriesMessage(text, null) : "No stories to show."),
|
|
1972
2030
|
});
|
|
1973
2031
|
}
|
|
@@ -2011,6 +2069,7 @@ async function printMetalNews(route: MetalNewsRoute, limit: number, ctx: Paramet
|
|
|
2011
2069
|
heading: metalNewsHeading(route),
|
|
2012
2070
|
rows: newsRows,
|
|
2013
2071
|
columns: NEWS_COLUMNS,
|
|
2072
|
+
rowNote: newsRowNote,
|
|
2014
2073
|
empty: failure === undefined ? noMetalStoriesMessage(route) : "No stories to show.",
|
|
2015
2074
|
...(ticker ? { footnote: `${word} ${cliArrow("→")} ${route.metal}'s market news. For ${ticker.name ?? ticker.symbol} (ticker ${ticker.symbol}), try: ${companyNews}` } : {}),
|
|
2016
2075
|
});
|
|
@@ -2113,6 +2172,7 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
2113
2172
|
...(identity ? { heading: listingHeading(identity) } : {}),
|
|
2114
2173
|
rows: newsRows,
|
|
2115
2174
|
columns: NEWS_COLUMNS,
|
|
2175
|
+
rowNote: newsRowNote,
|
|
2116
2176
|
...(empty ? { empty } : {}),
|
|
2117
2177
|
...(wordHint ? { footnote: wordHint } : {}),
|
|
2118
2178
|
});
|
|
@@ -2127,15 +2187,18 @@ const FILING_COLUMNS = [
|
|
|
2127
2187
|
{ key: "url", header: "URL", optional: true },
|
|
2128
2188
|
{ key: "items", header: "Items" },
|
|
2129
2189
|
{ key: "itemsLabel", header: "Item names" },
|
|
2190
|
+
{ key: "pressReleaseUrl", header: "Press release URL" },
|
|
2130
2191
|
];
|
|
2131
2192
|
|
|
2132
|
-
// The text table names the items before the URL, which a narrow terminal drops first.
|
|
2193
|
+
// The text table names the items before the URL, which a narrow terminal drops first. A current report's
|
|
2194
|
+
// URL is its press release where it has one, the page its figures are on; exports keep both links.
|
|
2133
2195
|
const FILING_TEXT_COLUMNS = [
|
|
2134
2196
|
{ key: "filingDate", header: "Date" },
|
|
2135
2197
|
{ key: "form", header: "Form" },
|
|
2136
2198
|
{ key: "companyName", header: "Company", maxWidth: 24 },
|
|
2137
2199
|
{ key: "itemsLabel", header: "Items" },
|
|
2138
|
-
{ key: "url", header: "URL", optional: true
|
|
2200
|
+
{ key: "url", header: "URL", optional: true,
|
|
2201
|
+
format: (value: unknown, row: Record<string, unknown>) => String(row.pressReleaseUrl || (value ?? "")) },
|
|
2139
2202
|
];
|
|
2140
2203
|
|
|
2141
2204
|
/** The US listing the SEC files a symbol under, from the symbol's venues: NYSE for SAN's Banco Santander. */
|
|
@@ -2631,6 +2694,7 @@ async function runAnalyst(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2631
2694
|
heading: listingHeading(identity),
|
|
2632
2695
|
rows: analystRows,
|
|
2633
2696
|
summary: (research) => analystSummary(research, quote),
|
|
2697
|
+
rowNote: analystRowNote,
|
|
2634
2698
|
columns: [
|
|
2635
2699
|
{ key: "date", header: "Date" },
|
|
2636
2700
|
{ key: "firm", header: "Firm" },
|
|
@@ -2722,7 +2786,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2722
2786
|
const refresh = ctx.cliOptions.refresh;
|
|
2723
2787
|
const venues = loadBareVenues(listing, market);
|
|
2724
2788
|
const quotePromise = loadListingQuote(market.dataProvider, listing);
|
|
2725
|
-
const fate = listingFateLoader(listing, market, quotePromise);
|
|
2789
|
+
const fate = listingFateLoader(listing, market, quotePromise, { successor: true });
|
|
2726
2790
|
const inputsPromise = loadOptionModelInputs(market.dataProvider, listing, quotePromise, refresh);
|
|
2727
2791
|
const now = Date.now();
|
|
2728
2792
|
// Expiries close on the listing's clock: a coin's options (Deribit) settle at 08:00 UTC on any day.
|
|
@@ -3078,7 +3142,9 @@ async function runFx(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]
|
|
|
3078
3142
|
}));
|
|
3079
3143
|
const freshness = fxPairFreshness(loaded);
|
|
3080
3144
|
// A rate that is its pair's bar says which bar, and a stale one why it stands.
|
|
3081
|
-
const
|
|
3145
|
+
const timings = new Map([...loaded].map(([code, leg]) => [code, leg.timing]));
|
|
3146
|
+
// --refresh read every leg again: one that is still stale says the data service has no newer rate.
|
|
3147
|
+
const rateNotes = [...fxRateNotes(timings), ...ctx.cliOptions.refresh ? refreshedFxRateNotes(timings) : []];
|
|
3082
3148
|
if (tenor && shortRates && !("error" in tenor)) {
|
|
3083
3149
|
const read = await shortRates;
|
|
3084
3150
|
if ("error" in read) throw read.error;
|
|
@@ -3214,12 +3280,14 @@ async function runEarnings(rawArgs: string[], ctx: Parameters<CliCommandDef["exe
|
|
|
3214
3280
|
return listed ? earningsChoiceNote(listing, eventOf(listing), await listed) : null;
|
|
3215
3281
|
})),
|
|
3216
3282
|
]);
|
|
3217
|
-
|
|
3283
|
+
// A listing at home the calendar has no date for is named as well: a list never drops a symbol unsaid.
|
|
3284
|
+
const undated = undatedListingsNote(listings.filter((listing) => !isListingAbroad(listing) && !eventOf(listing)));
|
|
3285
|
+
const noCalendar = [...abroadNotes, ...undated ? [undated] : []].join("\n");
|
|
3218
3286
|
// Text prints the listings without a calendar under the table; other formats carry them as warnings.
|
|
3219
3287
|
const warnings = [
|
|
3220
3288
|
...listings.flatMap(listingReadNotes),
|
|
3221
3289
|
...choiceNotes.filter((note): note is string => !!note),
|
|
3222
|
-
...ctx.cliOptions.format === "text" ? [] : abroadNotes,
|
|
3290
|
+
...ctx.cliOptions.format === "text" ? [] : [...abroadNotes, ...undated ? [undated] : []],
|
|
3223
3291
|
];
|
|
3224
3292
|
ctx.printResult({
|
|
3225
3293
|
data: events,
|
|
@@ -3292,7 +3360,8 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
3292
3360
|
}, {
|
|
3293
3361
|
title: "CSV",
|
|
3294
3362
|
lines: [
|
|
3295
|
-
"Every --csv has one header, whatever the symbol, range or market state: Date, Open, High, Low, Close, Volume, Flag, Partial, Filled, Unsettled, Volume unit, Volume note, Venue volume, Volume venue, status, then symbol_or_series, currency_or_unit, interval, source and as_of. Prices are plain numbers in the currency currency_or_unit names. Partial, Filled and Unsettled are true or false, and status says why a row has no prices.",
|
|
3363
|
+
"Every --csv has one header, whatever the symbol, range or market state: Date, Open, High, Low, Close, Volume, Flag, Partial, Filled, Unsettled, Volume unit, Volume note, Venue volume, Volume venue, Volume basis, status, then symbol_or_series, currency_or_unit, interval, source and as_of. Prices are plain numbers in the currency currency_or_unit names. Partial, Filled and Unsettled are true or false, and status says why a row has no prices.",
|
|
3364
|
+
"A continuous future whose bars name their contracts (NG=F) adds Contract, Roll and Thin before status: the contract per bar, and true or false for the bar it rolled at and a bar that traded far below those around it.",
|
|
3296
3365
|
],
|
|
3297
3366
|
}, {
|
|
3298
3367
|
title: "Several symbols",
|
|
@@ -72,14 +72,30 @@ export function newsFallbackText(symbol: string): string | null {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
* word in the headline
|
|
77
|
-
*
|
|
75
|
+
* Stories newest first, as `news <words>` lists them. The service picks the
|
|
76
|
+
* best matches (every word in the headline before words only in the summary);
|
|
77
|
+
* the list is then read by date, which its help promises. A story with no
|
|
78
|
+
* date goes last; equal dates keep the service's order.
|
|
79
|
+
*/
|
|
80
|
+
function newestFirst(articles: readonly NewsArticle[]): NewsArticle[] {
|
|
81
|
+
const time = (article: NewsArticle) => {
|
|
82
|
+
const at = new Date(article.publishedAt as Date | string | number).getTime();
|
|
83
|
+
return Number.isFinite(at) ? at : -Infinity;
|
|
84
|
+
};
|
|
85
|
+
return articles.map((article, index) => ({ article, index, at: time(article) }))
|
|
86
|
+
.sort((left, right) => right.at - left.at || left.index - right.index)
|
|
87
|
+
.map(({ article }) => article);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The stories matching the text, newest first (`newestFirst`): the service
|
|
92
|
+
* chooses them, stories with every word in the headline ahead of the ones with
|
|
93
|
+
* some only in the summary. The service holds stories back from an account
|
|
78
94
|
* without real-time access, as every other news request.
|
|
79
95
|
*/
|
|
80
96
|
export async function searchNewsStories(text: string, limit: number, topics?: readonly string[]): Promise<NewsArticle[]> {
|
|
81
97
|
const response = await apiClient.searchCloudNews(text, limit, topics);
|
|
82
|
-
return response.items.map((item) => mapCloudNewsArticle(item));
|
|
98
|
+
return newestFirst(response.items.map((item) => mapCloudNewsArticle(item)));
|
|
83
99
|
}
|
|
84
100
|
|
|
85
101
|
/** A search's stories, and whether they are its closest ones because no story has every word. */
|
|
@@ -96,7 +112,10 @@ export interface NewsSearchAnswer {
|
|
|
96
112
|
*/
|
|
97
113
|
export async function searchClosestNewsStories(text: string, limit: number): Promise<NewsSearchAnswer> {
|
|
98
114
|
const response = await apiClient.searchCloudNews(text, limit, undefined, { closest: true });
|
|
99
|
-
|
|
115
|
+
const found = response.items.map((item) => mapCloudNewsArticle(item));
|
|
116
|
+
// The closest stories stay most words first, as their heading says; a match of every word is read by date.
|
|
117
|
+
const closest = response.match === "closest";
|
|
118
|
+
return { found: closest ? found : newestFirst(found), closest };
|
|
100
119
|
}
|
|
101
120
|
|
|
102
121
|
/** What the heading says of a search no story has every word of. */
|
|
@@ -59,7 +59,7 @@ import { buildCorrelationSeries } from "../../plugins/builtin/correlation/matrix
|
|
|
59
59
|
import { alignDailyCloses, correlateDailyCloses } from "../../plugins/builtin/correlation/compute";
|
|
60
60
|
import { mixedSessionCloseNote } from "../../market-data/market/session-close-note";
|
|
61
61
|
import { CORRELATION_RETURN_BASIS, loadCorrelationHistory } from "../../plugins/builtin/correlation/history";
|
|
62
|
-
import { EXCHANGE_OPTION, listingIdentity, listingReadNotes, loadForListing, loadListingQuote, requireCliListing, type CliListing } from "../listing-arg";
|
|
62
|
+
import { coinListingNotes, EXCHANGE_OPTION, listingIdentity, listingReadNotes, loadForListing, loadListingQuote, requireCliListing, type CliListing } from "../listing-arg";
|
|
63
63
|
import { CLI_COMMAND_GROUPS } from "../help";
|
|
64
64
|
import { formatChangePercentCell, formatCompactCell } from "../helpers";
|
|
65
65
|
import { WORLD_INDICES } from "../../plugins/builtin/world-indices/indices";
|
|
@@ -481,7 +481,7 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
|
|
|
481
481
|
const country = parseCountry(takeOption(rawArgs, "--country"), ctx);
|
|
482
482
|
const impact: ImpactFilter = parseFilter(takeOption(rawArgs, "--impact"), FILTER_CYCLE, "impact", ctx);
|
|
483
483
|
const search = takeOption(rawArgs, "--search")?.trim() ?? null;
|
|
484
|
-
if (search === "") ctx.fail("--search needs the words of an event's name.", "Such as econ --search nonfarm.");
|
|
484
|
+
if (search === "") ctx.fail("--search needs the words of an event's name or its publisher.", "Such as econ --search nonfarm.");
|
|
485
485
|
const fromOption = parseDateOption(takeOption(rawArgs, "--from"), "--from", ctx);
|
|
486
486
|
const tzOption = takeOption(rawArgs, "--tz");
|
|
487
487
|
const viewerZone = tzOption == null ? null : canonicalTimeZone(tzOption);
|
|
@@ -909,7 +909,12 @@ async function runCorrelation(rawArgs: string[], ctx: Parameters<CliCommandDef["
|
|
|
909
909
|
asOf: newestReportTime(lastBars),
|
|
910
910
|
oldest: oldestReportTime(lastBars),
|
|
911
911
|
});
|
|
912
|
-
|
|
912
|
+
// A coin's code read as a listing (BTC, the Grayscale fund) says so, as `quote BTC` does.
|
|
913
|
+
const warnings = [
|
|
914
|
+
...listingReadNotes(leftListing!), ...listingReadNotes(rightListing!),
|
|
915
|
+
...coinListingNotes(leftListing!, leftQuote), ...coinListingNotes(rightListing!, rightQuote),
|
|
916
|
+
...sessionNote ? [sessionNote] : [],
|
|
917
|
+
];
|
|
913
918
|
ctx.printResult({
|
|
914
919
|
data: [{ left, right, samples: sampleSize, correlation }],
|
|
915
920
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
@@ -971,7 +976,7 @@ export const overviewCliCommands: CliCommandDef[] = [
|
|
|
971
976
|
options: [
|
|
972
977
|
{ flags: "--country <regions or countries>", description: "US, G7, EU, all (default), or countries by their two-letter codes, comma separated, such as MX or MX,BR,ZA" },
|
|
973
978
|
{ flags: "--impact <level>", description: "high, medium, low, or all (default all)" },
|
|
974
|
-
{ flags: "--search <words>", description: "Events whose name has these words, case, spaces and hyphens aside, such as nonfarm
|
|
979
|
+
{ flags: "--search <words>", description: "Events whose name or publisher has these words, case, spaces and hyphens aside, such as nonfarm, fed funds or USDA" },
|
|
975
980
|
{ flags: "--from <date>", description: `First day listed, as ${DATE_OPTION_EXAMPLES} (default today, UTC); an earlier day lists past events from the stored calendar and says where it starts` },
|
|
976
981
|
{ flags: "--tz <zone>", description: "Add a column with each release's time on that clock, an IANA zone such as America/New_York; with the weekday when it falls on another day than the UTC date" },
|
|
977
982
|
],
|
|
@@ -24,7 +24,7 @@ import { initMarketData, withMarketData } from "../context";
|
|
|
24
24
|
import { listingChangeLine, loadListingFate } from "../../tickers/listing-fate";
|
|
25
25
|
import { formatMarketPriceWithCurrency, quoteFormatOptions } from "../../market-data/market/format";
|
|
26
26
|
import { formerIssuerLine, startTickerChangeLookup, type TickerChangeLookup } from "../../tickers/ticker-changes";
|
|
27
|
-
import { fail, failRetryable } from "../errors";
|
|
27
|
+
import { fail, failRetryable, failWithError, isCliFailure, unreachableError } from "../errors";
|
|
28
28
|
import { isProviderBusy, SEARCH_BUSY_CODE, SEARCH_BUSY_MESSAGE } from "../../sources/provider-errors";
|
|
29
29
|
import { isNetworkFailure } from "../../utils/network-failure";
|
|
30
30
|
import type { MarketContext } from "../types";
|
|
@@ -36,6 +36,7 @@ import { knownGapMessage, commandPointer } from "../known-gaps";
|
|
|
36
36
|
import { instrumentTypeLabel } from "../../tickers/instrument-kind";
|
|
37
37
|
import { canonicalExchange, isUsListingExchange } from "../../utils/exchanges";
|
|
38
38
|
import { companyLines, companyNameExtraWords, groupByCompany, namedByService, sameCompany } from "../../tickers/company-name";
|
|
39
|
+
import { isLowQualityTickerName } from "../../tickers/search/result";
|
|
39
40
|
import { findCatalogSeries, renderSeriesMatches, type SeriesMatch } from "../series-search";
|
|
40
41
|
import { centralBankAskedAbout, centralBankCiting, type CentralBank } from "../../utils/central-banks";
|
|
41
42
|
import { createPaneCatalog } from "../pane-functions/discovery";
|
|
@@ -133,8 +134,8 @@ export async function searchCandidatesForCli({
|
|
|
133
134
|
const quote = BARE_SYMBOL.test(symbol) && candidates.some((candidate) => candidate.symbol.toUpperCase() === symbol)
|
|
134
135
|
? await (quoted ?? loadSearchedQuote(symbol, savedExchange, dataProvider))
|
|
135
136
|
: null;
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
const ordered = withQuotedNames(symbol, withUsLineOfPrimary(withHomeListingPrimary(trimmedQuery, withPrimaryListingFirst(symbol, candidates, quote))), quote);
|
|
138
|
+
return withOtherCompaniesApart(ordered).slice(0, totalLimit);
|
|
138
139
|
} catch (error) {
|
|
139
140
|
// Not "No matches found": a script or a reader would take that for a name nothing lists.
|
|
140
141
|
if (isProviderBusy(error)) failRetryable(SEARCH_BUSY_MESSAGE, SEARCH_BUSY_CODE);
|
|
@@ -346,6 +347,35 @@ function withQuotedNames(symbol: string, candidates: TickerSearchCandidate[], qu
|
|
|
346
347
|
});
|
|
347
348
|
}
|
|
348
349
|
|
|
350
|
+
/** A symbol without a venue's suffix or code: DK for DK.BA or DK:BYMA. */
|
|
351
|
+
const baseSymbol = (symbol: string) => symbol.toUpperCase().replace(/[:.][A-Z]{1,4}$/, "");
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Other Listings are the primary listing's company's own lines alone: by its
|
|
355
|
+
* name, a name it had (Cassava Sciences for Filana's FLNA), or, for a line
|
|
356
|
+
* search names no company for, its symbol on another venue. Another company's
|
|
357
|
+
* line, or a nameless line under another symbol (DKC1O on BYMA under
|
|
358
|
+
* `search DK`), is an Other Match, listed after them.
|
|
359
|
+
*/
|
|
360
|
+
function withOtherCompaniesApart(candidates: TickerSearchCandidate[]): TickerSearchCandidate[] {
|
|
361
|
+
// A line search names no company for: no name, or only its symbol (DKC1O).
|
|
362
|
+
const nameless = (candidate: TickerSearchCandidate) =>
|
|
363
|
+
isLowQualityTickerName(candidate.result ? candidate.result.name ?? "" : resolveSearchName(candidate), candidate.symbol);
|
|
364
|
+
const primary = candidates.find((candidate) => candidate.category === "Primary Listing");
|
|
365
|
+
// A primary line search names no company for gives nothing to tell its company's lines by.
|
|
366
|
+
if (!primary || nameless(primary)) return candidates;
|
|
367
|
+
const names = [resolveSearchName(primary), primary.result?.formerName, ...primary.nameAliases ?? []];
|
|
368
|
+
const ownLine = (candidate: TickerSearchCandidate) => nameless(candidate)
|
|
369
|
+
? baseSymbol(candidate.symbol) === baseSymbol(primary.symbol)
|
|
370
|
+
: names.some((company) => sameCompany(resolveSearchName(candidate), company));
|
|
371
|
+
const others = candidates.filter((candidate) => candidate.category === "Other Listings" && !ownLine(candidate));
|
|
372
|
+
if (others.length === 0) return candidates;
|
|
373
|
+
const kept = candidates.filter((candidate) => !others.includes(candidate));
|
|
374
|
+
const at = kept.findLastIndex((candidate) => candidate === primary || candidate.category === "Other Listings") + 1;
|
|
375
|
+
const apart = others.map((candidate) => ({ ...candidate, category: "Other Matches" as const }));
|
|
376
|
+
return [...kept.slice(0, at), ...apart, ...kept.slice(at)];
|
|
377
|
+
}
|
|
378
|
+
|
|
349
379
|
/** "Saved", or the broker a row came from; never the data service's internal id. */
|
|
350
380
|
function searchSourceLabel(candidate: TickerSearchCandidate): string {
|
|
351
381
|
return candidate.kind === "ticker" ? "Saved" : candidate.result?.brokerLabel || "";
|
|
@@ -498,18 +528,19 @@ export function buildSearchReport({
|
|
|
498
528
|
/** What the query was searched as: "Read VIX.X as ^VIX (Cboe Volatility Index)." */
|
|
499
529
|
readAs?: string;
|
|
500
530
|
}): string {
|
|
531
|
+
// Every note is prose: it wraps to a narrow --width.
|
|
501
532
|
const lines = [renderSection(`Search: ${query}`)];
|
|
502
|
-
if (readAs) lines.push("", readAs);
|
|
533
|
+
if (readAs) lines.push("", wrapToTerminal(readAs));
|
|
503
534
|
const notes = [...fate.notes, ...formerIssuerNotes(candidates, fate)];
|
|
504
|
-
if (notes.length > 0) lines.push("", ...notes);
|
|
535
|
+
if (notes.length > 0) lines.push("", ...notes.map((note) => wrapToTerminal(note)));
|
|
505
536
|
const gap = knownGapMessage(query);
|
|
506
537
|
const indexNote = gap ? null : indexRootNote(query, candidates);
|
|
507
|
-
if (indexNote) lines.push("", indexNote);
|
|
508
|
-
if (gap) lines.push("", gap);
|
|
538
|
+
if (indexNote) lines.push("", wrapToTerminal(indexNote));
|
|
539
|
+
if (gap) lines.push("", wrapToTerminal(gap));
|
|
509
540
|
const bank = searchedCentralBank(query);
|
|
510
541
|
if (bank) lines.push("", wrapToTerminal(centralBankLine(bank)));
|
|
511
542
|
const setting = commandPointer(query);
|
|
512
|
-
if (setting) lines.push("", setting);
|
|
543
|
+
if (setting) lines.push("", wrapToTerminal(setting));
|
|
513
544
|
const charted = renderSeriesMatches(query, series, SEARCH_SERIES_LIMIT, cliTerminalWidth() ?? 100, "catalog");
|
|
514
545
|
// A query that names the data ("carbon", "day-ahead") lists its series first; words that happen to
|
|
515
546
|
// be in a series name ("apple") list them after the tickers.
|
|
@@ -523,9 +554,9 @@ export function buildSearchReport({
|
|
|
523
554
|
if (functions.length > 0) lines.push("", renderFunctionMatches(functions));
|
|
524
555
|
else if (!gap && !bank && !setting && !charted) {
|
|
525
556
|
return [...lines, ...hasNonLatinLetters(query) ? [cliStyles.muted(OTHER_SCRIPT_NO_MATCH)]
|
|
526
|
-
: [cliStyles.muted("No matches found."), cliStyles.muted(catalogHint(query))]].join("\n");
|
|
557
|
+
: [cliStyles.muted("No matches found."), cliStyles.muted(wrapToTerminal(catalogHint(query)))]].join("\n");
|
|
527
558
|
}
|
|
528
|
-
if (pointToCatalog) lines.push("", cliStyles.muted(catalogHint(query)));
|
|
559
|
+
if (pointToCatalog) lines.push("", cliStyles.muted(wrapToTerminal(catalogHint(query))));
|
|
529
560
|
return lines.join("\n");
|
|
530
561
|
}
|
|
531
562
|
|
|
@@ -558,11 +589,39 @@ export function buildSearchReport({
|
|
|
558
589
|
));
|
|
559
590
|
}
|
|
560
591
|
if (charted && !seriesFirst) lines.push("", charted);
|
|
561
|
-
if (pointToCatalog) lines.push("", cliStyles.muted(catalogHint(query)));
|
|
592
|
+
if (pointToCatalog) lines.push("", cliStyles.muted(wrapToTerminal(catalogHint(query))));
|
|
562
593
|
|
|
563
594
|
return lines.join("\n");
|
|
564
595
|
}
|
|
565
596
|
|
|
597
|
+
/**
|
|
598
|
+
* What a search says from its own text, whatever the ticker lookup does: the
|
|
599
|
+
* data no source carries and its proxies ("baltic dry"), the central bank the
|
|
600
|
+
* query names, the setting or report it names.
|
|
601
|
+
*/
|
|
602
|
+
function localSearchNotes(query: string): string[] {
|
|
603
|
+
const bank = searchedCentralBank(query);
|
|
604
|
+
return [knownGapMessage(query), bank ? centralBankLine(bank) : null, commandPointer(query)].filter((note): note is string => !!note);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* A ticker lookup that was busy or never reached the data service fails only
|
|
609
|
+
* the ticker part of a search: the notes from local text print first (text) or
|
|
610
|
+
* ride in the failure's details (JSON), then the failure says that no tickers
|
|
611
|
+
* were looked up. A search with no such note fails as before.
|
|
612
|
+
*/
|
|
613
|
+
function failTickerLookup(error: unknown, query: string, printNotes: boolean): never {
|
|
614
|
+
const notes = localSearchNotes(query);
|
|
615
|
+
const busy = isCliFailure(error) && error.code === SEARCH_BUSY_CODE;
|
|
616
|
+
if (notes.length === 0 || !(busy || isNetworkFailure(error))) throw error;
|
|
617
|
+
if (printNotes) console.log([renderSection(`Search: ${query}`), "", ...notes.map((note) => wrapToTerminal(note))].join("\n"));
|
|
618
|
+
const failure = busy ? { code: SEARCH_BUSY_CODE, message: TICKER_SEARCH_BUSY_MESSAGE, retryable: true } : unreachableError();
|
|
619
|
+
return failWithError(printNotes ? failure : { ...failure, details: [failure.details, ...notes].filter(Boolean).join("\n") });
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** The busy line after a search's own notes printed: only the ticker lookup is missing. */
|
|
623
|
+
const TICKER_SEARCH_BUSY_MESSAGE = "Ticker search is busy, try again in a moment.";
|
|
624
|
+
|
|
566
625
|
/** The functions a search lists when it found no ticker, unless a known gap or a central bank already says where to look. */
|
|
567
626
|
function searchedFunctions(
|
|
568
627
|
query: string,
|
|
@@ -613,7 +672,7 @@ export async function search(query: string, dependencies: SearchCommandDependenc
|
|
|
613
672
|
tickers,
|
|
614
673
|
dataProvider,
|
|
615
674
|
...quote ? { quote } : {},
|
|
616
|
-
});
|
|
675
|
+
}).catch((error: unknown) => failTickerLookup(error, trimmedQuery, !dependencies.printResult));
|
|
617
676
|
const fate = quote && changes ? await searchFate(symbol, candidates, await quote, dataProvider, changes) : NO_SEARCH_FATE;
|
|
618
677
|
|
|
619
678
|
const series = findCatalogSeries(dependencies.plugins ?? [], trimmedQuery, "names");
|