gloomberb 0.18.4 → 0.19.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +36 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/pane.tsx +7 -5
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/feed.ts +2 -2
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
package/src/cli/listing-arg.ts
CHANGED
|
@@ -9,17 +9,18 @@ import {
|
|
|
9
9
|
quotedOnVenue,
|
|
10
10
|
readAmbiguousCurrencyPair,
|
|
11
11
|
readSharedCode,
|
|
12
|
+
savedRowOfBareSymbol,
|
|
12
13
|
type ListingDeps,
|
|
13
14
|
type ListingStore,
|
|
14
15
|
type ListingVenue,
|
|
15
16
|
} from "../tickers/listing-venues";
|
|
16
17
|
import { findUsListingOfCompany, type UsListing } from "../tickers/us-listing";
|
|
17
|
-
import { findListingOfCompanyName, isCompanyNameInput, readTerminalCodeArg } from "../tickers/company-name-listing";
|
|
18
|
+
import { findListingOfCompanyName, isCompanyNameInput, readTerminalCodeArg, type CompanyNameListing } from "../tickers/company-name-listing";
|
|
18
19
|
import { movedAwayChange, startTickerChangeLookup, type MovedTickerChange } from "../tickers/ticker-changes";
|
|
19
20
|
import { loadListingFate, MovedSymbolError, type ListingFate } from "../tickers/listing-fate";
|
|
20
21
|
import { quotedListingName } from "../tickers/listing-name";
|
|
21
22
|
import { cliStyles } from "../utils/cli-output";
|
|
22
|
-
import { groupByCompany, sameCompany, shortCompanyName } from "../tickers/company-name";
|
|
23
|
+
import { groupByCompany, namesDepositaryReceipt, sameCompany, sameListedName, shortCompanyName } from "../tickers/company-name";
|
|
23
24
|
import {
|
|
24
25
|
canonicalExchange,
|
|
25
26
|
exchangeCodeVenues,
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
import { withMarketData } from "./scoped-context";
|
|
35
36
|
import { failWithFields } from "./errors";
|
|
36
37
|
import { stripStrayEdges } from "./symbol-args";
|
|
37
|
-
import { isNoProviderMessage, isProviderBusy } from "../sources/provider-errors";
|
|
38
|
+
import { isNoProviderMessage, isNoSourceAnswer, isProviderBusy } from "../sources/provider-errors";
|
|
38
39
|
import { dashedCoinPair, MAJOR_COIN_NAMES } from "../utils/crypto-pair";
|
|
39
40
|
import { boardCoinFor, primeBoardCoins } from "./board-coins";
|
|
40
41
|
import { knownGapMessage } from "./known-gaps";
|
|
@@ -48,13 +49,16 @@ import {
|
|
|
48
49
|
bloombergMarketHint,
|
|
49
50
|
endsInLegalForm,
|
|
50
51
|
isSearchOnlyVenue,
|
|
52
|
+
readLondonDottedSymbol,
|
|
51
53
|
readNearMissListing,
|
|
52
54
|
readTerminalListingCode,
|
|
53
55
|
terminalCodeReadAs,
|
|
54
56
|
unknownBloombergMarket,
|
|
57
|
+
type NearMissListing,
|
|
55
58
|
type TerminalListingCode,
|
|
56
59
|
} from "../tickers/terminal-codes";
|
|
57
60
|
import { listingAbroadVenue } from "../utils/sec";
|
|
61
|
+
import { savedAbroadForBareSymbol, savedUsListingOf } from "../tickers/search/bare-symbol";
|
|
58
62
|
import { listingSuffixCandidate, listingSuffixVenue, tickerHasListingSuffix } from "../sources/listing-symbols";
|
|
59
63
|
import { notTheCoinNote } from "./commands/crypto-hints";
|
|
60
64
|
|
|
@@ -336,6 +340,15 @@ export function isNoProviderError(error: unknown): boolean {
|
|
|
336
340
|
return isNoProviderMessage(error instanceof Error ? error.message : String(error ?? ""));
|
|
337
341
|
}
|
|
338
342
|
|
|
343
|
+
/**
|
|
344
|
+
* The router's answer that no source served a request because one failed (a
|
|
345
|
+
* timeout, a rate limit, a refused session) rather than missed: it says
|
|
346
|
+
* nothing about whether the symbol exists, unlike "Not a ticker".
|
|
347
|
+
*/
|
|
348
|
+
export function isUnsettledNoProvider(error: unknown): boolean {
|
|
349
|
+
return isNoProviderError(error) && !isNoSourceAnswer(error);
|
|
350
|
+
}
|
|
351
|
+
|
|
339
352
|
const MAX_OTHER_LISTINGS = 5;
|
|
340
353
|
|
|
341
354
|
type BareListing = Pick<CliListing, "symbol" | "exchange"> & Partial<Pick<CliListing, "saved">>;
|
|
@@ -357,33 +370,42 @@ function listsSeveralCompanies(venues: readonly ListingVenue[]): boolean {
|
|
|
357
370
|
return groupByCompany(venues, (venue) => venue.name).length > 1;
|
|
358
371
|
}
|
|
359
372
|
|
|
360
|
-
/**
|
|
373
|
+
/**
|
|
374
|
+
* Every other company or fund that lists the symbol, once, at its first venue
|
|
375
|
+
* in search order. A venue is another's only when its name says so: one with
|
|
376
|
+
* no name, or a depositary receipt named unlike the company (a CEDEAR's short
|
|
377
|
+
* form of its fund's name), is left out rather than guessed at, and one
|
|
378
|
+
* fund's venues are one fund however each words it (`sameListedName`).
|
|
379
|
+
*/
|
|
361
380
|
function otherCompanies(venues: readonly ListingVenue[], company: string): ListingVenue[] {
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
if (!company.trim()) return [];
|
|
382
|
+
const known = venues.filter((venue) => !namesDepositaryReceipt(venue.name) || sameListedName(venue.name, company));
|
|
383
|
+
return groupByCompany(known, (venue) => venue.name, sameListedName)
|
|
384
|
+
.filter((group) => !group.some((venue) => sameListedName(venue.name, company)))
|
|
364
385
|
.map((group) => group[0]!);
|
|
365
386
|
}
|
|
366
387
|
|
|
367
388
|
/**
|
|
368
|
-
* "CAR = Avis Budget Group, NASDAQ (
|
|
369
|
-
* symbol several companies list was read on, and where the
|
|
370
|
-
* Null when
|
|
371
|
-
* nothing.
|
|
389
|
+
* "CAR = Avis Budget Group, NASDAQ (different companies on XETRA, ASX)": the
|
|
390
|
+
* listing a bare symbol several companies list was read on, and where the
|
|
391
|
+
* others list it. Null when no other company is known to list it, so a symbol
|
|
392
|
+
* that means one thing (XLK, and its receipt in Buenos Aires) says nothing.
|
|
372
393
|
*/
|
|
373
394
|
export function listingChoiceNote(
|
|
374
395
|
symbol: string,
|
|
375
396
|
chosen: { exchange: string; name?: string | null },
|
|
376
397
|
venues: readonly ListingVenue[],
|
|
377
398
|
): string | null {
|
|
378
|
-
if (!chosen.exchange
|
|
399
|
+
if (!chosen.exchange) return null;
|
|
379
400
|
const chosenVenue = venues.find((venue) => venue.exchange === chosen.exchange);
|
|
380
401
|
const name = chosen.name?.trim() || chosenVenue?.name || "";
|
|
381
402
|
const others = otherCompanies(venues, chosenVenue?.name || name).map((venue) => venue.exchange)
|
|
382
403
|
.filter((exchange) => exchange !== chosen.exchange);
|
|
383
404
|
if (others.length === 0) return null;
|
|
384
405
|
const more = others.length - MAX_OTHER_LISTINGS;
|
|
385
|
-
const
|
|
386
|
-
|
|
406
|
+
const named = `${others.slice(0, MAX_OTHER_LISTINGS).join(", ")}${more > 0 ? ` and ${more} more: gloom search ${symbol}` : ""}`;
|
|
407
|
+
const which = others.length === 1 ? "a different company" : "different companies";
|
|
408
|
+
return `${symbol} = ${name ? `${shortCompanyName(name)}, ` : ""}${chosen.exchange} (${which} on ${named})`;
|
|
387
409
|
}
|
|
388
410
|
|
|
389
411
|
// Quotes of these never name a company's listing, so they are never a choice between companies.
|
|
@@ -407,34 +429,50 @@ export function listingReadNotes(listing: Pick<ListingArg, "readAs">): string[]
|
|
|
407
429
|
return listing.readAs ? [listing.readAs] : [];
|
|
408
430
|
}
|
|
409
431
|
|
|
432
|
+
/**
|
|
433
|
+
* The coin line for a bare coin ticker (SOL, ETH) a command read as a listing that is
|
|
434
|
+
* not the coin, from the quote it was read on (`notTheCoinNote`). A list, so a command can
|
|
435
|
+
* spread it into its warnings. A named listing (`ETH:ARCA`, `--exchange`) asked for that
|
|
436
|
+
* listing on purpose and gets none.
|
|
437
|
+
*/
|
|
438
|
+
export function coinListingNotes(
|
|
439
|
+
listing: Pick<CliListing, "symbol" | "exchange">,
|
|
440
|
+
quote: Pick<Quote, "name" | "instrumentType" | "stale" | "delisting"> | null | undefined,
|
|
441
|
+
): string[] {
|
|
442
|
+
const note = listing.exchange ? null : notTheCoinNote(listing.symbol, quote);
|
|
443
|
+
return note ? [note] : [];
|
|
444
|
+
}
|
|
445
|
+
|
|
410
446
|
/**
|
|
411
447
|
* `listingChoiceNote` for a listing a command resolved, from the quote it was
|
|
412
448
|
* read on: only a bare symbol went where the data service chose. A named
|
|
413
449
|
* listing whose code names several venues says which it was read as
|
|
414
|
-
* (`listingReadNotes`). A
|
|
450
|
+
* (`listingReadNotes`). A bare coin ticker read as another listing ends with
|
|
451
|
+
* the coin's pair (`coinListingNotes`). A list, so a command can spread it into
|
|
452
|
+
* its warnings.
|
|
415
453
|
*/
|
|
416
454
|
export async function listingChoiceNotes(
|
|
417
455
|
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "readAs">,
|
|
418
456
|
deps: ListingDeps,
|
|
419
|
-
quote: Pick<Quote, "name" | "listingExchangeName" | "exchangeName" | "instrumentType"> | null | undefined,
|
|
457
|
+
quote: Pick<Quote, "name" | "listingExchangeName" | "exchangeName" | "instrumentType" | "stale" | "delisting"> | null | undefined,
|
|
420
458
|
/** The venues `loadBareVenues` already started; looked up here when not given. */
|
|
421
459
|
venues?: Promise<ListingVenue[]> | null,
|
|
422
460
|
): Promise<string[]> {
|
|
423
|
-
|
|
424
|
-
if (!
|
|
461
|
+
const coin = coinListingNotes(listing, quote);
|
|
462
|
+
if (!isBareListing(listing)) return [...listingReadNotes(listing), ...coin];
|
|
463
|
+
if (!quote || UNLISTED_TYPES.has(quote.instrumentType?.trim().toUpperCase() ?? "")) return coin;
|
|
425
464
|
const exchange = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
|
|
426
|
-
if (!exchange) return
|
|
465
|
+
if (!exchange) return coin;
|
|
427
466
|
const listed = await (venues ?? loadBareVenues(listing, deps) ?? []);
|
|
428
467
|
const note = listingChoiceNote(listing.symbol, { exchange, name: quote.name }, listed);
|
|
429
|
-
return note ? [note] : [];
|
|
468
|
+
return [...note ? [note] : [], ...coin];
|
|
430
469
|
}
|
|
431
470
|
|
|
432
471
|
/**
|
|
433
472
|
* `listingChoiceNotes` for a command with no quote of its own to read the
|
|
434
473
|
* listing from (`fn`): it loads the listing's quote, and only for a bare
|
|
435
474
|
* symbol, so a named or saved listing costs nothing. A bare coin ticker read
|
|
436
|
-
* as
|
|
437
|
-
* coin's pair.
|
|
475
|
+
* as another listing (BTC, the Grayscale trust) ends with the coin's pair.
|
|
438
476
|
*/
|
|
439
477
|
export async function quotedListingChoiceNotes(
|
|
440
478
|
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "request" | "readAs">,
|
|
@@ -442,9 +480,7 @@ export async function quotedListingChoiceNotes(
|
|
|
442
480
|
): Promise<string[]> {
|
|
443
481
|
const venues = loadBareVenues(listing, deps);
|
|
444
482
|
if (!venues) return listingReadNotes(listing);
|
|
445
|
-
|
|
446
|
-
const coin = notTheCoinNote(listing.symbol, quote);
|
|
447
|
-
return [...await listingChoiceNotes(listing, deps, quote, venues), ...coin ? [coin] : []];
|
|
483
|
+
return listingChoiceNotes(listing, deps, await loadListingQuote(deps.dataProvider, listing), venues);
|
|
448
484
|
}
|
|
449
485
|
|
|
450
486
|
/**
|
|
@@ -561,31 +597,43 @@ export function futuresTryLine(symbol: string, request: ListingDataRequest): str
|
|
|
561
597
|
return `${future.symbol} ${future.name} future${retry ? ` (try: ${retry})` : ""}`;
|
|
562
598
|
}
|
|
563
599
|
|
|
600
|
+
/** A venue's listing as its key names it: under its own symbol there (BP:LSE for `BP.`), else the one asked for. */
|
|
601
|
+
function venueKey(symbol: string, venue: Pick<ListingVenue, "exchange" | "symbol">): string {
|
|
602
|
+
return `${venue.symbol ?? symbol}:${venue.exchange}`;
|
|
603
|
+
}
|
|
604
|
+
|
|
564
605
|
function describeOtherListings(
|
|
565
606
|
listing: BareListing,
|
|
566
607
|
{ exchange, resolved, others, choose }: BareListingVenues,
|
|
567
608
|
request: ListingDataRequest,
|
|
609
|
+
/** Listings just asked that had none: named as having none, never offered to try. */
|
|
610
|
+
without: readonly ListingVenue[] = [],
|
|
568
611
|
): OtherListingsMessage | null {
|
|
569
612
|
const future = futuresTryLine(listing.symbol, request);
|
|
570
|
-
|
|
613
|
+
const offered = others.filter((venue) => !without.includes(venue));
|
|
614
|
+
if (offered.length === 0 && !future && without.length === 0) return null;
|
|
571
615
|
const name = resolved?.name || listing.saved?.metadata.name?.trim() || "";
|
|
572
|
-
const lines =
|
|
573
|
-
const key =
|
|
616
|
+
const lines = offered.slice(0, MAX_OTHER_LISTINGS).map((venue) => {
|
|
617
|
+
const key = venueKey(listing.symbol, venue);
|
|
574
618
|
const retry = retryCommand(request, key);
|
|
575
619
|
return `${key}${venue.name ? ` ${venue.name}` : ""}${retry ? ` (try: ${retry})` : ""}`;
|
|
576
620
|
});
|
|
577
|
-
if (
|
|
621
|
+
if (offered.length > MAX_OTHER_LISTINGS) lines.push(`and ${offered.length - MAX_OTHER_LISTINGS} more: gloom search ${listing.symbol}`);
|
|
578
622
|
if (future) lines.push(future);
|
|
579
|
-
|
|
623
|
+
// Every other listing was asked and had nothing: they are named as such, with nothing to try.
|
|
624
|
+
const heading = choose ? "Listings" : lines.length === 0 ? `No ${request.noun} on its other listings either` : "Other listings";
|
|
625
|
+
const named = lines.length === 0 ? without : offered;
|
|
626
|
+
const details = lines.length === 0 ? `${heading}: ${without.map((venue) => venueKey(listing.symbol, venue)).join(", ")}.`
|
|
627
|
+
: lines.length === 1 ? `${heading}: ${lines[0]}` : [`${heading}:`, ...lines.map((line) => ` ${line}`)].join("\n");
|
|
580
628
|
return {
|
|
581
629
|
// The symbol went to no venue, so none of them is said to lack the data.
|
|
582
630
|
message: choose
|
|
583
631
|
? `Choose an exchange for ${listing.symbol}.`
|
|
584
632
|
: `${listing.symbol} resolved to ${name ? `${name} (${exchange})` : exchange}, which has no ${request.noun}.`,
|
|
585
|
-
details
|
|
633
|
+
details,
|
|
586
634
|
heading,
|
|
587
|
-
listings: [...
|
|
588
|
-
choices:
|
|
635
|
+
listings: [...named.map((venue) => venueKey(listing.symbol, venue)), ...future ? [futuresContractByCode(listing.symbol)!.symbol] : []],
|
|
636
|
+
choices: offered.slice(0, MAX_OTHER_LISTINGS).map((venue) => ({ key: venueKey(listing.symbol, venue), name: venue.name || null })),
|
|
589
637
|
choose,
|
|
590
638
|
};
|
|
591
639
|
}
|
|
@@ -610,27 +658,44 @@ async function otherListingsMessage(
|
|
|
610
658
|
return venues && describeOtherListings(listing, venues, request);
|
|
611
659
|
}
|
|
612
660
|
|
|
613
|
-
/** The listing on one venue, as `SYM:EXCH` names it. */
|
|
614
|
-
function namedListing(listing: CliListing,
|
|
615
|
-
const
|
|
661
|
+
/** The listing on one venue, as `SYM:EXCH` names it, under the venue's own symbol when search lists one. */
|
|
662
|
+
function namedListing(listing: CliListing, venue: Pick<ListingVenue, "exchange" | "symbol">): CliListing {
|
|
663
|
+
const { exchange } = venue;
|
|
664
|
+
const symbol = venue.symbol ?? listing.symbol;
|
|
665
|
+
const key = `${symbol}:${exchange}`;
|
|
616
666
|
const saved = canonicalExchange(listing.saved?.metadata.exchange) === exchange ? listing.saved : null;
|
|
617
|
-
return { symbol
|
|
667
|
+
return { symbol, exchange, key, saved, request: { symbol: key, exchange } };
|
|
618
668
|
}
|
|
619
669
|
|
|
670
|
+
/**
|
|
671
|
+
* Each listing's quote, null where it has none; `undefined` where the request
|
|
672
|
+
* itself failed, which says nothing of whether it has one.
|
|
673
|
+
*/
|
|
620
674
|
async function quoteListings(
|
|
621
675
|
dataProvider: DataProvider,
|
|
622
676
|
listings: readonly CliListing[],
|
|
623
677
|
refresh: boolean,
|
|
624
|
-
|
|
678
|
+
datesChangeBasis = false,
|
|
679
|
+
): Promise<Array<Quote | null | undefined>> {
|
|
625
680
|
if (listings.length === 0) return [];
|
|
626
681
|
const targets = listings.map((listing) => listing.request);
|
|
627
682
|
if (dataProvider.getQuotesBatch) {
|
|
628
|
-
const results = await dataProvider.getQuotesBatch(targets, { forceRefresh: refresh
|
|
629
|
-
|
|
683
|
+
const results = await dataProvider.getQuotesBatch(targets, { forceRefresh: refresh, ...(datesChangeBasis ? { datesChangeBasis } : {}) })
|
|
684
|
+
.catch(logFailure("cli.listing-arg", "Read listing quote batch failed", null));
|
|
685
|
+
if (!results) return targets.map(() => undefined);
|
|
686
|
+
return targets.map((target, index) => {
|
|
687
|
+
const result = results.find((entry) => entry.target === target) ?? results[index];
|
|
688
|
+
// No source quotes it, which is an answer; any other failure is none.
|
|
689
|
+
return result?.quote ?? (result && (result.error == null || isNoProviderError(result.error)) ? null : undefined);
|
|
690
|
+
});
|
|
630
691
|
}
|
|
631
692
|
return Promise.all(targets.map((target) => dataProvider.getQuote(target.symbol, target.exchange, {
|
|
632
693
|
cacheMode: refresh ? "refresh" : "default",
|
|
633
|
-
|
|
694
|
+
...(datesChangeBasis ? { datesChangeBasis } : {}),
|
|
695
|
+
}).catch((error: unknown) => {
|
|
696
|
+
logFailure("cli.listing-arg", "Read listing quote failed")(error);
|
|
697
|
+
return isNoProviderError(error) ? null : undefined;
|
|
698
|
+
})));
|
|
634
699
|
}
|
|
635
700
|
|
|
636
701
|
export type BareListingQuote =
|
|
@@ -661,7 +726,7 @@ export async function quoteBareListing(
|
|
|
661
726
|
listing: CliListing,
|
|
662
727
|
deps: ListingDeps,
|
|
663
728
|
request: ListingDataRequest,
|
|
664
|
-
{ refresh = false }: { refresh?: boolean } = {},
|
|
729
|
+
{ refresh = false, datesChangeBasis = false }: { refresh?: boolean; datesChangeBasis?: boolean } = {},
|
|
665
730
|
): Promise<BareListingQuote | null> {
|
|
666
731
|
const venues = await bareListingVenues(listing, deps);
|
|
667
732
|
if (!venues) return null;
|
|
@@ -669,17 +734,19 @@ export async function quoteBareListing(
|
|
|
669
734
|
const other = describeOtherListings(listing, venues, request);
|
|
670
735
|
return other && { kind: "missing", ...other };
|
|
671
736
|
}
|
|
672
|
-
const first = [namedListing(listing, venues.exchange)];
|
|
673
|
-
const rest = venues.others.map((venue) => namedListing(listing, venue
|
|
737
|
+
const first = [namedListing(listing, venues.resolved ?? { exchange: venues.exchange })];
|
|
738
|
+
const rest = venues.others.map((venue) => namedListing(listing, venue));
|
|
674
739
|
let tried = first;
|
|
675
|
-
let quotes = await quoteListings(deps.dataProvider, first, refresh);
|
|
740
|
+
let quotes = await quoteListings(deps.dataProvider, first, refresh, datesChangeBasis);
|
|
676
741
|
if (!quotes[0] && rest.length > 0) {
|
|
677
742
|
tried = [...first, ...rest];
|
|
678
|
-
quotes = [...quotes, ...await quoteListings(deps.dataProvider, rest, refresh)];
|
|
743
|
+
quotes = [...quotes, ...await quoteListings(deps.dataProvider, rest, refresh, datesChangeBasis)];
|
|
679
744
|
}
|
|
680
745
|
const found = quotes.findIndex(Boolean);
|
|
681
746
|
if (found < 0) {
|
|
682
|
-
|
|
747
|
+
// A listing just asked that had no quote is no listing to try.
|
|
748
|
+
const without = venues.others.filter((_venue, index) => quotes[index + 1] === null);
|
|
749
|
+
const other = describeOtherListings(listing, venues, request, without);
|
|
683
750
|
return other && { kind: "missing", ...other };
|
|
684
751
|
}
|
|
685
752
|
const answer = tried[found]!;
|
|
@@ -692,7 +759,8 @@ export async function quoteBareListing(
|
|
|
692
759
|
kind: "quoted",
|
|
693
760
|
listing: answer,
|
|
694
761
|
quote: quotes[found]!,
|
|
695
|
-
|
|
762
|
+
// Under the venue's own symbol (BP:LSE for `BP.`), the note names the listing, not only its venue.
|
|
763
|
+
note: chosenListingNote(listing.symbol, answer.symbol === listing.symbol ? answer.exchange : answer.key, others, without),
|
|
696
764
|
};
|
|
697
765
|
}
|
|
698
766
|
|
|
@@ -703,6 +771,8 @@ export interface ReadListing {
|
|
|
703
771
|
exchange: string;
|
|
704
772
|
/** The listing's company, from its quote. */
|
|
705
773
|
name: string | null;
|
|
774
|
+
/** A bare coin ticker read as a listing that is not the coin (`coinListingNotes`); null otherwise. */
|
|
775
|
+
coinNote: string | null;
|
|
706
776
|
}
|
|
707
777
|
|
|
708
778
|
/**
|
|
@@ -723,10 +793,14 @@ export async function readBareListing(
|
|
|
723
793
|
const listing = await resolveCliListing(symbol, null, deps);
|
|
724
794
|
if (listing.exchange) return null;
|
|
725
795
|
const answer = await loadListingQuoteAnswer(deps.dataProvider, listing);
|
|
726
|
-
if (answer.quote)
|
|
796
|
+
if (answer.quote) {
|
|
797
|
+
return { symbol: listing.symbol, exchange: listing.request.exchange, name: answer.quote.name?.trim() || null, coinNote: coinListingNotes(listing, answer.quote)[0] ?? null };
|
|
798
|
+
}
|
|
727
799
|
if (!isNoProviderError(answer.error)) return null;
|
|
728
800
|
const other = await quoteBareListing(listing, deps, request, { refresh });
|
|
729
|
-
if (other?.kind === "quoted")
|
|
801
|
+
if (other?.kind === "quoted") {
|
|
802
|
+
return { symbol: listing.symbol, exchange: other.listing.exchange, name: other.quote.name?.trim() || null, coinNote: coinListingNotes(listing, other.quote)[0] ?? null };
|
|
803
|
+
}
|
|
730
804
|
if (other?.choose) throw new ListingArgError(other.message, other.details, other.choices);
|
|
731
805
|
return null;
|
|
732
806
|
}
|
|
@@ -948,6 +1022,10 @@ export async function loadSavedListingOf(
|
|
|
948
1022
|
for (const listing of listings) {
|
|
949
1023
|
if (found.some((entry) => entry.listing.key === listing.key)) continue;
|
|
950
1024
|
const ticker = await loadSavedListing(store, listing);
|
|
1025
|
+
// A bare symbol's US listing comes before a row abroad saved under its bare key (SHEL, saved from SHEL.L).
|
|
1026
|
+
if (ticker && !listing.exchange && savedAbroadForBareSymbol(listing.symbol, ticker)) {
|
|
1027
|
+
add(listing, savedUsListingOf(listing.symbol, await store.loadAllTickers()));
|
|
1028
|
+
}
|
|
951
1029
|
add(listing, ticker);
|
|
952
1030
|
// A suffixed key as typed may be saved with a venue (7203.T:JPX, GARAN.IS:XNYS): any row of that symbol.
|
|
953
1031
|
if (!ticker && !listing.exchange && tickerHasListingSuffix(listing.symbol)) {
|
|
@@ -1087,7 +1165,9 @@ export async function resolveCliListing(
|
|
|
1087
1165
|
const terminal = named ? null : read;
|
|
1088
1166
|
// "AAPL GP" is written as a code, with a market code no venue answers to and no company's name: one entry, never AAPL and GP.
|
|
1089
1167
|
if (!terminal && !named) await refuseUnknownMarketCode(typed, deps);
|
|
1090
|
-
|
|
1168
|
+
// `BP.` is London's code for BP: its London line where London lists it, and `GE.` a US ticker with a stray dot.
|
|
1169
|
+
const dotted = terminal || named || exchangeOption?.trim() ? null : readLondonDottedSymbol(typed);
|
|
1170
|
+
let parsed = named ?? (dotted ? await londonDottedListing(dotted, deps) : parseListingArg(raw, exchangeOption, { ...options, terminalCode: terminal }));
|
|
1091
1171
|
if (!parsed.symbol) throw new ListingArgError("Ticker symbol is required.");
|
|
1092
1172
|
// $VIX is Schwab's code for ^VIX, unless a security trades as VIX: $AAPL is a cashtag.
|
|
1093
1173
|
const index = named || parsed.exchange ? null : await readDollarIndexCode(raw, deps);
|
|
@@ -1115,7 +1195,9 @@ export async function resolveCliListing(
|
|
|
1115
1195
|
parsed = market;
|
|
1116
1196
|
}
|
|
1117
1197
|
let listing = await settleSharedCode(parsed, deps);
|
|
1118
|
-
const
|
|
1198
|
+
const found = await loadSavedListing(deps.store, listing) ?? (typedKey ? await loadSuffixedRow(deps.store, typedKey, listing.exchange) : null);
|
|
1199
|
+
// A bare symbol is its US listing when it has one, whatever row abroad holds the bare key (SHEL saved from SHEL.L).
|
|
1200
|
+
const saved = listing.exchange ? found : await savedRowOfBareSymbol(listing.symbol, found, deps);
|
|
1119
1201
|
// A code nothing could read stays the venue it is saved on.
|
|
1120
1202
|
const savedExchange = canonicalExchange(saved?.metadata.exchange);
|
|
1121
1203
|
if (listing.sharedCode && savedExchange) listing = { symbol: listing.symbol, exchange: savedExchange, key: `${listing.symbol}:${savedExchange}` };
|
|
@@ -1129,6 +1211,21 @@ export async function resolveCliListing(
|
|
|
1129
1211
|
};
|
|
1130
1212
|
}
|
|
1131
1213
|
|
|
1214
|
+
/**
|
|
1215
|
+
* A two-letter symbol written with London's trailing dot (`BP.`, `RR.`) as its
|
|
1216
|
+
* London line, said as "Read BP. as BP:LSE.", where search lists it on LSE or
|
|
1217
|
+
* its quote there says it trades. Elsewhere the dot was a slip after the
|
|
1218
|
+
* ticker, dropped as a stray edge is (`GE.` is GE).
|
|
1219
|
+
*/
|
|
1220
|
+
async function londonDottedListing(dotted: NearMissListing, deps: ListingDeps): Promise<ListingArg> {
|
|
1221
|
+
const london = { symbol: dotted.symbol, exchange: "LSE" };
|
|
1222
|
+
const venues = await listingVenues(dotted.symbol, deps).catch(logFailure("cli.listing-arg", "Read listing venues failed", []));
|
|
1223
|
+
const listed = venues.some((venue) => venue.exchange === london.exchange) || await quotedOnVenue(london, deps);
|
|
1224
|
+
if (!listed) return parseListingArg(dotted.symbol);
|
|
1225
|
+
const key = `${london.symbol}:${london.exchange}`;
|
|
1226
|
+
return { ...parseListingArg(key), readAs: `Read ${dotted.typed} as ${key}.` };
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1132
1229
|
/**
|
|
1133
1230
|
* Fails a line written as a Bloomberg code whose market code no venue answers
|
|
1134
1231
|
* to (`AAPL GP`), suggesting the code of the symbol's own listing (`AAPL
|
|
@@ -1224,17 +1321,23 @@ const ONE_WORD_NAME = /^\p{L}[\p{L}\p{N}&'’.-]*$/u;
|
|
|
1224
1321
|
* for anything else, and when search names no company or is too busy to say.
|
|
1225
1322
|
*/
|
|
1226
1323
|
export async function oneWordCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<ListingArg | null> {
|
|
1324
|
+
const found = await findOneWordCompany(typed, deps);
|
|
1325
|
+
return found ? companyListingArg(typed, found) : null;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/** The company `oneWordCompanyName` reads a word as, before it is read as a listing. */
|
|
1329
|
+
async function findOneWordCompany(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<CompanyNameListing | null> {
|
|
1227
1330
|
const word = typed.trim();
|
|
1228
1331
|
if (!ONE_WORD_NAME.test(word) || knownGapMessage(word) || isMajorCoinName(word)) return null;
|
|
1229
|
-
const
|
|
1332
|
+
const found = await findCompanyName(word, deps, { oneWord: true }).catch((error: unknown) => {
|
|
1230
1333
|
if (!isProviderBusy(error)) throw error;
|
|
1231
1334
|
// Intentional ignore: a busy search says nothing about the name, so the word keeps its own "Not a ticker".
|
|
1232
1335
|
return null;
|
|
1233
1336
|
});
|
|
1234
|
-
if (!
|
|
1337
|
+
if (!found) return null;
|
|
1235
1338
|
// The board is read only for a word that names a company, and once for the run (`primeBoardCoins`).
|
|
1236
1339
|
if (/^[A-Za-z0-9]+$/.test(word)) await primeBoardCoins([`Not a ticker: ${word.toUpperCase()}.`]);
|
|
1237
|
-
return boardCoinFor(word) ? null :
|
|
1340
|
+
return boardCoinFor(word) ? null : found;
|
|
1238
1341
|
}
|
|
1239
1342
|
|
|
1240
1343
|
function isMajorCoinName(word: string): boolean {
|
|
@@ -1242,9 +1345,16 @@ function isMajorCoinName(word: string): boolean {
|
|
|
1242
1345
|
return [...MAJOR_COIN_NAMES.values()].some((name) => name.toLowerCase() === wanted);
|
|
1243
1346
|
}
|
|
1244
1347
|
|
|
1348
|
+
function findCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">, options: { oneWord?: boolean } = {}): Promise<CompanyNameListing | null> {
|
|
1349
|
+
return findListingOfCompanyName(deps.dataProvider, typed, { meant: true, quoted: true, ...options });
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1245
1352
|
async function readCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">, options: { oneWord?: boolean } = {}): Promise<ListingArg | null> {
|
|
1246
|
-
const found = await
|
|
1247
|
-
|
|
1353
|
+
const found = await findCompanyName(typed, deps, options);
|
|
1354
|
+
return found ? companyListingArg(typed, found) : null;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
function companyListingArg(typed: string, found: CompanyNameListing): ListingArg {
|
|
1248
1358
|
const key = `${found.symbol}:${found.exchange}`;
|
|
1249
1359
|
// A fund or an index carrying the name asks about the company's stock: "Did you mean JPM (JPMorgan Chase & Co.)?".
|
|
1250
1360
|
const meant = found.meant ? { symbol: found.meant.symbol, name: found.meant.name } : null;
|
|
@@ -1252,6 +1362,35 @@ async function readCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvi
|
|
|
1252
1362
|
return { ...parseListingArg(key), readAs: `Read "${typed.trim()}" as ${key} (${found.name}).${question}`, ...meant ? { meant } : {} };
|
|
1253
1363
|
}
|
|
1254
1364
|
|
|
1365
|
+
/**
|
|
1366
|
+
* The listing search names for a company's name typed where a ticker goes,
|
|
1367
|
+
* as a sentence offers it: `PLTR (Palantir Technologies Inc.)`, a listing
|
|
1368
|
+
* abroad with its venue (`SIE:XETRA (Siemens AG)`), a fund or an index that
|
|
1369
|
+
* carries the name as the company's stock it means. Null when the words name
|
|
1370
|
+
* no company, search is busy, or the listing found is the word itself: a
|
|
1371
|
+
* real ticker is never a company's name. Never throws.
|
|
1372
|
+
*/
|
|
1373
|
+
export async function companyNameSuggestion(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<{ symbol: string; name: string } | null> {
|
|
1374
|
+
const words = typed.trim().replace(/^\$/, "");
|
|
1375
|
+
if (!words) return null;
|
|
1376
|
+
try {
|
|
1377
|
+
const found = isCompanyNameInput(words) ? await findCompanyName(words, deps).catch(busyAsMiss) : await findOneWordCompany(words, deps);
|
|
1378
|
+
const listing = found?.meant ?? found;
|
|
1379
|
+
if (!listing || listing.symbol.toUpperCase() === words.toUpperCase()) return null;
|
|
1380
|
+
return { symbol: isUsListingExchange(listing.exchange) ? listing.symbol : `${listing.symbol}:${listing.exchange}`, name: listing.name };
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
logFailure("cli.listing-arg", "Search a company name for a suggestion failed")(error);
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/** A search too busy to answer is no company, as a name that matches none. */
|
|
1388
|
+
function busyAsMiss(error: unknown): null {
|
|
1389
|
+
if (!isProviderBusy(error)) throw error;
|
|
1390
|
+
// Intentional ignore: a busy search says nothing about the name, so no company is suggested.
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1255
1394
|
/** `resolveCliListing` for a command: a bad exchange fails the command with its message. */
|
|
1256
1395
|
export async function requireCliListing(
|
|
1257
1396
|
raw: string,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { mkdir, stat, writeFile } from "fs/promises";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { posix, win32 } from "path";
|
|
4
|
+
|
|
5
|
+
interface OutputPathContext {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
home?: string;
|
|
8
|
+
platform?: NodeJS.Platform;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** `C:\Users\me\out.csv`, `C:/out.csv`, `\\server\share\out.csv`: a path only Windows reads. */
|
|
12
|
+
const WINDOWS_PATH = /^(?:[A-Za-z]:[\\/]|\\\\)/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The absolute path `--output <path>` names, in any form a shell passes it:
|
|
16
|
+
* relative to the folder the command runs in, absolute, or under the home
|
|
17
|
+
* folder (`~/Desktop/rv.csv`, which a shell leaves as typed after
|
|
18
|
+
* `--output=`). A Windows path is read as one on Windows; anywhere else it
|
|
19
|
+
* is refused, since it would write a file named after the whole path.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveOutputPath(raw: string, context: OutputPathContext = {}): string {
|
|
22
|
+
const platform = context.platform ?? process.platform;
|
|
23
|
+
const paths = platform === "win32" ? win32 : posix;
|
|
24
|
+
const typed = raw.trim();
|
|
25
|
+
if (!typed) throw new Error("--output needs a file to write, such as --output report.csv.");
|
|
26
|
+
if (platform !== "win32" && WINDOWS_PATH.test(typed)) {
|
|
27
|
+
throw new Error(`${typed} is a Windows path. Write a path on this computer, such as --output ~/Desktop/report.csv.`);
|
|
28
|
+
}
|
|
29
|
+
const home = context.home ?? homedir();
|
|
30
|
+
const homeRelative = /^~(?:$|[\\/])/.test(typed);
|
|
31
|
+
const expanded = homeRelative ? paths.join(home, typed.slice(1)) : typed;
|
|
32
|
+
return paths.resolve(context.cwd ?? process.cwd(), expanded);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function errorCode(error: unknown): string | undefined {
|
|
36
|
+
return error && typeof error === "object" && "code" in error ? String((error as { code: unknown }).code) : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isFolder(path: string, paths: typeof posix): Error {
|
|
40
|
+
return new Error(`Cannot write ${path}: it is a folder. Name a file in it, such as ${paths.join(path, "report.csv")}.`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Fails when `path` names a folder: checked before a report loads, so it stops at once. */
|
|
44
|
+
export async function checkOutputPath(path: string, platform: NodeJS.Platform = process.platform): Promise<void> {
|
|
45
|
+
const existing = await stat(path).catch(() => {
|
|
46
|
+
// Intentional ignore: a path that does not exist yet is the usual case, and writing reports any real failure.
|
|
47
|
+
return null;
|
|
48
|
+
});
|
|
49
|
+
if (existing?.isDirectory()) throw isFolder(path, platform === "win32" ? win32 : posix);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Writes a command's output to `path`, creating the folders it names as
|
|
54
|
+
* `shot --output` does, and failing with what to do when it cannot.
|
|
55
|
+
*/
|
|
56
|
+
export async function writeOutputFile(path: string, contents: string, platform: NodeJS.Platform = process.platform): Promise<void> {
|
|
57
|
+
const paths = platform === "win32" ? win32 : posix;
|
|
58
|
+
try {
|
|
59
|
+
await mkdir(paths.dirname(path), { recursive: true });
|
|
60
|
+
await writeFile(path, contents);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const code = errorCode(error);
|
|
63
|
+
if (code === "EISDIR") throw isFolder(path, paths);
|
|
64
|
+
if (code === "EACCES" || code === "EPERM") throw new Error(`Cannot write ${path}: permission denied.`);
|
|
65
|
+
if (code === "ENOTDIR" || code === "EEXIST") throw new Error(`Cannot write ${path}: a file stands where one of its folders would be.`);
|
|
66
|
+
throw new Error(`Cannot write ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
HeadlessPaneArgumentDef,
|
|
3
3
|
HeadlessPaneDefinition,
|
|
4
4
|
HeadlessPaneOptionDef,
|
|
5
|
+
HeadlessPaneReference,
|
|
5
6
|
PaneDef,
|
|
6
7
|
PaneTemplateDef,
|
|
7
8
|
} from "../../types/plugin";
|
|
@@ -26,6 +27,8 @@ export interface PaneFunctionCapability {
|
|
|
26
27
|
screenshotReadiness: PaneFunctionScreenshotReadiness;
|
|
27
28
|
dataRequirements: string[];
|
|
28
29
|
limitations: string[];
|
|
30
|
+
/** Lists the catalog prints under the options (EQS's sector and industry names). */
|
|
31
|
+
reference?: HeadlessPaneReference[];
|
|
29
32
|
options: PaneFunctionOptionDef[];
|
|
30
33
|
/** A rendered view that refuses any option it does not declare (PaneDef.reportOptionsOnly), as a headless report does. */
|
|
31
34
|
strictOptions?: boolean;
|