gloomberb 0.16.4 → 0.16.5
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 +14 -91
- package/package.json +4 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
- package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
- package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/data.ts +24 -2
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +4 -1
- package/src/api-client/market-discovery.ts +10 -1
- package/src/api-client/paths.ts +7 -0
- package/src/api-client/research-activity.ts +15 -10
- package/src/api-client/types.ts +17 -1
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +15 -1
- package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
- package/src/architecture/core-builtin-allowlist.json +4 -13
- package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
- package/src/capabilities/chart-series.ts +11 -1
- package/src/cli/commands/crypto-hints.ts +7 -0
- package/src/cli/commands/fred-series.ts +61 -0
- package/src/cli/commands/market.ts +407 -104
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-leaps.ts +5 -2
- package/src/cli/commands/overview.ts +93 -20
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +82 -9
- package/src/cli/commands/system.ts +55 -8
- package/src/cli/commands/ticker.ts +88 -11
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +59 -7
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/freshness.ts +18 -7
- package/src/cli/glossary.ts +429 -0
- package/src/cli/history-rows.ts +48 -7
- package/src/cli/index-roots.ts +7 -0
- package/src/cli/index.ts +21 -7
- package/src/cli/known-gaps.ts +27 -0
- package/src/cli/listing-arg.ts +417 -56
- package/src/cli/not-a-ticker.ts +12 -5
- package/src/cli/pane-functions/access-gate.ts +5 -0
- package/src/cli/pane-functions/capabilities.ts +3 -2
- package/src/cli/pane-functions/catalog.ts +35 -5
- package/src/cli/pane-functions/data.ts +12 -8
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +20 -6
- package/src/cli/pane-functions/freshness.ts +363 -43
- package/src/cli/pane-functions/function-help.ts +12 -5
- package/src/cli/pane-functions/headless.ts +45 -17
- package/src/cli/pane-functions/index.ts +63 -10
- package/src/cli/pane-functions/market-session.ts +22 -8
- package/src/cli/pane-functions/options.ts +63 -22
- package/src/cli/pane-functions/report-notices.ts +25 -7
- package/src/cli/pane-functions/report.ts +15 -8
- package/src/cli/pane-functions/resolver.ts +49 -12
- package/src/cli/pane-functions/screenshot.ts +110 -23
- package/src/cli/report-tables.ts +3 -0
- package/src/cli/result.ts +4 -1
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +9 -0
- package/src/cli/symbol-list.ts +172 -0
- package/src/cli/ticker-resolution.ts +223 -30
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +6 -2
- package/src/components/chart/composite/composite-chart.tsx +40 -6
- package/src/components/chart/composite/legend.tsx +49 -2
- package/src/components/chart/composite/panel-surface.tsx +44 -17
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/workflow/tickers.ts +23 -4
- package/src/components/layout/detached-pane-shell.tsx +9 -0
- package/src/components/layout/floating-pane.tsx +3 -0
- package/src/components/layout/header.tsx +20 -5
- package/src/components/layout/pane/content.tsx +7 -13
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/model.ts +35 -1
- package/src/components/layout/pane/header.tsx +24 -1
- package/src/components/layout/pane/index.tsx +12 -2
- package/src/components/layout/pane/sidebar.tsx +78 -10
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/drag/index.ts +39 -9
- package/src/components/layout/shell/drag/live.ts +61 -51
- package/src/components/layout/shell/drag/runtime.ts +54 -12
- package/src/components/layout/shell/index.tsx +25 -7
- package/src/components/layout/shell/menu.ts +5 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/layers.tsx +192 -45
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/markdown-text.tsx +62 -20
- package/src/components/metric-treemap/heat-surface.tsx +10 -1
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +2 -2
- package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +24 -1
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +7 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +17 -6
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +12 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +2 -0
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/store/normalize.ts +7 -2
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7416 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +61 -4
- package/src/i18n/ja.ts +61 -4
- package/src/i18n/ko.ts +61 -4
- package/src/i18n/zh-cn.ts +61 -4
- package/src/i18n/zh-tw.ts +61 -4
- package/src/layout/desks.ts +1 -1
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +81 -18
- package/src/market-data/market/status.ts +19 -23
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +3 -0
- package/src/market-data/quotes/metadata.ts +7 -24
- package/src/market-data/quotes/resolution.ts +2 -0
- package/src/market-data/quotes/screener-live-quotes.ts +5 -4
- package/src/market-data/result-types.ts +2 -0
- package/src/plugins/builtin/account-management/model.ts +1 -56
- package/src/plugins/builtin/account-management/pane.tsx +6 -4
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +41 -9
- package/src/plugins/builtin/alerts/pane.tsx +8 -0
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +4 -2
- package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +35 -5
- package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/pane.tsx +3 -1
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +70 -13
- package/src/plugins/builtin/cds/headless.ts +116 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +193 -31
- package/src/plugins/builtin/cds/pane.tsx +56 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
- package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless.ts +8 -3
- package/src/plugins/builtin/chart-composer/index.tsx +9 -2
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/controller/index.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
- package/src/plugins/builtin/cloud/askg/model.ts +27 -8
- package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +19 -5
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
- package/src/plugins/builtin/composite-plugins.ts +19 -13
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/credit-boards/index.tsx +2 -0
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
- package/src/plugins/builtin/credit-conditions/series.ts +26 -0
- package/src/plugins/builtin/credit-documents/headless.ts +31 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
- package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/pane.tsx +3 -1
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +70 -9
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +56 -25
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +101 -16
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/index.tsx +17 -8
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +36 -133
- package/src/plugins/builtin/holders/pane.tsx +11 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +26 -9
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/iv-history/client.ts +14 -4
- package/src/plugins/builtin/iv-history/headless.ts +7 -4
- package/src/plugins/builtin/iv-history/index.tsx +2 -0
- package/src/plugins/builtin/iv-history/model.ts +111 -7
- package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +28 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/macro-day/client.ts +44 -3
- package/src/plugins/builtin/macro-day/headless.ts +13 -10
- package/src/plugins/builtin/macro-day/model.ts +56 -10
- package/src/plugins/builtin/macro-day/pane.tsx +30 -20
- package/src/plugins/builtin/market-heatmap/model.ts +19 -11
- package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/index.tsx +3 -2
- package/src/plugins/builtin/market-movers/model.ts +30 -0
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +7 -2
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +3 -6
- package/src/plugins/builtin/mna/headless.ts +15 -3
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +1 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +2 -1
- package/src/plugins/builtin/options-scenario/headless.ts +1 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
- package/src/plugins/builtin/perps/client.ts +40 -6
- package/src/plugins/builtin/perps/equity-row.tsx +33 -11
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +45 -30
- package/src/plugins/builtin/perps/index.tsx +18 -1
- package/src/plugins/builtin/perps/model.ts +20 -5
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
- package/src/plugins/builtin/plugin-module.ts +3 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
- package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +6 -2
- package/src/plugins/builtin/power/headless.ts +71 -7
- package/src/plugins/builtin/power/index.tsx +4 -1
- package/src/plugins/builtin/power/model.ts +72 -1
- package/src/plugins/builtin/power/pane.tsx +32 -9
- package/src/plugins/builtin/rate-path/headless.ts +11 -3
- package/src/plugins/builtin/realized-vol/headless.ts +5 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/series-inventory.ts +91 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +30 -12
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +9 -0
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +2 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/short-volume/index.tsx +2 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +1 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
- package/src/plugins/builtin/vol-surface/headless.ts +1 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +2 -1
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +3 -1
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/registry/index.ts +89 -4
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/remote/app-host.tsx +20 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +123 -6
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +62 -0
- package/src/remote/types.ts +30 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +27 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/box.tsx +3 -0
- package/src/renderers/dom/host/chart-surface.tsx +6 -2
- package/src/renderers/dom/host/live-frame.ts +6 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/size-observer.ts +26 -0
- package/src/renderers/dom/host/surface-3d.tsx +7 -2
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/styles.css +61 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +107 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +145 -0
- package/src/series/validate.ts +171 -0
- package/src/sources/gloomberb-cloud/index.ts +7 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
- package/src/sources/history-result.ts +1 -0
- package/src/sources/listing-symbols.ts +4 -1
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +1 -1
- package/src/sources/provider-router/financial-routes.ts +1 -0
- package/src/sources/provider-router/financials.ts +13 -0
- package/src/sources/provider-router/history.ts +43 -25
- package/src/sources/provider-router/search.ts +1 -0
- package/src/sync/core-contributors.ts +1 -0
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +25 -0
- package/src/tickers/open-target.ts +3 -2
- package/src/tickers/search/index.ts +45 -7
- package/src/tickers/search/ranking.ts +33 -17
- package/src/tickers/search/types.ts +7 -1
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/resolve.ts +95 -16
- package/src/time-series/types.ts +4 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +6 -0
- package/src/types/data-provider.ts +5 -0
- package/src/types/financials.ts +52 -1
- package/src/types/headless.ts +61 -0
- package/src/types/plugin.ts +19 -0
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/host.tsx +18 -0
- package/src/ui/live-resize.ts +81 -0
- package/src/ui/live-stretch.ts +121 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/pane-fill.ts +11 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/cli-output.ts +7 -1
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +437 -52
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +107 -1
- package/src/utils/format.ts +16 -3
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +15 -5
- package/src/utils/price-history.ts +99 -42
- package/src/utils/sec-eps-basis.ts +24 -11
- package/src/utils/sec.ts +61 -7
- package/src/utils/utc-time.ts +51 -4
- package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
- /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
|
@@ -56,7 +56,8 @@ interface HistoryRequestDescriptor {
|
|
|
56
56
|
context?: MarketDataRequestContext;
|
|
57
57
|
cachePolicyKey: PriceHistoryCachePolicyKey;
|
|
58
58
|
missingProviderError?: string;
|
|
59
|
-
|
|
59
|
+
/** `fetchedAt` is when the cached copy was fetched. */
|
|
60
|
+
isCachedValueStale(value: PriceHistoryResult, fetchedAt: number): boolean;
|
|
60
61
|
isFetchedValueStale(value: PriceHistoryResult): boolean;
|
|
61
62
|
fetchBroker(candidate: BrokerCandidate): Promise<PricePoint[] | null>;
|
|
62
63
|
fetchProvider(provider: DataProvider): Promise<PriceHistoryResult | null>;
|
|
@@ -243,15 +244,20 @@ function mergeRequestHistoryGaps(
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
function normalizeRequestResult(value: PriceHistoryResult, request: HistoryRequestDescriptor): PriceHistoryResult {
|
|
246
|
-
|
|
247
|
+
// Only this router says a copy is behind, when it answers: neither a source
|
|
248
|
+
// nor a stored copy can carry that verdict in.
|
|
249
|
+
const { savedCopyBehind: _behind, ...kept } = value;
|
|
250
|
+
return { ...kept, points: normalizeRequestHistory(value.points, request),
|
|
247
251
|
// A public equity response cannot establish a broker contract's session.
|
|
248
252
|
...(request.context?.instrument ? { session: undefined } : {}),
|
|
249
253
|
};
|
|
250
254
|
}
|
|
251
255
|
|
|
252
|
-
|
|
256
|
+
/** A copy fetched at `fetchedAt`, now for a source's answer. */
|
|
257
|
+
function resultIsStale(value: PriceHistoryResult, symbol: string, exchange: string, fetchedAt: number, intervalMs?: number | null): boolean {
|
|
253
258
|
return isPriceHistoryStaleForCurrentWindow(value.points, Date.now(), {
|
|
254
259
|
symbol, exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
|
|
260
|
+
fetchedAt,
|
|
255
261
|
});
|
|
256
262
|
}
|
|
257
263
|
|
|
@@ -351,8 +357,8 @@ export class ProviderRouterHistoryRoutes {
|
|
|
351
357
|
context,
|
|
352
358
|
cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
|
|
353
359
|
missingProviderError: `No history provider available for ${ticker}`,
|
|
354
|
-
isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
|
|
355
|
-
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
|
|
360
|
+
isCachedValueStale: (value, fetchedAt) => intraday && resultIsStale(value, ticker, exchange, fetchedAt),
|
|
361
|
+
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, Date.now()),
|
|
356
362
|
fetchBroker: async (candidate) => candidate.broker.getPriceHistory
|
|
357
363
|
? candidate.broker.getPriceHistory(
|
|
358
364
|
ticker,
|
|
@@ -408,8 +414,8 @@ export class ProviderRouterHistoryRoutes {
|
|
|
408
414
|
context,
|
|
409
415
|
cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
|
|
410
416
|
missingProviderError: `No resolution-aware history provider available for ${ticker}`,
|
|
411
|
-
isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
|
|
412
|
-
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
|
|
417
|
+
isCachedValueStale: (value, fetchedAt) => intraday && resultIsStale(value, ticker, exchange, fetchedAt, intervalMs),
|
|
418
|
+
isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, Date.now(), intervalMs),
|
|
413
419
|
fetchBroker: async (candidate) => candidate.broker.getPriceHistoryForResolution
|
|
414
420
|
? candidate.broker.getPriceHistoryForResolution(
|
|
415
421
|
ticker,
|
|
@@ -523,10 +529,10 @@ export class ProviderRouterHistoryRoutes {
|
|
|
523
529
|
interval: barSize,
|
|
524
530
|
context,
|
|
525
531
|
cachePolicyKey: intervalMs != null && intervalMs >= 24 * 60 * 60 * 1000 ? "priceHistoryDaily" : "priceHistoryIntraday",
|
|
526
|
-
isCachedValueStale: (value) => currentWindowAtLookup
|
|
527
|
-
&& resultIsStale(value, ticker, exchange, intervalMs),
|
|
532
|
+
isCachedValueStale: (value, fetchedAt) => currentWindowAtLookup
|
|
533
|
+
&& resultIsStale(value, ticker, exchange, fetchedAt, intervalMs),
|
|
528
534
|
isFetchedValueStale: (value) => isCurrentHistoryWindow(endDate)
|
|
529
|
-
&& resultIsStale(value, ticker, exchange, intervalMs),
|
|
535
|
+
&& resultIsStale(value, ticker, exchange, Date.now(), intervalMs),
|
|
530
536
|
fetchBroker: async (candidate) => candidate.broker.getDetailedPriceHistory
|
|
531
537
|
? candidate.broker.getDetailedPriceHistory(
|
|
532
538
|
ticker,
|
|
@@ -604,7 +610,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
604
610
|
const withReportedGaps = (value: PriceHistoryResult) => historyCoverage(request).merge(value, reportedGaps);
|
|
605
611
|
const clip = (value: PriceHistoryResult) => request.requestedRange
|
|
606
612
|
? clipHistoryToRange(value, request.requestedRange, request.target.exchange) : value;
|
|
607
|
-
const cachedHistoryStale = request.isCachedValueStale(cachedValue);
|
|
613
|
+
const cachedHistoryStale = request.isCachedValueStale(cachedValue, cached?.fetchedAt ?? Number.NEGATIVE_INFINITY);
|
|
608
614
|
const forceRefresh = request.context?.cacheMode === "refresh";
|
|
609
615
|
// A recent re-check, even a failed, empty or rejected one, defers the next
|
|
610
616
|
// one; it does not make the copy current. A broader copy only tells what
|
|
@@ -613,8 +619,10 @@ export class ProviderRouterHistoryRoutes {
|
|
|
613
619
|
request.exactCacheVariantKeys.includes(cached.variantKey) ? cached.fetchedAt : Number.NEGATIVE_INFINITY)) : "current";
|
|
614
620
|
const cachedBeforeClose = servedState === "unsettled" || servedState === "recheck";
|
|
615
621
|
if (cachedBeforeClose) this.markCalendarChecked(request, now);
|
|
616
|
-
|
|
617
|
-
|
|
622
|
+
// A copy that a re-check since the close could not replace answers until
|
|
623
|
+
// the next one is due, though it does not hold the whole session.
|
|
624
|
+
const usableCached = hasUsablePriceHistory(cachedValue.points) && cached && !cached.expired
|
|
625
|
+
&& (!cachedHistoryStale || servedState === "pending") && !cachedBeforeClose;
|
|
618
626
|
// A copy behind or before the latest session is re-checked on the paced
|
|
619
627
|
// schedule above, not on its short TTL: the source is likely to answer
|
|
620
628
|
// the same. A short-lived process (the CLI) closes its store before a
|
|
@@ -627,9 +635,11 @@ export class ProviderRouterHistoryRoutes {
|
|
|
627
635
|
if (refreshDue) {
|
|
628
636
|
scheduleRouterRevalidation(this.historyRefreshInFlight, request.identity.revalidationKey, () => this.refreshHistory(request));
|
|
629
637
|
}
|
|
638
|
+
// Only a copy a re-check could not replace reaches here short of the latest session.
|
|
639
|
+
const answered = cachedHistoryStale ? { ...cachedValue, savedCopyBehind: true as const } : cachedValue;
|
|
630
640
|
return exactHit || !request.requestedRange
|
|
631
|
-
? withReportedGaps(
|
|
632
|
-
: clip(withReportedGaps(
|
|
641
|
+
? withReportedGaps(answered)
|
|
642
|
+
: clip(withReportedGaps(answered));
|
|
633
643
|
}
|
|
634
644
|
|
|
635
645
|
const supersededCacheSources = new Set<string>();
|
|
@@ -651,13 +661,21 @@ export class ProviderRouterHistoryRoutes {
|
|
|
651
661
|
if (providerResult && hasUsablePriceHistory(providerResult.value.points)) return withReportedGaps(providerResult.value);
|
|
652
662
|
if (providerResult) reportedGaps.push(providerResult.value);
|
|
653
663
|
// A failed refetch falls back in the same order the cache hit used, so a
|
|
654
|
-
// failing request answers what the next request will
|
|
655
|
-
|
|
664
|
+
// failing request answers what the next request will: a current copy
|
|
665
|
+
// first, else a saved copy short of the latest session, which beats an
|
|
666
|
+
// empty chart. Intraday copies expire two days after they are saved. A
|
|
667
|
+
// coin never closes, so its old copy is not shown: the miss says why.
|
|
668
|
+
const coin = isRoundTheClockCoin(target.symbol, target.exchange || request.target.exchange);
|
|
669
|
+
const fallbackRecords = ranked
|
|
656
670
|
.map((entry) => entry.record)
|
|
657
|
-
.filter((record) => !supersededCacheSources.has(record.sourceKey))
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (
|
|
671
|
+
.filter((record) => !supersededCacheSources.has(record.sourceKey));
|
|
672
|
+
const fallbackRecord = fallbackRecords.find((record) => !request.isCachedValueStale(record.value, record.fetchedAt))
|
|
673
|
+
?? (coin ? undefined : fallbackRecords[0]);
|
|
674
|
+
if (fallbackRecord) {
|
|
675
|
+
// A copy short of the latest session says so, so the chart can tell it
|
|
676
|
+
// from a current one: a thin listing's complete copy is not behind.
|
|
677
|
+
const fallbackValue = request.isCachedValueStale(fallbackRecord.value, fallbackRecord.fetchedAt)
|
|
678
|
+
? { ...fallbackRecord.value, savedCopyBehind: true as const } : fallbackRecord.value;
|
|
661
679
|
return request.requestedRange
|
|
662
680
|
? clip(withReportedGaps(fallbackValue))
|
|
663
681
|
: withReportedGaps(fallbackValue);
|
|
@@ -673,11 +691,11 @@ export class ProviderRouterHistoryRoutes {
|
|
|
673
691
|
}
|
|
674
692
|
if (!providerResult && request.missingProviderError && !reportedGaps.some((value) => value.points.length > 0)) {
|
|
675
693
|
// A source answered a coin's history but it stops short of the present: say so, not that nothing answered.
|
|
676
|
-
const behind =
|
|
677
|
-
&& [...attempts.outcomes.values()].some((outcome) => outcome.outcome === "stale");
|
|
694
|
+
const behind = coin && [...attempts.outcomes.values()].some((outcome) => outcome.outcome === "stale");
|
|
678
695
|
if (behind) throw new Error(`Latest history for ${target.symbol} is behind`);
|
|
679
|
-
//
|
|
680
|
-
|
|
696
|
+
// A reason the service gave ("ADX listings have daily bars only.") says why; one that has no
|
|
697
|
+
// listing for the symbol says it is not a ticker; otherwise no source answered.
|
|
698
|
+
throw noProviderError(request.missingProviderError, attempts.misses, request.target.symbol);
|
|
681
699
|
}
|
|
682
700
|
return withReportedGaps(providerResult?.value ?? { points: [], resolution: historyResolutionForInterval(request.interval) });
|
|
683
701
|
}
|
|
@@ -107,6 +107,7 @@ function sanitizePortfolio(portfolio: Portfolio): Portfolio {
|
|
|
107
107
|
// them would erase them on this device.
|
|
108
108
|
...(portfolio.cash ? { cash: { ...portfolio.cash } } : {}),
|
|
109
109
|
...(portfolio.targetWeights ? { targetWeights: { ...portfolio.targetWeights } } : {}),
|
|
110
|
+
...(portfolio.importedFrom ? { importedFrom: { ...portfolio.importedFrom } } : {}),
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Company names as venues spell them, compared and shortened for sentences.
|
|
3
|
+
* One company's listings carry its name with different legal forms and share
|
|
4
|
+
* wording ("Microsoft Corporation Common Stock", "Coca-Cola Company CDR"), so
|
|
5
|
+
* they are compared by the words that say whose they are.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Words that differ between one company's listings: legal forms, and how a depositary receipt is named.
|
|
9
|
+
const NAME_NOISE = new Set([
|
|
10
|
+
"co", "company", "corp", "corporation", "inc", "incorporated", "ltd", "limited", "llc", "lp", "plc", "sa", "ag", "nv", "se",
|
|
11
|
+
"spa", "ab", "as", "asa", "oyj", "bv", "gmbh", "kgaa", "bhd", "berhad", "pcl", "the", "adr", "ads", "cedear", "cedears",
|
|
12
|
+
"sponsored", "unsponsored", "cdr", "cdi",
|
|
13
|
+
]);
|
|
14
|
+
// Words a venue adds to the share it lists ("Microsoft Corporation Common Stock"), never part of whose it is.
|
|
15
|
+
const SHARE_NOISE = new Set(["common", "ordinary", "stock", "shares"]);
|
|
16
|
+
|
|
17
|
+
/** A company's name for comparing its listings: "Kotobuki Spirits Co., Ltd." is "kotobuki spirits". */
|
|
18
|
+
function companyKey(name: string): string {
|
|
19
|
+
return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase()
|
|
20
|
+
.replace(/\([^)]*\)/g, " ")
|
|
21
|
+
.replace(/\b(?:(?:american|global) )?depositary (?:shares?|receipts?)\b/g, " ")
|
|
22
|
+
.replace(/\./g, "")
|
|
23
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
24
|
+
.filter((word) => word && !NAME_NOISE.has(word))
|
|
25
|
+
.join(" ");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function companyWords(name: string | null | undefined): Set<string> {
|
|
29
|
+
return new Set(companyKey(name ?? "").split(" ").filter((word) => word && !SHARE_NOISE.has(word)));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether two names are one company's, however each venue spells it: one
|
|
34
|
+
* name's words are all in the other's, so "SPDR S&P 500 ETF Trust" is State
|
|
35
|
+
* Street's, "Coca-Cola Company CDR" Coca-Cola's, and "CAR Group" not Avis
|
|
36
|
+
* Budget Group. An empty name matches none.
|
|
37
|
+
*/
|
|
38
|
+
export function sameCompany(left: string | null | undefined, right: string | null | undefined): boolean {
|
|
39
|
+
const a = companyWords(left);
|
|
40
|
+
const b = companyWords(right);
|
|
41
|
+
if (a.size === 0 || b.size === 0) return false;
|
|
42
|
+
const [fewer, more] = a.size <= b.size ? [a, b] : [b, a];
|
|
43
|
+
return [...fewer].every((word) => more.has(word));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Items grouped by the company their names are, in the order each company first appears. Unnamed items are left out. */
|
|
47
|
+
export function groupByCompany<T>(items: readonly T[], nameOf: (item: T) => string | null | undefined): T[][] {
|
|
48
|
+
const groups: T[][] = [];
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
if (companyWords(nameOf(item)).size === 0) continue;
|
|
51
|
+
const group = groups.find((members) => members.some((member) => sameCompany(nameOf(member), nameOf(item))));
|
|
52
|
+
if (group) group.push(item);
|
|
53
|
+
else groups.push([item]);
|
|
54
|
+
}
|
|
55
|
+
return groups;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A company's name without its legal form, for sentences: "Avis Budget Group, Inc." is "Avis Budget Group". */
|
|
59
|
+
export function shortCompanyName(name: string): string {
|
|
60
|
+
const words = name.trim().split(/\s+/).filter(Boolean);
|
|
61
|
+
const legal = (word: string) => {
|
|
62
|
+
const plain = word.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^\p{L}\p{N}]/gu, "");
|
|
63
|
+
// "Public Limited Company" is a legal form too, though "Public" alone can be a name.
|
|
64
|
+
return NAME_NOISE.has(plain) || SHARE_NOISE.has(plain) || (plain === "public" && words.length > 2);
|
|
65
|
+
};
|
|
66
|
+
while (words.length > 1 && legal(words[words.length - 1]!)) words.pop();
|
|
67
|
+
if (words.length > 1 && words[0]!.toLowerCase() === "the") words.shift();
|
|
68
|
+
return words.join(" ").replace(/[,;]+$/, "");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** "CAR Group (ASX)": a listing named by its company without the legal form, for a sentence about the company. */
|
|
72
|
+
export function shortListingTitle(listing: { symbol: string; exchange: string; name: string | null }): string {
|
|
73
|
+
const name = listing.name?.trim() ? shortCompanyName(listing.name) : listing.symbol;
|
|
74
|
+
return listing.exchange ? `${name} (${listing.exchange})` : name;
|
|
75
|
+
}
|
|
@@ -130,3 +130,28 @@ export function resolveTickerInstrumentKind(
|
|
|
130
130
|
?? kindFromListing(ticker)
|
|
131
131
|
?? "equity";
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A coin-typed listing named "<asset> (Derivatives)", such as NATGAS-USD
|
|
136
|
+
* "Natural Gas (Derivatives) USD": the price perpetual futures on that asset
|
|
137
|
+
* trade at on crypto venues, around the clock. It is neither the asset's
|
|
138
|
+
* exchange-listed future nor its spot price, though its type reads as a coin.
|
|
139
|
+
*/
|
|
140
|
+
export function isCryptoVenuePerpetual(type: string | null | undefined, name: string | null | undefined): boolean {
|
|
141
|
+
return classifyInstrumentType(type) === "crypto" && /\(derivatives\)/i.test(name ?? "");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A listing's type, in plain words where the provider's type misleads (a
|
|
146
|
+
* perpetual typed as a coin). `detail` says what it is not, for a report line.
|
|
147
|
+
*/
|
|
148
|
+
export function instrumentTypeLabel(
|
|
149
|
+
type: string | null | undefined,
|
|
150
|
+
name: string | null | undefined,
|
|
151
|
+
options: { detail?: boolean } = {},
|
|
152
|
+
): string | undefined {
|
|
153
|
+
if (isCryptoVenuePerpetual(type, name)) {
|
|
154
|
+
return options.detail ? "Perpetual futures price (24/7 on crypto venues; not the listed future, not spot)" : "Perpetual futures price";
|
|
155
|
+
}
|
|
156
|
+
return type?.trim() || undefined;
|
|
157
|
+
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { tickerSelectionFromSearchResult } from "./selection";
|
|
12
12
|
import { normalizeTickerSymbol } from "./search/ranking";
|
|
13
13
|
import type { TickerOpenTarget } from "./search/types";
|
|
14
|
-
import { parsePublicTickerKey } from "../utils/exchanges";
|
|
14
|
+
import { isSharedExchangeCode, parsePublicTickerKey, typedExchangeCode } from "../utils/exchanges";
|
|
15
15
|
import { getListingSymbol, tickerHasListingSuffix } from "../sources/listing-symbols";
|
|
16
16
|
|
|
17
17
|
export type { TickerOpenTarget } from "./search/types";
|
|
@@ -34,7 +34,8 @@ export async function resolveTickerOpenTarget({
|
|
|
34
34
|
const symbol = normalizeTickerInput(null, query);
|
|
35
35
|
if (!symbol) return null;
|
|
36
36
|
const requested = parsePublicTickerKey(symbol);
|
|
37
|
-
|
|
37
|
+
// A code that names several venues (7203:TSE) opens the listing search settled on, under its own key.
|
|
38
|
+
const preserveListingKey = (!!requested.exchange && !isSharedExchangeCode(typedExchangeCode(symbol))) || tickerHasListingSuffix(symbol);
|
|
38
39
|
|
|
39
40
|
let resolved: Awaited<ReturnType<typeof resolveTickerSearch>> | null = null;
|
|
40
41
|
try {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { SearchRequestContext, DataProvider } from "../../types/data-provider";
|
|
2
2
|
import type { InstrumentSearchResult } from "../../types/instrument";
|
|
3
3
|
import type { TickerRecord } from "../../types/ticker";
|
|
4
|
-
import { canonicalExchange, parsePublicTickerKey, publicTickerKey } from "../../utils/exchanges";
|
|
4
|
+
import { canonicalExchange, isSharedExchangeCode, isUsListingExchange, parsePublicTickerKey, publicTickerKey, typedExchangeCode } from "../../utils/exchanges";
|
|
5
5
|
import { getListingSymbol, tickerHasListingSuffix } from "../../sources/listing-symbols";
|
|
6
6
|
import { parseOptionSymbol } from "../../utils/options";
|
|
7
7
|
import { resolveCurrencyUnit } from "../../utils/currency-units";
|
|
8
8
|
import { assetClassMarketSymbol, parseAssetClassQuery } from "./asset-classes";
|
|
9
|
+
import { bareFuturesCode, futuresContractByCode } from "../../market-data/futures-board";
|
|
9
10
|
import { searchContractKey, searchInstrumentKey } from "./identity";
|
|
10
11
|
import { tickerInstrumentLabel } from "../instrument-label";
|
|
11
12
|
import {
|
|
@@ -40,9 +41,21 @@ export {
|
|
|
40
41
|
} from "./ranking";
|
|
41
42
|
export { upsertTickerFromSearchResult } from "./upsert";
|
|
42
43
|
|
|
44
|
+
const MAX_NAMED_LISTINGS = 6;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Search rows a symbol's listings are read from, by the command bar's `SAN:`
|
|
48
|
+
* venue list and the CLI's venue list alike. A keystroke's ten stop short of a
|
|
49
|
+
* symbol several companies use: CAR's ASX line is its eleventh.
|
|
50
|
+
*/
|
|
51
|
+
export const LISTING_SEARCH_LIMIT = 30;
|
|
52
|
+
|
|
43
53
|
export class AmbiguousTickerError extends Error {
|
|
44
54
|
constructor(readonly query: string, readonly listings: readonly string[], readonly listingNames: Readonly<Record<string, string>> = {}) {
|
|
45
|
-
|
|
55
|
+
// Every listing is a choice: a list cut short could leave out the one meant.
|
|
56
|
+
const more = listings.length - MAX_NAMED_LISTINGS;
|
|
57
|
+
const named = `${listings.slice(0, MAX_NAMED_LISTINGS).join(", ")}${more > 0 ? ` or one of ${more} more in search` : ""}`;
|
|
58
|
+
super(`Multiple listings match ${query}. Choose an exchange in search or use ${named}.`);
|
|
46
59
|
this.name = "AmbiguousTickerError";
|
|
47
60
|
}
|
|
48
61
|
}
|
|
@@ -291,7 +304,10 @@ export function resolveTickerSearch(options: ResolveTickerSearchOptions): Promis
|
|
|
291
304
|
if (!requested) return Promise.resolve(null);
|
|
292
305
|
// `NET:` lists NET's venues instead of settling on one.
|
|
293
306
|
const pickListing = isListingPickerQuery(requested);
|
|
294
|
-
const
|
|
307
|
+
const typed = symbolSearchQuery(requested);
|
|
308
|
+
// A futures board code no security uses is its future (TTF is TTF=F), unless a saved ticker holds it.
|
|
309
|
+
const future = pickListing || options.tickers.has(typed) ? null : bareFuturesCode(typed);
|
|
310
|
+
const symbol = future?.symbol ?? typed;
|
|
295
311
|
const literal = resolveTickerSymbol(symbol, options, pickListing ? requested : null);
|
|
296
312
|
const alternatives = pickListing ? [] : dottedVenueQueries(symbol);
|
|
297
313
|
// Most input has no venue code to retry: hand back the literal lookup
|
|
@@ -346,6 +362,9 @@ async function resolveTickerSymbol(
|
|
|
346
362
|
publicTickerKey(item.symbol, listingExchange(item.result!)), item.result!.name,
|
|
347
363
|
])));
|
|
348
364
|
if (pickerQuery && listings.size > 1) throw ambiguity(pickerQuery);
|
|
365
|
+
// A code that names several venues (TSE) the symbol trades on more than one of is a choice.
|
|
366
|
+
if (!saved && isSharedExchangeCode(typedExchangeCode(symbol))
|
|
367
|
+
&& new Set(matches.map((item) => canonicalExchange(listingExchange(item.result!)))).size > 1) throw ambiguity(symbol);
|
|
349
368
|
if (saved) return saved;
|
|
350
369
|
if (listings.size > 1 && !parsePublicTickerKey(symbol).exchange && !tickerHasListingSuffix(symbol)) {
|
|
351
370
|
// Search order is relevance, not a canonical listing identifier. Align bare
|
|
@@ -360,14 +379,14 @@ async function resolveTickerSymbol(
|
|
|
360
379
|
const currency = resolveCurrencyUnit(quote.currency).currency;
|
|
361
380
|
if (exchange && currency && Number.isFinite(quote.price) && quote.price !== 0
|
|
362
381
|
&& Number.isFinite(quote.lastUpdated) && quote.lastUpdated > 0) {
|
|
363
|
-
|
|
382
|
+
const sameSecurity = matches.filter((item) => {
|
|
364
383
|
const result = item.result!;
|
|
365
384
|
const candidateCurrency = resolveCurrencyUnit(result.currency).currency;
|
|
366
|
-
return
|
|
367
|
-
&& (!candidateCurrency || candidateCurrency === currency)
|
|
385
|
+
return (!candidateCurrency || candidateCurrency === currency)
|
|
368
386
|
&& findExactTickerSearchMatch([{ label: quote.symbol, right: exchange, instrumentType: quote.instrumentType }], item.symbol)
|
|
369
387
|
&& (!isCryptoInstrumentType(result.type) || quote.price > 0);
|
|
370
|
-
})
|
|
388
|
+
});
|
|
389
|
+
verified = quotedListings(sameSecurity, exchange).map((item) => ({ ...item, result: { ...item.result!, currency } }));
|
|
371
390
|
}
|
|
372
391
|
} catch {
|
|
373
392
|
// An unavailable quote cannot establish the default listing.
|
|
@@ -376,6 +395,8 @@ async function resolveTickerSymbol(
|
|
|
376
395
|
throw ambiguity(symbol);
|
|
377
396
|
}
|
|
378
397
|
exactMatch = verified[0]!;
|
|
398
|
+
const chosen = publicTickerKey(exactMatch.symbol, listingExchange(exactMatch.result!));
|
|
399
|
+
return { kind: "provider", symbol: exactMatch.symbol, result: exactMatch.result!, otherListings: [...listings].filter((key) => key !== chosen) };
|
|
379
400
|
}
|
|
380
401
|
|
|
381
402
|
return {
|
|
@@ -385,6 +406,19 @@ async function resolveTickerSymbol(
|
|
|
385
406
|
};
|
|
386
407
|
}
|
|
387
408
|
|
|
409
|
+
/**
|
|
410
|
+
* The listings a bare symbol's own quote is on: those on the venue it names.
|
|
411
|
+
* The US exchanges share one symbol namespace, so a quote on any of them is
|
|
412
|
+
* the symbol's one US listing, whichever venue the feed labels it with (ET's
|
|
413
|
+
* NYSE line comes back as NYSE American).
|
|
414
|
+
*/
|
|
415
|
+
function quotedListings(items: TickerSearchCandidate[], exchange: string): TickerSearchCandidate[] {
|
|
416
|
+
const venue = (item: TickerSearchCandidate) => canonicalExchange(listingExchange(item.result!));
|
|
417
|
+
const named = items.filter((item) => venue(item) === exchange);
|
|
418
|
+
if (named.length > 0 || !isUsListingExchange(exchange)) return named;
|
|
419
|
+
return items.filter((item) => isUsListingExchange(venue(item)));
|
|
420
|
+
}
|
|
421
|
+
|
|
388
422
|
function listingExchange(result: InstrumentSearchResult): string {
|
|
389
423
|
return result.exchange === "SMART" ? result.primaryExchange || result.exchange
|
|
390
424
|
: result.exchange || result.primaryExchange || "";
|
|
@@ -455,6 +489,10 @@ async function searchProviderResults(
|
|
|
455
489
|
// number of spellings tried.
|
|
456
490
|
const searchQueries = buildProviderSearchQueries(query);
|
|
457
491
|
if (marketSymbol && !searchQueries.includes(marketSymbol)) searchQueries.push(marketSymbol);
|
|
492
|
+
// A futures board code also finds its future, which the catalogue files under
|
|
493
|
+
// the continuous symbol only: NG finds NovaGold and NG=F.
|
|
494
|
+
const boardFuture = assetClass ? null : futuresContractByCode(query)?.symbol;
|
|
495
|
+
if (boardFuture && !searchQueries.includes(boardFuture)) searchQueries.push(boardFuture);
|
|
458
496
|
await Promise.all(searchQueries.map(async (searchQuery) => {
|
|
459
497
|
try {
|
|
460
498
|
const results = await dataProvider.search(searchQuery, {
|
|
@@ -3,8 +3,10 @@ import type {
|
|
|
3
3
|
TickerSearchRankableItem,
|
|
4
4
|
} from "./types";
|
|
5
5
|
import { getListingSymbol, tickerHasListingSuffix } from "../../sources/listing-symbols";
|
|
6
|
-
import {
|
|
6
|
+
import { currencyPairCode } from "../../utils/currency-pair";
|
|
7
|
+
import { canonicalExchange, exchangeCodeVenues, parsePublicTickerKey, typedExchangeCode } from "../../utils/exchanges";
|
|
7
8
|
import { assetClassKeeps, assetClassMarketSymbol, instrumentClassCode, parseAssetClassQuery } from "./asset-classes";
|
|
9
|
+
import { futuresContractByCode } from "../../market-data/futures-board";
|
|
8
10
|
|
|
9
11
|
const FUND_TYPES = new Set(["ETF", "ETN", "ETP", "FUND", "MUTUALFUND", "CEF", "CLOSEDEND"]);
|
|
10
12
|
const DERIVATIVE_TYPES = new Set(["OPT", "OPTION", "OPTIONS", "FUT", "FUTURE", "FUTURES", "WARRANT", "WARRANTS", "RIGHT", "RIGHTS"]);
|
|
@@ -28,15 +30,15 @@ const COMPANY_NAME_SUFFIXES = new Set([
|
|
|
28
30
|
"SE",
|
|
29
31
|
]);
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
/** Words in a search that say where to look ("toyota tokyo"), each with the venue labels it matches. */
|
|
34
|
+
const VENUE_HINT_ALIASES: Record<string, string[]> = {
|
|
32
35
|
NASDAQ: ["NASDAQ", "NMS"],
|
|
33
36
|
NYSE: ["NYSE", "NYSE ARCA", "ARCA"],
|
|
34
37
|
AMEX: ["AMEX"],
|
|
35
38
|
TSX: ["TSX", "TORONTO"],
|
|
36
39
|
TORONTO: ["TORONTO", "TSX"],
|
|
37
40
|
XETRA: ["XETRA"],
|
|
38
|
-
|
|
39
|
-
TOKYO: ["TOKYO STOCK EXCHANGE", "TOKYO", "JPX", "TSE"],
|
|
41
|
+
TOKYO: ["TOKYO STOCK EXCHANGE", "TOKYO", "JPX"],
|
|
40
42
|
JPX: ["JPX", "TOKYO STOCK EXCHANGE", "TOKYO"],
|
|
41
43
|
LSE: ["LSE", "LONDON"],
|
|
42
44
|
HKEX: ["HKEX", "HONG KONG"],
|
|
@@ -46,6 +48,13 @@ const EXCHANGE_HINT_ALIASES: Record<string, string[]> = {
|
|
|
46
48
|
AIRES: ["BUENOS AIRES", "BUE"],
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
/** A code the venue list reads as several venues (TSE: Toronto or Tokyo) ranks a row on any of them. */
|
|
52
|
+
function sharedCodeHint(code: string): string[] {
|
|
53
|
+
return [code, ...exchangeCodeVenues(code).flatMap((venue) => VENUE_HINT_ALIASES[venue] ?? [venue])];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const EXCHANGE_HINT_ALIASES: Record<string, string[]> = { ...VENUE_HINT_ALIASES, TSE: sharedCodeHint("TSE") };
|
|
57
|
+
|
|
49
58
|
const ASSET_HINT_MAP: Record<string, TickerSearchInstrumentClass> = {
|
|
50
59
|
STOCK: "equity",
|
|
51
60
|
STK: "equity",
|
|
@@ -121,15 +130,8 @@ export function isExplicitMarketSymbol(symbol: string): boolean {
|
|
|
121
130
|
return /[=^]/.test(symbol) || /^[A-Z]{3}\/[A-Z]{3}$/i.test(symbol.trim());
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
function forexPair(symbol: string): string | null {
|
|
125
|
-
if (/^[A-Z]{3}\/[A-Z]{3}$/.test(symbol)) return symbol.replace("/", "");
|
|
126
|
-
if (/^[A-Z]{6}=X$/.test(symbol)) return symbol.slice(0, -2);
|
|
127
|
-
if (/^[A-Z]{3}=X$/.test(symbol)) return `USD${symbol.slice(0, -2)}`;
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
133
|
export function getForexQuoteCurrency(symbol: string): string | undefined {
|
|
132
|
-
return
|
|
134
|
+
return currencyPairCode(normalizeTickerSymbol(symbol))?.slice(3);
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
/** Venue and market syntax must survive exact matching. Only explicit FX forms are equivalent. */
|
|
@@ -145,12 +147,16 @@ function matchesQualifiedTicker(
|
|
|
145
147
|
const exchanges = [candidate.exchange, item.exchangeLabel, item.primaryExchangeLabel, item.right]
|
|
146
148
|
.filter((value): value is string => !!value);
|
|
147
149
|
if (requested.exchange) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
// EURUSD:CCY and EURUSD:FX name the pair listed as EURUSD=X on CCY.
|
|
151
|
+
const pair = currencyPairCode(requested.symbol, requested.exchange);
|
|
152
|
+
// A code that names several venues (TSE: Toronto or Tokyo) matches a row on any of them.
|
|
153
|
+
const venues = exchangeCodeVenues(typedExchangeCode(normalized));
|
|
154
|
+
return (candidate.symbol === requested.symbol || (pair != null && currencyPairCode(candidate.symbol) === pair))
|
|
155
|
+
&& exchanges.some((exchange) => venues.includes(canonicalExchange(exchange)));
|
|
150
156
|
}
|
|
151
157
|
if (isExplicitMarketSymbol(requested.symbol)) {
|
|
152
|
-
const pair =
|
|
153
|
-
return candidate.symbol === requested.symbol || (pair != null &&
|
|
158
|
+
const pair = currencyPairCode(requested.symbol);
|
|
159
|
+
return candidate.symbol === requested.symbol || (pair != null && currencyPairCode(candidate.symbol) === pair);
|
|
154
160
|
}
|
|
155
161
|
return exchanges.some((exchange) => getListingSymbol(candidate.symbol, exchange).toUpperCase() === normalized);
|
|
156
162
|
}
|
|
@@ -408,6 +414,16 @@ export function rankTickerSearchItems<T extends Pick<TickerSearchRankableItem, "
|
|
|
408
414
|
? deduped.findIndex((item) => parsePublicTickerKey(item.symbol || item.label).symbol === marketSymbol)
|
|
409
415
|
: -1;
|
|
410
416
|
if (marketIndex > 0) deduped.unshift(...deduped.splice(marketIndex, 1));
|
|
417
|
+
// A futures board code's future ranks with its code, not with the derivatives
|
|
418
|
+
// the word ranking puts last: first where no security uses the code (TTF),
|
|
419
|
+
// right after the security that keeps it otherwise (NovaGold, then NG=F).
|
|
420
|
+
const boardFuture = assetClass ? null : futuresContractByCode(query);
|
|
421
|
+
const futureIndex = boardFuture
|
|
422
|
+
? deduped.findIndex((item) => normalizeTickerSymbol(item.symbol || item.label) === boardFuture.symbol)
|
|
423
|
+
: -1;
|
|
424
|
+
if (boardFuture && futureIndex >= 0) {
|
|
425
|
+
deduped.splice(boardFuture.bareCode ? 0 : Math.min(1, futureIndex), 0, ...deduped.splice(futureIndex, 1));
|
|
426
|
+
}
|
|
411
427
|
return deduped;
|
|
412
428
|
}
|
|
413
429
|
|
|
@@ -432,7 +448,7 @@ export function buildSymbolAliases(symbol: string): string[] {
|
|
|
432
448
|
const normalizedSymbol = normalizeTickerSymbol(symbol);
|
|
433
449
|
if (!normalizedSymbol) return [];
|
|
434
450
|
if (isExplicitMarketSymbol(normalizedSymbol)) {
|
|
435
|
-
const pair =
|
|
451
|
+
const pair = currencyPairCode(normalizedSymbol);
|
|
436
452
|
return pair
|
|
437
453
|
? [...new Set([normalizedSymbol, `${pair}=X`, `${pair.slice(0, 3)}/${pair.slice(3)}`])]
|
|
438
454
|
: [normalizedSymbol];
|
|
@@ -38,7 +38,13 @@ export interface TickerSearchCandidate extends TickerSearchRankableItem {
|
|
|
38
38
|
|
|
39
39
|
export type ResolvedTickerSearch =
|
|
40
40
|
| { kind: "local"; symbol: string; ticker: TickerRecord }
|
|
41
|
-
| {
|
|
41
|
+
| {
|
|
42
|
+
kind: "provider";
|
|
43
|
+
symbol: string;
|
|
44
|
+
result: InstrumentSearchResult;
|
|
45
|
+
/** A bare symbol's other listings (SYM:EXCH) when its quote chose one of several. */
|
|
46
|
+
otherListings?: string[];
|
|
47
|
+
};
|
|
42
48
|
|
|
43
49
|
export interface TickerOpenTarget {
|
|
44
50
|
symbol: string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DataProvider } from "../types/data-provider";
|
|
2
|
+
import { canonicalExchange, isUsListingExchange, parsePublicTickerKey } from "../utils/exchanges";
|
|
3
|
+
import { sameCompany } from "./company-name";
|
|
4
|
+
import { LISTING_SEARCH_LIMIT } from "./search";
|
|
5
|
+
import { classifyInstrumentKind } from "./search/ranking";
|
|
6
|
+
|
|
7
|
+
export interface UsListing {
|
|
8
|
+
symbol: string;
|
|
9
|
+
/** Canonical code of its US exchange. */
|
|
10
|
+
exchange: string;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A company's own listing on a US exchange, found by its name: Sanofi's SNY,
|
|
16
|
+
* Toyota's TM. Funds and contracts that carry its name are not its listing.
|
|
17
|
+
* Null when search knows no US line of the company.
|
|
18
|
+
*/
|
|
19
|
+
export async function findUsListingOfCompany(
|
|
20
|
+
dataProvider: Pick<DataProvider, "search">,
|
|
21
|
+
company: string | null | undefined,
|
|
22
|
+
): Promise<UsListing | null> {
|
|
23
|
+
const name = company?.trim();
|
|
24
|
+
if (!name) return null;
|
|
25
|
+
const results = await dataProvider.search(name, { limit: LISTING_SEARCH_LIMIT }).catch(() => []);
|
|
26
|
+
for (const result of results) {
|
|
27
|
+
const exchange = canonicalExchange(result.exchange === "SMART" ? result.primaryExchange : result.exchange || result.primaryExchange);
|
|
28
|
+
const kind = classifyInstrumentKind(result.brokerContract?.secType || result.type);
|
|
29
|
+
if (!isUsListingExchange(exchange) || kind === "fund" || kind === "derivative") continue;
|
|
30
|
+
if (!sameCompany(result.name, name) || parsePublicTickerKey(result.symbol).exchange) continue;
|
|
31
|
+
return { symbol: result.symbol.toUpperCase(), exchange, name: result.name };
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
package/src/time-series/hooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { apiClient } from "../api-client";
|
|
|
3
3
|
import { loadCachedFredSeries } from "../sources/gloomberb-cloud/fred-series";
|
|
4
4
|
import { instrumentFromTicker } from "../market-data/request-types";
|
|
5
5
|
import { useAssetData, useCapabilityInvoker } from "../plugins/runtime";
|
|
6
|
+
import { getSharedRegistry } from "../plugins/registry";
|
|
6
7
|
import { createChartSeriesResolver } from "../capabilities";
|
|
7
8
|
import { useAppSelector } from "../state/app/context";
|
|
8
9
|
import type { FredSeriesRequest } from "../sources/gloomberb-cloud/fred-series";
|
|
@@ -17,6 +18,13 @@ import {
|
|
|
17
18
|
/** A scoped, immutable model for exports that must render exactly the loaded data. */
|
|
18
19
|
export const ChartSnapshotContext = createContext<ChartResolutionResult | null>(null);
|
|
19
20
|
|
|
21
|
+
/** A registered series' own reload cadence, when it has one. */
|
|
22
|
+
function registeredSeriesRefreshMs(source: { capabilityId: string; seriesId: string }): number | undefined {
|
|
23
|
+
const found = getSharedRegistry()?.series.findLoaded(source.capabilityId, source.seriesId);
|
|
24
|
+
const definition = found?.series.definition;
|
|
25
|
+
return definition && "refreshMs" in definition ? definition.refreshMs : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
async function loadFred(request: FredSeriesRequest) {
|
|
21
29
|
return loadCachedFredSeries(
|
|
22
30
|
request,
|
|
@@ -71,6 +79,7 @@ export function useResolvedChartSpec(
|
|
|
71
79
|
dataProvider,
|
|
72
80
|
loadFredSeries: loadFred,
|
|
73
81
|
resolveCapabilitySeries: createChartSeriesResolver(capabilityInvoker),
|
|
82
|
+
capabilityRefreshMs: registeredSeriesRefreshMs,
|
|
74
83
|
}), [capabilityInvoker, dataProvider]);
|
|
75
84
|
return useChartResolution(hydratedSpec, sources, { ...options, snapshot });
|
|
76
85
|
}
|