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
|
@@ -9,24 +9,53 @@ import type {
|
|
|
9
9
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
10
10
|
import { loadCloudResource } from "../shared/cloud-resource";
|
|
11
11
|
import { canonicalExchange, normalizeSymbol } from "../../../utils/exchanges";
|
|
12
|
+
import { resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
12
13
|
import type { PricePoint } from "../../../types/financials";
|
|
13
14
|
import { resolveDatedReturns, type DatedReturn } from "./metrics";
|
|
14
15
|
import { qualifySharpeCadence } from "./sharpe-cadence";
|
|
15
16
|
import { evidenceDay } from "./risk-evidence";
|
|
17
|
+
import { FX_LIVE_RATE_MAX_DEVIATION } from "../../../market-data/coordinator/fx-legs";
|
|
18
|
+
import {
|
|
19
|
+
convertClosesToUsd,
|
|
20
|
+
FX_DISAGREES,
|
|
21
|
+
FX_NO_PAIR,
|
|
22
|
+
FX_UNAVAILABLE,
|
|
23
|
+
riskConversion,
|
|
24
|
+
validateFxHistory,
|
|
25
|
+
type FxCloses,
|
|
26
|
+
type RiskConversion,
|
|
27
|
+
} from "./risk-fx";
|
|
16
28
|
|
|
17
29
|
export interface RiskInstrument {
|
|
18
30
|
symbol: string;
|
|
19
31
|
exchange: string;
|
|
20
32
|
}
|
|
33
|
+
/** A holding to load: what ranks it by value before any daily history is requested. */
|
|
34
|
+
export interface RiskHoldingRequest extends RiskInstrument {
|
|
35
|
+
/** Signed quantity; with a USD quote it ranks the holding by current value. */
|
|
36
|
+
quantity?: number;
|
|
37
|
+
/** The currency the position is recorded in, for the rate that values it when it is left out. */
|
|
38
|
+
currency?: string | null;
|
|
39
|
+
/** USD value from the broker's own snapshot, ranking a holding no USD quote values. */
|
|
40
|
+
snapshotValue?: number | null;
|
|
41
|
+
/** Outside the basket model whatever its history says (a short, a derivative): quoted, never history-requested. */
|
|
42
|
+
unsupported?: boolean;
|
|
43
|
+
}
|
|
21
44
|
interface RiskMarketHistory {
|
|
22
45
|
instrument: RiskInstrument;
|
|
23
46
|
currency: string | null;
|
|
24
47
|
quote: CloudQuotePayload | null;
|
|
25
|
-
points: CloudPricePointPayload[];
|
|
26
48
|
returns: DatedReturn[];
|
|
27
49
|
asOf: string | null;
|
|
28
50
|
/** Latest completed close, used as the mark when no current quote arrived. */
|
|
29
51
|
closeMark?: { price: number; date: string; currency: string } | null;
|
|
52
|
+
/** The listing's own price when it does not quote in USD: it values a left-out holding, never a return. */
|
|
53
|
+
listing?: { price: number; currency: string } | null;
|
|
54
|
+
/**
|
|
55
|
+
* Set when the listing quotes in another currency and its closes were
|
|
56
|
+
* restated in USD at same-date daily FX closes; quote and closeMark are then in USD.
|
|
57
|
+
*/
|
|
58
|
+
converted?: { currency: string; fxAsOf: string } | null;
|
|
30
59
|
error: string | null;
|
|
31
60
|
}
|
|
32
61
|
export interface RiskMarketSnapshot {
|
|
@@ -36,7 +65,22 @@ export interface RiskMarketSnapshot {
|
|
|
36
65
|
fetchedAt: string;
|
|
37
66
|
warnings: string[];
|
|
38
67
|
brokerOptions?: import("./risk-options").PortfolioOptionBook;
|
|
68
|
+
/** USD per unit of each listing or position currency of the holdings, at load time. */
|
|
69
|
+
fxRates?: Record<string, number>;
|
|
39
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The most holdings one load requests daily history for, largest current USD
|
|
73
|
+
* value first. Each holding costs one Cloud request of up to 18 months of
|
|
74
|
+
* closes, four in flight, again after every two-minute cache expiry, and the
|
|
75
|
+
* correlation view pairs every basket holding with every other. See
|
|
76
|
+
* docs/research-data.md for the measured cost behind the number.
|
|
77
|
+
*/
|
|
78
|
+
export const RISK_HISTORY_LIMIT = 150;
|
|
79
|
+
/** Holdings quoted in one load, 50 to a batch request. */
|
|
80
|
+
const RISK_QUOTE_LIMIT = 500;
|
|
81
|
+
const HISTORY_CONCURRENCY = 4;
|
|
82
|
+
const MAX_FX_CURRENCIES = 24;
|
|
83
|
+
export const BEYOND_SIZE_REASON = `Beyond the supported size: only the ${RISK_HISTORY_LIMIT} largest holdings by value are modelled`;
|
|
40
84
|
// Listing venues as the quotes report them; MTUM lists on Cboe BZX, not Arca.
|
|
41
85
|
export const RISK_FACTOR_INSTRUMENTS: RiskInstrument[] = [
|
|
42
86
|
["SPY", "ARCA"],
|
|
@@ -56,22 +100,23 @@ const message = (error: unknown) =>
|
|
|
56
100
|
export const portfolioRiskCache = createPluginCache<RiskMarketSnapshot>({
|
|
57
101
|
kind: "portfolio-risk",
|
|
58
102
|
source: "gloom-cloud",
|
|
59
|
-
schemaVersion:
|
|
103
|
+
schemaVersion: 4,
|
|
60
104
|
policy: { staleMs: 2 * 60_000, expireMs: 24 * 60 * 60_000 },
|
|
61
105
|
});
|
|
62
106
|
type RiskCloudClient = Pick<
|
|
63
107
|
typeof apiClient,
|
|
64
|
-
"getCloudHistory" | "getCloudQuotesBatch" | "getCloudFredSeries"
|
|
108
|
+
"getCloudHistory" | "getCloudQuotesBatch" | "getCloudFredSeries" | "getCloudExchangeRate"
|
|
65
109
|
>;
|
|
66
110
|
|
|
67
111
|
function validateRiskQuote(
|
|
68
112
|
quote: CloudQuotePayload | null,
|
|
69
113
|
instrument: RiskInstrument,
|
|
70
114
|
now = new Date(),
|
|
115
|
+
currency = "USD",
|
|
71
116
|
): asserts quote is CloudQuotePayload {
|
|
72
117
|
// A stale quote still establishes listing identity; it is not used as a mark.
|
|
73
|
-
if (!quote || quote.currency !==
|
|
74
|
-
throw new Error("Current USD listing identity unavailable");
|
|
118
|
+
if (!quote || quote.currency !== currency)
|
|
119
|
+
throw new Error(currency === "USD" ? "Current USD listing identity unavailable" : "Current listing identity unavailable");
|
|
75
120
|
if (normalizeSymbol(quote.symbol) !== normalizeSymbol(instrument.symbol))
|
|
76
121
|
throw new Error("Quote symbol differs from the requested holding");
|
|
77
122
|
const exchange = canonicalExchange(
|
|
@@ -94,17 +139,23 @@ function validateRiskQuote(
|
|
|
94
139
|
throw new Error("Current quote price or timestamp unavailable");
|
|
95
140
|
}
|
|
96
141
|
|
|
97
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Daily Cloud observations, currency and listing metadata must agree before
|
|
144
|
+
* risk math. With a conversion the listing quotes in that currency and its
|
|
145
|
+
* closes are restated in USD at same-date FX closes on the basket's calendar.
|
|
146
|
+
*/
|
|
98
147
|
export function validateRiskHistory(
|
|
99
148
|
response: CloudMarketResponse<CloudPricePointPayload[]>,
|
|
100
149
|
instrument: RiskInstrument,
|
|
101
150
|
quote: CloudQuotePayload | null,
|
|
102
151
|
now = new Date(),
|
|
152
|
+
conversion?: { listingCurrency: string; divisor: number; fx: FxCloses },
|
|
103
153
|
): {
|
|
104
|
-
points: CloudPricePointPayload[];
|
|
105
154
|
returns: DatedReturn[];
|
|
106
155
|
asOf: string;
|
|
107
156
|
closeMark: { price: number; date: string; currency: string } | null;
|
|
157
|
+
/** Latest completed close in the returns' currency, whatever marks the holding. */
|
|
158
|
+
latestClose: { price: number; date: string };
|
|
108
159
|
} {
|
|
109
160
|
if (
|
|
110
161
|
response.status !== "success" ||
|
|
@@ -118,9 +169,10 @@ export function validateRiskHistory(
|
|
|
118
169
|
response.providerMeta?.currency ??
|
|
119
170
|
quote?.currency ??
|
|
120
171
|
(US_LISTING_VENUES.has(canonicalExchange(instrument.exchange)) ? "USD" : null);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
172
|
+
const expectedCurrency = conversion?.listingCurrency ?? "USD";
|
|
173
|
+
if (quote) validateRiskQuote(quote, instrument, now, expectedCurrency);
|
|
174
|
+
else if (historyCurrency !== expectedCurrency)
|
|
175
|
+
throw new Error(conversion ? "Current listing identity unavailable" : "Current USD listing identity unavailable");
|
|
124
176
|
const exchange = canonicalExchange(
|
|
125
177
|
instrument.exchange || quote?.listingExchangeName || "",
|
|
126
178
|
);
|
|
@@ -182,24 +234,37 @@ export function validateRiskHistory(
|
|
|
182
234
|
...point,
|
|
183
235
|
date: new Date(point.date),
|
|
184
236
|
}));
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
237
|
+
const local = resolveDatedReturns(prices);
|
|
238
|
+
if (local.integrity)
|
|
187
239
|
throw new Error("Daily history has inconsistent OHLC observations");
|
|
188
|
-
|
|
189
|
-
|
|
240
|
+
// A converted series is on the NYSE calendar the basket and SPY share.
|
|
241
|
+
const series = conversion
|
|
242
|
+
? convertClosesToUsd(
|
|
243
|
+
points.map((point) => ({ date: new Date(point.date).toISOString().slice(0, 10), close: point.close })),
|
|
244
|
+
conversion.fx,
|
|
245
|
+
conversion.divisor,
|
|
246
|
+
)
|
|
247
|
+
: prices;
|
|
248
|
+
const returns = conversion ? resolveDatedReturns(series).returns : local.returns;
|
|
249
|
+
const cadence = qualifySharpeCadence(returns, [
|
|
250
|
+
{ symbol: instrument.symbol, exchange: conversion ? "NYSE" : exchange, history: series },
|
|
190
251
|
]);
|
|
191
252
|
if (!cadence.supported)
|
|
192
253
|
throw new Error(cadence.reason ?? "Daily session cadence unavailable");
|
|
193
|
-
const lastPoint =
|
|
254
|
+
const lastPoint = series.at(-1)!;
|
|
255
|
+
const latestClose = {
|
|
256
|
+
price: lastPoint.close,
|
|
257
|
+
date: lastPoint.date.toISOString().slice(0, 10),
|
|
258
|
+
};
|
|
194
259
|
return {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
asOf: last,
|
|
260
|
+
returns,
|
|
261
|
+
asOf: latestClose.date,
|
|
198
262
|
// A missing or stale quote is not a current mark; the latest dated close is.
|
|
199
263
|
closeMark:
|
|
200
264
|
quote && !quote.stale
|
|
201
265
|
? null
|
|
202
|
-
: {
|
|
266
|
+
: { ...latestClose, currency: conversion ? "USD" : historyCurrency! },
|
|
267
|
+
latestClose,
|
|
203
268
|
};
|
|
204
269
|
}
|
|
205
270
|
function validateFred(
|
|
@@ -229,38 +294,75 @@ function validateFred(
|
|
|
229
294
|
return data;
|
|
230
295
|
}
|
|
231
296
|
|
|
297
|
+
/** The USD value a holding ranks by before its history loads; unknown ranks last. */
|
|
298
|
+
function rankValue(request: RiskHoldingRequest, quote: CloudQuotePayload | null): number {
|
|
299
|
+
if (quote && request.quantity != null && Number.isFinite(request.quantity))
|
|
300
|
+
return Math.abs(request.quantity * quote.price);
|
|
301
|
+
const snapshot = request.snapshotValue;
|
|
302
|
+
return snapshot != null && Number.isFinite(snapshot) ? Math.abs(snapshot) : -1;
|
|
303
|
+
}
|
|
304
|
+
const largestFirst = (
|
|
305
|
+
left: { rank: number; order: number },
|
|
306
|
+
right: { rank: number; order: number },
|
|
307
|
+
) => right.rank - left.rank || left.order - right.order;
|
|
308
|
+
|
|
232
309
|
/**
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
310
|
+
* Quotes every holding, then requests daily history only for the factor
|
|
311
|
+
* proxies and the largest holdings by current USD value that can enter the
|
|
312
|
+
* basket, up to RISK_HISTORY_LIMIT. A holding that quotes in another currency
|
|
313
|
+
* competes at its value at the current rate and, when requested, also loads
|
|
314
|
+
* its currency's daily FX history once per currency, so its closes convert to
|
|
315
|
+
* USD. Holdings with no FX pair, positions the caller marks unsupported and
|
|
316
|
+
* holdings past the limit get no history request; their entry says why. The
|
|
317
|
+
* signal stops the fetch: no request starts once it
|
|
318
|
+
* aborts, the ones in flight are cancelled, and the call rejects instead of
|
|
319
|
+
* returning a partial snapshot nobody is waiting for.
|
|
236
320
|
*/
|
|
237
321
|
export async function fetchPortfolioRiskMarket(
|
|
238
|
-
|
|
322
|
+
holdings: readonly RiskHoldingRequest[],
|
|
239
323
|
client: RiskCloudClient = apiClient,
|
|
240
324
|
now = new Date(),
|
|
241
325
|
signal?: AbortSignal,
|
|
242
326
|
): Promise<RiskMarketSnapshot> {
|
|
243
327
|
signal?.throwIfAborted();
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
328
|
+
const normalized = (row: RiskInstrument): RiskInstrument => ({
|
|
329
|
+
symbol: normalizeSymbol(row.symbol),
|
|
330
|
+
exchange: canonicalExchange(row.exchange),
|
|
331
|
+
});
|
|
332
|
+
const factors = new Map(
|
|
333
|
+
RISK_FACTOR_INSTRUMENTS.map((row) => [riskInstrumentId(row), normalized(row)]),
|
|
334
|
+
);
|
|
335
|
+
const entries = new Map<
|
|
336
|
+
string,
|
|
337
|
+
{ id: string; instrument: RiskInstrument; request: RiskHoldingRequest; order: number; rank: number }
|
|
338
|
+
>();
|
|
339
|
+
for (const request of holdings) {
|
|
340
|
+
const id = riskInstrumentId(request);
|
|
341
|
+
if (!entries.has(id))
|
|
342
|
+
entries.set(id, {
|
|
343
|
+
id,
|
|
344
|
+
instrument: normalized(request),
|
|
345
|
+
request,
|
|
346
|
+
order: entries.size,
|
|
347
|
+
rank: rankValue(request, null),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
const quoted = [...entries.values()].sort(largestFirst).slice(0, RISK_QUOTE_LIMIT);
|
|
351
|
+
const targets = [
|
|
352
|
+
...new Map([
|
|
353
|
+
...quoted.map((row) => [row.id, row.instrument] as const),
|
|
354
|
+
...factors,
|
|
355
|
+
]).values(),
|
|
254
356
|
];
|
|
255
|
-
if (unique.length > 87)
|
|
256
|
-
throw new Error(
|
|
257
|
-
"Portfolio risk supports at most 80 holdings plus its factor proxies",
|
|
258
|
-
);
|
|
259
357
|
const quotes = new Map<string, CloudQuotePayload>();
|
|
358
|
+
const listings = new Map<string, { price: number; currency: string }>();
|
|
359
|
+
// Listings quoted in another currency that convert to USD, and why the others do not.
|
|
360
|
+
const conversions = new Map<string, RiskConversion>();
|
|
361
|
+
const foreignReasons = new Map<string, string>();
|
|
260
362
|
const warnings: string[] = [];
|
|
261
|
-
for (let start = 0; start <
|
|
363
|
+
for (let start = 0; start < targets.length; start += 50) {
|
|
262
364
|
signal?.throwIfAborted();
|
|
263
|
-
const requested =
|
|
365
|
+
const requested = targets.slice(start, start + 50);
|
|
264
366
|
try {
|
|
265
367
|
const response = await client.getCloudQuotesBatch(
|
|
266
368
|
requested,
|
|
@@ -275,17 +377,38 @@ export async function fetchPortfolioRiskMarket(
|
|
|
275
377
|
canonicalExchange(item.exchange),
|
|
276
378
|
);
|
|
277
379
|
if (
|
|
278
|
-
target
|
|
279
|
-
item.status
|
|
280
|
-
item.data
|
|
281
|
-
normalizeSymbol(item.data.symbol)
|
|
282
|
-
)
|
|
380
|
+
!target ||
|
|
381
|
+
item.status !== "success" ||
|
|
382
|
+
!item.data ||
|
|
383
|
+
normalizeSymbol(item.data.symbol) !== normalizeSymbol(target.symbol)
|
|
384
|
+
)
|
|
385
|
+
continue;
|
|
386
|
+
const id = riskInstrumentId(target);
|
|
387
|
+
// A holding quoted in another currency enters the USD basket only
|
|
388
|
+
// through its daily closes restated at same-date FX closes.
|
|
389
|
+
if (!factors.has(id) && item.data.currency && item.data.currency !== "USD") {
|
|
390
|
+
if (Number.isFinite(item.data.price) && item.data.price > 0)
|
|
391
|
+
listings.set(id, { price: item.data.price, currency: item.data.currency });
|
|
392
|
+
if (entries.get(id)?.request.unsupported) continue;
|
|
393
|
+
const conversion = riskConversion(item.data.currency);
|
|
394
|
+
if (!conversion) {
|
|
395
|
+
foreignReasons.set(id, FX_NO_PAIR);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
283
398
|
try {
|
|
284
|
-
validateRiskQuote(item.data, target, now);
|
|
285
|
-
quotes.set(
|
|
399
|
+
validateRiskQuote(item.data, target, now, item.data.currency);
|
|
400
|
+
quotes.set(id, item.data);
|
|
401
|
+
conversions.set(id, conversion);
|
|
286
402
|
} catch (error) {
|
|
287
|
-
|
|
403
|
+
foreignReasons.set(id, message(error));
|
|
288
404
|
}
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
validateRiskQuote(item.data, target, now);
|
|
409
|
+
quotes.set(id, item.data);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
warnings.push(`${target.symbol}: ${message(error)}`);
|
|
289
412
|
}
|
|
290
413
|
}
|
|
291
414
|
} catch (error) {
|
|
@@ -293,6 +416,63 @@ export async function fetchPortfolioRiskMarket(
|
|
|
293
416
|
warnings.push(`Holding marks: ${message(error)}`);
|
|
294
417
|
}
|
|
295
418
|
}
|
|
419
|
+
// Current rates value a foreign holding: its rank, its mark, and its size when left out.
|
|
420
|
+
const fxCurrencies = [
|
|
421
|
+
...new Set(
|
|
422
|
+
[...entries.values()].flatMap((row) => {
|
|
423
|
+
const currency = resolveCurrencyUnit(
|
|
424
|
+
listings.get(row.id)?.currency ?? (quotes.has(row.id) ? null : row.request.currency),
|
|
425
|
+
).currency;
|
|
426
|
+
return currency && currency !== "USD" ? [currency] : [];
|
|
427
|
+
}),
|
|
428
|
+
),
|
|
429
|
+
].slice(0, MAX_FX_CURRENCIES);
|
|
430
|
+
const fx = await Promise.allSettled(
|
|
431
|
+
fxCurrencies.map(async (currency) => {
|
|
432
|
+
const response = await client.getCloudExchangeRate(currency);
|
|
433
|
+
const rate = response.status === "success" || response.status === "partial"
|
|
434
|
+
? response.data?.rate
|
|
435
|
+
: undefined;
|
|
436
|
+
if (typeof rate !== "number" || !Number.isFinite(rate) || rate <= 0)
|
|
437
|
+
throw new Error(response.reasonCode ?? "Rate unavailable");
|
|
438
|
+
return [currency, rate] as const;
|
|
439
|
+
}),
|
|
440
|
+
);
|
|
441
|
+
signal?.throwIfAborted();
|
|
442
|
+
const fxRates: Record<string, number> = {};
|
|
443
|
+
for (const [index, result] of fx.entries()) {
|
|
444
|
+
if (result.status === "fulfilled") fxRates[result.value[0]] = result.value[1];
|
|
445
|
+
else {
|
|
446
|
+
if (isAccessDenied(result.reason)) throw result.reason;
|
|
447
|
+
warnings.push(`${fxCurrencies[index]} exchange rate: ${message(result.reason)}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/** The listing's quote in USD at the current rate; null when no rate converts it. */
|
|
451
|
+
const usdQuote = (id: string): CloudQuotePayload | null => {
|
|
452
|
+
const quote = quotes.get(id) ?? null,
|
|
453
|
+
conversion = conversions.get(id);
|
|
454
|
+
if (!quote || !conversion) return quote;
|
|
455
|
+
const rate = fxRates[conversion.currency];
|
|
456
|
+
return rate
|
|
457
|
+
? { ...quote, currency: "USD", price: (quote.price * rate) / conversion.divisor }
|
|
458
|
+
: null;
|
|
459
|
+
};
|
|
460
|
+
const candidates = quoted
|
|
461
|
+
.filter(
|
|
462
|
+
(row) =>
|
|
463
|
+
!factors.has(row.id) &&
|
|
464
|
+
!row.request.unsupported &&
|
|
465
|
+
(!listings.has(row.id) || conversions.has(row.id)),
|
|
466
|
+
)
|
|
467
|
+
.map((row) => ({ ...row, rank: rankValue(row.request, usdQuote(row.id)) }))
|
|
468
|
+
.sort(largestFirst);
|
|
469
|
+
const requestedIds = new Set([
|
|
470
|
+
...factors.keys(),
|
|
471
|
+
...candidates.slice(0, RISK_HISTORY_LIMIT).map((row) => row.id),
|
|
472
|
+
]);
|
|
473
|
+
const instruments = [
|
|
474
|
+
...new Map([...factors, ...[...entries.values()].map((row) => [row.id, row.instrument] as const)]).entries(),
|
|
475
|
+
];
|
|
296
476
|
const start = new Date(now);
|
|
297
477
|
start.setUTCMonth(start.getUTCMonth() - 18);
|
|
298
478
|
start.setUTCDate(1);
|
|
@@ -301,54 +481,117 @@ export async function fetchPortfolioRiskMarket(
|
|
|
301
481
|
endDate = new Date(Date.parse(today) - 86_400_000)
|
|
302
482
|
.toISOString()
|
|
303
483
|
.slice(0, 10);
|
|
304
|
-
const
|
|
484
|
+
const historyParams = {
|
|
485
|
+
interval: "1day",
|
|
486
|
+
startDate,
|
|
487
|
+
// validateRiskHistory drops today's bar; ending on yesterday
|
|
488
|
+
// loses yesterday's close for most listings.
|
|
489
|
+
endDate: today,
|
|
490
|
+
outputsize: 1000,
|
|
491
|
+
rangeKey: "2Y",
|
|
492
|
+
} as const;
|
|
493
|
+
const histories: RiskMarketHistory[] = instruments.map(([id, instrument]) => {
|
|
494
|
+
const quote = quotes.get(id) ?? null,
|
|
495
|
+
listing = listings.get(id) ?? null;
|
|
496
|
+
return {
|
|
497
|
+
instrument,
|
|
498
|
+
quote,
|
|
499
|
+
listing,
|
|
500
|
+
currency: quote?.currency ?? listing?.currency ?? null,
|
|
501
|
+
returns: [],
|
|
502
|
+
asOf: null,
|
|
503
|
+
error: requestedIds.has(id)
|
|
504
|
+
? null
|
|
505
|
+
: listing && !conversions.has(id)
|
|
506
|
+
? (foreignReasons.get(id) ??
|
|
507
|
+
(entries.get(id)?.request.unsupported ? "Outside the long USD equity basket" : FX_UNAVAILABLE))
|
|
508
|
+
: entries.get(id)?.request.unsupported
|
|
509
|
+
? "Outside the long USD equity basket"
|
|
510
|
+
: BEYOND_SIZE_REASON,
|
|
511
|
+
};
|
|
512
|
+
});
|
|
513
|
+
// One daily FX history per currency, shared by every holding that converts with it.
|
|
514
|
+
const fxHistories = new Map<string, Promise<FxCloses>>();
|
|
515
|
+
const fxHistory = (conversion: RiskConversion) => {
|
|
516
|
+
let loading = fxHistories.get(conversion.currency);
|
|
517
|
+
if (!loading) {
|
|
518
|
+
loading = (async () => {
|
|
519
|
+
const { symbol, exchange } = conversion.leg.instrument;
|
|
520
|
+
let closes: FxCloses;
|
|
521
|
+
try {
|
|
522
|
+
closes = validateFxHistory(
|
|
523
|
+
await client.getCloudHistory(symbol, exchange ?? "", historyParams, signal ? { signal } : undefined),
|
|
524
|
+
conversion.leg,
|
|
525
|
+
now,
|
|
526
|
+
);
|
|
527
|
+
} catch (error) {
|
|
528
|
+
if (isAccessDenied(error) || signal?.aborted) throw error;
|
|
529
|
+
throw new Error(message(error).startsWith("Foreign holdings") ? message(error) : FX_UNAVAILABLE);
|
|
530
|
+
}
|
|
531
|
+
// A latest close far from the current rate is a wrong or inverted pair, not a move.
|
|
532
|
+
const rate = fxRates[conversion.currency];
|
|
533
|
+
const latest = closes.closes.get(closes.asOf)!;
|
|
534
|
+
if (rate && Math.abs(latest / rate - 1) > FX_LIVE_RATE_MAX_DEVIATION)
|
|
535
|
+
throw new Error(FX_DISAGREES);
|
|
536
|
+
return closes;
|
|
537
|
+
})();
|
|
538
|
+
fxHistories.set(conversion.currency, loading);
|
|
539
|
+
}
|
|
540
|
+
return loading;
|
|
541
|
+
};
|
|
542
|
+
const pending = instruments.flatMap(([id], index) =>
|
|
543
|
+
requestedIds.has(id) ? [index] : [],
|
|
544
|
+
);
|
|
305
545
|
let next = 0;
|
|
306
546
|
const historyWork = Promise.all(
|
|
307
|
-
Array.from({ length: Math.min(
|
|
308
|
-
while (next <
|
|
547
|
+
Array.from({ length: Math.min(HISTORY_CONCURRENCY, pending.length) }, async () => {
|
|
548
|
+
while (next < pending.length) {
|
|
309
549
|
signal?.throwIfAborted();
|
|
310
|
-
const index = next
|
|
311
|
-
|
|
312
|
-
|
|
550
|
+
const index = pending[next++]!,
|
|
551
|
+
entry = histories[index]!,
|
|
552
|
+
conversion = conversions.get(instruments[index]![0]),
|
|
553
|
+
{ instrument, quote } = entry;
|
|
313
554
|
try {
|
|
555
|
+
const fxCloses = conversion ? await fxHistory(conversion) : null;
|
|
314
556
|
const response = await client.getCloudHistory(
|
|
315
557
|
instrument.symbol,
|
|
316
558
|
instrument.exchange || quote?.listingExchangeName || "",
|
|
317
|
-
|
|
318
|
-
interval: "1day",
|
|
319
|
-
startDate,
|
|
320
|
-
// validateRiskHistory drops today's bar; ending on yesterday
|
|
321
|
-
// loses yesterday's close for most listings.
|
|
322
|
-
endDate: today,
|
|
323
|
-
outputsize: 1000,
|
|
324
|
-
rangeKey: "2Y",
|
|
325
|
-
},
|
|
559
|
+
historyParams,
|
|
326
560
|
signal ? { signal } : undefined,
|
|
327
561
|
);
|
|
328
|
-
const resolved = validateRiskHistory(
|
|
562
|
+
const { latestClose, ...resolved } = validateRiskHistory(
|
|
329
563
|
response,
|
|
330
564
|
instrument,
|
|
331
565
|
quote,
|
|
332
566
|
now,
|
|
567
|
+
conversion && fxCloses
|
|
568
|
+
? { listingCurrency: conversion.listingCurrency, divisor: conversion.divisor, fx: fxCloses }
|
|
569
|
+
: undefined,
|
|
333
570
|
);
|
|
571
|
+
if (conversion && fxCloses) {
|
|
572
|
+
// A current quote marks at the current rate; without one, or without
|
|
573
|
+
// a rate, the latest close at its own date's FX close does.
|
|
574
|
+
const current = quote && !quote.stale ? usdQuote(instruments[index]![0]) : null;
|
|
575
|
+
histories[index] = {
|
|
576
|
+
...entry,
|
|
577
|
+
...resolved,
|
|
578
|
+
quote: current,
|
|
579
|
+
currency: "USD",
|
|
580
|
+
closeMark: current ? null : { ...latestClose, currency: "USD" },
|
|
581
|
+
converted: { currency: conversion.listingCurrency, fxAsOf: fxCloses.asOf },
|
|
582
|
+
error: null,
|
|
583
|
+
};
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
334
586
|
histories[index] = {
|
|
335
|
-
|
|
336
|
-
quote,
|
|
587
|
+
...entry,
|
|
337
588
|
currency: quote?.currency ?? resolved.closeMark?.currency ?? null,
|
|
338
589
|
...resolved,
|
|
339
590
|
error: null,
|
|
340
591
|
};
|
|
341
592
|
} catch (error) {
|
|
342
593
|
if (isAccessDenied(error) || signal?.aborted) throw error;
|
|
343
|
-
histories[index] = {
|
|
344
|
-
instrument,
|
|
345
|
-
quote,
|
|
346
|
-
currency: quote?.currency ?? null,
|
|
347
|
-
points: [],
|
|
348
|
-
returns: [],
|
|
349
|
-
asOf: null,
|
|
350
|
-
error: message(error),
|
|
351
|
-
};
|
|
594
|
+
histories[index] = { ...entry, error: message(error) };
|
|
352
595
|
}
|
|
353
596
|
}
|
|
354
597
|
}),
|
|
@@ -384,18 +627,26 @@ export async function fetchPortfolioRiskMarket(
|
|
|
384
627
|
volatility: fred[1]!.status === "fulfilled" ? fred[1]!.value : null,
|
|
385
628
|
fetchedAt: now.toISOString(),
|
|
386
629
|
warnings,
|
|
630
|
+
fxRates,
|
|
387
631
|
};
|
|
388
632
|
}
|
|
389
|
-
|
|
390
|
-
|
|
633
|
+
/** What changes the requests: identity, size and whether a holding can enter the basket. */
|
|
634
|
+
const cacheKey = (holdings: readonly RiskHoldingRequest[]) =>
|
|
635
|
+
[
|
|
636
|
+
...new Set(
|
|
637
|
+
holdings.map(
|
|
638
|
+
(row) => `${riskInstrumentId(row)}|${row.quantity ?? ""}|${row.currency ?? ""}|${row.unsupported ? 1 : 0}`,
|
|
639
|
+
),
|
|
640
|
+
),
|
|
641
|
+
].sort().join(",");
|
|
391
642
|
export async function loadPortfolioRiskMarket(
|
|
392
|
-
|
|
643
|
+
holdings: readonly RiskHoldingRequest[],
|
|
393
644
|
force = false,
|
|
394
645
|
) {
|
|
395
646
|
const { payload, stale, refreshError } = await loadCloudResource(
|
|
396
647
|
portfolioRiskCache,
|
|
397
|
-
cacheKey(
|
|
398
|
-
() => fetchPortfolioRiskMarket(
|
|
648
|
+
cacheKey(holdings),
|
|
649
|
+
() => fetchPortfolioRiskMarket(holdings),
|
|
399
650
|
{ force },
|
|
400
651
|
);
|
|
401
652
|
return {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { CloudMarketResponse, CloudPricePointPayload } from "../../../api-client/types";
|
|
2
|
+
import { fxLegForCurrency, type FxLeg } from "../../../market-data/coordinator/fx-legs";
|
|
3
|
+
import { dailyFxCloses, FxHistoryError, type FxCloses } from "../../../market-data/fx-closes";
|
|
4
|
+
import { getPublishedUsEquityCalendarDay } from "../../../market-data/published-us-sessions";
|
|
5
|
+
import type { PricePoint } from "../../../types/financials";
|
|
6
|
+
import { resolveCurrencyUnit } from "../../../utils/currency-units";
|
|
7
|
+
|
|
8
|
+
export type { FxCloses };
|
|
9
|
+
|
|
10
|
+
/** How a listing quoted in another currency is restated in USD. */
|
|
11
|
+
export interface RiskConversion {
|
|
12
|
+
/** The listing currency as quoted, sub-units included (GBp, ZAc). */
|
|
13
|
+
listingCurrency: string;
|
|
14
|
+
/** The currency the FX pair prices (GBP for GBp). */
|
|
15
|
+
currency: string;
|
|
16
|
+
/** Listing units per currency unit: 100 for pence. */
|
|
17
|
+
divisor: number;
|
|
18
|
+
leg: FxLeg;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const FX_UNAVAILABLE = "Foreign holdings: daily FX closes unavailable";
|
|
22
|
+
export const FX_NO_PAIR = "Foreign holdings: no daily FX pair for this currency";
|
|
23
|
+
export const FX_STALE = "Foreign holdings: daily FX history is stale";
|
|
24
|
+
export const FX_GAP = "Foreign holdings: daily FX history has gaps on the holding's dates";
|
|
25
|
+
export const FX_DISAGREES = "Foreign holdings: daily FX closes disagree with the current rate";
|
|
26
|
+
export const LOCAL_GAP = "Foreign holdings: local daily history has a gap longer than a market holiday";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Longest run of calendar days a listing may go without a close while US
|
|
30
|
+
* sessions continue (Golden Week, Lunar New Year, national holidays). A longer
|
|
31
|
+
* silence is missing data, not a holiday, and is never carried across.
|
|
32
|
+
*/
|
|
33
|
+
const MAX_LOCAL_CLOSED_DAYS = 10;
|
|
34
|
+
/** The calendar the basket, SPY and the factor proxies share. */
|
|
35
|
+
const BASKET_CALENDAR = "NYSE";
|
|
36
|
+
const DAY_MS = 86_400_000;
|
|
37
|
+
|
|
38
|
+
/** The pair that restates a listing currency in USD, or null when there is none. */
|
|
39
|
+
export function riskConversion(listingCurrency: string): RiskConversion | null {
|
|
40
|
+
const unit = resolveCurrencyUnit(listingCurrency);
|
|
41
|
+
if (!unit.currency || unit.currency === "USD") return null;
|
|
42
|
+
const leg = fxLegForCurrency(unit.currency);
|
|
43
|
+
return leg ? { listingCurrency, currency: unit.currency, divisor: unit.divisor, leg } : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Completed daily closes of the pair as USD per unit, today's still-trading bar left out. */
|
|
47
|
+
export function validateFxHistory(
|
|
48
|
+
response: CloudMarketResponse<CloudPricePointPayload[]>,
|
|
49
|
+
leg: FxLeg,
|
|
50
|
+
now = new Date(),
|
|
51
|
+
): FxCloses {
|
|
52
|
+
try {
|
|
53
|
+
return dailyFxCloses(response, leg, now);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error instanceof FxHistoryError) throw new Error(error.problem === "stale" ? FX_STALE : FX_UNAVAILABLE);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Restates a listing's completed local closes in USD on every NYSE session
|
|
62
|
+
* they span, each at the FX close of that same date, so the converted series
|
|
63
|
+
* shares the basket's calendar and its returns pair with SPY and the other
|
|
64
|
+
* holdings interval for interval.
|
|
65
|
+
*
|
|
66
|
+
* On a US session the listing did not trade (its own holiday), the holding is
|
|
67
|
+
* worth its last local close at that day's FX close: its USD value moves with
|
|
68
|
+
* the currency alone, as it would in the account. A US session with no FX
|
|
69
|
+
* close, or a local silence longer than any market holiday, rejects the whole
|
|
70
|
+
* series instead of bridging it. The series ends at the earlier of the last
|
|
71
|
+
* local close and the last FX close, so no close is carried past the data.
|
|
72
|
+
*/
|
|
73
|
+
export function convertClosesToUsd(
|
|
74
|
+
local: readonly { date: string; close: number }[],
|
|
75
|
+
fx: FxCloses,
|
|
76
|
+
divisor: number,
|
|
77
|
+
): PricePoint[] {
|
|
78
|
+
const points = [...local]
|
|
79
|
+
.map((point) => ({ date: point.date.slice(0, 10), close: point.close }))
|
|
80
|
+
.sort((left, right) => left.date.localeCompare(right.date));
|
|
81
|
+
if (points.length < 2) throw new Error("Daily history unavailable");
|
|
82
|
+
const start = points[0]!.date > fx.first ? points[0]!.date : fx.first;
|
|
83
|
+
const end = points.at(-1)!.date < fx.asOf ? points.at(-1)!.date : fx.asOf;
|
|
84
|
+
const converted: PricePoint[] = [];
|
|
85
|
+
let cursor = 0;
|
|
86
|
+
for (let time = Date.parse(`${start}T00:00:00Z`); time <= Date.parse(`${end}T00:00:00Z`); time += DAY_MS) {
|
|
87
|
+
const date = new Date(time).toISOString().slice(0, 10);
|
|
88
|
+
const day = getPublishedUsEquityCalendarDay(BASKET_CALENDAR, date);
|
|
89
|
+
if (day == null) throw new Error("Daily calendar unavailable");
|
|
90
|
+
if (day !== "session") continue;
|
|
91
|
+
while (cursor + 1 < points.length && points[cursor + 1]!.date <= date) cursor += 1;
|
|
92
|
+
const last = points[cursor]!;
|
|
93
|
+
if (last.date > date) continue;
|
|
94
|
+
if (time - Date.parse(`${last.date}T00:00:00Z`) > MAX_LOCAL_CLOSED_DAYS * DAY_MS) throw new Error(LOCAL_GAP);
|
|
95
|
+
const rate = fx.closes.get(date);
|
|
96
|
+
if (rate == null) throw new Error(FX_GAP);
|
|
97
|
+
const close = (last.close * rate) / divisor;
|
|
98
|
+
converted.push({ date: new Date(time), open: close, high: close, low: close, close });
|
|
99
|
+
}
|
|
100
|
+
if (converted.length < 2) throw new Error("Daily history unavailable");
|
|
101
|
+
return converted;
|
|
102
|
+
}
|