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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { MAX_CHART_SERIES_POINTS } from "../capabilities/chart-series";
|
|
2
|
+
import { subtractTimeRange } from "../time-series/date-window";
|
|
3
|
+
import type {
|
|
4
|
+
ChartViewportSpec,
|
|
5
|
+
ResolvedSeries,
|
|
6
|
+
SeriesPeriod,
|
|
7
|
+
TimeSeriesPoint,
|
|
8
|
+
} from "../time-series/types";
|
|
9
|
+
import { SERIES_COLORS } from "../theme/series-colors";
|
|
10
|
+
import type {
|
|
11
|
+
LoadedSeriesDefinition,
|
|
12
|
+
SeriesFrequency,
|
|
13
|
+
SeriesLoadOutput,
|
|
14
|
+
SeriesLoadRequest,
|
|
15
|
+
SeriesPoint,
|
|
16
|
+
} from "./types";
|
|
17
|
+
|
|
18
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
19
|
+
|
|
20
|
+
/** A point's date, with a bare `YYYY-MM-DD` read as that UTC day. */
|
|
21
|
+
function seriesPointDate(value: SeriesPoint["date"]): Date | null {
|
|
22
|
+
const date = typeof value === "string" && DATE_ONLY.test(value.trim())
|
|
23
|
+
? new Date(`${value.trim()}T00:00:00Z`)
|
|
24
|
+
: value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
25
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Oldest first, one point per instant (the last one given wins), at most the chart's point limit. */
|
|
29
|
+
export function normalizeSeriesPoints(points: readonly SeriesPoint[]): TimeSeriesPoint[] {
|
|
30
|
+
const byTime = new Map<number, number | null>();
|
|
31
|
+
for (const point of points) {
|
|
32
|
+
const date = point ? seriesPointDate(point.date) : null;
|
|
33
|
+
if (!date) continue;
|
|
34
|
+
const value = typeof point.value === "number" && Number.isFinite(point.value) ? point.value : null;
|
|
35
|
+
byTime.set(date.getTime(), value);
|
|
36
|
+
}
|
|
37
|
+
const sorted = [...byTime].sort(([left], [right]) => left - right);
|
|
38
|
+
return sorted.slice(-MAX_CHART_SERIES_POINTS).map(([time, value]) => {
|
|
39
|
+
const date = new Date(time);
|
|
40
|
+
return { date, observedAt: date, value };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function nativeFrequency(frequency: SeriesFrequency): SeriesPeriod {
|
|
45
|
+
return frequency === "intraday" || frequency === "irregular" ? "auto" : frequency;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Series in one unit share an axis; percentages share it with FRED's. */
|
|
49
|
+
function seriesUnitGroup(unit: string): string {
|
|
50
|
+
return unit === "%" ? "percent" : `series:${unit.toLowerCase()}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** One colour per id, so two loaded series on a chart are told apart. */
|
|
54
|
+
function seriesColor(id: string): string {
|
|
55
|
+
let hash = 0;
|
|
56
|
+
for (const char of id) hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
|
|
57
|
+
return SERIES_COLORS[hash % SERIES_COLORS.length]!;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The dates a chart viewport covers, as a series load request takes them. */
|
|
61
|
+
export function seriesLoadWindow(viewport: ChartViewportSpec, now = new Date()): Pick<SeriesLoadRequest, "start" | "end"> {
|
|
62
|
+
if (viewport.dateWindow) {
|
|
63
|
+
const start = new Date(viewport.dateWindow.start);
|
|
64
|
+
const end = new Date(viewport.dateWindow.end);
|
|
65
|
+
if (Number.isFinite(start.getTime()) && Number.isFinite(end.getTime())) return { start, end };
|
|
66
|
+
}
|
|
67
|
+
if (viewport.range === "ALL") return {};
|
|
68
|
+
return { start: subtractTimeRange(now, viewport.range) };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadResult(output: SeriesLoadOutput): { points: readonly SeriesPoint[]; warning?: string } {
|
|
72
|
+
if (Array.isArray(output)) return { points: output };
|
|
73
|
+
const result = output as Exclude<SeriesLoadOutput, readonly SeriesPoint[]>;
|
|
74
|
+
if (!result || !Array.isArray(result.points)) throw new Error("Series loader returned no points.");
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function resolveLoadedSeries(
|
|
79
|
+
definition: LoadedSeriesDefinition,
|
|
80
|
+
request: { viewport: ChartViewportSpec; ticker?: string; signal?: AbortSignal },
|
|
81
|
+
now = new Date(),
|
|
82
|
+
): Promise<ResolvedSeries> {
|
|
83
|
+
const ticker = request.ticker?.trim().toUpperCase() || undefined;
|
|
84
|
+
const { points, warning } = loadResult(await definition.load({
|
|
85
|
+
...seriesLoadWindow(request.viewport, now),
|
|
86
|
+
...(ticker ? { ticker } : {}),
|
|
87
|
+
...(request.signal ? { signal: request.signal } : {}),
|
|
88
|
+
}));
|
|
89
|
+
const id = ticker ? `${definition.id}/${ticker}` : definition.id;
|
|
90
|
+
const style = definition.style ?? "line";
|
|
91
|
+
return {
|
|
92
|
+
id,
|
|
93
|
+
label: ticker ? `${ticker} · ${definition.name}` : definition.name,
|
|
94
|
+
color: seriesColor(id),
|
|
95
|
+
unit: definition.unit,
|
|
96
|
+
unitGroup: seriesUnitGroup(definition.unit),
|
|
97
|
+
nativeFrequency: nativeFrequency(definition.frequency),
|
|
98
|
+
dataShape: "scalar",
|
|
99
|
+
style,
|
|
100
|
+
transform: "raw",
|
|
101
|
+
axis: "left",
|
|
102
|
+
panelId: "main",
|
|
103
|
+
interpolation: style === "step" ? "step-after" : "none",
|
|
104
|
+
...(warning ? { warning } : {}),
|
|
105
|
+
points: normalizeSeriesPoints(points),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { chartSeriesResolveRequestSchema } from "../capabilities/chart-series";
|
|
2
|
+
import type { CapabilityInvoker } from "../capabilities/types";
|
|
3
|
+
import type { GloomPlugin } from "../types/plugin";
|
|
4
|
+
import { resolveRegisteredSeries } from "./capability";
|
|
5
|
+
import { readSeriesFolder } from "./files";
|
|
6
|
+
import { SeriesRegistry } from "./registry";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Charts the catalog's loaded series where no plugin runtime runs, such as a
|
|
10
|
+
* `gloomberb fn G` report: the series plugins declare, and the files in the
|
|
11
|
+
* data directory's series folder. Series a plugin registers from its setup
|
|
12
|
+
* need the app.
|
|
13
|
+
*/
|
|
14
|
+
export function standaloneSeriesInvoker(
|
|
15
|
+
plugins: readonly GloomPlugin[],
|
|
16
|
+
dataDir: string,
|
|
17
|
+
disabledPlugins: readonly string[] = [],
|
|
18
|
+
): CapabilityInvoker {
|
|
19
|
+
let registry: Promise<SeriesRegistry> | null = null;
|
|
20
|
+
const build = async () => {
|
|
21
|
+
const built = new SeriesRegistry({ pluginHosts: (pluginId) => plugins.find((plugin) => plugin.id === pluginId)?.hosts });
|
|
22
|
+
const register = (pluginId: string, definitions: readonly Parameters<SeriesRegistry["register"]>[1][]) => {
|
|
23
|
+
for (const definition of definitions) {
|
|
24
|
+
try {
|
|
25
|
+
built.register(pluginId, definition);
|
|
26
|
+
} catch {
|
|
27
|
+
// The app logs a definition it refuses; a report charts the rest.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
for (const plugin of plugins) {
|
|
32
|
+
if (!disabledPlugins.includes(plugin.id)) register(plugin.id, plugin.series ?? []);
|
|
33
|
+
}
|
|
34
|
+
register("user", await readSeriesFolder(dataDir));
|
|
35
|
+
return built;
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
capabilityManifests: () => [],
|
|
39
|
+
async invokeCapability<T>(capabilityId: string, operationId: string, payload: unknown, options?: { signal?: AbortSignal }) {
|
|
40
|
+
if (operationId !== "resolve") throw new Error(`Capability "${capabilityId}" is not available.`);
|
|
41
|
+
registry ??= build();
|
|
42
|
+
const request = chartSeriesResolveRequestSchema.parse(payload);
|
|
43
|
+
const resolved = await resolveRegisteredSeries(await registry, capabilityId, request.seriesId, request.viewport, options?.signal);
|
|
44
|
+
if (!resolved) throw new Error(`Chart series capability "${capabilityId}" is unavailable. Enable its plugin or provider.`);
|
|
45
|
+
return resolved as T;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Series a plugin registers for the data catalog and the chart composer.
|
|
3
|
+
*
|
|
4
|
+
* One definition is one catalog entry: a named line with a unit and a
|
|
5
|
+
* frequency that the composer can draw next to any other entry. A definition
|
|
6
|
+
* either loads its own history (`load`) or points at something the composer
|
|
7
|
+
* already draws (`expression`), such as a FRED series or a ticker field.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** How often a series gets a new observation. */
|
|
11
|
+
export type SeriesFrequency =
|
|
12
|
+
| "intraday"
|
|
13
|
+
| "daily"
|
|
14
|
+
| "weekly"
|
|
15
|
+
| "monthly"
|
|
16
|
+
| "quarterly"
|
|
17
|
+
| "annual"
|
|
18
|
+
| "irregular";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What a series asks for before it can be charted: a ticker (AAPL), an option
|
|
22
|
+
* contract (AAPL 260618C00200000) or a crypto pair (BTC-USD). A series without
|
|
23
|
+
* an input charts as it is.
|
|
24
|
+
*/
|
|
25
|
+
export type SeriesInput = "ticker" | "option" | "crypto";
|
|
26
|
+
|
|
27
|
+
/** How a loaded series is drawn when it is first added to a chart. */
|
|
28
|
+
export type SeriesDrawStyle = "line" | "area" | "step" | "columns" | "points";
|
|
29
|
+
|
|
30
|
+
export interface SeriesPoint {
|
|
31
|
+
/** The observation date: a Date, an ISO date or date-time string, or epoch milliseconds. */
|
|
32
|
+
date: Date | string | number;
|
|
33
|
+
/** Null marks a missing observation, which the chart draws as a gap. */
|
|
34
|
+
value: number | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SeriesLoadRequest {
|
|
38
|
+
/** The first date the chart shows. Absent when the chart asks for all history. */
|
|
39
|
+
start?: Date;
|
|
40
|
+
/** The last date the chart shows. Absent means up to now. */
|
|
41
|
+
end?: Date;
|
|
42
|
+
/** The symbol typed for a series with an `input`, upper case (AAPL, BTC-USD). */
|
|
43
|
+
ticker?: string;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SeriesLoadResult {
|
|
48
|
+
points: readonly SeriesPoint[];
|
|
49
|
+
/** A note the chart shows above the plot, such as "cached data is stale". */
|
|
50
|
+
warning?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type SeriesLoadOutput = readonly SeriesPoint[] | SeriesLoadResult;
|
|
54
|
+
|
|
55
|
+
interface SeriesDefinitionBase {
|
|
56
|
+
/**
|
|
57
|
+
* `namespace:name`, unique across the app, e.g. `fear-greed:index`. The
|
|
58
|
+
* namespace belongs to the plugin that registers its first series; a loaded
|
|
59
|
+
* series charts as `CAP:<id>`. Letters, digits, `.`, `_` and `-`.
|
|
60
|
+
*/
|
|
61
|
+
id: string;
|
|
62
|
+
/** What the catalog and the chart legend call the series. */
|
|
63
|
+
name: string;
|
|
64
|
+
/**
|
|
65
|
+
* The unit every value is in. Series in the same unit share a chart axis.
|
|
66
|
+
* See `describeSeriesUnits()` for the accepted forms: `%`, `bp`, `index`,
|
|
67
|
+
* `USD`, `billion USD`, `thousand barrels/day` and so on.
|
|
68
|
+
*/
|
|
69
|
+
unit: string;
|
|
70
|
+
frequency: SeriesFrequency;
|
|
71
|
+
/** The catalog tab the series is listed under, such as "Credit" or "Sentiment". */
|
|
72
|
+
group?: string;
|
|
73
|
+
/** Extra words the catalog search matches, such as a ticker the series is often read against. */
|
|
74
|
+
tags?: readonly string[];
|
|
75
|
+
/** One sentence for the catalog search and for `gloomberb` automation. */
|
|
76
|
+
description?: string;
|
|
77
|
+
/**
|
|
78
|
+
* The official body that publishes the data (a statistics agency, a central
|
|
79
|
+
* bank, an exchange regulator), when the series is public data. Never the
|
|
80
|
+
* service a plugin fetches it from.
|
|
81
|
+
*/
|
|
82
|
+
publisher?: string;
|
|
83
|
+
/** The publisher's page for the series. */
|
|
84
|
+
url?: string;
|
|
85
|
+
/** Ask for a ticker, an option or a crypto pair before charting. */
|
|
86
|
+
input?: SeriesInput;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A series that loads its own history. */
|
|
90
|
+
export interface LoadedSeriesDefinition extends SeriesDefinitionBase {
|
|
91
|
+
/**
|
|
92
|
+
* The observations from `request.start` to `request.end`, oldest or newest
|
|
93
|
+
* first. Returning more history than asked for is fine; the chart clips it.
|
|
94
|
+
*/
|
|
95
|
+
load(request: SeriesLoadRequest): SeriesLoadOutput | Promise<SeriesLoadOutput>;
|
|
96
|
+
/**
|
|
97
|
+
* For a series that changes during the day: how often, in milliseconds, a
|
|
98
|
+
* chart showing it loads it again. At least one minute.
|
|
99
|
+
*/
|
|
100
|
+
refreshMs?: number;
|
|
101
|
+
/** How the series is drawn when first added; a line when omitted. */
|
|
102
|
+
style?: SeriesDrawStyle;
|
|
103
|
+
expression?: never;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A catalog entry for something the chart composer already draws:
|
|
108
|
+
* `FRED:UNRATE`, `UST:10Y`, `FUT:ES`, `^VIX:close`. With an `input`, write
|
|
109
|
+
* `{ticker}` where the symbol goes: `{ticker}:close`.
|
|
110
|
+
*/
|
|
111
|
+
export interface ExpressionSeriesDefinition extends SeriesDefinitionBase {
|
|
112
|
+
expression: string;
|
|
113
|
+
load?: never;
|
|
114
|
+
refreshMs?: never;
|
|
115
|
+
style?: never;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type SeriesDefinition = LoadedSeriesDefinition | ExpressionSeriesDefinition;
|
|
119
|
+
|
|
120
|
+
/** A definition as the registry holds it, with the plugin that registered it. */
|
|
121
|
+
export interface RegisteredSeries {
|
|
122
|
+
definition: SeriesDefinition;
|
|
123
|
+
/** The plugin that registered the series. */
|
|
124
|
+
pluginId: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* What the catalog lists for a series, without functions: the shape that
|
|
129
|
+
* crosses from the desktop's Bun process to its view.
|
|
130
|
+
*/
|
|
131
|
+
export interface SeriesCatalogEntry {
|
|
132
|
+
id: string;
|
|
133
|
+
name: string;
|
|
134
|
+
unit: string;
|
|
135
|
+
frequency: SeriesFrequency;
|
|
136
|
+
group?: string;
|
|
137
|
+
tags?: readonly string[];
|
|
138
|
+
description?: string;
|
|
139
|
+
publisher?: string;
|
|
140
|
+
url?: string;
|
|
141
|
+
input?: SeriesInput;
|
|
142
|
+
/** The chart expression; `{ticker}` stands for the input. */
|
|
143
|
+
expression: string;
|
|
144
|
+
pluginId: string;
|
|
145
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SeriesDefinition,
|
|
3
|
+
SeriesDrawStyle,
|
|
4
|
+
SeriesFrequency,
|
|
5
|
+
SeriesInput,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
export const SERIES_FREQUENCIES: readonly SeriesFrequency[] = [
|
|
9
|
+
"intraday", "daily", "weekly", "monthly", "quarterly", "annual", "irregular",
|
|
10
|
+
];
|
|
11
|
+
const SERIES_INPUTS: readonly SeriesInput[] = ["ticker", "option", "crypto"];
|
|
12
|
+
const DRAW_STYLES: readonly SeriesDrawStyle[] = ["line", "area", "step", "columns", "points"];
|
|
13
|
+
|
|
14
|
+
/** Units with no scale: a percentage of something, not a quantity of it. */
|
|
15
|
+
const RATE_UNITS = ["%", "bp", "x", "ratio", "index", "score", "points"] as const;
|
|
16
|
+
/** Quantities, which may take a scale ("thousand barrels"). */
|
|
17
|
+
const QUANTITY_UNITS = [
|
|
18
|
+
"number", "count", "units", "shares", "contracts", "days", "people", "jobs", "posts", "views", "hours",
|
|
19
|
+
"barrels", "barrels/day", "cubic feet", "MW", "MWh", "tons", "ounces",
|
|
20
|
+
// The instrument's own currency, for a field that applies to any ticker.
|
|
21
|
+
"currency", "currency/share",
|
|
22
|
+
] as const;
|
|
23
|
+
/** What a price is quoted per: `USD/barrel`, `USD/GPU-hour`. */
|
|
24
|
+
const PER_UNITS = ["share", "barrel", "gallon", "MMBtu", "MWh", "ounce", "ton", "hour", "GPU-hour"] as const;
|
|
25
|
+
const SCALES = ["thousand", "million", "billion", "trillion"] as const;
|
|
26
|
+
|
|
27
|
+
const RATE_SET: ReadonlySet<string> = new Set(RATE_UNITS);
|
|
28
|
+
const QUANTITY_SET: ReadonlySet<string> = new Set(QUANTITY_UNITS);
|
|
29
|
+
const PER_SET: ReadonlySet<string> = new Set(PER_UNITS);
|
|
30
|
+
const CURRENCY_CODE = /^[A-Z]{3}$/;
|
|
31
|
+
|
|
32
|
+
const NAMESPACE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
33
|
+
const NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/;
|
|
34
|
+
const TICKER_PLACEHOLDER = "{ticker}";
|
|
35
|
+
const MIN_REFRESH_MS = 60_000;
|
|
36
|
+
|
|
37
|
+
/** The unit forms `unit` accepts, in one line for an error or a doc. */
|
|
38
|
+
export function describeSeriesUnits(): string {
|
|
39
|
+
return `${RATE_UNITS.join(", ")}; ${QUANTITY_UNITS.join(", ")} or a currency code (USD), optionally after `
|
|
40
|
+
+ `${SCALES.join(", ")}; or a currency per ${PER_UNITS.join(", ")} (USD/barrel).`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isQuantityBase(value: string): boolean {
|
|
44
|
+
if (QUANTITY_SET.has(value) || CURRENCY_CODE.test(value)) return true;
|
|
45
|
+
const slash = value.indexOf("/");
|
|
46
|
+
return slash > 0 && CURRENCY_CODE.test(value.slice(0, slash)) && PER_SET.has(value.slice(slash + 1));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function isValidSeriesUnit(unit: string): boolean {
|
|
50
|
+
if (RATE_SET.has(unit)) return true;
|
|
51
|
+
const space = unit.indexOf(" ");
|
|
52
|
+
const scale = space > 0 ? unit.slice(0, space) : "";
|
|
53
|
+
if ((SCALES as readonly string[]).includes(scale)) return isQuantityBase(unit.slice(space + 1));
|
|
54
|
+
return isQuantityBase(unit);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Splits `namespace:name`; null when the id is not one. */
|
|
58
|
+
export function splitSeriesId(id: string): { namespace: string; name: string } | null {
|
|
59
|
+
const separator = id.indexOf(":");
|
|
60
|
+
if (separator < 0) return null;
|
|
61
|
+
const namespace = id.slice(0, separator);
|
|
62
|
+
const name = id.slice(separator + 1);
|
|
63
|
+
return NAMESPACE_PATTERN.test(namespace) && NAME_PATTERN.test(name) ? { namespace, name } : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The words a public label must not contain: the services the registering
|
|
68
|
+
* plugin reaches, taken from its declared hosts (`api.example.com` forbids
|
|
69
|
+
* "example"). A catalog says what a series is, not who serves it.
|
|
70
|
+
*/
|
|
71
|
+
export function hostLabelWords(hosts: readonly string[] = []): string[] {
|
|
72
|
+
const words = new Set<string>();
|
|
73
|
+
for (const host of hosts) {
|
|
74
|
+
const labels = host.trim().toLowerCase().split(".").filter(Boolean);
|
|
75
|
+
const name = labels.length >= 2 ? labels[labels.length - 2] : labels[0];
|
|
76
|
+
if (name && name.length >= 3) words.add(name);
|
|
77
|
+
}
|
|
78
|
+
return [...words];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface SeriesValidationContext {
|
|
82
|
+
/** Words no label may contain; see `hostLabelWords`. */
|
|
83
|
+
forbiddenWords?: readonly string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const DOMAIN = /\b[a-z0-9-]+\.(?:com|net|org|io|co|ai|dev|app|gov|edu|info|biz)\b/i;
|
|
87
|
+
const SOURCE_ID = /\b(?:provider|source|feed):[a-z0-9_-]/i;
|
|
88
|
+
|
|
89
|
+
function fail(id: unknown, message: string): never {
|
|
90
|
+
throw new Error(`Invalid series ${typeof id === "string" && id ? `"${id}"` : "definition"}: ${message}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function checkLabel(id: string, field: string, value: unknown, max: number, context: SeriesValidationContext, optional = true): void {
|
|
94
|
+
if (value === undefined && optional) return;
|
|
95
|
+
if (typeof value !== "string" || !value.trim() || value.length > max || /[\r\n\t]/.test(value)) {
|
|
96
|
+
fail(id, `${field} must be a single line of 1 to ${max} characters.`);
|
|
97
|
+
}
|
|
98
|
+
if (DOMAIN.test(value) || SOURCE_ID.test(value)) {
|
|
99
|
+
fail(id, `${field} "${value}" names a host or an internal source; say what the data is instead.`);
|
|
100
|
+
}
|
|
101
|
+
const lower = value.toLowerCase();
|
|
102
|
+
const named = context.forbiddenWords?.find((word) => new RegExp(`\\b${word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(lower));
|
|
103
|
+
if (named) fail(id, `${field} "${value}" names "${named}", a service the plugin fetches from.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Throws a message naming the series and the problem when a definition
|
|
108
|
+
* cannot be registered: a malformed id, an unknown unit or frequency, a label
|
|
109
|
+
* that names a host or a fetched-from service, or both or neither of `load`
|
|
110
|
+
* and `expression`. Ids are checked for uniqueness by the registry.
|
|
111
|
+
*/
|
|
112
|
+
export function validateSeriesDefinition(definition: SeriesDefinition, context: SeriesValidationContext = {}): void {
|
|
113
|
+
if (!definition || typeof definition !== "object") fail(undefined, "expected an object.");
|
|
114
|
+
const { id } = definition;
|
|
115
|
+
if (typeof id !== "string" || !splitSeriesId(id)) {
|
|
116
|
+
fail(id, "id must be namespace:name, with a lower-case namespace and letters, digits, \".\", \"_\" or \"-\".");
|
|
117
|
+
}
|
|
118
|
+
checkLabel(id, "name", definition.name, 120, context, false);
|
|
119
|
+
if (typeof definition.unit !== "string" || !isValidSeriesUnit(definition.unit)) {
|
|
120
|
+
fail(id, `unit "${String(definition.unit)}" is not one of: ${describeSeriesUnits()}`);
|
|
121
|
+
}
|
|
122
|
+
if (!SERIES_FREQUENCIES.includes(definition.frequency)) {
|
|
123
|
+
fail(id, `frequency must be one of ${SERIES_FREQUENCIES.join(", ")}.`);
|
|
124
|
+
}
|
|
125
|
+
checkLabel(id, "group", definition.group, 32, context);
|
|
126
|
+
checkLabel(id, "description", definition.description, 300, context);
|
|
127
|
+
checkLabel(id, "publisher", definition.publisher, 60, context);
|
|
128
|
+
if (definition.tags !== undefined) {
|
|
129
|
+
if (!Array.isArray(definition.tags) || definition.tags.length > 16) fail(id, "tags must be a list of at most 16 words.");
|
|
130
|
+
for (const tag of definition.tags) checkLabel(id, "tag", tag, 40, context, false);
|
|
131
|
+
}
|
|
132
|
+
if (definition.url !== undefined && (typeof definition.url !== "string" || !/^https?:\/\/\S+$/.test(definition.url))) {
|
|
133
|
+
fail(id, "url must be an http or https address.");
|
|
134
|
+
}
|
|
135
|
+
if (definition.input !== undefined && !SERIES_INPUTS.includes(definition.input)) {
|
|
136
|
+
fail(id, `input must be one of ${SERIES_INPUTS.join(", ")}.`);
|
|
137
|
+
}
|
|
138
|
+
const hasLoad = typeof definition.load === "function";
|
|
139
|
+
const hasExpression = definition.expression !== undefined;
|
|
140
|
+
if (hasLoad === hasExpression) fail(id, "give exactly one of load or expression.");
|
|
141
|
+
if (hasExpression) {
|
|
142
|
+
const expression = definition.expression;
|
|
143
|
+
if (typeof expression !== "string" || !expression.trim() || expression.length > 200 || /[;,\n]/.test(expression)) {
|
|
144
|
+
fail(id, "expression must be one chart series, such as FRED:UNRATE or {ticker}:close.");
|
|
145
|
+
}
|
|
146
|
+
if (expression.includes(TICKER_PLACEHOLDER) !== (definition.input !== undefined)) {
|
|
147
|
+
fail(id, definition.input
|
|
148
|
+
? `an expression with an input must contain ${TICKER_PLACEHOLDER}.`
|
|
149
|
+
: `${TICKER_PLACEHOLDER} needs an input.`);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const loaded = definition as Extract<SeriesDefinition, { load: unknown }>;
|
|
154
|
+
if (loaded.refreshMs !== undefined && (!Number.isFinite(loaded.refreshMs) || loaded.refreshMs < MIN_REFRESH_MS)) {
|
|
155
|
+
fail(id, `refreshMs must be at least ${MIN_REFRESH_MS}.`);
|
|
156
|
+
}
|
|
157
|
+
if (loaded.style !== undefined && !DRAW_STYLES.includes(loaded.style)) {
|
|
158
|
+
fail(id, `style must be one of ${DRAW_STYLES.join(", ")}.`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Checks a definition and returns it, so a plugin's static list fails where it
|
|
164
|
+
* is written: `series: [defineSeries({ ... })]`.
|
|
165
|
+
*/
|
|
166
|
+
export function defineSeries<T extends SeriesDefinition>(definition: T): T {
|
|
167
|
+
validateSeriesDefinition(definition);
|
|
168
|
+
return definition;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { TICKER_PLACEHOLDER };
|
|
@@ -201,15 +201,15 @@ function mapCloudPriceHistory(
|
|
|
201
201
|
if (stale && !reachesLatestSettledSession(points, Date.now(), { symbol: ticker, exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
|
|
202
202
|
throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
|
|
203
203
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
) {
|
|
204
|
+
const intraday = /^\d+(min|h)$/i.test(interval);
|
|
205
|
+
// Daily bars answering an intraday range: the listing has no intraday bars.
|
|
206
|
+
const dailyBars = intraday && response.servedResolution === "1day";
|
|
207
|
+
if (intraday && !dailyBars && hasMalformedIntradayHistory(points)) {
|
|
208
208
|
throw createProviderMiss(`Cloud chart data failed OHLC validation for ${ticker}`);
|
|
209
209
|
}
|
|
210
210
|
const matchingInterval = [response.providerMeta?.servedResolution, response.providerMeta?.requestedResolution]
|
|
211
211
|
.every((declared) => declared === undefined || canonicalHistoryInterval(declared) === canonicalHistoryInterval(interval));
|
|
212
|
-
const resolution = matchingInterval ? cloudHistoryResolution(interval) : null;
|
|
212
|
+
const resolution = dailyBars ? "1d" : matchingInterval ? cloudHistoryResolution(interval) : null;
|
|
213
213
|
// Metadata this client cannot read (a newer version, a far-off clock) is
|
|
214
214
|
// absent, not contradictory; only a readable mismatch rejects the history.
|
|
215
215
|
const declared = parseHistorySession(response.historySession) !== null;
|
|
@@ -490,9 +490,9 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
490
490
|
stale: data.stale === true || response.stale === true };
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
async search(query: string,
|
|
493
|
+
async search(query: string, context?: SearchRequestContext): Promise<InstrumentSearchResult[]> {
|
|
494
494
|
return withCloudFallback(
|
|
495
|
-
() => apiClient.searchInstruments(query, 10),
|
|
495
|
+
() => apiClient.searchInstruments(query, context?.limit ?? 10),
|
|
496
496
|
"Cloud search is unavailable",
|
|
497
497
|
);
|
|
498
498
|
}
|
|
@@ -119,6 +119,7 @@ export function mapQuote(
|
|
|
119
119
|
quote.postMarketChange,
|
|
120
120
|
divisor,
|
|
121
121
|
),
|
|
122
|
+
extendedSessionPrint: typeof quote.extendedSessionPrint === "boolean" ? quote.extendedSessionPrint : undefined,
|
|
122
123
|
listingExchangeName,
|
|
123
124
|
listingExchangeFullName,
|
|
124
125
|
exchangeName: listingExchangeName,
|
|
@@ -41,6 +41,7 @@ export function normalizeHistoryResult(
|
|
|
41
41
|
...(typeof result.sourceKey === "string" && /^(provider|broker):[^\r\n\0]+$/.test(result.sourceKey) ? { sourceKey: result.sourceKey } : {}),
|
|
42
42
|
...(isHistoryCoverageDate(result.coverageStart) ? { coverageStart: result.coverageStart } : {}),
|
|
43
43
|
...(result.extendedHours === true ? { extendedHours: true } : {}),
|
|
44
|
+
...(result.savedCopyBehind === true ? { savedCopyBehind: true as const } : {}),
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -21,9 +21,12 @@ const EXCHANGE_SUFFIX_MAP: Record<string, string> = {
|
|
|
21
21
|
SFB: ".ST", Stockholm: ".ST", OMX: ".ST", CPH: ".CO", HEX: ".HE", HEL: ".HE", OSE: ".OL", OSL: ".OL", OMXNO: ".OL", ICEX: ".IC",
|
|
22
22
|
VSE: ".VI", WSE: ".WA", GPW: ".WA", PRA: ".PR", BUX: ".BD", ATHEX: ".AT", BVB: ".RO", BIST: ".IS",
|
|
23
23
|
TASE: ".TA",
|
|
24
|
+
MSX: ".OM",
|
|
24
25
|
JSE: ".JO",
|
|
26
|
+
BHB: ".BH",
|
|
25
27
|
BVMF: ".SA", MEXI: ".MX", BYMA: ".BA", BCS: ".SN",
|
|
26
|
-
TADAWUL: ".SR", QE: ".QA", DFM: ".AE",
|
|
28
|
+
TADAWUL: ".SR", QE: ".QA", DFM: ".AE", KUWAIT: ".KW",
|
|
29
|
+
EGX: ".CA",
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
const EXCHANGE_FALLBACKS: Record<string, string[]> = {
|
|
@@ -137,7 +137,10 @@ export class ProviderRouterBatchRoutes {
|
|
|
137
137
|
this.deps.resources, this.deps.getEntityKey(target.symbol, context.instrument),
|
|
138
138
|
financialHistoryVariants(this.deps.getTickerVariantCandidates(target.exchange), context)[0] ?? "", this.deps.getProviderSourceKeys(),
|
|
139
139
|
);
|
|
140
|
-
|
|
140
|
+
// The selection wraps a cached quote even when no financials were ever
|
|
141
|
+
// fetched; a quote alone has no fundamentals, so it is not a hit.
|
|
142
|
+
const fetched = cached.brokerRecord != null || (cached.providerRecords?.length ?? 0) > 0;
|
|
143
|
+
if (fetched && cached.value?.quote && !forceRefresh && target.statementHistory !== "extended" && (!needsFinancialProfile(cached.value) || profileAttempted)) {
|
|
141
144
|
results[index] = { target, financials: cached.value };
|
|
142
145
|
return;
|
|
143
146
|
}
|
|
@@ -15,12 +15,13 @@ export const FINANCIALS_SCHEMA_VERSION = 13;
|
|
|
15
15
|
export const QUOTE_SCHEMA_VERSION = 3;
|
|
16
16
|
export const MARKET_METADATA_SCHEMA_VERSION = 2;
|
|
17
17
|
// Refresh persisted provider metadata through the managed backend on upgrade.
|
|
18
|
+
// corporateActions-v3: earnings rows resolved against the earnings history.
|
|
18
19
|
const SCHEMA_VERSIONS: Record<string, number> = {
|
|
19
20
|
financials: FINANCIALS_SCHEMA_VERSION,
|
|
20
21
|
quote: QUOTE_SCHEMA_VERSION,
|
|
21
22
|
holders: MARKET_METADATA_SCHEMA_VERSION,
|
|
22
23
|
"analystResearch-v2": MARKET_METADATA_SCHEMA_VERSION,
|
|
23
|
-
"corporateActions-
|
|
24
|
+
"corporateActions-v3": MARKET_METADATA_SCHEMA_VERSION,
|
|
24
25
|
"options-chain": MARKET_METADATA_SCHEMA_VERSION,
|
|
25
26
|
"exchange-rate": MARKET_METADATA_SCHEMA_VERSION,
|
|
26
27
|
};
|
|
@@ -34,7 +35,8 @@ const DEFAULT_CACHE_POLICIES = {
|
|
|
34
35
|
news: { staleMs: 15 * 60_000, expireMs: 2 * 24 * 60 * 60_000 },
|
|
35
36
|
holders: { staleMs: 24 * 60 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
|
|
36
37
|
analystResearch: { staleMs: 24 * 60 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
|
|
37
|
-
|
|
38
|
+
// Earnings rows carry report-day actuals and date changes; portfolios load these for every holding.
|
|
39
|
+
corporateActions: { staleMs: 6 * 60 * 60_000, expireMs: 14 * 24 * 60 * 60_000 },
|
|
38
40
|
secFilings: { staleMs: 15 * 60_000, expireMs: 2 * 24 * 60 * 60_000 },
|
|
39
41
|
secFilingDocuments: { staleMs: 30 * 24 * 60 * 60_000, expireMs: 365 * 24 * 60 * 60_000 },
|
|
40
42
|
secFilingContent: { staleMs: 30 * 24 * 60 * 60_000, expireMs: 365 * 24 * 60 * 60_000 },
|
|
@@ -211,7 +211,7 @@ export class ProviderRouterCachedRoutes {
|
|
|
211
211
|
error: `No analyst research provider available for ${ticker}`,
|
|
212
212
|
};
|
|
213
213
|
case "getCorporateActions": return {
|
|
214
|
-
...tickerRoute("corporateActions-
|
|
214
|
+
...tickerRoute("corporateActions-v3", "corporateActions"), request: (provider, force) => provider.getCorporateActions?.(ticker, exchange, force ? { ...extra, cacheMode: "refresh" } : extra),
|
|
215
215
|
rank: (data: CorporateActionsData) => !hasCorporateActionsValue(data) ? 0 : data.stale || Object.values(data.coverage ?? {}).includes("unavailable") ? 1 : 2,
|
|
216
216
|
acceptCached: (data: CorporateActionsData) => !data.stale && !Object.values(data.coverage ?? {}).includes("unavailable"), throwLastError: true,
|
|
217
217
|
error: `No corporate actions provider available for ${ticker}`,
|
|
@@ -471,6 +471,19 @@ function mergeFundamentals(primary: Fundamentals | undefined, fallback: Fundamen
|
|
|
471
471
|
// A fallback cannot retrospectively denominate an older cached snapshot.
|
|
472
472
|
delete merged.financialCurrency;
|
|
473
473
|
}
|
|
474
|
+
if (merged) {
|
|
475
|
+
// A conversion, or an EPS currency apart from the amounts', describes the currency it came with, never one
|
|
476
|
+
// merged from elsewhere.
|
|
477
|
+
const denominated = primary?.financialCurrency ? primary : merged.financialCurrency ? fallback : undefined;
|
|
478
|
+
if (denominated?.ttmConversion) merged.ttmConversion = denominated.ttmConversion;
|
|
479
|
+
else delete merged.ttmConversion;
|
|
480
|
+
if (denominated?.epsCurrency) merged.epsCurrency = denominated.epsCurrency;
|
|
481
|
+
else delete merged.epsCurrency;
|
|
482
|
+
// The rate a trailing P/E converted its EPS at pairs with that multiple.
|
|
483
|
+
const trailing = primary?.trailingPE != null ? primary : fallback;
|
|
484
|
+
if (trailing?.trailingPEConversion) merged.trailingPEConversion = trailing.trailingPEConversion;
|
|
485
|
+
else delete merged.trailingPEConversion;
|
|
486
|
+
}
|
|
474
487
|
return redactUnavailableFundamentals(merged);
|
|
475
488
|
}
|
|
476
489
|
|