gloomberb 0.11.3 → 0.13.1
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 -4
- package/README.zh-CN.md +4 -2
- package/package.json +28 -2
- package/src/api-client/auth.ts +12 -1
- package/src/api-client/data.ts +103 -1
- package/src/api-client/index.test.ts +127 -0
- package/src/api-client/index.ts +139 -3
- package/src/api-client/normalizers.ts +39 -0
- package/src/api-client/paths.ts +85 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +197 -0
- package/src/app/pane-runtime/index.tsx +15 -12
- package/src/app/pane-runtime/layout-placement.test.ts +27 -0
- package/src/app/pane-runtime/layout-placement.ts +13 -0
- package/src/app/pane-runtime/plugin-bindings.ts +62 -0
- package/src/app.tsx +60 -18
- package/src/architecture/browser-entry-graph.test.ts +96 -0
- package/src/brokers/profile-form.test.ts +25 -27
- package/src/brokers/test-broker.ts +68 -0
- package/src/cli/commands/plugins.ts +43 -14
- package/src/cli/desktop-pane-shot.ts +362 -24
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +126 -43
- package/src/cli/pane-functions/catalog.ts +16 -4
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/headless.test.ts +247 -0
- package/src/cli/pane-functions/headless.ts +423 -0
- package/src/cli/pane-functions/index.test.ts +20 -0
- package/src/cli/pane-functions/index.ts +10 -6
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/options.ts +27 -2
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +69 -4
- package/src/cli/pane-functions/resolver.ts +26 -8
- package/src/cli/pane-functions/screenshot.test.ts +76 -0
- package/src/cli/pane-functions/screenshot.ts +428 -42
- package/src/cli/restore-plugins.ts +41 -0
- package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
- package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
- package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
- package/src/components/chart/composite/composite-chart.test.tsx +136 -21
- package/src/components/chart/composite/composite-chart.tsx +378 -215
- package/src/components/chart/composite/format.ts +8 -2
- package/src/components/chart/composite/interactions.test.ts +68 -102
- package/src/components/chart/composite/interactions.ts +214 -264
- package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
- package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +149 -19
- package/src/components/chart/composite/text-renderer.ts +16 -2
- package/src/components/chart/composite/time-scale.ts +163 -62
- package/src/components/chart/composite/types.ts +51 -1
- package/src/components/chart/core/data.ts +9 -13
- package/src/components/chart/core/date-window.ts +7 -1
- package/src/components/chart/core/pointer.ts +3 -0
- package/src/components/chart/core/types.ts +0 -45
- package/src/components/chart/native/chart-rasterizer.ts +0 -1
- package/src/components/chart/native/kitty/index.test.ts +0 -188
- package/src/components/chart/native/raster/crosshair.ts +6 -5
- package/src/components/chart/native/raster/primitives.ts +0 -23
- package/src/components/chart/native/renderer-selection.ts +81 -31
- package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
- package/src/components/chart/static/chart-surface.tsx +150 -0
- package/src/components/chart/static/index.ts +8 -0
- package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
- package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
- package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
- package/src/components/chart/static/series.ts +49 -0
- package/src/components/command-bar/action-runtime.ts +1 -3
- package/src/components/command-bar/assist/model.test.ts +17 -2
- package/src/components/command-bar/assist/model.ts +40 -9
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +1 -1
- package/src/components/command-bar/commands/registry.ts +0 -11
- package/src/components/command-bar/helpers.test.ts +2 -2
- package/src/components/command-bar/helpers.ts +2 -6
- package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
- package/src/components/command-bar/list/badge.test.ts +54 -0
- package/src/components/command-bar/list/badge.ts +62 -0
- package/src/components/command-bar/list/model.test.ts +103 -0
- package/src/components/command-bar/list/model.ts +61 -4
- package/src/components/command-bar/list/view.test.tsx +113 -0
- package/src/components/command-bar/list/view.tsx +147 -131
- package/src/components/command-bar/pane-templates/workflow.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +51 -65
- package/src/components/command-bar/panel/index.tsx +48 -13
- package/src/components/command-bar/panel/layout.test.ts +139 -0
- package/src/components/command-bar/panel/layout.ts +65 -28
- package/src/components/command-bar/panel/native-surface.ts +24 -0
- package/src/components/command-bar/panel/prompt-binding.ts +54 -0
- package/src/components/command-bar/panel/runtime.ts +6 -3
- package/src/components/command-bar/panel/state.ts +41 -7
- package/src/components/command-bar/panel/types.ts +7 -1
- package/src/components/command-bar/routes/root/provider-search.ts +34 -15
- package/src/components/command-bar/routes/root/results.test.ts +161 -0
- package/src/components/command-bar/routes/root/results.ts +30 -28
- package/src/components/command-bar/routes/root/runtime.ts +37 -20
- package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
- package/src/components/command-bar/routes/root/search-providers.ts +205 -0
- package/src/components/command-bar/routes/root/selection.ts +1 -12
- package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
- package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
- package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
- package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
- package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
- package/src/components/command-bar/routing/actions.ts +1 -81
- package/src/components/command-bar/routing/launch-request.ts +1 -1
- package/src/components/command-bar/routing/list-state.ts +6 -21
- package/src/components/command-bar/selection-runtime.ts +1 -1
- package/src/components/command-bar/surface/assist.test.tsx +34 -19
- package/src/components/command-bar/surface/environment.ts +8 -1
- package/src/components/command-bar/surface/index.test.tsx +84 -68
- package/src/components/command-bar/surface/index.tsx +33 -36
- package/src/components/command-bar/surface/test-harness.tsx +38 -7
- package/src/components/command-bar/theme-picker.tsx +43 -15
- package/src/components/command-bar/view-model.test.ts +53 -8
- package/src/components/command-bar/view-model.ts +43 -14
- package/src/components/command-bar/workflow/tickers.ts +1 -0
- package/src/components/command-bar/workflow/types.ts +1 -1
- package/src/components/data-table/view.test.tsx +45 -6
- package/src/components/data-table/view.tsx +55 -9
- package/src/components/index.ts +10 -2
- package/src/components/input-search-bar.test.tsx +36 -2
- package/src/components/input-search-bar.tsx +18 -1
- package/src/components/layout/header.test.tsx +57 -10
- package/src/components/layout/header.tsx +292 -149
- package/src/components/layout/market-summary.test.ts +81 -0
- package/src/components/layout/market-summary.ts +109 -0
- package/src/components/layout/shell/chrome.ts +96 -1
- package/src/components/layout/shell/index.tsx +11 -3
- package/src/components/layout/shell/menu.ts +9 -0
- package/src/components/layout/shell/pane/actions.ts +29 -0
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/shortcuts.ts +3 -0
- package/src/components/layout/status-bar.test.tsx +9 -8
- package/src/components/layout/status-bar.tsx +85 -54
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-frame.tsx +2 -26
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/components/price-sparkline/index.ts +61 -34
- package/src/components/price-sparkline/view.tsx +1 -1
- package/src/components/sign-in-gate.test.tsx +128 -0
- package/src/components/sign-in-gate.tsx +149 -0
- package/src/components/speedometer-gauge/desktop.tsx +2 -12
- package/src/components/ticker/badge/list.tsx +17 -3
- package/src/components/ticker/list-table-view.tsx +6 -3
- package/src/components/ui/data-table/index.tsx +3 -2
- package/src/components/ui/data-table/opentui/index.tsx +165 -120
- package/src/components/ui/frame.tsx +28 -0
- package/src/components/ui/index.ts +3 -0
- package/src/components/ui/native-select.tsx +20 -10
- package/src/components/ui/select-button.tsx +138 -0
- package/src/components/ui/status.tsx +2 -2
- package/src/components/ui/table-layout.ts +1 -1
- package/src/components/use-double-click-activation.ts +7 -3
- package/src/core/app-services.ts +3 -3
- package/src/data/config/layout.ts +12 -6
- package/src/data/config/store/index.test.ts +120 -0
- package/src/data/config/store/migrations.ts +41 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +9 -3
- package/src/i18n/es.ts +6 -2
- package/src/i18n/ja.ts +6 -2
- package/src/i18n/ko.ts +6 -2
- package/src/i18n/zh-cn.ts +6 -2
- package/src/i18n/zh-tw.ts +6 -2
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/layout-marketplace/payload.test.ts +17 -0
- package/src/layout-marketplace/payload.ts +40 -5
- package/src/market-data/coordinator/index.ts +3 -2
- package/src/market-data/coordinator/quotes.ts +173 -66
- package/src/market-data/coordinator/subscriptions.test.ts +215 -37
- package/src/pane-settings.ts +1 -1
- package/src/plugins/builtin/analytics/pane-model.ts +1 -1
- package/src/plugins/builtin/analytics/view.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
- package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
- package/src/plugins/builtin/chat/content/composer.tsx +8 -4
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +189 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +274 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +19 -227
- package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/composite-plugins.ts +14 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
- package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
- package/src/plugins/builtin/credit-conditions/index.tsx +6 -21
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +223 -0
- package/src/plugins/builtin/earnings-calls/format.ts +49 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +760 -0
- package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
- package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +337 -0
- package/src/plugins/builtin/econ/detail-view.tsx +1 -2
- package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
- package/src/plugins/builtin/econ/index.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
- package/src/plugins/builtin/econ-statistics/client.ts +92 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
- package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
- package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
- package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
- package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
- package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
- package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
- package/src/plugins/builtin/econ-statistics/view.ts +90 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
- package/src/plugins/builtin/fear-greed/data.ts +6 -1
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/help/index.tsx +4 -0
- package/src/plugins/builtin/holders/index.tsx +1 -0
- package/src/plugins/builtin/insider/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/market-valuation/align.ts +122 -0
- package/src/plugins/builtin/market-valuation/cache.ts +32 -0
- package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
- package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
- package/src/plugins/builtin/market-valuation/client.ts +116 -0
- package/src/plugins/builtin/market-valuation/defs.ts +251 -0
- package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
- package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
- package/src/plugins/builtin/market-valuation/headless.ts +170 -0
- package/src/plugins/builtin/market-valuation/index.tsx +64 -0
- package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
- package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
- package/src/plugins/builtin/market-valuation/model.ts +8 -0
- package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
- package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
- package/src/plugins/builtin/market-valuation/series.ts +12 -0
- package/src/plugins/builtin/market-valuation/settings.ts +38 -0
- package/src/plugins/builtin/market-valuation/sources.ts +130 -0
- package/src/plugins/builtin/market-valuation/trend.ts +89 -0
- package/src/plugins/builtin/market-valuation/view.ts +137 -0
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
- package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
- package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
- package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
- package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
- package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
- package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
- package/src/plugins/builtin/news/wire/index.tsx +31 -44
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +29 -1
- package/src/plugins/builtin/options/view.tsx +1 -22
- package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
- package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
- package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
- package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
- package/src/plugins/builtin/plugin-module.ts +9 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/index.tsx +8 -0
- package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
- package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
- package/src/plugins/builtin/research-search/data.ts +109 -0
- package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
- package/src/plugins/builtin/research-search/document-view.tsx +254 -0
- package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
- package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
- package/src/plugins/builtin/research-search/index.tsx +85 -0
- package/src/plugins/builtin/research-search/model.test.ts +40 -0
- package/src/plugins/builtin/research-search/model.ts +291 -0
- package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
- package/src/plugins/builtin/research-search/pane.tsx +682 -0
- package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
- package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
- package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
- package/src/plugins/builtin/research-search/snippet.ts +134 -0
- package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
- package/src/plugins/builtin/scanner/flow-model.ts +29 -1
- package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/index.tsx +1 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/shared/series-cache.ts +93 -0
- package/src/plugins/builtin/short-interest/index.tsx +1 -0
- package/src/plugins/builtin/short-interest/pane.tsx +1 -1
- package/src/plugins/builtin/thirteenf/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +5 -1
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
- package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
- package/src/plugins/builtin/world-venue-map/model.ts +134 -13
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +7 -9
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +166 -0
- package/src/plugins/bundle.ts +141 -0
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +1 -13
- package/src/plugins/catalog-browser.ts +14 -0
- package/src/plugins/catalog-manifest.test.ts +58 -0
- package/src/plugins/catalog-manifest.ts +99 -0
- package/src/plugins/catalog-ui.ts +20 -10
- package/src/plugins/catalog.ts +9 -2
- package/src/plugins/current-target.ts +28 -0
- package/src/plugins/host-contract.ts +46 -0
- package/src/plugins/host-link.ts +158 -0
- package/src/plugins/host-modules.ts +73 -0
- package/src/plugins/loader.ts +90 -23
- package/src/plugins/ownership.ts +1 -0
- package/src/plugins/prediction-markets/index.tsx +1 -0
- package/src/plugins/registry/context.ts +3 -0
- package/src/plugins/registry/contributions.ts +27 -0
- package/src/plugins/registry/index.test.ts +25 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +6 -13
- package/src/plugins/seed.test.ts +83 -0
- package/src/plugins/seed.ts +83 -0
- package/src/public/broker.ts +37 -0
- package/src/public/market-data.ts +9 -0
- package/src/public/pane-hooks.ts +57 -0
- package/src/public/public-api.test.ts +285 -0
- package/src/public/react.ts +62 -0
- package/src/public/test-support.ts +23 -0
- package/src/public/time-series.ts +8 -0
- package/src/public/utils.ts +77 -0
- package/src/remote/layout-helpers.ts +6 -8
- package/src/remote/resources.ts +2 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +323 -39
- package/src/remote/semantic-tree.test.tsx +105 -0
- package/src/remote/semantic-tree.tsx +53 -35
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +8 -0
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/cloudflare/worker.test.ts +19 -0
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +156 -0
- package/src/renderers/electrobun/bun/index.ts +5 -2
- package/src/renderers/electrobun/shared/protocol.ts +21 -0
- package/src/renderers/electrobun/view/build-assets.ts +0 -3
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +114 -11
- package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
- package/src/renderers/electrobun/view/external-plugins.ts +63 -0
- package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
- package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
- package/src/renderers/electrobun/view/host/mouse.ts +16 -2
- package/src/renderers/electrobun/view/main.tsx +27 -2
- package/src/renderers/electrobun/view/styles.css +5 -0
- package/src/renderers/electrobun/view/toast-host.tsx +18 -0
- package/src/renderers/opentui/chart-surface.tsx +5 -1
- package/src/renderers/opentui/host.tsx +16 -2
- package/src/renderers/opentui/interaction-performance.test.ts +79 -0
- package/src/renderers/opentui/interaction-performance.ts +188 -0
- package/src/renderers/opentui/start.tsx +26 -1
- package/src/renderers/opentui/test-utils.tsx +13 -0
- package/src/renderers/opentui/toast-host.tsx +26 -2
- package/src/renderers/opentui/ui-host.tsx +1 -0
- package/src/sources/provider-router/search.test.ts +191 -0
- package/src/sources/provider-router/search.ts +92 -39
- package/src/state/app/context/index.tsx +2 -0
- package/src/state/app/context/selector.test.tsx +33 -1
- package/src/state/hooks/quote-streaming.test.tsx +93 -0
- package/src/state/hooks/quote-streaming.ts +106 -30
- package/src/state/pane-table-export-registry.ts +19 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/theme/color-utils.ts +1 -1
- package/src/theme/colors.ts +50 -0
- package/src/theme/themes.test.ts +15 -2
- package/src/theme/themes.ts +19 -1
- package/src/tickers/search/index.test.ts +26 -0
- package/src/tickers/search/index.ts +42 -17
- package/src/tickers/search/ranking.ts +14 -0
- package/src/time-series/chart-data.test.ts +216 -0
- package/src/time-series/resolution.test.ts +25 -26
- package/src/time-series/resolution.ts +41 -10
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +21 -16
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.ts +5 -0
- package/src/types/config.ts +125 -3
- package/src/types/data-provider.ts +11 -0
- package/src/types/headless.ts +161 -0
- package/src/types/plugin.ts +102 -0
- package/src/ui/host.tsx +12 -1
- package/src/ui/index.tsx +1 -0
- package/src/ui/toast.tsx +8 -4
- package/src/utils/format.ts +35 -0
- package/src/utils/fuzzy-search.test.ts +94 -0
- package/src/utils/fuzzy-search.ts +81 -22
- package/src/utils/shortcut-labels.ts +14 -0
- package/src/components/chart/core/axis-format.ts +0 -157
- package/src/components/chart/core/index.test.ts +0 -1075
- package/src/components/chart/core/projection.ts +0 -357
- package/src/components/chart/core/render-utils.ts +0 -4
- package/src/components/chart/core/renderer.ts +0 -244
- package/src/components/chart/core/scene.ts +0 -180
- package/src/components/chart/core/time-axis.ts +0 -330
- package/src/components/chart/cursor-motion.ts +0 -47
- package/src/components/chart/indicators/types.ts +0 -26
- package/src/components/chart/market-session.ts +0 -176
- package/src/components/chart/multi-line/renderer.test.ts +0 -83
- package/src/components/chart/multi-line/renderer.ts +0 -161
- package/src/components/chart/multi-line/scene.ts +0 -188
- package/src/components/chart/native/raster/layout.ts +0 -129
- package/src/components/chart/native/raster/price-chart.ts +0 -244
- package/src/components/chart/rendering/chart-draw.ts +0 -270
- package/src/components/chart/rendering/geometry.ts +0 -95
- package/src/components/chart/rendering/pixel-buffer.ts +0 -267
- package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
- package/src/components/chart/scatter-chart-renderer.ts +0 -223
- package/src/components/chart/static/chart/surface.tsx +0 -358
- package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
- package/src/components/command-bar/routes/root/article-results.ts +0 -55
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
- package/src/plugins/builtin/news/wire/article-search.ts +0 -140
- package/src/plugins/builtin/substack/api/auth.ts +0 -185
- package/src/plugins/builtin/substack/api/cache.ts +0 -82
- package/src/plugins/builtin/substack/api/loaders.ts +0 -170
- package/src/plugins/builtin/substack/api/store.ts +0 -186
- package/src/plugins/builtin/substack/api/types.ts +0 -42
- package/src/plugins/builtin/substack/api.test.ts +0 -298
- package/src/plugins/builtin/substack/article-detail.tsx +0 -397
- package/src/plugins/builtin/substack/article-stack.tsx +0 -133
- package/src/plugins/builtin/substack/content.ts +0 -478
- package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
- package/src/plugins/builtin/substack/index.tsx +0 -49
- package/src/plugins/builtin/substack/login-view.tsx +0 -126
- package/src/plugins/builtin/substack/model.test.ts +0 -155
- package/src/plugins/builtin/substack/normalize.ts +0 -343
- package/src/plugins/builtin/substack/pane-footer.ts +0 -52
- package/src/plugins/builtin/substack/pane-state.ts +0 -170
- package/src/plugins/builtin/substack/pane.tsx +0 -547
- package/src/plugins/builtin/substack/read-state.ts +0 -30
- package/src/plugins/builtin/substack/table.ts +0 -111
- package/src/plugins/builtin/substack/types.ts +0 -71
- package/src/plugins/builtin/substack/utils.ts +0 -131
- package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
- package/src/plugins/ibkr/account-cache.test.ts +0 -102
- package/src/plugins/ibkr/account-cache.ts +0 -24
- package/src/plugins/ibkr/broker-adapter.ts +0 -286
- package/src/plugins/ibkr/config.test.ts +0 -79
- package/src/plugins/ibkr/config.ts +0 -250
- package/src/plugins/ibkr/dialogs.tsx +0 -40
- package/src/plugins/ibkr/flex/index.test.ts +0 -264
- package/src/plugins/ibkr/flex/index.ts +0 -367
- package/src/plugins/ibkr/flex/statement-client.ts +0 -235
- package/src/plugins/ibkr/fundamental-parser.ts +0 -149
- package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
- package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
- package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
- package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
- package/src/plugins/ibkr/gateway/connection.ts +0 -152
- package/src/plugins/ibkr/gateway/contracts.ts +0 -132
- package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
- package/src/plugins/ibkr/gateway/helpers.ts +0 -33
- package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
- package/src/plugins/ibkr/gateway/history.ts +0 -120
- package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
- package/src/plugins/ibkr/gateway/market-data.ts +0 -345
- package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
- package/src/plugins/ibkr/gateway/orders.ts +0 -63
- package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
- package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
- package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
- package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
- package/src/plugins/ibkr/gateway/selection.ts +0 -51
- package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
- package/src/plugins/ibkr/gateway/service/index.ts +0 -429
- package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
- package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
- package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
- package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
- package/src/plugins/ibkr/gateway/service/native.ts +0 -543
- package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
- package/src/plugins/ibkr/gateway/types.ts +0 -27
- package/src/plugins/ibkr/index.test.tsx +0 -162
- package/src/plugins/ibkr/index.tsx +0 -159
- package/src/plugins/ibkr/instance-selection.test.ts +0 -120
- package/src/plugins/ibkr/instance-selection.ts +0 -36
- package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
- package/src/plugins/ibkr/portfolio-performance.ts +0 -129
- package/src/plugins/ibkr/trade/badge.tsx +0 -28
- package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
- package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
- package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
- package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
- package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
- package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
- package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
- package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
- package/src/plugins/ibkr/trade/tab/model.ts +0 -159
- package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
- package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
- package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
- package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
- package/src/plugins/ibkr/trade/utils.test.ts +0 -175
- package/src/plugins/ibkr/trade/utils.ts +0 -144
- package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
- package/src/plugins/ibkr/trading/pane.tsx +0 -332
- package/src/plugins/ibkr/trading/state.test.ts +0 -109
- package/src/plugins/ibkr/trading/state.ts +0 -335
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { appendLiveQuotePoint } from "./chart-data";
|
|
3
|
+
import type { PricePoint, Quote } from "../types/financials";
|
|
4
|
+
|
|
5
|
+
function quoteFixture(overrides: Partial<Quote> = {}): Quote {
|
|
6
|
+
return {
|
|
7
|
+
symbol: "INTC",
|
|
8
|
+
price: 129,
|
|
9
|
+
currency: "USD",
|
|
10
|
+
change: 0,
|
|
11
|
+
changePercent: 0,
|
|
12
|
+
lastUpdated: Date.parse("2026-05-15T20:30:00Z"),
|
|
13
|
+
listingExchangeName: "NASDAQ",
|
|
14
|
+
marketState: "REGULAR",
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("appendLiveQuotePoint", () => {
|
|
20
|
+
test("extends coarse chart histories with a fresh quote tail", () => {
|
|
21
|
+
const history: PricePoint[] = [
|
|
22
|
+
{ date: new Date("2026-05-04T00:00:00Z"), close: 56 },
|
|
23
|
+
{ date: new Date("2026-05-11T00:00:00Z"), close: 68 },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const extended = appendLiveQuotePoint(
|
|
27
|
+
history,
|
|
28
|
+
quoteFixture(),
|
|
29
|
+
{ now: Date.parse("2026-05-15T21:00:00Z") },
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(extended).toHaveLength(3);
|
|
33
|
+
expect(extended.at(-1)).toEqual({
|
|
34
|
+
date: new Date("2026-05-15T20:30:00Z"),
|
|
35
|
+
close: 129,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("merges a quote into the active OHLC bucket", () => {
|
|
40
|
+
const history: PricePoint[] = [
|
|
41
|
+
{
|
|
42
|
+
date: new Date("2026-05-15T20:25:00Z"),
|
|
43
|
+
open: 124,
|
|
44
|
+
high: 130,
|
|
45
|
+
low: 122,
|
|
46
|
+
close: 126,
|
|
47
|
+
volume: 1_000,
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const extended = appendLiveQuotePoint(
|
|
52
|
+
history,
|
|
53
|
+
quoteFixture({ lastUpdated: Date.parse("2026-05-15T20:29:00Z") }),
|
|
54
|
+
{
|
|
55
|
+
now: Date.parse("2026-05-15T20:30:00Z"),
|
|
56
|
+
mode: "ohlc",
|
|
57
|
+
resolution: "5m",
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(extended).toHaveLength(1);
|
|
62
|
+
expect(extended[0]).toEqual({
|
|
63
|
+
date: new Date("2026-05-15T20:25:00Z"),
|
|
64
|
+
open: 124,
|
|
65
|
+
high: 130,
|
|
66
|
+
low: 122,
|
|
67
|
+
close: 129,
|
|
68
|
+
volume: 1_000,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("seeds a new OHLC bucket from the live price instead of prior-bar extremes", () => {
|
|
73
|
+
const history: PricePoint[] = [
|
|
74
|
+
{
|
|
75
|
+
date: new Date("2026-05-15T20:25:00Z"),
|
|
76
|
+
open: 124,
|
|
77
|
+
high: 180,
|
|
78
|
+
low: 80,
|
|
79
|
+
close: 126,
|
|
80
|
+
volume: 1_000,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const extended = appendLiveQuotePoint(
|
|
85
|
+
history,
|
|
86
|
+
quoteFixture({ lastUpdated: Date.parse("2026-05-15T20:30:00Z") }),
|
|
87
|
+
{
|
|
88
|
+
now: Date.parse("2026-05-15T20:31:00Z"),
|
|
89
|
+
mode: "ohlc",
|
|
90
|
+
resolution: "5m",
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(extended).toHaveLength(2);
|
|
95
|
+
expect(extended[1]).toEqual({
|
|
96
|
+
date: new Date("2026-05-15T20:30:00Z"),
|
|
97
|
+
open: 129,
|
|
98
|
+
high: 129,
|
|
99
|
+
low: 129,
|
|
100
|
+
close: 129,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("treats calendar-month bars as variable-length buckets", () => {
|
|
105
|
+
const history: PricePoint[] = [{
|
|
106
|
+
date: new Date("2026-01-01T00:00:00Z"),
|
|
107
|
+
open: 120,
|
|
108
|
+
high: 140,
|
|
109
|
+
low: 110,
|
|
110
|
+
close: 125,
|
|
111
|
+
}];
|
|
112
|
+
|
|
113
|
+
const endOfJanuary = appendLiveQuotePoint(
|
|
114
|
+
history,
|
|
115
|
+
quoteFixture({ lastUpdated: Date.parse("2026-01-31T20:30:00Z") }),
|
|
116
|
+
{
|
|
117
|
+
now: Date.parse("2026-01-31T20:31:00Z"),
|
|
118
|
+
mode: "ohlc",
|
|
119
|
+
resolution: "1mo",
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
const startOfFebruary = appendLiveQuotePoint(
|
|
123
|
+
history,
|
|
124
|
+
quoteFixture({ lastUpdated: Date.parse("2026-02-01T20:30:00Z") }),
|
|
125
|
+
{
|
|
126
|
+
now: Date.parse("2026-02-01T20:31:00Z"),
|
|
127
|
+
mode: "ohlc",
|
|
128
|
+
resolution: "1mo",
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(endOfJanuary).toHaveLength(1);
|
|
133
|
+
expect(endOfJanuary[0]?.close).toBe(129);
|
|
134
|
+
expect(startOfFebruary).toHaveLength(2);
|
|
135
|
+
expect(startOfFebruary[1]).toMatchObject({
|
|
136
|
+
open: 129,
|
|
137
|
+
high: 129,
|
|
138
|
+
low: 129,
|
|
139
|
+
close: 129,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("uses the active extended-hours price for the live tail", () => {
|
|
144
|
+
const history: PricePoint[] = [
|
|
145
|
+
{ date: new Date("2026-05-15T19:30:00Z"), close: 128 },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const extended = appendLiveQuotePoint(
|
|
149
|
+
history,
|
|
150
|
+
quoteFixture({
|
|
151
|
+
marketState: "POST",
|
|
152
|
+
postMarketPrice: 131,
|
|
153
|
+
lastUpdated: Date.parse("2026-05-15T21:10:00Z"),
|
|
154
|
+
}),
|
|
155
|
+
{ now: Date.parse("2026-05-15T21:15:00Z") },
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(extended.at(-1)?.close).toBe(131);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("does not bridge a missing intraday history window with one synthetic candle", () => {
|
|
162
|
+
const history: PricePoint[] = [
|
|
163
|
+
{ date: new Date("2026-07-22T14:49:00Z"), close: 347.73 },
|
|
164
|
+
{ date: new Date("2026-07-22T14:50:00Z"), close: 347.76 },
|
|
165
|
+
{ date: new Date("2026-07-22T14:51:00Z"), close: 347.68 },
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const extended = appendLiveQuotePoint(
|
|
169
|
+
history,
|
|
170
|
+
quoteFixture({
|
|
171
|
+
symbol: "GOOG",
|
|
172
|
+
price: 346.27,
|
|
173
|
+
lastUpdated: Date.parse("2026-07-22T18:52:00Z"),
|
|
174
|
+
}),
|
|
175
|
+
Date.parse("2026-07-22T18:53:00Z"),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
expect(extended).toBe(history);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("does not append a live quote tail with a likely unit mismatch", () => {
|
|
182
|
+
const history: PricePoint[] = [
|
|
183
|
+
{ date: new Date("2026-05-18T00:00:00Z"), close: 405 },
|
|
184
|
+
{ date: new Date("2026-05-22T00:00:00Z"), close: 379 },
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
const extended = appendLiveQuotePoint(
|
|
188
|
+
history,
|
|
189
|
+
quoteFixture({
|
|
190
|
+
symbol: "FTC",
|
|
191
|
+
currency: "GBP",
|
|
192
|
+
price: 3.79,
|
|
193
|
+
lastUpdated: Date.parse("2026-05-22T16:39:00Z"),
|
|
194
|
+
}),
|
|
195
|
+
{ now: Date.parse("2026-05-22T16:45:00Z") },
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
expect(extended).toBe(history);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("does not append stale quotes from an older active session", () => {
|
|
202
|
+
const history: PricePoint[] = [
|
|
203
|
+
{ date: new Date("2026-05-11T00:00:00Z"), close: 68 },
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
const extended = appendLiveQuotePoint(
|
|
207
|
+
history,
|
|
208
|
+
quoteFixture({
|
|
209
|
+
lastUpdated: Date.parse("2026-05-08T20:00:00Z"),
|
|
210
|
+
}),
|
|
211
|
+
{ now: Date.parse("2026-05-15T15:00:00Z") },
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
expect(extended).toBe(history);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -44,7 +44,7 @@ describe("chart-resolution", () => {
|
|
|
44
44
|
expect(sortChartResolutions(["1wk", "auto", "15m", "1d"])).toEqual(["auto", "15m", "1d", "1wk"]);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
test("picks
|
|
47
|
+
test("picks the supported resolution whose bar count sits nearest the target", () => {
|
|
48
48
|
const support = [
|
|
49
49
|
{ resolution: "1m", maxRange: "1D" },
|
|
50
50
|
{ resolution: "5m", maxRange: "1W" },
|
|
@@ -53,31 +53,30 @@ describe("chart-resolution", () => {
|
|
|
53
53
|
{ resolution: "1d", maxRange: "5Y" },
|
|
54
54
|
{ resolution: "1wk", maxRange: "ALL" },
|
|
55
55
|
] as const;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
expect(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
expect(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}, support, 100)).toBe("1m");
|
|
56
|
+
const pick = (start: string, end: string, current?: "5m" | "15m") => (
|
|
57
|
+
getBestSupportedResolutionForVisibleWindow(
|
|
58
|
+
{ start: new Date(start), end: new Date(end) },
|
|
59
|
+
support,
|
|
60
|
+
230,
|
|
61
|
+
current,
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(pick("2021-01-01T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("1wk");
|
|
66
|
+
expect(pick("2025-01-01T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("1d");
|
|
67
|
+
// Five months of daily bars beat 1h bars packed three to a pixel.
|
|
68
|
+
expect(pick("2025-08-01T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("1d");
|
|
69
|
+
expect(pick("2025-12-01T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("1h");
|
|
70
|
+
// Two weeks hold ten sessions: 15m bars, not the 780 5m bars of a
|
|
71
|
+
// calendar-day estimate.
|
|
72
|
+
expect(pick("2025-12-18T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("15m");
|
|
73
|
+
expect(pick("2025-12-29T00:00:00Z", "2026-01-01T00:00:00Z")).toBe("5m");
|
|
74
|
+
expect(pick("2026-01-01T00:00:00Z", "2026-01-02T00:00:00Z")).toBe("1m");
|
|
75
|
+
|
|
76
|
+
// A modest zoom keeps the resolution on screen; a large one still switches.
|
|
77
|
+
expect(pick("2025-12-26T09:36:00Z", "2026-01-01T00:00:00Z", "15m")).toBe("15m");
|
|
78
|
+
expect(pick("2025-12-29T00:00:00Z", "2026-01-01T00:00:00Z", "15m")).toBe("5m");
|
|
79
|
+
expect(pick("2025-12-15T00:00:00Z", "2026-01-01T00:00:00Z", "5m")).toBe("15m");
|
|
81
80
|
|
|
82
81
|
expect(CHART_RESOLUTION_STEP_MS["1h"]).toBe(60 * 60_000);
|
|
83
82
|
});
|
|
@@ -256,15 +256,42 @@ export function isRangePresetSupported(
|
|
|
256
256
|
return maxRange !== null && isTimeRangeAtOrBelow(range, maxRange);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
const INTRADAY_TRADING_DAY_RATIO = 5 / 7;
|
|
260
|
+
/** Bars packed tighter than the target cost more than bars spread wider. */
|
|
261
|
+
const DENSE_SCORE_PENALTY = 1.5;
|
|
262
|
+
/** Score margin a candidate must win by before the active resolution changes. */
|
|
263
|
+
const RESOLUTION_SWITCH_HYSTERESIS = 0.35;
|
|
264
|
+
|
|
265
|
+
function estimateVisiblePointCount(resolution: ManualChartResolution, spanMs: number): number {
|
|
266
|
+
const calendarDays = Math.max(spanMs / CHART_RESOLUTION_STEP_MS["1d"], 1 / 24);
|
|
267
|
+
// Intraday bars only exist on trading days, so a multi-day window holds
|
|
268
|
+
// about five sessions a week. Anything up to a day is one session.
|
|
269
|
+
const days = isIntradayResolution(resolution) && calendarDays > 1
|
|
270
|
+
? Math.max(calendarDays * INTRADAY_TRADING_DAY_RATIO, 1)
|
|
271
|
+
: calendarDays;
|
|
272
|
+
return Math.max(days * CHART_RESOLUTION_POINTS_PER_DAY[resolution], 1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function resolutionFitScore(estimatedPointCount: number, targetPointCount: number): number {
|
|
276
|
+
const ratio = estimatedPointCount / targetPointCount;
|
|
277
|
+
return ratio > 1 ? Math.log(ratio) * DENSE_SCORE_PENALTY : -Math.log(ratio);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Picks the supported resolution whose bar count sits closest to the target
|
|
282
|
+
* on a log scale, leaning toward fewer bars. A current resolution is kept
|
|
283
|
+
* until another candidate is clearly better, so a small zoom does not swap
|
|
284
|
+
* the whole data set.
|
|
285
|
+
*/
|
|
259
286
|
export function getBestSupportedResolutionForVisibleWindow(
|
|
260
287
|
window: { start: Date | null; end: Date | null } | null,
|
|
261
288
|
support: readonly ChartResolutionSupport[] | ReadonlyMap<ManualChartResolution, TimeRange>,
|
|
262
289
|
targetPointCount: number,
|
|
290
|
+
currentResolution?: ManualChartResolution | null,
|
|
263
291
|
): ManualChartResolution | null {
|
|
264
292
|
if (!window?.start || !window.end) return null;
|
|
265
293
|
|
|
266
294
|
const spanMs = Math.max(window.end.getTime() - window.start.getTime(), 0);
|
|
267
|
-
const dayCount = Math.max(spanMs / CHART_RESOLUTION_STEP_MS["1d"], 1 / 24);
|
|
268
295
|
const supportedResolutions = sortChartResolutions(
|
|
269
296
|
CHART_RESOLUTION_ORDER
|
|
270
297
|
.filter((resolution): resolution is ManualChartResolution => resolution !== "auto")
|
|
@@ -273,16 +300,20 @@ export function getBestSupportedResolutionForVisibleWindow(
|
|
|
273
300
|
return maxRange !== null && isDateWindowWithinTimeRange(window.start!, window.end!, maxRange);
|
|
274
301
|
}),
|
|
275
302
|
);
|
|
276
|
-
|
|
277
303
|
if (supportedResolutions.length === 0) return null;
|
|
278
304
|
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
305
|
+
const target = Math.max(targetPointCount, 1);
|
|
306
|
+
const scores = new Map(supportedResolutions.map((resolution) => [
|
|
307
|
+
resolution,
|
|
308
|
+
resolutionFitScore(estimateVisiblePointCount(resolution, spanMs), target),
|
|
309
|
+
] as const));
|
|
310
|
+
let best = supportedResolutions[0]!;
|
|
311
|
+
for (const resolution of supportedResolutions) {
|
|
312
|
+
if (scores.get(resolution)! <= scores.get(best)!) best = resolution;
|
|
285
313
|
}
|
|
286
|
-
|
|
287
|
-
|
|
314
|
+
if (currentResolution && scores.has(currentResolution)) {
|
|
315
|
+
const currentScore = scores.get(currentResolution)!;
|
|
316
|
+
if (currentScore <= scores.get(best)! + RESOLUTION_SWITCH_HYSTERESIS) return currentResolution;
|
|
317
|
+
}
|
|
318
|
+
return best;
|
|
288
319
|
}
|
|
@@ -3,7 +3,12 @@ import type { FredSeriesData, FredSeriesLoadResult } from "../data/fred-series";
|
|
|
3
3
|
import { createTestDataProvider } from "../test-support/data-provider";
|
|
4
4
|
import type { TickerFinancials } from "../types/financials";
|
|
5
5
|
import { CHART_SPEC_VERSION, type ChartSpec } from "./types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ChartResolveCache,
|
|
8
|
+
mergePriceHistoryWindows,
|
|
9
|
+
resolveChartSpecData,
|
|
10
|
+
seedChartResolutionResult,
|
|
11
|
+
} from "./resolve";
|
|
7
12
|
import { chartQuoteOverrideKeyForSource } from "./live-quotes";
|
|
8
13
|
import { chartSeriesSourceKey } from "../capabilities";
|
|
9
14
|
import { buildCustomChartPreset } from "../plugins/builtin/chart-composer/presets";
|
|
@@ -26,6 +31,118 @@ const fredLoad = (
|
|
|
26
31
|
});
|
|
27
32
|
|
|
28
33
|
describe("resolveChartSpecData", () => {
|
|
34
|
+
test("seeds study series so their panels survive the wait for real data", async () => {
|
|
35
|
+
const date = new Date("2026-01-05T00:00:00.000Z");
|
|
36
|
+
const seeded = seedChartResolutionResult(
|
|
37
|
+
{
|
|
38
|
+
version: CHART_SPEC_VERSION,
|
|
39
|
+
viewport: { range: "1M", resolution: "auto" },
|
|
40
|
+
panels: [{ id: "main" }, { id: "volume" }],
|
|
41
|
+
series: [{
|
|
42
|
+
id: "price",
|
|
43
|
+
source: {
|
|
44
|
+
kind: "security",
|
|
45
|
+
instrument: { symbol: "TEST", exchange: "NASDAQ" },
|
|
46
|
+
fieldId: "market.ohlcv",
|
|
47
|
+
},
|
|
48
|
+
style: "candles",
|
|
49
|
+
transform: "raw",
|
|
50
|
+
axis: "left",
|
|
51
|
+
panelId: "main",
|
|
52
|
+
interpolation: "none",
|
|
53
|
+
}],
|
|
54
|
+
studies: [{
|
|
55
|
+
id: "volume",
|
|
56
|
+
kind: "volume",
|
|
57
|
+
inputSeriesIds: ["price"],
|
|
58
|
+
parameters: {},
|
|
59
|
+
panelId: "volume",
|
|
60
|
+
axis: "left",
|
|
61
|
+
}],
|
|
62
|
+
},
|
|
63
|
+
new Map([[
|
|
64
|
+
chartQuoteOverrideKeyForSource({
|
|
65
|
+
kind: "security",
|
|
66
|
+
instrument: { symbol: "TEST", exchange: "NASDAQ" },
|
|
67
|
+
fieldId: "market.ohlcv",
|
|
68
|
+
}),
|
|
69
|
+
[{ date, open: 10, high: 12, low: 9, close: 11, volume: 5_000 }],
|
|
70
|
+
]]),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const volume = seeded?.series.find((entry) => entry.panelId === "volume");
|
|
74
|
+
expect(volume?.points.map((point) => point.value)).toEqual([5_000]);
|
|
75
|
+
expect(seeded?.bufferedSeries).toBe(seeded?.series);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("keeps study output over the whole buffered history its base series carries", async () => {
|
|
79
|
+
const history = Array.from({ length: 900 }, (_, day) => {
|
|
80
|
+
const date = new Date(Date.UTC(2024, 0, 1) + day * 86_400_000);
|
|
81
|
+
return date.getUTCDay() === 0 || date.getUTCDay() === 6
|
|
82
|
+
? null
|
|
83
|
+
: { date, open: 100, high: 101, low: 99, close: 100 + (day % 7), volume: 1_000 + day };
|
|
84
|
+
}).filter((point): point is NonNullable<typeof point> => point !== null);
|
|
85
|
+
const provider = createTestDataProvider({
|
|
86
|
+
getTickerFinancials: async () => emptyFinancials(),
|
|
87
|
+
getChartResolutionSupport: () => [{ resolution: "1d", maxRange: "ALL" }],
|
|
88
|
+
getPriceHistoryForResolution: async () => history,
|
|
89
|
+
getDetailedPriceHistory: async () => history,
|
|
90
|
+
});
|
|
91
|
+
const spec: ChartSpec = {
|
|
92
|
+
version: CHART_SPEC_VERSION,
|
|
93
|
+
viewport: { range: "1Y", resolution: "1d" },
|
|
94
|
+
panels: [{ id: "main" }, { id: "volume", label: "Volume", height: 0.24 }],
|
|
95
|
+
series: [{
|
|
96
|
+
id: "price",
|
|
97
|
+
source: {
|
|
98
|
+
kind: "security",
|
|
99
|
+
instrument: { symbol: "TEST", exchange: "NASDAQ" },
|
|
100
|
+
fieldId: "market.close",
|
|
101
|
+
},
|
|
102
|
+
style: "line",
|
|
103
|
+
transform: "raw",
|
|
104
|
+
axis: "left",
|
|
105
|
+
panelId: "main",
|
|
106
|
+
interpolation: "none",
|
|
107
|
+
}],
|
|
108
|
+
studies: [{
|
|
109
|
+
id: "volume",
|
|
110
|
+
kind: "volume",
|
|
111
|
+
inputSeriesIds: ["price"],
|
|
112
|
+
parameters: {},
|
|
113
|
+
panelId: "volume",
|
|
114
|
+
axis: "left",
|
|
115
|
+
}],
|
|
116
|
+
};
|
|
117
|
+
const sources = {
|
|
118
|
+
dataProvider: provider,
|
|
119
|
+
now: new Date("2026-06-15T00:00:00.000Z"),
|
|
120
|
+
loadFredSeries: async () => fredLoad(),
|
|
121
|
+
};
|
|
122
|
+
const cache = new ChartResolveCache();
|
|
123
|
+
|
|
124
|
+
await resolveChartSpecData(spec, sources, cache);
|
|
125
|
+
// Panning back accumulates older bars into the buffer. The study has to
|
|
126
|
+
// follow, or it empties out wherever the base series has already been.
|
|
127
|
+
const panned = await resolveChartSpecData(spec, sources, cache, {
|
|
128
|
+
requestViewport: {
|
|
129
|
+
start: new Date("2024-02-01T00:00:00.000Z"),
|
|
130
|
+
end: new Date("2024-08-01T00:00:00.000Z"),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const span = (id: string) => {
|
|
135
|
+
const entry = panned.bufferedSeries?.find((candidate) => candidate.id === id);
|
|
136
|
+
return entry && entry.points.length > 0
|
|
137
|
+
? {
|
|
138
|
+
first: entry.points[0]!.date.toISOString(),
|
|
139
|
+
last: entry.points.at(-1)!.date.toISOString(),
|
|
140
|
+
}
|
|
141
|
+
: null;
|
|
142
|
+
};
|
|
143
|
+
expect(span("volume")).toEqual(span("price"));
|
|
144
|
+
});
|
|
145
|
+
|
|
29
146
|
test("routes futures and Treasury aliases through the existing market and FRED pipelines", async () => {
|
|
30
147
|
const marketRequests: string[] = [];
|
|
31
148
|
const fredRequests: string[] = [];
|
|
@@ -98,6 +98,8 @@ export interface ChartResolveOptions {
|
|
|
98
98
|
requestViewport?: { start: Date; end: Date } | null;
|
|
99
99
|
/** Approximate number of horizontal observations the current surface can use. */
|
|
100
100
|
targetPointCount?: number;
|
|
101
|
+
/** Resolution currently on screen, kept through small Auto zooms. */
|
|
102
|
+
currentResolution?: ManualChartResolution | null;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
/** Raw source data retained while live quotes recompute the chart tail. */
|
|
@@ -290,6 +292,7 @@ function requestResolution(
|
|
|
290
292
|
{ start: new Date(runtimeBounds.start), end: new Date(runtimeBounds.end) },
|
|
291
293
|
sharedSupport,
|
|
292
294
|
options.targetPointCount ?? 120,
|
|
295
|
+
options.currentResolution,
|
|
293
296
|
)
|
|
294
297
|
: null;
|
|
295
298
|
const preferred = adaptive
|
|
@@ -381,13 +384,22 @@ export function seedChartResolutionResult(
|
|
|
381
384
|
if (resolved?.points.length) series.push(resolved);
|
|
382
385
|
});
|
|
383
386
|
if (series.length === 0) return null;
|
|
387
|
+
// Studies ride along with the seed. Without them the chart briefly drops to
|
|
388
|
+
// base series alone whenever it falls back here, and every panel a study
|
|
389
|
+
// owns loses its rows until the first real resolve lands.
|
|
390
|
+
const seeded = spec.studies.length > 0
|
|
391
|
+
? [...series, ...resolveStudies(series, spec.studies).series]
|
|
392
|
+
: series;
|
|
384
393
|
return {
|
|
385
|
-
series,
|
|
386
|
-
legendSeries:
|
|
387
|
-
bufferedSeries:
|
|
394
|
+
series: seeded,
|
|
395
|
+
legendSeries: seeded,
|
|
396
|
+
bufferedSeries: seeded,
|
|
388
397
|
loading: false,
|
|
389
398
|
errors: [],
|
|
390
399
|
warnings: [],
|
|
400
|
+
resolution: spec.viewport.resolution === "auto"
|
|
401
|
+
? getPresetResolution(spec.viewport.range)
|
|
402
|
+
: spec.viewport.resolution,
|
|
391
403
|
};
|
|
392
404
|
}
|
|
393
405
|
|
|
@@ -799,13 +811,6 @@ function prepareBaseSeriesForStudies(
|
|
|
799
811
|
);
|
|
800
812
|
}
|
|
801
813
|
|
|
802
|
-
function rawCalculationSeries(series: ResolvedSeries, bounds: DateBounds): ResolvedSeries {
|
|
803
|
-
if (bounds.start !== null && bounds.end !== null) {
|
|
804
|
-
return clipSeriesToWindow(series, new Date(bounds.start), new Date(bounds.end));
|
|
805
|
-
}
|
|
806
|
-
return { ...series, points: filterPoints(series.points, bounds) };
|
|
807
|
-
}
|
|
808
|
-
|
|
809
814
|
function scalarBaseline(series: ResolvedSeries, bounds: DateBounds): number | null {
|
|
810
815
|
const points = filterPoints(series.points, bounds);
|
|
811
816
|
for (const point of points) {
|
|
@@ -1142,15 +1147,14 @@ export async function resolveChartSpecData(
|
|
|
1142
1147
|
? requestVisibleBounds
|
|
1143
1148
|
: followLatestMarketObservation(initialVisibleBounds, rawSeries);
|
|
1144
1149
|
const resolution = initialResolution;
|
|
1145
|
-
const studyBounds = bounds.end !== null
|
|
1146
|
-
&& initialCalculationBounds.end !== null
|
|
1147
|
-
&& bounds.end > initialCalculationBounds.end
|
|
1148
|
-
? { ...initialCalculationBounds, end: bounds.end }
|
|
1149
|
-
: initialCalculationBounds;
|
|
1150
1150
|
const baseSeries = rawSeries
|
|
1151
1151
|
.filter((entry) => visibleSeriesIds.has(entry.id))
|
|
1152
1152
|
.map((entry) => prepareBaseSeriesForStudies(entry, bounds, false, requestVisibleBounds));
|
|
1153
|
-
|
|
1153
|
+
// Studies run over the same loaded history their base series carries. Clipping
|
|
1154
|
+
// them to the requested window instead left a study with no observations
|
|
1155
|
+
// wherever the accumulated buffer had already been panned past, so a study's
|
|
1156
|
+
// panel emptied out mid-pan and only refilled once the next fetch landed.
|
|
1157
|
+
const calculationSeries = rawSeries;
|
|
1154
1158
|
let resolved = baseSeries;
|
|
1155
1159
|
|
|
1156
1160
|
// Study outputs are appended by the pure engine before the final viewport clip.
|
|
@@ -1224,5 +1228,6 @@ export async function resolveChartSpecData(
|
|
|
1224
1228
|
errors,
|
|
1225
1229
|
warnings: [...new Set([...priorityWarnings, ...warnings])],
|
|
1226
1230
|
viewport,
|
|
1231
|
+
resolution,
|
|
1227
1232
|
};
|
|
1228
1233
|
}
|
package/src/time-series/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChartResolution, TimeRange } from "./range";
|
|
2
|
-
import type { ChartResolutionSupport } from "./resolution";
|
|
2
|
+
import type { ChartResolutionSupport, ManualChartResolution } from "./resolution";
|
|
3
3
|
import type { InstrumentRef } from "../market-data/request-types";
|
|
4
4
|
|
|
5
5
|
export const CHART_SPEC_VERSION = 2 as const;
|
|
@@ -175,4 +175,6 @@ export interface ChartResolutionResult {
|
|
|
175
175
|
warnings: string[];
|
|
176
176
|
/** Effective inclusive bounds used to clip the resolved chart data. */
|
|
177
177
|
viewport?: { start: Date; end: Date };
|
|
178
|
+
/** Bar resolution the loaded market history was fetched at. */
|
|
179
|
+
resolution?: ManualChartResolution;
|
|
178
180
|
}
|
|
@@ -35,6 +35,7 @@ export interface UseChartResolutionOptions {
|
|
|
35
35
|
autoViewport?: ChartResolveOptions["autoViewport"];
|
|
36
36
|
requestViewport?: ChartResolveOptions["requestViewport"];
|
|
37
37
|
targetPointCount?: number;
|
|
38
|
+
currentResolution?: ChartResolveOptions["currentResolution"];
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const DEFAULT_QUOTE_POLL_INTERVAL_MS = 60_000;
|
|
@@ -146,10 +147,14 @@ export function useChartResolution(
|
|
|
146
147
|
? { start: new Date(requestViewportStart), end: new Date(requestViewportEnd) }
|
|
147
148
|
: null;
|
|
148
149
|
const adaptiveTargetPointCount = validAutoViewport ? options.targetPointCount : undefined;
|
|
150
|
+
// Read at resolve time only: hysteresis breaks ties when the viewport moves,
|
|
151
|
+
// and echoing the chosen resolution back must not trigger another resolve.
|
|
152
|
+
const currentResolution = validAutoViewport ? options.currentResolution ?? null : null;
|
|
149
153
|
const resolveOptions: ChartResolveOptions = {
|
|
150
154
|
autoViewport: validAutoViewport,
|
|
151
155
|
requestViewport: validRequestViewport,
|
|
152
156
|
targetPointCount: adaptiveTargetPointCount,
|
|
157
|
+
currentResolution,
|
|
153
158
|
};
|
|
154
159
|
const latestRequestRef = useRef({ spec, sources, options: resolveOptions });
|
|
155
160
|
latestRequestRef.current = { spec, sources, options: resolveOptions };
|