gloomberb 0.18.3 → 0.18.4
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 +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +84 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +22 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +21 -4
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1117 -264
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +192 -47
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +47 -10
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +198 -68
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +305 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +13 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +12 -6
- package/src/components/layout/header.tsx +7 -6
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +107 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +76 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +6 -3
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +6 -8
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +79 -23
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +5 -3
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +3 -2
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +18 -11
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +6 -1
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -4
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +10 -4
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +33 -19
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +29 -8
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +36 -15
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +4 -1
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +8 -1
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +2 -1
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/styles.css +76 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +32 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +17 -4
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/index.ts +10 -5
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +3 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- package/src/cli/pane-functions/screenshot.ts +0 -2122
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { publicTickerKey } from "../utils/exchanges";
|
|
2
|
+
import type { ChartSeriesSpec, ChartStudySpec } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What a series' errors are prefixed with: its label, else what was asked for
|
|
6
|
+
* (`VIX`, `FRED GDP`), never the internal id a preset gave it (`vix-market-close-3`).
|
|
7
|
+
*/
|
|
8
|
+
export function seriesMessageName(spec: Pick<ChartSeriesSpec, "label" | "source">): string {
|
|
9
|
+
const label = spec.label?.trim();
|
|
10
|
+
if (label) return label;
|
|
11
|
+
switch (spec.source.kind) {
|
|
12
|
+
case "security": return publicTickerKey(spec.source.instrument.symbol, spec.source.instrument.exchange);
|
|
13
|
+
case "economic": return `FRED ${spec.source.seriesId}`;
|
|
14
|
+
case "capability": return spec.source.seriesId;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const STUDY_MESSAGE_NAMES: Partial<Record<ChartStudySpec["kind"], string>> = {
|
|
19
|
+
"realized-vol": "Realized volatility",
|
|
20
|
+
"volume-profile": "Volume profile",
|
|
21
|
+
"anchored-vwap": "Anchored VWAP",
|
|
22
|
+
"leverage-decay": "Leverage decay",
|
|
23
|
+
atr: "ATR",
|
|
24
|
+
sma: "SMA",
|
|
25
|
+
ema: "EMA",
|
|
26
|
+
rsi: "RSI",
|
|
27
|
+
macd: "MACD",
|
|
28
|
+
vwap: "VWAP",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What a study's errors and warnings are prefixed with: what it is
|
|
33
|
+
* (`Ratio`, `Realized volatility`), never the id the chart gave it
|
|
34
|
+
* (`pair:ratio`, `builtin:realized-vol:price`).
|
|
35
|
+
*/
|
|
36
|
+
export function studyMessageName(spec: Pick<ChartStudySpec, "kind">): string {
|
|
37
|
+
return STUDY_MESSAGE_NAMES[spec.kind] ?? `${spec.kind[0]!.toUpperCase()}${spec.kind.slice(1)}`;
|
|
38
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { limitSeriesObservations } from "./financial-period-coverage";
|
|
2
|
+
import type { ManualChartResolution } from "./resolution";
|
|
3
|
+
import { getTimeSeriesField } from "./field-catalog";
|
|
4
|
+
import { collectPriceHistoryIntegrity } from "./market";
|
|
5
|
+
import { activeStudyInputSeriesIds } from "./studies";
|
|
6
|
+
import { applyResolvedSeriesTransform } from "./transforms";
|
|
7
|
+
import { clipSeriesToWindow } from "./alignment";
|
|
8
|
+
import type { StudyAnchor } from "./leverage";
|
|
9
|
+
import {
|
|
10
|
+
comparedBufferPoints,
|
|
11
|
+
priceComparisonBoundsForSeries,
|
|
12
|
+
priceObservationWindowFilter,
|
|
13
|
+
type PriceComparison,
|
|
14
|
+
} from "./price-comparison";
|
|
15
|
+
import { parsePublicTickerKey, publicTickerKey } from "../utils/exchanges";
|
|
16
|
+
import type {
|
|
17
|
+
ChartSeriesSpec,
|
|
18
|
+
ChartSpec,
|
|
19
|
+
ResolvedSeries,
|
|
20
|
+
} from "./types";
|
|
21
|
+
import { type DateBounds, filterPoints } from "./chart-bounds";
|
|
22
|
+
|
|
23
|
+
export function resolvePriceHistoryIntegrity(
|
|
24
|
+
spec: ChartSpec,
|
|
25
|
+
requestedSeries: readonly ResolvedSeries[],
|
|
26
|
+
visibleSeries: readonly ResolvedSeries[],
|
|
27
|
+
bounds: DateBounds,
|
|
28
|
+
resolution: ManualChartResolution | "auto",
|
|
29
|
+
dateWindow: ChartSpec["viewport"]["dateWindow"] | null,
|
|
30
|
+
) {
|
|
31
|
+
const filters = new Map(requestedSeries.map((entry) => [entry.id, priceObservationWindowFilter(entry, bounds, resolution, dateWindow)]));
|
|
32
|
+
const requestedIds = activeStudyInputSeriesIds(spec.studies);
|
|
33
|
+
spec.series.filter((entry) => entry.visible !== false).forEach((entry) => requestedIds.add(entry.id));
|
|
34
|
+
const selected = requestedSeries.filter((entry) => requestedIds.has(entry.id)).map((entry) => limitSeriesObservations(spec, {
|
|
35
|
+
...entry, points: entry.points.filter((point) => filters.get(entry.id)!(point.date.getTime())),
|
|
36
|
+
}));
|
|
37
|
+
return [
|
|
38
|
+
...collectPriceHistoryIntegrity(selected, "requested-observation", (id, time) => filters.get(id)!(time)),
|
|
39
|
+
...collectPriceHistoryIntegrity(visibleSeries, "visible-calculation"),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function assignAxes(
|
|
44
|
+
series: ResolvedSeries[],
|
|
45
|
+
specs: readonly { id: string; axis: ChartSeriesSpec["axis"] }[],
|
|
46
|
+
warnings: string[],
|
|
47
|
+
): ResolvedSeries[] {
|
|
48
|
+
const requested = new Map(specs.map((spec) => [spec.id, spec.axis] as const));
|
|
49
|
+
const groupsByPanel = new Map<string, Partial<Record<"left" | "right", string>>>();
|
|
50
|
+
return series.map((entry) => {
|
|
51
|
+
const groups = groupsByPanel.get(entry.panelId) ?? {};
|
|
52
|
+
groupsByPanel.set(entry.panelId, groups);
|
|
53
|
+
const preferred = requested.get(entry.id);
|
|
54
|
+
let axis: "left" | "right";
|
|
55
|
+
if (preferred === "left" || preferred === "right") {
|
|
56
|
+
axis = preferred;
|
|
57
|
+
} else if (groups.left === entry.unitGroup) {
|
|
58
|
+
axis = "left";
|
|
59
|
+
} else if (groups.right === entry.unitGroup) {
|
|
60
|
+
axis = "right";
|
|
61
|
+
} else if (!groups.left) {
|
|
62
|
+
axis = "left";
|
|
63
|
+
} else {
|
|
64
|
+
axis = "right";
|
|
65
|
+
}
|
|
66
|
+
if (groups[axis] && groups[axis] !== entry.unitGroup) {
|
|
67
|
+
warnings.push(`${entry.label} shares the ${axis} axis with a different unit; choose an explicit panel for independent scaling.`);
|
|
68
|
+
}
|
|
69
|
+
groups[axis] ??= entry.unitGroup;
|
|
70
|
+
return { ...entry, axis };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A series drawn in a ticker's price (a moving average of it, a band around
|
|
76
|
+
* it) takes the units of that ticker's price series when the chart has it, so
|
|
77
|
+
* the two share an axis and the line sits on the price instead of on a scale
|
|
78
|
+
* of its own.
|
|
79
|
+
*/
|
|
80
|
+
export function adoptPriceUnits(series: ResolvedSeries[], specs: readonly ChartSeriesSpec[]): ResolvedSeries[] {
|
|
81
|
+
if (!series.some((entry) => entry.priceOf)) return series;
|
|
82
|
+
const specsById = new Map(specs.map((spec) => [spec.id, spec]));
|
|
83
|
+
const prices = series.filter((entry) => {
|
|
84
|
+
const source = specsById.get(entry.id)?.source;
|
|
85
|
+
return source?.kind === "security" && getTimeSeriesField(source.fieldId)?.unitGroup === "price"
|
|
86
|
+
&& entry.transform === "raw";
|
|
87
|
+
});
|
|
88
|
+
if (prices.length === 0) return series;
|
|
89
|
+
return series.map((entry) => {
|
|
90
|
+
if (!entry.priceOf) return entry;
|
|
91
|
+
const wanted = parsePublicTickerKey(entry.priceOf.toUpperCase());
|
|
92
|
+
const key = publicTickerKey(wanted.symbol, wanted.exchange);
|
|
93
|
+
const named = (price: ResolvedSeries) => {
|
|
94
|
+
const source = specsById.get(price.id)?.source;
|
|
95
|
+
if (source?.kind !== "security") return false;
|
|
96
|
+
return publicTickerKey(source.instrument.symbol, source.instrument.exchange) === key
|
|
97
|
+
|| (!wanted.exchange && source.instrument.symbol.toUpperCase() === wanted.symbol);
|
|
98
|
+
};
|
|
99
|
+
const price = prices.find((candidate) => named(candidate) && candidate.panelId === entry.panelId)
|
|
100
|
+
?? prices.find(named);
|
|
101
|
+
return price && entry.transform === "raw"
|
|
102
|
+
? { ...entry, unit: price.unit, unitGroup: price.unitGroup }
|
|
103
|
+
: entry;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Where studies that read returns over the chart's period measure each series
|
|
109
|
+
* from and to: a compared leg's shared first and last session, else the
|
|
110
|
+
* window in view.
|
|
111
|
+
*/
|
|
112
|
+
export function studyAnchors(
|
|
113
|
+
series: readonly ResolvedSeries[],
|
|
114
|
+
comparison: PriceComparison | null,
|
|
115
|
+
bounds: DateBounds,
|
|
116
|
+
): Map<string, StudyAnchor> {
|
|
117
|
+
return new Map(series.map((entry) => [entry.id, priceComparisonBoundsForSeries(entry, comparison) ?? bounds]));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function prepareBaseSeriesForStudies(
|
|
121
|
+
series: ResolvedSeries,
|
|
122
|
+
bounds: DateBounds,
|
|
123
|
+
clipToBounds = false,
|
|
124
|
+
fallbackBaselineBounds?: DateBounds,
|
|
125
|
+
): ResolvedSeries {
|
|
126
|
+
const baselineTransform = series.transform === "percent" || series.transform === "index100";
|
|
127
|
+
let source = series;
|
|
128
|
+
if (clipToBounds && bounds.start !== null && bounds.end !== null) {
|
|
129
|
+
source = clipSeriesToWindow(series, new Date(bounds.start), new Date(bounds.end));
|
|
130
|
+
} else if (clipToBounds) {
|
|
131
|
+
source = { ...series, points: filterPoints(series.points, bounds) };
|
|
132
|
+
}
|
|
133
|
+
const baseline = baselineTransform
|
|
134
|
+
? scalarBaseline(series, bounds)
|
|
135
|
+
?? (fallbackBaselineBounds ? scalarBaseline(series, fallbackBaselineBounds) : null)
|
|
136
|
+
: null;
|
|
137
|
+
return applyResolvedSeriesTransform(
|
|
138
|
+
source,
|
|
139
|
+
source.transform,
|
|
140
|
+
baselineTransform ? { baseline } : undefined,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function scalarBaseline(series: ResolvedSeries, bounds: DateBounds): number | null {
|
|
145
|
+
const points = filterPoints(series.points, bounds);
|
|
146
|
+
for (const point of points) {
|
|
147
|
+
const value = typeof point.value === "number" && Number.isFinite(point.value)
|
|
148
|
+
? point.value
|
|
149
|
+
: typeof point.close === "number" && Number.isFinite(point.close)
|
|
150
|
+
? point.close
|
|
151
|
+
: null;
|
|
152
|
+
if (value !== null && value !== 0) return value;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function studyForOutput(
|
|
158
|
+
outputId: string,
|
|
159
|
+
studies: readonly ChartSpec["studies"][number][],
|
|
160
|
+
): ChartSpec["studies"][number] | undefined {
|
|
161
|
+
return studies
|
|
162
|
+
.filter((study) => outputId === study.id || outputId.startsWith(`${study.id}:`))
|
|
163
|
+
.sort((left, right) => right.id.length - left.id.length)[0];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Price overlays read in the price's own terms, so they take its transform. */
|
|
167
|
+
function followsInputTransform(kind: ChartSpec["studies"][number]["kind"]): boolean {
|
|
168
|
+
return kind === "sma" || kind === "ema" || kind === "bollinger" || kind === "vwap" || kind === "anchored-vwap";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function presentationBounds(
|
|
172
|
+
series: ResolvedSeries,
|
|
173
|
+
studies: readonly ChartSpec["studies"][number][],
|
|
174
|
+
comparison: PriceComparison | null,
|
|
175
|
+
fallback: DateBounds,
|
|
176
|
+
): DateBounds {
|
|
177
|
+
const study = studyForOutput(series.id, studies);
|
|
178
|
+
const sourceId = study && followsInputTransform(study.kind)
|
|
179
|
+
? study.inputSeriesIds[0] : series.id;
|
|
180
|
+
return (sourceId && comparison?.sourceBounds?.[sourceId]) || fallback;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function applyStudyPresentationTransforms(
|
|
184
|
+
outputs: ResolvedSeries[],
|
|
185
|
+
studies: readonly ChartSpec["studies"][number][],
|
|
186
|
+
rawSeries: readonly ResolvedSeries[],
|
|
187
|
+
visibleBounds: DateBounds,
|
|
188
|
+
fallbackBaselineBounds?: DateBounds,
|
|
189
|
+
priceComparison?: PriceComparison | null,
|
|
190
|
+
view: DateBounds = visibleBounds,
|
|
191
|
+
): ResolvedSeries[] {
|
|
192
|
+
const rawById = new Map(rawSeries.map((series) => [series.id, series] as const));
|
|
193
|
+
return outputs.map((output) => {
|
|
194
|
+
const study = studyForOutput(output.id, studies);
|
|
195
|
+
if (!study || !followsInputTransform(study.kind)) {
|
|
196
|
+
return output;
|
|
197
|
+
}
|
|
198
|
+
const input = rawById.get(study.inputSeriesIds[0] ?? "");
|
|
199
|
+
if (!input || input.transform === "raw") return output;
|
|
200
|
+
const baseline = input.transform === "percent" || input.transform === "index100"
|
|
201
|
+
? scalarBaseline(input, priceComparisonBoundsForSeries(input, priceComparison ?? null) ?? visibleBounds)
|
|
202
|
+
?? (fallbackBaselineBounds ? scalarBaseline(input, fallbackBaselineBounds) : null)
|
|
203
|
+
: undefined;
|
|
204
|
+
const transformed = applyResolvedSeriesTransform(output, input.transform, { baseline });
|
|
205
|
+
const inputBounds = priceComparison?.sourceBounds?.[input.id];
|
|
206
|
+
return inputBounds ? { ...transformed, points: comparedBufferPoints(transformed.points, inputBounds, view) } : transformed;
|
|
207
|
+
});
|
|
208
|
+
}
|
package/src/time-series/spec.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TIME_RANGES, type ChartResolution, type TimeRange } from "./range";
|
|
2
2
|
import { isRealizedVolatilityEstimator } from "../market-data/realized-volatility";
|
|
3
|
-
import { getChartResolutionLabel } from "./resolution";
|
|
3
|
+
import { getChartResolutionLabel, isChartResolution } from "./resolution";
|
|
4
4
|
import {
|
|
5
5
|
canonicalTimeSeriesFieldId,
|
|
6
6
|
getTimeSeriesField,
|
|
@@ -32,9 +32,9 @@ import {
|
|
|
32
32
|
isValidChartSeriesId,
|
|
33
33
|
} from "../capabilities/chart-series";
|
|
34
34
|
import { recordOrNull } from "../utils/guards";
|
|
35
|
+
import { isLeverageFactor } from "./leverage";
|
|
35
36
|
|
|
36
37
|
const RANGE_SET = new Set<TimeRange>(TIME_RANGES);
|
|
37
|
-
const RESOLUTIONS = new Set<ChartResolution>(CHART_RESOLUTIONS);
|
|
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
40
|
const STYLES = new Set<SeriesStyle>(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc", "band"]);
|
|
@@ -58,6 +58,8 @@ const STUDIES = new Set<ChartStudyKind>([
|
|
|
58
58
|
"spread",
|
|
59
59
|
"correlation",
|
|
60
60
|
"formula",
|
|
61
|
+
"leverage",
|
|
62
|
+
"leverage-decay",
|
|
61
63
|
]);
|
|
62
64
|
|
|
63
65
|
/**
|
|
@@ -321,7 +323,7 @@ export function normalizeChartSpec(value: unknown, fallback: ChartSpec = DEFAULT
|
|
|
321
323
|
range: RANGE_SET.has(viewport?.range as TimeRange)
|
|
322
324
|
? viewport!.range as TimeRange
|
|
323
325
|
: fallbackCopy.viewport.range,
|
|
324
|
-
resolution:
|
|
326
|
+
resolution: isChartResolution(viewport?.resolution)
|
|
325
327
|
? viewport!.resolution as ChartResolution
|
|
326
328
|
: fallbackCopy.viewport.resolution,
|
|
327
329
|
dateWindow: normalizedDateWindow(viewport?.dateWindow),
|
|
@@ -343,7 +345,7 @@ function issue(path: string, code: string, message: string): ChartSpecIssue {
|
|
|
343
345
|
}
|
|
344
346
|
|
|
345
347
|
function requiredInputCount(kind: ChartStudyKind): number {
|
|
346
|
-
return kind === "ratio" || kind === "spread" || kind === "correlation" ? 2 : 1;
|
|
348
|
+
return kind === "ratio" || kind === "spread" || kind === "correlation" || kind === "leverage-decay" ? 2 : 1;
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
/** Whether a study has the inputs its kind takes: two for a pair, one or more for a formula, one for the rest. */
|
|
@@ -361,7 +363,7 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
|
|
|
361
363
|
errors.push(issue("version", "unsupported-version", `Unsupported chart spec version ${String(spec.version)}.`));
|
|
362
364
|
}
|
|
363
365
|
if (!RANGE_SET.has(spec.viewport.range)) errors.push(issue("viewport.range", "invalid-range", "Invalid date range."));
|
|
364
|
-
if (!
|
|
366
|
+
if (!isChartResolution(spec.viewport.resolution)) {
|
|
365
367
|
errors.push(issue("viewport.resolution", "invalid-resolution", "Invalid chart resolution."));
|
|
366
368
|
}
|
|
367
369
|
if (spec.viewport.dateWindow) {
|
|
@@ -544,6 +546,9 @@ export function validateChartSpec(spec: ChartSpec): ChartSpecValidationResult {
|
|
|
544
546
|
const slow = study.parameters.slow ?? 26;
|
|
545
547
|
if (typeof fast === "number" && typeof slow === "number" && fast >= slow) errors.push(issue(`${path}.parameters`, "invalid-macd-periods", "MACD fast period must be shorter than slow period."));
|
|
546
548
|
}
|
|
549
|
+
if ((study.kind === "leverage" || study.kind === "leverage-decay") && !isLeverageFactor(study.parameters.factor)) {
|
|
550
|
+
errors.push(issue(`${path}.parameters.factor`, "invalid-factor", "A leverage factor is a nonzero multiple of at most 5, such as 3 or -3."));
|
|
551
|
+
}
|
|
547
552
|
});
|
|
548
553
|
|
|
549
554
|
if (spec.series.length === 0) warnings.push(issue("series", "empty-chart", "Chart has no data series."));
|
|
@@ -17,6 +17,8 @@ import type {
|
|
|
17
17
|
TimeSeriesPoint,
|
|
18
18
|
} from "./types";
|
|
19
19
|
import { isFiniteNumber } from "../utils/guards";
|
|
20
|
+
import { studyMessageName } from "./series-name";
|
|
21
|
+
import { dailyLeverageGrowth, isLeverageFactor, leverageDecayPoints, leverageFactorLabel, leverageSymbol, sessionCloses, type StudyAnchor } from "./leverage";
|
|
20
22
|
import { GALLONS_PER_BARREL, isPerBarrelUnit, isPerGallonUnit, perBarrelUnit, quantityIdentity, unitCurrencyToken } from "./price-units";
|
|
21
23
|
|
|
22
24
|
export interface StudyResolutionResult {
|
|
@@ -45,7 +47,10 @@ const KIND_COLORS: Partial<Record<ChartStudyKind, string>> = {
|
|
|
45
47
|
"anchored-vwap": "#b197fc",
|
|
46
48
|
"volume-profile": "#adb5bd",
|
|
47
49
|
atr: "#63e6be",
|
|
50
|
+
leverage: "#e599f7",
|
|
48
51
|
};
|
|
52
|
+
/** The decay study's two lines: the fund against the daily line, the daily line against the period multiple. */
|
|
53
|
+
const DECAY_COLORS = ["#ffa94d", "#66d9e8"] as const;
|
|
49
54
|
/**
|
|
50
55
|
* One per anchor, so eight lines never repeat a colour. None is red or pink
|
|
51
56
|
* (falling candles take the theme's negative), orange (VWAP), yellow (the
|
|
@@ -117,6 +122,7 @@ function outputSeries(
|
|
|
117
122
|
nativeFrequency: options.nativeFrequency ?? input.nativeFrequency,
|
|
118
123
|
...(historyResolution !== undefined ? { historyResolution } : {}),
|
|
119
124
|
...(input.historyStart && historyResolution === input.historyResolution ? { historyStart: input.historyStart } : {}),
|
|
125
|
+
...(input.barSize && historyResolution === input.historyResolution ? { barSize: input.barSize } : {}),
|
|
120
126
|
dataShape: "scalar",
|
|
121
127
|
style: options.style ?? "line",
|
|
122
128
|
transform: "raw",
|
|
@@ -1084,7 +1090,70 @@ function resolveFormula(
|
|
|
1084
1090
|
}
|
|
1085
1091
|
|
|
1086
1092
|
function requiredInputs(kind: ChartStudyKind): number {
|
|
1087
|
-
return kind === "ratio" || kind === "spread" || kind === "correlation" ? 2 : 1;
|
|
1093
|
+
return kind === "ratio" || kind === "spread" || kind === "correlation" || kind === "leverage-decay" ? 2 : 1;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/** The objective's line and its decay compound session returns: they need daily bars, whatever the chart's range. */
|
|
1097
|
+
function dailyBarsIssue(spec: ChartStudySpec, input: ResolvedSeries, marketResolution: ManualChartResolution | undefined): string | null {
|
|
1098
|
+
const resolution = input.historyResolution === undefined ? marketResolution : input.historyResolution;
|
|
1099
|
+
if (input.nativeFrequency === "daily" && (resolution === undefined || resolution === "1d")) return null;
|
|
1100
|
+
return `${studyMessageName(spec)}: ${leverageFactorLabel(Number(spec.parameters.factor))} daily needs daily bars. Choose D bars.`;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* The line a leveraged or inverse fund's daily objective draws from its index
|
|
1105
|
+
* fund, in percent from where the chart compares (3× daily QQQ beside TQQQ),
|
|
1106
|
+
* on the percent axis the compared legs share.
|
|
1107
|
+
*/
|
|
1108
|
+
function resolveLeverageLine(spec: ChartStudySpec, index: ResolvedSeries, color: string, anchor: StudyAnchor | undefined): ResolvedSeries[] {
|
|
1109
|
+
const factor = spec.parameters.factor as number;
|
|
1110
|
+
const closes = sessionCloses(index, anchor);
|
|
1111
|
+
const growth = dailyLeverageGrowth(closes.map((entry) => entry.close), factor);
|
|
1112
|
+
return [outputSeries(spec, index, {
|
|
1113
|
+
label: `${leverageFactorLabel(factor)} daily ${leverageSymbol(index)}`,
|
|
1114
|
+
points: closes.map((entry, position) => derivedPoint({ point: entry.point, value: entry.close }, (growth[position]! - 1) * 100)),
|
|
1115
|
+
color,
|
|
1116
|
+
unit: "%",
|
|
1117
|
+
unitGroup: "percent",
|
|
1118
|
+
statedUnit: null,
|
|
1119
|
+
priceAssetCategory: null,
|
|
1120
|
+
axis: "left",
|
|
1121
|
+
})];
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* How far the fund falls short of its daily objective, in percentage points
|
|
1126
|
+
* from where the chart compares: the fund against the daily line (fees,
|
|
1127
|
+
* financing, tracking), and the daily line against the factor times the index
|
|
1128
|
+
* fund's return over the period (compounding, which daily resets cost in a
|
|
1129
|
+
* market that swings).
|
|
1130
|
+
*/
|
|
1131
|
+
function resolveLeverageDecay(
|
|
1132
|
+
spec: ChartStudySpec,
|
|
1133
|
+
fund: ResolvedSeries,
|
|
1134
|
+
index: ResolvedSeries,
|
|
1135
|
+
anchors: ReadonlyMap<string, StudyAnchor> | undefined,
|
|
1136
|
+
): ResolvedSeries[] {
|
|
1137
|
+
const factor = spec.parameters.factor as number;
|
|
1138
|
+
const decay = leverageDecayPoints(sessionCloses(fund, anchors?.get(fund.id)), sessionCloses(index, anchors?.get(index.id)), factor);
|
|
1139
|
+
const daily = `${leverageFactorLabel(factor)} daily`;
|
|
1140
|
+
const line = (id: string, label: string, color: string, value: (point: (typeof decay)[number]) => number) => outputSeries(spec, fund, {
|
|
1141
|
+
id: `${spec.id}:${id}`,
|
|
1142
|
+
label,
|
|
1143
|
+
points: decay.map((entry) => derivedPoint({ point: entry.point, value: entry.fund }, value(entry))),
|
|
1144
|
+
color: spec.color ?? color,
|
|
1145
|
+
unit: "pp",
|
|
1146
|
+
// Not a percent group: a difference of returns reads in points (-0.96 pp), not as a return.
|
|
1147
|
+
unitGroup: "pp",
|
|
1148
|
+
statedUnit: null,
|
|
1149
|
+
priceAssetCategory: null,
|
|
1150
|
+
axis: "left",
|
|
1151
|
+
});
|
|
1152
|
+
return [
|
|
1153
|
+
line("fund", `${leverageSymbol(fund)} decay vs ${daily} ${leverageSymbol(index)}`, DECAY_COLORS[0], (entry) => entry.fund - entry.daily),
|
|
1154
|
+
// `3× QQQ`: three times QQQ's return over the period, the multiple a daily reset does not deliver.
|
|
1155
|
+
line("compounding", `${daily} ${leverageSymbol(index)} decay vs ${leverageFactorLabel(factor)} ${leverageSymbol(index)}`, DECAY_COLORS[1], (entry) => entry.daily - entry.period),
|
|
1156
|
+
];
|
|
1088
1157
|
}
|
|
1089
1158
|
|
|
1090
1159
|
/**
|
|
@@ -1188,16 +1257,23 @@ export function activeStudyInputSeriesIds(
|
|
|
1188
1257
|
.flatMap((spec) => spec.inputSeriesIds));
|
|
1189
1258
|
}
|
|
1190
1259
|
|
|
1260
|
+
/**
|
|
1261
|
+
* `anchors`: where each input series is measured from and to, for studies
|
|
1262
|
+
* that read returns over the chart's period (a leveraged fund's daily line and
|
|
1263
|
+
* its decay start where a comparison starts its legs). Without one, a series'
|
|
1264
|
+
* own first and last observations.
|
|
1265
|
+
*/
|
|
1191
1266
|
export function resolveStudies(
|
|
1192
1267
|
baseSeries: readonly ResolvedSeries[],
|
|
1193
1268
|
studySpecs: readonly ChartStudySpec[],
|
|
1194
1269
|
marketResolution?: ManualChartResolution,
|
|
1195
1270
|
historicalPriceSeries?: ReadonlyMap<string, ResolvedSeries>,
|
|
1271
|
+
anchors?: ReadonlyMap<string, StudyAnchor>,
|
|
1196
1272
|
): StudyResolutionResult {
|
|
1197
1273
|
// With one security on the chart its volume is just "Volume"; beside a
|
|
1198
1274
|
// comparison the legend names whose volume it is.
|
|
1199
1275
|
const nameVolumeInput = baseSeries.filter((series) => series.observationKind === "market").length > 1;
|
|
1200
|
-
return resolveStudySpecs(baseSeries, studySpecs, nameVolumeInput, marketResolution, historicalPriceSeries);
|
|
1276
|
+
return resolveStudySpecs(baseSeries, studySpecs, nameVolumeInput, marketResolution, historicalPriceSeries, anchors);
|
|
1201
1277
|
}
|
|
1202
1278
|
|
|
1203
1279
|
function resolveStudySpecs(
|
|
@@ -1206,6 +1282,7 @@ function resolveStudySpecs(
|
|
|
1206
1282
|
nameVolumeInput: boolean,
|
|
1207
1283
|
marketResolution?: ManualChartResolution,
|
|
1208
1284
|
historicalPriceSeries?: ReadonlyMap<string, ResolvedSeries>,
|
|
1285
|
+
anchors?: ReadonlyMap<string, StudyAnchor>,
|
|
1209
1286
|
): StudyResolutionResult {
|
|
1210
1287
|
const byId = new Map(baseSeries.map((series) => [series.id, series]));
|
|
1211
1288
|
const resolved: ResolvedSeries[] = [];
|
|
@@ -1218,7 +1295,7 @@ function resolveStudySpecs(
|
|
|
1218
1295
|
const inputs = spec.inputSeriesIds.map((id) => spec.kind === "realized-vol"
|
|
1219
1296
|
? historicalPriceSeries?.get(id) ?? byId.get(id) : byId.get(id));
|
|
1220
1297
|
if (inputs.length !== required || inputs.some((input) => !input)) {
|
|
1221
|
-
errors.push(`${spec
|
|
1298
|
+
errors.push(`${studyMessageName(spec)}: ${spec.kind} requires ${required} valid input series.`);
|
|
1222
1299
|
return;
|
|
1223
1300
|
}
|
|
1224
1301
|
const input = inputs[0]!;
|
|
@@ -1226,7 +1303,7 @@ function resolveStudySpecs(
|
|
|
1226
1303
|
const pairedInput = inputs[1]!;
|
|
1227
1304
|
const inputUnit = knownUnitSignature(input);
|
|
1228
1305
|
if (inputUnit === null || inputUnit !== knownUnitSignature(pairedInput)) {
|
|
1229
|
-
errors.push(`${spec
|
|
1306
|
+
errors.push(`${studyMessageName(spec)}: ${unitMismatchMessage("spread", { series: input, unit: input.unit }, { series: pairedInput, unit: pairedInput.unit })}`);
|
|
1230
1307
|
return;
|
|
1231
1308
|
}
|
|
1232
1309
|
}
|
|
@@ -1235,17 +1312,37 @@ function resolveStudySpecs(
|
|
|
1235
1312
|
const read = formulaTerms(spec, inputs as ResolvedSeries[]);
|
|
1236
1313
|
const mismatch = "error" in read ? read.error : formulaUnitMismatch(read, inputs as ResolvedSeries[]);
|
|
1237
1314
|
if (mismatch) {
|
|
1238
|
-
errors.push(`${spec
|
|
1315
|
+
errors.push(`${studyMessageName(spec)}: ${mismatch}`);
|
|
1239
1316
|
return;
|
|
1240
1317
|
}
|
|
1241
1318
|
terms = read as FormulaTerms;
|
|
1242
1319
|
}
|
|
1243
1320
|
const color = spec.color ?? KIND_COLORS[spec.kind] ?? STUDY_COLORS[index % STUDY_COLORS.length]!;
|
|
1321
|
+
if (spec.kind === "leverage" || spec.kind === "leverage-decay") {
|
|
1322
|
+
const index = inputs.at(-1)!;
|
|
1323
|
+
if (!isLeverageFactor(spec.parameters.factor)) {
|
|
1324
|
+
errors.push(`${studyMessageName(spec)}: choose a nonzero leverage factor of at most 5.`);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const issue = inputs.map((entry) => dailyBarsIssue(spec, entry!, marketResolution)).find(Boolean);
|
|
1328
|
+
if (issue) {
|
|
1329
|
+
warnings.push(issue);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
const outputs = spec.kind === "leverage"
|
|
1333
|
+
? resolveLeverageLine(spec, index, color, anchors?.get(index.id))
|
|
1334
|
+
: resolveLeverageDecay(spec, input, index, anchors);
|
|
1335
|
+
if (outputs.every((output) => output.points.length < 2)) {
|
|
1336
|
+
warnings.push(`${studyMessageName(spec)}: not enough shared daily closes to measure ${leverageFactorLabel(spec.parameters.factor as number)} daily ${leverageSymbol(index)}.`);
|
|
1337
|
+
}
|
|
1338
|
+
resolved.push(...outputs);
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1244
1341
|
if (spec.kind === "realized-vol") {
|
|
1245
1342
|
const window = spec.parameters.window ?? 30;
|
|
1246
1343
|
if (typeof window !== "number" || !Number.isInteger(window) || window < 2
|
|
1247
1344
|
|| (spec.parameters.estimator !== undefined && !isRealizedVolatilityEstimator(spec.parameters.estimator))) {
|
|
1248
|
-
errors.push(`${spec
|
|
1345
|
+
errors.push(`${studyMessageName(spec)}: choose a supported volatility estimator and a whole window of at least two sessions.`);
|
|
1249
1346
|
return;
|
|
1250
1347
|
}
|
|
1251
1348
|
const inputResolution = input.historyResolution === undefined ? marketResolution : input.historyResolution;
|
|
@@ -1253,17 +1350,17 @@ function resolveStudySpecs(
|
|
|
1253
1350
|
|| (inputResolution !== undefined && inputResolution !== "1d")
|
|
1254
1351
|
|| (input.timeBasis?.cadenceMs !== undefined && input.timeBasis.cadenceMs !== 86_400_000)
|
|
1255
1352
|
|| !(input.unitGroup === "price" || input.unitGroup.startsWith("price:"))) {
|
|
1256
|
-
errors.push(`${spec
|
|
1353
|
+
errors.push(`${studyMessageName(spec)}: realized volatility requires daily prices. Choose Auto or 1D resolution and a daily price source.`);
|
|
1257
1354
|
return;
|
|
1258
1355
|
}
|
|
1259
1356
|
const cadenceIssue = realizedVolatilityCadenceIssue(input.points);
|
|
1260
1357
|
if (cadenceIssue) {
|
|
1261
|
-
errors.push(`${spec
|
|
1358
|
+
errors.push(`${studyMessageName(spec)}: ${cadenceIssue}`);
|
|
1262
1359
|
return;
|
|
1263
1360
|
}
|
|
1264
1361
|
const outputs = resolveRealizedVolatility(spec, input, color);
|
|
1265
1362
|
if (outputs.every((output) => output.points.every((point) => point.value === null))) {
|
|
1266
|
-
warnings.push(`${spec
|
|
1363
|
+
warnings.push(`${studyMessageName(spec)}: not enough valid daily history to calculate ${spec.parameters.estimator ?? "close-to-close"} realized volatility.`);
|
|
1267
1364
|
}
|
|
1268
1365
|
resolved.push(...outputs);
|
|
1269
1366
|
return;
|
|
@@ -1307,11 +1404,11 @@ function resolveStudySpecs(
|
|
|
1307
1404
|
&& inputCurrency !== pairedCurrency;
|
|
1308
1405
|
if (spec.kind === "ratio" && differentCurrencies) {
|
|
1309
1406
|
warnings.push(
|
|
1310
|
-
`${spec
|
|
1407
|
+
`${studyMessageName(spec)}: ${spec.kind} inputs use different currencies (${inputCurrency} and ${pairedCurrency}); raw values are not FX-converted.`,
|
|
1311
1408
|
);
|
|
1312
1409
|
}
|
|
1313
1410
|
if (spec.kind === "correlation" && input.nativeFrequency !== pairedInput.nativeFrequency) {
|
|
1314
|
-
warnings.push(`${spec
|
|
1411
|
+
warnings.push(`${studyMessageName(spec)}: correlation mixes ${input.nativeFrequency} and ${pairedInput.nativeFrequency} observations; only matching observation times contribute.`);
|
|
1315
1412
|
}
|
|
1316
1413
|
outputs = resolvePairStudy(spec, input, pairedInput, color);
|
|
1317
1414
|
}
|
|
@@ -1319,7 +1416,7 @@ function resolveStudySpecs(
|
|
|
1319
1416
|
spec.kind !== "volume"
|
|
1320
1417
|
&& (outputs.length === 0 || outputs.every((output) => output.points.length === 0))
|
|
1321
1418
|
) {
|
|
1322
|
-
warnings.push(`${spec
|
|
1419
|
+
warnings.push(`${studyMessageName(spec)}: not enough valid history to calculate ${spec.kind}.`);
|
|
1323
1420
|
}
|
|
1324
1421
|
resolved.push(...outputs);
|
|
1325
1422
|
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { logFailure } from "../utils/log-failure";
|
|
2
|
+
import { fetchHistoryResult } from "../sources/history-result";
|
|
3
|
+
import type { PriceHistoryResult } from "../types/price-history";
|
|
4
|
+
import { CHART_RESOLUTION_STEP_MS } from "./resolution";
|
|
5
|
+
import type { DataProvider } from "../types/data-provider";
|
|
6
|
+
import type { TickerFinancials } from "../types/financials";
|
|
7
|
+
import { getPricePointTimestamp } from "../utils/price-history";
|
|
8
|
+
import { type ChartResolveSources, type ChartResolveCache, requestContext } from "./resolve-context";
|
|
9
|
+
import { DAY_MS } from "./chart-bounds";
|
|
10
|
+
import { priceHistoryAcquisitionIdentity, priceHistoryTailAcquisition, priceHistoryWindowsAgree, mergePriceHistoryWindows } from "./history-acquisition";
|
|
11
|
+
|
|
12
|
+
/** Recent-window requests for the same history, shared by every chart asking at once. */
|
|
13
|
+
const tailRequestsInFlight = new WeakMap<DataProvider, Map<string, Promise<PriceHistoryResult | null>>>();
|
|
14
|
+
|
|
15
|
+
/** Older tails are left to a full history refresh rather than a recent-window request. */
|
|
16
|
+
const MAX_TAIL_RECONCILE_LAG_MS = 3 * DAY_MS;
|
|
17
|
+
|
|
18
|
+
function latestObservationTime(points: TickerFinancials["priceHistory"]): number {
|
|
19
|
+
for (let index = points.length - 1; index >= 0; index -= 1) {
|
|
20
|
+
const point = points[index]!;
|
|
21
|
+
const time = getPricePointTimestamp(point);
|
|
22
|
+
if (Number.isFinite(time) && Number.isFinite(point.close)) return time;
|
|
23
|
+
}
|
|
24
|
+
return Number.NaN;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function samePricePoints(left: TickerFinancials["priceHistory"], right: TickerFinancials["priceHistory"]): boolean {
|
|
28
|
+
if (left.length !== right.length) return false;
|
|
29
|
+
for (let index = left.length - 1; index >= 0; index -= 1) {
|
|
30
|
+
const a = left[index]!;
|
|
31
|
+
const b = right[index]!;
|
|
32
|
+
if (a === b) continue;
|
|
33
|
+
if (getPricePointTimestamp(a) !== getPricePointTimestamp(b) || a.open !== b.open || a.high !== b.high
|
|
34
|
+
|| a.low !== b.low || a.close !== b.close || a.volume !== b.volume) return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Refreshes the newest bars of each intraday history the last resolve plotted
|
|
41
|
+
* up to the present, so bars formed from quotes settle to the provider's
|
|
42
|
+
* version shortly after they close. Only the recent window is requested, and a
|
|
43
|
+
* window from a different source or session contract is never mixed in.
|
|
44
|
+
* Resolves true when any plotted bar changed.
|
|
45
|
+
*/
|
|
46
|
+
export function reconcileChartTail(
|
|
47
|
+
sources: Pick<ChartResolveSources, "dataProvider">,
|
|
48
|
+
cache: ChartResolveCache,
|
|
49
|
+
now = Date.now(),
|
|
50
|
+
): Promise<boolean> {
|
|
51
|
+
if (cache.tailReconcile) return cache.tailReconcile;
|
|
52
|
+
const provider = sources.dataProvider;
|
|
53
|
+
if (!provider || cache.liveTails.size === 0
|
|
54
|
+
|| !(provider.getDetailedPriceHistoryWithMetadata || provider.getDetailedPriceHistory)) {
|
|
55
|
+
return Promise.resolve(false);
|
|
56
|
+
}
|
|
57
|
+
const run: Promise<boolean> = Promise.all([...cache.liveTails].map(async ([key, tail]) => {
|
|
58
|
+
const pending = cache.priceHistoryByRequest.get(key);
|
|
59
|
+
const loaded = pending ? await pending.catch(() => {
|
|
60
|
+
// Intentional ignore: resolveChartSpecPass already includes this history failure in the series errors.
|
|
61
|
+
return null;
|
|
62
|
+
}) : null;
|
|
63
|
+
if (!pending || !loaded || loaded.resolution !== tail.resolution) return false;
|
|
64
|
+
const accumulated = cache.accumulatedPriceHistory.get(tail.accumulationKey);
|
|
65
|
+
const latest = latestObservationTime(accumulated?.points ?? loaded.points);
|
|
66
|
+
if (!Number.isFinite(latest) || now - latest > MAX_TAIL_RECONCILE_LAG_MS) return false;
|
|
67
|
+
// The newest loaded bar may still have been forming when it was fetched.
|
|
68
|
+
// Whole-bar starts and whole-minute ends let every chart of this history
|
|
69
|
+
// that asks at the same boundary share one request and one cached window.
|
|
70
|
+
const step = CHART_RESOLUTION_STEP_MS[tail.resolution];
|
|
71
|
+
const start = Math.floor((latest - step) / step) * step;
|
|
72
|
+
const end = Math.ceil(now / 60_000) * 60_000;
|
|
73
|
+
const { symbol, exchange } = tail.source.instrument;
|
|
74
|
+
const context = { ...requestContext(tail.source), ...(tail.historySession ? { historySession: tail.historySession } : {}) };
|
|
75
|
+
const requestKey = JSON.stringify([symbol, exchange ?? "", context.brokerId ?? null,
|
|
76
|
+
context.brokerInstanceId ?? null, context.instrument ?? null, tail.resolution, start, end, tail.historySession ?? null]);
|
|
77
|
+
const shared = tailRequestsInFlight.get(provider) ?? new Map<string, Promise<PriceHistoryResult | null>>();
|
|
78
|
+
tailRequestsInFlight.set(provider, shared);
|
|
79
|
+
let request = shared.get(requestKey);
|
|
80
|
+
if (!request) {
|
|
81
|
+
const fetched = fetchHistoryResult(provider, symbol, exchange ?? "",
|
|
82
|
+
{ kind: "detail", start: new Date(start), end: new Date(end), interval: tail.resolution }, context)
|
|
83
|
+
.catch(logFailure("time-series-resolve", "Could not reconcile chart history", null))
|
|
84
|
+
.finally(() => {
|
|
85
|
+
if (shared.get(requestKey) === fetched) shared.delete(requestKey);
|
|
86
|
+
});
|
|
87
|
+
shared.set(requestKey, fetched);
|
|
88
|
+
request = fetched;
|
|
89
|
+
}
|
|
90
|
+
const result = await request;
|
|
91
|
+
if (!result || result.resolution !== loaded.resolution || !!result.extendedHours !== !!loaded.extendedHours
|
|
92
|
+
|| priceHistoryAcquisitionIdentity(result) !== priceHistoryAcquisitionIdentity(loaded)) return false;
|
|
93
|
+
const points = result.points.filter((point) => {
|
|
94
|
+
const time = getPricePointTimestamp(point);
|
|
95
|
+
return Number.isFinite(time) && time >= start && Number.isFinite(point.close);
|
|
96
|
+
});
|
|
97
|
+
// A full refetch that landed meanwhile supersedes this window.
|
|
98
|
+
if (points.length === 0 || cache.priceHistoryByRequest.get(key) !== pending) return false;
|
|
99
|
+
// A tail on another split basis (a split since the window was loaded) is not drawn beside it:
|
|
100
|
+
// the window stays as loaded, on one basis, until it is refetched whole.
|
|
101
|
+
if (!priceHistoryWindowsAgree(loaded.points, points, tail.resolution)) return false;
|
|
102
|
+
const refreshed = mergePriceHistoryWindows(loaded.points, points, tail.resolution);
|
|
103
|
+
if (samePricePoints(refreshed, loaded.points)) return false;
|
|
104
|
+
const acquisition = priceHistoryTailAcquisition(loaded, result);
|
|
105
|
+
// The merged bars are as current as the newer acquisition says.
|
|
106
|
+
cache.priceHistoryByRequest.set(key, Promise.resolve({ ...loaded, points: refreshed,
|
|
107
|
+
session: acquisition.session, sourceKey: acquisition.sourceKey, savedCopyBehind: acquisition.savedCopyBehind }));
|
|
108
|
+
if (accumulated) {
|
|
109
|
+
const accumulatedAcquisition = priceHistoryTailAcquisition(accumulated, result);
|
|
110
|
+
cache.accumulatedPriceHistory.set(tail.accumulationKey, { ...accumulated,
|
|
111
|
+
points: mergePriceHistoryWindows(accumulated.points, points, tail.resolution),
|
|
112
|
+
session: accumulatedAcquisition.session, sourceKey: accumulatedAcquisition.sourceKey });
|
|
113
|
+
}
|
|
114
|
+
cache.revision += 1;
|
|
115
|
+
return true;
|
|
116
|
+
})).then((changes) => changes.some(Boolean)).finally(() => {
|
|
117
|
+
if (cache.tailReconcile === run) cache.tailReconcile = null;
|
|
118
|
+
});
|
|
119
|
+
cache.tailReconcile = run;
|
|
120
|
+
return run;
|
|
121
|
+
}
|