gloomberb 0.18.4 → 0.19.0
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +36 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/pane.tsx +7 -5
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/feed.ts +2 -2
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { currencyPairSpelling } from "../utils/currency-pair";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* What a price counts, read from its unit: the currency it is in and the
|
|
3
5
|
* quantity one unit of that currency buys ("USD / gallon"), as futures
|
|
@@ -7,9 +9,22 @@
|
|
|
7
9
|
/** Gallons in one US oil barrel: heating oil and gasoline quote per gallon, crude per barrel. */
|
|
8
10
|
export const GALLONS_PER_BARREL = 42;
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Barrels in a metric tonne of a refined product, the conversions a formula
|
|
14
|
+
* knows by their number: gasoil 7.45 bbl/t (ICE gasoil's own), gasoline 8.45.
|
|
15
|
+
* Times one, a price per barrel is a price per tonne.
|
|
16
|
+
*/
|
|
17
|
+
const BARRELS_PER_TONNE = { gasoil: 7.45, gasoline: 8.45 } as const;
|
|
18
|
+
|
|
19
|
+
/** Whether a formula's multiplier is the barrels in a tonne of a product (7.45 or 8.45). */
|
|
20
|
+
export function isBarrelsPerTonne(value: number): boolean {
|
|
21
|
+
return Object.values(BARRELS_PER_TONNE).includes(value as 7.45 | 8.45);
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
const US_CENTS = /^us\s*cents?$/i;
|
|
11
25
|
const GALLON = /^(?:gallons?|gal)$/i;
|
|
12
26
|
const BARREL = /^(?:barrels?|bbl)$/i;
|
|
27
|
+
const TONNE = /^(?:tonnes?|metric tons?|mt|t)$/i;
|
|
13
28
|
|
|
14
29
|
/**
|
|
15
30
|
* The currency code a unit's leading factor names: `USD` for "USD", `USX`
|
|
@@ -41,10 +56,43 @@ export function contractPriceUnit(unit: string | null | undefined, currency: str
|
|
|
41
56
|
return parts.currency.toUpperCase() === currency.trim().toUpperCase() ? unit!.trim() : null;
|
|
42
57
|
}
|
|
43
58
|
|
|
44
|
-
/** One name for a quantity however it is written: "gal" and "gallons" are a gallon, "bbl" a barrel. */
|
|
59
|
+
/** One name for a quantity however it is written: "gal" and "gallons" are a gallon, "bbl" a barrel, "t" a tonne. */
|
|
45
60
|
export function quantityIdentity(quantity: string): string {
|
|
46
61
|
const token = quantity.trim().toLowerCase();
|
|
47
|
-
return GALLON.test(token) ? "gallon" : BARREL.test(token) ? "barrel" :
|
|
62
|
+
return GALLON.test(token) ? "gallon" : BARREL.test(token) ? "barrel" : TONNE.test(token) ? "tonne"
|
|
63
|
+
: token.replace(/^shares$/, "share");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The physical quantities a price can count, each by one name: what sets a
|
|
68
|
+
* price's scale, so a per-gallon and a per-barrel price never share an axis.
|
|
69
|
+
* A share, a unit or a currency is none of them.
|
|
70
|
+
*/
|
|
71
|
+
const PHYSICAL_QUANTITIES: ReadonlyArray<readonly [RegExp, string]> = [
|
|
72
|
+
[GALLON, "gallon"],
|
|
73
|
+
[BARREL, "barrel"],
|
|
74
|
+
[TONNE, "tonne"],
|
|
75
|
+
[/^(?:dry\s+metric\s+tons?|dmtu?)$/i, "dry metric ton"],
|
|
76
|
+
[/^(?:short\s+)?tons?$/i, "short ton"],
|
|
77
|
+
[/^(?:bushels?|bu)$/i, "bushel"],
|
|
78
|
+
[/^(?:pounds?|lbs?)$/i, "pound"],
|
|
79
|
+
[/^(?:troy\s+)?(?:ounces?|oz)$/i, "troy ounce"],
|
|
80
|
+
[/^(?:hundredweight|cwt)$/i, "hundredweight"],
|
|
81
|
+
[/^(?:kilograms?|kg)$/i, "kilogram"],
|
|
82
|
+
[/^mmbtu$/i, "MMBtu"],
|
|
83
|
+
[/^therms?$/i, "therm"],
|
|
84
|
+
[/^gj$/i, "GJ"],
|
|
85
|
+
[/^mwh$/i, "MWh"],
|
|
86
|
+
[/^kwh$/i, "kWh"],
|
|
87
|
+
[/^(?:board\s+feet|mbf)$/i, "board feet"],
|
|
88
|
+
[/^(?:litres?|liters?)$/i, "litre"],
|
|
89
|
+
[/^(?:cubic\s+met(?:er|re)s?|m3)$/i, "cubic metre"],
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/** The physical quantity a unit's denominator names ("barrel" for "bbl"), or null for a share, a unit or a currency. */
|
|
93
|
+
export function physicalQuantity(quantity: string): string | null {
|
|
94
|
+
const token = quantity.trim();
|
|
95
|
+
return PHYSICAL_QUANTITIES.find(([pattern]) => pattern.test(token))?.[1] ?? null;
|
|
48
96
|
}
|
|
49
97
|
|
|
50
98
|
/** A price per gallon: "USD / gallon", "USD/gallon". */
|
|
@@ -67,3 +115,25 @@ export function isPerBarrelUnit(unit: string): boolean {
|
|
|
67
115
|
export function perBarrelUnit(unit: string): string {
|
|
68
116
|
return isPerGallonUnit(unit) ? unit.replace(/(\/\s*)\S.*$/, "$1barrel") : unit;
|
|
69
117
|
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The per-tonne unit a per-barrel price takes once multiplied by the barrels
|
|
121
|
+
* in a tonne (7.45 for gasoil), in the same wording: "USD / barrel" is
|
|
122
|
+
* "USD / tonne". Any other unit is returned as it is.
|
|
123
|
+
*/
|
|
124
|
+
export function perTonneUnit(unit: string): string {
|
|
125
|
+
return isPerBarrelUnit(unit) ? unit.replace(/(\/\s*)\S.*$/, "$1tonne") : unit;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* What a currency pair's price counts: the quote currency one unit of the
|
|
130
|
+
* base buys, so EURUSD=X is USD / EUR and USDJPY=X JPY / USD. Null for a
|
|
131
|
+
* listing that is not a pair, or whose quote is in another currency.
|
|
132
|
+
*/
|
|
133
|
+
export function currencyPairPriceUnit(symbol: string, exchange: string | undefined, currency: string | null | undefined): string | null {
|
|
134
|
+
const pair = currencyPairSpelling(symbol, exchange);
|
|
135
|
+
if (!pair || pair.ambiguous || !currency) return null;
|
|
136
|
+
const base = pair.symbol.slice(0, 3);
|
|
137
|
+
const quote = pair.symbol.slice(3, 6);
|
|
138
|
+
return quote === currency.trim().toUpperCase() ? `${quote} / ${base}` : null;
|
|
139
|
+
}
|
|
@@ -3,7 +3,8 @@ import type { PriceHistoryResult } from "../types/price-history";
|
|
|
3
3
|
import { LiveBarAccumulator } from "./live-bars";
|
|
4
4
|
import type { ChartResolutionSupport, ManualChartResolution } from "./resolution";
|
|
5
5
|
import type { DataProvider, MarketDataRequestContext } from "../types/data-provider";
|
|
6
|
-
import type { Quote, QuoteMetadata, TickerFinancials } from "../types/financials";
|
|
6
|
+
import type { CorporateActionsData, EarningsAction, Quote, QuoteMetadata, TickerFinancials } from "../types/financials";
|
|
7
|
+
import type { EarningsHistoryPayload } from "../api-client/earnings";
|
|
7
8
|
import type { FredSeriesLoadResult, FredSeriesRequest } from "../sources/gloomberb-cloud/fred-series";
|
|
8
9
|
import { chartQuoteOverrideKeyForSource } from "./live-quotes";
|
|
9
10
|
import { publicTickerKey } from "../utils/exchanges";
|
|
@@ -16,6 +17,8 @@ import type {
|
|
|
16
17
|
export interface ChartResolveSources {
|
|
17
18
|
dataProvider: DataProvider | null;
|
|
18
19
|
loadFredSeries: (request: FredSeriesRequest) => Promise<FredSeriesLoadResult>;
|
|
20
|
+
/** Dated releases from the same history the earnings calendar reads. US listings only. */
|
|
21
|
+
loadEarningsHistory?: (symbol: string) => Promise<EarningsHistoryPayload>;
|
|
19
22
|
now?: Date;
|
|
20
23
|
/** Latest streamed quote per security identity, layered over snapshot data. */
|
|
21
24
|
quoteOverrides?: ReadonlyMap<string, Quote>;
|
|
@@ -74,6 +77,9 @@ export interface LiveTailRequest {
|
|
|
74
77
|
/** Raw source data retained while live quotes recompute the chart tail. */
|
|
75
78
|
export class ChartResolveCache {
|
|
76
79
|
readonly financialsByInstrument = new Map<string, Promise<TickerFinancials | null>>();
|
|
80
|
+
readonly earningsReleasesByInstrument = new Map<string, Promise<EarningsAction[]>>();
|
|
81
|
+
/** Each listing's cash distribution history, for the charts that draw total returns. */
|
|
82
|
+
readonly distributionsByInstrument = new Map<string, Promise<CorporateActionsData | null>>();
|
|
77
83
|
readonly quoteMetadataByInstrument = new Map<string, Promise<QuoteMetadata | null>>();
|
|
78
84
|
/** Each futures symbol's contract price unit, asked once per chart once known. */
|
|
79
85
|
readonly futuresPriceUnits = new Map<string, Promise<string | null>>();
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import { aggregatePriceBars, futuresOpenMinuteOf, withBuiltBarSize } from "./bar-aggregation";
|
|
24
24
|
import { TIME_RANGES, type TimeRange } from "./range";
|
|
25
25
|
import type { DataProvider } from "../types/data-provider";
|
|
26
|
-
import type { Quote, TickerFinancials } from "../types/financials";
|
|
26
|
+
import type { CorporateActionsData, EarningsAction, Quote, TickerFinancials } from "../types/financials";
|
|
27
27
|
import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../market-data/quotes/metadata";
|
|
28
28
|
import type { FredSeriesRequest } from "../sources/gloomberb-cloud/fred-series";
|
|
29
29
|
import { fredCreditCoverageNotice } from "./economic";
|
|
@@ -44,12 +44,14 @@ import { VALUATION_CURRENCY_WARNING_PREFIX } from "./valuation-currency";
|
|
|
44
44
|
import { chartPriceHistoryIntegrityNotices } from "./market";
|
|
45
45
|
import { maxStudyWarmupPoints, resolveStudies } from "./studies";
|
|
46
46
|
import { clipSeriesToWindow } from "./alignment";
|
|
47
|
+
import { totalReturnSeries } from "./total-return";
|
|
47
48
|
import {
|
|
48
49
|
bufferPriceComparison,
|
|
49
50
|
clipPriceComparison,
|
|
50
51
|
priceComparisonBoundsForSeries,
|
|
51
52
|
priceComparisonSeriesIds,
|
|
52
53
|
resolvePriceComparison,
|
|
54
|
+
totalReturnLevelsNotice,
|
|
53
55
|
} from "./price-comparison";
|
|
54
56
|
import { chartQuoteOverrideKeyForSource } from "./live-quotes";
|
|
55
57
|
import { chartSeriesSourceKey } from "../capabilities/chart-series";
|
|
@@ -266,6 +268,13 @@ export async function resolveChartSpecData(
|
|
|
266
268
|
return result;
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
/** The price fields whose returns a distribution adds to. */
|
|
272
|
+
function totalReturnFieldId(fieldId: string): boolean {
|
|
273
|
+
const canonical = getTimeSeriesField(fieldId)?.id ?? fieldId;
|
|
274
|
+
return canonical === "market.ohlcv" || canonical === "market.close" || canonical === "market.open"
|
|
275
|
+
|| canonical === "market.high" || canonical === "market.low";
|
|
276
|
+
}
|
|
277
|
+
|
|
269
278
|
async function resolveChartSpecPass(
|
|
270
279
|
spec: ChartSpec,
|
|
271
280
|
sources: ChartResolveSources,
|
|
@@ -277,6 +286,7 @@ async function resolveChartSpecPass(
|
|
|
277
286
|
&& entry.source.kind === "security" && isFundamentalFieldId(entry.source.fieldId))
|
|
278
287
|
? [FINANCIAL_VINTAGE_NOTICE] : [];
|
|
279
288
|
const priorityWarnings: string[] = [];
|
|
289
|
+
const coverageNotices: string[] = [];
|
|
280
290
|
const delistings = new Map<string, { symbol: string; label: string; notice: string }>();
|
|
281
291
|
const baseSeriesIds = new Set(spec.series.map((entry) => entry.id));
|
|
282
292
|
const visibleSeriesIds = new Set(spec.series
|
|
@@ -300,9 +310,9 @@ async function resolveChartSpecPass(
|
|
|
300
310
|
const fieldId = getTimeSeriesField(source.fieldId)?.id ?? source.fieldId;
|
|
301
311
|
// Calendar-window research needs the same full source bundle as a period
|
|
302
312
|
// count. The extended bundle also carries the estimate history behind the
|
|
303
|
-
// forward multiples; PEG
|
|
313
|
+
// forward multiples; daily PEG also needs consecutive trailing years.
|
|
304
314
|
const statementHistory = /^(fundamental|valuation)\./.test(fieldId)
|
|
305
|
-
&& fieldId !== "valuation.pegRatio"
|
|
315
|
+
&& (fieldId !== "valuation.pegRatio" || source.period === "daily")
|
|
306
316
|
? "extended" as const : undefined;
|
|
307
317
|
const key = `${instrumentKey(source)}|history:${statementHistory ?? "default"}`;
|
|
308
318
|
let pending = cache.financialsByInstrument.get(key);
|
|
@@ -330,6 +340,53 @@ async function resolveChartSpecPass(
|
|
|
330
340
|
}
|
|
331
341
|
return pending;
|
|
332
342
|
};
|
|
343
|
+
const loadEarningsReleases = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
|
|
344
|
+
const key = instrumentKey(source);
|
|
345
|
+
let pending = cache.earningsReleasesByInstrument.get(key);
|
|
346
|
+
if (!pending) {
|
|
347
|
+
pending = (async () => {
|
|
348
|
+
const [events, history] = await Promise.allSettled([
|
|
349
|
+
sources.dataProvider?.getCorporateActions?.(source.instrument.symbol, source.instrument.exchange, requestContext(source)),
|
|
350
|
+
isUsListingExchange(source.instrument.exchange) ? sources.loadEarningsHistory?.(source.instrument.symbol) : undefined,
|
|
351
|
+
]);
|
|
352
|
+
const releases: EarningsAction[] = [];
|
|
353
|
+
if (history.status === "fulfilled") for (const report of history.value?.reports ?? []) {
|
|
354
|
+
if (report.dateStatus !== "reported") continue;
|
|
355
|
+
releases.push({ date: report.date, dateType: "announcement", dateStatus: "reported",
|
|
356
|
+
fiscalPeriod: report.fiscalPeriod ?? undefined, reportedAt: report.reportedAt ?? undefined,
|
|
357
|
+
time: report.timingSource !== "history" ? report.timing ?? undefined : undefined });
|
|
358
|
+
}
|
|
359
|
+
else logFailure("time-series-resolve", "Loading earnings release history failed")(history.reason);
|
|
360
|
+
if (events.status === "fulfilled") releases.push(...events.value?.earnings ?? []);
|
|
361
|
+
else logFailure("time-series-resolve", "Loading earnings release events failed")(events.reason);
|
|
362
|
+
if (events.status === "rejected" || history.status === "rejected") cache.earningsReleasesByInstrument.delete(key);
|
|
363
|
+
return releases;
|
|
364
|
+
})();
|
|
365
|
+
cache.earningsReleasesByInstrument.set(key, pending);
|
|
366
|
+
cache.revision += 1;
|
|
367
|
+
}
|
|
368
|
+
return pending;
|
|
369
|
+
};
|
|
370
|
+
// Total returns reinvest the cash a listing paid; each listing's history is read once per chart.
|
|
371
|
+
const loadDistributions = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
|
|
372
|
+
const key = instrumentKey(source);
|
|
373
|
+
let pending = cache.distributionsByInstrument.get(key);
|
|
374
|
+
if (!pending) {
|
|
375
|
+
pending = (async (): Promise<CorporateActionsData | null> => {
|
|
376
|
+
const read = sources.dataProvider?.getCorporateActions;
|
|
377
|
+
if (!read) return null;
|
|
378
|
+
try {
|
|
379
|
+
return await read.call(sources.dataProvider, source.instrument.symbol, source.instrument.exchange, requestContext(source));
|
|
380
|
+
} catch (error) {
|
|
381
|
+
cache.distributionsByInstrument.delete(key);
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
cache.distributionsByInstrument.set(key, pending);
|
|
386
|
+
cache.revision += 1;
|
|
387
|
+
}
|
|
388
|
+
return pending;
|
|
389
|
+
};
|
|
333
390
|
const loadQuoteMetadata = (source: Extract<ChartSeriesSpec["source"], { kind: "security" }>) => {
|
|
334
391
|
const key = instrumentKey(source);
|
|
335
392
|
let pending = cache.quoteMetadataByInstrument.get(key);
|
|
@@ -496,8 +553,16 @@ async function resolveChartSpecPass(
|
|
|
496
553
|
all = false,
|
|
497
554
|
) => {
|
|
498
555
|
const support = await loadResolutionSupport(source, priceOnly);
|
|
499
|
-
const
|
|
556
|
+
const dailyValuation = source.period === "daily" && source.fieldId.startsWith("valuation.");
|
|
557
|
+
const sourceResolution = dailyValuation ? "1d" : initialResolution;
|
|
558
|
+
const maxRange = getSupportMaxRange(support, sourceResolution);
|
|
500
559
|
const ahead = movedWindow ? barsAhead(requestVisibleBounds, maxRange, referenceNow.getTime()) : 0;
|
|
560
|
+
// Bars ahead that reach the present end wherever the clock is on this pass.
|
|
561
|
+
// Such a window is named by its start and the present, so every pass shares
|
|
562
|
+
// one acquisition and the stale-copy refresh below keeps it current. Named
|
|
563
|
+
// by the clock, each live pass fetched and kept another copy of the history.
|
|
564
|
+
const toPresent = ahead > 0 && requestVisibleBounds.end !== null
|
|
565
|
+
&& requestVisibleBounds.end + ahead >= referenceNow.getTime();
|
|
501
566
|
const durationEnd = rangeDurationBounds(spec, initialCalculationBounds).end;
|
|
502
567
|
const historyBounds = clampHistoryBoundsToSupport(
|
|
503
568
|
ahead > 0 && initialCalculationBounds.end !== null
|
|
@@ -517,8 +582,8 @@ async function resolveChartSpecPass(
|
|
|
517
582
|
explicitWindow: hasExplicitWindow,
|
|
518
583
|
authoredWindow: hasExplicitWindow && !movedWindow,
|
|
519
584
|
fallbackRange,
|
|
520
|
-
resolution:
|
|
521
|
-
allowProviderDefaultFallback: spec.viewport.resolution === "auto",
|
|
585
|
+
resolution: sourceResolution,
|
|
586
|
+
allowProviderDefaultFallback: !dailyValuation && spec.viewport.resolution === "auto",
|
|
522
587
|
support,
|
|
523
588
|
requiredWarmupPoints: maxStudyWarmupPoints(spec.studies),
|
|
524
589
|
...(extendedHoursFor(source, initialResolution) ? { historySession: "extended" as const } : {}),
|
|
@@ -531,7 +596,9 @@ async function resolveChartSpecPass(
|
|
|
531
596
|
request.allowProviderDefaultFallback ? "auto" : "manual",
|
|
532
597
|
isMarketFieldId(source.fieldId) ? source.period ?? "auto" : "auto",
|
|
533
598
|
...(request.explicitWindow
|
|
534
|
-
?
|
|
599
|
+
? toPresent
|
|
600
|
+
? [initialCalculationBounds.start ?? "open", "present"]
|
|
601
|
+
: [request.bounds.start ?? "open", request.bounds.end ?? "open"]
|
|
535
602
|
: []),
|
|
536
603
|
// A moved view can chart retained bars that the same authored dates cannot.
|
|
537
604
|
...(request.explicitWindow && !request.authoredWindow && !request.allowProviderDefaultFallback ? ["moved"] : []),
|
|
@@ -774,7 +841,10 @@ async function resolveChartSpecPass(
|
|
|
774
841
|
const result = baseEconomicSeries(seriesSpec, fred, index);
|
|
775
842
|
const coverageNotice = fredCreditCoverageNotice(seriesSpec.source.seriesId, fred.data.info,
|
|
776
843
|
requestVisibleBounds.start, fred.data.observations);
|
|
777
|
-
if (result && coverageNotice)
|
|
844
|
+
if (result && coverageNotice) {
|
|
845
|
+
priorityWarnings.push(`${result.label}: ${coverageNotice}`);
|
|
846
|
+
coverageNotices.push(`${result.label}: ${coverageNotice}`);
|
|
847
|
+
}
|
|
778
848
|
const freshnessWarning = staleFredWarning(fred);
|
|
779
849
|
if (result && freshnessWarning) priorityWarnings.push(`${result.label}: ${freshnessWarning}`);
|
|
780
850
|
return result;
|
|
@@ -782,8 +852,9 @@ async function resolveChartSpecPass(
|
|
|
782
852
|
|
|
783
853
|
const source = seriesSpec.source;
|
|
784
854
|
const marketField = isMarketFieldId(source.fieldId);
|
|
855
|
+
const dailyValuation = source.period === "daily" && source.fieldId.startsWith("valuation.");
|
|
785
856
|
const quoteDerivedValuation = valuationSeriesUsesLiveQuote(source.fieldId);
|
|
786
|
-
const historyPricedValuation = valuationSeriesUsesPriceHistory(source.fieldId);
|
|
857
|
+
const historyPricedValuation = valuationSeriesUsesPriceHistory(source.fieldId, source.period);
|
|
787
858
|
// Forward P/E only prices a history when the bundle carries estimates;
|
|
788
859
|
// otherwise the provider's single figure stands and history is not required.
|
|
789
860
|
const forwardPE = getTimeSeriesField(source.fieldId)?.id === "valuation.forwardPE";
|
|
@@ -833,6 +904,10 @@ async function resolveChartSpecPass(
|
|
|
833
904
|
let merged = financials ?? (history ? emptyFinancials() : null);
|
|
834
905
|
if (merged && quoteOverride) merged = { ...merged, quote: latestQuote(merged.quote, quoteOverride) };
|
|
835
906
|
if (!merged) throw new Error(`No financial data is available for ${instrumentLabel(source)}.`);
|
|
907
|
+
if (dailyValuation && source.fieldId !== "valuation.forwardPE" && source.fieldId !== "valuation.realizedNtmPE"
|
|
908
|
+
&& merged.earningsReleases === undefined) {
|
|
909
|
+
merged = { ...merged, earningsReleases: await loadEarningsReleases(resolvedSource) };
|
|
910
|
+
}
|
|
836
911
|
// Historical labels can retain listing facts after the live price is
|
|
837
912
|
// unavailable. Reuse existing facts before requesting a price-free snapshot.
|
|
838
913
|
const existingMetadata = merged.quoteMetadata && quoteMetadataMatchesTarget(merged.quoteMetadata, resolvedSource.instrument.symbol, resolvedSource.instrument.exchange)
|
|
@@ -883,17 +958,23 @@ async function resolveChartSpecPass(
|
|
|
883
958
|
// Keep provider history separate from quote-extended display bars. A live
|
|
884
959
|
// mark is not a daily closing observation, including on snapshot replay.
|
|
885
960
|
const historical = history ? { ...merged, priceHistory: history.points } : merged;
|
|
886
|
-
sources.onSecurityData?.(resolvedSpec, history?.session || realizedVolInputs.has(seriesSpec.id) ? historical : sourceBars, history !== null);
|
|
961
|
+
sources.onSecurityData?.(resolvedSpec, dailyValuation || history?.session || realizedVolInputs.has(seriesSpec.id) ? historical : sourceBars, history !== null);
|
|
887
962
|
const base = baseSecuritySeries(
|
|
888
963
|
resolvedSpec,
|
|
889
|
-
merged,
|
|
964
|
+
dailyValuation ? historical : merged,
|
|
890
965
|
index,
|
|
891
966
|
history?.resolution,
|
|
892
967
|
quoteMetadata,
|
|
893
968
|
contractUnit,
|
|
894
969
|
);
|
|
895
970
|
if (!base) throw new Error(`Unknown field ${source.fieldId}.`);
|
|
896
|
-
|
|
971
|
+
let result = builtBars ? withBuiltBarSize(base, builtBars) : base;
|
|
972
|
+
if (spec.viewport.totalReturn === true && totalReturnFieldId(source.fieldId)) {
|
|
973
|
+
const outcome = await totalReturnSeries(result, () => loadDistributions(resolvedSource),
|
|
974
|
+
merged.quote?.currency || quoteMetadata?.currency, referenceNow);
|
|
975
|
+
result = outcome.series;
|
|
976
|
+
if (outcome.notice) warnings.push(`${instrumentLabel(resolvedSource)}: ${outcome.notice}`);
|
|
977
|
+
}
|
|
897
978
|
if (marketField && history?.historyStart !== undefined && Number.isFinite(history.historyStart)) {
|
|
898
979
|
result.historyStart = new Date(history.historyStart);
|
|
899
980
|
}
|
|
@@ -909,7 +990,7 @@ async function resolveChartSpecPass(
|
|
|
909
990
|
if (historicalResult) historicalPriceSeries.set(seriesSpec.id, historicalResult);
|
|
910
991
|
}
|
|
911
992
|
const reportedForwardPE = merged.fundamentals?.forwardPE;
|
|
912
|
-
if (source.fieldId === "valuation.forwardPE" && merged.epsEstimates) {
|
|
993
|
+
if (source.fieldId === "valuation.forwardPE" && merged.epsEstimates && !dailyValuation) {
|
|
913
994
|
warnings.push(FORWARD_PE_BASIS_NOTICE);
|
|
914
995
|
} else if (source.fieldId === "valuation.forwardPE" && reportedForwardPE != null
|
|
915
996
|
&& Number.isFinite(reportedForwardPE) && reportedForwardPE <= 0) {
|
|
@@ -918,7 +999,7 @@ async function resolveChartSpecPass(
|
|
|
918
999
|
if (source.fieldId === "valuation.realizedNtmPE" && merged.epsEstimates) {
|
|
919
1000
|
warnings.push(REALIZED_NTM_PE_BASIS_NOTICE);
|
|
920
1001
|
}
|
|
921
|
-
if (isFundamentalFieldId(source.fieldId) && fundamentalSeriesUsesAvailabilityFallback(merged, source)) {
|
|
1002
|
+
if (!dailyValuation && isFundamentalFieldId(source.fieldId) && fundamentalSeriesUsesAvailabilityFallback(merged, source)) {
|
|
922
1003
|
result.warning = [result.warning, "Publication dates are unavailable for some observations; period-end dates are used as a fallback."].filter(Boolean).join(" ");
|
|
923
1004
|
}
|
|
924
1005
|
return result;
|
|
@@ -943,9 +1024,24 @@ async function resolveChartSpecPass(
|
|
|
943
1024
|
? latestSessionView(primaryRaw, rawSeries.filter((entry) => entry.observationKind === "market"),
|
|
944
1025
|
extendedHistories.has(primaryRaw?.id ?? ""), quoteReferences.get(primaryRaw?.id ?? ""))
|
|
945
1026
|
: null;
|
|
1027
|
+
const dailyValuationView = spec.series.filter((entry) => entry.visible !== false);
|
|
1028
|
+
const shortDailyValuation = !hasExplicitWindow && (spec.viewport.range === "1D" || spec.viewport.range === "1W")
|
|
1029
|
+
&& rawSeries.every(series => series.nativeFrequency === "daily")
|
|
1030
|
+
&& dailyValuationView.length > 0 && dailyValuationView.every(({ source }) => source.kind === "security"
|
|
1031
|
+
&& source.period === "daily" && source.fieldId.startsWith("valuation."));
|
|
1032
|
+
const dailyTimes = shortDailyValuation ? [...new Set(rawSeries.flatMap((entry) => entry.points.map((point) => point.date.getTime())))]
|
|
1033
|
+
.filter(Number.isFinite).sort((a, b) => a - b) : [];
|
|
1034
|
+
const latestDaily = dailyTimes.at(-1);
|
|
1035
|
+
// A daily multiple has no intraday observations. The day tab compares its
|
|
1036
|
+
// last two closes; the week tab ends at the last close even on a weekend.
|
|
1037
|
+
const dailyBounds = latestDaily === undefined ? null : {
|
|
1038
|
+
start: spec.viewport.range === "1D" ? dailyTimes.at(-2) ?? latestDaily - CHART_RESOLUTION_STEP_MS["1d"]
|
|
1039
|
+
: latestDaily - 7 * CHART_RESOLUTION_STEP_MS["1d"],
|
|
1040
|
+
end: latestDaily,
|
|
1041
|
+
};
|
|
946
1042
|
const bounds = hasExplicitWindow
|
|
947
1043
|
? requestVisibleBounds
|
|
948
|
-
: sessionView?.bounds ?? (isDatasetChart(spec)
|
|
1044
|
+
: dailyBounds ?? sessionView?.bounds ?? (isDatasetChart(spec)
|
|
949
1045
|
? endAtNewestObservation(initialVisibleBounds, rawSeries)
|
|
950
1046
|
: followLatestMarketObservation(initialVisibleBounds, rawSeries));
|
|
951
1047
|
if (sessionView?.priorClose != null && primaryRaw?.transform === "raw"
|
|
@@ -1024,6 +1120,9 @@ async function resolveChartSpecPass(
|
|
|
1024
1120
|
bounds, comparisonResolution, requestBounds ? null : spec.viewport.dateWindow ?? null,
|
|
1025
1121
|
);
|
|
1026
1122
|
if (priceComparison) warnings.push(priceComparison.notice);
|
|
1123
|
+
// A raw-price chart of total returns is no comparison, and its levels say what they are instead.
|
|
1124
|
+
const returnBasisNotice = priceComparison ? null : totalReturnLevelsNotice(spec, rawSeries);
|
|
1125
|
+
if (returnBasisNotice) warnings.push(returnBasisNotice);
|
|
1027
1126
|
warnings.push(...chartPriceHistoryIntegrityNotices(priceHistoryIntegrity));
|
|
1028
1127
|
warnings.push(...financialPeriodCoverageWarnings(financialPeriodCoverage(spec, resolved)));
|
|
1029
1128
|
const resolvedById = new Map(resolved.map((entry) => [entry.id, entry] as const));
|
|
@@ -1081,6 +1180,8 @@ async function resolveChartSpecPass(
|
|
|
1081
1180
|
loading: false,
|
|
1082
1181
|
errors,
|
|
1083
1182
|
warnings: [...new Set([...priorityWarnings, ...warnings])],
|
|
1183
|
+
...(coverageNotices.length ? { coverageNotices: [...new Set(coverageNotices)] } : {}),
|
|
1184
|
+
...(returnBasisNotice ? { returnBasisNotice } : {}),
|
|
1084
1185
|
...(delistings.size ? { delistings: [...delistings.values()] } : {}),
|
|
1085
1186
|
viewport,
|
|
1086
1187
|
resolution,
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
type PriceComparison,
|
|
14
14
|
} from "./price-comparison";
|
|
15
15
|
import { parsePublicTickerKey, publicTickerKey } from "../utils/exchanges";
|
|
16
|
+
import { physicalQuantity } from "./price-units";
|
|
17
|
+
import { isCurrencyCode } from "../utils/currency-pair";
|
|
16
18
|
import type {
|
|
17
19
|
ChartSeriesSpec,
|
|
18
20
|
ChartSpec,
|
|
@@ -40,33 +42,65 @@ export function resolvePriceHistoryIntegrity(
|
|
|
40
42
|
];
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
* What sets a price's scale beyond its currency: the physical quantity it
|
|
47
|
+
* counts ("gallon" for USD / gallon, "barrel" for USD/bbl), or "currency" for
|
|
48
|
+
* an FX rate (USD / EUR). Null for a bare currency, a share or a unit, which
|
|
49
|
+
* scale as any price in their currency does.
|
|
50
|
+
*/
|
|
51
|
+
function scaleQuantity(unit: string): string | null {
|
|
52
|
+
const slash = unit.indexOf("/");
|
|
53
|
+
if (slash < 0) return null;
|
|
54
|
+
const per = unit.slice(slash + 1).trim();
|
|
55
|
+
return physicalQuantity(per) ?? (/^[A-Z]{3}$/.test(per) && isCurrencyCode(per) ? "currency" : null);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Each series' axis: one a spec names, else the side whose series count the
|
|
60
|
+
* same unit. Prices in one currency per different physical quantities
|
|
61
|
+
* (heating oil USD / gallon beside Brent USD / barrel), or a price beside an
|
|
62
|
+
* FX rate, scale on axes of their own; per-share and per-unit prices, and FX
|
|
63
|
+
* rates quoted in one currency, share one as any prices in that currency do.
|
|
64
|
+
*/
|
|
43
65
|
export function assignAxes(
|
|
44
66
|
series: ResolvedSeries[],
|
|
45
67
|
specs: readonly { id: string; axis: ChartSeriesSpec["axis"] }[],
|
|
46
68
|
warnings: string[],
|
|
47
69
|
): ResolvedSeries[] {
|
|
70
|
+
type AxisUnit = { group: string; quantity: string | null };
|
|
48
71
|
const requested = new Map(specs.map((spec) => [spec.id, spec.axis] as const));
|
|
49
|
-
const groupsByPanel = new Map<string, Partial<Record<"left" | "right",
|
|
72
|
+
const groupsByPanel = new Map<string, Partial<Record<"left" | "right", AxisUnit>>>();
|
|
50
73
|
return series.map((entry) => {
|
|
51
74
|
const groups = groupsByPanel.get(entry.panelId) ?? {};
|
|
52
75
|
groupsByPanel.set(entry.panelId, groups);
|
|
76
|
+
const quantity = scaleQuantity(entry.unit);
|
|
77
|
+
const sameGroup = (side: "left" | "right") => groups[side]?.group === entry.unitGroup;
|
|
78
|
+
const fits = (side: "left" | "right") => {
|
|
79
|
+
const held = groups[side];
|
|
80
|
+
return sameGroup(side) && (!held!.quantity || !quantity || held!.quantity === quantity);
|
|
81
|
+
};
|
|
53
82
|
const preferred = requested.get(entry.id);
|
|
54
83
|
let axis: "left" | "right";
|
|
55
84
|
if (preferred === "left" || preferred === "right") {
|
|
56
85
|
axis = preferred;
|
|
57
|
-
} else if (
|
|
86
|
+
} else if (fits("left")) {
|
|
58
87
|
axis = "left";
|
|
59
|
-
} else if (
|
|
88
|
+
} else if (fits("right")) {
|
|
60
89
|
axis = "right";
|
|
61
90
|
} else if (!groups.left) {
|
|
62
91
|
axis = "left";
|
|
63
|
-
} else {
|
|
92
|
+
} else if (!groups.right || !sameGroup("left")) {
|
|
64
93
|
axis = "right";
|
|
94
|
+
} else {
|
|
95
|
+
// Both axes taken: a third quantity in the same currency shares as it would without them.
|
|
96
|
+
axis = "left";
|
|
65
97
|
}
|
|
66
|
-
|
|
98
|
+
const held = groups[axis];
|
|
99
|
+
if (held && held.group !== entry.unitGroup) {
|
|
67
100
|
warnings.push(`${entry.label} shares the ${axis} axis with a different unit; choose an explicit panel for independent scaling.`);
|
|
68
101
|
}
|
|
69
|
-
groups[axis]
|
|
102
|
+
if (!held) groups[axis] = { group: entry.unitGroup, quantity };
|
|
103
|
+
else if (!held.quantity && quantity && fits(axis)) held.quantity = quantity;
|
|
70
104
|
return { ...entry, axis };
|
|
71
105
|
});
|
|
72
106
|
}
|
|
@@ -136,3 +136,14 @@ export function withSessionBar(
|
|
|
136
136
|
}
|
|
137
137
|
return points;
|
|
138
138
|
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The daily bars `history` shows as final, which alerts on a daily close read
|
|
142
|
+
* too: the bars as served, less the bar of a session still trading
|
|
143
|
+
* (withSessionBar, which `history` rebuilds from the quote and marks partial)
|
|
144
|
+
* and any bar the source marks partial. A close `history` prints without
|
|
145
|
+
* `partial` is final here, and one it prints with it is not.
|
|
146
|
+
*/
|
|
147
|
+
export function finalDailyBars(points: PricePoint[], options: SessionBarOptions): PricePoint[] {
|
|
148
|
+
return withSessionBar(points, null, options).filter((point) => !point.partial);
|
|
149
|
+
}
|
package/src/time-series/spec.ts
CHANGED
|
@@ -37,7 +37,7 @@ import { isLeverageFactor } from "./leverage";
|
|
|
37
37
|
const RANGE_SET = new Set<TimeRange>(TIME_RANGES);
|
|
38
38
|
const PERIODS = new Set<SeriesPeriod>(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
|
|
39
39
|
// `band` only stays on a capability series: a field's styles and an economic series' leave it out.
|
|
40
|
-
const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc", "band"]);
|
|
40
|
+
const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "hollow", "heikin-ashi", "ohlc", "hlc", "band"]);
|
|
41
41
|
const ECONOMIC_STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points"]);
|
|
42
42
|
const TRANSFORMS = new Set<SeriesTransform>(["raw", "percent", "index100", "yoy", "qoq", "log"]);
|
|
43
43
|
const AXES = new Set<SeriesAxis>(["auto", "left", "right"]);
|
|
@@ -71,7 +71,7 @@ export function isDatasetChart(spec: Pick<ChartSpec, "series">): boolean {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export function isOhlcSeriesStyle(style: SeriesStyle): boolean {
|
|
74
|
-
return style === "candles" || style === "ohlc" || style === "hlc";
|
|
74
|
+
return style === "candles" || style === "hollow" || style === "heikin-ashi" || style === "ohlc" || style === "hlc";
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export function coerceSeriesTransformForStyle(
|
|
@@ -333,6 +333,7 @@ export function normalizeChartSpec(value: unknown, fallback: ChartSpec = DEFAULT
|
|
|
333
333
|
? Math.min(10_000, Math.floor(viewport.maxPoints))
|
|
334
334
|
: undefined,
|
|
335
335
|
...(viewport?.extendedHours === true ? { extendedHours: true } : {}),
|
|
336
|
+
...(viewport?.totalReturn === true ? { totalReturn: true } : {}),
|
|
336
337
|
},
|
|
337
338
|
panels,
|
|
338
339
|
series,
|
|
@@ -448,7 +449,8 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
|
|
|
448
449
|
));
|
|
449
450
|
}
|
|
450
451
|
if ((definition.id.startsWith("fundamental.") || definition.id.startsWith("valuation."))
|
|
451
|
-
&& (entry.source.period === "daily"
|
|
452
|
+
&& ((entry.source.period === "daily" && !definition.id.startsWith("valuation."))
|
|
453
|
+
|| entry.source.period === "weekly" || entry.source.period === "monthly")) {
|
|
452
454
|
errors.push(issue(`${path}.source.period`, "unsupported-period", "Financial fields support automatic, quarterly, annual, or TTM periods."));
|
|
453
455
|
}
|
|
454
456
|
const groups = unitGroupsByPanel.get(entry.panelId) ?? new Set<string>();
|