gloomberb 0.17.6 → 0.18.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 +2 -0
- package/README.zh-CN.md +2 -0
- package/package.json +6 -1
- package/patches/electrobun@1.18.1.patch +74 -0
- package/src/account/auth-model.ts +3 -2
- package/src/account/device-signin-dialog.tsx +38 -13
- package/src/account/device-signin.ts +2 -1
- package/src/account/upgrade-dialog.tsx +6 -2
- package/src/api-client/askg.ts +5 -3
- package/src/api-client/bank-figures.ts +73 -0
- package/src/api-client/credit-boards.ts +21 -1
- package/src/api-client/crypto-markets.ts +88 -1
- package/src/api-client/data.ts +104 -4
- package/src/api-client/debt-maturities.ts +18 -0
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/errors.ts +13 -0
- package/src/api-client/fund-facts.ts +72 -0
- package/src/api-client/futures-curve.ts +26 -3
- package/src/api-client/geo.ts +11 -1
- package/src/api-client/index.ts +25 -2
- package/src/api-client/insider-signals.ts +95 -11
- package/src/api-client/normalizers.ts +1 -0
- package/src/api-client/paths.ts +20 -1
- package/src/api-client/request.ts +11 -3
- package/src/api-client/research-activity.ts +15 -8
- package/src/api-client/socket.ts +6 -3
- package/src/api-client/supply-chain-graph.ts +2 -2
- package/src/api-client/supply-chain.ts +8 -0
- package/src/api-client/tape.ts +12 -0
- package/src/api-client/types.ts +66 -2
- package/src/api-client/upgrade-experiment.ts +4 -3
- package/src/api-client/wall-experiments.ts +5 -3
- package/src/api-client/wall-summary.ts +3 -1
- package/src/api-client/web-experiments.ts +5 -2
- package/src/api-client/yield-curves.ts +17 -0
- package/src/app/command-bar-intro.ts +38 -0
- package/src/app/keybindings/actions.ts +1 -1
- package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
- package/src/app/pane-runtime/plugin-bindings.ts +4 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
- package/src/app/pane-runtime/ticker-open-target.ts +31 -0
- package/src/app/runtime/desktop-deeplink.ts +3 -4
- package/src/app/runtime/plugin-bindings.ts +3 -2
- package/src/app/runtime/startup.ts +3 -4
- package/src/app/runtime/ticker-refresh.ts +13 -12
- package/src/app/runtime/update.ts +3 -2
- package/src/architecture/core-builtin-allowlist.json +0 -3
- package/src/architecture/silent-catch-allowlist.json +3 -0
- package/src/architecture/ui-literals-allowlist.json +186 -0
- package/src/architecture/ui-tokens-allowlist.json +40 -0
- package/src/ask-gloom/pane-context.ts +3 -1
- package/src/brokers/signed-in/catalog.ts +2 -1
- package/src/brokers/signed-in/client.ts +7 -2
- package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
- package/src/brokers/signed-in/sign-in.ts +22 -20
- package/src/brokers/sync-broker-instance.ts +2 -1
- package/src/capabilities/chart-series.ts +2 -0
- package/src/capabilities/registry.ts +1 -3
- package/src/cli/bank-section.ts +147 -0
- package/src/cli/base-converter.ts +5 -2
- package/src/cli/board-coins.ts +70 -0
- package/src/cli/commands/fred-series.ts +90 -0
- package/src/cli/commands/market.ts +340 -69
- package/src/cli/commands/news-search.ts +35 -2
- package/src/cli/commands/options-chain.ts +7 -5
- package/src/cli/commands/options-leaps.ts +3 -3
- package/src/cli/commands/overview.ts +78 -29
- package/src/cli/commands/plugins.ts +1 -0
- package/src/cli/commands/search.ts +87 -24
- package/src/cli/commands/system.ts +5 -2
- package/src/cli/commands/ticker.ts +337 -60
- package/src/cli/context.ts +2 -1
- package/src/cli/desktop-pane-shot.ts +11 -3
- package/src/cli/earnings-calendar.ts +2 -1
- package/src/cli/errors.ts +10 -0
- package/src/cli/export-source.ts +18 -3
- package/src/cli/glossary.ts +196 -14
- package/src/cli/history-rows.ts +60 -9
- package/src/cli/index-roots.ts +26 -0
- package/src/cli/index.ts +34 -9
- package/src/cli/known-gaps.ts +156 -22
- package/src/cli/listing-arg.ts +156 -12
- package/src/cli/listing-suggestion.ts +4 -2
- package/src/cli/name-entries.ts +76 -0
- package/src/cli/not-a-ticker.ts +15 -6
- package/src/cli/pane-functions/access-gate.ts +22 -0
- package/src/cli/pane-functions/capabilities.ts +62 -4
- package/src/cli/pane-functions/catalog.ts +94 -39
- package/src/cli/pane-functions/data.ts +6 -5
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +3 -3
- package/src/cli/pane-functions/freshness.ts +49 -8
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +177 -42
- package/src/cli/pane-functions/index.ts +64 -8
- package/src/cli/pane-functions/options.ts +17 -3
- package/src/cli/pane-functions/report-notices.ts +17 -3
- package/src/cli/pane-functions/resolver.ts +48 -13
- package/src/cli/pane-functions/screenshot.ts +124 -23
- package/src/cli/pane-functions/shot-options.ts +68 -0
- package/src/cli/perpetual-contract.ts +3 -1
- package/src/cli/report-tables.ts +10 -2
- package/src/cli/staged-tickers.ts +5 -0
- package/src/cli/statement-filings.ts +261 -0
- package/src/cli/symbol-args.ts +47 -8
- package/src/cli/symbol-list.ts +9 -2
- package/src/cli/symbol-rows.ts +269 -29
- package/src/cli/ticker-resolution.ts +148 -34
- package/src/cli/treasury-yields.ts +86 -0
- package/src/cli/types.ts +3 -0
- package/src/cli/uncovered-instrument.ts +20 -0
- package/src/components/chart/composite/axis-overlays.tsx +16 -9
- package/src/components/chart/composite/composite-chart.tsx +85 -6
- package/src/components/chart/composite/downsample.ts +19 -3
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart/composite/legend.tsx +155 -76
- package/src/components/chart/composite/panel-bitmap.ts +8 -2
- package/src/components/chart/composite/panel-surface.tsx +3 -3
- package/src/components/chart/composite/toolbar.tsx +12 -7
- package/src/components/chart/composite/types.ts +6 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
- package/src/components/chart/curve/curve-surface.tsx +1 -1
- package/src/components/chart/static/series.ts +39 -0
- package/src/components/chart/static/trail-chart-surface.tsx +2 -1
- package/src/components/command-bar/commands/collection-list.ts +45 -15
- package/src/components/command-bar/commands/collection.ts +10 -3
- package/src/components/command-bar/commands/registry.ts +2 -2
- package/src/components/command-bar/list/badge.ts +4 -3
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/list/view.tsx +1 -1
- package/src/components/command-bar/panel/index.tsx +19 -8
- package/src/components/command-bar/panel/native-surface.ts +6 -4
- package/src/components/command-bar/panel/prompt-example.ts +22 -0
- package/src/components/command-bar/routes/root/provider-search.ts +3 -9
- package/src/components/command-bar/routes/root/results.ts +13 -0
- package/src/components/command-bar/routes/root/start-here.ts +90 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
- package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
- package/src/components/command-bar/theme-picker.tsx +3 -1
- package/src/components/command-bar/workflow/builtin.ts +5 -1
- package/src/components/command-bar/workflow/collection-actions.ts +21 -12
- package/src/components/command-bar/workflow/ops.ts +21 -1
- package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
- package/src/components/command-bar/workflow/tickers.ts +6 -4
- package/src/components/feedback-dialog.tsx +1 -1
- package/src/components/form-modal/broker-step.tsx +7 -63
- package/src/components/form-modal/content.tsx +7 -2
- package/src/components/form-modal/field-row.tsx +6 -8
- package/src/components/form-modal/host.tsx +3 -3
- package/src/components/form-modal/model.ts +3 -0
- package/src/components/form-modal/routes.ts +17 -2
- package/src/components/index.ts +6 -6
- package/src/components/input-search-bar.tsx +43 -6
- package/src/components/layout/detached-pane-shell.tsx +0 -1
- package/src/components/layout/floating-pane.tsx +0 -1
- package/src/components/layout/header.tsx +8 -7
- package/src/components/layout/pane/footer/index.tsx +7 -4
- package/src/components/layout/pane/footer/model.ts +28 -2
- package/src/components/layout/pane/header.tsx +9 -19
- package/src/components/layout/pane/index.tsx +0 -1
- package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
- package/src/components/layout/pane/pane-tabs.tsx +4 -3
- package/src/components/layout/pane/sizing.ts +9 -0
- package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
- package/src/components/layout/shell/pane/capture.ts +44 -20
- package/src/components/layout/status-bar.tsx +3 -6
- package/src/components/markdown-text.tsx +17 -2
- package/src/components/market-board/index.tsx +19 -8
- package/src/components/metric-treemap/heat-surface.tsx +2 -7
- package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
- package/src/components/onboarding/onboarding-frame.tsx +24 -21
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
- package/src/components/onboarding/wizard-positions.ts +40 -11
- package/src/components/pane-settings-dialog/desktop.tsx +12 -50
- package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
- package/src/components/price-performance.tsx +18 -2
- package/src/components/reader/article-reader.tsx +11 -10
- package/src/components/reader/detail-reader.tsx +9 -5
- package/src/components/reader/reader-text.tsx +51 -24
- package/src/components/sign-in-code-panel.tsx +129 -75
- package/src/components/ticker/badge/index.tsx +2 -1
- package/src/components/ui/action-row.tsx +5 -2
- package/src/components/ui/audio-player.tsx +70 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/data-table/index.tsx +4 -2
- package/src/components/ui/data-table/types.ts +11 -0
- package/src/components/ui/detail-scroll-body.tsx +1 -1
- package/src/components/ui/dialog-actions.tsx +1 -21
- package/src/components/ui/display.tsx +13 -5
- package/src/components/ui/external-link.tsx +4 -2
- package/src/components/ui/fields.tsx +12 -0
- package/src/components/ui/figure.tsx +5 -3
- package/src/components/ui/frame.tsx +40 -6
- package/src/components/ui/icon.tsx +4 -0
- package/src/components/ui/index.ts +4 -1
- package/src/components/ui/list-view.tsx +8 -0
- package/src/components/ui/prose.tsx +97 -25
- package/src/components/ui/query-bar-keys.tsx +3 -1
- package/src/components/ui/query-bar.tsx +6 -0
- package/src/components/ui/quote.ts +42 -0
- package/src/components/ui/range-track.tsx +15 -2
- package/src/components/ui/reveal-in-scroll-box.ts +21 -3
- package/src/components/ui/select-button.tsx +38 -3
- package/src/components/ui/status.tsx +73 -4
- package/src/components/ui/table-layout.ts +20 -0
- package/src/components/use-pinned-moment.ts +19 -0
- package/src/core/app-runtime.ts +2 -2
- package/src/core/state/app/instrument.ts +18 -1
- package/src/core/state/app/layout.ts +6 -0
- package/src/core/state/app/state.ts +14 -2
- package/src/data/cached-query.ts +1 -1
- package/src/data/config/chart-settings.ts +3 -1
- package/src/data/config/home.ts +4 -1
- package/src/data/config/store/node.ts +9 -4
- package/src/data/config/store/normalize.ts +9 -0
- package/src/data/json-storage.ts +6 -3
- package/src/data/json-ticker-repository.ts +3 -1
- package/src/data/plugin-cache.ts +5 -1
- package/src/data/sqlite/json.ts +3 -1
- package/src/data/ticker-repository.ts +5 -2
- package/src/feedback/diagnostics.ts +2 -6
- package/src/i18n/es.ts +86 -6
- package/src/i18n/index.ts +3 -9
- package/src/i18n/ja.ts +86 -6
- package/src/i18n/ko.ts +86 -6
- package/src/i18n/zh-cn.ts +86 -6
- package/src/i18n/zh-tw.ts +86 -6
- package/src/layout-marketplace/gallery-desktop.tsx +8 -4
- package/src/market-data/bars-per-year.ts +43 -0
- package/src/market-data/bond-funds.ts +85 -0
- package/src/market-data/coordinator/auxiliary.ts +5 -2
- package/src/market-data/coordinator/index.ts +6 -5
- package/src/market-data/futures-contract.ts +102 -5
- package/src/market-data/hooks.tsx +20 -11
- package/src/market-data/market/bar-period.ts +36 -0
- package/src/market-data/market/format.ts +5 -13
- package/src/market-data/market/freshness.ts +1 -3
- package/src/market-data/market/index-level.ts +51 -0
- package/src/market-data/model-dividend-yield.ts +57 -16
- package/src/market-data/quotes/live-valuation.ts +48 -2
- package/src/market-data/realized-volatility.ts +16 -7
- package/src/market-data/tax-equivalent.ts +344 -0
- package/src/news/aggregator.ts +4 -2
- package/src/notifications/notification-log.ts +5 -3
- package/src/plugins/auto-update-node.ts +1 -0
- package/src/plugins/builtin/account-management/footer.ts +1 -1
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +71 -19
- package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
- package/src/plugins/builtin/alerts/cli.ts +7 -18
- package/src/plugins/builtin/alerts/constants.ts +8 -0
- package/src/plugins/builtin/alerts/event-rows.ts +16 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
- package/src/plugins/builtin/alerts/headless.ts +182 -0
- package/src/plugins/builtin/alerts/index.tsx +3 -0
- package/src/plugins/builtin/alerts/pane.tsx +1 -7
- package/src/plugins/builtin/alerts/storage.ts +17 -1
- package/src/plugins/builtin/analytics/index.tsx +6 -1
- package/src/plugins/builtin/analytics/metrics.ts +47 -9
- package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
- package/src/plugins/builtin/analytics/risk-math.ts +83 -0
- package/src/plugins/builtin/analytics/risk-model.ts +97 -4
- package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
- package/src/plugins/builtin/analytics/sector-model.ts +14 -3
- package/src/plugins/builtin/awards/headless.ts +6 -5
- package/src/plugins/builtin/awards/index.tsx +1 -1
- package/src/plugins/builtin/backtest/client.ts +38 -8
- package/src/plugins/builtin/backtest/engine.ts +25 -13
- package/src/plugins/builtin/backtest/export.ts +27 -0
- package/src/plugins/builtin/backtest/format.ts +19 -3
- package/src/plugins/builtin/backtest/headless.ts +42 -28
- package/src/plugins/builtin/backtest/index.tsx +3 -0
- package/src/plugins/builtin/backtest/pane.tsx +39 -10
- package/src/plugins/builtin/backtest/report.ts +160 -0
- package/src/plugins/builtin/backtest/rules.ts +2 -0
- package/src/plugins/builtin/backtest/window.ts +112 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
- package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
- package/src/plugins/builtin/catalysts/headless.ts +24 -13
- package/src/plugins/builtin/catalysts/index.tsx +4 -2
- package/src/plugins/builtin/catalysts/model.ts +18 -1
- package/src/plugins/builtin/catalysts/pane.tsx +2 -2
- package/src/plugins/builtin/cds/client.ts +28 -5
- package/src/plugins/builtin/cds/headless.ts +183 -31
- package/src/plugins/builtin/cds/index.tsx +1 -1
- package/src/plugins/builtin/cds/model.ts +229 -53
- package/src/plugins/builtin/cds/pane.tsx +82 -26
- package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
- package/src/plugins/builtin/chart-composer/headless.ts +99 -21
- package/src/plugins/builtin/chart-composer/index.tsx +70 -13
- package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
- package/src/plugins/builtin/chart-composer/presets.ts +79 -11
- package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
- package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
- package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
- package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
- package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
- package/src/plugins/builtin/chat/content/composer.tsx +22 -1
- package/src/plugins/builtin/chat/content/index.tsx +35 -5
- package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
- package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
- package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
- package/src/plugins/builtin/chat/ghost-user.ts +11 -3
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
- package/src/plugins/builtin/chat/message/header.tsx +26 -4
- package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
- package/src/plugins/builtin/chat/message/render-state.ts +28 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
- package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
- package/src/plugins/builtin/chat/share/flow.tsx +9 -73
- package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
- package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
- package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/team/module.tsx +5 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
- package/src/plugins/builtin/cloud/team/views.ts +11 -4
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
- package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
- package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
- package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
- package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
- package/src/plugins/builtin/company-attention/headless.ts +15 -7
- package/src/plugins/builtin/company-attention/index.tsx +1 -1
- package/src/plugins/builtin/company-attention/model.ts +49 -4
- package/src/plugins/builtin/company-attention/pane.tsx +11 -9
- package/src/plugins/builtin/company-kpis/headless.ts +4 -3
- package/src/plugins/builtin/company-kpis/index.tsx +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
- package/src/plugins/builtin/congress-trades/headless.ts +23 -2
- package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
- package/src/plugins/builtin/cot/headless.ts +5 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
- package/src/plugins/builtin/credit-boards/client.ts +9 -2
- package/src/plugins/builtin/credit-boards/headless.ts +93 -27
- package/src/plugins/builtin/credit-boards/index.tsx +15 -4
- package/src/plugins/builtin/credit-boards/model.ts +105 -6
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
- package/src/plugins/builtin/credit-documents/index.tsx +3 -0
- package/src/plugins/builtin/credit-documents/model.ts +3 -10
- package/src/plugins/builtin/crypto-board/client.ts +152 -2
- package/src/plugins/builtin/crypto-board/headless.ts +138 -19
- package/src/plugins/builtin/crypto-board/index.tsx +5 -3
- package/src/plugins/builtin/crypto-board/model.ts +210 -12
- package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
- package/src/plugins/builtin/crypto-board/series.ts +52 -1
- package/src/plugins/builtin/crypto-board/supply.ts +123 -0
- package/src/plugins/builtin/debt-maturities/client.ts +54 -1
- package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/model.ts +58 -13
- package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
- package/src/plugins/builtin/dividend-yield/client.ts +42 -36
- package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
- package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
- package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
- package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
- package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
- package/src/plugins/builtin/dividend-yield/types.ts +6 -1
- package/src/plugins/builtin/dividend-yield/view.ts +1 -1
- package/src/plugins/builtin/docs/blocks.tsx +54 -8
- package/src/plugins/builtin/docs/pane.tsx +8 -13
- package/src/plugins/builtin/earnings/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/data.ts +51 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
- package/src/plugins/builtin/earnings-calls/model.ts +97 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
- package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
- package/src/plugins/builtin/econ/calendar-model.ts +22 -0
- package/src/plugins/builtin/econ/calendar-source.ts +35 -0
- package/src/plugins/builtin/econ/index.tsx +40 -8
- package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
- package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
- package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
- package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
- package/src/plugins/builtin/econ-statistics/view.ts +19 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +46 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
- package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
- package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
- package/src/plugins/builtin/executives/index.tsx +1 -0
- package/src/plugins/builtin/exposure/editor.tsx +3 -3
- package/src/plugins/builtin/fear-greed/index.tsx +9 -0
- package/src/plugins/builtin/filing-events/index.tsx +1 -0
- package/src/plugins/builtin/futures/index.tsx +2 -2
- package/src/plugins/builtin/futures/model.ts +34 -21
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
- package/src/plugins/builtin/futures-curve/headless.ts +4 -2
- package/src/plugins/builtin/futures-curve/model.ts +5 -7
- package/src/plugins/builtin/help/index.tsx +55 -8
- package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
- package/src/plugins/builtin/holders/headless.ts +12 -0
- package/src/plugins/builtin/insider/headless.ts +311 -41
- package/src/plugins/builtin/insider/index.tsx +280 -106
- package/src/plugins/builtin/insider/insiders.tsx +224 -0
- package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
- package/src/plugins/builtin/insider/signals-client.ts +135 -11
- package/src/plugins/builtin/insider/signals-model.ts +628 -56
- package/src/plugins/builtin/insider/signals.tsx +283 -83
- package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
- package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
- package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
- package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
- package/src/plugins/builtin/iv-history/headless.ts +2 -0
- package/src/plugins/builtin/jobs/headless.ts +1 -0
- package/src/plugins/builtin/jobs/index.tsx +1 -0
- package/src/plugins/builtin/jodi/headless.ts +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
- package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
- package/src/plugins/builtin/mna/distress/headless.ts +2 -1
- package/src/plugins/builtin/mna/headless.ts +14 -2
- package/src/plugins/builtin/mna/index.tsx +1 -0
- package/src/plugins/builtin/news/headless.ts +4 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
- package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
- package/src/plugins/builtin/notes/cli.ts +8 -4
- package/src/plugins/builtin/notes/files.ts +66 -20
- package/src/plugins/builtin/notes/migration.ts +18 -6
- package/src/plugins/builtin/notes/owner.tsx +3 -2
- package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
- package/src/plugins/builtin/options-scenario/client.ts +7 -4
- package/src/plugins/builtin/options-scenario/headless.ts +5 -3
- package/src/plugins/builtin/pe-band/index.tsx +1 -1
- package/src/plugins/builtin/pe-band/model.ts +1 -15
- package/src/plugins/builtin/perps/headless.ts +30 -13
- package/src/plugins/builtin/perps/model.ts +45 -3
- package/src/plugins/builtin/perps/pane.tsx +7 -7
- package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
- package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
- package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
- package/src/plugins/builtin/portfolio-list/income.ts +296 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
- package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
- package/src/plugins/builtin/power/prices-headless.ts +2 -1
- package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
- package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
- package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
- package/src/plugins/builtin/realized-vol/headless.ts +6 -4
- package/src/plugins/builtin/realized-vol/model.ts +5 -1
- package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
- package/src/plugins/builtin/realized-vol/settings.ts +1 -1
- package/src/plugins/builtin/research/analyst-headless.ts +4 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
- package/src/plugins/builtin/research/event-model.ts +22 -2
- package/src/plugins/builtin/research/headless-schema.ts +1 -0
- package/src/plugins/builtin/research/headless.ts +3 -0
- package/src/plugins/builtin/research/index.tsx +3 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
- package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
- package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
- package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
- package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
- package/src/plugins/builtin/risk-factors/headless.ts +7 -0
- package/src/plugins/builtin/risk-factors/index.tsx +1 -0
- package/src/plugins/builtin/scanner/feed.ts +5 -3
- package/src/plugins/builtin/scanner/headless.ts +9 -57
- package/src/plugins/builtin/scanner/index.ts +1 -2
- package/src/plugins/builtin/sec/forms.ts +2 -6
- package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
- package/src/plugins/builtin/sec/fund-facts.ts +328 -0
- package/src/plugins/builtin/sec/headless.ts +140 -44
- package/src/plugins/builtin/sec/index.tsx +95 -30
- package/src/plugins/builtin/sec/model.ts +2 -44
- package/src/plugins/builtin/shared/headless-options.ts +10 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
- package/src/plugins/builtin/shared/move-rank.ts +108 -0
- package/src/plugins/builtin/shared/report-freshness.ts +25 -9
- package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
- package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
- package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
- package/src/plugins/builtin/short-interest/headless.ts +3 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
- package/src/plugins/builtin/social-mentions/client.ts +8 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
- package/src/plugins/builtin/supply-chain/client.ts +12 -3
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
- package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
- package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
- package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
- package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
- package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
- package/src/plugins/builtin/supply-chain/headless.ts +69 -27
- package/src/plugins/builtin/supply-chain/index.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +76 -15
- package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
- package/src/plugins/builtin/themes/model.ts +2 -5
- package/src/plugins/builtin/thirteenf/api.ts +50 -2
- package/src/plugins/builtin/thirteenf/data.ts +250 -11
- package/src/plugins/builtin/thirteenf/headless.ts +311 -28
- package/src/plugins/builtin/thirteenf/index.tsx +4 -1
- package/src/plugins/builtin/thirteenf/model.ts +244 -19
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
- package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
- package/src/plugins/builtin/thirteenf/types.ts +49 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
- package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
- package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
- package/src/plugins/builtin/time-sales/client.ts +7 -4
- package/src/plugins/builtin/time-sales/headless.ts +71 -22
- package/src/plugins/builtin/time-sales/model.ts +12 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -7
- package/src/plugins/builtin/time-sales/window.ts +128 -0
- package/src/plugins/builtin/trace-bonds/client.ts +351 -33
- package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
- package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
- package/src/plugins/builtin/trace-bonds/model.ts +27 -25
- package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
- package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
- package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
- package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
- package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
- package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
- package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
- package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
- package/src/plugins/builtin/vol-surface/headless.ts +3 -2
- package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
- package/src/plugins/builtin/vol-surface/stored.ts +4 -7
- package/src/plugins/builtin/volatility/chart-model.ts +16 -15
- package/src/plugins/builtin/volatility/charts.tsx +5 -5
- package/src/plugins/builtin/volatility/client.ts +73 -2
- package/src/plugins/builtin/volatility/evidence.ts +13 -14
- package/src/plugins/builtin/volatility/headless.ts +13 -7
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/volatility/model.ts +104 -38
- package/src/plugins/builtin/volatility/pane.tsx +42 -24
- package/src/plugins/builtin/volatility/series.ts +22 -13
- package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
- package/src/plugins/builtin/world-venue-map/index.ts +16 -2
- package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
- package/src/plugins/builtin/yield-curve/curves.ts +29 -5
- package/src/plugins/builtin/yield-curve/headless.ts +41 -8
- package/src/plugins/builtin/yield-curve/history.ts +5 -5
- package/src/plugins/builtin/yield-curve/index.tsx +30 -13
- package/src/plugins/compat.ts +3 -1
- package/src/plugins/dependencies.ts +8 -2
- package/src/plugins/host-link.ts +12 -5
- package/src/plugins/installer.ts +16 -7
- package/src/plugins/loader.ts +9 -2
- package/src/plugins/marketplace/activation.ts +10 -4
- package/src/plugins/registry/host-actions.ts +3 -0
- package/src/plugins/registry/index.ts +3 -1
- package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
- package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
- package/src/portfolio/futures-positions.ts +213 -0
- package/src/portfolio/futures-terms.ts +75 -0
- package/src/portfolio/manual-lots.ts +78 -0
- package/src/portfolio/mutations.ts +54 -10
- package/src/portfolio/position-metrics.ts +44 -10
- package/src/portfolio/valuation-basis.ts +31 -4
- package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
- package/src/public/time-series.ts +2 -1
- package/src/remote/confirm-change.tsx +2 -1
- package/src/remote/server.ts +1 -1
- package/src/remote/watchlist.ts +9 -1
- package/src/renderers/dom/data-table/row.tsx +1 -1
- package/src/renderers/dom/desktop/audio-player.tsx +312 -0
- package/src/renderers/dom/desktop/control-styles.ts +17 -82
- package/src/renderers/dom/desktop/controls.tsx +39 -63
- package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/desktop/list-view.tsx +6 -6
- package/src/renderers/dom/desktop/popover.tsx +0 -3
- package/src/renderers/dom/desktop/query-bar.tsx +6 -4
- package/src/renderers/dom/desktop/select-field.tsx +9 -8
- package/src/renderers/dom/desktop/stack-header.ts +3 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -2
- package/src/renderers/dom/host/chart-surface.tsx +3 -3
- package/src/renderers/dom/host/size-observer.ts +33 -4
- package/src/renderers/dom/host/surface-3d.tsx +3 -2
- package/src/renderers/dom/host/surface-fit.ts +4 -1
- package/src/renderers/dom/host/tabs.tsx +22 -132
- package/src/renderers/dom/styles.css +358 -160
- package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
- package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
- package/src/series/capability.ts +1 -0
- package/src/series/files.ts +9 -3
- package/src/series/registry.ts +40 -14
- package/src/series/resolve.ts +121 -18
- package/src/series/standalone.ts +3 -2
- package/src/series/types.ts +55 -1
- package/src/series/validate.ts +39 -0
- package/src/shares/api.ts +26 -7
- package/src/shares/bug-report-dialog.tsx +2 -2
- package/src/shares/bug-report-flow.tsx +4 -7
- package/src/shares/capture-flow.ts +5 -4
- package/src/shares/capture-preview.tsx +2 -2
- package/src/shares/chat.ts +7 -27
- package/src/shares/flow.tsx +86 -40
- package/src/shares/location.ts +1 -3
- package/src/shares/pane-image.ts +11 -9
- package/src/shares/pane.ts +5 -2
- package/src/shares/payload.ts +1 -1
- package/src/shares/personal-pane.ts +3 -3
- package/src/shares/portable-layout.ts +9 -2
- package/src/shares/ready.ts +42 -0
- package/src/shares/test-harness.ts +62 -0
- package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
- package/src/sources/gloomberb-cloud/index.ts +64 -12
- package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
- package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
- package/src/sources/history-omitted.ts +34 -0
- package/src/sources/history-result.ts +3 -0
- package/src/sources/history-retention.ts +1 -1
- package/src/sources/provider-errors.ts +43 -1
- package/src/sources/provider-router/batches.ts +3 -2
- package/src/sources/provider-router/cache.ts +2 -1
- package/src/sources/provider-router/cached-routes.ts +9 -3
- package/src/sources/provider-router/history.ts +9 -8
- package/src/sources/provider-router/index.ts +18 -3
- package/src/sources/provider-router/primary.ts +5 -4
- package/src/sources/provider-router/quote-route.ts +3 -1
- package/src/sources/provider-router/routing.ts +2 -1
- package/src/sources/provider-router/search.ts +47 -13
- package/src/sources/provider-router/streaming.ts +7 -4
- package/src/sources/sec-edgar.ts +1 -3
- package/src/state/app/context/index.tsx +9 -13
- package/src/state/app/tab-cursor.ts +149 -12
- package/src/state/hooks/quote-streaming.ts +3 -2
- package/src/state/hooks/session-snapshot.ts +3 -2
- package/src/state/persist-scheduler.ts +12 -4
- package/src/sync/controller.ts +22 -1
- package/src/sync/core-contributors.ts +58 -15
- package/src/team/collections-sync.ts +14 -4
- package/src/team/collections.ts +40 -11
- package/src/team/store.ts +5 -4
- package/src/telemetry/attention-counts.ts +8 -2
- package/src/telemetry/crash-reports-dom.ts +3 -0
- package/src/telemetry/crash-reports-node.ts +7 -4
- package/src/telemetry/crash-reports.ts +11 -11
- package/src/telemetry/usage-counts.ts +16 -12
- package/src/theme/colors.ts +2 -0
- package/src/theme/font-scale.ts +2 -0
- package/src/theme/ui-tokens.ts +71 -0
- package/src/tickers/batched-lookups.ts +186 -0
- package/src/tickers/company-name-listing.ts +97 -0
- package/src/tickers/company-name.ts +13 -0
- package/src/tickers/instrument-coverage.ts +112 -0
- package/src/tickers/listing-fate.ts +49 -9
- package/src/tickers/listing-venues.ts +24 -5
- package/src/tickers/open-target.ts +13 -5
- package/src/tickers/search/index.ts +112 -35
- package/src/tickers/search/ranking.ts +49 -1
- package/src/tickers/terminal-codes.ts +79 -0
- package/src/tickers/ticker-changes.ts +74 -3
- package/src/tickers/us-listing.ts +2 -1
- package/src/time-series/chart-data.ts +79 -8
- package/src/time-series/fundamentals.ts +55 -7
- package/src/time-series/ice-credit.ts +17 -2
- package/src/time-series/live-bars.ts +21 -8
- package/src/time-series/live-quotes.ts +2 -3
- package/src/time-series/price-comparison.ts +6 -1
- package/src/time-series/resolve.ts +65 -10
- package/src/time-series/spec.ts +2 -1
- package/src/time-series/studies.ts +3 -3
- package/src/time-series/types.ts +22 -0
- package/src/time-series/use-chart-resolution.ts +2 -1
- package/src/types/config.ts +7 -0
- package/src/types/data-provider.ts +20 -0
- package/src/types/headless.ts +142 -1
- package/src/types/instrument.ts +6 -0
- package/src/types/plugin.ts +32 -9
- package/src/types/price-history.ts +5 -0
- package/src/types/ticker-changes.ts +19 -0
- package/src/types/ticker.ts +10 -2
- package/src/ui/context-menu.tsx +2 -0
- package/src/ui/host.tsx +40 -4
- package/src/ui/measured-cells.ts +13 -7
- package/src/ui/qr.ts +7 -5
- package/src/updater/index.ts +5 -5
- package/src/utils/calendar-date.ts +86 -27
- package/src/utils/currency-units.ts +17 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/dividend-cash.ts +48 -0
- package/src/utils/dom-screenshot-rasters.ts +3 -1
- package/src/utils/dom-screenshot.ts +14 -10
- package/src/utils/exchange-rate-snapshot.ts +9 -4
- package/src/utils/external-url.ts +1 -0
- package/src/utils/fiscal-period.ts +47 -0
- package/src/utils/format.ts +5 -2
- package/src/utils/forward-dividend.ts +160 -11
- package/src/utils/futures-generic.ts +34 -8
- package/src/utils/http-proxy-response.ts +2 -1
- package/src/utils/local-utc-offset.ts +2 -0
- package/src/utils/log-failure.ts +31 -0
- package/src/utils/option-expiry.ts +12 -12
- package/src/utils/price-history-integrity.ts +56 -0
- package/src/utils/price-history.ts +13 -1
- package/src/utils/sec-filings.ts +245 -0
- package/src/utils/selection-clipboard.ts +5 -2
- package/src/utils/throttled-fetch.ts +1 -0
- package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
- package/src/utils/url.ts +1 -0
- package/src/utils/utc-time.ts +11 -0
- package/src/plugins/builtin/chat/share/targets.ts +0 -27
- package/src/renderers/dom/host/top-surface.ts +0 -119
- package/src/shares/dialog.tsx +0 -221
- package/src/shares/live.ts +0 -19
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { requestAuthDialog } from "../account/auth-dialog";
|
|
2
|
+
import { apiClient } from "../api-client";
|
|
3
|
+
import type { AppNotificationRequest } from "../types/plugin";
|
|
4
|
+
|
|
5
|
+
type Notify = (notification: AppNotificationRequest) => unknown;
|
|
6
|
+
|
|
7
|
+
function errorText(error: unknown, fallback: string): string {
|
|
8
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether the account can make a share link or post to chat now. When it
|
|
13
|
+
* cannot, one toast says what is missing: a sign-in, which then calls `retry`
|
|
14
|
+
* so Share or the report carries on, or a verified email. `purpose` finishes
|
|
15
|
+
* "Sign in to ...".
|
|
16
|
+
*/
|
|
17
|
+
export function readyToShare(notify: Notify, retry: () => void, purpose: string): boolean {
|
|
18
|
+
if (!apiClient.isSignedIn()) {
|
|
19
|
+
notify({
|
|
20
|
+
body: `Sign in to ${purpose}.`,
|
|
21
|
+
type: "info",
|
|
22
|
+
action: { label: "Sign in", onClick: () => { requestAuthDialog({ onSignedIn: retry }); } },
|
|
23
|
+
});
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (!apiClient.isVerified()) {
|
|
27
|
+
notify({
|
|
28
|
+
body: `Verify your email to ${purpose}.`,
|
|
29
|
+
type: "info",
|
|
30
|
+
action: {
|
|
31
|
+
label: "Resend link",
|
|
32
|
+
onClick: () => {
|
|
33
|
+
void apiClient.sendVerification()
|
|
34
|
+
.then(() => notify({ body: "Verification link sent.", type: "success" }))
|
|
35
|
+
.catch((error) => notify({ body: errorText(error, "Couldn't send the email. Try again."), type: "error" }));
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { afterEach } from "bun:test";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
import { apiClient } from "../api-client";
|
|
4
|
+
import type { ConfirmDialogProps } from "../components/ui/confirm-dialog";
|
|
5
|
+
import type { AppNotificationRequest } from "../types/plugin";
|
|
6
|
+
import type { PaneShareHost, PaneShareView } from "./flow";
|
|
7
|
+
import { buildPaneSharePayload } from "./pane";
|
|
8
|
+
|
|
9
|
+
export const SHARE_ID = "a".repeat(32);
|
|
10
|
+
export const SHARE_LINK = `https://term.gloom.sh/api/shares/${SHARE_ID}/open`;
|
|
11
|
+
|
|
12
|
+
/** Puts back the account and share calls a suite's fixtures replace. */
|
|
13
|
+
export function restoreShareApiAfterEach(): void {
|
|
14
|
+
const original = { signedIn: apiClient.isSignedIn, verified: apiClient.isVerified, create: apiClient.createTerminalShare };
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
apiClient.isSignedIn = original.signedIn;
|
|
17
|
+
apiClient.isVerified = original.verified;
|
|
18
|
+
apiClient.createTerminalShare = original.create;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A pane Share reads, the account and share service it talks to, and what it
|
|
24
|
+
* did: the toasts, what it copied and the links it made. A question shows as
|
|
25
|
+
* the shared confirm, recorded and answered as told; a suite that draws the
|
|
26
|
+
* real dialog replaces `host.promptDialog`.
|
|
27
|
+
*/
|
|
28
|
+
export function shareFlowFixture(view: Partial<PaneShareView> = {}, { signedIn = true } = {}) {
|
|
29
|
+
const payload = buildPaneSharePayload({ panes: new Map([["test", { id: "test", name: "Test" }]]), paneTemplates: new Map() } as any,
|
|
30
|
+
{ paneId: "test", instanceId: "test:1", settings: { selectedTab: "options", expiry: "2027-01-15" } });
|
|
31
|
+
const toasts: AppNotificationRequest[] = [];
|
|
32
|
+
const copied: string[] = [];
|
|
33
|
+
const created: unknown[] = [];
|
|
34
|
+
const questions: ConfirmDialogProps[] = [];
|
|
35
|
+
let reads = 0;
|
|
36
|
+
let answer: boolean | undefined = true;
|
|
37
|
+
let createShare = async () => ({ id: SHARE_ID, expiresAt: "2027-01-01T00:00:00Z" });
|
|
38
|
+
let copyText = async (text: string) => { copied.push(text); };
|
|
39
|
+
apiClient.isSignedIn = () => signedIn;
|
|
40
|
+
apiClient.isVerified = () => signedIn;
|
|
41
|
+
apiClient.createTerminalShare = async (value) => { created.push(value); return createShare(); };
|
|
42
|
+
const host: PaneShareHost = {
|
|
43
|
+
notify: (toast) => { toasts.push(toast); },
|
|
44
|
+
promptDialog: async <T,>(options: Parameters<PaneShareHost["promptDialog"]>[0]) => {
|
|
45
|
+
const content = options.content as (context: { resolve(value: T): void; dismiss(): void }) => ReactElement<ConfirmDialogProps>;
|
|
46
|
+
let result: T | undefined;
|
|
47
|
+
questions.push(content({ resolve: (value) => { result = value; }, dismiss: () => {} }).props);
|
|
48
|
+
if (answer !== undefined) result = answer as T;
|
|
49
|
+
return result;
|
|
50
|
+
},
|
|
51
|
+
copyText: (text) => copyText(text),
|
|
52
|
+
readShareView: async () => { reads += 1; return { sharePayload: payload, personalData: null, ...view }; },
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
host, payload, toasts, copied, created, questions,
|
|
56
|
+
get reads() { return reads; },
|
|
57
|
+
set answer(value: boolean | undefined) { answer = value; },
|
|
58
|
+
set createShare(value: () => Promise<{ id: string; expiresAt: string }>) { createShare = value; },
|
|
59
|
+
/** Replaces the clipboard, as a browser that refuses a write does. */
|
|
60
|
+
set copyText(value: (text: string) => Promise<void>) { copyText = value; },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { apiClient } from "../../api-client";
|
|
2
|
+
import { ApiRequestError } from "../../api-client/errors";
|
|
3
|
+
import type { InstrumentSearchResult } from "../../types/instrument";
|
|
4
|
+
import type { TickerChanges } from "../../types/ticker-changes";
|
|
5
|
+
import { logFailure } from "../../utils/log-failure";
|
|
6
|
+
import { chunked, mapWithConcurrency } from "../../utils/concurrency";
|
|
7
|
+
|
|
8
|
+
/** Symbols asked for in one batched ticker change request; the service reads up to 500. */
|
|
9
|
+
const TICKER_CHANGES_BATCH_SIZE = 500;
|
|
10
|
+
/** Queries in one batched search; the service runs up to 100, as many as a signed-out quote batch reads. */
|
|
11
|
+
const SEARCH_BATCH_SIZE = 100;
|
|
12
|
+
/** Batch requests of one lookup in flight at once, so a long list does not ask the service for everything at the same moment. */
|
|
13
|
+
const BATCHES_IN_FLIGHT = 3;
|
|
14
|
+
|
|
15
|
+
type BatchRoute = "ticker-changes" | "search";
|
|
16
|
+
|
|
17
|
+
/** Batch routes the service answered 404 for: an older service, asked one symbol at a time from then on. */
|
|
18
|
+
const unsupported = new Set<BatchRoute>();
|
|
19
|
+
|
|
20
|
+
/** A service without the batch route answers 404 (or 405, the path served for another method). */
|
|
21
|
+
const isMissingRoute = (error: unknown) => error instanceof ApiRequestError && (error.status === 404 || error.status === 405);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The answers to `keys`, a request per `size` of them, a few in flight at a
|
|
25
|
+
* time. A request that fails leaves its keys out, to be asked one at a time; a
|
|
26
|
+
* service without the route answers null, and is not asked for it again.
|
|
27
|
+
*/
|
|
28
|
+
async function inBatches<T>(
|
|
29
|
+
route: BatchRoute,
|
|
30
|
+
keys: readonly string[],
|
|
31
|
+
size: number,
|
|
32
|
+
load: (chunk: string[]) => Promise<Map<string, T>>,
|
|
33
|
+
): Promise<Map<string, T> | null> {
|
|
34
|
+
if (unsupported.has(route)) return null;
|
|
35
|
+
const answers = new Map<string, T>();
|
|
36
|
+
await mapWithConcurrency(chunked([...new Set(keys)], size), BATCHES_IN_FLIGHT, async (chunk) => {
|
|
37
|
+
// The first chunk an older service refused says the rest would be refused too.
|
|
38
|
+
if (unsupported.has(route)) return;
|
|
39
|
+
try {
|
|
40
|
+
for (const [key, value] of await load(chunk)) answers.set(key, value);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (isMissingRoute(error)) unsupported.add(route);
|
|
43
|
+
else logFailure("sources.gloomberb-cloud.batched-lookups", `Batched ${route} request failed`)(error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return unsupported.has(route) ? null : answers;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Each symbol's ticker changes, by symbol as asked (null: none on record); null from a service that cannot batch them. */
|
|
50
|
+
export function cloudTickerChangesBatch(
|
|
51
|
+
symbols: readonly string[],
|
|
52
|
+
options: { signal?: AbortSignal } = {},
|
|
53
|
+
): Promise<Map<string, TickerChanges | null> | null> {
|
|
54
|
+
return inBatches("ticker-changes", symbols, TICKER_CHANGES_BATCH_SIZE, (chunk) => apiClient.getCloudTickerChangesBatch(chunk, options));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Each query's listings as the service's search answers it, by query as asked;
|
|
59
|
+
* null from a service that cannot batch searches. Perpetual markets, which
|
|
60
|
+
* `search` asks the board for beside the listings, are not included: the
|
|
61
|
+
* symbols a list is looked up by never name one.
|
|
62
|
+
*/
|
|
63
|
+
export function cloudSearchBatch(
|
|
64
|
+
queries: readonly string[],
|
|
65
|
+
options: { limit?: number; signal?: AbortSignal } = {},
|
|
66
|
+
): Promise<Map<string, InstrumentSearchResult[]> | null> {
|
|
67
|
+
return inBatches("search", queries, SEARCH_BATCH_SIZE, (chunk) => apiClient.searchInstrumentsBatch(chunk, options.limit ?? 10, options));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Forgets which batch routes the service lacked. */
|
|
71
|
+
export function resetCloudBatchSupport(): void {
|
|
72
|
+
unsupported.clear();
|
|
73
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { logFailure } from "../../utils/log-failure";
|
|
1
2
|
import { exchangeRateMetadata } from "../../utils/exchange-rate-snapshot";
|
|
2
3
|
import type { ExchangeRateSnapshot } from "../../types/exchange-rate";
|
|
3
4
|
import type { TimeRange } from "../../time-series/range";
|
|
4
5
|
import { subtractTimeRange } from "../../time-series/date-window";
|
|
5
6
|
import { HistoryCoverageError, parseHistoryCoverageNote, parseHistoryCoverageStart } from "../history-coverage";
|
|
7
|
+
import { cloudNonPositiveCloseDates } from "../history-omitted";
|
|
6
8
|
import {
|
|
7
9
|
normalizeChartResolutionSupport,
|
|
8
10
|
type ChartResolutionSupport,
|
|
@@ -39,9 +41,11 @@ import {
|
|
|
39
41
|
import type { NewsArticle, NewsQuery } from "../../types/news-source";
|
|
40
42
|
import { normalizeNewsFeed } from "../../news/news-model";
|
|
41
43
|
import { resolveCurrencyUnit } from "../../utils/currency-units";
|
|
44
|
+
import { isRoundTheClockCoin } from "../../utils/crypto-pair";
|
|
42
45
|
import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
|
|
43
46
|
import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
|
|
44
|
-
import { createProviderMiss, providerMissReason } from "../provider-errors";
|
|
47
|
+
import { createProviderMiss, ProviderBusyError, providerMissReason } from "../provider-errors";
|
|
48
|
+
import { isRetryableApiError } from "../../api-client/errors";
|
|
45
49
|
import { assertSecRegistrantMatches } from "../sec-registrant";
|
|
46
50
|
import { nonUsSecListingVenue } from "../../utils/sec";
|
|
47
51
|
import { publicListingTarget } from "../listing-target";
|
|
@@ -57,6 +61,7 @@ import {
|
|
|
57
61
|
import {
|
|
58
62
|
GLOOMBERB_CLOUD_PROVIDER_ID,
|
|
59
63
|
formatCloudDateTime,
|
|
64
|
+
formatCloudHistoryEnd,
|
|
60
65
|
isEmptyCloudStatus,
|
|
61
66
|
mapBatchError,
|
|
62
67
|
NOT_FOUND_REASON,
|
|
@@ -73,11 +78,17 @@ import {
|
|
|
73
78
|
namesPerpetualMarket,
|
|
74
79
|
perpetualTarget,
|
|
75
80
|
searchPerpetualMarkets,
|
|
81
|
+
boardSearchesFrom,
|
|
76
82
|
} from "./perpetuals";
|
|
77
83
|
import { predictionMarketListing } from "../../tickers/prediction-listing";
|
|
78
84
|
import { settleWithin } from "../../utils/async-deadline";
|
|
85
|
+
import { cloudSearchBatch, cloudTickerChangesBatch } from "./batched-lookups";
|
|
79
86
|
|
|
80
87
|
const providerId = GLOOMBERB_CLOUD_PROVIDER_ID;
|
|
88
|
+
/** How long a search that is not typed waits for perpetual markets. */
|
|
89
|
+
const PERPETUAL_SEARCH_WAIT_MS = 2_000;
|
|
90
|
+
/** How long a batched search waits for the board it answers every query's perpetual markets from. */
|
|
91
|
+
const PERPETUAL_BOARD_WAIT_MS = 5_000;
|
|
81
92
|
const CLOUD_RESOLUTION_SUPPORT = normalizeChartResolutionSupport([
|
|
82
93
|
{ resolution: "1m", maxRange: "1W" },
|
|
83
94
|
{ resolution: "5m", maxRange: "1M" },
|
|
@@ -109,6 +120,7 @@ function mapCloudSecFiling(item: {
|
|
|
109
120
|
companyName?: string;
|
|
110
121
|
filingUrl: string;
|
|
111
122
|
primaryDocumentUrl?: string;
|
|
123
|
+
fileNumber?: string;
|
|
112
124
|
}): SecFilingItem {
|
|
113
125
|
return {
|
|
114
126
|
accessionNumber: item.accessionNumber,
|
|
@@ -123,6 +135,8 @@ function mapCloudSecFiling(item: {
|
|
|
123
135
|
companyName: item.companyName,
|
|
124
136
|
filingUrl: item.filingUrl,
|
|
125
137
|
primaryDocumentUrl: item.primaryDocumentUrl,
|
|
138
|
+
// Absent from an older server: no file number is not the same as an empty one.
|
|
139
|
+
...(typeof item.fileNumber === "string" ? { fileNumber: item.fileNumber } : {}),
|
|
126
140
|
};
|
|
127
141
|
}
|
|
128
142
|
|
|
@@ -162,8 +176,8 @@ async function loadCloudHistory(
|
|
|
162
176
|
if (response.status === "success" || response.status === "partial" || response.status === "empty") {
|
|
163
177
|
return { response, extendedHours: response.session === "extended" };
|
|
164
178
|
}
|
|
165
|
-
} catch {
|
|
166
|
-
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logFailure("sources.gloomberb-cloud.index", "Loading extended-hours history failed")(error);
|
|
167
181
|
}
|
|
168
182
|
return { response: await load(), extendedHours: false };
|
|
169
183
|
}
|
|
@@ -204,11 +218,12 @@ function mapCloudPriceHistory(
|
|
|
204
218
|
const { divisor } = resolveCurrencyUnit(
|
|
205
219
|
response.currency ?? response.providerMeta?.currency,
|
|
206
220
|
);
|
|
221
|
+
const coin = isRoundTheClockCoin(ticker, exchange);
|
|
207
222
|
const points = normalizePriceHistory(
|
|
208
223
|
unwrapRequiredCloudResponse(
|
|
209
224
|
response,
|
|
210
225
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
211
|
-
).map((point) => mapPricePoint(point, divisor, exchange)),
|
|
226
|
+
).map((point) => mapPricePoint(point, divisor, exchange, coin)),
|
|
212
227
|
);
|
|
213
228
|
if (stale && !reachesLatestSettledSession(points, Date.now(), { symbol: ticker, exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
|
|
214
229
|
throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
|
|
@@ -234,10 +249,13 @@ function mapCloudPriceHistory(
|
|
|
234
249
|
if (declared && (!session || !matchingSource || !matchingIdentity || !matchingInterval)) {
|
|
235
250
|
throw createProviderMiss(`Cloud chart session metadata does not match the requested history for ${ticker}`);
|
|
236
251
|
}
|
|
252
|
+
// Sessions of daily bars the answer left out for a close at or below zero.
|
|
253
|
+
const nonPositiveCloseDates = resolution === "1d" ? cloudNonPositiveCloseDates(response.omittedSessions) : undefined;
|
|
237
254
|
return {
|
|
238
255
|
points,
|
|
239
256
|
resolution,
|
|
240
257
|
...(session && matchingSource && matchingIdentity ? { session } : {}),
|
|
258
|
+
...(nonPositiveCloseDates ? { nonPositiveCloseDates } : {}),
|
|
241
259
|
...(coverageStart ? { coverageStart } : {}),
|
|
242
260
|
...(coverageNote ? { coverageNote } : {}),
|
|
243
261
|
...(extendedHours ? { extendedHours: true } : {}),
|
|
@@ -318,7 +336,10 @@ function matchCloudBatchItems<T extends { symbol: string; exchange?: string }, I
|
|
|
318
336
|
const requested = new Set(pending.keys());
|
|
319
337
|
const responses = items.flatMap((item) => {
|
|
320
338
|
try { return [{ item, key: quoteTargetKey(item.symbol, item.exchange) }]; }
|
|
321
|
-
catch {
|
|
339
|
+
catch (error) {
|
|
340
|
+
logFailure("sources.gloomberb-cloud.index", "Matching a cloud batch response failed")(error);
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
322
343
|
});
|
|
323
344
|
for (const { item, key: responseKey } of responses) {
|
|
324
345
|
const key = cloudResponseTargetKey(responseKey, requested);
|
|
@@ -565,12 +586,15 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
565
586
|
async search(query: string, context?: SearchRequestContext): Promise<InstrumentSearchResult[]> {
|
|
566
587
|
// Perpetual markets are listings too, asked for beside the others. They are optional: a failed lookup leaves the
|
|
567
588
|
// other results, and a keystroke waits for them only briefly once the listings are in, unless the query names one.
|
|
568
|
-
const pending = searchPerpetualMarkets(query).catch((
|
|
589
|
+
const pending = searchPerpetualMarkets(query).catch(logFailure("sources.gloomberb-cloud.index", "Searching perpetual markets failed", [] as InstrumentSearchResult[]));
|
|
569
590
|
const listings = await withCloudFallback(
|
|
570
591
|
() => apiClient.searchInstruments(query, context?.limit ?? 10),
|
|
571
592
|
"Cloud search is unavailable",
|
|
572
|
-
)
|
|
573
|
-
|
|
593
|
+
).catch((error: unknown) => {
|
|
594
|
+
// Rate limited or overloaded: the search was not run, which is not a search that found nothing.
|
|
595
|
+
throw isRetryableApiError(error) ? new ProviderBusyError("Cloud search is busy", { cause: error }) : error;
|
|
596
|
+
});
|
|
597
|
+
const perpetuals = await settleWithin(pending, namesPerpetualMarket(query) || !context?.interactive ? PERPETUAL_SEARCH_WAIT_MS : 250) ?? [];
|
|
574
598
|
// Ahead of the listings, so a contract found by its plain name ("brent" for BZUSDT:BINANCE) is not cut
|
|
575
599
|
// behind every warrant that names it; an exact symbol still leads.
|
|
576
600
|
return perpetuals.length ? [...perpetuals, ...listings] : listings;
|
|
@@ -599,7 +623,8 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
599
623
|
const quote = await this.getQuote(ticker, venue);
|
|
600
624
|
const quoteVenue = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
|
|
601
625
|
return quoteVenue === venue ? quote.name?.trim() || null : null;
|
|
602
|
-
} catch {
|
|
626
|
+
} catch (error) {
|
|
627
|
+
logFailure("sources.gloomberb-cloud.index", "Loading the filing listing name failed")(error);
|
|
603
628
|
return null;
|
|
604
629
|
}
|
|
605
630
|
}
|
|
@@ -664,6 +689,30 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
664
689
|
return response.status === "success" || response.status === "partial" ? response.data : null;
|
|
665
690
|
}
|
|
666
691
|
|
|
692
|
+
getTickerChangesBatch(symbols: string[], options: { signal?: AbortSignal } = {}): Promise<Map<string, TickerChanges | null> | null> {
|
|
693
|
+
return cloudTickerChangesBatch(symbols, options);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async searchBatch(queries: string[], options: { limit?: number; signal?: AbortSignal } = {}): Promise<Map<string, InstrumentSearchResult[]> | null> {
|
|
697
|
+
const answers = await cloudSearchBatch(queries, options);
|
|
698
|
+
if (!answers) return null;
|
|
699
|
+
// Perpetual markets lead each answer, as `search` gives them, so a symbol in a list resolves as it does alone:
|
|
700
|
+
// one read of the board answers every query's search of it. A query that found no listing is searched again
|
|
701
|
+
// on its own, which finds them then; a board that does not answer leaves them out, as a search does.
|
|
702
|
+
const found = queries.filter((query) => answers.get(query)?.length);
|
|
703
|
+
const board = found.length
|
|
704
|
+
? await settleWithin(apiClient.getCloudPerpsBoard().catch(logFailure("sources.gloomberb-cloud.index", "Reading the perpetual board failed", null)), PERPETUAL_BOARD_WAIT_MS)
|
|
705
|
+
: null;
|
|
706
|
+
if (!board) return answers;
|
|
707
|
+
const client = boardSearchesFrom(board);
|
|
708
|
+
for (const query of found) {
|
|
709
|
+
const perpetuals = await searchPerpetualMarkets(query, client)
|
|
710
|
+
.catch(logFailure("sources.gloomberb-cloud.index", "Searching perpetual markets failed", [] as InstrumentSearchResult[]));
|
|
711
|
+
if (perpetuals.length) answers.set(query, [...perpetuals, ...answers.get(query)!]);
|
|
712
|
+
}
|
|
713
|
+
return answers;
|
|
714
|
+
}
|
|
715
|
+
|
|
667
716
|
async getArticleSummary(url: string): Promise<string | null> {
|
|
668
717
|
return (await apiClient.getCloudArticleSummary(url)).summary;
|
|
669
718
|
}
|
|
@@ -736,7 +785,7 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
736
785
|
() => loadCloudHistory(interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
737
786
|
interval,
|
|
738
787
|
startDate: formatCloudDateTime(startDate, includeTime, exchange),
|
|
739
|
-
endDate:
|
|
788
|
+
endDate: formatCloudHistoryEnd(endDate, includeTime, exchange),
|
|
740
789
|
...(session ? { session } : {}),
|
|
741
790
|
}), _context),
|
|
742
791
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
@@ -800,7 +849,7 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
800
849
|
const { valid } = cloudBatchTargets(splitPerpetualTargets(targets).others);
|
|
801
850
|
if (!valid.length) return () => {};
|
|
802
851
|
// No-op without a session credential; covers browser cookie sessions too.
|
|
803
|
-
void apiClient.ensureVerifiedSession().catch((
|
|
852
|
+
void apiClient.ensureVerifiedSession().catch(logFailure("sources.gloomberb-cloud.index", "Verifying the quote streaming session failed"));
|
|
804
853
|
const targetMap = new Map<string, QuoteSubscriptionTarget[]>();
|
|
805
854
|
for (const { target } of valid) {
|
|
806
855
|
const key = quoteTargetKey(target.symbol, target.exchange);
|
|
@@ -821,7 +870,10 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
821
870
|
(target, quote) => {
|
|
822
871
|
let key: string;
|
|
823
872
|
try { key = quoteTargetKey(target.symbol, target.exchange); }
|
|
824
|
-
catch {
|
|
873
|
+
catch (error) {
|
|
874
|
+
logFailure("sources.gloomberb-cloud.index", "Matching a streamed quote failed")(error);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
825
877
|
const matchedKey = cloudResponseTargetKey(key, requested);
|
|
826
878
|
const matches = matchedKey ? targetMap.get(matchedKey) ?? [] : [];
|
|
827
879
|
const mappedQuote = mapQuote(quote);
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
} from "../../api-client";
|
|
21
21
|
import { normalizePriceValueByDivisor, resolveCurrencyUnit } from "../../utils/currency-units";
|
|
22
22
|
import { resolveExchangeTimeZone } from "../../utils/exchanges";
|
|
23
|
+
import { isRoundTheClockCoin } from "../../utils/crypto-pair";
|
|
24
|
+
import { latestSettledSessionDate } from "../../utils/price-history";
|
|
23
25
|
import { zonedDateTimeParts, zonedWallClockToUtcMs } from "../../utils/zoned-date-time";
|
|
24
26
|
import { createProviderMiss } from "../provider-errors";
|
|
25
27
|
import { reconcileQuoteDayRange } from "../../market-data/quotes/day-range";
|
|
@@ -216,10 +218,17 @@ function parseCloudPricePointDate(
|
|
|
216
218
|
: new Date(value);
|
|
217
219
|
}
|
|
218
220
|
|
|
221
|
+
/**
|
|
222
|
+
* One served bar. `coin`: the bars are a coin's, which trades every hour of every day. Its intraday volume is the
|
|
223
|
+
* value traded in the bar on major spot venues, and a bar no venue reported has none rather than 0, so a 0 (from an
|
|
224
|
+
* older copy, or a daily bar) is volume not reported, not a bar nothing traded in. It is read as missing, so no table
|
|
225
|
+
* prints 0 and no sum or average counts it. Any other listing keeps a 0, which a thin stock can truly trade.
|
|
226
|
+
*/
|
|
219
227
|
export function mapPricePoint(
|
|
220
228
|
point: CloudPricePointPayload,
|
|
221
229
|
divisor = 1,
|
|
222
230
|
exchange = "",
|
|
231
|
+
coin = false,
|
|
223
232
|
): PricePoint {
|
|
224
233
|
return {
|
|
225
234
|
date: parseCloudPricePointDate(point.date, exchange),
|
|
@@ -227,7 +236,7 @@ export function mapPricePoint(
|
|
|
227
236
|
high: normalizePriceValueByDivisor(point.high, divisor),
|
|
228
237
|
low: normalizePriceValueByDivisor(point.low, divisor),
|
|
229
238
|
close: normalizePriceValueByDivisor(point.close, divisor) ?? point.close,
|
|
230
|
-
volume: point.volume,
|
|
239
|
+
volume: coin && point.volume === 0 ? undefined : point.volume,
|
|
231
240
|
...(point.partial === true ? { partial: true } : {}),
|
|
232
241
|
...(point.filled === true ? { filled: true } : {}),
|
|
233
242
|
...(point.unsettled === true ? { unsettled: true } : {}),
|
|
@@ -286,6 +295,7 @@ export function mapCloudFinancials(
|
|
|
286
295
|
const quote = rawQuote ? mapQuote(rawQuote, providerMeta) : undefined;
|
|
287
296
|
const divisor = rawQuote ? resolveCurrencyUnit(rawQuote.currency).divisor : 1;
|
|
288
297
|
const exchange = rawQuote?.listingExchangeName ?? rawQuote?.exchangeName ?? "";
|
|
298
|
+
const coin = isRoundTheClockCoin(rawQuote?.symbol, exchange);
|
|
289
299
|
return {
|
|
290
300
|
quote,
|
|
291
301
|
quoteMetadata: financials.quoteMetadata,
|
|
@@ -300,7 +310,7 @@ export function mapCloudFinancials(
|
|
|
300
310
|
priceHistory: (financials.priceHistory ?? []).map((point) =>
|
|
301
311
|
point.date instanceof Date
|
|
302
312
|
? point
|
|
303
|
-
: mapPricePoint(point as unknown as CloudPricePointPayload, divisor, exchange),
|
|
313
|
+
: mapPricePoint(point as unknown as CloudPricePointPayload, divisor, exchange, coin),
|
|
304
314
|
),
|
|
305
315
|
...(financials.epsEstimates ? { epsEstimates: financials.epsEstimates } : {}),
|
|
306
316
|
};
|
|
@@ -393,6 +403,25 @@ export function formatCloudDateTime(
|
|
|
393
403
|
return `${year}-${month}-${day}`;
|
|
394
404
|
}
|
|
395
405
|
|
|
406
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* The end of a window of bars that ends now. Gloom Cloud reads a date-only end
|
|
410
|
+
* as exclusive, so a daily or longer window ending on today's date holds no
|
|
411
|
+
* bar of today. Once today's regular session has settled its bar is final and
|
|
412
|
+
* the window ends the day after, so the bar is served as the range requests
|
|
413
|
+
* serve it. While the session trades, or before it opens, today's date stands:
|
|
414
|
+
* the day's bar is the live quote's then. Intraday ends carry their time.
|
|
415
|
+
*/
|
|
416
|
+
export function formatCloudHistoryEnd(end: Date, includeTime: boolean, exchange = ""): string {
|
|
417
|
+
const formatted = formatCloudDateTime(end, includeTime, exchange);
|
|
418
|
+
if (includeTime) return formatted;
|
|
419
|
+
const settled = latestSettledSessionDate(exchange, end.getTime());
|
|
420
|
+
return settled && settled >= formatted
|
|
421
|
+
? new Date(Date.parse(`${settled}T00:00:00Z`) + DAY_MS).toISOString().slice(0, 10)
|
|
422
|
+
: formatted;
|
|
423
|
+
}
|
|
424
|
+
|
|
396
425
|
const HISTORY_OUTPUT_SIZE: Record<TimeRange, number> = {
|
|
397
426
|
"1D": 24 * 12,
|
|
398
427
|
"1W": 7 * 24,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../api-client";
|
|
2
|
-
import type { PerpBoardRow, PerpMarketPayload } from "../../api-client/perps";
|
|
2
|
+
import type { PerpBoardPayload, PerpBoardRow, PerpMarketPayload } from "../../api-client/perps";
|
|
3
3
|
import type { InstrumentSearchResult } from "../../types/instrument";
|
|
4
4
|
import type { PricePoint, Quote } from "../../types/financials";
|
|
5
5
|
import type { PriceHistoryResult } from "../../types/price-history";
|
|
@@ -140,6 +140,25 @@ function searchResult(row: PerpBoardRow): InstrumentSearchResult | null {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* A client whose board searches are answered from one copy of the board, as
|
|
145
|
+
* the board answers them: its rows whose symbol, base asset or name holds the
|
|
146
|
+
* words, in its order. The board read without a search is the one a search
|
|
147
|
+
* reads (all of it with Pro, its preview without), so a list's searches cost
|
|
148
|
+
* one board request and find what each would find alone.
|
|
149
|
+
*/
|
|
150
|
+
export function boardSearchesFrom(board: PerpBoardPayload, client: PerpsClient = apiClient): PerpsClient {
|
|
151
|
+
const rows = Array.isArray(board?.rows) ? board.rows : [];
|
|
152
|
+
return {
|
|
153
|
+
getCloudPerpsMarket: client.getCloudPerpsMarket,
|
|
154
|
+
getCloudPerpsHistory: client.getCloudPerpsHistory,
|
|
155
|
+
getCloudPerpsBoard: async ({ search } = {}) => {
|
|
156
|
+
const wanted = search?.trim().toUpperCase();
|
|
157
|
+
return { ...board, rows: wanted ? rows.filter((row) => `${row.symbol} ${row.baseAsset} ${row.displayName}`.toUpperCase().includes(wanted)) : rows };
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
/** The one market a query names by its market id or listing key; null for a search. */
|
|
144
163
|
const namedListing = (query: string) => typedPerpetualListing(query) ?? (query.includes(":") ? perpetualListing(query) : null);
|
|
145
164
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daily sessions a source left out of a price history because its close was
|
|
3
|
+
* at or below zero (CL=F settled at -37.63 on 2020-04-20): no price can stand
|
|
4
|
+
* for them, so they are not bars, but a reader of the series is told they
|
|
5
|
+
* were sessions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const SESSION_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
9
|
+
// More than any daily history holds; a longer list is not one a source wrote.
|
|
10
|
+
const MAX_DATES = 10_000;
|
|
11
|
+
|
|
12
|
+
function isSessionDate(value: unknown): value is string {
|
|
13
|
+
return typeof value === "string" && SESSION_DATE.test(value) && Number.isFinite(Date.parse(`${value}T00:00:00Z`));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A stored or passed-on list: unique session dates, ascending; undefined for anything else or nothing. */
|
|
17
|
+
export function readNonPositiveCloseDates(value: unknown): string[] | undefined {
|
|
18
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_DATES || !value.every(isSessionDate)) return undefined;
|
|
19
|
+
return [...new Set(value)].sort();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The sessions a Gloom Cloud answer lists as left out for a close at or below
|
|
24
|
+
* zero (`omittedSessions: [{ date, reason: "non-positive-close" }]`). Entries
|
|
25
|
+
* for a reason this client does not know, or that do not read, are ignored.
|
|
26
|
+
*/
|
|
27
|
+
export function cloudNonPositiveCloseDates(value: unknown): string[] | undefined {
|
|
28
|
+
if (!Array.isArray(value) || value.length > MAX_DATES) return undefined;
|
|
29
|
+
return readNonPositiveCloseDates(value.flatMap((entry: unknown) => {
|
|
30
|
+
if (!entry || typeof entry !== "object") return [];
|
|
31
|
+
const { date, reason } = entry as { date?: unknown; reason?: unknown };
|
|
32
|
+
return reason === "non-positive-close" && isSessionDate(date) ? [date] : [];
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { PriceHistoryResult } from "../types/price-history";
|
|
|
5
5
|
import { parseHistorySession } from "../market-data/history-session";
|
|
6
6
|
import { canonicalHistoryInterval } from "./history-retention";
|
|
7
7
|
import { historyCoverageNoteText, isHistoryCoverageDate } from "./history-coverage";
|
|
8
|
+
import { readNonPositiveCloseDates } from "./history-omitted";
|
|
8
9
|
|
|
9
10
|
export type HistoryResultRequest =
|
|
10
11
|
| { kind: "range"; range: TimeRange }
|
|
@@ -36,8 +37,10 @@ export function normalizeHistoryResult(
|
|
|
36
37
|
interval: requestedInterval ?? result.resolution ?? undefined });
|
|
37
38
|
// A contradictory declaration cannot be laundered into unknown legacy data.
|
|
38
39
|
if (result.session != null && (!session || result.resolution === null)) return null;
|
|
40
|
+
const nonPositiveCloseDates = readNonPositiveCloseDates(result.nonPositiveCloseDates);
|
|
39
41
|
return { points: result.points, resolution: result.resolution,
|
|
40
42
|
...(session ? { session } : {}),
|
|
43
|
+
...(nonPositiveCloseDates ? { nonPositiveCloseDates } : {}),
|
|
41
44
|
...(typeof result.sourceKey === "string" && /^(provider|broker):[^\r\n\0]+$/.test(result.sourceKey) ? { sourceKey: result.sourceKey } : {}),
|
|
42
45
|
...(isHistoryCoverageDate(result.coverageStart) ? { coverageStart: result.coverageStart } : {}),
|
|
43
46
|
...(isHistoryCoverageDate(result.coverageStart) && historyCoverageNoteText(result.coverageNote)
|
|
@@ -173,5 +173,5 @@ export function parseHistoryRetentionError(value: unknown): HistoryRetentionErro
|
|
|
173
173
|
return new HistoryRetentionError(retention, {
|
|
174
174
|
candidates: value.candidates as HistoryRecoveryCandidate[], outcomes: value.outcomes as HistorySourceOutcome[],
|
|
175
175
|
});
|
|
176
|
-
} catch { return null; }
|
|
176
|
+
} catch { /* Intentional ignore: Invalid recovery metadata is rejected at the desktop JSON boundary. */ return null; }
|
|
177
177
|
}
|
|
@@ -47,6 +47,27 @@ function isProviderMiss(error: unknown): error is ProviderMissError {
|
|
|
47
47
|
|| (!!error && typeof error === "object" && PROVIDER_MISS_BRAND in error);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A source that could not answer just now (rate limited, overloaded, timed
|
|
52
|
+
* out) and may if asked again shortly. Unlike a miss, it says nothing about
|
|
53
|
+
* whether what was asked for exists.
|
|
54
|
+
*/
|
|
55
|
+
export class ProviderBusyError extends Error {
|
|
56
|
+
constructor(message = "The data service is busy", options?: { cause?: unknown }) {
|
|
57
|
+
super(message, options);
|
|
58
|
+
this.name = "ProviderBusyError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isProviderBusy(error: unknown): error is ProviderBusyError {
|
|
63
|
+
return error instanceof ProviderBusyError;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What a search its sources could not answer says, rather than that nothing matched. */
|
|
67
|
+
export const SEARCH_BUSY_MESSAGE = "Search is busy, try again in a moment.";
|
|
68
|
+
/** The code a structured answer (CLI `--json`, remote control) gives that failure. */
|
|
69
|
+
export const SEARCH_BUSY_CODE = "search_busy";
|
|
70
|
+
|
|
50
71
|
/** The reason a provider gave for a miss, or undefined when it gave none. */
|
|
51
72
|
export function providerMissReason(error: unknown): string | undefined {
|
|
52
73
|
return isProviderMiss(error) ? error.reason : undefined;
|
|
@@ -57,12 +78,15 @@ export interface ProviderMissNote {
|
|
|
57
78
|
reason?: string;
|
|
58
79
|
/** True while every provider that failed said it found no listing; one that failed another way (a timeout) clears it. */
|
|
59
80
|
notFound?: boolean;
|
|
81
|
+
/** The last provider failure that was not a miss (a refused session, a rate limit, a timeout). */
|
|
82
|
+
failure?: unknown;
|
|
60
83
|
}
|
|
61
84
|
|
|
62
85
|
export function noteProviderMiss(note: ProviderMissNote | undefined, error: unknown): void {
|
|
63
86
|
if (!note) return;
|
|
64
87
|
const reason = providerMissReason(error);
|
|
65
88
|
if (reason) note.reason = reason;
|
|
89
|
+
if (!isProviderMiss(error)) note.failure = error;
|
|
66
90
|
note.notFound = note.notFound !== false && isProviderMiss(error) && error.notFound === true;
|
|
67
91
|
}
|
|
68
92
|
|
|
@@ -93,6 +117,13 @@ export function isNoProviderMessage(message: string): boolean {
|
|
|
93
117
|
return /\bprovider available for\b/i.test(message) || notATickerSymbol(message) !== null;
|
|
94
118
|
}
|
|
95
119
|
|
|
120
|
+
/** The router's generic answer that no provider served a request, holding the failure behind it when a provider failed rather than missed. */
|
|
121
|
+
class NoProviderError extends Error {
|
|
122
|
+
constructor(message: string, readonly failure: unknown) {
|
|
123
|
+
super(message);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
96
127
|
/**
|
|
97
128
|
* The router's final error when no provider answered. A reason a provider gave
|
|
98
129
|
* replaces the generic wording, and so does the service's answer that no
|
|
@@ -102,7 +133,18 @@ export function isNoProviderMessage(message: string): boolean {
|
|
|
102
133
|
export function noProviderError(message: string, note: ProviderMissNote | undefined, symbol?: string): Error {
|
|
103
134
|
if (note?.reason) return createProviderMiss(note.reason, note.reason);
|
|
104
135
|
if (note?.notFound === true && symbol) return createProviderMiss(notATickerMessage(symbol), undefined, { notFound: true });
|
|
105
|
-
return new
|
|
136
|
+
return new NoProviderError(message, note?.failure);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Whether a failed read means no source has what was asked for: a source's
|
|
141
|
+
* miss, or the router's answer that none served it when none failed another
|
|
142
|
+
* way. A refused session, a rate limit or a timeout is not, as asked again it may answer.
|
|
143
|
+
*/
|
|
144
|
+
export function isNoSourceAnswer(error: unknown): boolean {
|
|
145
|
+
if (isProviderMiss(error)) return true;
|
|
146
|
+
if (error instanceof NoProviderError) return error.failure === undefined;
|
|
147
|
+
return error instanceof Error && !(error instanceof ProviderBusyError) && isNoProviderMessage(error.message);
|
|
106
148
|
}
|
|
107
149
|
|
|
108
150
|
export function shouldLogProviderError(error: unknown): boolean {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../../utils/log-failure";
|
|
1
2
|
import type {
|
|
2
3
|
CachedFinancialsTarget,
|
|
3
4
|
MarketDataRequestContext,
|
|
@@ -96,7 +97,7 @@ export class ProviderRouterBatchRoutes {
|
|
|
96
97
|
providerIndexes.set(key, bucket);
|
|
97
98
|
}
|
|
98
99
|
const uniqueTargets = [...providerIndexes.values()].map((bucket) => bucket[0]!.target);
|
|
99
|
-
const batchResults = await batchProvider.getQuotesBatch!(uniqueTargets, providerOptions).catch((
|
|
100
|
+
const batchResults = await batchProvider.getQuotesBatch!(uniqueTargets, providerOptions).catch(logFailure("sources.provider-router.batches", "Loading batch quotes failed", []));
|
|
100
101
|
for (const item of batchResults) {
|
|
101
102
|
if (!isProviderQuoteUsableForCurrentSession(item.quote, item.target.exchange, item.target.symbol, { recentAnswer: true })) continue;
|
|
102
103
|
const key = this.quoteBatchKey(item.target);
|
|
@@ -163,7 +164,7 @@ export class ProviderRouterBatchRoutes {
|
|
|
163
164
|
providerIndexes.set(key, bucket);
|
|
164
165
|
}
|
|
165
166
|
const uniqueTargets = [...providerIndexes.values()].map((bucket) => bucket[0]!.target);
|
|
166
|
-
const batchResults = await batchProvider.getTickerFinancialsBatch!(uniqueTargets, options).catch((
|
|
167
|
+
const batchResults = await batchProvider.getTickerFinancialsBatch!(uniqueTargets, options).catch(logFailure("sources.provider-router.batches", "Loading batch financials failed", []));
|
|
167
168
|
for (const item of batchResults) {
|
|
168
169
|
if (!item.financials) continue;
|
|
169
170
|
if (!item.target.instrument && !providerFinancialsMatchTarget(item.financials, item.target.symbol, item.target.exchange)) continue;
|