gloomberb 0.18.1 → 0.18.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/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +106 -5
- package/src/api-client/commodity-balances.ts +6 -0
- package/src/api-client/company-kpis.ts +9 -0
- package/src/api-client/crypto-markets.ts +9 -0
- package/src/api-client/data.ts +21 -5
- package/src/api-client/equity-screener.ts +2 -0
- package/src/api-client/index.ts +2 -0
- package/src/api-client/request.ts +5 -3
- package/src/api-client/telemetry.ts +17 -0
- package/src/api-client/types.ts +5 -0
- package/src/app/global-shortcuts.ts +1 -16
- package/src/app/pane-runtime/plugin-bindings.ts +3 -1
- package/src/app/pane-runtime/ticker-open-runtime.ts +17 -5
- package/src/app/runtime/plugin-bindings.ts +6 -2
- package/src/app/runtime/update.ts +28 -8
- package/src/architecture/pane-tabs-allowlist.json +65 -0
- package/src/brokers/logos.ts +66 -0
- package/src/brokers/profile-form.ts +3 -0
- package/src/brokers/remote-broker-adapter.ts +13 -3
- package/src/capabilities/index.ts +1 -1
- package/src/capabilities/service-capabilities.ts +7 -1
- package/src/cli/base-converter.ts +60 -18
- package/src/cli/commands/market.ts +354 -187
- package/src/cli/commands/news-search.ts +35 -8
- package/src/cli/commands/system.ts +35 -1
- package/src/cli/commands/ticker.ts +167 -60
- package/src/cli/currency-hints.ts +27 -0
- package/src/cli/fx-pair.ts +41 -4
- package/src/cli/glossary.ts +57 -4
- package/src/cli/help.ts +1 -1
- package/src/cli/helpers.ts +36 -3
- package/src/cli/index.ts +12 -1
- package/src/cli/issuer-membership.ts +103 -0
- package/src/cli/known-gaps.ts +37 -1
- package/src/cli/listing-arg.ts +51 -3
- package/src/cli/name-entries.ts +51 -12
- package/src/cli/pane-functions/capabilities.ts +2 -0
- package/src/cli/pane-functions/catalog.ts +19 -1
- package/src/cli/pane-functions/discovery.ts +15 -1
- package/src/cli/pane-functions/dom.ts +11 -5
- package/src/cli/pane-functions/freshness.ts +4 -4
- package/src/cli/pane-functions/function-help.ts +27 -3
- package/src/cli/pane-functions/headless.ts +31 -11
- package/src/cli/pane-functions/index.ts +67 -7
- package/src/cli/pane-functions/lock-note.ts +7 -4
- package/src/cli/pane-functions/resolver.ts +21 -6
- package/src/cli/quote-answer.ts +179 -0
- package/src/cli/report-tables.ts +136 -18
- package/src/cli/result.ts +12 -4
- package/src/cli/series-search.ts +2 -1
- package/src/cli/symbol-list.ts +47 -11
- package/src/cli/types.ts +4 -0
- package/src/components/chart/composite/composite-chart.tsx +25 -7
- package/src/components/chart/composite/drawing-store.ts +26 -6
- package/src/components/chart/composite/format.ts +13 -1
- package/src/components/chart/composite/interactions.ts +2 -1
- package/src/components/chart/composite/level-labels.ts +30 -0
- package/src/components/chart/composite/levels.ts +17 -6
- package/src/components/chart/composite/panel-surface.tsx +69 -8
- package/src/components/chart/composite/types.ts +6 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +1 -1
- package/src/components/command-bar/routes/root/command-items.ts +32 -6
- package/src/components/command-bar/routes/ticker-search/actions.ts +6 -0
- package/src/components/data-table/view.tsx +23 -3
- package/src/components/index.ts +3 -0
- package/src/components/layout/detached-pane-shell.tsx +1 -0
- package/src/components/layout/floating-pane.tsx +1 -0
- package/src/components/layout/header.tsx +228 -55
- package/src/components/layout/pane/content.tsx +16 -14
- package/src/components/layout/pane/footer/index.tsx +22 -3
- package/src/components/layout/pane/footer/model.ts +99 -4
- package/src/components/layout/pane/header-tabs.tsx +15 -1
- package/src/components/layout/pane/header.tsx +10 -3
- package/src/components/layout/pane/index.tsx +1 -0
- package/src/components/layout/pane/pane-tabs.tsx +9 -1
- package/src/components/onboarding/onboarding-frame.tsx +9 -11
- package/src/components/onboarding/use-onboarding-broker-form.ts +5 -2
- package/src/components/onboarding/wizard-model.ts +10 -3
- package/src/components/ui/button.tsx +11 -4
- package/src/components/ui/choice-dialog.tsx +8 -5
- package/src/components/ui/display.tsx +11 -6
- package/src/components/ui/fields.tsx +100 -20
- package/src/components/ui/icon.tsx +6 -1
- package/src/components/ui/list-row-lines.tsx +87 -0
- package/src/components/ui/list-view.tsx +14 -0
- package/src/components/ui/logo-mark.tsx +64 -0
- package/src/components/ui/number-step.ts +54 -0
- package/src/components/ui/select-button.tsx +5 -4
- package/src/components/ui/select-field.tsx +3 -3
- package/src/components/ui/stat-grid.tsx +28 -3
- package/src/components/ui/status.tsx +20 -4
- package/src/components/ui/tabs.tsx +9 -1
- package/src/components/ui/toggle.tsx +17 -3
- package/src/core/app-services.ts +4 -0
- package/src/core/state/app/instrument.ts +8 -3
- package/src/data/config/store/normalize.ts +4 -1
- package/src/data/plugin-cache.ts +32 -6
- package/src/docs/snapshot.json +5 -5
- package/src/i18n/es.ts +31 -3
- package/src/i18n/ja.ts +31 -3
- package/src/i18n/ko.ts +31 -3
- package/src/i18n/zh-cn.ts +32 -4
- package/src/i18n/zh-tw.ts +32 -4
- package/src/market-data/commodity-names.ts +80 -0
- package/src/market-data/coordinator/auxiliary.ts +3 -1
- package/src/market-data/coordinator/chart.ts +3 -1
- package/src/market-data/futures-board.ts +38 -1
- package/src/market-data/futures-contract.ts +91 -0
- package/src/market-data/fx-closes.ts +78 -1
- package/src/market-data/market/change-basis.ts +219 -0
- package/src/market-data/market/format.ts +28 -12
- package/src/market-data/market/freshness.ts +38 -0
- package/src/market-data/market/status.ts +55 -9
- package/src/market-data/market/year-to-date.ts +46 -0
- package/src/market-data/quotes/contributions.ts +12 -0
- package/src/market-data/quotes/delisting.ts +34 -0
- package/src/market-data/quotes/live-valuation.ts +23 -0
- package/src/market-data/quotes/resolution.ts +8 -2
- package/src/market-data/tax-equivalent.ts +10 -2
- package/src/notifications/app-notifier.ts +2 -2
- package/src/notifications/notification-log.ts +8 -6
- package/src/plugins/builtin/alerts/alert-engine.ts +3 -1
- package/src/plugins/builtin/alerts/cli.ts +26 -18
- package/src/plugins/builtin/alerts/events.ts +1 -0
- package/src/plugins/builtin/alerts/headless.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +13 -4
- package/src/plugins/builtin/alerts/research-rules.ts +1 -0
- package/src/plugins/builtin/analytics/risk-pane.tsx +10 -7
- package/src/plugins/builtin/attention/pane.tsx +16 -14
- package/src/plugins/builtin/attention/trending.tsx +8 -6
- package/src/plugins/builtin/awards/pane.tsx +6 -6
- package/src/plugins/builtin/backtest/client.ts +5 -2
- package/src/plugins/builtin/backtest/export.ts +1 -1
- package/src/plugins/builtin/backtest/window.ts +16 -3
- package/src/plugins/builtin/broker-manager/add-flow.tsx +40 -8
- package/src/plugins/builtin/broker-manager/detail.tsx +26 -1
- package/src/plugins/builtin/broker-manager/index.tsx +20 -9
- package/src/plugins/builtin/broker-manager/model.ts +5 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -1
- package/src/plugins/builtin/broker-manager/table.ts +5 -1
- package/src/plugins/builtin/broker-trading/account-picker.tsx +91 -0
- package/src/plugins/builtin/broker-trading/chart-levels.ts +54 -0
- package/src/plugins/builtin/broker-trading/command.ts +103 -0
- package/src/plugins/builtin/broker-trading/controller.ts +688 -0
- package/src/plugins/builtin/broker-trading/dock.tsx +184 -0
- package/src/plugins/builtin/broker-trading/headless.ts +76 -0
- package/src/plugins/builtin/broker-trading/index.tsx +77 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +383 -0
- package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
- package/src/plugins/builtin/broker-trading/orders.tsx +437 -0
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +121 -0
- package/src/plugins/builtin/broker-trading/pane-model.ts +207 -0
- package/src/plugins/builtin/broker-trading/pane.tsx +507 -0
- package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +117 -0
- package/src/plugins/builtin/broker-trading/ticket.tsx +578 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +1 -1
- package/src/plugins/builtin/buildout/format.ts +1 -0
- package/src/plugins/builtin/buildout/model/index.ts +16 -7
- package/src/plugins/builtin/buildout/pane/index.tsx +11 -7
- package/src/plugins/builtin/buildout/pane/status.tsx +3 -3
- package/src/plugins/builtin/catalysts/client.ts +2 -2
- package/src/plugins/builtin/catalysts/pane.tsx +3 -3
- package/src/plugins/builtin/chart-composer/cli-options.ts +19 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -3
- package/src/plugins/builtin/chart-composer/headless.ts +2 -1
- package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +32 -11
- package/src/plugins/builtin/chart-composer/presets.ts +2 -2
- package/src/plugins/builtin/chart-composer/series-catalog.ts +4 -2
- package/src/plugins/builtin/chart-composer/shortcuts.ts +3 -3
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +5 -2
- package/src/plugins/builtin/chat/index.tsx +1 -1
- package/src/plugins/builtin/chat/status-widget.tsx +1 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +6 -1
- package/src/plugins/builtin/chat/unread-inbox.ts +1 -1
- package/src/plugins/builtin/commodity-balances/client.ts +6 -4
- package/src/plugins/builtin/commodity-balances/headless.ts +41 -7
- package/src/plugins/builtin/commodity-balances/index.tsx +19 -4
- package/src/plugins/builtin/commodity-balances/model.ts +22 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +39 -9
- package/src/plugins/builtin/company-attention/app-rank.tsx +4 -5
- package/src/plugins/builtin/company-attention/pane.tsx +13 -11
- package/src/plugins/builtin/company-kpis/client.ts +3 -2
- package/src/plugins/builtin/company-kpis/headless.ts +5 -7
- package/src/plugins/builtin/company-kpis/model.ts +85 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +30 -15
- package/src/plugins/builtin/composite-plugins.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +2 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
- package/src/plugins/builtin/correlation/settings.ts +1 -0
- package/src/plugins/builtin/cot/headless.ts +1 -1
- package/src/plugins/builtin/cot/index.tsx +1 -1
- package/src/plugins/builtin/cot/model.ts +35 -12
- package/src/plugins/builtin/cot/pane.tsx +4 -3
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +2 -1
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
- package/src/plugins/builtin/crypto-board/client.ts +33 -2
- package/src/plugins/builtin/crypto-board/headless.ts +5 -2
- package/src/plugins/builtin/crypto-board/model.ts +28 -8
- package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
- package/src/plugins/builtin/dividend-yield/headless.ts +30 -8
- package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
- package/src/plugins/builtin/doe/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/headless.ts +4 -4
- package/src/plugins/builtin/earnings-calls/lock-note.ts +12 -3
- package/src/plugins/builtin/econ/calendar-model.ts +35 -0
- package/src/plugins/builtin/econ/index.tsx +31 -4
- package/src/plugins/builtin/econ/print-watch.ts +279 -0
- package/src/plugins/builtin/equity-screener/headless.ts +30 -5
- package/src/plugins/builtin/equity-screener/model.ts +68 -2
- package/src/plugins/builtin/equity-screener/pane.tsx +129 -119
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -2
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +29 -7
- package/src/plugins/builtin/exposure/client.ts +4 -1
- package/src/plugins/builtin/exposure/editor.tsx +3 -3
- package/src/plugins/builtin/exposure/evidence.ts +4 -1
- package/src/plugins/builtin/exposure/holdings.ts +3 -1
- package/src/plugins/builtin/exposure/pane.tsx +43 -21
- package/src/plugins/builtin/filing-events/index.tsx +9 -3
- package/src/plugins/builtin/futures/table.tsx +6 -3
- package/src/plugins/builtin/futures-curve/pane.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +4 -1
- package/src/plugins/builtin/gpu/changes.tsx +10 -10
- package/src/plugins/builtin/gpu/equities.tsx +4 -1
- package/src/plugins/builtin/gpu/history.tsx +4 -2
- package/src/plugins/builtin/gpu/pane.tsx +5 -3
- package/src/plugins/builtin/holders/client.ts +2 -1
- package/src/plugins/builtin/holders/headless.ts +2 -1
- package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
- package/src/plugins/builtin/insider/base-values.ts +76 -0
- package/src/plugins/builtin/insider/client.ts +3 -0
- package/src/plugins/builtin/insider/headless.ts +48 -10
- package/src/plugins/builtin/insider/index.tsx +17 -8
- package/src/plugins/builtin/insider/issuer.ts +34 -4
- package/src/plugins/builtin/insider/signals-client.ts +2 -2
- package/src/plugins/builtin/insider/table-model.ts +35 -13
- package/src/plugins/builtin/insider/table.tsx +5 -2
- package/src/plugins/builtin/iv-history/events.ts +145 -0
- package/src/plugins/builtin/iv-history/format.ts +3 -0
- package/src/plugins/builtin/iv-history/headless.ts +91 -7
- package/src/plugins/builtin/jobs/headless.ts +2 -2
- package/src/plugins/builtin/jobs/lock-note.ts +14 -3
- package/src/plugins/builtin/jodi/pane.tsx +4 -4
- package/src/plugins/builtin/market-halts/model.ts +31 -6
- package/src/plugins/builtin/market-halts/pane.tsx +3 -0
- package/src/plugins/builtin/market-movers/client.ts +4 -3
- package/src/plugins/builtin/market-movers/screener.ts +4 -3
- package/src/plugins/builtin/market-valuation/pane.tsx +2 -1
- package/src/plugins/builtin/members/pane.tsx +8 -3
- package/src/plugins/builtin/news/wire/feed-config.ts +29 -12
- package/src/plugins/builtin/news/wire/index.tsx +7 -2
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +11 -3
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -2
- package/src/plugins/builtin/news/wire/rss/source.ts +13 -4
- package/src/plugins/builtin/notes/cli.ts +1 -1
- package/src/plugins/builtin/notes/export.ts +17 -6
- package/src/plugins/builtin/notes/files.ts +16 -5
- package/src/plugins/builtin/options/enrichment-model.ts +10 -9
- package/src/plugins/builtin/options/footer.ts +25 -20
- package/src/plugins/builtin/options/live-quotes.ts +33 -18
- package/src/plugins/builtin/options/model-spot.ts +70 -4
- package/src/plugins/builtin/options/view.tsx +118 -35
- package/src/plugins/builtin/options-calculator/pane.tsx +7 -4
- package/src/plugins/builtin/options-positioning/headless.ts +11 -3
- package/src/plugins/builtin/options-positioning/model.ts +24 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
- package/src/plugins/builtin/options-scenario/client.ts +19 -1
- package/src/plugins/builtin/options-scenario/headless.ts +13 -6
- package/src/plugins/builtin/options-scenario/pane.tsx +54 -11
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +5 -4
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +3 -0
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -5
- package/src/plugins/builtin/portfolio-list/cli/render.ts +34 -10
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +23 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +33 -14
- package/src/plugins/builtin/portfolio-list/settings.ts +20 -2
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +24 -3
- package/src/plugins/builtin/power/client.ts +2 -2
- package/src/plugins/builtin/power/pane.tsx +36 -31
- package/src/plugins/builtin/primary-dealers/client.ts +137 -62
- package/src/plugins/builtin/primary-dealers/headless.ts +30 -5
- package/src/plugins/builtin/primary-dealers/index.tsx +3 -2
- package/src/plugins/builtin/primary-dealers/model.ts +179 -129
- package/src/plugins/builtin/primary-dealers/pane.tsx +135 -15
- package/src/plugins/builtin/research/analyst-model.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +4 -3
- package/src/plugins/builtin/risk-factors/index.tsx +12 -3
- package/src/plugins/builtin/scanner/feed.ts +6 -1
- package/src/plugins/builtin/scanner/headless.ts +1 -1
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/client.ts +2 -1
- package/src/plugins/builtin/sectors/client.ts +4 -3
- package/src/plugins/builtin/series-inventory.ts +1 -0
- package/src/plugins/builtin/shared/cloud-denied.ts +7 -6
- package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +10 -11
- package/src/plugins/builtin/shared/free-routes.ts +24 -0
- package/src/plugins/builtin/shared/headless-options.ts +11 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +89 -7
- package/src/plugins/builtin/shared/research-trading.tsx +64 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
- package/src/plugins/builtin/shared/volatility/options.ts +2 -1
- package/src/plugins/builtin/social-mentions/client.ts +2 -1
- package/src/plugins/builtin/social-mentions/headless.ts +1 -1
- package/src/plugins/builtin/social-mentions/lock-note.ts +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -2
- package/src/plugins/builtin/themes/model.ts +43 -10
- package/src/plugins/builtin/themes/pane.tsx +8 -6
- package/src/plugins/builtin/ticker-detail/overview/model.ts +44 -10
- package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +82 -21
- package/src/plugins/builtin/ticker-detail/pane.tsx +21 -14
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +5 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +37 -8
- package/src/plugins/builtin/time-sales/pane.tsx +2 -2
- package/src/plugins/builtin/trace-bonds/client.ts +14 -3
- package/src/plugins/builtin/trace-bonds/headless.ts +1 -1
- package/src/plugins/builtin/vol-surface/client.ts +23 -3
- package/src/plugins/builtin/vol-surface/headless.ts +33 -14
- package/src/plugins/builtin/vol-surface/model.ts +30 -10
- package/src/plugins/builtin/vol-surface/pane.tsx +35 -15
- package/src/plugins/builtin/vol-surface/stored.ts +34 -3
- package/src/plugins/builtin/vol-surface/tables.ts +14 -0
- package/src/plugins/builtin-editorial.ts +1 -1
- package/src/plugins/compat.ts +3 -0
- package/src/plugins/external-runtime.ts +5 -0
- package/src/plugins/loader.ts +6 -0
- package/src/plugins/plugin-icon.ts +61 -0
- package/src/plugins/registry/host-actions.ts +2 -1
- package/src/plugins/registry/index.ts +9 -1
- package/src/plugins/runtime/context.tsx +5 -0
- package/src/plugins/runtime/index.tsx +3 -0
- package/src/portfolio/column-values.ts +13 -4
- package/src/portfolio/cost-fx.ts +5 -28
- package/src/public/__snapshots__/public-api.test.ts.snap +5 -0
- package/src/public/broker.ts +8 -0
- package/src/public/market-data.ts +5 -0
- package/src/renderers/dom/desktop/control-styles.ts +7 -1
- package/src/renderers/dom/desktop/controls.tsx +40 -8
- package/src/renderers/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/list-view.tsx +51 -19
- package/src/renderers/dom/desktop/menu.tsx +10 -1
- package/src/renderers/dom/desktop/query-bar.tsx +2 -0
- package/src/renderers/dom/desktop/select-field.tsx +8 -6
- package/src/renderers/dom/host/style.ts +6 -1
- package/src/renderers/dom/host/tabs.tsx +22 -5
- package/src/renderers/dom/host/top-surface.ts +127 -0
- package/src/renderers/dom/styles.css +51 -5
- package/src/renderers/electrobun/bun/core-capabilities.ts +21 -1
- package/src/renderers/electrobun/bun/desktop/backend.ts +10 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
- package/src/renderers/electrobun/bun/external-plugins.ts +3 -0
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +16 -0
- package/src/renderers/electrobun/view/external-plugins.ts +1 -0
- package/src/renderers/electrobun/view/news-source.ts +4 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +8 -1
- package/src/renderers/opentui/start.tsx +7 -0
- package/src/series/validate.ts +2 -2
- package/src/sources/gloomberb-cloud/index.ts +57 -17
- package/src/sources/gloomberb-cloud/normalizers.ts +4 -0
- package/src/sources/history-result.ts +3 -0
- package/src/sources/provider-errors.ts +3 -1
- package/src/sources/provider-router/history.ts +49 -6
- package/src/sources/provider-router/index.ts +18 -1
- package/src/state/app/activity.ts +13 -0
- package/src/sync/controller.ts +4 -0
- package/src/sync/core-contributors.ts +62 -11
- package/src/sync/local-only.ts +121 -0
- package/src/sync/react.ts +6 -0
- package/src/sync/types.ts +3 -0
- package/src/telemetry/crash-reports.ts +4 -0
- package/src/telemetry/update-failures-node.ts +59 -0
- package/src/telemetry/update-failures.ts +307 -0
- package/src/tickers/company-name-listing.ts +9 -7
- package/src/tickers/company-name.ts +43 -0
- package/src/tickers/listing-fate.ts +57 -8
- package/src/tickers/search/index.ts +24 -3
- package/src/time-series/price-comparison.ts +35 -0
- package/src/time-series/resolve.ts +52 -11
- package/src/time-series/spec.ts +1 -1
- package/src/time-series/types.ts +5 -0
- package/src/types/broker.ts +56 -1
- package/src/types/config.ts +6 -0
- package/src/types/data-provider.ts +11 -1
- package/src/types/financials.ts +23 -0
- package/src/types/headless.ts +19 -2
- package/src/types/instrument.ts +4 -0
- package/src/types/issuer-listings.ts +28 -0
- package/src/types/plugin.ts +24 -6
- package/src/types/price-history.ts +6 -1
- package/src/types/trading.ts +67 -0
- package/src/ui/host.tsx +4 -0
- package/src/updater/failure.ts +163 -0
- package/src/updater/index.ts +87 -23
- package/src/utils/base-currency.ts +108 -0
- package/src/utils/currency-names.ts +128 -0
- package/src/utils/currency-pair.ts +28 -1
- package/src/utils/currency-units.ts +32 -1
- package/src/utils/dom-screenshot.ts +22 -9
- package/src/utils/exchange-notation.generated.ts +360 -0
- package/src/utils/exchanges.ts +15 -322
- package/src/utils/format.ts +31 -1
- package/src/utils/instrument-identity.ts +38 -1
- package/src/utils/options.ts +46 -1
- package/src/utils/price-history.ts +4 -0
- package/src/utils/reported-money.ts +8 -4
- package/src/utils/sec.ts +68 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ConnectionHealthRegistry } from "../../core/connection-health";
|
|
2
2
|
import type { TickerRepository } from "../../data/ticker-repository";
|
|
3
3
|
import type { AppConfig } from "../../types/config";
|
|
4
|
+
import type { BrokerAdapter } from "../../types/broker";
|
|
4
5
|
import type { DataProvider } from "../../types/data-provider";
|
|
5
6
|
import type { PersistedResourceValue } from "../../types/persistence";
|
|
6
7
|
import type {
|
|
@@ -20,6 +21,7 @@ export interface PaneDiscoveryOptions {
|
|
|
20
21
|
tickerRepository?: TickerRepository;
|
|
21
22
|
panes?: Map<string, PaneDef>;
|
|
22
23
|
paneTemplates?: Map<string, PaneTemplateDef>;
|
|
24
|
+
brokers?: Map<string, BrokerAdapter>;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** Throws only if a plugin actually reaches for a dependency discovery has no answer for. */
|
|
@@ -43,10 +45,12 @@ export function createPaneDiscoveryContext(options: PaneDiscoveryOptions): Gloom
|
|
|
43
45
|
} {
|
|
44
46
|
const panes = options.panes ?? new Map<string, PaneDef>();
|
|
45
47
|
const paneTemplates = options.paneTemplates ?? new Map<string, PaneTemplateDef>();
|
|
48
|
+
const brokers = options.brokers ?? new Map<string, BrokerAdapter>();
|
|
46
49
|
return {
|
|
47
50
|
...buildDiscoveryContext({
|
|
48
51
|
panes,
|
|
49
52
|
paneTemplates,
|
|
53
|
+
brokers,
|
|
50
54
|
getConfig: options.getConfig,
|
|
51
55
|
marketData: options.marketData ?? unavailable<DataProvider>("Market data"),
|
|
52
56
|
tickerRepository: options.tickerRepository ?? unavailable<TickerRepository>("The ticker repository"),
|
|
@@ -60,6 +64,7 @@ export function createPaneDiscoveryContext(options: PaneDiscoveryOptions): Gloom
|
|
|
60
64
|
function buildDiscoveryContext({
|
|
61
65
|
panes,
|
|
62
66
|
paneTemplates,
|
|
67
|
+
brokers,
|
|
63
68
|
getConfig,
|
|
64
69
|
marketData,
|
|
65
70
|
tickerRepository,
|
|
@@ -67,6 +72,7 @@ function buildDiscoveryContext({
|
|
|
67
72
|
}: {
|
|
68
73
|
panes: Map<string, PaneDef>;
|
|
69
74
|
paneTemplates: Map<string, PaneTemplateDef>;
|
|
75
|
+
brokers: Map<string, BrokerAdapter>;
|
|
70
76
|
getConfig: () => AppConfig;
|
|
71
77
|
marketData: DataProvider;
|
|
72
78
|
tickerRepository: TickerRepository;
|
|
@@ -84,7 +90,8 @@ function buildDiscoveryContext({
|
|
|
84
90
|
registerCommand: () => {},
|
|
85
91
|
registerCommandBarSearchProvider: () => () => {},
|
|
86
92
|
registerColumn: () => {},
|
|
87
|
-
registerBroker: () => {},
|
|
93
|
+
registerBroker: (broker) => { brokers.set(broker.id, broker); },
|
|
94
|
+
getBrokerAdapter: (brokerType) => brokers.get(brokerType) ?? null,
|
|
88
95
|
registerCapability: () => {},
|
|
89
96
|
registerTickerResearchTab: () => {},
|
|
90
97
|
registerShortcut: () => {},
|
|
@@ -155,10 +162,15 @@ function buildDiscoveryContext({
|
|
|
155
162
|
|
|
156
163
|
export async function createPaneCatalog(context: MarketContext, plugins: GloomPlugin[]): Promise<PaneFunctionCatalog> {
|
|
157
164
|
const setupPlugins: GloomPlugin[] = [];
|
|
165
|
+
const brokers = new Map<string, BrokerAdapter>();
|
|
166
|
+
const previousResolver = context.resolveBroker;
|
|
167
|
+
const resolveBroker = (brokerType: string) => brokers.get(brokerType) ?? null;
|
|
168
|
+
context.resolveBroker = resolveBroker;
|
|
158
169
|
const { panes, paneTemplates, ...discoveryContext } = createPaneDiscoveryContext({
|
|
159
170
|
getConfig: () => context.config,
|
|
160
171
|
marketData: context.dataProvider,
|
|
161
172
|
tickerRepository: context.store,
|
|
173
|
+
brokers,
|
|
162
174
|
});
|
|
163
175
|
|
|
164
176
|
const destroy = () => {
|
|
@@ -166,6 +178,8 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
|
|
|
166
178
|
for (const plugin of setupPlugins.splice(0).reverse()) {
|
|
167
179
|
try { plugin.dispose?.(); } catch (error) { failure ??= error; }
|
|
168
180
|
}
|
|
181
|
+
brokers.clear();
|
|
182
|
+
if (context.resolveBroker === resolveBroker) context.resolveBroker = previousResolver;
|
|
169
183
|
if (failure) throw failure;
|
|
170
184
|
};
|
|
171
185
|
|
|
@@ -6,6 +6,8 @@ import type { PaneFunctionReport } from "./report";
|
|
|
6
6
|
import type { ResolvedPaneFunction } from "./resolver";
|
|
7
7
|
import { collectShotSymbols } from "./data";
|
|
8
8
|
import { lockNoteLines } from "./lock-note";
|
|
9
|
+
import { createHeadlessPaneContext } from "./headless";
|
|
10
|
+
import type { HeadlessPaneContext } from "../../types/headless";
|
|
9
11
|
import { lockNoteReason } from "../../plugins/builtin/shared/pro-lock-note";
|
|
10
12
|
import { renderDesktopShot, type PaneScreenshotResult } from "./screenshot";
|
|
11
13
|
import { deriveRenderedFreshness, formatFreshnessLine, type ReportFreshness } from "./freshness";
|
|
@@ -204,10 +206,12 @@ function reportRows(result: PaneScreenshotResult): DesktopPaneShotRenderedRow[]
|
|
|
204
206
|
}];
|
|
205
207
|
}
|
|
206
208
|
|
|
207
|
-
export function buildDomPaneReportFromRender(
|
|
209
|
+
export async function buildDomPaneReportFromRender(
|
|
208
210
|
resolved: ResolvedPaneFunction,
|
|
209
211
|
result: PaneScreenshotResult,
|
|
210
|
-
|
|
212
|
+
/** Reads data for the lock note, which names a free route only when it has data for the symbol. */
|
|
213
|
+
context?: HeadlessPaneContext,
|
|
214
|
+
): Promise<PaneFunctionReport> {
|
|
211
215
|
const rows = reportRows(result);
|
|
212
216
|
const hasStructuredRows = (result.render.reportRows ?? result.render.rows).length > 0;
|
|
213
217
|
// A table that reported every row is whole, whatever the viewport cut from the capture.
|
|
@@ -218,7 +222,8 @@ export function buildDomPaneReportFromRender(
|
|
|
218
222
|
const truncated = truncationReasons.length > 0
|
|
219
223
|
|| (result.render.reportTruncationReasons ? false : result.render.truncated);
|
|
220
224
|
const failureReason = renderedFailureReason(result, rows);
|
|
221
|
-
|
|
225
|
+
// A pane that drew rows beside an error or empty marker ("Quote unavailable.") has data: only one with none has no data for its symbols.
|
|
226
|
+
const unavailableSymbols = failureReason && rows.length === 0 && result.symbols.length > 0 ? result.symbols : [];
|
|
222
227
|
// What the view leaves out and how to see more, such as OMON's strikes outside its window.
|
|
223
228
|
const rendered = renderedReportNotices(result.render.semanticUi ?? []);
|
|
224
229
|
const notices = [...(resolved.pane.reportNotices?.(resolved.instance.settings ?? {}) ?? []), ...rendered.notices];
|
|
@@ -227,11 +232,12 @@ export function buildDomPaneReportFromRender(
|
|
|
227
232
|
// what is locked and how to reach it, and cites no source.
|
|
228
233
|
const gate = !hasStructuredRows ? result.render.accessGate ?? null : null;
|
|
229
234
|
const lockNote = gate
|
|
230
|
-
? resolved.pane.reportLockNote?.({
|
|
235
|
+
? await resolved.pane.reportLockNote?.({
|
|
231
236
|
gate,
|
|
232
237
|
symbol: result.symbols[0] ?? null,
|
|
233
238
|
token: resolved.token,
|
|
234
239
|
...gate === "sign-in" && asksToVerifyEmail(result.render.visibleText ?? "") ? { verifyEmail: true } : {},
|
|
240
|
+
...context ? { context } : {},
|
|
235
241
|
}) ?? null
|
|
236
242
|
: null;
|
|
237
243
|
const locked = gate === "sign-in" || lockNote ? gate : null;
|
|
@@ -399,7 +405,7 @@ export async function buildDomFunctionReport(
|
|
|
399
405
|
const failure = renderedReportFailure(result.render.semanticUi ?? []);
|
|
400
406
|
if (failure) throw new RenderedReportFailure(failure.message, failure.details);
|
|
401
407
|
try {
|
|
402
|
-
return buildDomPaneReportFromRender(resolved, result);
|
|
408
|
+
return await buildDomPaneReportFromRender(resolved, result, createHeadlessPaneContext(resolved, context, new AbortController().signal));
|
|
403
409
|
} catch (error) {
|
|
404
410
|
return failedDomReport(resolved, rawArg, error);
|
|
405
411
|
}
|
|
@@ -43,7 +43,7 @@ export interface ReportFreshness {
|
|
|
43
43
|
feed?: "live" | "delayed";
|
|
44
44
|
basis?: string;
|
|
45
45
|
/** Set when the dates are the start of a period: the status line then reads `Sep 2026 (monthly)`. */
|
|
46
|
-
periodicity?: "monthly" | "quarterly" | "annual";
|
|
46
|
+
periodicity?: "monthly" | "quarterly" | "semiannual" | "annual";
|
|
47
47
|
/**
|
|
48
48
|
* The newest bar's week or month had not ended, and the last day it covers
|
|
49
49
|
* so far (`asOf`, when known): the status line reads `month to date, through
|
|
@@ -335,7 +335,7 @@ function datasetLagDays(observed: ReportTime, periodicity: ReportFreshness["peri
|
|
|
335
335
|
const month = day.getUTCMonth();
|
|
336
336
|
const through = !periodicity ? Date.UTC(year, month, day.getUTCDate())
|
|
337
337
|
// Day 0 of the month after the period is its last day.
|
|
338
|
-
: periodicity === "monthly" ? Date.UTC(year, month + 1, 0)
|
|
338
|
+
: periodicity === "monthly" || periodicity === "semiannual" ? Date.UTC(year, month + 1, 0)
|
|
339
339
|
: periodicity === "quarterly" ? Date.UTC(year, month - (month % 3) + 3, 0)
|
|
340
340
|
: Date.UTC(year, 12, 0);
|
|
341
341
|
const today = new Date(now);
|
|
@@ -589,13 +589,13 @@ function tradingDayText(date: string, market: string | undefined): string {
|
|
|
589
589
|
return market ? `${market} trading day ${dayText(date)}` : dayText(date);
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
-
/** `Sep 2026`, `Q2 2026` or `2026` for the period a date starts. */
|
|
592
|
+
/** `Sep 2026`, `Q2 2026` or `2026` for the period a date starts; a semiannual estimate's month. */
|
|
593
593
|
function periodText(date: string, periodicity: NonNullable<ReportFreshness["periodicity"]>): string {
|
|
594
594
|
const day = new Date(`${date.slice(0, 10)}T00:00:00Z`);
|
|
595
595
|
if (!Number.isFinite(day.getTime())) return date;
|
|
596
596
|
const year = day.getUTCFullYear();
|
|
597
597
|
const month = day.getUTCMonth();
|
|
598
|
-
return periodicity === "monthly" ? `${MONTHS[month]} ${year}`
|
|
598
|
+
return periodicity === "monthly" || periodicity === "semiannual" ? `${MONTHS[month]} ${year}`
|
|
599
599
|
: periodicity === "quarterly" ? `Q${Math.floor(month / 3) + 1} ${year}`
|
|
600
600
|
: `${year}`;
|
|
601
601
|
}
|
|
@@ -93,7 +93,7 @@ const POP_OUT = key("p", "op out");
|
|
|
93
93
|
const STEP = key("←/→", " step");
|
|
94
94
|
/** THEM and MEMB members: open them in RRG, CORR, SIW or RIPL, or save them as a watchlist. */
|
|
95
95
|
const MEMBERS_MENU = key(".", " open members in, save as watchlist");
|
|
96
|
-
const CHART_KEYS = [key("s", "eries"), key("i", "ndicators"), key("
|
|
96
|
+
const CHART_KEYS = [key("s", "eries"), key("i", "ndicators"), key("b", "ars"), key("f", "ormulas")];
|
|
97
97
|
|
|
98
98
|
export const FUNCTION_HELP: Readonly<Record<string, FunctionHelp>> = {
|
|
99
99
|
// Research companies
|
|
@@ -971,7 +971,7 @@ export const FUNCTION_HELP: Readonly<Record<string, FunctionHelp>> = {
|
|
|
971
971
|
bloomberg: [],
|
|
972
972
|
},
|
|
973
973
|
PDLR: {
|
|
974
|
-
summary: "Weekly primary dealer net positions and
|
|
974
|
+
summary: "Weekly primary dealer net positions with history, by sector and Treasury tenor (bills, coupons, TIPS, FRN), and settlement fails.",
|
|
975
975
|
usage: ["PDLR"],
|
|
976
976
|
keys: [TABS],
|
|
977
977
|
data: same("Weekly, as published"),
|
|
@@ -1110,7 +1110,7 @@ export const FUNCTION_HELP: Readonly<Record<string, FunctionHelp>> = {
|
|
|
1110
1110
|
docs: "PORT",
|
|
1111
1111
|
},
|
|
1112
1112
|
ALRT: {
|
|
1113
|
-
summary: "Your price alerts with current price, distance to target and trigger state, plus alerts
|
|
1113
|
+
summary: "Your price alerts with current price, distance to target and trigger state, plus alerts on earnings, CPI and FOMC dates, filings and 13F, news keywords, analyst changes, 52-week highs and lows, unusual volume, short interest, insider and congress trades, catalysts, IV spikes, options flow and perps.",
|
|
1114
1114
|
usage: ["ALRT"],
|
|
1115
1115
|
keys: [key("a", "dd alert"), key("e", "dit"), key("d", "elete"), key("m", "re-arm")],
|
|
1116
1116
|
data: QUOTES,
|
|
@@ -1238,6 +1238,30 @@ export const FUNCTION_HELP: Readonly<Record<string, FunctionHelp>> = {
|
|
|
1238
1238
|
data: null,
|
|
1239
1239
|
bloomberg: [],
|
|
1240
1240
|
},
|
|
1241
|
+
BUY: {
|
|
1242
|
+
summary: "Open the shared Trade ticket with a buy quantity and optional limit price. Focus starts on Review; choose an account and review the broker preview before confirming.",
|
|
1243
|
+
usage: ["BUY AAPL 10", "BUY AAPL 10 150.25"],
|
|
1244
|
+
keys: [key("r", "eview"), key("o", "rders")],
|
|
1245
|
+
data: same("Broker account and quote"),
|
|
1246
|
+
bloomberg: [],
|
|
1247
|
+
docs: "BR",
|
|
1248
|
+
},
|
|
1249
|
+
SELL: {
|
|
1250
|
+
summary: "Open the shared Trade ticket with a sell quantity and optional limit price. Focus starts on Review; holdings and broker capabilities are checked before confirmation.",
|
|
1251
|
+
usage: ["SELL AAPL 10", "SELL AAPL 10 350.25"],
|
|
1252
|
+
keys: [key("r", "eview"), key("o", "rders")],
|
|
1253
|
+
data: same("Broker account and quote"),
|
|
1254
|
+
bloomberg: [],
|
|
1255
|
+
docs: "BR",
|
|
1256
|
+
},
|
|
1257
|
+
ORD: {
|
|
1258
|
+
summary: "Open orders and recent activity for a selected broker profile and account. Review a modification or confirm cancellation from an open order; uncertain outcomes require reconciliation before another action.",
|
|
1259
|
+
usage: ["ORD"],
|
|
1260
|
+
keys: [key("m", "odify"), key("c", "ancel")],
|
|
1261
|
+
data: same("Broker account snapshot"),
|
|
1262
|
+
bloomberg: [],
|
|
1263
|
+
docs: "BR",
|
|
1264
|
+
},
|
|
1241
1265
|
|
|
1242
1266
|
// Layouts and settings
|
|
1243
1267
|
GL: {
|
|
@@ -89,17 +89,36 @@ function normalizeSymbolList(value: string): string[] {
|
|
|
89
89
|
return symbols;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
const COUNT_WORDS = ["zero", "one", "two", "three", "four", "five"];
|
|
93
|
+
const EXAMPLE_TICKERS = ["SPY", "QQQ", "IWM", "DIA", "TLT"];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* What a function that compares several tickers answers fewer: how many it
|
|
97
|
+
* needs and a command that runs, the reader's own tickers first (`CMP needs
|
|
98
|
+
* two or more tickers: fn CMP SPY,QQQ`).
|
|
99
|
+
*/
|
|
100
|
+
function tooFewTickersMessage(token: string, minimum: number, given: readonly string[], command: "fn" | "shot" = "fn"): string {
|
|
101
|
+
const example = [...given, ...EXAMPLE_TICKERS.filter((ticker) => !given.includes(ticker))].slice(0, Math.max(minimum, given.length + 1));
|
|
102
|
+
return `${token} needs ${COUNT_WORDS[minimum] ?? minimum} or more tickers: ${command} ${token} ${example.join(",")}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The tickers a function's argument lists, as its report reads them. */
|
|
106
|
+
export function argumentSymbols(rawArgument: string): string[] {
|
|
107
|
+
return normalizeSymbolList(rawArgument);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function validateSymbolCount(
|
|
93
111
|
definition: HeadlessPaneDefinition,
|
|
94
112
|
token: string,
|
|
95
113
|
symbols: string[],
|
|
114
|
+
command: "fn" | "shot" = "fn",
|
|
96
115
|
): void {
|
|
97
116
|
const minimum = definition.argument.minimum
|
|
98
117
|
?? (definition.argument.optional ? 0 : 1);
|
|
99
118
|
const maximum = definition.argument.maximum;
|
|
100
119
|
if (symbols.length < minimum) {
|
|
101
120
|
if (minimum <= 1) throw requiredArgumentError(definition, token);
|
|
102
|
-
throw new Error(
|
|
121
|
+
throw new Error(tooFewTickersMessage(token, minimum, symbols, command));
|
|
103
122
|
}
|
|
104
123
|
if (maximum != null && symbols.length > maximum) {
|
|
105
124
|
throw new Error(`${token} accepts at most ${maximum} symbols.`);
|
|
@@ -168,12 +187,12 @@ async function loadHeadlessPaneModel(
|
|
|
168
187
|
return result;
|
|
169
188
|
}
|
|
170
189
|
|
|
171
|
-
type HeadlessMarketContext = Pick<MarketContext, "config" | "store" | "refresh"> & Partial<Pick<MarketContext, "persistence" | "seriesCapabilities">> & {
|
|
190
|
+
type HeadlessMarketContext = Pick<MarketContext, "config" | "store" | "refresh" | "resolveBroker"> & Partial<Pick<MarketContext, "persistence" | "seriesCapabilities">> & {
|
|
172
191
|
dataProvider: HeadlessPaneContext["marketData"];
|
|
173
192
|
};
|
|
174
193
|
|
|
175
194
|
/** What a headless loader reads the market, the account and the saved collections through. */
|
|
176
|
-
function createHeadlessPaneContext(
|
|
195
|
+
export function createHeadlessPaneContext(
|
|
177
196
|
resolved: ResolvedPaneFunction,
|
|
178
197
|
context: HeadlessMarketContext,
|
|
179
198
|
signal: AbortSignal,
|
|
@@ -186,6 +205,7 @@ function createHeadlessPaneContext(
|
|
|
186
205
|
...(context.refresh ? { refresh: true } : {}),
|
|
187
206
|
settings: resolved.instance.settings,
|
|
188
207
|
capabilities: getSharedRegistry() ?? context.seriesCapabilities,
|
|
208
|
+
resolveBroker: context.resolveBroker,
|
|
189
209
|
async resolvePortfolio(id) {
|
|
190
210
|
const portfolio = findNamedCollection(context.config.portfolios, id);
|
|
191
211
|
if (!portfolio) return null;
|
|
@@ -498,7 +518,7 @@ function renderRows(
|
|
|
498
518
|
};
|
|
499
519
|
return renderTable(
|
|
500
520
|
columns.map((column) => ({
|
|
501
|
-
header: displayedHeader(column),
|
|
521
|
+
header: column.textHeader ?? displayedHeader(column),
|
|
502
522
|
align: column.align,
|
|
503
523
|
width: column.width,
|
|
504
524
|
shrink: column.shrink,
|
|
@@ -734,14 +754,14 @@ export function headlessReportTables(
|
|
|
734
754
|
const rowsResult = result as HeadlessRowsResult;
|
|
735
755
|
const columns = displayedColumns(columnsFor(rowsResult.rows, rowsResult.columns, definition.columns));
|
|
736
756
|
const rowIdentities = rowIdentitiesOf(rowsResult.rows, rowsResult);
|
|
737
|
-
const own = exportRowsTable(title, columns, rowsResult.rows);
|
|
757
|
+
const own = exportRowsTable(title, columns, rowsResult.rows, { status: true });
|
|
738
758
|
const interval = rowsResult.interval?.trim();
|
|
739
759
|
tables = [{ ...own, ...(rowIdentities ? { rowIdentities } : {}), ...(interval ? { identity: { interval } } : {}) }, ...(rowsResult.exportTables ?? []).map((extra) => {
|
|
740
760
|
const identity = {
|
|
741
761
|
...(extra.unit?.trim() ? { currencyOrUnit: extra.unit.trim() } : {}),
|
|
742
762
|
...(extra.interval?.trim() ? { interval: extra.interval.trim() } : {}),
|
|
743
763
|
};
|
|
744
|
-
const table = exportRowsTable(extra.title, displayedColumns(extra.columns), extra.rows);
|
|
764
|
+
const table = exportRowsTable(extra.title, displayedColumns(extra.columns), extra.rows, { status: true });
|
|
745
765
|
return Object.keys(identity).length > 0 ? { ...table, identity } : table;
|
|
746
766
|
})];
|
|
747
767
|
break;
|
|
@@ -757,7 +777,7 @@ export function headlessReportTables(
|
|
|
757
777
|
const entries = exportEntriesTable(section.title, section.entries);
|
|
758
778
|
return Object.keys(named).length > 0 ? { ...entries, identity: named } : entries;
|
|
759
779
|
}
|
|
760
|
-
const table = exportRowsTable(section.title, displayedColumns(columnsFor(section.rows, section.columns, definition.columns)), section.rows);
|
|
780
|
+
const table = exportRowsTable(section.title, displayedColumns(columnsFor(section.rows, section.columns, definition.columns)), section.rows, { status: true });
|
|
761
781
|
const identity = {
|
|
762
782
|
...named,
|
|
763
783
|
...(section.unit?.trim() ? { currencyOrUnit: section.unit.trim() } : {}),
|
|
@@ -775,7 +795,7 @@ export function headlessReportTables(
|
|
|
775
795
|
}
|
|
776
796
|
case "snapshot": {
|
|
777
797
|
const snapshot = result as HeadlessSnapshotResult;
|
|
778
|
-
tables = [exportRowsTable(title, displayedColumns(columnsFor(snapshot.items, undefined, definition.columns)), snapshot.items)];
|
|
798
|
+
tables = [exportRowsTable(title, displayedColumns(columnsFor(snapshot.items, undefined, definition.columns)), snapshot.items, { status: true })];
|
|
779
799
|
break;
|
|
780
800
|
}
|
|
781
801
|
default: {
|
|
@@ -939,9 +959,9 @@ export async function buildHeadlessFunctionReport(
|
|
|
939
959
|
loaded = await loadResolvedHeadlessPaneModel(resolved, context, rawArgument);
|
|
940
960
|
} catch (error) {
|
|
941
961
|
// A Pro pane the server refused reports what its wall covers, as its rendered view does.
|
|
942
|
-
const locked = lockedHeadlessReport(resolved, error, () => (
|
|
962
|
+
const locked = await lockedHeadlessReport(resolved, error, () => (
|
|
943
963
|
buildHeadlessPaneLoadArgs(resolved.headless!, resolved.token, rawArgument, resolved.options).symbols
|
|
944
|
-
));
|
|
964
|
+
), createHeadlessPaneContext(resolved, context, new AbortController().signal));
|
|
945
965
|
if (locked) return locked;
|
|
946
966
|
throw error;
|
|
947
967
|
}
|
|
@@ -36,9 +36,9 @@ import {
|
|
|
36
36
|
import { isNotATickerMessage } from "../not-a-ticker";
|
|
37
37
|
import { isCliFailure } from "../errors";
|
|
38
38
|
import { isTickerLinkPeer } from "../../layout/ticker-navigation";
|
|
39
|
-
import { knownGapMessage, commandPointer, topicPointer } from "../known-gaps";
|
|
40
|
-
import { selectReportTables } from "../report-tables";
|
|
41
|
-
import { cliOutputWritten } from "../result";
|
|
39
|
+
import { knownGapMessage, commandPointer, commandPointerLeads, topicPointer } from "../known-gaps";
|
|
40
|
+
import { selectReportTables, type CliReportTables } from "../report-tables";
|
|
41
|
+
import { cliOutputWritten, type CliErrorObject } from "../result";
|
|
42
42
|
import type { CatalogCommandHit } from "../unknown-command";
|
|
43
43
|
import { CLI_EXIT_CODES } from "../exit-codes";
|
|
44
44
|
import { shotOptionRefusal } from "./shot-options";
|
|
@@ -204,6 +204,63 @@ function foundNothing(data: { rowCount: number; unavailableSymbols: readonly str
|
|
|
204
204
|
return data.rowCount === 0 || data.unavailableSymbols.length >= data.rowCount;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
/** A listing key as asked or reported (`BHP:XASX`, `fred:DGS10`, `SPY`), in one case. */
|
|
208
|
+
const listingKey = (symbol: string) => symbol.trim().toUpperCase();
|
|
209
|
+
const bareKey = (key: string) => key.split(":")[0]!;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The listings asked that a report has no data for. A report names one by its
|
|
213
|
+
* full key; one that names it by the bare symbol (`SAN` for `SAN:EPA`), or by
|
|
214
|
+
* the listing a bare symbol resolved to (`SXR8:XETRA` for `SXR8`), stands for
|
|
215
|
+
* the one listing asked that way, never for a listing asked by its own key:
|
|
216
|
+
* with `BHP:XASX` and `BHP` asked, `BHP:XASX` unavailable leaves `BHP`.
|
|
217
|
+
*/
|
|
218
|
+
function missingListings(asked: readonly string[], unavailable: readonly string[]): Set<string> {
|
|
219
|
+
const keys = asked.map(listingKey);
|
|
220
|
+
const exact = new Set(keys);
|
|
221
|
+
const missing = new Set<string>();
|
|
222
|
+
for (const entry of unavailable.map(listingKey)) {
|
|
223
|
+
if (exact.has(entry)) {
|
|
224
|
+
missing.add(entry);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const loose = keys.filter((key) => bareKey(key) === bareKey(entry) && key.includes(":") !== entry.includes(":"));
|
|
228
|
+
if (loose.length === 1) missing.add(loose[0]!);
|
|
229
|
+
}
|
|
230
|
+
return missing;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Whether a table holds a value past its first column (a row's label or date) and its reasons: a figure, a time, any text. */
|
|
234
|
+
function tablesHoldValues(tables: CliReportTables): boolean {
|
|
235
|
+
return tables.tables.some((table) => {
|
|
236
|
+
const skipped = new Set(table.columns.flatMap((header, index) => index === 0 || header === "status" || header === "value_status" ? [index] : []));
|
|
237
|
+
return table.rows.some((row) => row.some((cell, index) => !skipped.has(index) && cell !== "" && cell != null));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Why `fn` fails a report it printed, or null when the report has data. It
|
|
243
|
+
* fails when it is empty (a pane behind a wall reads nothing), when it has no
|
|
244
|
+
* data for any listing asked and its tables hold no value either, or when it
|
|
245
|
+
* is a list (`failsWithoutData`) with no name that has any. A report with data
|
|
246
|
+
* for some of what was asked, warnings or errors beside it, is a result: a
|
|
247
|
+
* loss-maker's P/E band without a P/E, or IV history with only realized
|
|
248
|
+
* volatility, exits 0.
|
|
249
|
+
*/
|
|
250
|
+
export function reportFailure(
|
|
251
|
+
data: { empty: boolean; rowCount: number; symbols: readonly string[]; unavailableSymbols: readonly string[]; errors?: readonly string[]; reason?: unknown },
|
|
252
|
+
asked: string,
|
|
253
|
+
{ failsWithoutData = false, tables }: { failsWithoutData?: boolean; tables?: CliReportTables } = {},
|
|
254
|
+
): CliErrorObject | null {
|
|
255
|
+
const missing = missingListings(data.symbols, data.unavailableSymbols);
|
|
256
|
+
const noListing = data.symbols.length > 0 && data.symbols.every((symbol) => missing.has(listingKey(symbol)));
|
|
257
|
+
const noValues = tables ? !tablesHoldValues(tables) : false;
|
|
258
|
+
if (!data.empty && !(noListing && noValues) && !(failsWithoutData && foundNothing(data))) return null;
|
|
259
|
+
const reasons = [...data.errors ?? [], ...typeof data.reason === "string" ? [data.reason] : []];
|
|
260
|
+
const unavailable = data.unavailableSymbols.length > 0 ? `no data for ${data.unavailableSymbols.join(", ")}` : "no data";
|
|
261
|
+
return { code: "no_data", message: `${asked}: ${reasons.length > 0 ? reasons.join("; ") : unavailable}.`.replace(/\.\.$/, ".") };
|
|
262
|
+
}
|
|
263
|
+
|
|
207
264
|
export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
|
|
208
265
|
await runPaneCliCommand(ctx, async () => {
|
|
209
266
|
await withPaneRuntime(ctx, args, async ({ parsed, context, registry, resolved }) => {
|
|
@@ -277,13 +334,15 @@ export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
|
|
|
277
334
|
? { ...report.data, glossary: glossary.map(({ term, short, definition }) => ({ term, ...(short ? { short } : {}), definition })) }
|
|
278
335
|
: report.data;
|
|
279
336
|
const warnings = [...parsed.listingNotes ?? [], ...await choice ?? [], ...report.data.warnings ?? []];
|
|
337
|
+
// A report with no data is printed as it is, and fails: exit 1, and `ok: false` in JSON with the reason.
|
|
338
|
+
const failure = reportFailure(report.data, ["fn", resolved.token, parsed.arg.trim()].filter(Boolean).join(" "), {
|
|
339
|
+
failsWithoutData: resolved.headless?.failsWithoutData, tables: report.tables });
|
|
280
340
|
// The report states its own freshness in its data and text; JSON cites it as `source` too.
|
|
281
|
-
ctx.printResult({ data, exportFreshness: report.data.freshness, ...(warnings.length > 0 ? { warnings } : {}) }, {
|
|
341
|
+
ctx.printResult({ data, exportFreshness: report.data.freshness, ...(warnings.length > 0 ? { warnings } : {}), ...(failure ? { failure } : {}) }, {
|
|
282
342
|
text: () => glossary ? `${report.text}\n\n${renderReportGlossary(resolved.token, glossary)}` : report.text,
|
|
283
343
|
...(tabular ? { tables: selectReportTables(report.tables, resolved.tableSection) } : {}),
|
|
284
344
|
});
|
|
285
|
-
|
|
286
|
-
if (resolved.headless?.failsWithoutData && foundNothing(report.data)) process.exitCode = CLI_EXIT_CODES.error;
|
|
345
|
+
if (failure) process.exitCode = CLI_EXIT_CODES.error;
|
|
287
346
|
}, { strictHeadlessOptions: true, tableSection: true });
|
|
288
347
|
});
|
|
289
348
|
}
|
|
@@ -486,6 +545,7 @@ export async function runPaneCatalog(args: string[], ctx: CliCommandContext) {
|
|
|
486
545
|
// A setting or a report can be a command, not a function: "light theme" names `config themes` beside THEM,
|
|
487
546
|
// "drip" names `portfolio show --drip` beside DVD.
|
|
488
547
|
const setting = commandPointer(parsed.query);
|
|
548
|
+
const settingLeads = commandPointerLeads(parsed.query);
|
|
489
549
|
// The data catalog series the query names, which `fn G` charts.
|
|
490
550
|
const series = findCatalogSeries(ctx.plugins, parsed.query);
|
|
491
551
|
const listed = pointer
|
|
@@ -496,7 +556,7 @@ export async function runPaneCatalog(args: string[], ctx: CliCommandContext) {
|
|
|
496
556
|
...series.length ? { metadata: { series } } : {},
|
|
497
557
|
...(gap || setting) && ctx.cliOptions.format !== "text" ? { warnings: [gap, setting].filter((note): note is string => !!note) } : {},
|
|
498
558
|
}, {
|
|
499
|
-
text: () => renderPaneCatalogReport(filtered, effectiveParsed, glossary, gap, series, setting),
|
|
559
|
+
text: () => renderPaneCatalogReport(filtered, effectiveParsed, glossary, gap, series, setting, settingLeads),
|
|
500
560
|
});
|
|
501
561
|
} finally {
|
|
502
562
|
registry.destroy();
|
|
@@ -2,6 +2,7 @@ import { refusedLock } from "../../plugins/builtin/shared/cloud-denied";
|
|
|
2
2
|
import { lockNoteReason } from "../../plugins/builtin/shared/pro-lock-note";
|
|
3
3
|
import { cliTerminalWidth, wrapText } from "../../utils/cli-output";
|
|
4
4
|
import { reportFooterLines } from "../report-tables";
|
|
5
|
+
import type { HeadlessPaneContext } from "../../types/headless";
|
|
5
6
|
import type { PaneFunctionReport } from "./report";
|
|
6
7
|
import type { ResolvedPaneFunction } from "./resolver";
|
|
7
8
|
|
|
@@ -22,19 +23,21 @@ export function lockNoteLines(note: string): string[] {
|
|
|
22
23
|
* `reportLockNote`, no source line and `locked` in JSON. The refusal may be
|
|
23
24
|
* the error itself or the cause of the one a headless read wrote for it
|
|
24
25
|
* (`explainRefusal`). Null when the error is no such refusal or the pane has
|
|
25
|
-
* no note, and the error stands.
|
|
26
|
+
* no note, and the error stands. The context lets the note check the free
|
|
27
|
+
* route it names.
|
|
26
28
|
*/
|
|
27
|
-
export function lockedHeadlessReport(
|
|
29
|
+
export async function lockedHeadlessReport(
|
|
28
30
|
resolved: ResolvedPaneFunction,
|
|
29
31
|
error: unknown,
|
|
30
32
|
/** The symbols the report was asked for, read once the error is known to be a refusal. */
|
|
31
33
|
symbolsOf: () => string[],
|
|
32
|
-
|
|
34
|
+
context?: HeadlessPaneContext,
|
|
35
|
+
): Promise<PaneFunctionReport | null> {
|
|
33
36
|
const refused = refusedLock(error);
|
|
34
37
|
if (!refused || !resolved.pane.reportLockNote) return null;
|
|
35
38
|
const gate = refused.gate;
|
|
36
39
|
const symbols = symbolsOf();
|
|
37
|
-
const note = resolved.pane.reportLockNote({ ...refused, symbol: symbols[0] ?? null, token: resolved.token });
|
|
40
|
+
const note = await resolved.pane.reportLockNote({ ...refused, symbol: symbols[0] ?? null, token: resolved.token, ...context ? { context } : {} });
|
|
38
41
|
const reason = lockNoteReason(note);
|
|
39
42
|
return {
|
|
40
43
|
data: {
|
|
@@ -23,7 +23,9 @@ import type { MarketContext } from "../types";
|
|
|
23
23
|
import {
|
|
24
24
|
buildPaneFunctionLookup,
|
|
25
25
|
buildTemplateContext,
|
|
26
|
+
findFunctionCode,
|
|
26
27
|
nearFunctionCodes,
|
|
28
|
+
suggestedFunctionCodes,
|
|
27
29
|
suggestPaneFunctions,
|
|
28
30
|
type NearFunctionCode,
|
|
29
31
|
type PaneFunctionCatalog,
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
optionSettings,
|
|
37
39
|
type ParsedPaneFunctionArgs,
|
|
38
40
|
} from "./options";
|
|
41
|
+
import { argumentSymbols, validateSymbolCount } from "./headless";
|
|
39
42
|
import {
|
|
40
43
|
capabilityHasOption,
|
|
41
44
|
capabilityPaneSettings,
|
|
@@ -65,9 +68,10 @@ export interface ResolvedPaneFunction {
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
async function buildPaneInstance(
|
|
68
|
-
resolved: Pick<ResolvedPaneFunction, "pane" | "template" | "headless" | "createOptions" | "optionSettings" | "capability" | "options">,
|
|
71
|
+
resolved: Pick<ResolvedPaneFunction, "token" | "pane" | "template" | "headless" | "createOptions" | "optionSettings" | "capability" | "options">,
|
|
69
72
|
context: MarketContext,
|
|
70
73
|
target: string,
|
|
74
|
+
command: "fn" | "shot",
|
|
71
75
|
): Promise<PaneInstanceConfig> {
|
|
72
76
|
// Free text is a phrase, not a symbol. Treating it as a ticker makes the
|
|
73
77
|
// pane look for a provider for values such as "HIGH GROWTH SOFTWARE".
|
|
@@ -88,7 +92,11 @@ async function buildPaneInstance(
|
|
|
88
92
|
return { ...spec.settings, ...resolved.optionSettings };
|
|
89
93
|
}
|
|
90
94
|
const chartSpec = parseChartSpec(spec.settings?.chartSpec);
|
|
91
|
-
if (!chartSpec)
|
|
95
|
+
if (!chartSpec) {
|
|
96
|
+
// A chart of too few tickers (CMP of one) has none to draw: say how many it takes.
|
|
97
|
+
if (resolved.headless) validateSymbolCount(resolved.headless, resolved.token, argumentSymbols(target), command);
|
|
98
|
+
throw new Error("The chart template did not produce a valid chart specification.");
|
|
99
|
+
}
|
|
92
100
|
return {
|
|
93
101
|
...spec.settings,
|
|
94
102
|
chartSpec: applyChartComposerCapabilityOptions(
|
|
@@ -148,7 +156,8 @@ export async function applyListingArgument(
|
|
|
148
156
|
// A list entry with spaces is a company's name search finds, or the symbols it holds.
|
|
149
157
|
const typed = takesOneTicker ? [args.arg] : await readNameEntries(splitSymbolArgs([args.arg]), context.dataProvider);
|
|
150
158
|
if (typed.length === 0) return args;
|
|
151
|
-
|
|
159
|
+
// RY.TO is the listing its quote names, RY:TSX, as `quote` reads it: a function's data is read by venue.
|
|
160
|
+
let listings = await Promise.all(typed.map((raw) => resolveCliListing(raw, exchange, context, { ownExchangeWins: typed.length > 1, venueSuffix: true })));
|
|
152
161
|
// A separator stuck to the end of a pasted list (`UPST;`) is not part of a symbol; it is named.
|
|
153
162
|
const notes: string[] = takesOneTicker ? [] : stripStrayEdges(args.arg).notes;
|
|
154
163
|
const token = found.template?.shortcut?.prefix ?? args.target.toUpperCase();
|
|
@@ -242,15 +251,21 @@ async function unknownPaneFunctionError(
|
|
|
242
251
|
// NIM is one slip from NI, but a bank figure is in `fundamentals`, not in a function's statement lines.
|
|
243
252
|
const bank = bankFigurePointer(name, arg);
|
|
244
253
|
if (bank) return new UnknownPaneFunctionError(name, bank.details, null, bank.message);
|
|
245
|
-
|
|
254
|
+
// A word that asks for a function's data (IVOL for HIVG and OVDV) names them as a slip from a code names it.
|
|
255
|
+
const suggested = suggestedFunctionCodes(registry, name);
|
|
256
|
+
const near = [...suggested, ...nearFunctionCodes(registry, name).filter(({ code }) => !suggested.some((entry) => entry.code === code))];
|
|
246
257
|
// A catalog that cannot be read still leaves the pointer to it.
|
|
247
258
|
const closest = (await suggestPaneFunctions(registry, context, name).catch(logFailure("cli.pane-functions-resolver", "Find pane function suggestions failed", [])))
|
|
248
259
|
.filter((entry) => !near.some(({ code }) => code === entry.token));
|
|
249
260
|
const tokenWidth = Math.max(0, ...closest.map((entry) => entry.token.length));
|
|
250
261
|
const word = /^[\w.:-]+$/.test(name) ? name : JSON.stringify(name);
|
|
251
262
|
const argument = arg.trim() ? ` ${/^[\w.:,=%+@/$^-]+$/.test(arg.trim()) ? arg.trim() : JSON.stringify(arg.trim())}` : "";
|
|
263
|
+
// One code is run on the argument; the functions a word asks for are each.
|
|
264
|
+
const runs = suggested.length > 0 ? suggested : near.length === 1 ? near : [];
|
|
252
265
|
return new UnknownPaneFunctionError(name, [
|
|
253
|
-
|
|
266
|
+
// A function a word asks for, typed with no argument, shows the one it takes (`<ticker>`).
|
|
267
|
+
...runs.map(({ code }, index) => [index === 0 ? "Run:" : " ", "gloomberb", command, code,
|
|
268
|
+
argument.trim() || (suggested.length > 0 ? findFunctionCode(registry, code)?.argument : "")].filter(Boolean).join(" ")),
|
|
254
269
|
...closest.length > 0
|
|
255
270
|
? ["Closest matches:", ...closest.map((entry) => ` ${entry.token.padEnd(tokenWidth)} ${entry.label}`), `Search the catalog: gloomberb catalog ${word}`]
|
|
256
271
|
: near.length > 0 ? [] : [`No function code is close to ${word}. List every function: gloomberb catalog`],
|
|
@@ -317,7 +332,7 @@ export async function resolvePaneFunction(
|
|
|
317
332
|
options: normalizedOptions,
|
|
318
333
|
...(tableSection !== undefined ? { tableSection } : {}),
|
|
319
334
|
};
|
|
320
|
-
resolved.instance = await buildPaneInstance(resolved, context, args.arg);
|
|
335
|
+
resolved.instance = await buildPaneInstance(resolved, context, args.arg, resolutionSettings.command ?? "fn");
|
|
321
336
|
return resolved;
|
|
322
337
|
}
|
|
323
338
|
|