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,5 +1,7 @@
|
|
|
1
1
|
import type { MarketState, Quote } from "../../types/financials";
|
|
2
2
|
import { blendHex, colors, priceColor, type ThemeColors } from "../../theme/colors";
|
|
3
|
+
import { isFiniteNumber } from "../../utils/guards";
|
|
4
|
+
import { quoteTradingDay } from "../quotes/day-range";
|
|
3
5
|
|
|
4
6
|
const CLOSED_CHANGE_MUTING_RATIO = 0.55;
|
|
5
7
|
const US_SESSION_TIME = new Intl.DateTimeFormat("en-US", {
|
|
@@ -20,6 +22,12 @@ export interface ActiveQuoteDisplay {
|
|
|
20
22
|
changePercent?: number;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
export type ExtendedSession = "PRE" | "POST";
|
|
26
|
+
|
|
27
|
+
export interface ExtendedSessionDisplay extends ActiveQuoteDisplay {
|
|
28
|
+
session: ExtendedSession;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
export function marketStateLabel(state: MarketState): string {
|
|
24
32
|
switch (state) {
|
|
25
33
|
case "PRE": return "PRE-MKT";
|
|
@@ -124,6 +132,11 @@ export function exchangeShortName(exchangeName?: string, fullExchangeName?: stri
|
|
|
124
132
|
return map[name] || name;
|
|
125
133
|
}
|
|
126
134
|
|
|
135
|
+
/**
|
|
136
|
+
* The live print against the daily reference, extended hours included: what
|
|
137
|
+
* a position is worth now and its day P&L. A headline that shows an
|
|
138
|
+
* extended-hours line beside it uses getRegularSessionDisplay instead.
|
|
139
|
+
*/
|
|
127
140
|
export function getActiveQuoteDisplay(quote: Quote | null | undefined): ActiveQuoteDisplay | null {
|
|
128
141
|
if (!quote) return null;
|
|
129
142
|
if ((quote.marketState === "PRE" || quote.marketState === "PREPRE") && quote.preMarketPrice != null) {
|
|
@@ -141,3 +154,141 @@ export function getActiveQuoteDisplay(quote: Quote | null | undefined): ActiveQu
|
|
|
141
154
|
}
|
|
142
155
|
return { price: quote.price, change: quote.change, changePercent: quote.changePercent };
|
|
143
156
|
}
|
|
157
|
+
|
|
158
|
+
function isPositive(value: number | undefined): value is number {
|
|
159
|
+
return isFiniteNumber(value) && value > 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface CompletedRegularSession {
|
|
163
|
+
close: number;
|
|
164
|
+
/** The close's move from the session before it, when the quote reports it with the close. */
|
|
165
|
+
reported: { change: number; changePercent: number } | null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function reportedRegularMove(quote: Quote, close: number): CompletedRegularSession["reported"] {
|
|
169
|
+
const change = quote.regularChange;
|
|
170
|
+
if (!isFiniteNumber(change)) return null;
|
|
171
|
+
const reference = close - change;
|
|
172
|
+
const changePercent = isFiniteNumber(quote.regularChangePercent)
|
|
173
|
+
? quote.regularChangePercent
|
|
174
|
+
: isPositive(reference) ? (change / reference) * 100 : null;
|
|
175
|
+
return changePercent == null ? null : { change, changePercent };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The regular session the quote's day follows, once it is over: after hours,
|
|
180
|
+
* overnight, and through a weekend or holiday until the next pre-market, and
|
|
181
|
+
* in the pre-market when the quote reports the last session's close and move,
|
|
182
|
+
* which its own previous close can no longer give once the day has rolled.
|
|
183
|
+
* Only a US listing with extended trading reports one. Without a reported
|
|
184
|
+
* close, an after-hours print less its move from the close gives it. Null in
|
|
185
|
+
* the regular session.
|
|
186
|
+
*/
|
|
187
|
+
function completedRegularSession(quote: Quote): CompletedRegularSession | null {
|
|
188
|
+
const state = quote.marketState;
|
|
189
|
+
if (state == null || state === "REGULAR") return null;
|
|
190
|
+
const close = quote.regularClose;
|
|
191
|
+
if (isPositive(close)) {
|
|
192
|
+
const closeDate = quote.regularCloseSessionDate;
|
|
193
|
+
const reported = reportedRegularMove(quote, close);
|
|
194
|
+
if (state === "PRE") {
|
|
195
|
+
// The quote's own day is today; the close must be an earlier one, with its move.
|
|
196
|
+
const today = quoteTradingDay(quote);
|
|
197
|
+
return reported && closeDate && today && closeDate < today ? { close, reported } : null;
|
|
198
|
+
}
|
|
199
|
+
if (closeDate ? closeDate === quoteTradingDay(quote) : state === "POST") return { close, reported };
|
|
200
|
+
}
|
|
201
|
+
if (state === "POST" && isFiniteNumber(quote.postMarketPrice) && isFiniteNumber(quote.postMarketChange)) {
|
|
202
|
+
const derived = quote.postMarketPrice - quote.postMarketChange;
|
|
203
|
+
if (derived > 0) return { close: derived, reported: null };
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function completedRegularClose(quote: Quote): number | null {
|
|
209
|
+
return completedRegularSession(quote)?.close ?? null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The day's headline: the regular session, which stops at its close. Once it
|
|
214
|
+
* is over, and in the pre-market while the quote carries the last session's
|
|
215
|
+
* close and move, the price is that close and the move is its own, so
|
|
216
|
+
* extended trading never moves it; the extended print is
|
|
217
|
+
* getExtendedSessionDisplay. A close without its reported move is measured
|
|
218
|
+
* against the previous close. In the pre-market without that close and move
|
|
219
|
+
* the headline is the live price against the previous close, as the
|
|
220
|
+
* pre-market line is.
|
|
221
|
+
*/
|
|
222
|
+
export function getRegularSessionDisplay(quote: Quote | null | undefined): ActiveQuoteDisplay | null {
|
|
223
|
+
if (!quote) return null;
|
|
224
|
+
const session = completedRegularSession(quote);
|
|
225
|
+
if (session == null) return { price: quote.price, change: quote.change, changePercent: quote.changePercent };
|
|
226
|
+
const { close, reported } = session;
|
|
227
|
+
if (reported) return { price: close, ...reported };
|
|
228
|
+
const reference = isPositive(quote.previousClose)
|
|
229
|
+
? quote.previousClose
|
|
230
|
+
: isFiniteNumber(quote.change) && isPositive(quote.price - quote.change) ? quote.price - quote.change : null;
|
|
231
|
+
if (reference == null) return { price: close };
|
|
232
|
+
const change = close - reference;
|
|
233
|
+
return { price: close, change, changePercent: (change / reference) * 100 };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* getRegularSessionDisplay once the regular session is over (and in the
|
|
238
|
+
* pre-market, while the quote carries the last session's close and move), null
|
|
239
|
+
* while it is open. A table row headlines this when it has a column for the
|
|
240
|
+
* extended move: the close and its move stay put, and the extended column is
|
|
241
|
+
* the only figure that follows the extended print.
|
|
242
|
+
*/
|
|
243
|
+
export function getCompletedRegularSessionDisplay(quote: Quote | null | undefined): ActiveQuoteDisplay | null {
|
|
244
|
+
return quote && completedRegularClose(quote) != null ? getRegularSessionDisplay(quote) : null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The pre-market or after-hours print, measured from the regular close before
|
|
249
|
+
* it, while the quote has one: the pre-market until the open, the after-hours
|
|
250
|
+
* session from the close, and its last print until the next pre-market. Null
|
|
251
|
+
* when there is no extended print to show.
|
|
252
|
+
*/
|
|
253
|
+
export function getExtendedSessionDisplay(quote: Quote | null | undefined): ExtendedSessionDisplay | null {
|
|
254
|
+
if (!quote) return null;
|
|
255
|
+
const state = quote.marketState;
|
|
256
|
+
if (state === "PRE" || state === "PREPRE") {
|
|
257
|
+
if (isFiniteNumber(quote.preMarketPrice)) {
|
|
258
|
+
// Against the close the headline shows, so the two cannot disagree about it.
|
|
259
|
+
const close = state === "PRE" && quote.preMarketPrice > 0 ? completedRegularClose(quote) : null;
|
|
260
|
+
if (close != null) {
|
|
261
|
+
const change = quote.preMarketPrice - close;
|
|
262
|
+
return { session: "PRE", price: quote.preMarketPrice, change, changePercent: (change / close) * 100 };
|
|
263
|
+
}
|
|
264
|
+
return { session: "PRE", price: quote.preMarketPrice, change: quote.preMarketChange, changePercent: quote.preMarketChangePercent };
|
|
265
|
+
}
|
|
266
|
+
if (state === "PRE") return null;
|
|
267
|
+
}
|
|
268
|
+
const close = completedRegularClose(quote);
|
|
269
|
+
if (close == null) {
|
|
270
|
+
return state === "POST" && isFiniteNumber(quote.postMarketPrice)
|
|
271
|
+
? { session: "POST", price: quote.postMarketPrice, change: quote.postMarketChange, changePercent: quote.postMarketChangePercent }
|
|
272
|
+
: null;
|
|
273
|
+
}
|
|
274
|
+
const price = state === "POST" ? quote.postMarketPrice ?? quote.price : quote.price;
|
|
275
|
+
// Once the session is over, a last print at the close means nothing traded after it.
|
|
276
|
+
if (!isPositive(price) || (state !== "POST" && price === close)) return null;
|
|
277
|
+
const change = price - close;
|
|
278
|
+
return { session: "POST", price, change, changePercent: (change / close) * 100 };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The extended display only while its session is open: the pre-market or the after-hours session. */
|
|
282
|
+
function getOpenExtendedSessionDisplay(quote: Quote | null | undefined): ExtendedSessionDisplay | null {
|
|
283
|
+
return quote?.marketState === "PRE" || quote?.marketState === "POST" ? getExtendedSessionDisplay(quote) : null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The move a board of tiles colors by: the open pre-market or after-hours
|
|
288
|
+
* session's, from the regular close, otherwise the day's regular session.
|
|
289
|
+
*/
|
|
290
|
+
export function getSessionMoveDisplay(
|
|
291
|
+
quote: Quote | null | undefined,
|
|
292
|
+
): (ActiveQuoteDisplay & { session?: ExtendedSession }) | null {
|
|
293
|
+
return getOpenExtendedSessionDisplay(quote) ?? getRegularSessionDisplay(quote);
|
|
294
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Published full-day closures (weekend dates omitted) for Asia-Pacific venues,
|
|
2
|
+
// not a holiday-rule engine. A year a venue has not published is unknown, not
|
|
3
|
+
// open: every weekday of it reads as a session. Half-day sessions (HKEX and
|
|
4
|
+
// SGX eves, ASX's 14:10 closes) are not modelled. Checked 2026-10-09:
|
|
5
|
+
// KRX, also KOSDAQ: http://open.krx.co.kr/contents/MKD/01/0110/01100305/MKD01100305.jsp
|
|
6
|
+
// TWSE, whose closures TPEx shares: https://www.twse.com.tw/en/trading/holiday.html
|
|
7
|
+
// (2027 not yet published; 12 and 13 Feb 2026 are clearing-only days without trading)
|
|
8
|
+
// HKEX: https://www.hkex.com.hk/-/media/HKEX-Market/Services/Circulars-and-Notices/Participant-and-Members-Circulars/SEHK/2025/ce_SEHK_CT_075_2025.pdf
|
|
9
|
+
// and https://www.hkex.com.hk/-/media/HKEX-Market/Services/Circulars-and-Notices/Participant-and-Members-Circulars/SEHK/2026/ce_SEHK_CT_077_2026.pdf
|
|
10
|
+
// SGX closes on Singapore's gazetted public holidays, one on a Sunday moving to the Monday:
|
|
11
|
+
// https://www.sgx.com/stock-exchange/trading and https://www.mom.gov.sg/employment-practices/public-holidays
|
|
12
|
+
// ASX: https://www.asx.com.au/markets/market-resources/trading-hours-calendar/cash-market-trading-hours/trading-calendar
|
|
13
|
+
const KRX_CLOSURES: Record<number, readonly string[]> = {
|
|
14
|
+
2026: [
|
|
15
|
+
"01-01", "02-16", "02-17", "02-18", "03-02", "05-01", "05-05", "05-25", "06-03",
|
|
16
|
+
"07-17", "08-17", "09-24", "09-25", "10-05", "10-09", "12-25", "12-31",
|
|
17
|
+
],
|
|
18
|
+
2027: [
|
|
19
|
+
"01-01", "02-08", "02-09", "03-01", "05-03", "05-05", "05-13", "07-19", "08-16",
|
|
20
|
+
"09-14", "09-15", "09-16", "10-04", "10-11", "12-27", "12-31",
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const TWSE_CLOSURES: Record<number, readonly string[]> = {
|
|
25
|
+
2026: [
|
|
26
|
+
"01-01", "02-12", "02-13", "02-16", "02-17", "02-18", "02-19", "02-20", "02-27",
|
|
27
|
+
"04-03", "04-06", "05-01", "06-19", "09-25", "09-28", "10-09", "10-26", "12-25",
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** Closures by canonical venue code. */
|
|
32
|
+
export const PUBLISHED_APAC_CLOSURES: Readonly<Record<string, Record<number, readonly string[]>>> = {
|
|
33
|
+
KRX: KRX_CLOSURES,
|
|
34
|
+
KOSDAQ: KRX_CLOSURES,
|
|
35
|
+
TWSE: TWSE_CLOSURES,
|
|
36
|
+
TPEX: TWSE_CLOSURES,
|
|
37
|
+
HKEX: {
|
|
38
|
+
2026: [
|
|
39
|
+
"01-01", "02-17", "02-18", "02-19", "04-03", "04-06", "04-07", "05-01", "05-25",
|
|
40
|
+
"06-19", "07-01", "10-01", "10-19", "12-25",
|
|
41
|
+
],
|
|
42
|
+
2027: [
|
|
43
|
+
"01-01", "02-08", "02-09", "03-26", "03-29", "04-05", "05-13", "06-09", "07-01",
|
|
44
|
+
"09-16", "10-01", "10-08", "12-27",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
SGX: {
|
|
48
|
+
2026: ["01-01", "02-17", "02-18", "04-03", "05-01", "05-27", "06-01", "08-10", "11-09", "12-25"],
|
|
49
|
+
2027: ["01-01", "02-08", "03-10", "03-26", "05-17", "05-20", "08-09", "10-28"],
|
|
50
|
+
},
|
|
51
|
+
ASX: {
|
|
52
|
+
2026: ["01-01", "01-26", "04-03", "04-06", "06-08", "12-25", "12-28"],
|
|
53
|
+
2027: ["01-01", "01-26", "03-26", "03-29", "06-14", "12-27", "12-28"],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** True only for a published full-day closure; unpublished years are unknown, not open. */
|
|
58
|
+
export function isPublishedApacClosure(exchange: string, date: string): boolean {
|
|
59
|
+
if (!Object.hasOwn(PUBLISHED_APAC_CLOSURES, exchange)) return false;
|
|
60
|
+
return PUBLISHED_APAC_CLOSURES[exchange]![Number(date.slice(0, 4))]?.includes(date.slice(5)) ?? false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** True when the venue's closures of that year are published. */
|
|
64
|
+
export function hasPublishedApacCalendar(exchange: string, year: number): boolean {
|
|
65
|
+
return Object.hasOwn(PUBLISHED_APAC_CLOSURES, exchange) && PUBLISHED_APAC_CLOSURES[exchange]![year] !== undefined;
|
|
66
|
+
}
|
|
@@ -152,6 +152,12 @@ export function mergeQuoteContribution(
|
|
|
152
152
|
}
|
|
153
153
|
if (!current) return next;
|
|
154
154
|
|
|
155
|
+
// NAV responses are complete dated observations. Never carry an earlier
|
|
156
|
+
// date or intraday session fields into one, or its marker into a later tick.
|
|
157
|
+
if (current.priceObservation != null || next.priceObservation != null) {
|
|
158
|
+
return { ...next, name: next.name ?? current.name };
|
|
159
|
+
}
|
|
160
|
+
|
|
155
161
|
const priorBasis = resolvePriceBasis(current.priceBasis, current.instrumentType);
|
|
156
162
|
const samePriceBasis = priorBasis !== null && priorBasis === resolvePriceBasis(next.priceBasis, next.instrumentType || current.instrumentType);
|
|
157
163
|
const merged: QuoteContribution = {
|
|
@@ -171,6 +177,8 @@ export function mergeQuoteContribution(
|
|
|
171
177
|
priceBasis: next.priceBasis,
|
|
172
178
|
regularClose: next.regularClose,
|
|
173
179
|
regularCloseSessionDate: next.regularClose != null ? next.regularCloseSessionDate : undefined,
|
|
180
|
+
regularChange: next.regularClose != null ? next.regularChange : undefined,
|
|
181
|
+
regularChangePercent: next.regularClose != null ? next.regularChangePercent : undefined,
|
|
174
182
|
};
|
|
175
183
|
|
|
176
184
|
if (isOtherTradingDay(current, next)) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Quote } from "../../types/financials";
|
|
2
2
|
import { isUsListingExchange } from "../../utils/exchanges";
|
|
3
|
+
import { zonedDateKey } from "../../utils/zoned-date-time";
|
|
4
|
+
import { getPublishedUsEquitySession } from "../published-us-sessions";
|
|
3
5
|
import { quoteFutureToleranceMs } from "./clock";
|
|
4
6
|
import {
|
|
5
7
|
activeUsExtendedHoursSession,
|
|
@@ -7,6 +9,9 @@ import {
|
|
|
7
9
|
isUsPriorSessionPremarketQuote,
|
|
8
10
|
} from "../market/freshness";
|
|
9
11
|
|
|
12
|
+
// Give the next regular session's NAV time to publish after its actual close.
|
|
13
|
+
const NAV_PUBLICATION_ALLOWANCE_MS = 12 * 60 * 60_000;
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Receipt time cannot establish when the source observed a quoted price. A
|
|
12
17
|
* stamp slightly ahead of local time is clock skew, not a bad observation.
|
|
@@ -18,6 +23,36 @@ export function hasValidQuoteObservationTime(quote: Pick<Quote, "lastUpdated">,
|
|
|
18
23
|
&& Number.isFinite(new Date(timestamp).getTime()) && Number.isFinite(new Date(now).getTime());
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
/** The source date of a validated daily fund observation, including an old NAV. */
|
|
27
|
+
export function dailyNavDate(quote: Quote, now = Date.now()): string | null {
|
|
28
|
+
if (quote.priceObservation !== "nav" || quote.instrumentType !== "MUTUALFUND"
|
|
29
|
+
|| (quote.priceBasis !== undefined && quote.priceBasis !== "per-unit")
|
|
30
|
+
|| !Number.isFinite(quote.price) || quote.price <= 0
|
|
31
|
+
|| !hasValidQuoteObservationTime(quote, now) || quote.lastUpdated > now) return null;
|
|
32
|
+
const exchange = quote.listingExchangeName || quote.exchangeName;
|
|
33
|
+
const date = quote.changeSessionDate;
|
|
34
|
+
if (typeof exchange !== "string" || typeof date !== "string"
|
|
35
|
+
|| getPublishedUsEquitySession(exchange, date)?.kind !== "session"
|
|
36
|
+
|| zonedDateKey(quote.lastUpdated, "America/New_York") !== date) return null;
|
|
37
|
+
return date;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isDailyNavStale(quote: Quote, now: number): boolean {
|
|
41
|
+
const date = dailyNavDate(quote, now);
|
|
42
|
+
if (!date) return true;
|
|
43
|
+
const exchange = quote.listingExchangeName || quote.exchangeName;
|
|
44
|
+
const today = zonedDateKey(now, "America/New_York");
|
|
45
|
+
if (!today || !getPublishedUsEquitySession(exchange!, today) || date > today) return true;
|
|
46
|
+
const sourceDay = Date.parse(`${date}T00:00:00Z`);
|
|
47
|
+
for (let ahead = 1; ahead <= 10; ahead++) {
|
|
48
|
+
const nextDate = new Date(sourceDay + ahead * 24 * 60 * 60_000).toISOString().slice(0, 10);
|
|
49
|
+
const next = getPublishedUsEquitySession(exchange!, nextDate);
|
|
50
|
+
if (!next) return true;
|
|
51
|
+
if (next.kind === "session") return now >= next.close + NAV_PUBLICATION_ALLOWANCE_MS;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
21
56
|
export function isExtendedHoursExchange(quote: Quote): boolean {
|
|
22
57
|
return isUsListingExchange(quote.listingExchangeName || quote.exchangeName);
|
|
23
58
|
}
|
|
@@ -38,6 +73,7 @@ export function isQuoteStaleForCurrentSession(quote: Quote | null | undefined, n
|
|
|
38
73
|
if (!quote) return false;
|
|
39
74
|
if (quote.stale === true) return true;
|
|
40
75
|
if (!hasValidQuoteObservationTime(quote, now)) return true;
|
|
76
|
+
if (quote.priceObservation != null) return isDailyNavStale(quote, now);
|
|
41
77
|
if (isQuoteMissingActiveSessionPrice(quote, now)) return true;
|
|
42
78
|
|
|
43
79
|
// A tolerated future stamp belongs to the session in progress, not the next one.
|
|
@@ -12,7 +12,7 @@ import { mergeQuoteMetadata, quoteMetadataFromQuote } from "./metadata";
|
|
|
12
12
|
import { resolvePriceBasis } from "../market/price-basis";
|
|
13
13
|
import { hasLikelyQuoteUnitMismatch } from "../../utils/currency-units";
|
|
14
14
|
import { debugLog } from "../../utils/debug-log";
|
|
15
|
-
import { hasValidQuoteObservationTime, isExtendedHoursExchange, isQuoteStaleForCurrentSession } from "./freshness";
|
|
15
|
+
import { dailyNavDate, hasValidQuoteObservationTime, isExtendedHoursExchange, isQuoteStaleForCurrentSession } from "./freshness";
|
|
16
16
|
import { activeUsExtendedHoursSession, isTimestampStaleForExchangeSession } from "../market/freshness";
|
|
17
17
|
import {
|
|
18
18
|
finalizeSessionFields,
|
|
@@ -243,7 +243,10 @@ function buildAcceptedPriceCandidates(contributions: QuoteContribution[], now: n
|
|
|
243
243
|
} {
|
|
244
244
|
const ranks = new Map(contributions.map((quote) => [quote, priceRank(quote, now)] as const));
|
|
245
245
|
const sorted = [...contributions]
|
|
246
|
-
|
|
246
|
+
// Even a stale research snapshot must have its own NAV evidence. Canonical
|
|
247
|
+
// fallbacks cannot supply a missing observation time or security type.
|
|
248
|
+
.filter((quote) => Number.isFinite(quote.price)
|
|
249
|
+
&& (quote.priceObservation == null || dailyNavDate(quote, now) !== null))
|
|
247
250
|
.sort((left, right) => {
|
|
248
251
|
const rankDelta = ranks.get(left)! - ranks.get(right)!;
|
|
249
252
|
if (rankDelta !== 0) return rankDelta;
|
|
@@ -281,7 +284,7 @@ function buildAcceptedPriceCandidates(contributions: QuoteContribution[], now: n
|
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
export function isQuoteContributionStaleForCurrentSession(contribution: Quote, now = Date.now()): boolean {
|
|
284
|
-
if (contribution.marketState != null) return isQuoteStaleForCurrentSession(contribution, now);
|
|
287
|
+
if (contribution.priceObservation != null || contribution.marketState != null) return isQuoteStaleForCurrentSession(contribution, now);
|
|
285
288
|
// A price-only source can be combined with independent session metadata.
|
|
286
289
|
// Its observation must still belong to the current active session.
|
|
287
290
|
if (contribution.stale === true || !hasValidQuoteObservationTime(contribution, now)) return true;
|
|
@@ -387,8 +390,8 @@ export function resolveCanonicalQuote(
|
|
|
387
390
|
const { accepted: acceptedPriceCandidates, rejectedProviders } = buildAcceptedPriceCandidates(effectiveQuoteCandidates, now);
|
|
388
391
|
if (acceptedPriceCandidates.length === 0) return {};
|
|
389
392
|
|
|
390
|
-
|
|
391
|
-
|
|
393
|
+
let sessionCandidates = buildSessionCandidates(effectiveQuoteCandidates);
|
|
394
|
+
let listingCandidates = buildListingCandidates(contributions);
|
|
392
395
|
const routingCandidates = buildRoutingCandidates(contributions);
|
|
393
396
|
const descriptiveCandidates = buildDescriptiveCandidates(contributions);
|
|
394
397
|
|
|
@@ -401,9 +404,18 @@ export function resolveCanonicalQuote(
|
|
|
401
404
|
// The selected response owns the price convention; metadata enrichment must
|
|
402
405
|
// not declare units for another provider's price or supply incompatible anchors.
|
|
403
406
|
assignField(resolved, provenance, "priceBasis", priceProvider);
|
|
407
|
+
assignField(resolved, provenance, "priceObservation", priceProvider);
|
|
408
|
+
const isNav = priceProvider?.priceObservation === "nav";
|
|
409
|
+
if (isNav) {
|
|
410
|
+
sessionCandidates = priceProvider ? [priceProvider] : [];
|
|
411
|
+
listingCandidates = sessionCandidates;
|
|
412
|
+
} else {
|
|
413
|
+
sessionCandidates = sessionCandidates.filter((quote) => quote.priceObservation == null);
|
|
414
|
+
}
|
|
404
415
|
const selectedBasis = resolvePriceBasis(priceProvider?.priceBasis, priceProvider?.instrumentType);
|
|
405
416
|
const compatiblePrice = (candidate: QuoteContribution) => candidate === priceProvider
|
|
406
|
-
||
|
|
417
|
+
|| !isNav && candidate.priceObservation == null
|
|
418
|
+
&& selectedBasis !== null && resolvePriceBasis(candidate.priceBasis, candidate.instrumentType) === selectedBasis;
|
|
407
419
|
const compatiblePriceCandidates = acceptedPriceCandidates.filter(compatiblePrice);
|
|
408
420
|
for (const field of PRICE_FIELD_KEYS) {
|
|
409
421
|
if (field === "price") continue;
|
|
@@ -459,13 +471,14 @@ export function resolveCanonicalQuote(
|
|
|
459
471
|
|
|
460
472
|
let descriptiveProvider: QuoteContribution | undefined;
|
|
461
473
|
for (const field of DESCRIPTIVE_FIELD_KEYS) {
|
|
462
|
-
const
|
|
474
|
+
const ownsPriceField = field === "high52w" || field === "low52w" || isNav && (field === "volume" || field === "marketCap");
|
|
475
|
+
const provider = pickField(resolved, provenance, field, ownsPriceField ? descriptiveCandidates.filter(compatiblePrice) : descriptiveCandidates);
|
|
463
476
|
descriptiveProvider ??= provider;
|
|
464
477
|
}
|
|
465
478
|
provenance.descriptive = toProvenance(descriptiveProvider);
|
|
466
479
|
// Descriptive enrichment cannot turn an unknown-unit bond observation into
|
|
467
480
|
// an ordinary share price by replacing its source-reported security type.
|
|
468
|
-
if (priceProvider?.instrumentType?.trim().toUpperCase() === "BOND") {
|
|
481
|
+
if (isNav || priceProvider?.instrumentType?.trim().toUpperCase() === "BOND") {
|
|
469
482
|
assignField(resolved, provenance, "instrumentType", priceProvider);
|
|
470
483
|
}
|
|
471
484
|
|
|
@@ -474,6 +487,7 @@ export function resolveCanonicalQuote(
|
|
|
474
487
|
providerId: priceProvider?.providerId ?? contributions[0]!.providerId,
|
|
475
488
|
price: Number(resolved.price ?? priceProvider?.price ?? 0),
|
|
476
489
|
priceBasis: priceProvider?.priceBasis,
|
|
490
|
+
priceObservation: priceProvider?.priceObservation,
|
|
477
491
|
// The price's own source unit, like its basis; another provider's divisor does not apply.
|
|
478
492
|
...(typeof priceProvider?.providerPriceDivisor === "number" ? { providerPriceDivisor: priceProvider.providerPriceDivisor } : {}),
|
|
479
493
|
currency: String(resolved.currency ?? priceProvider?.currency ?? contributions[0]!.currency ?? ""),
|
|
@@ -482,6 +496,8 @@ export function resolveCanonicalQuote(
|
|
|
482
496
|
previousClose: resolved.previousClose as Quote["previousClose"],
|
|
483
497
|
regularClose: priceProvider?.regularClose,
|
|
484
498
|
regularCloseSessionDate: priceProvider?.regularClose != null ? priceProvider.regularCloseSessionDate : undefined,
|
|
499
|
+
regularChange: priceProvider?.regularClose != null ? priceProvider.regularChange : undefined,
|
|
500
|
+
regularChangePercent: priceProvider?.regularClose != null ? priceProvider.regularChangePercent : undefined,
|
|
485
501
|
changeSessionDate: resolved.changeSessionDate as Quote["changeSessionDate"],
|
|
486
502
|
high52w: resolved.high52w as Quote["high52w"],
|
|
487
503
|
low52w: resolved.low52w as Quote["low52w"],
|
|
@@ -5,6 +5,7 @@ import { buildQuoteKey, resolveEntryData } from "../selectors";
|
|
|
5
5
|
import type { QueryEntry } from "../result-types";
|
|
6
6
|
import { resolveCurrencyUnit } from "../../utils/currency-units";
|
|
7
7
|
import { isFiniteNumber } from "../../utils/guards";
|
|
8
|
+
import { getCompletedRegularSessionDisplay, getSessionMoveDisplay, type ExtendedSession } from "../market/status";
|
|
8
9
|
|
|
9
10
|
const STREAM_FRESHNESS_MS = 2 * 60_000;
|
|
10
11
|
const STREAM_CONNECTING_GRACE_MS = 15_000;
|
|
@@ -20,6 +21,24 @@ export interface ScreenerQuoteRow {
|
|
|
20
21
|
exchange: string;
|
|
21
22
|
lastUpdated?: number;
|
|
22
23
|
previousClose?: number;
|
|
24
|
+
/** Set when the price and move are an open pre-market or after-hours session's (see `extendedSessions`). */
|
|
25
|
+
extendedSession?: ExtendedSession;
|
|
26
|
+
/** Set when the price and move are the last completed regular session's close and its move; the move, in percent. */
|
|
27
|
+
regularChangePercent?: number | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ScreenerQuoteOverlayOptions {
|
|
31
|
+
/**
|
|
32
|
+
* While a pre-market or after-hours session is open, show its price and its
|
|
33
|
+
* move from the regular close, marked with `extendedSession`; otherwise the
|
|
34
|
+
* regular session's, which stops at the close. Without it a row shows the
|
|
35
|
+
* live price against the previous close. A row that is a completed regular
|
|
36
|
+
* session's close and move (`regularChangePercent`) keeps them outside the
|
|
37
|
+
* regular session when its quote has neither an open extended print nor
|
|
38
|
+
* that session's close of its own, so its price and move never come from
|
|
39
|
+
* two different days.
|
|
40
|
+
*/
|
|
41
|
+
extendedSessions?: boolean;
|
|
23
42
|
}
|
|
24
43
|
|
|
25
44
|
export interface ScreenerQuoteFreshness {
|
|
@@ -64,12 +83,14 @@ function overlayCurrency(row: ScreenerQuoteRow, quote: Quote): string {
|
|
|
64
83
|
* entries map, but most rows' quotes did not move; returning the same row
|
|
65
84
|
* object for those lets memoized table rows skip the render.
|
|
66
85
|
*/
|
|
67
|
-
const overlayCache = new WeakMap<object, { quote: Quote; row: ScreenerQuoteRow }>();
|
|
86
|
+
const overlayCache = new WeakMap<object, { quote: Quote; extended: boolean; row: ScreenerQuoteRow }>();
|
|
68
87
|
|
|
69
88
|
export function overlayScreenerQuoteEntries<T extends ScreenerQuoteRow>(
|
|
70
89
|
rows: readonly T[],
|
|
71
90
|
entries: ReadonlyMap<string, QueryEntry<Quote>>,
|
|
91
|
+
options: ScreenerQuoteOverlayOptions = {},
|
|
72
92
|
): T[] {
|
|
93
|
+
const extended = options.extendedSessions === true;
|
|
73
94
|
return rows.map((row) => {
|
|
74
95
|
const quote = resolveEntryData(entries.get(quoteKey(row)));
|
|
75
96
|
if (!quote || !isFiniteNumber(quote.price)) return row;
|
|
@@ -81,9 +102,9 @@ export function overlayScreenerQuoteEntries<T extends ScreenerQuoteRow>(
|
|
|
81
102
|
return row;
|
|
82
103
|
}
|
|
83
104
|
const cached = overlayCache.get(row);
|
|
84
|
-
if (cached?.quote === quote) return cached.row as T;
|
|
85
|
-
const overlaid = overlayQuote(row, quote);
|
|
86
|
-
overlayCache.set(row, { quote, row: overlaid });
|
|
105
|
+
if (cached?.quote === quote && cached.extended === extended) return cached.row as T;
|
|
106
|
+
const overlaid = extended ? overlaySessionQuote(row, quote) : overlayQuote(row, quote);
|
|
107
|
+
overlayCache.set(row, { quote, extended, row: overlaid });
|
|
87
108
|
return overlaid;
|
|
88
109
|
});
|
|
89
110
|
}
|
|
@@ -101,6 +122,30 @@ function overlayQuote<T extends ScreenerQuoteRow>(row: T, quote: Quote): T {
|
|
|
101
122
|
currency: overlayCurrency(row, quote),
|
|
102
123
|
previousClose: isFiniteNumber(quote.previousClose) ? quote.previousClose : undefined,
|
|
103
124
|
lastUpdated: quote.lastUpdated,
|
|
125
|
+
// The streamed price and move are no longer the snapshot's completed session.
|
|
126
|
+
...(row.regularChangePercent !== undefined ? { regularChangePercent: null } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function overlaySessionQuote<T extends ScreenerQuoteRow>(row: T, quote: Quote): T {
|
|
131
|
+
const overlaid = overlayQuote(row, quote);
|
|
132
|
+
const display = getSessionMoveDisplay(quote);
|
|
133
|
+
if (!display || !isFiniteNumber(display.price)) return overlaid;
|
|
134
|
+
if (
|
|
135
|
+
!display.session
|
|
136
|
+
&& row.regularChangePercent != null
|
|
137
|
+
&& quote.marketState != null
|
|
138
|
+
&& quote.marketState !== "REGULAR"
|
|
139
|
+
&& !getCompletedRegularSessionDisplay(quote)
|
|
140
|
+
) {
|
|
141
|
+
return { ...overlaid, price: row.price, change: row.change, changePercent: row.changePercent, regularChangePercent: row.regularChangePercent };
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
...overlaid,
|
|
145
|
+
price: display.price,
|
|
146
|
+
change: isFiniteNumber(display.change) ? display.change : null,
|
|
147
|
+
changePercent: isFiniteNumber(display.changePercent) ? display.changePercent : null,
|
|
148
|
+
extendedSession: display.session,
|
|
104
149
|
};
|
|
105
150
|
}
|
|
106
151
|
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { Quote } from "../../types/financials";
|
|
2
|
+
import { getLanguage, tf } from "../../i18n";
|
|
3
|
+
import { dailyNavDate } from "./freshness";
|
|
4
|
+
|
|
5
|
+
/** The published NAV session date, independent of local time and receipt time. */
|
|
6
|
+
export function formatQuoteNavAsOf(quote: Quote | null | undefined, now = Date.now()): string | null {
|
|
7
|
+
const date = quote && dailyNavDate(quote, now);
|
|
8
|
+
if (!date) return null;
|
|
9
|
+
const sourceDate = new Date(`${date}T00:00:00Z`);
|
|
10
|
+
const dateText = new Intl.DateTimeFormat(getLanguage(), {
|
|
11
|
+
timeZone: "UTC",
|
|
12
|
+
month: "short",
|
|
13
|
+
day: "numeric",
|
|
14
|
+
...(sourceDate.getUTCFullYear() !== new Date(now).getUTCFullYear() ? { year: "numeric" as const } : {}),
|
|
15
|
+
}).format(sourceDate);
|
|
16
|
+
return tf("NAV · as of {date}", { date: dateText });
|
|
17
|
+
}
|
|
2
18
|
|
|
3
19
|
function clampQuoteTimestamp(lastUpdated: number | undefined, now = Date.now()): number | null {
|
|
4
20
|
if (typeof lastUpdated !== "number" || !Number.isFinite(lastUpdated) || lastUpdated <= 0) {
|
|
@@ -22,11 +22,13 @@ import {
|
|
|
22
22
|
ScrollBox,
|
|
23
23
|
Textarea,
|
|
24
24
|
TextAttributes,
|
|
25
|
+
useUiCapabilities,
|
|
25
26
|
type ScrollBoxRenderable,
|
|
26
27
|
type TextareaRenderable,
|
|
27
28
|
} from "../../../ui";
|
|
28
29
|
import { useDialog, type AlertContext } from "../../../ui/dialog";
|
|
29
30
|
import type { SelectControl } from "../../../components/ui/select-button";
|
|
31
|
+
import { FRAMED_TEXTAREA_DESKTOP_STYLE } from "../../../components/ui/fields";
|
|
30
32
|
import { apiClient, type AccountProfile, type CloudPricing } from "../../../api-client";
|
|
31
33
|
import { chatController } from "../chat/controller";
|
|
32
34
|
import { SignInWall } from "../cloud/auth-actions";
|
|
@@ -220,6 +222,7 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
|
|
|
220
222
|
setActiveField(ACCOUNT_TAB_FIELD_ORDER[tab][0] ?? "username");
|
|
221
223
|
}), []);
|
|
222
224
|
const syncStatus = useCloudSyncStatus();
|
|
225
|
+
const { nativePaneChrome } = useUiCapabilities();
|
|
223
226
|
const bioRef = useRef<TextareaRenderable | null>(null);
|
|
224
227
|
const portfolioSelectRef = useRef<SelectControl | null>(null);
|
|
225
228
|
const refreshedSyncRevisionRef = useRef<number | null>(null);
|
|
@@ -821,6 +824,7 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
|
|
|
821
824
|
backgroundColor={colors.panel}
|
|
822
825
|
flexGrow={1}
|
|
823
826
|
wrapText
|
|
827
|
+
{...(nativePaneChrome ? { style: FRAMED_TEXTAREA_DESKTOP_STYLE } : {})}
|
|
824
828
|
onInput={(value: string) => setDraftValue("bio", value)}
|
|
825
829
|
/>
|
|
826
830
|
</Box>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { withConfigData } from "../../../cli/scoped-context";
|
|
2
2
|
import { CLI_COMMAND_GROUPS } from "../../../cli/help";
|
|
3
3
|
import { dryRunNote } from "../../../cli/helpers";
|
|
4
|
-
import { requireArg } from "../../../cli/commands/command-utils";
|
|
4
|
+
import { requireArg, takeOption } from "../../../cli/commands/command-utils";
|
|
5
|
+
import { EXCHANGE_OPTION, requireListingArg } from "../../../cli/listing-arg";
|
|
5
6
|
import { saveConfig } from "../../../data/config/store";
|
|
6
7
|
import type { CliCommandDef } from "../../../types/plugin";
|
|
7
8
|
import { cliStyles } from "../../../utils/cli-output";
|
|
@@ -18,10 +19,14 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
18
19
|
help: {
|
|
19
20
|
group: CLI_COMMAND_GROUPS.portfolios,
|
|
20
21
|
usage: ["alerts list", "alerts add <symbol> <above|below|crosses> <price>", "alerts delete <id>", "alerts rearm <id>"],
|
|
21
|
-
|
|
22
|
+
options: [EXCHANGE_OPTION],
|
|
23
|
+
examples: ["alerts", "alerts add AAPL above 250", "alerts add SAN:EPA below 70", "alerts add BTC-USD below 60000"],
|
|
22
24
|
},
|
|
23
|
-
execute: async (
|
|
25
|
+
execute: async (rawArgs, ctx) => {
|
|
26
|
+
const args = [...rawArgs];
|
|
27
|
+
const exchange = takeOption(args, "--exchange");
|
|
24
28
|
const action = args[0] ?? "list";
|
|
29
|
+
const listing = action === "add" && args[1] ? await requireListingArg(args[1], exchange, ctx) : null;
|
|
25
30
|
await withConfigData(ctx, async (config) => {
|
|
26
31
|
const raw = config.config.pluginConfig[ALERTS_PLUGIN_ID]?.[ALERTS_KEY];
|
|
27
32
|
const alerts = deserializeAlerts(typeof raw === "string" ? raw : "[]");
|
|
@@ -56,12 +61,12 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
56
61
|
return;
|
|
57
62
|
}
|
|
58
63
|
if (action === "add") {
|
|
59
|
-
const symbol = requireArg(
|
|
64
|
+
const symbol = requireArg(listing?.key, "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx);
|
|
60
65
|
const condition = requireArg(args[2], "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx) as AlertCondition;
|
|
61
66
|
if (!["above", "below", "crosses"].includes(condition)) ctx.fail("Condition must be above, below, or crosses.");
|
|
62
67
|
const price = Number(requireArg(args[3], "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>", ctx));
|
|
63
68
|
if (!Number.isFinite(price)) ctx.fail("Alert price must be a finite number.");
|
|
64
|
-
const alert = createAlert(symbol, condition, price);
|
|
69
|
+
const alert = createAlert(symbol, condition, price, listing?.exchange);
|
|
65
70
|
await saveAlerts([...alerts, alert]);
|
|
66
71
|
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert } }, {
|
|
67
72
|
text: (data) => `Added alert ${data.alert.id}: ${symbol} ${condition} ${price}.${dryRunNote(data.dryRun)}`,
|
|
@@ -33,13 +33,18 @@ export function syntheticPositionUnsupportedReason(ticker: TickerRecord, quoteCu
|
|
|
33
33
|
if (positions.some((position) => position.side === "short" || position.shares < 0)) {
|
|
34
34
|
return "Short positions: signed exposure history required";
|
|
35
35
|
}
|
|
36
|
+
const category = (ticker.metadata.assetCategory ?? "").toUpperCase();
|
|
37
|
+
// Crypto trades every day at another close, so its daily returns do not pair with the equity factor proxies.
|
|
38
|
+
if (category.startsWith("CRYPTO") || /-[A-Z]{3,4}$/.test(ticker.metadata.ticker)) {
|
|
39
|
+
return "Crypto is not covered by the equity basket";
|
|
40
|
+
}
|
|
36
41
|
// "Common Stock" is the category provider search hands a ticker added from
|
|
37
42
|
// the command bar, so a plain equity must not read as unsupported.
|
|
38
43
|
const equityCategories = new Set(["", "STK", "STOCK", "COMMON STOCK", "EQUITY", "ETF", "ETN", "FUND", "ADR", "REIT"]);
|
|
39
|
-
if (!equityCategories.has(
|
|
44
|
+
if (!equityCategories.has(category)
|
|
40
45
|
|| positions.some((position) => position.multiplier != null && position.multiplier !== 1)
|
|
41
|
-
|| /=[A-Z]
|
|
42
|
-
return "
|
|
46
|
+
|| /=[A-Z]+$|\d{6}[CP]\d{8}$/.test(ticker.metadata.ticker)) {
|
|
47
|
+
return "Asset type is not covered by the equity basket";
|
|
43
48
|
}
|
|
44
49
|
if (quoteCurrency !== "USD") return "Foreign holdings: historical FX returns required";
|
|
45
50
|
return null;
|