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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ANONYMOUS_ID_STORAGE_KEY, ATTRIBUTION_STORAGE_KEY } from "../../../api-client/research-activity";
|
|
2
|
+
import { SYNC_CLIENT_ID_STORAGE_KEY } from "../../../sync/controller";
|
|
3
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
4
|
+
import type { DesktopViewIds } from "../shared/protocol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What the desktop view keeps only in its WebView storage: the id Cloud sync
|
|
8
|
+
* knows this device by, and the visitor id and first touch a gloom.sh link
|
|
9
|
+
* handed over. macOS and Windows keep that storage under the app's bundle
|
|
10
|
+
* identifier, so a build under another identifier would start without them.
|
|
11
|
+
* The Bun process keeps a copy in the data folder (bun/desktop/view-ids.ts),
|
|
12
|
+
* and a view whose storage lacks one takes it back from there.
|
|
13
|
+
*/
|
|
14
|
+
const VIEW_ID_KEYS = [SYNC_CLIENT_ID_STORAGE_KEY, ANONYMOUS_ID_STORAGE_KEY, ATTRIBUTION_STORAGE_KEY] as const;
|
|
15
|
+
|
|
16
|
+
interface ViewIdStorage {
|
|
17
|
+
getItem(key: string): string | null;
|
|
18
|
+
setItem(key: string, value: string): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The view's WebView storage, or null where it is refused. */
|
|
22
|
+
export function viewIdStorage(): ViewIdStorage | null {
|
|
23
|
+
try {
|
|
24
|
+
return globalThis.localStorage ?? null;
|
|
25
|
+
} catch {
|
|
26
|
+
// Intentional ignore: without storage there is nothing to restore or keep.
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readViewIds(storage: ViewIdStorage): DesktopViewIds {
|
|
32
|
+
const ids: DesktopViewIds = {};
|
|
33
|
+
for (const key of VIEW_ID_KEYS) {
|
|
34
|
+
const value = storage.getItem(key);
|
|
35
|
+
if (value) ids[key] = value;
|
|
36
|
+
}
|
|
37
|
+
return ids;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sameIds(left: DesktopViewIds, right: DesktopViewIds): boolean {
|
|
41
|
+
const keys = Object.keys(left);
|
|
42
|
+
return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Puts the ids kept on disk into `storage` where it has none. One it has is never replaced. */
|
|
46
|
+
export function restoreViewIds(storage: ViewIdStorage, kept: DesktopViewIds | undefined): void {
|
|
47
|
+
try {
|
|
48
|
+
for (const key of VIEW_ID_KEYS) {
|
|
49
|
+
const value = kept?.[key];
|
|
50
|
+
if (value && !storage.getItem(key)) storage.setItem(key, value);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logFailure("desktop-view-ids", "Could not restore the view's ids")(error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A function that sends the ids in `storage` to be kept on disk when they
|
|
59
|
+
* differ from the copy there (`kept`, from `init`). It reads three keys, so it
|
|
60
|
+
* can run whenever one of them may have been written. A save that fails is
|
|
61
|
+
* tried again at the next call.
|
|
62
|
+
*/
|
|
63
|
+
export function createViewIdMirror(
|
|
64
|
+
storage: ViewIdStorage,
|
|
65
|
+
kept: DesktopViewIds | undefined,
|
|
66
|
+
save: (ids: DesktopViewIds) => Promise<unknown>,
|
|
67
|
+
): () => void {
|
|
68
|
+
let onDisk: DesktopViewIds = { ...kept };
|
|
69
|
+
return () => {
|
|
70
|
+
let ids: DesktopViewIds;
|
|
71
|
+
try {
|
|
72
|
+
ids = readViewIds(storage);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
logFailure("desktop-view-ids", "Could not read the view's ids")(error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (sameIds(ids, onDisk)) return;
|
|
78
|
+
const previous = onDisk;
|
|
79
|
+
onDisk = ids;
|
|
80
|
+
save(ids).catch((error: unknown) => {
|
|
81
|
+
if (onDisk === ids) onDisk = previous;
|
|
82
|
+
logFailure("desktop-view-ids", "Could not send the view's ids to be kept")(error);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -38,6 +38,9 @@ import {
|
|
|
38
38
|
} from "../../telemetry/crash-reports-node";
|
|
39
39
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
40
40
|
import { installUpdateFailureReporter } from "../../telemetry/update-failures";
|
|
41
|
+
import { installStallReporter, recordStall } from "../../telemetry/stall-reports";
|
|
42
|
+
import { createNodeStallReporterHost } from "../../telemetry/stall-reports-node";
|
|
43
|
+
import { setStallSink } from "../../utils/main-thread-stall";
|
|
41
44
|
import { createNodeUpdateFailureHost } from "../../telemetry/update-failures-node";
|
|
42
45
|
import { terminalInstallKind } from "../../updater";
|
|
43
46
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
@@ -124,6 +127,8 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
124
127
|
installKind: terminalInstallKind(),
|
|
125
128
|
getDataDir: () => config.dataDir,
|
|
126
129
|
}));
|
|
130
|
+
installStallReporter(createNodeStallReporterHost({ getDataDir: () => config.dataDir }));
|
|
131
|
+
setStallSink((event) => recordStall("terminal", event));
|
|
127
132
|
installUsageCounter({
|
|
128
133
|
surface: "terminal",
|
|
129
134
|
os: describeNodeOs(),
|
|
@@ -4,9 +4,13 @@ import { useSyncExternalStore } from "react";
|
|
|
4
4
|
import { colors } from "../../theme/colors";
|
|
5
5
|
import { createToastStore, type ToastHost, type ToastTone } from "../../ui/toast";
|
|
6
6
|
import { useActionShortcut } from "../../ui";
|
|
7
|
+
import { displayWidth } from "../../utils/format";
|
|
7
8
|
|
|
8
9
|
const toastStore = createToastStore();
|
|
9
10
|
|
|
11
|
+
/** The cells beside a toast's text on its first line: border and padding each side, the icon, the close mark and the gaps after and before them. */
|
|
12
|
+
const TOAST_CHROME_CELLS = 8;
|
|
13
|
+
|
|
10
14
|
function toneColor(tone: ToastTone): string {
|
|
11
15
|
if (tone === "success") return colors.positive;
|
|
12
16
|
if (tone === "error") return colors.negative;
|
|
@@ -44,56 +48,80 @@ function ToastViewport({ position = "bottom-right" }: { position?: string }) {
|
|
|
44
48
|
flexDirection="column"
|
|
45
49
|
gap={1}
|
|
46
50
|
>
|
|
47
|
-
{shownToasts.map((toast) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
51
|
+
{shownToasts.map((toast) => {
|
|
52
|
+
const actionLabel = toast.options?.action
|
|
53
|
+
? toast.id === newestActionId && actionShortcut
|
|
54
|
+
? `[${toast.options.action.label} ${actionShortcut}]`
|
|
55
|
+
: `[${toast.options.action.label}]`
|
|
56
|
+
: null;
|
|
57
|
+
const secondaryLabel = toast.options?.secondaryAction ? `[${toast.options.secondaryAction.label}]` : null;
|
|
58
|
+
const actionCells = [actionLabel, secondaryLabel].reduce((cells, label) => cells + (label ? displayWidth(label) + 1 : 0), 0);
|
|
59
|
+
// Actions sit beside text that fits on one line with them. Beside longer text they would
|
|
60
|
+
// squeeze it into a narrow column, or out of sight, so they go under it and it wraps at full width.
|
|
61
|
+
const inline = actionCells === 0 || displayWidth(toast.body) + actionCells + TOAST_CHROME_CELLS <= maxWidth;
|
|
62
|
+
const actions = (
|
|
63
|
+
<>
|
|
64
|
+
{toast.options?.action && (
|
|
65
|
+
<text
|
|
66
|
+
fg={colors.textBright}
|
|
67
|
+
attributes={1}
|
|
68
|
+
onMouseDown={(event) => {
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
toast.options?.action?.onClick();
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{actionLabel}
|
|
74
|
+
</text>
|
|
75
|
+
)}
|
|
76
|
+
{toast.options?.secondaryAction && (
|
|
77
|
+
<text
|
|
78
|
+
fg={colors.textMuted}
|
|
79
|
+
onMouseDown={(event) => {
|
|
80
|
+
event.stopPropagation();
|
|
81
|
+
toast.options?.secondaryAction?.onClick();
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{secondaryLabel}
|
|
85
|
+
</text>
|
|
86
|
+
)}
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
return (
|
|
90
|
+
<box
|
|
91
|
+
key={toast.id}
|
|
92
|
+
width="100%"
|
|
93
|
+
border
|
|
94
|
+
borderStyle="single"
|
|
95
|
+
borderColor={toneColor(toast.tone)}
|
|
96
|
+
backgroundColor={colors.panel}
|
|
97
|
+
paddingX={1}
|
|
98
|
+
flexDirection="row"
|
|
99
|
+
gap={1}
|
|
100
|
+
>
|
|
101
|
+
<text fg={toneColor(toast.tone)}>{toneIcon(toast.tone)}</text>
|
|
102
|
+
{inline ? (
|
|
103
|
+
<>
|
|
104
|
+
<text fg={colors.text} flexGrow={1} flexShrink={1} flexBasis={0} wrapMode="word">{toast.body}</text>
|
|
105
|
+
{actions}
|
|
106
|
+
</>
|
|
107
|
+
) : (
|
|
108
|
+
<box flexGrow={1} flexShrink={1} flexBasis={0} flexDirection="column">
|
|
109
|
+
<text fg={colors.text} wrapMode="word">{toast.body}</text>
|
|
110
|
+
<box flexDirection="row" gap={1}>{actions}</box>
|
|
111
|
+
</box>
|
|
112
|
+
)}
|
|
76
113
|
<text
|
|
77
114
|
fg={colors.textMuted}
|
|
78
115
|
onMouseDown={(event) => {
|
|
79
116
|
event.stopPropagation();
|
|
80
|
-
toast.
|
|
117
|
+
toastStore.dismiss(toast.id);
|
|
81
118
|
}}
|
|
82
119
|
>
|
|
83
|
-
|
|
120
|
+
×
|
|
84
121
|
</text>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
onMouseDown={(event) => {
|
|
89
|
-
event.stopPropagation();
|
|
90
|
-
toastStore.dismiss(toast.id);
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
×
|
|
94
|
-
</text>
|
|
95
|
-
</box>
|
|
96
|
-
))}
|
|
122
|
+
</box>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
97
125
|
</box>
|
|
98
126
|
);
|
|
99
127
|
}
|
package/src/series/registry.ts
CHANGED
|
@@ -63,6 +63,7 @@ export function seriesCatalogEntry(series: RegisteredSeries): SeriesCatalogEntry
|
|
|
63
63
|
...(definition.description ? { description: definition.description } : {}),
|
|
64
64
|
...(definition.publisher ? { publisher: definition.publisher } : {}),
|
|
65
65
|
...(definition.url ? { url: definition.url } : {}),
|
|
66
|
+
...(definition.limitation ? { limitation: definition.limitation } : {}),
|
|
66
67
|
...(definition.input ? { input: definition.input } : {}),
|
|
67
68
|
...(definition.placement ? { placement: definition.placement } : {}),
|
|
68
69
|
...(definition.lines?.length ? {
|
package/src/series/types.ts
CHANGED
|
@@ -133,6 +133,11 @@ interface SeriesDefinitionBase {
|
|
|
133
133
|
publisher?: string;
|
|
134
134
|
/** The publisher's page for the series. */
|
|
135
135
|
url?: string;
|
|
136
|
+
/**
|
|
137
|
+
* A limit on the data the publisher sets, such as a cap on how far back its
|
|
138
|
+
* history goes. One sentence: the catalog lists it beside the series.
|
|
139
|
+
*/
|
|
140
|
+
limitation?: string;
|
|
136
141
|
/** Ask for a ticker, an option or a crypto pair before charting. */
|
|
137
142
|
input?: SeriesInput;
|
|
138
143
|
/**
|
|
@@ -203,6 +208,7 @@ export interface SeriesCatalogEntry {
|
|
|
203
208
|
description?: string;
|
|
204
209
|
publisher?: string;
|
|
205
210
|
url?: string;
|
|
211
|
+
limitation?: string;
|
|
206
212
|
input?: SeriesInput;
|
|
207
213
|
placement?: SeriesPlacement;
|
|
208
214
|
/** The lines of a series that draws several, each with the expression that charts it alone. */
|
package/src/series/validate.ts
CHANGED
|
@@ -136,6 +136,7 @@ export function validateSeriesDefinition(definition: SeriesDefinition, context:
|
|
|
136
136
|
checkLabel(id, "group", definition.group, 32, context);
|
|
137
137
|
checkLabel(id, "description", definition.description, 300, context);
|
|
138
138
|
checkLabel(id, "publisher", definition.publisher, 60, context);
|
|
139
|
+
checkLabel(id, "limitation", definition.limitation, 160, context);
|
|
139
140
|
if (definition.tags !== undefined) {
|
|
140
141
|
if (!Array.isArray(definition.tags) || definition.tags.length > 16) fail(id, "tags must be a list of at most 16 words.");
|
|
141
142
|
for (const tag of definition.tags) checkLabel(id, "tag", tag, 40, context, false);
|
|
@@ -964,6 +964,7 @@ export function createGloomberbCloudCapabilities(provider = createGloomberbCloud
|
|
|
964
964
|
return {
|
|
965
965
|
articles: cloudNewsReadArticles(read, query.ticker),
|
|
966
966
|
nextCursor: read.nextCursor ?? null,
|
|
967
|
+
access: read.access ?? null,
|
|
967
968
|
};
|
|
968
969
|
},
|
|
969
970
|
async fetchNews(query: NewsQuery): Promise<NewsArticle[]> {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { normalizeNewsFeed } from "../../news/news-model";
|
|
2
2
|
import { readableNewsSource } from "../../news/source-label";
|
|
3
3
|
import { collectNewsDisplayTickers } from "../../news/ticker-symbols";
|
|
4
|
-
import type { NewsArticle, NewsQuery, NewsStoryItem } from "../../types/news-source";
|
|
4
|
+
import type { NewsAccess, NewsArticle, NewsQuery, NewsStoryItem } from "../../types/news-source";
|
|
5
|
+
import { CLOUD_NEWS_DELAY_HOURS } from "../../api-client/plan-rules";
|
|
5
6
|
import type {
|
|
6
7
|
apiClient,
|
|
7
8
|
CloudNewsListResponse,
|
|
@@ -134,10 +135,44 @@ export function cloudNewsParams(query: NewsQuery): CloudNewsParams {
|
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
/**
|
|
138
|
-
|
|
138
|
+
/**
|
|
139
|
+
* A page of Gloom Cloud stories, with the text searched for when a coin pair's
|
|
140
|
+
* feed had none filed under it, and which news the service answered with.
|
|
141
|
+
*/
|
|
142
|
+
export type CloudNewsRead = Omit<CloudNewsListResponse, "access"> & { searchedFor?: string; access?: NewsAccess | null };
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* How recently the service must have seen a story for the answer to be the
|
|
146
|
+
* live wire. It holds a story back from an account without real-time news
|
|
147
|
+
* until it last saw it {@link CLOUD_NEWS_DELAY_HOURS} hours ago; the hour
|
|
148
|
+
* less leaves room for this clock and the service's to disagree.
|
|
149
|
+
*/
|
|
150
|
+
const LIVE_STORY_WINDOW_MS = (CLOUD_NEWS_DELAY_HOURS - 1) * 60 * 60 * 1000;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Which news a Gloom Cloud answer is, from the answer: what the service says
|
|
154
|
+
* when it says it; held back when the request carried no session, since the
|
|
155
|
+
* service holds news back for anyone it cannot name; live when it holds a
|
|
156
|
+
* story seen too recently to have been held back. Otherwise null: a quiet
|
|
157
|
+
* feed read live looks the same as a held-back one.
|
|
158
|
+
*/
|
|
159
|
+
export function cloudNewsAccess(
|
|
160
|
+
response: Pick<CloudNewsListResponse, "items" | "access">,
|
|
161
|
+
sentSession: boolean,
|
|
162
|
+
now = Date.now(),
|
|
163
|
+
): NewsAccess | null {
|
|
164
|
+
if (response.access === "realtime" || response.access === "delayed") return response.access;
|
|
165
|
+
if (!sentSession) return "delayed";
|
|
166
|
+
const live = response.items.some((item) => {
|
|
167
|
+
const seenAt = Date.parse(item.lastSeenAt);
|
|
168
|
+
return Number.isFinite(seenAt) && now - seenAt < LIVE_STORY_WINDOW_MS;
|
|
169
|
+
});
|
|
170
|
+
return live ? "realtime" : null;
|
|
171
|
+
}
|
|
139
172
|
|
|
140
|
-
|
|
173
|
+
/** A client that cannot say whether a session goes with its requests (a test double) is taken to send one. */
|
|
174
|
+
type CloudNewsClient = Pick<typeof apiClient, "getCloudNews" | "searchCloudNews">
|
|
175
|
+
& Partial<Pick<typeof apiClient, "hasSessionCredential">>;
|
|
141
176
|
|
|
142
177
|
/**
|
|
143
178
|
* A news query's first page from Gloom Cloud. Stories are filed under company
|
|
@@ -147,13 +182,18 @@ type CloudNewsClient = Pick<typeof apiClient, "getCloudNews" | "searchCloudNews"
|
|
|
147
182
|
* no name to search keeps the empty answer.
|
|
148
183
|
*/
|
|
149
184
|
export async function readCloudNews(client: CloudNewsClient, query: NewsQuery): Promise<CloudNewsRead> {
|
|
185
|
+
// Asked in the tick the request goes out in, which is when its headers take the session.
|
|
186
|
+
const sentSession = client.hasSessionCredential?.() ?? true;
|
|
150
187
|
const response = await client.getCloudNews(cloudNewsParams(query));
|
|
151
|
-
|
|
188
|
+
const read: CloudNewsRead = { ...response, access: cloudNewsAccess(response, sentSession) };
|
|
189
|
+
if (response.items.length > 0 || query.cursor || normalizeNewsFeed(query) !== "ticker") return read;
|
|
152
190
|
const text = coinSearchName(query.ticker, query.exchange, query.listingName);
|
|
153
|
-
if (!text) return
|
|
191
|
+
if (!text) return read;
|
|
154
192
|
const found = await client.searchCloudNews(text, query.limit)
|
|
155
193
|
.catch(logFailure("sources.gloomberb-cloud.news", "Search a coin's stories failed", null));
|
|
156
|
-
return found && found.items.length > 0
|
|
194
|
+
return found && found.items.length > 0
|
|
195
|
+
? { items: found.items, nextCursor: null, searchedFor: text, access: cloudNewsAccess(found, sentSession) }
|
|
196
|
+
: read;
|
|
157
197
|
}
|
|
158
198
|
|
|
159
199
|
/** A read's stories as articles, each marked with the text a coin's fallback searched for. */
|
|
@@ -349,6 +349,8 @@ export function mapOptionsChain(
|
|
|
349
349
|
asOf: chain.asOf,
|
|
350
350
|
// Left absent when the server does not send it, so the date is then established against OPX.
|
|
351
351
|
...(chain.openInterestAsOf !== undefined ? { openInterestAsOf: chain.openInterestAsOf } : {}),
|
|
352
|
+
...(Number.isFinite(chain.rate) ? { rate: chain.rate } : {}),
|
|
353
|
+
...(chain.venue ? { venue: chain.venue } : {}),
|
|
352
354
|
};
|
|
353
355
|
}
|
|
354
356
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
type CachedFinancialsSelection,
|
|
27
27
|
} from "./financials";
|
|
28
28
|
import type { ProviderRouterCoreDeps } from "./route-types";
|
|
29
|
+
import { isNetworkFailure } from "../../utils/network-failure";
|
|
29
30
|
|
|
30
31
|
export interface ProviderRouterBatchDeps extends ProviderRouterCoreDeps {
|
|
31
32
|
readCachedMergedFinancialsSelection(
|
|
@@ -89,6 +90,7 @@ export class ProviderRouterBatchRoutes {
|
|
|
89
90
|
const batchProvider = this.deps.providersInPriorityOrder().find((provider) => provider.getQuotesBatch);
|
|
90
91
|
const providerMisses = misses.filter(({ target }) => !this.deps.hasBrokerContext(target.context));
|
|
91
92
|
const providerIndexes = new Map<string, Array<{ index: number; target: QuoteSubscriptionTarget }>>();
|
|
93
|
+
let batchFailure: unknown = null;
|
|
92
94
|
if (batchProvider && providerMisses.length > 0) {
|
|
93
95
|
for (const entry of providerMisses) {
|
|
94
96
|
const key = this.quoteBatchKey(entry.target);
|
|
@@ -97,7 +99,10 @@ export class ProviderRouterBatchRoutes {
|
|
|
97
99
|
providerIndexes.set(key, bucket);
|
|
98
100
|
}
|
|
99
101
|
const uniqueTargets = [...providerIndexes.values()].map((bucket) => bucket[0]!.target);
|
|
100
|
-
const batchResults = await batchProvider.getQuotesBatch!(uniqueTargets, providerOptions).catch(
|
|
102
|
+
const batchResults = await batchProvider.getQuotesBatch!(uniqueTargets, providerOptions).catch((error: unknown) => {
|
|
103
|
+
batchFailure = error;
|
|
104
|
+
return logFailure("sources.provider-router.batches", "Loading batch quotes failed", [] as QuoteBatchResult[])(error);
|
|
105
|
+
});
|
|
101
106
|
for (const item of batchResults) {
|
|
102
107
|
if (!isProviderQuoteUsableForCurrentSession(item.quote, item.target.exchange, item.target.symbol, { recentAnswer: true })) continue;
|
|
103
108
|
const key = this.quoteBatchKey(item.target);
|
|
@@ -111,12 +116,15 @@ export class ProviderRouterBatchRoutes {
|
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
// The batch never reached the data service: a copy saved a moment ago is still a saved copy, not the
|
|
120
|
+
// service's answer, so each is asked for as a refresh, which fails at once and says it answers from that copy.
|
|
121
|
+
const unreached = isNetworkFailure(batchFailure) ? new Set(providerMisses.map(({ index }) => index)) : null;
|
|
114
122
|
await Promise.all(misses.map(async ({ index, target }) => {
|
|
115
123
|
if (results[index]) return;
|
|
116
124
|
try {
|
|
117
125
|
const quote = await this.deps.getQuote(target.symbol, target.exchange, {
|
|
118
126
|
...target.context,
|
|
119
|
-
cacheMode: forceRefresh ? "refresh" : "default",
|
|
127
|
+
cacheMode: forceRefresh || unreached?.has(index) ? "refresh" : "default",
|
|
120
128
|
});
|
|
121
129
|
results[index] = { target, quote };
|
|
122
130
|
} catch (error) {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
sortCachedRecords,
|
|
17
17
|
} from "./cache";
|
|
18
18
|
import { noProviderError, type ProviderMissNote } from "../provider-errors";
|
|
19
|
-
import { servedFromSavedCopy } from "../../data/saved-copies";
|
|
19
|
+
import { quoteServedFromSavedCopy, servedFromSavedCopy } from "../../data/saved-copies";
|
|
20
20
|
import { withBrokerTimeout } from "./brokers";
|
|
21
21
|
import {
|
|
22
22
|
hasMeaningfulProfile,
|
|
@@ -240,7 +240,7 @@ export class ProviderRouterFinancialRoutes {
|
|
|
240
240
|
}
|
|
241
241
|
if (cached) {
|
|
242
242
|
// The refresh failed rather than finding nothing newer: the saved copy answers, and says so.
|
|
243
|
-
return misses.failure !== undefined ?
|
|
243
|
+
return misses.failure !== undefined ? quoteServedFromSavedCopy(cached.value, cached.fetchedAt) : cached.value;
|
|
244
244
|
}
|
|
245
245
|
// No source has a current quote: the last price one answered with, flagged
|
|
246
246
|
// stale and dated by its own time, rather than none.
|
|
@@ -379,5 +379,5 @@ export class ProviderRouterFinancialRoutes {
|
|
|
379
379
|
/** Financials as served from their saved copy, with their quote when it is the saved one. */
|
|
380
380
|
function savedFinancials(value: TickerFinancials, savedAt: number, savedQuote: boolean): TickerFinancials {
|
|
381
381
|
const served = servedFromSavedCopy(value, savedAt);
|
|
382
|
-
return savedQuote && served.quote ? { ...served, quote:
|
|
382
|
+
return savedQuote && served.quote ? { ...served, quote: quoteServedFromSavedCopy(served.quote, savedAt) } : served;
|
|
383
383
|
}
|
|
@@ -3,7 +3,7 @@ import type { AnalystResearchData, CompanyProfile, CorporateActionsData, Financi
|
|
|
3
3
|
import { hasLikelyQuoteUnitMismatch } from "../../utils/currency-units";
|
|
4
4
|
import { coalesceFinancialPeriodAliases, mergeFinancialStatementRows } from "../../utils/financial-statements";
|
|
5
5
|
import { normalizePriceHistory, normalizeTickerFinancialsPriceHistory } from "../../utils/price-history";
|
|
6
|
-
import { redactUnavailableFundamentals, RETRACTABLE_VALUATION_FIELDS } from "../../utils/fundamentals";
|
|
6
|
+
import { redactUnavailableFundamentals, RETRACTABLE_VALUATION_FIELDS, RETRACTED_FUNDAMENTALS_FIELDS } from "../../utils/fundamentals";
|
|
7
7
|
import { hasValidQuoteObservationTime, isExtendedHoursExchange, isQuoteStaleForCurrentSession } from "../../market-data/quotes/freshness";
|
|
8
8
|
import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../../market-data/quotes/metadata";
|
|
9
9
|
import { canonicalExchange, parsePublicTickerKey } from "../../utils/exchanges";
|
|
@@ -458,12 +458,21 @@ function mergeFundamentals(primary: Fundamentals | undefined, fallback: Fundamen
|
|
|
458
458
|
const merged = mergeDefinedObject(primary, fallback);
|
|
459
459
|
if (merged) {
|
|
460
460
|
const unavailable = new Set(fallback?.unavailableFields ?? []);
|
|
461
|
-
for (const field of
|
|
461
|
+
for (const field of RETRACTED_FUNDAMENTALS_FIELDS) {
|
|
462
462
|
if (primary?.unavailableFields?.includes(field)) unavailable.add(field);
|
|
463
463
|
else if (typeof primary?.[field] === "number" && Number.isFinite(primary[field])) unavailable.delete(field);
|
|
464
464
|
}
|
|
465
465
|
if (unavailable.size) merged.unavailableFields = [...unavailable];
|
|
466
466
|
else delete merged.unavailableFields;
|
|
467
|
+
// A retracted cap is none, whatever an older observation held; its reason goes with the retraction that stands.
|
|
468
|
+
if (unavailable.has("marketCap")) {
|
|
469
|
+
delete merged.marketCap;
|
|
470
|
+
merged.marketCapUnavailable = primary?.unavailableFields?.includes("marketCap")
|
|
471
|
+
? primary.marketCapUnavailable : fallback?.marketCapUnavailable;
|
|
472
|
+
if (!merged.marketCapUnavailable) delete merged.marketCapUnavailable;
|
|
473
|
+
} else {
|
|
474
|
+
delete merged.marketCapUnavailable;
|
|
475
|
+
}
|
|
467
476
|
}
|
|
468
477
|
const dividend = primary?.dividendYield != null ? primary : fallback;
|
|
469
478
|
if (merged && dividend) {
|
|
@@ -557,7 +566,10 @@ export function mergeRefreshedFinancials(cached: TickerFinancials, fresh: Ticker
|
|
|
557
566
|
const merged = mergeFinancials(cached, fresh)!;
|
|
558
567
|
const statistics = redactUnavailableFundamentals(fresh?.fundamentals);
|
|
559
568
|
if (!statistics) return merged;
|
|
560
|
-
const update: Fundamentals = {
|
|
569
|
+
const update: Fundamentals = {
|
|
570
|
+
unavailableFields: statistics.unavailableFields,
|
|
571
|
+
...statistics.marketCapUnavailable ? { marketCapUnavailable: statistics.marketCapUnavailable } : {},
|
|
572
|
+
};
|
|
561
573
|
const currencyConflict = cached.fundamentals?.financialCurrency && statistics.financialCurrency
|
|
562
574
|
&& cached.fundamentals.financialCurrency !== statistics.financialCurrency;
|
|
563
575
|
for (const field of RETRACTABLE_VALUATION_FIELDS) {
|
|
@@ -59,6 +59,12 @@ interface HistoryRequestDescriptor {
|
|
|
59
59
|
interval?: string;
|
|
60
60
|
method: "getPriceHistory" | "getPriceHistoryForResolution" | "getDetailedPriceHistory";
|
|
61
61
|
context?: MarketDataRequestContext;
|
|
62
|
+
/**
|
|
63
|
+
* False to fetch from the providers alone: a broker's saved copy still
|
|
64
|
+
* answers, but a broker is not asked, so reads made for every quote never
|
|
65
|
+
* spend its request pacing.
|
|
66
|
+
*/
|
|
67
|
+
askBrokers?: boolean;
|
|
62
68
|
cachePolicyKey: PriceHistoryCachePolicyKey;
|
|
63
69
|
missingProviderError?: string;
|
|
64
70
|
/** `fetchedAt` is when the cached copy was fetched. */
|
|
@@ -68,6 +74,11 @@ interface HistoryRequestDescriptor {
|
|
|
68
74
|
fetchProvider(provider: DataProvider): Promise<PriceHistoryResult | null>;
|
|
69
75
|
}
|
|
70
76
|
|
|
77
|
+
/** The brokers a request may ask: none for one made for the providers alone. */
|
|
78
|
+
function askedBrokers(request: HistoryRequestDescriptor, candidates: BrokerCandidate[]): BrokerCandidate[] {
|
|
79
|
+
return request.askBrokers === false ? [] : candidates;
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
interface HistoryAttempts {
|
|
72
83
|
outcomes: Map<string, HistorySourceOutcome>;
|
|
73
84
|
candidates: Map<string, HistoryRecoveryCandidate>;
|
|
@@ -363,8 +374,14 @@ export class ProviderRouterHistoryRoutes {
|
|
|
363
374
|
}
|
|
364
375
|
}
|
|
365
376
|
|
|
366
|
-
async getPriceHistory(
|
|
367
|
-
|
|
377
|
+
async getPriceHistory(
|
|
378
|
+
ticker: string,
|
|
379
|
+
exchange: string,
|
|
380
|
+
range: TimeRange,
|
|
381
|
+
context?: MarketDataRequestContext,
|
|
382
|
+
options: Pick<HistoryRequestDescriptor, "askBrokers"> = {},
|
|
383
|
+
): Promise<PricePoint[]> {
|
|
384
|
+
return (await this.getPriceHistoryWithMetadata(ticker, exchange, range, context, options)).points;
|
|
368
385
|
}
|
|
369
386
|
|
|
370
387
|
async getPriceHistoryWithMetadata(
|
|
@@ -372,6 +389,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
372
389
|
exchange: string,
|
|
373
390
|
range: TimeRange,
|
|
374
391
|
context?: MarketDataRequestContext,
|
|
392
|
+
{ askBrokers }: Pick<HistoryRequestDescriptor, "askBrokers"> = {},
|
|
375
393
|
): Promise<PriceHistoryResult> {
|
|
376
394
|
if (context?.historyRecovery) throw new Error("History recovery requires an exact detailed request");
|
|
377
395
|
const identity = makeHistoryRequestIdentity(this.deps, {
|
|
@@ -390,6 +408,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
390
408
|
requestedStart: subtractTimeRange(new Date(), range).getTime(),
|
|
391
409
|
method: "getPriceHistory",
|
|
392
410
|
context,
|
|
411
|
+
askBrokers,
|
|
393
412
|
cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
|
|
394
413
|
missingProviderError: `No history provider available for ${ticker}`,
|
|
395
414
|
isCachedValueStale: (value, fetchedAt) => intraday && resultIsStale(value, ticker, exchange, fetchedAt),
|
|
@@ -688,7 +707,9 @@ export class ProviderRouterHistoryRoutes {
|
|
|
688
707
|
if (usableCached && !forceRefresh && !awaitRefresh) {
|
|
689
708
|
const exactHit = request.exactCacheVariantKeys.includes(cached.variantKey);
|
|
690
709
|
if (refreshDue) {
|
|
691
|
-
|
|
710
|
+
// A refresh from the providers alone never stands in for one that asks the brokers too.
|
|
711
|
+
const refreshKey = request.askBrokers === false ? `${request.identity.revalidationKey}|providers` : request.identity.revalidationKey;
|
|
712
|
+
scheduleRouterRevalidation(this.historyRefreshInFlight, refreshKey, () => this.refreshHistory(request));
|
|
692
713
|
}
|
|
693
714
|
// Only a copy a re-check could not replace reaches here short of the latest session.
|
|
694
715
|
const answered = cachedHistoryStale ? { ...cachedValue, savedCopyBehind: true as const } : cachedValue;
|
|
@@ -702,7 +723,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
702
723
|
if (value.points.length && !hasUsablePriceHistory(value.points)) supersededCacheSources.add(sourceKey);
|
|
703
724
|
};
|
|
704
725
|
const attempts: HistoryAttempts = { outcomes: new Map(), candidates: new Map(), pending: new Set(), misses: {} };
|
|
705
|
-
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request, brokerCandidates, onUnavailable, attempts));
|
|
726
|
+
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request, askedBrokers(request, brokerCandidates), onUnavailable, attempts));
|
|
706
727
|
for (const sourceKey of [...attempts.pending]) recordHistoryOutcome(attempts, sourceKey, "timeout");
|
|
707
728
|
if (brokerResult && hasUsablePriceHistory(brokerResult.value.points)) return withReportedGaps(brokerResult.value);
|
|
708
729
|
if (brokerResult) reportedGaps.push(brokerResult.value);
|
|
@@ -715,7 +736,7 @@ export class ProviderRouterHistoryRoutes {
|
|
|
715
736
|
});
|
|
716
737
|
if (providerResult && hasUsablePriceHistory(providerResult.value.points)) return withReportedGaps(providerResult.value);
|
|
717
738
|
if (providerResult) reportedGaps.push(providerResult.value);
|
|
718
|
-
if (regularSessionBrokers.length) {
|
|
739
|
+
if (askedBrokers(request, regularSessionBrokers).length) {
|
|
719
740
|
const regularResult = await withBrokerTimeout(this.fetchBrokerHistory(request, regularSessionBrokers, onUnavailable, attempts));
|
|
720
741
|
for (const sourceKey of [...attempts.pending]) recordHistoryOutcome(attempts, sourceKey, "timeout");
|
|
721
742
|
if (regularResult && hasUsablePriceHistory(regularResult.value.points)) return withReportedGaps(regularResult.value);
|
|
@@ -785,11 +806,11 @@ export class ProviderRouterHistoryRoutes {
|
|
|
785
806
|
try {
|
|
786
807
|
const recovery = this.validateRecovery(request);
|
|
787
808
|
const { first, last } = brokersForSession(recovery ? [] : this.deps.getBrokerCandidatesForContext(request.context, false), request.context);
|
|
788
|
-
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request, first));
|
|
809
|
+
const brokerResult = await withBrokerTimeout(this.fetchBrokerHistory(request, askedBrokers(request, first)));
|
|
789
810
|
if (brokerResult && hasUsablePriceHistory(brokerResult.value.points)) return;
|
|
790
811
|
const providerResult = await this.fetchProviderHistory(request);
|
|
791
812
|
if (providerResult && hasUsablePriceHistory(providerResult.value.points)) return;
|
|
792
|
-
if (last.length) await withBrokerTimeout(this.fetchBrokerHistory(request, last));
|
|
813
|
+
if (askedBrokers(request, last).length) await withBrokerTimeout(this.fetchBrokerHistory(request, last));
|
|
793
814
|
} catch (error) {
|
|
794
815
|
logFailure("sources.provider-router.history", "Refreshing background price history failed")(error);
|
|
795
816
|
}
|