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,12 +1,13 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
2
|
import { newsProvider, type NewsCapability } from "../../../capabilities";
|
|
3
3
|
import { cloudNewsParams, mapCloudNewsArticle } from "../../../sources/gloomberb-cloud/news";
|
|
4
|
-
import type { NewsArticle, NewsQuery } from "../../../types
|
|
4
|
+
import type { NewsArticle, NewsPage, NewsQuery } from "../../../news/types";
|
|
5
5
|
|
|
6
6
|
interface BackendNews {
|
|
7
7
|
id: string;
|
|
8
8
|
name: string;
|
|
9
9
|
getNews(query: NewsQuery): Promise<NewsArticle[]>;
|
|
10
|
+
getNewsPage(query: NewsQuery): Promise<NewsPage>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
type CloudNewsClient = Pick<typeof apiClient, "getCloudNews" | "getCloudNewsStory">;
|
|
@@ -33,7 +34,8 @@ export function createDesktopNewsSource(backend: BackendNews, client: CloudNewsC
|
|
|
33
34
|
nextCursor: response.nextCursor ?? null,
|
|
34
35
|
};
|
|
35
36
|
} catch {
|
|
36
|
-
|
|
37
|
+
// The aggregate page keeps RSS failures beside any usable stories.
|
|
38
|
+
return backend.getNewsPage(query);
|
|
37
39
|
}
|
|
38
40
|
},
|
|
39
41
|
fetchNewsStory: async (storyId) => mapCloudNewsArticle(await client.getCloudNewsStory(storyId)),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CapabilityManifest } from "../../../../capabilities";
|
|
2
|
-
import type { NewsArticle, NewsQuery } from "../../../../news/types";
|
|
2
|
+
import type { NewsArticle, NewsPage, NewsQuery } from "../../../../news/types";
|
|
3
3
|
import type { DataProvider } from "../../../../types/data-provider";
|
|
4
4
|
import type { TickerFinancials } from "../../../../types/financials";
|
|
5
5
|
import { backendRequest, getElectrobunBackendInitSnapshot, subscribeBackendCapability } from "../backend-rpc";
|
|
@@ -14,6 +14,7 @@ const ASSET_DATA_REQUEST_TIMEOUT_MS = 115_000;
|
|
|
14
14
|
|
|
15
15
|
export type RemoteAssetDataClient = DataProvider & {
|
|
16
16
|
getNews(query: NewsQuery): Promise<NewsArticle[]>;
|
|
17
|
+
getNewsPage(query: NewsQuery): Promise<NewsPage>;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
interface RemoteAssetDataClientBase {
|
|
@@ -21,6 +22,7 @@ interface RemoteAssetDataClientBase {
|
|
|
21
22
|
name: string;
|
|
22
23
|
getCachedFinancialsForTargets: NonNullable<DataProvider["getCachedFinancialsForTargets"]>;
|
|
23
24
|
getNews(query: NewsQuery): Promise<NewsArticle[]>;
|
|
25
|
+
getNewsPage(query: NewsQuery): Promise<NewsPage>;
|
|
24
26
|
subscribeQuotes: NonNullable<DataProvider["subscribeQuotes"]>;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -130,6 +132,11 @@ export function createRemoteAssetDataClient(): RemoteAssetDataClient {
|
|
|
130
132
|
? invoke<NewsArticle[]>(NEWS_CAPABILITY_ID, "fetchNews", { query })
|
|
131
133
|
: Promise.resolve([])
|
|
132
134
|
),
|
|
135
|
+
getNewsPage: (query) => (
|
|
136
|
+
hasRendererOperation(newsOperations, "fetchNewsPage")
|
|
137
|
+
? invoke<NewsPage>(NEWS_CAPABILITY_ID, "fetchNewsPage", { query })
|
|
138
|
+
: Promise.reject(new Error("News is unavailable."))
|
|
139
|
+
),
|
|
133
140
|
subscribeQuotes: (targets, onQuote) => {
|
|
134
141
|
if (!hasRendererOperation(assetDataOperations, "subscribeQuotes")) return () => {};
|
|
135
142
|
return quoteSubscriptions.subscribe(targets, onQuote);
|
|
@@ -37,6 +37,9 @@ import {
|
|
|
37
37
|
readOrCreateInstallId,
|
|
38
38
|
} from "../../telemetry/crash-reports-node";
|
|
39
39
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
40
|
+
import { installUpdateFailureReporter } from "../../telemetry/update-failures";
|
|
41
|
+
import { createNodeUpdateFailureHost } from "../../telemetry/update-failures-node";
|
|
42
|
+
import { terminalInstallKind } from "../../updater";
|
|
40
43
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
41
44
|
import { attentionCountsEnabled, installAttentionCounter } from "../../telemetry/attention-counts";
|
|
42
45
|
import { starPromptEnvironmentAllows } from "../../app/star-prompt/model";
|
|
@@ -117,6 +120,10 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
117
120
|
surface: "terminal",
|
|
118
121
|
getConfig: () => ({ dataDir: config.dataDir, telemetry: currentTelemetryConfig(config)?.telemetry }),
|
|
119
122
|
}));
|
|
123
|
+
installUpdateFailureReporter(createNodeUpdateFailureHost({
|
|
124
|
+
installKind: terminalInstallKind(),
|
|
125
|
+
getDataDir: () => config.dataDir,
|
|
126
|
+
}));
|
|
120
127
|
installUsageCounter({
|
|
121
128
|
surface: "terminal",
|
|
122
129
|
os: describeNodeOs(),
|
package/src/series/validate.ts
CHANGED
|
@@ -14,8 +14,8 @@ const DRAW_STYLES: readonly SeriesDrawStyle[] = ["line", "area", "step", "column
|
|
|
14
14
|
const PLACEMENTS: readonly SeriesPlacement[] = ["price", "panel"];
|
|
15
15
|
const LINE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
16
16
|
const COLOR_PATTERN = /^#[0-9A-Fa-f]{6}$/;
|
|
17
|
-
/** A chart holds
|
|
18
|
-
const MAX_SERIES_LINES =
|
|
17
|
+
/** A chart holds twenty series: the ticker's price and up to sixteen lines, with room for a few more. */
|
|
18
|
+
const MAX_SERIES_LINES = 16;
|
|
19
19
|
|
|
20
20
|
/** Units with no scale: a percentage of something, not a quantity of it. */
|
|
21
21
|
const RATE_UNITS = ["%", "bp", "x", "ratio", "index", "score", "points"] as const;
|
|
@@ -30,6 +30,7 @@ import { parseHistorySession } from "../../market-data/history-session";
|
|
|
30
30
|
import { quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../../market-data/quotes/metadata";
|
|
31
31
|
import type { InstrumentSearchResult } from "../../types/instrument";
|
|
32
32
|
import type { TickerChanges } from "../../types/ticker-changes";
|
|
33
|
+
import type { IssuerListings } from "../../types/issuer-listings";
|
|
33
34
|
import {
|
|
34
35
|
apiClient,
|
|
35
36
|
type CloudAnalystResearchPayload,
|
|
@@ -45,6 +46,7 @@ import { isRoundTheClockCoin } from "../../utils/crypto-pair";
|
|
|
45
46
|
import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
|
|
46
47
|
import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
|
|
47
48
|
import { createProviderMiss, ProviderBusyError, providerMissReason } from "../provider-errors";
|
|
49
|
+
import { parseListingDelisting } from "../../market-data/quotes/delisting";
|
|
48
50
|
import { isRetryableApiError } from "../../api-client/errors";
|
|
49
51
|
import { assertSecRegistrantMatches } from "../sec-registrant";
|
|
50
52
|
import { nonUsSecListingVenue } from "../../utils/sec";
|
|
@@ -251,9 +253,11 @@ function mapCloudPriceHistory(
|
|
|
251
253
|
}
|
|
252
254
|
// Sessions of daily bars the answer left out for a close at or below zero.
|
|
253
255
|
const nonPositiveCloseDates = resolution === "1d" ? cloudNonPositiveCloseDates(response.omittedSessions) : undefined;
|
|
256
|
+
const delisting = parseListingDelisting(response.delisting);
|
|
254
257
|
return {
|
|
255
258
|
points,
|
|
256
259
|
resolution,
|
|
260
|
+
...(delisting ? { delisting } : {}),
|
|
257
261
|
...(session && matchingSource && matchingIdentity ? { session } : {}),
|
|
258
262
|
...(nonPositiveCloseDates ? { nonPositiveCloseDates } : {}),
|
|
259
263
|
...(coverageStart ? { coverageStart } : {}),
|
|
@@ -262,6 +266,28 @@ function mapCloudPriceHistory(
|
|
|
262
266
|
};
|
|
263
267
|
}
|
|
264
268
|
|
|
269
|
+
const DAY_MS = 86_400_000;
|
|
270
|
+
/** Listings Gloom Cloud said no longer trade, by listing key: the day after each one's last session. */
|
|
271
|
+
const delistedHistoryEnds = new Map<string, Date>();
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Where a trailing window ends for a listing that no longer trades: the day
|
|
275
|
+
* after its last session, as a window ending now ends the day after today's.
|
|
276
|
+
* A window ending now would hold none of its bars, or only the last few.
|
|
277
|
+
*/
|
|
278
|
+
function delistedHistoryEnd(response: CloudMarketResponse<unknown>, key: string): Date | null {
|
|
279
|
+
const delisting = parseListingDelisting(response.delisting);
|
|
280
|
+
if (!delisting) {
|
|
281
|
+
// The symbol trades again (another issuer took it): its windows end now.
|
|
282
|
+
delistedHistoryEnds.delete(key);
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const end = new Date(Date.parse(`${delisting.lastTradeDate}T00:00:00Z`) + DAY_MS);
|
|
286
|
+
if (delistedHistoryEnds.size >= 1024) delistedHistoryEnds.clear();
|
|
287
|
+
delistedHistoryEnds.set(key, end);
|
|
288
|
+
return end;
|
|
289
|
+
}
|
|
290
|
+
|
|
265
291
|
function cloudHistoryResolution(interval: string): ManualChartResolution | null {
|
|
266
292
|
const canonical = canonicalHistoryInterval(interval);
|
|
267
293
|
return (["1m", "5m", "15m", "30m", "1h", "1d", "1wk", "1mo"] as const)
|
|
@@ -549,14 +575,14 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
549
575
|
// A successful batch can contain an expired cache fallback for only
|
|
550
576
|
// one listing. Match the single-quote freshness boundary and let the
|
|
551
577
|
// normal router retry that item without discarding its healthy peers.
|
|
552
|
-
|
|
578
|
+
// A listing that no longer trades has only that last quote: kept, flagged stale.
|
|
579
|
+
const delisted = !!parseListingDelisting(item.data?.delisting);
|
|
580
|
+
if (item.stale === true && !delisted) {
|
|
553
581
|
return { target, quote: null, error: createProviderMiss(`Cloud quotes are stale for ${target.symbol}`) };
|
|
554
582
|
}
|
|
555
583
|
if ((item.status === "success" || item.status === "partial") && item.data) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
quote: retainRequestedQuoteSymbol(mapQuote(item.data), target.symbol),
|
|
559
|
-
};
|
|
584
|
+
const quote = retainRequestedQuoteSymbol(mapQuote(item.data), target.symbol);
|
|
585
|
+
return { target, quote: item.stale === true ? { ...quote, stale: true } : quote };
|
|
560
586
|
}
|
|
561
587
|
return {
|
|
562
588
|
target,
|
|
@@ -693,6 +719,12 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
693
719
|
return cloudTickerChangesBatch(symbols, options);
|
|
694
720
|
}
|
|
695
721
|
|
|
722
|
+
async getIssuerListings(symbol: string, exchange = "", options: { signal?: AbortSignal } = {}): Promise<IssuerListings | null> {
|
|
723
|
+
const response = await apiClient.getCloudIssuerListings(symbol, exchange, options);
|
|
724
|
+
// "empty" is an answer: the service holds no company for the listing.
|
|
725
|
+
return response.status === "success" || response.status === "partial" ? response.data : null;
|
|
726
|
+
}
|
|
727
|
+
|
|
696
728
|
async searchBatch(queries: string[], options: { limit?: number; signal?: AbortSignal } = {}): Promise<Map<string, InstrumentSearchResult[]> | null> {
|
|
697
729
|
const answers = await cloudSearchBatch(queries, options);
|
|
698
730
|
if (!answers) return null;
|
|
@@ -778,19 +810,27 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
778
810
|
const target = cloudInstrumentTarget(ticker, exchange);
|
|
779
811
|
exchange = target.exchange ?? "";
|
|
780
812
|
const interval = toCloudInterval(resolution);
|
|
781
|
-
const endDate = new Date();
|
|
782
|
-
const startDate = subtractTimeRange(endDate, bufferRange);
|
|
783
813
|
const includeTime = /^\d+(min|h)$/i.test(interval);
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
814
|
+
const listingKey = canonicalTickerKey(target.symbol, exchange);
|
|
815
|
+
const load = (endDate: Date) => {
|
|
816
|
+
const startDate = subtractTimeRange(endDate, bufferRange);
|
|
817
|
+
return withCloudFallback(
|
|
818
|
+
() => loadCloudHistory(interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
819
|
+
interval,
|
|
820
|
+
startDate: formatCloudDateTime(startDate, includeTime, exchange),
|
|
821
|
+
endDate: formatCloudHistoryEnd(endDate, includeTime, exchange),
|
|
822
|
+
...(session ? { session } : {}),
|
|
823
|
+
}), _context),
|
|
824
|
+
`Cloud chart data is unavailable for ${ticker}`,
|
|
825
|
+
).then((answer) => ({ ...answer, startDate, endDate }));
|
|
826
|
+
};
|
|
827
|
+
// A listing that no longer trades is read up to its last session, not up to now.
|
|
828
|
+
const knownEnd = delistedHistoryEnds.get(listingKey);
|
|
829
|
+
const now = new Date();
|
|
830
|
+
let answer = await load(knownEnd ?? now);
|
|
831
|
+
const delistedEnd = delistedHistoryEnd(answer.response, listingKey);
|
|
832
|
+
if (delistedEnd ? delistedEnd.getTime() < answer.endDate.getTime() : knownEnd) answer = await load(delistedEnd ?? now);
|
|
833
|
+
return mapCloudPriceHistory(answer.response, target.symbol, exchange, interval, answer.startDate, answer.endDate, answer.extendedHours);
|
|
794
834
|
}
|
|
795
835
|
|
|
796
836
|
async getDetailedPriceHistory(
|
|
@@ -27,6 +27,7 @@ import { createProviderMiss } from "../provider-errors";
|
|
|
27
27
|
import { reconcileQuoteDayRange } from "../../market-data/quotes/day-range";
|
|
28
28
|
import { redactUnavailableFundamentals } from "../../utils/fundamentals";
|
|
29
29
|
import { readWithdrawnObservations } from "../../utils/statement-gaps";
|
|
30
|
+
import { parseListingDelisting } from "../../market-data/quotes/delisting";
|
|
30
31
|
|
|
31
32
|
export const GLOOMBERB_CLOUD_PROVIDER_ID = "gloomberb-cloud" as const;
|
|
32
33
|
|
|
@@ -94,6 +95,7 @@ export function mapQuote(
|
|
|
94
95
|
: futuresBasis ?? quote.priceBasis;
|
|
95
96
|
return withoutUndefinedFields(reconcileQuoteDayRange({
|
|
96
97
|
...quote,
|
|
98
|
+
delisting: parseListingDelisting(quote.delisting),
|
|
97
99
|
priceBasis,
|
|
98
100
|
priceObservation,
|
|
99
101
|
currency: currency || quote.currency,
|
|
@@ -130,6 +132,8 @@ export function mapQuote(
|
|
|
130
132
|
divisor,
|
|
131
133
|
),
|
|
132
134
|
extendedSessionPrint: typeof quote.extendedSessionPrint === "boolean" ? quote.extendedSessionPrint : undefined,
|
|
135
|
+
// A coin's move over 24 hours, or none, when its previous close is undatable.
|
|
136
|
+
changeBasis: quote.changeBasis === "24h" || quote.changeBasis === "none" ? quote.changeBasis : undefined,
|
|
133
137
|
overnightPrice: normalizePriceValueByDivisor(quote.overnightPrice, divisor),
|
|
134
138
|
overnightChange: normalizePriceValueByDivisor(quote.overnightChange, divisor),
|
|
135
139
|
overnightPriceKind: quote.overnightPriceKind === "trade" || quote.overnightPriceKind === "mark"
|
|
@@ -6,6 +6,7 @@ import { parseHistorySession } from "../market-data/history-session";
|
|
|
6
6
|
import { canonicalHistoryInterval } from "./history-retention";
|
|
7
7
|
import { historyCoverageNoteText, isHistoryCoverageDate } from "./history-coverage";
|
|
8
8
|
import { readNonPositiveCloseDates } from "./history-omitted";
|
|
9
|
+
import { parseListingDelisting } from "../market-data/quotes/delisting";
|
|
9
10
|
|
|
10
11
|
export type HistoryResultRequest =
|
|
11
12
|
| { kind: "range"; range: TimeRange }
|
|
@@ -38,6 +39,7 @@ export function normalizeHistoryResult(
|
|
|
38
39
|
// A contradictory declaration cannot be laundered into unknown legacy data.
|
|
39
40
|
if (result.session != null && (!session || result.resolution === null)) return null;
|
|
40
41
|
const nonPositiveCloseDates = readNonPositiveCloseDates(result.nonPositiveCloseDates);
|
|
42
|
+
const delisting = parseListingDelisting(result.delisting);
|
|
41
43
|
return { points: result.points, resolution: result.resolution,
|
|
42
44
|
...(session ? { session } : {}),
|
|
43
45
|
...(nonPositiveCloseDates ? { nonPositiveCloseDates } : {}),
|
|
@@ -47,6 +49,7 @@ export function normalizeHistoryResult(
|
|
|
47
49
|
? { coverageNote: historyCoverageNoteText(result.coverageNote) } : {}),
|
|
48
50
|
...(result.extendedHours === true ? { extendedHours: true } : {}),
|
|
49
51
|
...(result.savedCopyBehind === true ? { savedCopyBehind: true as const } : {}),
|
|
52
|
+
...(delisting ? { delisting } : {}),
|
|
50
53
|
};
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// Registered, so a miss raised by another copy of this module (a plugin that
|
|
2
|
+
// resolved `gloomberb/market-data` from the source tree) is still recognized.
|
|
3
|
+
const PROVIDER_MISS_BRAND = Symbol.for("gloomberb.provider-miss");
|
|
2
4
|
const EXPECTED_PROVIDER_MISS = /No data found|symbol may be delisted|"code":"Not Found"|No history for /i;
|
|
3
5
|
|
|
4
6
|
const MAX_REASON_LENGTH = 200;
|
|
@@ -4,6 +4,7 @@ import { canonicalHistoryInterval, HistoryRetentionError, isHistoryRetentionErro
|
|
|
4
4
|
type HistoryRecoveryCandidate, type HistoryRetention, type HistorySourceOutcome } from "../history-retention";
|
|
5
5
|
import type { BrokerCandidate } from "./brokers";
|
|
6
6
|
import { withBrokerTimeout } from "./brokers";
|
|
7
|
+
import type { BrokerHistoryOptions } from "../../types/broker";
|
|
7
8
|
import type { DataProvider, MarketDataRequestContext } from "../../types/data-provider";
|
|
8
9
|
import type { PriceHistoryResult } from "../../types/price-history";
|
|
9
10
|
import { fetchHistoryResult, historyResolutionForInterval, InvalidHistoryResultError, normalizeHistoryResult } from "../history-result";
|
|
@@ -244,6 +245,29 @@ function mergeRequestHistoryGaps(
|
|
|
244
245
|
})));
|
|
245
246
|
}
|
|
246
247
|
|
|
248
|
+
/** The extended session goes only to a broker that serves it, and only when the chart asked. */
|
|
249
|
+
function brokerHistoryOptions(candidate: BrokerCandidate, context?: MarketDataRequestContext): [BrokerHistoryOptions] | [] {
|
|
250
|
+
return context?.historySession === "extended" && candidate.broker.extendedHoursHistory === true ? [{ session: "extended" }] : [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* With Ext hours on, a broker that serves only the regular session answers
|
|
255
|
+
* after the market-data providers, which may hold the extended session; it is
|
|
256
|
+
* still the last resort before nothing. Otherwise the order is unchanged.
|
|
257
|
+
*/
|
|
258
|
+
function brokersForSession(candidates: BrokerCandidate[], context?: MarketDataRequestContext): { first: BrokerCandidate[]; last: BrokerCandidate[] } {
|
|
259
|
+
if (context?.historySession !== "extended") return { first: candidates, last: [] };
|
|
260
|
+
return {
|
|
261
|
+
first: candidates.filter((candidate) => candidate.broker.extendedHoursHistory === true),
|
|
262
|
+
last: candidates.filter((candidate) => candidate.broker.extendedHoursHistory !== true),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function isIntradayInterval(interval: string | undefined): boolean {
|
|
267
|
+
const ms = interval ? priceHistoryIntervalMs(interval) : null;
|
|
268
|
+
return ms != null && ms < 24 * 60 * 60 * 1000;
|
|
269
|
+
}
|
|
270
|
+
|
|
247
271
|
function normalizeRequestResult(value: PriceHistoryResult, request: HistoryRequestDescriptor): PriceHistoryResult {
|
|
248
272
|
// Only this router says a copy is behind, when it answers: neither a source
|
|
249
273
|
// nor a stored copy can carry that verdict in.
|
|
@@ -254,8 +278,12 @@ function normalizeRequestResult(value: PriceHistoryResult, request: HistoryReque
|
|
|
254
278
|
};
|
|
255
279
|
}
|
|
256
280
|
|
|
257
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* A copy fetched at `fetchedAt`, now for a source's answer. A listing that no
|
|
283
|
+
* longer trades is never behind: its bars end on its last session for good.
|
|
284
|
+
*/
|
|
258
285
|
function resultIsStale(value: PriceHistoryResult, symbol: string, exchange: string, fetchedAt: number, intervalMs?: number | null): boolean {
|
|
286
|
+
if (value.delisting) return false;
|
|
259
287
|
return isPriceHistoryStaleForCurrentWindow(value.points, Date.now(), {
|
|
260
288
|
symbol, exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
|
|
261
289
|
fetchedAt,
|
|
@@ -425,6 +453,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
425
453
|
bufferRange,
|
|
426
454
|
resolution,
|
|
427
455
|
context?.instrument ?? null,
|
|
456
|
+
...brokerHistoryOptions(candidate, context),
|
|
428
457
|
)
|
|
429
458
|
: null,
|
|
430
459
|
fetchProvider: (provider) => fetchHistoryResult(provider, ticker, exchange, { kind: "resolution", range: bufferRange, resolution }, context),
|
|
@@ -543,6 +572,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
543
572
|
endDate,
|
|
544
573
|
barSize,
|
|
545
574
|
context?.instrument ?? null,
|
|
575
|
+
...brokerHistoryOptions(candidate, context),
|
|
546
576
|
)
|
|
547
577
|
: null,
|
|
548
578
|
fetchProvider: (provider) => fetchHistoryResult(provider, ticker, exchange, { kind: "detail", start: startDate, end: endDate, interval: barSize }, context),
|
|
@@ -551,10 +581,13 @@ export class ProviderRouterHistoryRoutes {
|
|
|
551
581
|
|
|
552
582
|
private async executeHistoryRequest(request: HistoryRequestDescriptor): Promise<PriceHistoryResult> {
|
|
553
583
|
const recovery = this.validateRecovery(request);
|
|
554
|
-
const brokerCandidates
|
|
584
|
+
const { first: brokerCandidates, last: regularSessionBrokers } = brokersForSession(
|
|
585
|
+
recovery ? [] : this.deps.getBrokerCandidatesForContext(request.context, false), request.context);
|
|
586
|
+
// Cached copies rank in the order the sources are asked.
|
|
555
587
|
const sourceKeys = recovery ? [recovery.sourceKey] : [
|
|
556
588
|
...brokerCandidates.map((candidate) => this.deps.brokerSourceKey(candidate)),
|
|
557
589
|
...this.deps.getProviderSourceKeys(),
|
|
590
|
+
...regularSessionBrokers.map((candidate) => this.deps.brokerSourceKey(candidate)),
|
|
558
591
|
];
|
|
559
592
|
const cachedRecords = listCachedResources<unknown>(
|
|
560
593
|
this.deps.resources,
|
|
@@ -584,6 +617,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
584
617
|
const currentWindow = request.requestedEnd === undefined || isCurrentHistoryWindow(new Date(request.requestedEnd));
|
|
585
618
|
const now = Date.now();
|
|
586
619
|
const fetchState = (record: { fetchedAt: number; value: PriceHistoryResult }, checkedAt?: number): CalendarHistoryFetchState => currentWindow
|
|
620
|
+
&& !record.value.delisting
|
|
587
621
|
? calendarHistoryFetchState(record.value.points, record.fetchedAt, now, {
|
|
588
622
|
symbol: target.symbol,
|
|
589
623
|
exchange: target.exchange || request.target.exchange,
|
|
@@ -661,6 +695,12 @@ export class ProviderRouterHistoryRoutes {
|
|
|
661
695
|
});
|
|
662
696
|
if (providerResult && hasUsablePriceHistory(providerResult.value.points)) return withReportedGaps(providerResult.value);
|
|
663
697
|
if (providerResult) reportedGaps.push(providerResult.value);
|
|
698
|
+
if (regularSessionBrokers.length) {
|
|
699
|
+
const regularResult = await withBrokerTimeout(this.fetchBrokerHistory(request, regularSessionBrokers, onUnavailable, attempts));
|
|
700
|
+
for (const sourceKey of [...attempts.pending]) recordHistoryOutcome(attempts, sourceKey, "timeout");
|
|
701
|
+
if (regularResult && hasUsablePriceHistory(regularResult.value.points)) return withReportedGaps(regularResult.value);
|
|
702
|
+
if (regularResult) reportedGaps.push(regularResult.value);
|
|
703
|
+
}
|
|
664
704
|
// A failed refetch falls back in the same order the cache hit used, so a
|
|
665
705
|
// failing request answers what the next request will: a current copy
|
|
666
706
|
// first, else a saved copy short of the latest session, which beats an
|
|
@@ -722,10 +762,12 @@ export class ProviderRouterHistoryRoutes {
|
|
|
722
762
|
private async refreshHistory(request: HistoryRequestDescriptor): Promise<void> {
|
|
723
763
|
try {
|
|
724
764
|
const recovery = this.validateRecovery(request);
|
|
725
|
-
const
|
|
726
|
-
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request,
|
|
765
|
+
const { first, last } = brokersForSession(recovery ? [] : this.deps.getBrokerCandidatesForContext(request.context, false), request.context);
|
|
766
|
+
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request, first));
|
|
727
767
|
if (brokerResult && hasUsablePriceHistory(brokerResult.value.points)) return;
|
|
728
|
-
await this.fetchProviderHistory(request);
|
|
768
|
+
const providerResult = await this.fetchProviderHistory(request);
|
|
769
|
+
if (providerResult && hasUsablePriceHistory(providerResult.value.points)) return;
|
|
770
|
+
if (last.length) await withBrokerTimeout(this.fetchBrokerHistory(request, last));
|
|
729
771
|
} catch (error) {
|
|
730
772
|
logFailure("sources.provider-router.history", "Refreshing background price history failed")(error);
|
|
731
773
|
}
|
|
@@ -745,7 +787,8 @@ export class ProviderRouterHistoryRoutes {
|
|
|
745
787
|
const fetched = await request.fetchBroker(candidate);
|
|
746
788
|
if (fetched === null) { recordHistoryOutcome(attempts, sourceKey, "empty"); return null; }
|
|
747
789
|
if (!Array.isArray(fetched)) { recordHistoryOutcome(attempts, sourceKey, "malformed"); return null; }
|
|
748
|
-
const value = normalizeRequestResult({ points: fetched, resolution: historyResolutionForInterval(request.interval), sourceKey
|
|
790
|
+
const value = normalizeRequestResult({ points: fetched, resolution: historyResolutionForInterval(request.interval), sourceKey,
|
|
791
|
+
...(brokerHistoryOptions(candidate, request.context).length && isIntradayInterval(request.interval) ? { extendedHours: true } : {}) }, request);
|
|
749
792
|
if (hasUsablePriceHistory(value.points) && request.isFetchedValueStale(value)) { recordHistoryOutcome(attempts, sourceKey, "stale"); return null; }
|
|
750
793
|
recordHistoryOutcome(attempts, sourceKey, hasUsablePriceHistory(value.points) ? "success" : value.points.length ? "reported-gaps" : "empty");
|
|
751
794
|
onUnavailable?.(this.deps.brokerSourceKey(candidate), value);
|
|
@@ -26,6 +26,7 @@ import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget
|
|
|
26
26
|
import type { NewsArticle, NewsQuery } from "../../news/types";
|
|
27
27
|
import type { BrokerContractRef, InstrumentSearchResult } from "../../types/instrument";
|
|
28
28
|
import type { TickerChanges } from "../../types/ticker-changes";
|
|
29
|
+
import type { IssuerListings } from "../../types/issuer-listings";
|
|
29
30
|
import type { TimeRange } from "../../time-series/range";
|
|
30
31
|
import type { ChartResolutionSupport, ManualChartResolution } from "../../time-series/resolution";
|
|
31
32
|
import { debugLog } from "../../utils/debug-log";
|
|
@@ -317,6 +318,21 @@ export class AssetDataRouter implements DataProvider {
|
|
|
317
318
|
return null;
|
|
318
319
|
}
|
|
319
320
|
|
|
321
|
+
/** The first source that holds the company's listings; one that fails is passed over. */
|
|
322
|
+
async getIssuerListings(symbol: string, exchange?: string, options?: { signal?: AbortSignal }): Promise<IssuerListings | null> {
|
|
323
|
+
for (const provider of this.providersInPriorityOrder()) {
|
|
324
|
+
if (!provider.getIssuerListings) continue;
|
|
325
|
+
try {
|
|
326
|
+
const listings = await provider.getIssuerListings(symbol, exchange, options);
|
|
327
|
+
if (listings) return listings;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (options?.signal?.aborted) return null;
|
|
330
|
+
logFailure("sources.provider-router.index", "Issuer listings failed")(error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
320
336
|
async getEarningsCalendar(symbols: string[], context?: MarketDataRequestContext) {
|
|
321
337
|
return this.supplementalRoutes.getEarningsCalendar(symbols, context);
|
|
322
338
|
}
|
|
@@ -412,7 +428,8 @@ export class AssetDataRouter implements DataProvider {
|
|
|
412
428
|
}
|
|
413
429
|
|
|
414
430
|
async getOptionsChain(ticker: string, exchange?: string, expirationDate?: number, context?: MarketDataRequestContext): Promise<OptionsChain> {
|
|
415
|
-
return (await this.getCachedQuery("getOptionsChain", [ticker, exchange, expirationDate, context])
|
|
431
|
+
return (await this.getCachedQuery("getOptionsChain", [ticker, exchange, expirationDate, context])
|
|
432
|
+
.load({ force: context?.cacheMode === "refresh", background: this.revalidateInBackground && context?.cacheMode !== "current" })).value;
|
|
416
433
|
}
|
|
417
434
|
|
|
418
435
|
getCachedQuery<K extends CachedAssetMethod>(method: K, args: CachedAssetArgs<K>) {
|
|
@@ -190,6 +190,19 @@ export function usePaneInView(): boolean {
|
|
|
190
190
|
return useContext(PaneInViewContext);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Whether the pane around the caller is the focused one, as its frame draws
|
|
195
|
+
* it (a pane under the open command bar is not). Null outside a pane.
|
|
196
|
+
*/
|
|
197
|
+
const PaneFocusedContext = createContext<boolean | null>(null);
|
|
198
|
+
|
|
199
|
+
export const PaneFocusedProvider = PaneFocusedContext.Provider;
|
|
200
|
+
|
|
201
|
+
/** For chrome a pane shows only while focused (a chart's drawing tools); null outside a pane. */
|
|
202
|
+
export function usePaneFocused(): boolean | null {
|
|
203
|
+
return useContext(PaneFocusedContext);
|
|
204
|
+
}
|
|
205
|
+
|
|
193
206
|
/**
|
|
194
207
|
* Gate a pane's market data (stream priority, polls, refresh clocks) on this:
|
|
195
208
|
* the app can be seen and the pane is on screen. A covered pane should keep
|
package/src/sync/controller.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type SyncSnapshot,
|
|
14
14
|
type SyncTransport,
|
|
15
15
|
} from "./types";
|
|
16
|
+
import type { LocalMarketDataLookup } from "./local-only";
|
|
16
17
|
|
|
17
18
|
type CloudSyncPhase = "idle" | "disabled" | "syncing" | "synced" | "error";
|
|
18
19
|
|
|
@@ -32,6 +33,8 @@ interface SyncRuntime {
|
|
|
32
33
|
getContributors: () => RegisteredSyncContributor[];
|
|
33
34
|
getTransport: () => RegisteredSyncTransport | null;
|
|
34
35
|
baselineStore?: SyncBaselineStore;
|
|
36
|
+
/** Sources whose quotes and bars never leave this device (`BrokerAdapter.localMarketData`); none when absent. */
|
|
37
|
+
isLocalMarketData?: LocalMarketDataLookup;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
const CLIENT_ID_STORAGE_KEY = "gloomberb.sync.clientId";
|
|
@@ -357,6 +360,7 @@ export class CloudSyncController {
|
|
|
357
360
|
state: runtime.getState(),
|
|
358
361
|
baselinePayload: this.loadBaseline(runtime)?.[entry.contributor.id] ?? null,
|
|
359
362
|
memo: this.memoFor(runtime, entry.contributor.id),
|
|
363
|
+
isLocalMarketData: runtime.isLocalMarketData,
|
|
360
364
|
});
|
|
361
365
|
payloads[entry.contributor.id] = {
|
|
362
366
|
schemaVersion: entry.contributor.schemaVersion,
|