gloomberb 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -11
- package/package.json +12 -5
- package/src/app.tsx +414 -191
- package/src/architecture/import-boundaries.test.ts +118 -0
- package/src/assets/gloomberb-logo.png +0 -0
- package/src/assets/gloomberb-logo.svg +73 -0
- package/src/brokers/account-cache.ts +157 -0
- package/src/brokers/profile-form.test.ts +73 -0
- package/src/brokers/profile-form.ts +109 -0
- package/src/brokers/remote-broker-adapter.ts +91 -0
- package/src/brokers/sync-broker-instance.test.ts +124 -1
- package/src/brokers/sync-broker-instance.ts +113 -17
- package/src/capabilities/factories.ts +100 -0
- package/src/capabilities/index.ts +4 -0
- package/src/capabilities/registry.test.ts +137 -0
- package/src/capabilities/registry.ts +131 -0
- package/src/capabilities/service-capabilities.ts +27 -0
- package/src/capabilities/types.ts +84 -0
- package/src/cli/commands/ticker.ts +11 -3
- package/src/cli/context.ts +19 -13
- package/src/cli/helpers.ts +1 -0
- package/src/cli/types.ts +2 -2
- package/src/components/chart/chart-controller.ts +4 -24
- package/src/components/chart/chart-data.ts +3 -14
- package/src/components/chart/chart-indicator-selector.tsx +28 -6
- package/src/components/chart/chart-pane-settings.ts +15 -28
- package/src/components/chart/chart-renderer.ts +74 -99
- package/src/components/chart/chart-resolution.ts +10 -10
- package/src/components/chart/chart-scroll.test.ts +6 -5
- package/src/components/chart/chart-scroll.ts +2 -2
- package/src/components/chart/chart-types.ts +0 -1
- package/src/components/chart/chart-viewport.ts +2 -1
- package/src/components/chart/chart.test.ts +37 -0
- package/src/components/chart/comparison-chart-data.ts +3 -17
- package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
- package/src/components/chart/comparison-chart-renderer.ts +1 -1
- package/src/components/chart/comparison-stock-chart.tsx +304 -108
- package/src/components/chart/cursor-motion.ts +3 -2
- package/src/components/chart/indicators/bands.test.ts +0 -14
- package/src/components/chart/indicators/moving-averages.test.ts +0 -15
- package/src/components/chart/indicators/options.ts +3 -3
- package/src/components/chart/indicators/oscillators.test.ts +0 -21
- package/src/components/chart/indicators/types.ts +1 -2
- package/src/components/chart/native/chart-rasterizer.ts +37 -44
- package/src/components/chart/native/kitty-adapter.ts +2 -5
- package/src/components/chart/native/kitty-manager.ts +1 -1
- package/src/components/chart/native/kitty-protocol.ts +0 -1
- package/src/components/chart/native/kitty-support.ts +1 -1
- package/src/components/chart/native/kitty.test.ts +30 -13
- package/src/components/chart/native/renderer-selection.ts +1 -1
- package/src/components/chart/native/surface-manager.ts +1 -1
- package/src/components/chart/static-chart-surface.test.tsx +51 -0
- package/src/components/chart/static-chart-surface.tsx +191 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
- package/src/components/chart/stock-chart.tsx +438 -141
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +269 -20
- package/src/components/command-bar/command-bar.tsx +830 -353
- package/src/components/command-bar/command-registry.ts +10 -20
- package/src/components/command-bar/helpers.test.ts +1 -1
- package/src/components/command-bar/helpers.ts +9 -11
- package/src/components/command-bar/pane-template-display.ts +4 -8
- package/src/components/command-bar/root-shortcuts.ts +12 -11
- package/src/components/command-bar/view-model.test.ts +8 -42
- package/src/components/command-bar/view-model.ts +8 -36
- package/src/components/command-bar/workflow-ops.test.ts +76 -2
- package/src/components/command-bar/workflow-ops.ts +40 -9
- package/src/components/command-bar/workflow-types.ts +2 -1
- package/src/components/data-table-stack-view.test.tsx +206 -0
- package/src/components/data-table-stack-view.tsx +152 -0
- package/src/components/data-table-view.test.tsx +188 -0
- package/src/components/data-table-view.tsx +279 -0
- package/src/components/feed-data-table-stack-view.test.tsx +225 -0
- package/src/components/feed-data-table-stack-view.tsx +365 -0
- package/src/components/index.ts +26 -31
- package/src/components/layout/detached-pane-shell.tsx +185 -0
- package/src/components/layout/floating-pane.tsx +87 -14
- package/src/components/layout/header.tsx +139 -49
- package/src/components/layout/pane-content.tsx +40 -0
- package/src/components/layout/pane-footer.test.tsx +195 -0
- package/src/components/layout/pane-footer.tsx +410 -0
- package/src/components/layout/pane-header.test.tsx +39 -0
- package/src/components/layout/pane-header.tsx +192 -14
- package/src/components/layout/pane-sizing.ts +9 -10
- package/src/components/layout/pane-title.ts +35 -0
- package/src/components/layout/pane.tsx +52 -11
- package/src/components/layout/shell.test.tsx +315 -107
- package/src/components/layout/shell.tsx +1010 -368
- package/src/components/layout/status-bar.test.tsx +17 -10
- package/src/components/layout/status-bar.tsx +367 -100
- package/src/components/layout/titlebar-overlay.ts +2 -0
- package/src/components/markdown-editor.tsx +29 -14
- package/src/components/markdown-text.tsx +25 -24
- package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
- package/src/components/onboarding/onboarding-wizard.tsx +347 -473
- package/src/components/pane-settings-dialog.tsx +521 -89
- package/src/components/pane-template-wizard.test.tsx +1 -1
- package/src/components/pane-template-wizard.tsx +38 -37
- package/src/components/price-selector-dialog.tsx +22 -21
- package/src/components/ticker-badge-list.tsx +53 -0
- package/src/components/ticker-badge-text.test.tsx +44 -1
- package/src/components/ticker-badge-text.tsx +78 -38
- package/src/components/ticker-badge.tsx +28 -7
- package/src/components/ticker-list-table-view.tsx +248 -0
- package/src/components/ticker-list-table.test.tsx +47 -1
- package/src/components/ticker-list-table.tsx +212 -41
- package/src/components/toggle-list.tsx +114 -8
- package/src/components/ui/button.tsx +24 -16
- package/src/components/ui/choice-dialog.tsx +89 -0
- package/src/components/ui/data-table.tsx +313 -114
- package/src/components/ui/external-link.tsx +52 -13
- package/src/components/ui/fields.tsx +44 -30
- package/src/components/ui/frame.tsx +24 -57
- package/src/components/ui/index.ts +15 -50
- package/src/components/ui/list-view.tsx +65 -25
- package/src/components/ui/loading.tsx +5 -60
- package/src/components/ui/message-composer.test.tsx +81 -0
- package/src/components/ui/message-composer.tsx +139 -0
- package/src/components/ui/multi-select-dialog.tsx +100 -32
- package/src/components/ui/multi-select.ts +112 -0
- package/src/components/ui/native-select.tsx +120 -0
- package/src/components/ui/page-stack-view.tsx +49 -19
- package/src/components/ui/remote-image.tsx +75 -0
- package/src/components/ui/row-value-cache.test.ts +17 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/shortcut-hint.tsx +55 -0
- package/src/components/ui/status.tsx +12 -67
- package/src/components/ui/table-layout.ts +59 -0
- package/src/components/ui/tabs.tsx +249 -27
- package/src/components/ui/toggle.tsx +19 -127
- package/src/components/ui/ui.test.tsx +266 -53
- package/src/components/use-double-click-activation.ts +7 -2
- package/src/components/use-external-link-footer.ts +79 -0
- package/src/core/app-services.ts +95 -0
- package/src/core/state/app-state.ts +886 -0
- package/src/{state → core/state}/session-persistence.ts +7 -6
- package/src/data/config-layout.ts +225 -0
- package/src/data/config-store-node.ts +390 -0
- package/src/data/config-store.test.ts +72 -0
- package/src/data/config-store.ts +35 -478
- package/src/data/sqlite-json.ts +2 -5
- package/src/data/sqlite-retry.ts +1 -0
- package/src/index.tsx +2 -68
- package/src/market-data/coordinator-subscriptions.test.ts +125 -0
- package/src/market-data/coordinator.test.ts +73 -4
- package/src/market-data/coordinator.ts +190 -24
- package/src/market-data/hooks.test.tsx +1 -1
- package/src/market-data/hooks.tsx +109 -87
- package/src/market-data/query-store.ts +3 -3
- package/src/market-data/request-types.test.ts +0 -14
- package/src/market-data/result-types.ts +1 -0
- package/src/news/aggregator.test.ts +267 -0
- package/src/news/aggregator.ts +452 -0
- package/src/news/hooks.ts +39 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +77 -0
- package/src/notifications/app-notifier.ts +21 -10
- package/src/pane-settings.test.ts +0 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
- package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +41 -5
- package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
- package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
- package/src/plugins/builtin/ai/settings.ts +0 -1
- package/src/plugins/builtin/ai/utils.ts +1 -71
- package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
- package/src/plugins/builtin/alerts/index.test.tsx +61 -82
- package/src/plugins/builtin/alerts/index.tsx +263 -155
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.test.tsx +13 -8
- package/src/plugins/builtin/analytics/index.tsx +78 -120
- package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
- package/src/plugins/builtin/ask-ai.test.tsx +55 -54
- package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
- package/src/plugins/builtin/broker-manager/index.tsx +759 -0
- package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
- package/src/plugins/builtin/broker-manager/model.ts +142 -0
- package/src/plugins/builtin/chat-controller.test.ts +129 -0
- package/src/plugins/builtin/chat-controller.ts +85 -14
- package/src/plugins/builtin/chat.test.tsx +283 -36
- package/src/plugins/builtin/chat.tsx +363 -170
- package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
- package/src/plugins/builtin/cloud-tweets.tsx +896 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
- package/src/plugins/builtin/comparison-chart.tsx +11 -11
- package/src/plugins/builtin/correlation/compute.test.ts +0 -17
- package/src/plugins/builtin/correlation/index.tsx +51 -46
- package/src/plugins/builtin/correlation/settings.ts +1 -2
- package/src/plugins/builtin/debug.tsx +99 -70
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
- package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
- package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
- package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
- package/src/plugins/builtin/earnings/index.tsx +187 -94
- package/src/plugins/builtin/econ/calendar-source.ts +9 -116
- package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
- package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
- package/src/plugins/builtin/econ/index.tsx +236 -421
- package/src/plugins/builtin/econ/types.ts +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
- package/src/plugins/builtin/help.test.tsx +120 -60
- package/src/plugins/builtin/help.tsx +266 -82
- package/src/plugins/builtin/holders/index.test.ts +45 -0
- package/src/plugins/builtin/holders/index.tsx +949 -0
- package/src/plugins/builtin/insider/index.tsx +207 -253
- package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
- package/src/plugins/builtin/insider/insider-data.ts +1 -0
- package/src/plugins/builtin/layout-manager.tsx +12 -40
- package/src/plugins/builtin/market-movers/index.tsx +70 -144
- package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
- package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
- package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
- package/src/plugins/builtin/news-wire/categories.ts +114 -0
- package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
- package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
- package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
- package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
- package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
- package/src/plugins/builtin/news-wire/hash.ts +9 -0
- package/src/plugins/builtin/news-wire/index.tsx +92 -0
- package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
- package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
- package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
- package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
- package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
- package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
- package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
- package/src/plugins/builtin/news-wire/read-state.ts +76 -0
- package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
- package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
- package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
- package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
- package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
- package/src/plugins/builtin/news.tsx +98 -25
- package/src/plugins/builtin/notes-files.ts +51 -10
- package/src/plugins/builtin/notes.tsx +332 -131
- package/src/plugins/builtin/options.test.tsx +185 -0
- package/src/plugins/builtin/options.tsx +346 -206
- package/src/plugins/builtin/plugin-groups.ts +92 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
- package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
- package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
- package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
- package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
- package/src/plugins/builtin/portfolio-list.test.ts +60 -1
- package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
- package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
- package/src/plugins/builtin/research/index.tsx +724 -0
- package/src/plugins/builtin/sec.tsx +67 -19
- package/src/plugins/builtin/sectors/index.tsx +42 -80
- package/src/plugins/builtin/sectors/sector-data.ts +1 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
- package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
- package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
- package/src/plugins/builtin/ticker-surface.ts +68 -0
- package/src/plugins/builtin/world-indices/index.tsx +44 -82
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/builtin/yahoo.ts +55 -1
- package/src/plugins/builtin/yield-curve/index.tsx +87 -145
- package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
- package/src/plugins/catalog-ui.test.ts +24 -0
- package/src/plugins/catalog-ui.ts +44 -0
- package/src/plugins/catalog.ts +2 -50
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/account-cache.test.ts +15 -14
- package/src/plugins/ibkr/account-cache.ts +15 -120
- package/src/plugins/ibkr/broker-adapter.ts +67 -0
- package/src/plugins/ibkr/config.test.ts +0 -18
- package/src/plugins/ibkr/dialogs.tsx +14 -56
- package/src/plugins/ibkr/flex.test.ts +57 -2
- package/src/plugins/ibkr/flex.ts +116 -12
- package/src/plugins/ibkr/gateway-helpers.ts +8 -3
- package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
- package/src/plugins/ibkr/gateway-service.test.ts +47 -9
- package/src/plugins/ibkr/gateway-service.ts +264 -2235
- package/src/plugins/ibkr/gateway-types.ts +27 -0
- package/src/plugins/ibkr/index.test.tsx +3 -11
- package/src/plugins/ibkr/index.tsx +1 -0
- package/src/plugins/ibkr/price-normalization.test.ts +1 -6
- package/src/plugins/ibkr/price-normalization.ts +1 -0
- package/src/plugins/ibkr/trade-badge.tsx +6 -5
- package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
- package/src/plugins/ibkr/trade-tab.tsx +164 -117
- package/src/plugins/ibkr/trading-pane.tsx +117 -83
- package/src/plugins/pane-manager.test.ts +3 -1
- package/src/plugins/pane-manager.ts +93 -19
- package/src/plugins/plugin-runtime.test.tsx +176 -74
- package/src/plugins/plugin-runtime.tsx +228 -10
- package/src/plugins/prediction-markets/cache.ts +49 -0
- package/src/plugins/prediction-markets/categories.ts +0 -10
- package/src/plugins/prediction-markets/chart.test.tsx +1 -1
- package/src/plugins/prediction-markets/chart.tsx +21 -20
- package/src/plugins/prediction-markets/cli.ts +1 -0
- package/src/plugins/prediction-markets/controller-data.ts +135 -89
- package/src/plugins/prediction-markets/controller.ts +37 -108
- package/src/plugins/prediction-markets/detail/book.tsx +23 -22
- package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
- package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
- package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
- package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
- package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
- package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/keyboard.ts +1 -0
- package/src/plugins/prediction-markets/launch.test.ts +1 -1
- package/src/plugins/prediction-markets/launch.ts +1 -1
- package/src/plugins/prediction-markets/metrics.ts +25 -12
- package/src/plugins/prediction-markets/pane.test.tsx +16 -7
- package/src/plugins/prediction-markets/pane.tsx +180 -82
- package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
- package/src/plugins/prediction-markets/services/fetch.ts +27 -10
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/settings.ts +0 -5
- package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
- package/src/plugins/registry.test.ts +307 -0
- package/src/plugins/registry.ts +293 -82
- package/src/react/input.ts +53 -0
- package/src/react/plugins/plugin-slot.tsx +21 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
- package/src/renderers/electrobun/bun/application-menu.ts +118 -0
- package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
- package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
- package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
- package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
- package/src/renderers/electrobun/bun/index.ts +1149 -0
- package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
- package/src/renderers/electrobun/bun/window-frame.ts +46 -0
- package/src/renderers/electrobun/shared/protocol.ts +72 -0
- package/src/renderers/electrobun/view/ai-host.ts +93 -0
- package/src/renderers/electrobun/view/app-services.ts +348 -0
- package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
- package/src/renderers/electrobun/view/config-host.ts +35 -0
- package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
- package/src/renderers/electrobun/view/context-menu.ts +164 -0
- package/src/renderers/electrobun/view/data-table.tsx +773 -0
- package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
- package/src/renderers/electrobun/view/http-fetch.ts +113 -0
- package/src/renderers/electrobun/view/input-host.tsx +65 -0
- package/src/renderers/electrobun/view/key-event.test.ts +34 -0
- package/src/renderers/electrobun/view/key-event.ts +134 -0
- package/src/renderers/electrobun/view/main.tsx +104 -0
- package/src/renderers/electrobun/view/native-renderer.ts +110 -0
- package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
- package/src/renderers/electrobun/view/notes-files.ts +56 -0
- package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
- package/src/renderers/electrobun/view/resource-store.ts +106 -0
- package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
- package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
- package/src/renderers/electrobun/view/toast-host.tsx +65 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
- package/src/renderers/electrobun/view/update-host.ts +38 -0
- package/src/renderers/opentui/dialog-host.test.tsx +44 -0
- package/src/renderers/opentui/dialog-host.tsx +56 -0
- package/src/renderers/opentui/host.tsx +134 -0
- package/src/renderers/opentui/input-host.tsx +24 -0
- package/src/renderers/opentui/start.tsx +71 -0
- package/src/renderers/opentui/test-utils.tsx +76 -0
- package/src/renderers/opentui/toast-host.tsx +10 -0
- package/src/renderers/opentui/ui-host.tsx +97 -0
- package/src/sources/gloomberb-cloud.test.ts +288 -17
- package/src/sources/gloomberb-cloud.ts +176 -9
- package/src/sources/provider-errors.ts +1 -0
- package/src/sources/provider-router.test.ts +220 -81
- package/src/sources/provider-router.ts +294 -111
- package/src/sources/sec-edgar.ts +19 -6
- package/src/sources/yahoo-finance.test.ts +0 -5
- package/src/sources/yahoo-finance.ts +420 -11
- package/src/state/app-activity.ts +7 -7
- package/src/state/app-bootstrap.ts +114 -40
- package/src/state/app-context-selector.test.tsx +70 -1
- package/src/state/app-context.test.ts +71 -3
- package/src/state/app-context.test.tsx +14 -7
- package/src/state/app-context.tsx +205 -869
- package/src/state/config-save-scheduler.ts +35 -0
- package/src/state/persist-scheduler.test.ts +67 -0
- package/src/state/persist-scheduler.ts +69 -0
- package/src/state/selectors-ui.ts +55 -0
- package/src/state/selectors.ts +15 -71
- package/src/state/session-persistence.test.ts +1 -1
- package/src/state/ticker-refresh-queue.ts +52 -3
- package/src/state/use-inline-tickers.ts +18 -16
- package/src/state/use-quote-streaming.test.ts +0 -6
- package/src/state/use-quote-streaming.test.tsx +1 -1
- package/src/state/use-quote-streaming.ts +3 -2
- package/src/state/use-session-snapshot.ts +32 -17
- package/src/test-support/plugin-runtime.ts +149 -0
- package/src/theme/color-utils.ts +1 -0
- package/src/theme/colors.ts +16 -0
- package/src/types/broker.ts +16 -1
- package/src/types/capability-route-source.ts +23 -0
- package/src/types/config.test.ts +23 -0
- package/src/types/config.ts +145 -10
- package/src/types/context-menu.ts +95 -0
- package/src/types/data-provider.ts +34 -3
- package/src/types/desktop-menu.ts +13 -0
- package/src/types/desktop-window.ts +29 -0
- package/src/types/financials.ts +123 -2
- package/src/types/instrument.ts +1 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +1 -0
- package/src/types/plugin.ts +32 -8
- package/src/ui/ascii-font.ts +85 -0
- package/src/ui/context-menu.test.ts +94 -0
- package/src/ui/context-menu.tsx +433 -0
- package/src/ui/dialog.tsx +55 -0
- package/src/ui/host.tsx +361 -0
- package/src/ui/index.tsx +126 -0
- package/src/ui/toast.tsx +40 -0
- package/src/updater.test.ts +69 -0
- package/src/updater.ts +61 -18
- package/src/utils/api-client.test.ts +99 -9
- package/src/utils/api-client.ts +378 -18
- package/src/utils/back-navigation.ts +6 -0
- package/src/utils/cli-output.ts +1 -0
- package/src/utils/currency-units.test.ts +1 -6
- package/src/utils/currency-units.ts +1 -0
- package/src/utils/debug-log.ts +68 -0
- package/src/utils/dom-screenshot.ts +326 -0
- package/src/utils/exchange-rate-map.ts +1 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/format.test.ts +7 -0
- package/src/utils/format.ts +1 -0
- package/src/utils/fuzzy-search.ts +1 -0
- package/src/utils/html-entities.test.ts +8 -0
- package/src/utils/html-entities.ts +32 -0
- package/src/utils/http-transport.ts +11 -0
- package/src/utils/main-thread-monitor.ts +127 -0
- package/src/utils/perf-marks.ts +54 -0
- package/src/utils/quote-freshness.ts +27 -50
- package/src/utils/quote-resolution.test.ts +5 -5
- package/src/utils/quote-resolution.ts +0 -1
- package/src/utils/quote-time.ts +1 -5
- package/src/utils/sec.test.ts +0 -12
- package/src/utils/selection-clipboard.ts +14 -6
- package/src/utils/shortcut-labels.test.ts +20 -0
- package/src/utils/shortcut-labels.ts +67 -0
- package/src/utils/slugify.ts +1 -0
- package/src/utils/terminal-input-reset.ts +1 -0
- package/src/utils/text-wrap.ts +82 -0
- package/src/utils/throttled-fetch.test.ts +19 -0
- package/src/utils/throttled-fetch.ts +11 -9
- package/src/utils/ticker-list.test.ts +1 -7
- package/src/utils/ticker-list.ts +1 -0
- package/src/utils/timestamp.ts +29 -0
- package/src/utils/tweet-text.test.ts +13 -0
- package/src/utils/tweet-text.ts +18 -0
- package/src/utils/url.ts +11 -0
- package/src/version.ts +1 -1
- package/src/components/chart/chart-control-hint.tsx +0 -39
- package/src/components/detail-feed-view.test.tsx +0 -180
- package/src/components/detail-feed-view.tsx +0 -531
- package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
- package/src/components/layout/pixel-mouse.test.ts +0 -40
- package/src/components/spinner.tsx +0 -2
- package/src/components/tab-bar.tsx +0 -17
- package/src/components/ui/multi-select-chips.tsx +0 -69
- package/src/plugins/builtin/ai/index.test.tsx +0 -139
- package/src/plugins/builtin/ask-ai.tsx +0 -9
- package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
- package/src/plugins/builtin/correlation/settings.test.ts +0 -34
- package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
- package/src/plugins/builtin/econ/fred-client.ts +0 -79
- package/src/plugins/builtin/fred-settings.ts +0 -10
- package/src/plugins/builtin/options-availability.test.tsx +0 -179
- package/src/plugins/builtin/options-availability.ts +0 -158
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
- package/src/plugins/prediction-markets/status.ts +0 -24
- package/src/plugins/prediction-markets/table.tsx +0 -87
- package/src/utils/quote-time.test.ts +0 -22
- package/src/utils/selection-clipboard.test.ts +0 -43
- package/src/utils/terminal-input-reset.test.ts +0 -50
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CachedResourceRecord, ResourceStore } from "../data/resource-store";
|
|
2
2
|
import type { PluginRegistry } from "../plugins/registry";
|
|
3
|
+
import type { AssetDataCapability, NewsCapability } from "../capabilities";
|
|
3
4
|
import type { BrokerAdapter } from "../types/broker";
|
|
4
5
|
import type { AppConfig } from "../types/config";
|
|
5
6
|
import { createDefaultConfig } from "../types/config";
|
|
@@ -7,12 +8,14 @@ import type {
|
|
|
7
8
|
CachedFinancialsTarget,
|
|
8
9
|
DataProvider,
|
|
9
10
|
MarketDataRequestContext,
|
|
10
|
-
NewsItem,
|
|
11
11
|
QuoteSubscriptionTarget,
|
|
12
12
|
SearchRequestContext,
|
|
13
13
|
SecFilingItem,
|
|
14
14
|
} from "../types/data-provider";
|
|
15
|
-
import type {
|
|
15
|
+
import type { CapabilityRouteSource } from "../types/capability-route-source";
|
|
16
|
+
import { routeSourcePriority } from "../types/capability-route-source";
|
|
17
|
+
import type { AnalystResearchData, CorporateActionsData, HolderData, OptionsChain, PricePoint, Quote, TickerFinancials } from "../types/financials";
|
|
18
|
+
import type { NewsArticle, NewsQuery } from "../news/types";
|
|
16
19
|
import type { BrokerContractRef, InstrumentSearchResult } from "../types/instrument";
|
|
17
20
|
import type { CachePolicy, CachePolicyMap } from "../types/persistence";
|
|
18
21
|
import type { TimeRange } from "../components/chart/chart-types";
|
|
@@ -24,6 +27,7 @@ import {
|
|
|
24
27
|
} from "../components/chart/chart-resolution";
|
|
25
28
|
import { hasLikelyQuoteUnitMismatch } from "../utils/currency-units";
|
|
26
29
|
import { debugLog } from "../utils/debug-log";
|
|
30
|
+
import { canonicalExchange } from "../utils/exchanges";
|
|
27
31
|
import { normalizePriceHistory, normalizeTickerFinancialsPriceHistory } from "../utils/price-history";
|
|
28
32
|
import { isQuoteStaleForCurrentSession } from "../utils/quote-freshness";
|
|
29
33
|
import {
|
|
@@ -34,7 +38,7 @@ import {
|
|
|
34
38
|
} from "../utils/quote-resolution";
|
|
35
39
|
import { isProviderMiss } from "./provider-errors";
|
|
36
40
|
|
|
37
|
-
const providerLog = debugLog.createLogger("
|
|
41
|
+
const providerLog = debugLog.createLogger("asset-data-router");
|
|
38
42
|
|
|
39
43
|
const BROKER_ATTEMPT_TIMEOUT = 10_000;
|
|
40
44
|
const EXPECTED_PROVIDER_MISS = /No data found|symbol may be delisted|"code":"Not Found"|No history for /i;
|
|
@@ -47,6 +51,9 @@ const DEFAULT_CACHE_POLICIES: Record<string, CachePolicy> = {
|
|
|
47
51
|
priceHistoryIntraday: { staleMs: 5 * 60_000, expireMs: 2 * 24 * 60 * 60_000 },
|
|
48
52
|
priceHistoryDaily: { staleMs: 24 * 60 * 60_000, expireMs: 30 * 24 * 60 * 60_000 },
|
|
49
53
|
news: { staleMs: 15 * 60_000, expireMs: 2 * 24 * 60 * 60_000 },
|
|
54
|
+
holders: { staleMs: 24 * 60 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
|
|
55
|
+
analystResearch: { staleMs: 24 * 60 * 60_000, expireMs: 7 * 24 * 60 * 60_000 },
|
|
56
|
+
corporateActions: { staleMs: 24 * 60 * 60_000, expireMs: 14 * 24 * 60 * 60_000 },
|
|
50
57
|
secFilings: { staleMs: 15 * 60_000, expireMs: 2 * 24 * 60 * 60_000 },
|
|
51
58
|
secFilingContent: { staleMs: 30 * 24 * 60 * 60_000, expireMs: 365 * 24 * 60 * 60_000 },
|
|
52
59
|
articleSummary: { staleMs: 30 * 24 * 60 * 60_000, expireMs: 90 * 24 * 60 * 60_000 },
|
|
@@ -74,10 +81,6 @@ function normalizeTicker(ticker: string): string {
|
|
|
74
81
|
return ticker.trim().toUpperCase();
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
function normalizeExchange(exchange?: string): string {
|
|
78
|
-
return (exchange ?? "").trim().toUpperCase();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
84
|
function sanitizeCachedFinancials(financials: TickerFinancials): TickerFinancials {
|
|
82
85
|
if (!isQuoteStaleForCurrentSession(financials.quote)) return financials;
|
|
83
86
|
return {
|
|
@@ -143,6 +146,20 @@ function hasMeaningfulProfile(data: TickerFinancials | null | undefined): boolea
|
|
|
143
146
|
);
|
|
144
147
|
}
|
|
145
148
|
|
|
149
|
+
function hasAnalystResearchValue(data: AnalystResearchData): boolean {
|
|
150
|
+
return !!data.priceTarget
|
|
151
|
+
|| data.recommendations.length > 0
|
|
152
|
+
|| data.ratings.length > 0
|
|
153
|
+
|| data.earningsEstimates.length > 0
|
|
154
|
+
|| data.revenueEstimates.length > 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hasCorporateActionsValue(data: CorporateActionsData): boolean {
|
|
158
|
+
return data.dividends.length > 0
|
|
159
|
+
|| data.splits.length > 0
|
|
160
|
+
|| data.earnings.length > 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
146
163
|
function hasLikelyPriceUnitMismatch(primary: TickerFinancials, fallback: TickerFinancials): boolean {
|
|
147
164
|
return hasLikelyQuoteUnitMismatch(primary.quote, fallback.quote);
|
|
148
165
|
}
|
|
@@ -229,20 +246,63 @@ interface SourceResult<T> {
|
|
|
229
246
|
value: T;
|
|
230
247
|
}
|
|
231
248
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
type RouteSourceCapability = AssetDataCapability | NewsCapability;
|
|
250
|
+
|
|
251
|
+
function routeSourceFromMarketProvider(provider: DataProvider): CapabilityRouteSource {
|
|
252
|
+
return {
|
|
253
|
+
id: provider.id,
|
|
254
|
+
name: provider.name,
|
|
255
|
+
priority: provider.priority,
|
|
256
|
+
cachePolicy: provider.cachePolicy,
|
|
257
|
+
market: provider,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function normalizeRouteSource(source: CapabilityRouteSource | DataProvider): CapabilityRouteSource {
|
|
262
|
+
return "market" in source || "news" in source
|
|
263
|
+
? source as CapabilityRouteSource
|
|
264
|
+
: routeSourceFromMarketProvider(source as DataProvider);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function capabilityRouteSourceId(capability: RouteSourceCapability): string {
|
|
268
|
+
return capability.sourceId ?? capability.id;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function mergeCapabilityRouteSource(current: CapabilityRouteSource | undefined, capability: RouteSourceCapability): CapabilityRouteSource {
|
|
272
|
+
const priority = Math.min(
|
|
273
|
+
current?.priority ?? Number.MAX_SAFE_INTEGER,
|
|
274
|
+
capability.priority ?? 1000,
|
|
275
|
+
);
|
|
276
|
+
return {
|
|
277
|
+
id: capabilityRouteSourceId(capability),
|
|
278
|
+
name: current?.name ?? capability.name,
|
|
279
|
+
priority,
|
|
280
|
+
cachePolicy: capability.cachePolicy ?? current?.cachePolicy,
|
|
281
|
+
isEnabled: capability.isEnabled ?? current?.isEnabled,
|
|
282
|
+
market: capability.kind === "asset-data" ? capability.provider : current?.market,
|
|
283
|
+
news: capability.kind === "news" ? capability.provider : current?.news,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export class AssetDataRouter implements DataProvider {
|
|
288
|
+
readonly id = "asset-data-router";
|
|
289
|
+
readonly name = "Asset Data Router";
|
|
235
290
|
readonly priority = Number.MAX_SAFE_INTEGER;
|
|
236
291
|
|
|
237
292
|
private registry: PluginRegistry | null = null;
|
|
238
293
|
private readonly revalidationInFlight = new Map<string, Promise<unknown>>();
|
|
239
294
|
private getConfigFn: () => AppConfig = () => createDefaultConfig("");
|
|
295
|
+
private readonly fallbackSource: CapabilityRouteSource | null;
|
|
296
|
+
private readonly extraSources: CapabilityRouteSource[];
|
|
240
297
|
|
|
241
298
|
constructor(
|
|
242
|
-
|
|
243
|
-
|
|
299
|
+
fallbackSource: CapabilityRouteSource | DataProvider | null = null,
|
|
300
|
+
extraSources: Array<CapabilityRouteSource | DataProvider> = [],
|
|
244
301
|
private readonly resources?: ResourceStore,
|
|
245
|
-
) {
|
|
302
|
+
) {
|
|
303
|
+
this.fallbackSource = fallbackSource ? normalizeRouteSource(fallbackSource) : null;
|
|
304
|
+
this.extraSources = extraSources.map(normalizeRouteSource);
|
|
305
|
+
}
|
|
246
306
|
|
|
247
307
|
attachRegistry(registry: PluginRegistry): void {
|
|
248
308
|
this.registry = registry;
|
|
@@ -417,36 +477,88 @@ export class ProviderRouter implements DataProvider {
|
|
|
417
477
|
return results;
|
|
418
478
|
}
|
|
419
479
|
|
|
420
|
-
async getNews(
|
|
480
|
+
async getNews(query: NewsQuery): Promise<NewsArticle[]> {
|
|
481
|
+
const sources = this.newsSourcesInPriorityOrder()
|
|
482
|
+
.filter((source) => source.news?.supports?.(query) ?? true);
|
|
483
|
+
const feed = query.feed ?? (query.scope === "ticker" ? "ticker" : "latest");
|
|
484
|
+
if (feed === "ticker") {
|
|
485
|
+
let firstEmpty: NewsArticle[] | null = null;
|
|
486
|
+
for (const source of sources) {
|
|
487
|
+
try {
|
|
488
|
+
const articles = await source.news!.fetchNews(query);
|
|
489
|
+
if (articles.length > 0) return articles;
|
|
490
|
+
firstEmpty ??= articles;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
if (shouldLogProviderError(error)) {
|
|
493
|
+
providerLog.error(`${source.id} failed: ${error}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return firstEmpty ?? [];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const settled = await Promise.allSettled(sources.map((source) => source.news!.fetchNews(query)));
|
|
501
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? result.value : []);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async getHolders(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<HolderData> {
|
|
421
505
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
422
|
-
const variantKeys =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (cached) {
|
|
429
|
-
this.scheduleRevalidation(this.makeRevalidationKey("news", ticker, exchange, context, count), async () => {
|
|
430
|
-
await this.revalidateNews(ticker, count, exchange, context);
|
|
506
|
+
const variantKeys = this.getTickerVariantCandidates(exchange);
|
|
507
|
+
const cached = this.selectCachedResource<HolderData>("holders", entityKey, variantKeys, this.getProviderSourceKeys(), false);
|
|
508
|
+
const forceRefresh = context?.cacheMode === "refresh";
|
|
509
|
+
if (cached && !forceRefresh) {
|
|
510
|
+
this.scheduleRevalidation(this.makeRevalidationKey("holders", ticker, exchange, context), async () => {
|
|
511
|
+
await this.revalidateHolders(ticker, exchange, context);
|
|
431
512
|
});
|
|
432
513
|
return cached.value;
|
|
433
514
|
}
|
|
434
515
|
|
|
435
|
-
const result = await this.
|
|
436
|
-
if (
|
|
437
|
-
|
|
516
|
+
const result = await this.fetchProviderHolders(ticker, exchange, context);
|
|
517
|
+
if (result) return result.value;
|
|
518
|
+
if (cached) return cached.value;
|
|
519
|
+
throw new Error(`No holder data provider available for ${ticker}`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async getAnalystResearch(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<AnalystResearchData> {
|
|
523
|
+
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
524
|
+
const variantKeys = this.getTickerVariantCandidates(exchange);
|
|
525
|
+
const cached = this.selectCachedResource<AnalystResearchData>("analystResearch", entityKey, variantKeys, this.getProviderSourceKeys(), false);
|
|
526
|
+
const forceRefresh = context?.cacheMode === "refresh";
|
|
527
|
+
if (cached && !forceRefresh) {
|
|
528
|
+
this.scheduleRevalidation(this.makeRevalidationKey("analystResearch", ticker, exchange, context), async () => {
|
|
529
|
+
await this.revalidateAnalystResearch(ticker, exchange, context);
|
|
530
|
+
});
|
|
531
|
+
return cached.value;
|
|
438
532
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
533
|
+
|
|
534
|
+
const result = await this.fetchProviderAnalystResearch(ticker, exchange, context);
|
|
535
|
+
if (result) return result.value;
|
|
536
|
+
if (cached) return cached.value;
|
|
537
|
+
throw new Error(`No analyst research provider available for ${ticker}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async getCorporateActions(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<CorporateActionsData> {
|
|
541
|
+
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
542
|
+
const variantKeys = this.getTickerVariantCandidates(exchange);
|
|
543
|
+
const cached = this.selectCachedResource<CorporateActionsData>("corporateActions", entityKey, variantKeys, this.getProviderSourceKeys(), false);
|
|
544
|
+
const forceRefresh = context?.cacheMode === "refresh";
|
|
545
|
+
if (cached && !forceRefresh) {
|
|
546
|
+
this.scheduleRevalidation(this.makeRevalidationKey("corporateActions", ticker, exchange, context), async () => {
|
|
547
|
+
await this.revalidateCorporateActions(ticker, exchange, context);
|
|
548
|
+
});
|
|
549
|
+
return cached.value;
|
|
442
550
|
}
|
|
443
|
-
|
|
551
|
+
|
|
552
|
+
const result = await this.fetchProviderCorporateActions(ticker, exchange, context);
|
|
553
|
+
if (result) return result.value;
|
|
554
|
+
if (cached) return cached.value;
|
|
555
|
+
throw new Error(`No corporate actions provider available for ${ticker}`);
|
|
444
556
|
}
|
|
445
557
|
|
|
446
558
|
async getSecFilings(ticker: string, count = 15, exchange?: string, context?: MarketDataRequestContext): Promise<SecFilingItem[]> {
|
|
447
559
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
448
560
|
const variantKeys = [
|
|
449
|
-
buildVariantKey([["exchange",
|
|
561
|
+
buildVariantKey([["exchange", canonicalExchange(exchange)], ["count", count]]),
|
|
450
562
|
buildVariantKey([["count", count]]),
|
|
451
563
|
"",
|
|
452
564
|
];
|
|
@@ -506,7 +618,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
506
618
|
async getPriceHistory(ticker: string, exchange: string, range: TimeRange, context?: MarketDataRequestContext): Promise<PricePoint[]> {
|
|
507
619
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
508
620
|
const variantKeys = [
|
|
509
|
-
buildVariantKey([["exchange",
|
|
621
|
+
buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", range]]),
|
|
510
622
|
buildVariantKey([["range", range]]),
|
|
511
623
|
];
|
|
512
624
|
const sourceKeys = [
|
|
@@ -543,7 +655,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
543
655
|
): Promise<PricePoint[]> {
|
|
544
656
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
545
657
|
const variantKeys = [
|
|
546
|
-
buildVariantKey([["exchange",
|
|
658
|
+
buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", bufferRange], ["resolution", resolution]]),
|
|
547
659
|
buildVariantKey([["range", bufferRange], ["resolution", resolution]]),
|
|
548
660
|
];
|
|
549
661
|
const sourceKeys = [
|
|
@@ -603,7 +715,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
603
715
|
): Promise<PricePoint[]> {
|
|
604
716
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
605
717
|
const variantKeys = [
|
|
606
|
-
buildVariantKey([["exchange",
|
|
718
|
+
buildVariantKey([["exchange", canonicalExchange(exchange)], ["start", compactDate(startDate)], ["end", compactDate(endDate)], ["bar", barSize]]),
|
|
607
719
|
buildVariantKey([["start", compactDate(startDate)], ["end", compactDate(endDate)], ["bar", barSize]]),
|
|
608
720
|
];
|
|
609
721
|
const sourceKeys = [
|
|
@@ -630,7 +742,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
630
742
|
async getOptionsChain(ticker: string, exchange?: string, expirationDate?: number, context?: MarketDataRequestContext): Promise<OptionsChain> {
|
|
631
743
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
632
744
|
const variantKeys = [
|
|
633
|
-
buildVariantKey([["exchange",
|
|
745
|
+
buildVariantKey([["exchange", canonicalExchange(exchange)], ["expiration", expirationDate ?? "default"]]),
|
|
634
746
|
buildVariantKey([["expiration", expirationDate ?? "default"]]),
|
|
635
747
|
"",
|
|
636
748
|
];
|
|
@@ -682,7 +794,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
682
794
|
}
|
|
683
795
|
|
|
684
796
|
private getTickerVariantCandidates(exchange?: string): string[] {
|
|
685
|
-
const normalizedExchange =
|
|
797
|
+
const normalizedExchange = canonicalExchange(exchange);
|
|
686
798
|
return [
|
|
687
799
|
buildVariantKey([["exchange", normalizedExchange]]),
|
|
688
800
|
"",
|
|
@@ -701,10 +813,6 @@ export class ProviderRouter implements DataProvider {
|
|
|
701
813
|
return this.providersInPriorityOrder().map((provider) => this.providerSourceKey(provider));
|
|
702
814
|
}
|
|
703
815
|
|
|
704
|
-
private firstAvailableProvider(): DataProvider | null {
|
|
705
|
-
return this.providersInPriorityOrder()[0] ?? null;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
816
|
private resolvePolicy(overrides: CachePolicyMap | undefined, key: keyof typeof DEFAULT_CACHE_POLICIES): CachePolicy {
|
|
709
817
|
return overrides?.[key] ?? DEFAULT_CACHE_POLICIES[key]!;
|
|
710
818
|
}
|
|
@@ -928,27 +1036,48 @@ export class ProviderRouter implements DataProvider {
|
|
|
928
1036
|
}));
|
|
929
1037
|
}
|
|
930
1038
|
|
|
931
|
-
private
|
|
932
|
-
const
|
|
1039
|
+
private sortedSources(): CapabilityRouteSource[] {
|
|
1040
|
+
const sources = [...this.extraSources];
|
|
933
1041
|
if (this.registry) {
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1042
|
+
const capabilitySources = new Map<string, CapabilityRouteSource>();
|
|
1043
|
+
for (const capability of this.registry.getEnabledCapabilities("asset-data") as AssetDataCapability[]) {
|
|
1044
|
+
const sourceId = capabilityRouteSourceId(capability);
|
|
1045
|
+
capabilitySources.set(sourceId, mergeCapabilityRouteSource(capabilitySources.get(sourceId), capability));
|
|
1046
|
+
}
|
|
1047
|
+
for (const capability of this.registry.getEnabledCapabilities("news") as NewsCapability[]) {
|
|
1048
|
+
const sourceId = capabilityRouteSourceId(capability);
|
|
1049
|
+
capabilitySources.set(sourceId, mergeCapabilityRouteSource(capabilitySources.get(sourceId), capability));
|
|
1050
|
+
}
|
|
1051
|
+
sources.push(...capabilitySources.values());
|
|
938
1052
|
}
|
|
939
|
-
return
|
|
940
|
-
.filter((
|
|
941
|
-
.sort((a, b) => (a
|
|
1053
|
+
return sources
|
|
1054
|
+
.filter((source) => source.id !== this.id && source.id !== this.fallbackSource?.id)
|
|
1055
|
+
.sort((a, b) => routeSourcePriority(a) - routeSourcePriority(b));
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
private sortedProviders(): DataProvider[] {
|
|
1059
|
+
return this.sortedSources()
|
|
1060
|
+
.map((source) => source.market)
|
|
1061
|
+
.filter((provider): provider is DataProvider => !!provider);
|
|
942
1062
|
}
|
|
943
1063
|
|
|
944
1064
|
private providersInPriorityOrder(): DataProvider[] {
|
|
945
1065
|
const providers = [...this.sortedProviders()];
|
|
946
|
-
|
|
947
|
-
|
|
1066
|
+
const fallbackProvider = this.fallbackSource?.market ?? null;
|
|
1067
|
+
if (fallbackProvider && !providers.some((provider) => provider.id === fallbackProvider.id)) {
|
|
1068
|
+
providers.push(fallbackProvider);
|
|
948
1069
|
}
|
|
949
1070
|
return providers;
|
|
950
1071
|
}
|
|
951
1072
|
|
|
1073
|
+
private newsSourcesInPriorityOrder(): CapabilityRouteSource[] {
|
|
1074
|
+
const sources = this.sortedSources().filter((source) => !!source.news);
|
|
1075
|
+
if (this.fallbackSource?.news && !sources.some((source) => source.id === this.fallbackSource?.id)) {
|
|
1076
|
+
sources.push(this.fallbackSource);
|
|
1077
|
+
}
|
|
1078
|
+
return sources;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
952
1081
|
private async firstProvider<T>(fn: (provider: DataProvider) => Promise<T | null | undefined>): Promise<SourceResult<T> | null> {
|
|
953
1082
|
for (const provider of this.providersInPriorityOrder()) {
|
|
954
1083
|
try {
|
|
@@ -1090,7 +1219,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1090
1219
|
context?: MarketDataRequestContext,
|
|
1091
1220
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1092
1221
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1093
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1222
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", range]]);
|
|
1094
1223
|
for (const candidate of this.getBrokerCandidatesForContext(context, false)) {
|
|
1095
1224
|
if (!candidate.broker.getPriceHistory) continue;
|
|
1096
1225
|
try {
|
|
@@ -1125,7 +1254,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1125
1254
|
context?: MarketDataRequestContext,
|
|
1126
1255
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1127
1256
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1128
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1257
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", bufferRange], ["resolution", resolution]]);
|
|
1129
1258
|
for (const candidate of this.getBrokerCandidatesForContext(context, false)) {
|
|
1130
1259
|
if (!candidate.broker.getPriceHistoryForResolution) continue;
|
|
1131
1260
|
try {
|
|
@@ -1160,7 +1289,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1160
1289
|
context?: MarketDataRequestContext,
|
|
1161
1290
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1162
1291
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1163
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1292
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", range]]);
|
|
1164
1293
|
let firstEmptyResult: SourceResult<PricePoint[]> | null = null;
|
|
1165
1294
|
|
|
1166
1295
|
for (const provider of this.providersInPriorityOrder()) {
|
|
@@ -1197,7 +1326,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1197
1326
|
context?: MarketDataRequestContext,
|
|
1198
1327
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1199
1328
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1200
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1329
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["range", bufferRange], ["resolution", resolution]]);
|
|
1201
1330
|
let firstEmptyResult: SourceResult<PricePoint[]> | null = null;
|
|
1202
1331
|
|
|
1203
1332
|
for (const provider of this.providersInPriorityOrder()) {
|
|
@@ -1293,7 +1422,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1293
1422
|
context?: MarketDataRequestContext,
|
|
1294
1423
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1295
1424
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1296
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1425
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["start", compactDate(startDate)], ["end", compactDate(endDate)], ["bar", barSize]]);
|
|
1297
1426
|
for (const candidate of this.getBrokerCandidatesForContext(context, false)) {
|
|
1298
1427
|
if (!candidate.broker.getDetailedPriceHistory) continue;
|
|
1299
1428
|
try {
|
|
@@ -1331,7 +1460,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1331
1460
|
context?: MarketDataRequestContext,
|
|
1332
1461
|
): Promise<SourceResult<PricePoint[]> | null> {
|
|
1333
1462
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1334
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1463
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["start", compactDate(startDate)], ["end", compactDate(endDate)], ["bar", barSize]]);
|
|
1335
1464
|
let firstEmptyResult: SourceResult<PricePoint[]> | null = null;
|
|
1336
1465
|
|
|
1337
1466
|
for (const provider of this.providersInPriorityOrder()) {
|
|
@@ -1368,7 +1497,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1368
1497
|
context?: MarketDataRequestContext,
|
|
1369
1498
|
): Promise<SourceResult<OptionsChain> | null> {
|
|
1370
1499
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1371
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1500
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["expiration", expirationDate ?? "default"]]);
|
|
1372
1501
|
for (const candidate of this.getBrokerCandidatesForContext(context, false)) {
|
|
1373
1502
|
if (!candidate.broker.getOptionsChain) continue;
|
|
1374
1503
|
try {
|
|
@@ -1402,7 +1531,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1402
1531
|
context?: MarketDataRequestContext,
|
|
1403
1532
|
): Promise<SourceResult<OptionsChain> | null> {
|
|
1404
1533
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1405
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1534
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["expiration", expirationDate ?? "default"]]);
|
|
1406
1535
|
const result = await this.firstProvider(async (provider) => {
|
|
1407
1536
|
if (!provider.getOptionsChain) return null;
|
|
1408
1537
|
return provider.getOptionsChain(ticker, exchange, expirationDate, context);
|
|
@@ -1415,6 +1544,99 @@ export class ProviderRouter implements DataProvider {
|
|
|
1415
1544
|
return result;
|
|
1416
1545
|
}
|
|
1417
1546
|
|
|
1547
|
+
private async fetchProviderHolders(
|
|
1548
|
+
ticker: string,
|
|
1549
|
+
exchange?: string,
|
|
1550
|
+
context?: MarketDataRequestContext,
|
|
1551
|
+
): Promise<SourceResult<HolderData> | null> {
|
|
1552
|
+
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1553
|
+
const variantKey = this.getTickerVariantCandidates(exchange)[0] ?? "";
|
|
1554
|
+
let firstEmptyResult: SourceResult<HolderData> | null = null;
|
|
1555
|
+
let lastError: unknown = null;
|
|
1556
|
+
|
|
1557
|
+
for (const provider of this.providersInPriorityOrder()) {
|
|
1558
|
+
if (!provider.getHolders) continue;
|
|
1559
|
+
try {
|
|
1560
|
+
const value = await provider.getHolders(ticker, exchange, context);
|
|
1561
|
+
const sourceKey = this.providerSourceKey(provider);
|
|
1562
|
+
this.cacheResource("holders", entityKey, variantKey, sourceKey, value, this.resolveProviderPolicy("holders", provider));
|
|
1563
|
+
if (value.holders.length > 0) return { sourceKey, value };
|
|
1564
|
+
firstEmptyResult ??= { sourceKey, value };
|
|
1565
|
+
} catch (error) {
|
|
1566
|
+
lastError = error;
|
|
1567
|
+
if (shouldLogProviderError(error)) {
|
|
1568
|
+
providerLog.error(`${provider.id} failed: ${error}`);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (firstEmptyResult) return firstEmptyResult;
|
|
1574
|
+
if (lastError) throw lastError;
|
|
1575
|
+
return null;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
private async fetchProviderAnalystResearch(
|
|
1579
|
+
ticker: string,
|
|
1580
|
+
exchange?: string,
|
|
1581
|
+
context?: MarketDataRequestContext,
|
|
1582
|
+
): Promise<SourceResult<AnalystResearchData> | null> {
|
|
1583
|
+
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1584
|
+
const variantKey = this.getTickerVariantCandidates(exchange)[0] ?? "";
|
|
1585
|
+
let firstEmptyResult: SourceResult<AnalystResearchData> | null = null;
|
|
1586
|
+
let lastError: unknown = null;
|
|
1587
|
+
|
|
1588
|
+
for (const provider of this.providersInPriorityOrder()) {
|
|
1589
|
+
if (!provider.getAnalystResearch) continue;
|
|
1590
|
+
try {
|
|
1591
|
+
const value = await provider.getAnalystResearch(ticker, exchange, context);
|
|
1592
|
+
const sourceKey = this.providerSourceKey(provider);
|
|
1593
|
+
this.cacheResource("analystResearch", entityKey, variantKey, sourceKey, value, this.resolveProviderPolicy("analystResearch", provider));
|
|
1594
|
+
if (hasAnalystResearchValue(value)) return { sourceKey, value };
|
|
1595
|
+
firstEmptyResult ??= { sourceKey, value };
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
lastError = error;
|
|
1598
|
+
if (shouldLogProviderError(error)) {
|
|
1599
|
+
providerLog.error(`${provider.id} failed: ${error}`);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
if (firstEmptyResult) return firstEmptyResult;
|
|
1605
|
+
if (lastError) throw lastError;
|
|
1606
|
+
return null;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
private async fetchProviderCorporateActions(
|
|
1610
|
+
ticker: string,
|
|
1611
|
+
exchange?: string,
|
|
1612
|
+
context?: MarketDataRequestContext,
|
|
1613
|
+
): Promise<SourceResult<CorporateActionsData> | null> {
|
|
1614
|
+
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1615
|
+
const variantKey = this.getTickerVariantCandidates(exchange)[0] ?? "";
|
|
1616
|
+
let firstEmptyResult: SourceResult<CorporateActionsData> | null = null;
|
|
1617
|
+
let lastError: unknown = null;
|
|
1618
|
+
|
|
1619
|
+
for (const provider of this.providersInPriorityOrder()) {
|
|
1620
|
+
if (!provider.getCorporateActions) continue;
|
|
1621
|
+
try {
|
|
1622
|
+
const value = await provider.getCorporateActions(ticker, exchange, context);
|
|
1623
|
+
const sourceKey = this.providerSourceKey(provider);
|
|
1624
|
+
this.cacheResource("corporateActions", entityKey, variantKey, sourceKey, value, this.resolveProviderPolicy("corporateActions", provider));
|
|
1625
|
+
if (hasCorporateActionsValue(value)) return { sourceKey, value };
|
|
1626
|
+
firstEmptyResult ??= { sourceKey, value };
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
lastError = error;
|
|
1629
|
+
if (shouldLogProviderError(error)) {
|
|
1630
|
+
providerLog.error(`${provider.id} failed: ${error}`);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
if (firstEmptyResult) return firstEmptyResult;
|
|
1636
|
+
if (lastError) throw lastError;
|
|
1637
|
+
return null;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1418
1640
|
private async fetchProviderSecFilings(
|
|
1419
1641
|
ticker: string,
|
|
1420
1642
|
count = 15,
|
|
@@ -1422,7 +1644,7 @@ export class ProviderRouter implements DataProvider {
|
|
|
1422
1644
|
context?: MarketDataRequestContext,
|
|
1423
1645
|
): Promise<SourceResult<SecFilingItem[]> | null> {
|
|
1424
1646
|
const entityKey = this.getEntityKey(ticker, exchange, context?.instrument);
|
|
1425
|
-
const variantKey = buildVariantKey([["exchange",
|
|
1647
|
+
const variantKey = buildVariantKey([["exchange", canonicalExchange(exchange)], ["count", count]]);
|
|
1426
1648
|
let lastError: unknown = null;
|
|
1427
1649
|
|
|
1428
1650
|
for (const provider of this.providersInPriorityOrder()) {
|
|
@@ -1474,21 +1696,6 @@ export class ProviderRouter implements DataProvider {
|
|
|
1474
1696
|
return null;
|
|
1475
1697
|
}
|
|
1476
1698
|
|
|
1477
|
-
private async revalidateFinancials(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1478
|
-
const brokerResult = await withBrokerTimeout(this.fetchBrokerFinancials(ticker, exchange, context));
|
|
1479
|
-
const needsProvider = !brokerResult || !hasMeaningfulFundamentals(brokerResult.value) || !hasMeaningfulProfile(brokerResult.value);
|
|
1480
|
-
if (needsProvider) {
|
|
1481
|
-
await this.fetchProviderFinancials(ticker, exchange, context);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
private async revalidateQuote(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1486
|
-
const brokerQuote = await withBrokerTimeout(this.fetchBrokerQuote(ticker, exchange, context));
|
|
1487
|
-
if (!brokerQuote) {
|
|
1488
|
-
await this.fetchProviderQuote(ticker, exchange, context);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
1699
|
private async revalidateExchangeRate(fromCurrency: string): Promise<void> {
|
|
1493
1700
|
const result = await this.firstProvider(async (provider) => {
|
|
1494
1701
|
const rate = await provider.getExchangeRate(fromCurrency);
|
|
@@ -1498,25 +1705,22 @@ export class ProviderRouter implements DataProvider {
|
|
|
1498
1705
|
this.cacheExchangeRate(fromCurrency, result.value.rate, result.value.provider);
|
|
1499
1706
|
}
|
|
1500
1707
|
|
|
1501
|
-
private async revalidateNews(ticker: string, count = 15, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1502
|
-
const result = await this.firstProvider((provider) => provider.getNews(ticker, count, exchange, context));
|
|
1503
|
-
if (!result) return;
|
|
1504
|
-
const provider = this.resolveProviderBySourceKey(result.sourceKey);
|
|
1505
|
-
if (!provider) return;
|
|
1506
|
-
this.cacheResource(
|
|
1507
|
-
"news",
|
|
1508
|
-
this.getEntityKey(ticker, exchange, context?.instrument),
|
|
1509
|
-
buildVariantKey([["exchange", normalizeExchange(exchange)], ["count", count]]),
|
|
1510
|
-
result.sourceKey,
|
|
1511
|
-
result.value,
|
|
1512
|
-
this.resolveProviderPolicy("news", provider),
|
|
1513
|
-
);
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
1708
|
private async revalidateSecFilings(ticker: string, count = 15, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1517
1709
|
await this.fetchProviderSecFilings(ticker, count, exchange, context);
|
|
1518
1710
|
}
|
|
1519
1711
|
|
|
1712
|
+
private async revalidateHolders(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1713
|
+
await this.fetchProviderHolders(ticker, exchange, context);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
private async revalidateAnalystResearch(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1717
|
+
await this.fetchProviderAnalystResearch(ticker, exchange, context);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
private async revalidateCorporateActions(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<void> {
|
|
1721
|
+
await this.fetchProviderCorporateActions(ticker, exchange, context);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1520
1724
|
private async revalidateSecFilingContent(filing: SecFilingItem): Promise<void> {
|
|
1521
1725
|
await this.fetchProviderSecFilingContent(filing);
|
|
1522
1726
|
}
|
|
@@ -1530,27 +1734,6 @@ export class ProviderRouter implements DataProvider {
|
|
|
1530
1734
|
}
|
|
1531
1735
|
}
|
|
1532
1736
|
|
|
1533
|
-
private async revalidatePriceHistory(ticker: string, exchange: string, range: TimeRange, context?: MarketDataRequestContext): Promise<void> {
|
|
1534
|
-
const brokerHistory = await withBrokerTimeout(this.fetchBrokerPriceHistory(ticker, exchange, range, context));
|
|
1535
|
-
if (!brokerHistory || brokerHistory.value.length === 0) {
|
|
1536
|
-
await this.fetchProviderPriceHistory(ticker, exchange, range, context);
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
private async revalidateDetailedPriceHistory(
|
|
1541
|
-
ticker: string,
|
|
1542
|
-
exchange: string,
|
|
1543
|
-
startDate: Date,
|
|
1544
|
-
endDate: Date,
|
|
1545
|
-
barSize: string,
|
|
1546
|
-
context?: MarketDataRequestContext,
|
|
1547
|
-
): Promise<void> {
|
|
1548
|
-
const brokerHistory = await withBrokerTimeout(this.fetchBrokerDetailedPriceHistory(ticker, exchange, startDate, endDate, barSize, context));
|
|
1549
|
-
if (!brokerHistory || brokerHistory.value.length === 0) {
|
|
1550
|
-
await this.fetchProviderDetailedPriceHistory(ticker, exchange, startDate, endDate, barSize, context);
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
1737
|
private async revalidateOptionsChain(
|
|
1555
1738
|
ticker: string,
|
|
1556
1739
|
exchange?: string,
|