gloomberb 0.16.0 → 0.16.2
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 -1
- package/package.json +2 -1
- package/src/api-client/beneficial-owners.ts +92 -0
- package/src/api-client/data.ts +44 -11
- package/src/api-client/errors.ts +12 -0
- package/src/api-client/geo.ts +363 -0
- package/src/api-client/index.ts +5 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/members.ts +26 -0
- package/src/api-client/paths.ts +30 -1
- package/src/api-client/perps.ts +41 -0
- package/src/api-client/request.ts +5 -2
- package/src/api-client/types.ts +9 -1
- package/src/app/global-shortcuts.ts +14 -6
- package/src/app/keybindings/actions.ts +1 -1
- package/src/app/runtime/update.ts +38 -2
- package/src/app.tsx +10 -6
- package/src/brokers/account-cache.ts +24 -0
- package/src/cli/commands/command-utils.ts +8 -0
- package/src/cli/commands/crypto-hints.ts +75 -0
- package/src/cli/commands/market.ts +414 -106
- package/src/cli/commands/overview.ts +77 -32
- package/src/cli/commands/ticker.ts +101 -43
- package/src/cli/desktop-pane-shot.ts +34 -4
- package/src/cli/freshness.ts +61 -0
- package/src/cli/help.ts +1 -0
- package/src/cli/helpers.ts +4 -8
- package/src/cli/index.ts +12 -2
- package/src/cli/listing-arg.ts +321 -0
- package/src/cli/pane-functions/access-gate.ts +42 -0
- package/src/cli/pane-functions/catalog.ts +28 -1
- package/src/cli/pane-functions/data.ts +3 -1
- package/src/cli/pane-functions/dom.ts +20 -2
- package/src/cli/pane-functions/freshness.ts +400 -0
- package/src/cli/pane-functions/function-help.ts +36 -22
- package/src/cli/pane-functions/headless.ts +39 -11
- package/src/cli/pane-functions/index.ts +15 -4
- package/src/cli/pane-functions/options.ts +3 -0
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +35 -7
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +7 -3
- package/src/cli/result.ts +41 -27
- package/src/cli/ticker-resolution.ts +7 -5
- package/src/components/chart/composite/composite-chart.tsx +88 -2536
- package/src/components/chart/composite/drawing-store.ts +59 -0
- package/src/components/chart/composite/legend.tsx +319 -0
- package/src/components/chart/composite/panel-bitmap.ts +139 -0
- package/src/components/chart/composite/panel-gestures.ts +59 -0
- package/src/components/chart/composite/panel-labels.ts +107 -0
- package/src/components/chart/composite/panel-surface.tsx +876 -0
- package/src/components/chart/composite/tool-catalog.ts +98 -0
- package/src/components/chart/composite/toolbar.tsx +186 -0
- package/src/components/chart/composite/tools.ts +8 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
- package/src/components/chart/composite/use-composite-viewport.ts +143 -0
- package/src/components/command-bar/commands/collection.ts +5 -1
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/list/view.tsx +16 -3
- package/src/components/command-bar/pane-templates/workflow.ts +1 -0
- package/src/components/command-bar/routes/root/command-items.ts +9 -2
- package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
- package/src/components/command-bar/routes/root/provider-search.ts +19 -5
- package/src/components/command-bar/routes/root/runtime.ts +14 -5
- package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
- package/src/components/command-bar/routing/actions.ts +2 -0
- package/src/components/command-bar/selection-runtime.ts +38 -0
- package/src/components/command-bar/surface/index.tsx +7 -2
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/command-bar/workflow/tickers.ts +6 -0
- package/src/components/data-table/view.tsx +29 -10
- package/src/components/feedback-dialog.tsx +2 -1
- package/src/components/form-modal/field-row.tsx +2 -2
- package/src/components/index.ts +2 -2
- package/src/components/layout/detached-pane-shell.tsx +6 -3
- package/src/components/layout/floating-pane.tsx +6 -0
- package/src/components/layout/header.tsx +24 -7
- package/src/components/layout/missing-pane.tsx +2 -2
- package/src/components/layout/pane/content.tsx +23 -10
- package/src/components/layout/pane/error-boundary.tsx +103 -0
- package/src/components/layout/pane/footer/registration.tsx +68 -23
- package/src/components/layout/pane/header.tsx +61 -28
- package/src/components/layout/pane/index.tsx +6 -0
- package/src/components/layout/pane/title.ts +31 -7
- package/src/components/layout/shell/drag/index.ts +6 -5
- package/src/components/layout/shell/drag/runtime.ts +3 -0
- package/src/components/layout/shell/index.tsx +6 -0
- package/src/components/layout/shell/menu.ts +2 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +29 -10
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
- package/src/components/layout/shell/ticker-link-menu.ts +23 -9
- package/src/components/metric-treemap/heat-surface.tsx +745 -0
- package/src/components/metric-treemap/heat-tiles.ts +228 -0
- package/src/components/metric-treemap/index.tsx +2 -0
- package/src/components/metric-treemap/layout.ts +8 -0
- package/src/components/metric-treemap/scene.ts +432 -0
- package/src/components/onboarding/onboarding-keyboard.ts +311 -0
- package/src/components/onboarding/onboarding-wizard.tsx +101 -788
- package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
- package/src/components/onboarding/use-onboarding-progress.ts +298 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
- package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
- package/src/components/ticker/list-table-view.tsx +13 -1
- package/src/components/toggle-list.tsx +38 -12
- package/src/components/ui/country-flag-art.tsx +449 -0
- package/src/components/ui/country-flag.tsx +41 -0
- package/src/components/ui/data-table/types.ts +13 -0
- package/src/components/ui/fields.tsx +8 -0
- package/src/components/ui/icon.tsx +2 -0
- package/src/components/ui/index.ts +4 -0
- package/src/components/ui/multi-select/dialog.tsx +108 -22
- package/src/components/ui/range-track.tsx +53 -0
- package/src/components/ui/status.tsx +8 -2
- package/src/components/ui/tabs.tsx +11 -4
- package/src/components/ui/text-prompt-dialog.tsx +4 -2
- package/src/core/state/app/types.ts +10 -1
- package/src/data/config/store/normalize.ts +17 -1
- package/src/i18n/es.ts +24 -4
- package/src/i18n/ja.ts +24 -4
- package/src/i18n/ko.ts +24 -4
- package/src/i18n/zh-cn.ts +24 -4
- package/src/i18n/zh-tw.ts +24 -4
- package/src/layout/ticker-navigation.ts +28 -2
- package/src/market-data/coordinator/chart.ts +1 -1
- package/src/market-data/coordinator/quotes.ts +23 -4
- package/src/market-data/fx-closes.ts +72 -0
- package/src/market-data/market/freshness.ts +78 -6
- package/src/market-data/market/session-close-note.ts +57 -0
- package/src/market-data/market/status.ts +151 -0
- package/src/market-data/published-apac-sessions.ts +66 -0
- package/src/market-data/quotes/contributions.ts +8 -0
- package/src/market-data/quotes/freshness.ts +36 -0
- package/src/market-data/quotes/resolution.ts +24 -8
- package/src/market-data/quotes/screener-live-quotes.ts +49 -4
- package/src/market-data/quotes/time.ts +16 -0
- package/src/plugins/builtin/account-management/pane.tsx +4 -0
- package/src/plugins/builtin/alerts/cli.ts +10 -5
- package/src/plugins/builtin/alerts/index.tsx +1 -0
- package/src/plugins/builtin/analytics/metrics.ts +8 -3
- package/src/plugins/builtin/analytics/risk-client.ts +330 -79
- package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
- package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
- package/src/plugins/builtin/analytics/risk-math.ts +33 -13
- package/src/plugins/builtin/analytics/risk-model.ts +282 -64
- package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
- package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
- package/src/plugins/builtin/attention/headless.ts +6 -4
- package/src/plugins/builtin/attention/index.tsx +1 -1
- package/src/plugins/builtin/attention/pane.tsx +1 -1
- package/src/plugins/builtin/awards/headless.ts +1 -0
- package/src/plugins/builtin/awards/pane.tsx +1 -1
- package/src/plugins/builtin/backtest/headless.ts +1 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
- package/src/plugins/builtin/buildout/cells.tsx +15 -12
- package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
- package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
- package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
- package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
- package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
- package/src/plugins/builtin/buildout/index.tsx +1 -0
- package/src/plugins/builtin/catalysts/headless.ts +3 -1
- package/src/plugins/builtin/catalysts/index.tsx +2 -2
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/headless.ts +2 -0
- package/src/plugins/builtin/cds/index.tsx +1 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
- package/src/plugins/builtin/chart-composer/headless.ts +38 -3
- package/src/plugins/builtin/chart-composer/index.tsx +3 -1
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
- package/src/plugins/builtin/chat/channels.ts +19 -3
- package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
- package/src/plugins/builtin/chat/content/footer.tsx +15 -6
- package/src/plugins/builtin/chat/content/index.tsx +120 -21
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
- package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
- package/src/plugins/builtin/chat/direct-messages.ts +200 -0
- package/src/plugins/builtin/chat/index.tsx +1 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
- package/src/plugins/builtin/chat/message/header.tsx +16 -6
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
- package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
- package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
- package/src/plugins/builtin/chat/message/types.ts +14 -2
- package/src/plugins/builtin/chat/profile-popover.ts +18 -13
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/cloud/team/module.tsx +1 -0
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/comment-letters/index.tsx +1 -0
- package/src/plugins/builtin/company-attention/headless.ts +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +1 -1
- package/src/plugins/builtin/company-kpis/headless.ts +1 -0
- package/src/plugins/builtin/congress-trades/headless.ts +1 -0
- package/src/plugins/builtin/correlation/compute.ts +59 -16
- package/src/plugins/builtin/correlation/geo.ts +34 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
- package/src/plugins/builtin/correlation/headless.ts +42 -8
- package/src/plugins/builtin/correlation/history.ts +2 -0
- package/src/plugins/builtin/correlation/index.tsx +68 -14
- package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
- package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
- package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
- package/src/plugins/builtin/correlation/settings.ts +1 -1
- package/src/plugins/builtin/cot/headless.ts +6 -3
- package/src/plugins/builtin/cot/index.tsx +3 -3
- package/src/plugins/builtin/cot/model.ts +36 -3
- package/src/plugins/builtin/cpi/headless.ts +4 -0
- package/src/plugins/builtin/credit-boards/headless.ts +2 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
- package/src/plugins/builtin/credit-documents/headless.ts +2 -0
- package/src/plugins/builtin/crypto-board/headless.ts +3 -0
- package/src/plugins/builtin/custom-view/loader.ts +1 -1
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
- package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
- package/src/plugins/builtin/dividend-yield/client.ts +4 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
- package/src/plugins/builtin/doe/headless.ts +7 -0
- package/src/plugins/builtin/doe/range-bar.tsx +7 -37
- package/src/plugins/builtin/earnings/client.ts +2 -2
- package/src/plugins/builtin/earnings/headless.ts +5 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
- package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
- package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
- package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
- package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
- package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
- package/src/plugins/builtin/econ/index.tsx +1 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
- package/src/plugins/builtin/equity-screener/headless.ts +2 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
- package/src/plugins/builtin/executives/index.tsx +1 -0
- package/src/plugins/builtin/exposure/headless.ts +3 -1
- package/src/plugins/builtin/fear-greed/headless.ts +2 -0
- package/src/plugins/builtin/filing-events/index.tsx +1 -0
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +39 -10
- package/src/plugins/builtin/futures-curve/index.tsx +3 -3
- package/src/plugins/builtin/futures-curve/model.ts +188 -4
- package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
- package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
- package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
- package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
- package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
- package/src/plugins/builtin/gpu/headless.ts +7 -3
- package/src/plugins/builtin/gpu/ladder.tsx +26 -30
- package/src/plugins/builtin/gpu/model.ts +27 -1
- package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
- package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
- package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
- package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
- package/src/plugins/builtin/holders/headless.ts +139 -5
- package/src/plugins/builtin/holders/index.tsx +10 -3
- package/src/plugins/builtin/holders/pane.tsx +20 -3
- package/src/plugins/builtin/holders/report-header.ts +95 -0
- package/src/plugins/builtin/holders/table-model.ts +20 -5
- package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
- package/src/plugins/builtin/holders/types.ts +2 -1
- package/src/plugins/builtin/insider/headless.ts +2 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -1
- package/src/plugins/builtin/jobs/headless.ts +1 -0
- package/src/plugins/builtin/jobs/index.tsx +1 -0
- package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +2 -0
- package/src/plugins/builtin/market-halts/index.tsx +1 -0
- package/src/plugins/builtin/market-heatmap/data.ts +30 -2
- package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
- package/src/plugins/builtin/market-heatmap/live.ts +88 -0
- package/src/plugins/builtin/market-heatmap/model.ts +146 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
- package/src/plugins/builtin/market-movers/client.ts +3 -2
- package/src/plugins/builtin/market-movers/headless.ts +10 -1
- package/src/plugins/builtin/market-movers/screener.ts +18 -5
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/members/client.ts +53 -0
- package/src/plugins/builtin/members/headless.ts +35 -0
- package/src/plugins/builtin/members/index.tsx +20 -0
- package/src/plugins/builtin/members/model.ts +68 -0
- package/src/plugins/builtin/members/pane.tsx +175 -0
- package/src/plugins/builtin/mna/distress/headless.ts +1 -0
- package/src/plugins/builtin/mna/headless.ts +1 -0
- package/src/plugins/builtin/mna/index.tsx +2 -1
- package/src/plugins/builtin/mna/pane.tsx +5 -2
- package/src/plugins/builtin/money-markets/headless.ts +1 -0
- package/src/plugins/builtin/news/headless.ts +2 -0
- package/src/plugins/builtin/news/wire/index.tsx +8 -1
- package/src/plugins/builtin/notes/cli.ts +14 -6
- package/src/plugins/builtin/notes/index.tsx +1 -0
- package/src/plugins/builtin/openfda/index.tsx +1 -0
- package/src/plugins/builtin/options/index.tsx +2 -1
- package/src/plugins/builtin/options-calculator/headless.ts +2 -0
- package/src/plugins/builtin/options-positioning/headless.ts +3 -0
- package/src/plugins/builtin/options-scenario/headless.ts +5 -0
- package/src/plugins/builtin/pe-band/client.ts +74 -2
- package/src/plugins/builtin/pe-band/headless.ts +26 -8
- package/src/plugins/builtin/pe-band/model.ts +102 -22
- package/src/plugins/builtin/pe-band/pane.tsx +34 -16
- package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
- package/src/plugins/builtin/perps/client.ts +66 -5
- package/src/plugins/builtin/perps/equity-identity.ts +16 -0
- package/src/plugins/builtin/perps/equity-row.tsx +6 -4
- package/src/plugins/builtin/perps/headless.ts +144 -16
- package/src/plugins/builtin/perps/index.tsx +6 -6
- package/src/plugins/builtin/perps/model.ts +189 -12
- package/src/plugins/builtin/perps/pane.tsx +248 -47
- package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
- package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
- package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
- package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
- package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
- package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
- package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
- package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
- package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
- package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
- package/src/plugins/builtin/power/headless.ts +3 -1
- package/src/plugins/builtin/rate-path/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
- package/src/plugins/builtin/research/analyst-headless.ts +3 -0
- package/src/plugins/builtin/research/events-headless.ts +2 -0
- package/src/plugins/builtin/research/headless.ts +2 -0
- package/src/plugins/builtin/research/index.tsx +1 -0
- package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
- package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
- package/src/plugins/builtin/research-search/index.tsx +1 -0
- package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
- package/src/plugins/builtin/risk-factors/headless.ts +2 -0
- package/src/plugins/builtin/scanner/flow-history.ts +2 -13
- package/src/plugins/builtin/scanner/headless.ts +14 -0
- package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
- package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
- package/src/plugins/builtin/seasonality/headless.ts +2 -0
- package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
- package/src/plugins/builtin/sec/filing-content.ts +42 -17
- package/src/plugins/builtin/sec/forms.ts +12 -0
- package/src/plugins/builtin/sec/headless.ts +2 -0
- package/src/plugins/builtin/sectors/client.ts +1 -0
- package/src/plugins/builtin/sectors/headless.ts +9 -1
- package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
- package/src/plugins/builtin/sectors/sector-model.ts +2 -0
- package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
- package/src/plugins/builtin/shared/report-freshness.ts +57 -0
- package/src/plugins/builtin/shared/research-cells.tsx +4 -9
- package/src/plugins/builtin/short-interest/headless.ts +2 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/social-mentions/headless.ts +1 -0
- package/src/plugins/builtin/social-mentions/index.tsx +1 -1
- package/src/plugins/builtin/supply-chain/headless.ts +7 -1
- package/src/plugins/builtin/supply-chain/index.tsx +10 -1
- package/src/plugins/builtin/supply-chain/model.ts +27 -2
- package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
- package/src/plugins/builtin/themes/headless.ts +3 -0
- package/src/plugins/builtin/thirteenf/headless.ts +110 -25
- package/src/plugins/builtin/thirteenf/index.tsx +2 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
- package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
- package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
- package/src/plugins/builtin/time-sales/headless.ts +5 -1
- package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/volatility/headless.ts +6 -1
- package/src/plugins/builtin/world-indices/headless.ts +2 -0
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
- package/src/plugins/builtin/world-venue-map/client.ts +45 -0
- package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
- package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
- package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
- package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
- package/src/plugins/builtin/world-venue-map/index.ts +67 -4
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
- package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
- package/src/plugins/builtin/world-venue-map/model.ts +7 -4
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
- package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
- package/src/plugins/builtin/yield-curve/headless.ts +16 -0
- package/src/plugins/catalog-browser.ts +2 -0
- package/src/plugins/catalog-manifest.ts +1 -0
- package/src/plugins/catalog-ui.ts +3 -1
- package/src/plugins/catalog.ts +1 -1
- package/src/plugins/host-link.ts +33 -1
- package/src/plugins/installer.ts +48 -12
- package/src/plugins/loader.ts +30 -3
- package/src/plugins/manager-node.ts +15 -5
- package/src/plugins/registry/index.ts +1 -1
- package/src/plugins/registry/pane-settings.ts +26 -7
- package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
- package/src/renderers/dom/build-assets.ts +81 -3
- package/src/renderers/dom/data-table/row.tsx +52 -16
- package/src/renderers/dom/desktop/controls.tsx +7 -3
- package/src/renderers/dom/desktop/icons.tsx +1 -0
- package/src/renderers/dom/desktop/list-view.tsx +35 -10
- package/src/renderers/dom/desktop/popover.tsx +73 -14
- package/src/renderers/dom/host/box.tsx +69 -26
- package/src/renderers/dom/host/input.tsx +14 -3
- package/src/renderers/dom/host/tabs.tsx +37 -1
- package/src/renderers/dom/styles.css +41 -7
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
- package/src/renderers/electrobun/bun/index.ts +73 -6
- package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
- package/src/renderers/electrobun/shared/protocol.ts +3 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
- package/src/renderers/electrobun/view/main.tsx +7 -1
- package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
- package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
- package/src/renderers/electrobun/view/update-host.ts +45 -21
- package/src/renderers/opentui/host.tsx +5 -0
- package/src/sources/gloomberb-cloud/index.ts +25 -4
- package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
- package/src/sources/provider-router/cached-routes.ts +4 -1
- package/src/sources/provider-router/financials.ts +11 -8
- package/src/sources/provider-router/history.ts +15 -10
- package/src/sources/sec-registrant.ts +75 -0
- package/src/state/app/context/index.tsx +24 -8
- package/src/state/hooks/quote-streaming.ts +41 -26
- package/src/sync/core-contributors.ts +4 -0
- package/src/telemetry/benign-window-error.ts +7 -0
- package/src/telemetry/crash-reports-dom.ts +2 -0
- package/src/telemetry/crash-reports.ts +4 -1
- package/src/test-support/cli-context.ts +10 -2
- package/src/test-support/daily-nav.ts +25 -0
- package/src/theme/heat-colors.ts +357 -2
- package/src/tickers/instrument-kind.ts +34 -7
- package/src/tickers/search/asset-classes.ts +24 -9
- package/src/tickers/search/index.ts +44 -11
- package/src/tickers/search/ranking.ts +23 -3
- package/src/time-series/resolve.ts +1 -1
- package/src/time-series/use-chart-resolution.ts +1 -1
- package/src/types/context-menu.ts +6 -0
- package/src/types/data-provider.ts +2 -0
- package/src/types/financials.ts +5 -0
- package/src/types/headless.ts +79 -2
- package/src/types/plugin.ts +38 -6
- package/src/types/ticker.ts +15 -0
- package/src/ui/context-menu.tsx +1 -0
- package/src/ui/host.tsx +16 -0
- package/src/updater/index.ts +38 -13
- package/src/utils/async-deadline.ts +21 -0
- package/src/utils/cli-output.ts +5 -1
- package/src/utils/crypto-pair.ts +41 -0
- package/src/utils/currency-units.ts +14 -0
- package/src/utils/depositary-receipt.ts +26 -0
- package/src/utils/exchanges.ts +72 -0
- package/src/utils/fire-and-forget.ts +24 -0
- package/src/utils/format.ts +13 -0
- package/src/utils/price-earnings.ts +70 -3
- package/src/utils/price-history.ts +58 -13
- package/src/utils/rpc-timeout-error.ts +27 -0
- package/src/utils/sec.ts +13 -1
- package/src/utils/utc-time.ts +79 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { getCloudApiBaseUrl } from "../../../api-client/request";
|
|
2
|
+
import { RpcTimeoutError } from "../../../utils/rpc-timeout-error";
|
|
2
3
|
import type { DesktopBackendRequestMethod } from "../shared/protocol";
|
|
3
4
|
|
|
5
|
+
/** How long the view waits for the Bun process to answer one request. */
|
|
6
|
+
export const RPC_MAX_REQUEST_TIME_MS = 120_000;
|
|
7
|
+
|
|
4
8
|
/** What Electrobun's request timer rejects with: no method, request id or caller. */
|
|
5
9
|
const ELECTROBUN_REQUEST_TIMEOUT_MESSAGE = "RPC request timed out.";
|
|
6
10
|
|
|
@@ -16,7 +20,9 @@ const ELECTROBUN_REQUEST_TIMEOUT_MESSAGE = "RPC request timed out.";
|
|
|
16
20
|
* from the user's config (a broker gateway, a feed, a plugin endpoint) and is
|
|
17
21
|
* only called external. Never the payload, rest of the path, query or headers.
|
|
18
22
|
* Elapsed time is rounded to 10 s because crash reports are deduplicated per
|
|
19
|
-
* session on the message.
|
|
23
|
+
* session on the message. The error keeps its message and `Error` type, and
|
|
24
|
+
* carries the elapsed time so a timer that fired long after its limit (the
|
|
25
|
+
* machine slept) can be told from a request that really stalled.
|
|
20
26
|
*/
|
|
21
27
|
export async function nameRpcTimeout<T>(
|
|
22
28
|
method: DesktopBackendRequestMethod,
|
|
@@ -28,8 +34,13 @@ export async function nameRpcTimeout<T>(
|
|
|
28
34
|
return await request();
|
|
29
35
|
} catch (error) {
|
|
30
36
|
if (!(error instanceof Error) || error.message !== ELECTROBUN_REQUEST_TIMEOUT_MESSAGE) throw error;
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
const elapsedMs = performance.now() - startedAt;
|
|
38
|
+
const seconds = Math.round(elapsedMs / 10_000) * 10;
|
|
39
|
+
throw new RpcTimeoutError(`RPC request timed out: ${describeRequest(method, payload)} after ~${seconds}s`, {
|
|
40
|
+
elapsedMs,
|
|
41
|
+
limitMs: RPC_MAX_REQUEST_TIME_MS,
|
|
42
|
+
cause: error,
|
|
43
|
+
});
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -6,33 +6,57 @@ import {
|
|
|
6
6
|
} from "../../../updater";
|
|
7
7
|
import { backendRequest, onBackendMessage } from "./backend-rpc";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Starts a backend update request and follows its `update.progress` messages
|
|
11
|
+
* until one ends the flow: `ready` ends a download, and `done` or `error` end
|
|
12
|
+
* either flow. A request that itself fails is reported as an error.
|
|
13
|
+
*/
|
|
14
|
+
function followUpdateFlow(
|
|
15
|
+
start: () => Promise<unknown>,
|
|
16
|
+
onProgress: (progress: UpdateProgress) => void,
|
|
17
|
+
endsFlow: (progress: UpdateProgress) => boolean,
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
let settled = false;
|
|
21
|
+
const unsubscribe = onBackendMessage("update.progress", ({ progress }) => {
|
|
22
|
+
onProgress(progress);
|
|
23
|
+
if (endsFlow(progress)) {
|
|
24
|
+
settled = true;
|
|
25
|
+
unsubscribe();
|
|
26
|
+
resolve();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
start().catch((error) => {
|
|
31
|
+
if (settled) return;
|
|
32
|
+
unsubscribe();
|
|
33
|
+
onProgress({
|
|
34
|
+
phase: "error",
|
|
35
|
+
error: error instanceof Error ? error.message : String(error),
|
|
36
|
+
});
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
9
42
|
export function installElectrobunUpdateHost(): void {
|
|
10
43
|
setUpdateHost({
|
|
11
44
|
checkForUpdateDetailed(currentVersion: string): Promise<UpdateCheckResult> {
|
|
12
45
|
return backendRequest("update.check", { currentVersion });
|
|
13
46
|
},
|
|
14
47
|
performUpdate(release: ReleaseInfo, onProgress: (progress: UpdateProgress) => void): Promise<void> {
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (settled) return;
|
|
28
|
-
unsubscribe();
|
|
29
|
-
onProgress({
|
|
30
|
-
phase: "error",
|
|
31
|
-
error: error instanceof Error ? error.message : String(error),
|
|
32
|
-
});
|
|
33
|
-
resolve();
|
|
34
|
-
});
|
|
35
|
-
});
|
|
48
|
+
return followUpdateFlow(
|
|
49
|
+
() => backendRequest("update.start", { release }),
|
|
50
|
+
onProgress,
|
|
51
|
+
(progress) => progress.phase === "ready" || progress.phase === "done" || progress.phase === "error",
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
applyUpdate(onProgress: (progress: UpdateProgress) => void): Promise<void> {
|
|
55
|
+
return followUpdateFlow(
|
|
56
|
+
() => backendRequest("update.apply"),
|
|
57
|
+
onProgress,
|
|
58
|
+
(progress) => progress.phase === "done" || progress.phase === "error",
|
|
59
|
+
);
|
|
36
60
|
},
|
|
37
61
|
});
|
|
38
62
|
}
|
|
@@ -108,6 +108,11 @@ export async function createOpenTuiHost(): Promise<OpenTuiHost> {
|
|
|
108
108
|
enableMouseMovement: true,
|
|
109
109
|
});
|
|
110
110
|
const root = createRoot(renderer);
|
|
111
|
+
// Every mounted ScrollBox listens for `selection` and many components for
|
|
112
|
+
// `resize`, so the counts grow with the panes on screen (14 and 18 with one
|
|
113
|
+
// DES open at 80x24) and fall back when they close. Node's default warning
|
|
114
|
+
// at 11 is not a leak here, and it prints over the panes.
|
|
115
|
+
renderer.setMaxListeners(0);
|
|
111
116
|
installResolutionEventBridge(renderer);
|
|
112
117
|
// Streamed quotes apply and notify on one clock of up to 10 Hz: every
|
|
113
118
|
// visible row stays live while a burst of ticks costs one terminal redraw,
|
|
@@ -40,6 +40,8 @@ import { resolveCurrencyUnit } from "../../utils/currency-units";
|
|
|
40
40
|
import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
|
|
41
41
|
import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
|
|
42
42
|
import { createProviderMiss } from "../provider-errors";
|
|
43
|
+
import { assertSecRegistrantMatches } from "../sec-registrant";
|
|
44
|
+
import { nonUsSecListingVenue } from "../../utils/sec";
|
|
43
45
|
import { publicListingTarget } from "../listing-target";
|
|
44
46
|
import { canonicalHistoryInterval, HistoryRetentionError, parseHistoryRecoveryCandidate, parseHistoryRetention, type HistoryRetention } from "../history-retention";
|
|
45
47
|
import { getRouterEntityKey } from "../provider-router/cache";
|
|
@@ -195,7 +197,7 @@ function mapCloudPriceHistory(
|
|
|
195
197
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
196
198
|
).map((point) => mapPricePoint(point, divisor, exchange)),
|
|
197
199
|
);
|
|
198
|
-
if (stale && !reachesLatestSettledSession(points, Date.now(), { exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
|
|
200
|
+
if (stale && !reachesLatestSettledSession(points, Date.now(), { symbol: ticker, exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
|
|
199
201
|
throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
|
|
200
202
|
}
|
|
201
203
|
if (
|
|
@@ -486,11 +488,30 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
486
488
|
);
|
|
487
489
|
}
|
|
488
490
|
|
|
489
|
-
async getSecFilings(ticker: string, count = 15): Promise<SecFilingItem[]> {
|
|
490
|
-
|
|
491
|
-
|
|
491
|
+
async getSecFilings(ticker: string, count = 15, exchange?: string, context?: MarketDataRequestContext): Promise<SecFilingItem[]> {
|
|
492
|
+
const venue = nonUsSecListingVenue(ticker, exchange);
|
|
493
|
+
const name = venue ? context?.listingName?.trim() || await this.listingName(ticker, venue) || undefined : undefined;
|
|
494
|
+
const filings = await withCloudFallback(async () => {
|
|
495
|
+
const response = await apiClient.getCloudSecFilings({ ticker, exchange, name, limit: count, offset: 0 });
|
|
492
496
|
return response.filings.map(mapCloudSecFiling);
|
|
493
497
|
}, `Cloud SEC filings are unavailable for ${ticker}`);
|
|
498
|
+
if (!venue || filings.length === 0) return filings;
|
|
499
|
+
const symbol = parsePublicTickerKey(ticker).symbol;
|
|
500
|
+
// Without the listing's company the filings cannot be told apart from another company's.
|
|
501
|
+
if (!name) throw createProviderMiss(`SEC filings for ${symbol} could not be matched to its ${venue} listing`);
|
|
502
|
+
assertSecRegistrantMatches(filings, { symbol, exchange: venue, name });
|
|
503
|
+
return filings;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** The company a listing belongs to, from its own quote; null when the quote prices another venue. */
|
|
507
|
+
private async listingName(ticker: string, venue: string): Promise<string | null> {
|
|
508
|
+
try {
|
|
509
|
+
const quote = await this.getQuote(ticker, venue);
|
|
510
|
+
const quoteVenue = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
|
|
511
|
+
return quoteVenue === venue ? quote.name?.trim() || null : null;
|
|
512
|
+
} catch {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
494
515
|
}
|
|
495
516
|
|
|
496
517
|
async getSecFilingDocuments(filing: SecFilingItem): Promise<SecFilingDocument[]> {
|
|
@@ -36,10 +36,31 @@ function cloudInternalProviderId(providerMeta?: CloudProviderMeta): string | nul
|
|
|
36
36
|
return upstream ? `${GLOOMBERB_CLOUD_PROVIDER_ID}:${upstream}` : null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* A previous close is a price from inside the trailing 52 weeks, so it cannot
|
|
41
|
+
* sit far below the 52-week low. The service has sent 0.000205 as the
|
|
42
|
+
* Shanghai Composite's close (52-week low 3,741), which drew a +1.86e9% move.
|
|
43
|
+
* Today's price plays no part, so a real gap of any size still shows; two
|
|
44
|
+
* orders of magnitude leave room for a 52-week low that lags a crash or a split.
|
|
45
|
+
*/
|
|
46
|
+
const IMPOSSIBLE_CLOSE_FACTOR = 100;
|
|
47
|
+
|
|
48
|
+
function isImpossiblePreviousClose(
|
|
49
|
+
previousClose: number | null | undefined,
|
|
50
|
+
low52w: number | null | undefined,
|
|
51
|
+
): boolean {
|
|
52
|
+
return typeof previousClose === "number" && Number.isFinite(previousClose) && previousClose > 0
|
|
53
|
+
&& typeof low52w === "number" && Number.isFinite(low52w) && low52w > 0
|
|
54
|
+
&& previousClose * IMPOSSIBLE_CLOSE_FACTOR < low52w;
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
export function mapQuote(
|
|
40
58
|
quote: CloudQuotePayload,
|
|
41
59
|
providerMeta?: CloudProviderMeta,
|
|
42
60
|
): Quote {
|
|
61
|
+
if (quote.priceBasis != null && !["nav", "per-unit", "percent-of-par", "points", "thirty-seconds"].includes(quote.priceBasis)) {
|
|
62
|
+
throw createProviderMiss("Unsupported quote price basis");
|
|
63
|
+
}
|
|
43
64
|
const { currency, divisor } = resolveCurrencyUnit(quote.currency);
|
|
44
65
|
const listingExchangeName = quote.listingExchangeName ?? quote.exchangeName;
|
|
45
66
|
const listingExchangeFullName =
|
|
@@ -47,24 +68,34 @@ export function mapQuote(
|
|
|
47
68
|
quote.fullExchangeName ??
|
|
48
69
|
listingExchangeName;
|
|
49
70
|
const internalProviderId = cloudInternalProviderId(providerMeta);
|
|
50
|
-
const
|
|
71
|
+
const impossibleClose = isImpossiblePreviousClose(quote.previousClose, quote.low52w);
|
|
72
|
+
const change = !impossibleClose && typeof quote.change === "number" && Number.isFinite(quote.change)
|
|
51
73
|
? quote.change / divisor
|
|
52
74
|
: Number.NaN;
|
|
53
|
-
const changePercent = typeof quote.changePercent === "number" && Number.isFinite(quote.changePercent)
|
|
75
|
+
const changePercent = !impossibleClose && typeof quote.changePercent === "number" && Number.isFinite(quote.changePercent)
|
|
54
76
|
? quote.changePercent
|
|
55
77
|
: Number.NaN;
|
|
56
78
|
// A generic future (VX1, TY1) reads in points or 32nds, not dollars.
|
|
57
79
|
const generic = futuresGenericListing(quote.symbol, quote.listingExchangeName ?? quote.exchangeName);
|
|
58
|
-
const
|
|
80
|
+
const priceObservation = quote.priceBasis === "nav" ? "nav" : undefined;
|
|
81
|
+
const priceBasis = quote.priceBasis === "nav"
|
|
82
|
+
? "per-unit"
|
|
83
|
+
: (generic ? futuresGenericPriceBasis(generic) : null) ?? quote.priceBasis;
|
|
59
84
|
return withoutUndefinedFields(reconcileQuoteDayRange({
|
|
60
85
|
...quote,
|
|
61
|
-
|
|
86
|
+
priceBasis,
|
|
87
|
+
priceObservation,
|
|
62
88
|
currency: currency || quote.currency,
|
|
63
89
|
price: normalizePriceValueByDivisor(quote.price, divisor) ?? quote.price,
|
|
64
90
|
change,
|
|
65
91
|
changePercent,
|
|
66
|
-
previousClose: normalizePriceValueByDivisor(quote.previousClose, divisor),
|
|
92
|
+
previousClose: impossibleClose ? undefined : normalizePriceValueByDivisor(quote.previousClose, divisor),
|
|
67
93
|
regularClose: normalizePriceValueByDivisor(quote.regularClose, divisor),
|
|
94
|
+
// JSON carries an unavailable move as null; the percent is unitless.
|
|
95
|
+
regularChange: normalizePriceValueByDivisor(quote.regularChange ?? undefined, divisor),
|
|
96
|
+
regularChangePercent: typeof quote.regularChangePercent === "number" && Number.isFinite(quote.regularChangePercent)
|
|
97
|
+
? quote.regularChangePercent
|
|
98
|
+
: undefined,
|
|
68
99
|
high52w: normalizePriceValueByDivisor(quote.high52w, divisor),
|
|
69
100
|
low52w: normalizePriceValueByDivisor(quote.low52w, divisor),
|
|
70
101
|
bid: normalizePriceValueByDivisor(quote.bid, divisor),
|
|
@@ -3,6 +3,7 @@ import { CachedQuery, type CachedValue } from "../../data/cached-query";
|
|
|
3
3
|
import type { CachedAssetArgs, CachedAssetMethod, CachedAssetValue, DataProvider, MarketDataRequestContext, SecFilingItem } from "../../types/data-provider";
|
|
4
4
|
import type { AnalystResearchData, CorporateActionsData, HolderData } from "../../types/financials";
|
|
5
5
|
import { canonicalExchange } from "../../utils/exchanges";
|
|
6
|
+
import { nonUsSecListingVenue } from "../../utils/sec";
|
|
6
7
|
import { shouldLogProviderError } from "../provider-errors";
|
|
7
8
|
import { hasBrokerContext, withBrokerTimeout } from "./brokers";
|
|
8
9
|
import { publicListingExchange } from "../listing-target";
|
|
@@ -220,9 +221,11 @@ export class ProviderRouterCachedRoutes {
|
|
|
220
221
|
};
|
|
221
222
|
case "getSecFilings": {
|
|
222
223
|
const [symbol, count = 15, listingExchange, requestContext] = args as CachedAssetArgs<"getSecFilings">;
|
|
224
|
+
const exact = buildVariantKey([["exchange", canonicalExchange(listingExchange)], ["count", count]]);
|
|
223
225
|
return {
|
|
224
226
|
kind: "sec-filings", policy: "secFilings", entityKey: this.deps.getEntityKey(symbol, requestContext?.instrument),
|
|
225
|
-
|
|
227
|
+
// A non-US listing never reads the bare symbol's filings: SAN in Paris is Sanofi, SAN at the SEC is Santander.
|
|
228
|
+
variants: nonUsSecListingVenue(symbol, listingExchange) ? [exact] : [...new Set([exact, buildVariantKey([["count", count]]), ""])],
|
|
226
229
|
request: (provider, force) => provider.getSecFilings?.(symbol, count, listingExchange, force ? { ...requestContext, cacheMode: "refresh" } : requestContext),
|
|
227
230
|
throwLastError: true, error: `No SEC filings provider available for ${symbol}`,
|
|
228
231
|
};
|
|
@@ -7,7 +7,7 @@ import { redactUnavailableFundamentals, RETRACTABLE_VALUATION_FIELDS } from "../
|
|
|
7
7
|
import { isExtendedHoursExchange, isQuoteStaleForCurrentSession } from "../../market-data/quotes/freshness";
|
|
8
8
|
import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../../market-data/quotes/metadata";
|
|
9
9
|
import { parsePublicTickerKey } from "../../utils/exchanges";
|
|
10
|
-
import { activeUsMarketSession, isUsPriorSessionPremarketQuote } from "../../market-data/market/freshness";
|
|
10
|
+
import { activeUsMarketSession, delayedFeedLagMs, isMiddayBreakPrint, isUsPriorSessionPremarketQuote } from "../../market-data/market/freshness";
|
|
11
11
|
import {
|
|
12
12
|
mergeQuoteContributionMaps,
|
|
13
13
|
isQuoteContributionStaleForCurrentSession,
|
|
@@ -92,7 +92,7 @@ export function sanitizeCachedFinancials(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export function quoteWithFreshnessExchange(quote: Quote, exchange?: string): Quote {
|
|
95
|
-
if (!exchange || quote.listingExchangeName || quote.exchangeName) return quote;
|
|
95
|
+
if (!exchange || quote.priceObservation != null || quote.listingExchangeName || quote.exchangeName) return quote;
|
|
96
96
|
return {
|
|
97
97
|
...quote,
|
|
98
98
|
listingExchangeName: exchange,
|
|
@@ -123,18 +123,21 @@ function isQuoteInActiveSession(quote: Quote, now: number): boolean {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function isActiveProviderQuoteTooOld(quote: Quote, now = Date.now()): boolean {
|
|
126
|
+
// The shared freshness predicate already validates and dates a daily NAV.
|
|
127
|
+
if (quote.priceObservation === "nav") return false;
|
|
126
128
|
if (!isQuoteInActiveSession(quote, now)) return false;
|
|
127
129
|
// A prior-session close before any pre-market trade has no in-session print
|
|
128
130
|
// to age; the session-date rules bound it instead.
|
|
129
|
-
|
|
131
|
+
const exchange = quote.listingExchangeName || quote.exchangeName;
|
|
132
|
+
if (isUsPriorSessionPremarketQuote(quote.lastUpdated, exchange, quote.marketState, now)) {
|
|
130
133
|
return false;
|
|
131
134
|
}
|
|
132
135
|
if (!Number.isFinite(quote.lastUpdated)) return false;
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return
|
|
136
|
+
const delayed = quote.dataSource === "delayed";
|
|
137
|
+
const maxAge = delayed ? ACTIVE_DELAYED_PROVIDER_QUOTE_MAX_AGE_MS : ACTIVE_PROVIDER_QUOTE_MAX_AGE_MS;
|
|
138
|
+
if (now - quote.lastUpdated <= maxAge) return false;
|
|
139
|
+
// While the venue pauses at midday, the morning's last print is the current price.
|
|
140
|
+
return !isMiddayBreakPrint(quote.lastUpdated, exchange, now, delayed ? delayedFeedLagMs(exchange) : 0);
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
export function providerQuoteMatchesTarget(quote: Quote | null | undefined, symbol?: string, exchange?: string): quote is Quote {
|
|
@@ -19,6 +19,7 @@ import { subtractTimeRange } from "../../time-series/date-window";
|
|
|
19
19
|
import { clipPriceHistoryToRange } from "../../time-series/history-window";
|
|
20
20
|
import { repairIsolatedIntradayOhlcOutliers } from "../../time-series/history-quality";
|
|
21
21
|
import { canonicalExchange, parsePublicTickerKey, resolveExchangeTimeZone } from "../../utils/exchanges";
|
|
22
|
+
import { isRoundTheClockCoin } from "../../utils/crypto-pair";
|
|
22
23
|
import { zonedDateKey } from "../../utils/zoned-date-time";
|
|
23
24
|
import { resolvePriceHistoryCurrencyUnit } from "../../utils/currency-units";
|
|
24
25
|
import { calendarHistoryFetchState, calendarHistoryLastBarDate, dropLeadingPlaceholderBars, getPricePointTimestamp, hasUsablePriceHistory, preservePriceHistoryGaps, isPriceHistoryStaleForCurrentWindow, normalizePriceHistory, priceHistoryIntervalMs, type CalendarHistoryFetchState } from "../../utils/price-history";
|
|
@@ -245,9 +246,9 @@ function normalizeRequestResult(value: PriceHistoryResult, request: HistoryReque
|
|
|
245
246
|
};
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
function resultIsStale(value: PriceHistoryResult, exchange: string, intervalMs?: number | null): boolean {
|
|
249
|
+
function resultIsStale(value: PriceHistoryResult, symbol: string, exchange: string, intervalMs?: number | null): boolean {
|
|
249
250
|
return isPriceHistoryStaleForCurrentWindow(value.points, Date.now(), {
|
|
250
|
-
exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
|
|
251
|
+
symbol, exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
|
|
251
252
|
});
|
|
252
253
|
}
|
|
253
254
|
|
|
@@ -342,8 +343,8 @@ export class ProviderRouterHistoryRoutes {
|
|
|
342
343
|
context,
|
|
343
344
|
cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
|
|
344
345
|
missingProviderError: `No history provider available for ${ticker}`,
|
|
345
|
-
isCachedValueStale: (value) => intraday && resultIsStale(value, exchange),
|
|
346
|
-
isFetchedValueStale: (value) => intraday && resultIsStale(value, exchange),
|
|
346
|
+
isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
|
|
347
|
+
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
|
|
347
348
|
fetchBroker: async (candidate) => candidate.broker.getPriceHistory
|
|
348
349
|
? candidate.broker.getPriceHistory(
|
|
349
350
|
ticker,
|
|
@@ -399,8 +400,8 @@ export class ProviderRouterHistoryRoutes {
|
|
|
399
400
|
context,
|
|
400
401
|
cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
|
|
401
402
|
missingProviderError: `No resolution-aware history provider available for ${ticker}`,
|
|
402
|
-
isCachedValueStale: (value) => intraday && resultIsStale(value, exchange, intervalMs),
|
|
403
|
-
isFetchedValueStale: (value) => intraday && resultIsStale(value, exchange, intervalMs),
|
|
403
|
+
isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
|
|
404
|
+
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
|
|
404
405
|
fetchBroker: async (candidate) => candidate.broker.getPriceHistoryForResolution
|
|
405
406
|
? candidate.broker.getPriceHistoryForResolution(
|
|
406
407
|
ticker,
|
|
@@ -515,9 +516,9 @@ export class ProviderRouterHistoryRoutes {
|
|
|
515
516
|
context,
|
|
516
517
|
cachePolicyKey: intervalMs != null && intervalMs >= 24 * 60 * 60 * 1000 ? "priceHistoryDaily" : "priceHistoryIntraday",
|
|
517
518
|
isCachedValueStale: (value) => currentWindowAtLookup
|
|
518
|
-
&& resultIsStale(value, exchange, intervalMs),
|
|
519
|
+
&& resultIsStale(value, ticker, exchange, intervalMs),
|
|
519
520
|
isFetchedValueStale: (value) => isCurrentHistoryWindow(endDate)
|
|
520
|
-
&& resultIsStale(value, exchange, intervalMs),
|
|
521
|
+
&& resultIsStale(value, ticker, exchange, intervalMs),
|
|
521
522
|
fetchBroker: async (candidate) => candidate.broker.getDetailedPriceHistory
|
|
522
523
|
? candidate.broker.getDetailedPriceHistory(
|
|
523
524
|
ticker,
|
|
@@ -568,6 +569,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
568
569
|
const now = Date.now();
|
|
569
570
|
const fetchState = (record: { fetchedAt: number; value: PriceHistoryResult }, checkedAt?: number): CalendarHistoryFetchState => currentWindow
|
|
570
571
|
? calendarHistoryFetchState(record.value.points, record.fetchedAt, now, {
|
|
572
|
+
symbol: target.symbol,
|
|
571
573
|
exchange: target.exchange || request.target.exchange,
|
|
572
574
|
checkedAt,
|
|
573
575
|
// Bar size comes from the request: a cached weekly or monthly series
|
|
@@ -580,7 +582,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
580
582
|
const ranked = cachedRecords.filter((record) => hasUsablePriceHistory(record.value.points)).map((record) => {
|
|
581
583
|
const tier = fetchTier[fetchState(record)];
|
|
582
584
|
return { record, tier, exact: request.exactCacheVariantKeys.includes(record.variantKey),
|
|
583
|
-
lastBar: tier === 0 ? "" : calendarHistoryLastBarDate(record.value.points, target.exchange || request.target.exchange) ?? "" };
|
|
585
|
+
lastBar: tier === 0 ? "" : calendarHistoryLastBarDate(record.value.points, target.exchange || request.target.exchange, target.symbol) ?? "" };
|
|
584
586
|
});
|
|
585
587
|
// Current copies keep their listed order. Otherwise ties go to this key's
|
|
586
588
|
// own copy, then the latest fetch, rather than to row order.
|
|
@@ -657,7 +659,10 @@ export class ProviderRouterHistoryRoutes {
|
|
|
657
659
|
throw new HistoryRetentionError(candidates[0]?.retention ?? attempts.retention, { candidates, outcomes: [...attempts.outcomes.values()] });
|
|
658
660
|
}
|
|
659
661
|
if (!providerResult && request.missingProviderError && !reportedGaps.some((value) => value.points.length > 0)) {
|
|
660
|
-
|
|
662
|
+
// A source answered a coin's history but it stops short of the present: say so, not that nothing answered.
|
|
663
|
+
const behind = isRoundTheClockCoin(target.symbol, target.exchange || request.target.exchange)
|
|
664
|
+
&& [...attempts.outcomes.values()].some((outcome) => outcome.outcome === "stale");
|
|
665
|
+
throw new Error(behind ? `Latest history for ${target.symbol} is behind` : request.missingProviderError);
|
|
661
666
|
}
|
|
662
667
|
return withReportedGaps(providerResult?.value ?? { points: [], resolution: historyResolutionForInterval(request.interval) });
|
|
663
668
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { SecFilingItem } from "../types/data-provider";
|
|
2
|
+
import { ProviderMissError } from "./provider-errors";
|
|
3
|
+
|
|
4
|
+
/** Legal forms and grouping words, which say nothing about which company a name is. */
|
|
5
|
+
const COMPANY_NAME_FILLER = new Set([
|
|
6
|
+
"THE", "SA", "PLC", "LTD", "LIMITED", "CORP", "CORPORATION", "INC", "INCORPORATED", "NV", "AG", "SE",
|
|
7
|
+
"HOLDING", "HOLDINGS", "GROUP", "COMPANY", "CO",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The first word of a company name that tells it apart, in capitals without
|
|
12
|
+
* accents: "BANCO" for "Banco Santander, S.A.", "BP" for "BP p.l.c.". Null
|
|
13
|
+
* when the name is only legal forms.
|
|
14
|
+
*/
|
|
15
|
+
function companyNameToken(name: string | null | undefined): string | null {
|
|
16
|
+
const words = (name ?? "")
|
|
17
|
+
.normalize("NFKD")
|
|
18
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
19
|
+
.toUpperCase()
|
|
20
|
+
// An initialism (S.A., N.V., p.l.c.) is one word; any other dot separates two (Amazon.com).
|
|
21
|
+
.replace(/\b(?:[A-Z]\.){2,}/g, (initialism) => initialism.replaceAll(".", ""))
|
|
22
|
+
.replace(/['\u2019]/g, "")
|
|
23
|
+
.split(/[^A-Z0-9]+/);
|
|
24
|
+
return words.find((word) => word && !COMPANY_NAME_FILLER.has(word)) ?? null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether two names are clearly different companies. Names that cannot be
|
|
29
|
+
* compared count as the same, so only a plain conflict hides anything.
|
|
30
|
+
*/
|
|
31
|
+
export function areDifferentCompanies(left: string | null | undefined, right: string | null | undefined): boolean {
|
|
32
|
+
const leftToken = companyNameToken(left);
|
|
33
|
+
const rightToken = companyNameToken(right);
|
|
34
|
+
return !!leftToken && !!rightToken && leftToken !== rightToken;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SecListing {
|
|
38
|
+
/** The bare symbol, such as SAN. */
|
|
39
|
+
symbol: string;
|
|
40
|
+
/** The listing's canonical venue, such as EPA. */
|
|
41
|
+
exchange: string;
|
|
42
|
+
/** The listing's company, from its own quote. */
|
|
43
|
+
name: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** "No SEC filings found for Sanofi (EPA). The SEC lists SAN as Banco Santander, S.A. (NYSE)." */
|
|
47
|
+
export function secRegistrantMismatchMessage(listing: SecListing, registrantName: string, registrantExchange?: string | null): string {
|
|
48
|
+
const registrant = registrantExchange ? `${registrantName} (${registrantExchange})` : registrantName;
|
|
49
|
+
return `No SEC filings found for ${listing.name} (${listing.exchange}). The SEC lists ${listing.symbol} as ${registrant}${registrant.endsWith(".") ? "" : "."}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The SEC knows the listing's symbol as another company: SAN on EPA is Sanofi, SAN at the SEC is Banco Santander. */
|
|
53
|
+
export class SecRegistrantMismatchError extends ProviderMissError {
|
|
54
|
+
constructor(readonly listing: SecListing, readonly registrantName: string) {
|
|
55
|
+
super(secRegistrantMismatchMessage(listing, registrantName));
|
|
56
|
+
this.name = "SecRegistrantMismatchError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The company the filings were filed by. */
|
|
61
|
+
function registrantName(filings: readonly SecFilingItem[]): string | null {
|
|
62
|
+
return filings.map((filing) => filing.companyName?.trim()).find((name): name is string => !!name) ?? null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A lookup for a listing outside the US can answer with the US registrant of
|
|
67
|
+
* the same symbol: a backend that predates venue-aware lookups ignores the
|
|
68
|
+
* venue. Those filings are another company's, so they are refused.
|
|
69
|
+
*/
|
|
70
|
+
export function assertSecRegistrantMatches(filings: readonly SecFilingItem[], listing: SecListing): void {
|
|
71
|
+
const registrant = registrantName(filings);
|
|
72
|
+
if (registrant && areDifferentCompanies(listing.name, registrant)) {
|
|
73
|
+
throw new SecRegistrantMismatchError(listing, registrant);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
import type { AppAction, AppState } from "../../../core/state/app/state";
|
|
45
45
|
import { scheduleConfigSave } from "../../config-save-scheduler";
|
|
46
46
|
import { LOW_PRIORITY_CONFIG_SAVE_DEBOUNCE_MS } from "../../persist-scheduler";
|
|
47
|
+
import { fireAndForget } from "../../../utils/fire-and-forget";
|
|
47
48
|
|
|
48
49
|
export {
|
|
49
50
|
appReducer,
|
|
@@ -565,10 +566,16 @@ export function AppProvider({
|
|
|
565
566
|
if (signature === lastMainSyncRef.current) return;
|
|
566
567
|
lastMainSyncRef.current = signature;
|
|
567
568
|
latestMainStateRevisionRef.current += 1;
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
569
|
+
// A snapshot that did not arrive must not count as sent. Forget it, unless
|
|
570
|
+
// a newer one has replaced it, so the next effect run sends it again. The
|
|
571
|
+
// two syncs below dedupe the same way.
|
|
572
|
+
fireAndForget(
|
|
573
|
+
desktopBridge.syncMainState({ ...snapshot, mainStateRevision: latestMainStateRevisionRef.current }),
|
|
574
|
+
"desktop.syncMainState",
|
|
575
|
+
() => {
|
|
576
|
+
if (lastMainSyncRef.current === signature) lastMainSyncRef.current = null;
|
|
577
|
+
},
|
|
578
|
+
);
|
|
572
579
|
}, [
|
|
573
580
|
buildDesktopSnapshot,
|
|
574
581
|
desktopBridge,
|
|
@@ -581,9 +588,12 @@ export function AppProvider({
|
|
|
581
588
|
|
|
582
589
|
useEffect(() => {
|
|
583
590
|
if (desktopBridge?.kind !== "main" || !desktopBridge.syncThemePreview) return;
|
|
584
|
-
|
|
585
|
-
lastThemePreviewSyncRef.current
|
|
586
|
-
|
|
591
|
+
const theme = state.themePreview;
|
|
592
|
+
if (lastThemePreviewSyncRef.current === theme) return;
|
|
593
|
+
lastThemePreviewSyncRef.current = theme;
|
|
594
|
+
fireAndForget(desktopBridge.syncThemePreview({ theme }), "desktop.setThemePreview", () => {
|
|
595
|
+
if (lastThemePreviewSyncRef.current === theme) lastThemePreviewSyncRef.current = undefined;
|
|
596
|
+
});
|
|
587
597
|
}, [desktopBridge, state.themePreview]);
|
|
588
598
|
|
|
589
599
|
useEffect(() => {
|
|
@@ -598,7 +608,13 @@ export function AppProvider({
|
|
|
598
608
|
const paneSignature = serializePaneState(state.paneState[desktopBridge.paneId]);
|
|
599
609
|
if (paneSignature === lastDetachedPaneSyncRef.current) return;
|
|
600
610
|
lastDetachedPaneSyncRef.current = paneSignature;
|
|
601
|
-
|
|
611
|
+
fireAndForget(
|
|
612
|
+
desktopBridge.replaceDetachedPaneState(desktopBridge.paneId, state.paneState[desktopBridge.paneId] ?? {}),
|
|
613
|
+
"desktop.replaceDetachedPaneState",
|
|
614
|
+
() => {
|
|
615
|
+
if (lastDetachedPaneSyncRef.current === paneSignature) lastDetachedPaneSyncRef.current = null;
|
|
616
|
+
},
|
|
617
|
+
);
|
|
602
618
|
}, [desktopBridge, serializePaneState, state.paneState]);
|
|
603
619
|
|
|
604
620
|
useLayoutEffect(() => {
|
|
@@ -106,21 +106,28 @@ export function useQuoteStreaming(
|
|
|
106
106
|
const paneInView = usePaneInView();
|
|
107
107
|
const coordinator = getSharedMarketDataCoordinator();
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
.sort()
|
|
122
|
-
.
|
|
123
|
-
|
|
109
|
+
// Keyed on the array, not its contents: callers memoize their targets, and a
|
|
110
|
+
// portfolio re-renders on every data frame, where re-keying and sorting a
|
|
111
|
+
// thousand targets cost a few milliseconds each time.
|
|
112
|
+
const { coordinatorTargets, identityKey, subscriptionKey } = useMemo(() => {
|
|
113
|
+
const normalizedEntries = new Map<string, QuoteSubscriptionTarget>();
|
|
114
|
+
for (const target of targets) {
|
|
115
|
+
const resolved = normalizeQuoteStreamSubscriptionTarget(target);
|
|
116
|
+
if (!resolved) continue;
|
|
117
|
+
const normalized = paneInView ? resolved : downgradeOffscreenQuoteTarget(resolved);
|
|
118
|
+
const key = buildQuoteStreamSubscriptionKey(normalized);
|
|
119
|
+
normalizedEntries.set(key, normalized);
|
|
120
|
+
}
|
|
121
|
+
const sortedEntries = [...normalizedEntries.entries()].sort(([left], [right]) => left.localeCompare(right));
|
|
122
|
+
const normalizedTargets = sortedEntries.map(([, target]) => target);
|
|
123
|
+
return {
|
|
124
|
+
coordinatorTargets: toCoordinatorQuoteTargets(normalizedTargets),
|
|
125
|
+
identityKey: [...new Set(normalizedTargets.map(buildQuoteStreamSubscriptionIdentityKey))]
|
|
126
|
+
.sort()
|
|
127
|
+
.join("\u001f"),
|
|
128
|
+
subscriptionKey: sortedEntries.map(([key]) => key).join("|"),
|
|
129
|
+
};
|
|
130
|
+
}, [paneInView, targets]);
|
|
124
131
|
const latestSubscriptionRef = useRef({
|
|
125
132
|
identityKey,
|
|
126
133
|
subscriptionKey,
|
|
@@ -208,11 +215,16 @@ export function useQuoteUpdates(
|
|
|
208
215
|
): void {
|
|
209
216
|
const appActive = useAppVisible();
|
|
210
217
|
const coordinator = getSharedMarketDataCoordinator();
|
|
211
|
-
const normalizedTargets =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
const { instrumentKey, normalizedTargets } = useMemo(() => {
|
|
219
|
+
const normalized = normalizeQuoteStreamSubscriptionTargets(targets);
|
|
220
|
+
return {
|
|
221
|
+
instrumentKey: normalized
|
|
222
|
+
.map((target) => buildQuoteKey(instrumentFromQuoteTarget(target)))
|
|
223
|
+
.sort()
|
|
224
|
+
.join("\u001f"),
|
|
225
|
+
normalizedTargets: normalized,
|
|
226
|
+
};
|
|
227
|
+
}, [targets]);
|
|
216
228
|
const instruments = useMemo(() => uniqueQuoteInstruments(normalizedTargets), [instrumentKey]);
|
|
217
229
|
|
|
218
230
|
useQuoteStreaming(targets, { enabled: liveStreaming });
|
|
@@ -270,12 +282,15 @@ export function useLiveQuoteEntries(
|
|
|
270
282
|
subscriptionStartedAt: number;
|
|
271
283
|
} {
|
|
272
284
|
const appActive = useAppVisible();
|
|
273
|
-
const normalizedTargets =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
285
|
+
const { normalizedTargets, targetKey } = useMemo(() => {
|
|
286
|
+
const normalized = normalizeQuoteStreamSubscriptionTargets(targets);
|
|
287
|
+
return {
|
|
288
|
+
normalizedTargets: normalized,
|
|
289
|
+
targetKey: [...new Set(normalized.map((target) => buildQuoteStreamSubscriptionIdentityKey(target)))]
|
|
290
|
+
.sort()
|
|
291
|
+
.join("\u001f"),
|
|
292
|
+
};
|
|
293
|
+
}, [targets]);
|
|
279
294
|
const liveStreaming = options.liveStreaming !== false;
|
|
280
295
|
const freshnessKey = options.freshnessScopeKey ?? targetKey;
|
|
281
296
|
const subscriptionKey = `${appActive ? "active" : "inactive"}\u001f${liveStreaming ? "live" : "poll"}\u001f${freshnessKey}`;
|
|
@@ -102,6 +102,10 @@ function sanitizePortfolio(portfolio: Portfolio): Portfolio {
|
|
|
102
102
|
currency: portfolio.currency,
|
|
103
103
|
brokerId: portfolio.brokerId,
|
|
104
104
|
lastSyncedAt: portfolio.lastSyncedAt,
|
|
105
|
+
// Cash and targets are the user's own entries: a pulled portfolio without
|
|
106
|
+
// them would erase them on this device.
|
|
107
|
+
...(portfolio.cash ? { cash: { ...portfolio.cash } } : {}),
|
|
108
|
+
...(portfolio.targetWeights ? { targetWeights: { ...portfolio.targetWeights } } : {}),
|
|
105
109
|
};
|
|
106
110
|
}
|
|
107
111
|
|