@gloom-sh/terminal 0.18.5 → 0.19.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useMemo, useRef } from "react";
|
|
2
|
-
import { QUICK_ADD_KEY } from "./quick-add";
|
|
3
2
|
import {
|
|
4
3
|
TickerListTableView,
|
|
5
4
|
type DataTableKeyEvent,
|
|
@@ -111,6 +110,7 @@ export function PortfolioTickerTable({
|
|
|
111
110
|
onCursorChange,
|
|
112
111
|
financialsMap,
|
|
113
112
|
columnContext,
|
|
113
|
+
futuresFrontNotes,
|
|
114
114
|
flashSymbols,
|
|
115
115
|
sinceLastSeen,
|
|
116
116
|
onRootKeyDown,
|
|
@@ -119,7 +119,7 @@ export function PortfolioTickerTable({
|
|
|
119
119
|
visibleRangeBuffer,
|
|
120
120
|
resetScrollKey,
|
|
121
121
|
onRowActivate,
|
|
122
|
-
|
|
122
|
+
addTickerShortcut,
|
|
123
123
|
onOpenPerpetual,
|
|
124
124
|
rootHeight,
|
|
125
125
|
}: {
|
|
@@ -134,6 +134,7 @@ export function PortfolioTickerTable({
|
|
|
134
134
|
onCursorChange?: (ticker: TickerRecord, index: number) => void;
|
|
135
135
|
financialsMap: Map<string, TickerFinancials>;
|
|
136
136
|
columnContext: ColumnContext;
|
|
137
|
+
futuresFrontNotes?: ReadonlyMap<string, string>;
|
|
137
138
|
flashSymbols: Map<string, QuoteFlashDirection>;
|
|
138
139
|
/** For a few seconds after coming back to the pane, LAST is tinted by each row's move since the user left. */
|
|
139
140
|
sinceLastSeen?: SinceLastSeenRecap | null;
|
|
@@ -144,8 +145,8 @@ export function PortfolioTickerTable({
|
|
|
144
145
|
visibleRangeBuffer?: number;
|
|
145
146
|
resetScrollKey?: unknown;
|
|
146
147
|
onRowActivate?: (ticker: TickerRecord) => void;
|
|
147
|
-
/**
|
|
148
|
-
|
|
148
|
+
/** The key of the add row, which the empty table names. The pane shows the row itself, once. */
|
|
149
|
+
addTickerShortcut?: string;
|
|
149
150
|
/** Opens PERP on a row's stock perpetual: its PERP% and FUND 8H cells link to it. */
|
|
150
151
|
onOpenPerpetual?: (marketId: string) => void;
|
|
151
152
|
rootHeight?: number;
|
|
@@ -155,21 +156,26 @@ export function PortfolioTickerTable({
|
|
|
155
156
|
openPerpetualRef.current = onOpenPerpetual;
|
|
156
157
|
const sparklineVisible = columns.some((column) => column.id === PRICE_SPARKLINE_COLUMN_ID);
|
|
157
158
|
const tableFinancialsMap = useLiveSparklineFinancials(financialsMap, sortedTickers, sparklineVisible);
|
|
159
|
+
const noteColumnId = columns.some((column) => column.id === "name") ? "name" : "ticker";
|
|
160
|
+
const hasFrontReference = useMemo(() => sortedTickers.some((ticker) => columnContext.futuresTerms?.get(ticker.metadata.ticker)?.front),
|
|
161
|
+
[columnContext.futuresTerms, sortedTickers]);
|
|
158
162
|
const resolveCell = useCallback(
|
|
159
163
|
(column: ColumnConfig, ticker: TickerRecord, financials: TickerFinancials | undefined) => {
|
|
160
164
|
const key = `${ticker.metadata.ticker}:${column.id}`;
|
|
161
165
|
const version = buildCellVersion(column, ticker, financials, columnContext);
|
|
162
|
-
const
|
|
166
|
+
const cached = cellCacheRef.current.get(key, version, () => {
|
|
163
167
|
const cell = getColumnValue(column, ticker, financials, columnContext);
|
|
164
168
|
const marketId = PERPETUAL_COLUMN_IDS.has(column.id) ? columnContext.perpetuals?.get(ticker.metadata.ticker)?.marketId : undefined;
|
|
165
169
|
return marketId ? { ...cell, onPress: () => openPerpetualRef.current?.(marketId) } : cell;
|
|
166
170
|
});
|
|
171
|
+
const note = column.id === noteColumnId ? futuresFrontNotes?.get(ticker.metadata.ticker) : undefined;
|
|
172
|
+
const cell = note ? { ...cached, note } : cached;
|
|
167
173
|
// LAST keeps its price: a move since the user last looked only tints the cell.
|
|
168
174
|
if (column.id !== "price" || !sinceLastSeen) return cell;
|
|
169
175
|
const move = sinceLastSeen.moves.get(ticker.metadata.ticker);
|
|
170
176
|
return move == null ? cell : { ...cell, backgroundColor: sinceLastSeenTint(move, sinceLastSeen.fading) };
|
|
171
177
|
},
|
|
172
|
-
[columnContext, sinceLastSeen],
|
|
178
|
+
[columnContext, futuresFrontNotes, noteColumnId, sinceLastSeen],
|
|
173
179
|
);
|
|
174
180
|
const pnlColumn = columns.find((column) => column.id === "pnl" || column.id === "pnl_pct");
|
|
175
181
|
// Reads the same cached P&L cells the rows show; only changed rows recompute.
|
|
@@ -202,7 +208,8 @@ export function PortfolioTickerTable({
|
|
|
202
208
|
: pnlLabel !== "P&L" && (column.id === "pnl" || column.id === "pnl_pct") ? {
|
|
203
209
|
...column,
|
|
204
210
|
label: column.id === "pnl_pct" ? pnlLabel.replace("P&L", "%") : pnlLabel,
|
|
205
|
-
} : column)
|
|
211
|
+
} : column).map((column) => hasFrontReference && column.id === noteColumnId ? { ...column, flexGrow: 1 } : column),
|
|
212
|
+
[columns, grossWeights, hasFrontReference, hasNonShareQuantity, noteColumnId, pnlLabel, sharesWidth, tickerWidth]);
|
|
206
213
|
// The table offers "Sort by…" while it has a sort column. A watchlist starts
|
|
207
214
|
// unsorted and a third header click turns sorting off, so the unsorted state
|
|
208
215
|
// needs its own entry until TickerListTableView takes `sortable`.
|
|
@@ -235,8 +242,7 @@ export function PortfolioTickerTable({
|
|
|
235
242
|
resetScrollKey={resetScrollKey}
|
|
236
243
|
onRowActivate={onRowActivate}
|
|
237
244
|
rowMenuAccelerators={PORTFOLIO_ROW_MENU_KEYS}
|
|
238
|
-
|
|
239
|
-
addTickerShortcut={onAddTicker ? QUICK_ADD_KEY : undefined}
|
|
245
|
+
addTickerShortcut={addTickerShortcut}
|
|
240
246
|
rootHeight={rootHeight}
|
|
241
247
|
/>
|
|
242
248
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
1
2
|
import { httpFetch } from "../../../utils/http-transport";
|
|
2
3
|
import { isoDateOrNull, numericOrNull, recordOrNull, textOrNull } from "../../../utils/guards";
|
|
3
4
|
import {
|
|
@@ -84,20 +85,14 @@ async function readJson(url: string): Promise<unknown> {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
/** The series break changes about every two years; one read serves the session. */
|
|
87
|
-
|
|
88
|
+
const seriesBreaks = createTtlCache<string>({ ttlMs: Infinity });
|
|
88
89
|
|
|
89
90
|
function currentSeriesBreak(force: boolean): Promise<string> {
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
seriesBreakRead = read;
|
|
97
|
-
// A failed read is not kept, so the next one asks again.
|
|
98
|
-
read.catch(() => { if (seriesBreakRead === read) seriesBreakRead = null; });
|
|
99
|
-
}
|
|
100
|
-
return seriesBreakRead;
|
|
91
|
+
return seriesBreaks.load("current", async () => {
|
|
92
|
+
const seriesBreak = newestSeriesBreak(parseSeriesBreaks(await readJson(`${API}/list/seriesbreaks.json`)));
|
|
93
|
+
if (!seriesBreak) throw new Error("No series break was listed.");
|
|
94
|
+
return seriesBreak;
|
|
95
|
+
}, { force });
|
|
101
96
|
}
|
|
102
97
|
|
|
103
98
|
/**
|
|
@@ -117,31 +112,11 @@ export async function fetchPrimaryDealers(): Promise<DealerBoard> {
|
|
|
117
112
|
};
|
|
118
113
|
}
|
|
119
114
|
|
|
120
|
-
|
|
121
|
-
at: number;
|
|
122
|
-
read: Promise<SeriesPoint[]>;
|
|
123
|
-
points: SeriesPoint[] | null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const seriesReads = new Map<string, SeriesRead>();
|
|
115
|
+
const seriesReads = createTtlCache<SeriesPoint[]>({ ttlMs: HISTORY_TTL_MS });
|
|
127
116
|
|
|
128
117
|
function seriesRead(seriesBreak: string, key: string, force: boolean): Promise<SeriesPoint[]> {
|
|
129
|
-
const id = `${seriesBreak}:${key}`;
|
|
130
|
-
const known = seriesReads.get(id);
|
|
131
|
-
if (known && !force && Date.now() - known.at < HISTORY_TTL_MS) return known.read;
|
|
132
118
|
const url = `${API}/get/${encodeURIComponent(seriesBreak)}/timeseries/${encodeURIComponent(key)}.json`;
|
|
133
|
-
|
|
134
|
-
at: Date.now(),
|
|
135
|
-
points: null,
|
|
136
|
-
read: readJson(url).then((payload) => {
|
|
137
|
-
entry.points = parseSeriesPoints(payload);
|
|
138
|
-
return entry.points;
|
|
139
|
-
}),
|
|
140
|
-
};
|
|
141
|
-
seriesReads.set(id, entry);
|
|
142
|
-
// A failed read is not kept, so the next one asks again.
|
|
143
|
-
entry.read.catch(() => { if (seriesReads.get(id) === entry) seriesReads.delete(id); });
|
|
144
|
-
return entry.read;
|
|
119
|
+
return seriesReads.load(`${seriesBreak}:${key}`, async () => parseSeriesPoints(await readJson(url)), { force });
|
|
145
120
|
}
|
|
146
121
|
|
|
147
122
|
/** A row's history, one read per series behind it, each kept for the session. */
|
|
@@ -158,9 +133,9 @@ export async function loadDealerHistory(
|
|
|
158
133
|
function heldSeries(seriesBreak: string, row: Pick<DealerRow, "keys">): SeriesPoint[][] | null {
|
|
159
134
|
const series: SeriesPoint[][] = [];
|
|
160
135
|
for (const key of row.keys) {
|
|
161
|
-
const
|
|
162
|
-
if (!
|
|
163
|
-
series.push(
|
|
136
|
+
const points = seriesReads.peek(`${seriesBreak}:${key}`);
|
|
137
|
+
if (!points) return null;
|
|
138
|
+
series.push(points);
|
|
164
139
|
}
|
|
165
140
|
return series;
|
|
166
141
|
}
|
|
@@ -178,6 +153,6 @@ export function cachedDealerHistory(seriesBreak: string, row: Pick<DealerRow, "i
|
|
|
178
153
|
|
|
179
154
|
/** Forgets what was read, for a test or a refresh that must see the release again. */
|
|
180
155
|
export function resetPrimaryDealersCache(): void {
|
|
181
|
-
|
|
156
|
+
seriesBreaks.clear();
|
|
182
157
|
seriesReads.clear();
|
|
183
158
|
}
|
|
@@ -4,6 +4,7 @@ import type { QueryEntry } from "../../../market-data/result-types";
|
|
|
4
4
|
import type { DataProvider } from "../../../types/data-provider";
|
|
5
5
|
import type { PricePoint } from "../../../types/financials";
|
|
6
6
|
import { createSurfaceDependencies, loadVolatilitySurface, type SurfaceLoaderDependencies } from "../vol-surface/client";
|
|
7
|
+
import type { SurfaceSnapshot } from "../vol-surface/model";
|
|
7
8
|
import { projectCurrentAtmIv, type CurrentAtmIvSnapshot } from "./model";
|
|
8
9
|
import { abortable, abortError } from "../../../utils/async-deadline";
|
|
9
10
|
import { errorMessage } from "../../../utils/errors";
|
|
@@ -70,7 +71,9 @@ export interface CurrentAtmIvRequest extends RealizedHistoryRequest {
|
|
|
70
71
|
* Re-read the current reference's own expiry at the live spot: that one chain
|
|
71
72
|
* is refetched and refitted, the catalogue comes from cache, and the other
|
|
72
73
|
* slices of the full load are not touched. When the refreshed slice cannot
|
|
73
|
-
* price an ATM level, the
|
|
74
|
+
* price an ATM level, the refresh rejects with why ("ATM IV 31d not
|
|
75
|
+
* refreshed: ..."): the dated reference already shown stays, and the pane
|
|
76
|
+
* warns that it is not moving.
|
|
74
77
|
*/
|
|
75
78
|
export async function refreshCurrentAtmIv(
|
|
76
79
|
request: CurrentAtmIvRequest,
|
|
@@ -80,14 +83,19 @@ export async function refreshCurrentAtmIv(
|
|
|
80
83
|
const reference = previous.reference;
|
|
81
84
|
if (!reference || !(request.spot > 0) || !Number.isFinite(request.spot)) return previous;
|
|
82
85
|
if (request.signal?.aborted) throw abortError(CANCELLED);
|
|
86
|
+
const notRefreshed = (reason: string) => new Error(`${reference.label} not refreshed: ${reason}`);
|
|
87
|
+
let surface: SurfaceSnapshot;
|
|
83
88
|
try {
|
|
84
|
-
|
|
85
|
-
settings: { ivSource: reference.ivSource } }, dependencies)
|
|
86
|
-
return next.reference?.expiration === reference.expiration ? { ...previous, reference: next.reference } : previous;
|
|
89
|
+
surface = await loadVolatilitySurface({ ...request, forceRefresh: true, expiries: [reference.expiration],
|
|
90
|
+
settings: { ivSource: reference.ivSource } }, dependencies);
|
|
87
91
|
} catch (error) {
|
|
88
92
|
if (request.signal?.aborted || (error instanceof Error && error.name === "AbortError")) throw abortError(CANCELLED);
|
|
89
|
-
|
|
93
|
+
throw notRefreshed(errorMessage(error));
|
|
90
94
|
}
|
|
95
|
+
const next = projectCurrentAtmIv(surface);
|
|
96
|
+
if (next.reference?.expiration === reference.expiration) return { ...previous, reference: next.reference };
|
|
97
|
+
const reasons = [...new Set(surface.failures.map((failure) => failure.message))];
|
|
98
|
+
throw notRefreshed(reasons.join("; ") || "no clean quote at the money");
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/** Options are independent of daily history and use the existing surface chain cache. */
|
|
@@ -19,9 +19,18 @@ export interface RealizedVolEvidenceStatus {
|
|
|
19
19
|
source: string | null;
|
|
20
20
|
asOf: string | null;
|
|
21
21
|
errors: string[];
|
|
22
|
+
/** The errors that are the current IV's alone: a chart missing only its IV point names that, not the symbol. */
|
|
23
|
+
ivErrors?: string[];
|
|
24
|
+
/** The windows the history is too short for: their series have no values, and the capture is whole without them. */
|
|
25
|
+
shortWindows?: number[];
|
|
22
26
|
currentIv: { value: number; date: string; label: string; source: string | null; expiration: number } | null;
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
/** A series a short history leaves without values: a selected window it is too short for. */
|
|
30
|
+
function shortSeries(id: string, shortWindows: readonly number[] | undefined): boolean {
|
|
31
|
+
return /^hv-\d+$/.test(id) && (shortWindows ?? []).includes(Number(id.slice(3)));
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
export interface RealizedVolEvidence extends RealizedVolEvidenceStatus {
|
|
26
35
|
kind: "realized-volatility";
|
|
27
36
|
version: 1;
|
|
@@ -38,7 +47,8 @@ export function realizedVolSemanticEvidence(series: readonly ResolvedSeries[], s
|
|
|
38
47
|
const plottedValueCount = plotted.reduce((sum, entry) => sum + entry.points.filter((point) => isFiniteNumber(point.value)).length, 0);
|
|
39
48
|
return { ...status, kind: "realized-volatility", version: 1, series: plotted, plottedValueCount,
|
|
40
49
|
complete: !status.loading && !status.stale && status.errors.length === 0 && !!status.asOf
|
|
41
|
-
&& plotted.length > 0 && plotted.
|
|
50
|
+
&& plotted.length > 0 && plotted.some((entry) => entry.points.some((point) => isFiniteNumber(point.value)))
|
|
51
|
+
&& plotted.every((entry) => shortSeries(entry.id, status.shortWindows) || entry.points.some((point) => isFiniteNumber(point.value)))
|
|
42
52
|
&& (!status.showIv || status.currentIv != null),
|
|
43
53
|
};
|
|
44
54
|
}
|
|
@@ -56,12 +66,15 @@ function readRealizedVolEvidence(value: unknown): RealizedVolEvidence | null {
|
|
|
56
66
|
|| !isFiniteNumber(value.lookbackYears) || typeof value.showIv !== "boolean" || typeof value.complete !== "boolean"
|
|
57
67
|
|| typeof value.loading !== "boolean" || typeof value.stale !== "boolean" || !isDateString(value.asOf)
|
|
58
68
|
|| !isStringArray(value.errors)
|
|
69
|
+
|| (value.shortWindows !== undefined && (!Array.isArray(value.shortWindows) || !value.shortWindows.every(isFiniteNumber)))
|
|
70
|
+
|| (value.ivErrors !== undefined && !isStringArray(value.ivErrors))
|
|
59
71
|
|| !Array.isArray(value.series) || !value.series.every((entry) => isRecord(entry) && typeof entry.id === "string"
|
|
60
72
|
&& typeof entry.unit === "string" && Array.isArray(entry.points) && entry.points.every((point) => isRecord(point)
|
|
61
73
|
&& isDateString(point.date) && (point.value === null || isFiniteNumber(point.value))))) return null;
|
|
62
74
|
const evidence = value as unknown as RealizedVolEvidence;
|
|
75
|
+
// The IV point is drawn only when there is one: a chart without it matches its data, and is incomplete for it.
|
|
63
76
|
const required = evidence.view === "graph" ? [...evidence.windows.map((window) => `hv-${window}`), "price",
|
|
64
|
-
...(evidence.showIv ? ["current-iv"] : [])] : ["min", "max", "mean", "current"];
|
|
77
|
+
...(evidence.showIv && evidence.currentIv != null ? ["current-iv"] : [])] : ["min", "max", "mean", "current"];
|
|
65
78
|
if (!required.length || (evidence.view === "graph" && !evidence.windows.length)
|
|
66
79
|
|| evidence.series.length !== required.length || new Set(evidence.series.map((entry) => entry.id)).size !== required.length
|
|
67
80
|
|| required.some((id) => !evidence.series.some((entry) => entry.id === id))) return null;
|
|
@@ -75,10 +88,19 @@ function readRealizedVolEvidence(value: unknown): RealizedVolEvidence | null {
|
|
|
75
88
|
}
|
|
76
89
|
if (evidence.complete && (evidence.loading || evidence.stale || evidence.errors.length > 0
|
|
77
90
|
|| (evidence.showIv && !evidence.currentIv)
|
|
78
|
-
|| evidence.series.some((entry) => !entry.points.some((point) => isFiniteNumber(point.value))))) return null;
|
|
91
|
+
|| evidence.series.some((entry) => !shortSeries(entry.id, evidence.shortWindows) && !entry.points.some((point) => isFiniteNumber(point.value))))) return null;
|
|
79
92
|
return evidence;
|
|
80
93
|
}
|
|
81
94
|
|
|
95
|
+
/** Drawn, current and whole but for the current IV, whose errors are the only ones. */
|
|
96
|
+
function missingOnlyIv(evidence: RealizedVolEvidence): boolean {
|
|
97
|
+
const ivErrors = new Set(evidence.ivErrors ?? []);
|
|
98
|
+
return evidence.view === "graph" && evidence.showIv && evidence.currentIv == null
|
|
99
|
+
&& !evidence.loading && !evidence.stale && evidence.plottedValueCount > 0
|
|
100
|
+
&& evidence.errors.every((error) => ivErrors.has(error))
|
|
101
|
+
&& evidence.series.every((entry) => shortSeries(entry.id, evidence.shortWindows) || entry.points.some((point) => isFiniteNumber(point.value)));
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
export const realizedVolScreenshotEvidence: PaneScreenshotEvidenceHook<RealizedVolEvidence> = {
|
|
83
105
|
paneId: "realized-vol",
|
|
84
106
|
kind: "realized-volatility",
|
|
@@ -98,6 +120,9 @@ export const realizedVolScreenshotEvidence: PaneScreenshotEvidenceHook<RealizedV
|
|
|
98
120
|
},
|
|
99
121
|
unavailable(evidence, { resolved, payload }) {
|
|
100
122
|
const symbol = payload.financials[0]?.[0] ?? resolved.createOptions?.symbol;
|
|
101
|
-
|
|
123
|
+
if (evidence?.complete && !evidence.loading) return [];
|
|
124
|
+
// The volatility drawn whole and only the IV point missing: the capture names the IV, not the symbol.
|
|
125
|
+
if (evidence && missingOnlyIv(evidence)) return [`${symbol ?? evidence.symbol} current ATM IV`];
|
|
126
|
+
return [symbol ?? "realized volatility"];
|
|
102
127
|
},
|
|
103
128
|
};
|
|
@@ -3,7 +3,7 @@ import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
|
3
3
|
import { createSurfaceDependencies } from "../vol-surface/client";
|
|
4
4
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
5
5
|
import { createRealizedVolatilityDependencies, loadCurrentAtmIv, loadRealizedVolatilityHistory } from "./client";
|
|
6
|
-
import { projectRealizedVolatility, type CurrentAtmIvSnapshot } from "./model";
|
|
6
|
+
import { atmIvObservedLabel, projectRealizedVolatility, type CurrentAtmIvSnapshot } from "./model";
|
|
7
7
|
import { ESTIMATOR_OPTIONS, selectedWindows } from "./settings";
|
|
8
8
|
import { DAILY_CLOSES } from "../shared/report-freshness";
|
|
9
9
|
import { listingAbroadVenue } from "../../../utils/sec";
|
|
@@ -69,13 +69,17 @@ export function realizedVolHeadless(initialView: "graph" | "cone"): HeadlessPane
|
|
|
69
69
|
formatted: iv?.reference ? new Date(iv.reference.expiration * 1000).toISOString().slice(0, 10) : "--" },
|
|
70
70
|
{ label: "Days", value: iv?.reference?.daysToExpiry ?? null,
|
|
71
71
|
formatted: iv?.reference ? String(Math.round(iv.reference.daysToExpiry)) : "--" },
|
|
72
|
-
|
|
72
|
+
// To the minute, as the pane reads it; the JSON keeps the chain's read time whole.
|
|
73
|
+
{ label: "Observed", value: iv?.reference?.date.toISOString() ?? null,
|
|
74
|
+
formatted: iv?.reference ? atmIvObservedLabel(iv.reference.date) : "--" },
|
|
73
75
|
] },
|
|
74
76
|
],
|
|
75
|
-
|
|
77
|
+
// A short history is whole: the windows it is too short for have no estimate yet, and the note says when it starts.
|
|
78
|
+
complete: model.cone.every((row) => row.current != null || model.shortWindows.includes(row.window))
|
|
79
|
+
&& model.warnings.length === 0 && !history.stale && errors.length === 0
|
|
76
80
|
&& (!args.options.showIv || iv?.reference != null || iv?.noOptionChain === true),
|
|
77
81
|
unavailableSymbols: model.history.length ? [] : [instrument.symbol], errors,
|
|
78
|
-
notes: [barsPerYearLabel(model.barsPerYear)],
|
|
82
|
+
notes: [barsPerYearLabel(model.barsPerYear), ...model.historyNote ? [model.historyNote] : []],
|
|
79
83
|
metadata: { unit: "decimal annualized volatility", annualization: model.barsPerYear, model, currentIv: iv,
|
|
80
84
|
...(abroad && args.options.showIv ? { notices: [`Options data covers US listings only, so ${args.symbols[0]} has no current IV.`] } : {}),
|
|
81
85
|
source: history.source, stale: history.stale, fetchedAt: history.fetchedAt,
|
|
@@ -21,13 +21,13 @@ export const realizedVolModule: PluginModule = {
|
|
|
21
21
|
{ ...createTickerSurfacePaneTemplate({
|
|
22
22
|
id: "realized-vol-graph-pane", paneId: "realized-vol", label: "Realized Volatility Graph",
|
|
23
23
|
description: "Rolling realized volatility, price and a dated current ATM IV reference.",
|
|
24
|
-
keywords: ["hvg", "realized", "historical", "volatility", "gv"], shortcut: "HVG", publicShare: true,
|
|
24
|
+
keywords: ["hvg", "realized", "historical", "volatility", "vol", "gv"], shortcut: "HVG", publicShare: true,
|
|
25
25
|
viewKey: "graph", settings: () => ({ initialView: "graph" }),
|
|
26
26
|
}), headless: realizedVolHeadless("graph") },
|
|
27
27
|
{ ...createTickerSurfacePaneTemplate({
|
|
28
28
|
id: "realized-vol-table-pane", paneId: "realized-vol", label: "Realized Volatility Cone",
|
|
29
29
|
description: "Current realized volatility against its historical range and percentile.",
|
|
30
|
-
keywords: ["hvt", "volatility", "cone", "percentile"], shortcut: "HVT", publicShare: true,
|
|
30
|
+
keywords: ["hvt", "volatility", "vol", "cone", "percentile"], shortcut: "HVT", publicShare: true,
|
|
31
31
|
viewKey: "cone", settings: () => ({ initialView: "cone" }),
|
|
32
32
|
}), headless: realizedVolHeadless("cone") },
|
|
33
33
|
],
|
|
@@ -19,6 +19,24 @@ export interface RealizedVolatilityModel {
|
|
|
19
19
|
/** What the volatility is annualised at, read from the series' own dates. */
|
|
20
20
|
barsPerYear: BarsPerYear;
|
|
21
21
|
warnings: string[];
|
|
22
|
+
/**
|
|
23
|
+
* The windows the history is too short for, longest last: a stock that
|
|
24
|
+
* listed in May has no 180- or 260-bar estimate yet, and nothing failed.
|
|
25
|
+
*/
|
|
26
|
+
shortWindows: number[];
|
|
27
|
+
/**
|
|
28
|
+
* What a short history leaves out, said once: "History starts 2026-05-07: 121
|
|
29
|
+
* daily bars so far; the 180- and 260-bar windows need more". Null when the
|
|
30
|
+
* history covers the lookback and every window.
|
|
31
|
+
*/
|
|
32
|
+
historyNote: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** "the 180-bar window", "the 180- and 260-bar windows", "the 90-, 180- and 260-bar windows". */
|
|
36
|
+
function windowsText(windows: readonly number[]): string {
|
|
37
|
+
if (windows.length === 1) return `the ${windows[0]}-bar window`;
|
|
38
|
+
const head = windows.slice(0, -1).map((window) => `${window}-`);
|
|
39
|
+
return `the ${head.join(", ")} and ${windows.at(-1)}-bar windows`;
|
|
22
40
|
}
|
|
23
41
|
|
|
24
42
|
export interface RealizedVolatilityProjectionOptions {
|
|
@@ -45,6 +63,10 @@ export function projectRealizedVolatility(
|
|
|
45
63
|
const windows = [...new Set(options.windows ?? REALIZED_VOLATILITY_WINDOWS)]
|
|
46
64
|
.filter((window) => REALIZED_VOLATILITY_WINDOWS.some((standard) => standard === window));
|
|
47
65
|
const warnings: string[] = [];
|
|
66
|
+
// What follows from a history shorter than the windows and lookback asked for: notes, not failures.
|
|
67
|
+
let shortLookback = false;
|
|
68
|
+
let shortWarmup = false;
|
|
69
|
+
const shortWindows: number[] = [];
|
|
48
70
|
const dates = new Map<number, PricePoint>();
|
|
49
71
|
for (const point of points) {
|
|
50
72
|
const date = new Date(point.date);
|
|
@@ -66,24 +88,28 @@ export function projectRealizedVolatility(
|
|
|
66
88
|
const cone = volatilityCone(cadenceIssue ? [] : history, { estimator, lookbackYears });
|
|
67
89
|
const visible = history.filter((point) => point.date.getTime() > cutoff);
|
|
68
90
|
const visibleSeries = series.filter((point) => point.date.getTime() > cutoff);
|
|
69
|
-
if (history.length > 0 && history[0]!.date.getTime() > cutoff)
|
|
70
|
-
warnings.push(`History covers less than the requested ${lookbackYears}Y lookback`);
|
|
71
|
-
}
|
|
91
|
+
if (history.length > 0 && history[0]!.date.getTime() > cutoff) shortLookback = true;
|
|
72
92
|
for (const window of history.length > 0 && !cadenceIssue ? REALIZED_VOLATILITY_WINDOWS : []) {
|
|
73
93
|
const result = realizedVolatilityResult(history, window, estimator);
|
|
74
|
-
if (result.reason === "insufficient-history")
|
|
94
|
+
if (result.reason === "insufficient-history") shortWindows.push(window);
|
|
75
95
|
else if (result.reason) warnings.push(`HV${window} unavailable: ${result.reason.replaceAll("-", " ")}`);
|
|
76
96
|
}
|
|
77
97
|
const priorWarmup = history.filter((point) => point.date.getTime() <= cutoff).length;
|
|
78
98
|
const requiredWarmup = estimator === "close-to-close" || estimator === "yang-zhang" ? 260 : 259;
|
|
79
99
|
if (history.length > 0 && !cadenceIssue && priorWarmup < requiredWarmup) {
|
|
80
|
-
|
|
100
|
+
shortWarmup = true;
|
|
81
101
|
} else if (!cadenceIssue && cone.some((row) => row.sampleSize < visible.length)) {
|
|
82
102
|
warnings.push("Unavailable historical estimates reduce volatility-cone sample coverage");
|
|
83
103
|
}
|
|
104
|
+
const first = history[0]?.date.toISOString().slice(0, 10);
|
|
105
|
+
// Where the history starts, never read as the listing date: a source's first bar is not proof of when it listed.
|
|
106
|
+
const historyNote = !first || (!shortLookback && !shortWarmup && !shortWindows.length) ? null
|
|
107
|
+
: `History starts ${first}: ${history.length} daily bars so far; ${shortWindows.length
|
|
108
|
+
? `${windowsText(shortWindows)} need${shortWindows.length === 1 ? "s" : ""} more`
|
|
109
|
+
: "the long windows' cone covers part of the lookback"}`;
|
|
84
110
|
return { symbol: options.symbol.trim().toUpperCase(), estimator, lookbackYears, windows,
|
|
85
111
|
history: visible, series: visibleSeries, cone, asOf, barsPerYear: barsPerYear(history.map((point) => point.date)),
|
|
86
|
-
warnings: [...new Set(warnings)] };
|
|
112
|
+
warnings: [...new Set(warnings)], shortWindows, historyNote };
|
|
87
113
|
}
|
|
88
114
|
|
|
89
115
|
interface CurrentAtmIvReference {
|
|
@@ -107,6 +133,11 @@ export interface CurrentAtmIvSnapshot {
|
|
|
107
133
|
noOptionChain?: boolean;
|
|
108
134
|
}
|
|
109
135
|
|
|
136
|
+
/** When an IV was observed, to the minute in UTC (`2026-10-27 20:14 UTC`), in the pane and the report alike. */
|
|
137
|
+
export function atmIvObservedLabel(date: Date): string {
|
|
138
|
+
return `${date.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
139
|
+
}
|
|
140
|
+
|
|
110
141
|
/** A LEAPS ATM IV is not comparable with a 10 to 260 session realized cone. */
|
|
111
142
|
const MAX_CURRENT_ATM_IV_DAYS = 90;
|
|
112
143
|
|
|
@@ -18,7 +18,7 @@ import { useLiveSessionRefresh } from "../shared/volatility/live-session";
|
|
|
18
18
|
import type { RealizedVolatilityEstimator, VolatilityConeStatistics } from "../shared/volatility";
|
|
19
19
|
import { loadCurrentAtmIv, loadRealizedVolatilityHistory, refreshCurrentAtmIv } from "./client";
|
|
20
20
|
import type { CurrentAtmIvSnapshot } from "./model";
|
|
21
|
-
import { projectRealizedVolatility } from "./model";
|
|
21
|
+
import { atmIvObservedLabel, projectRealizedVolatility } from "./model";
|
|
22
22
|
import { RealizedVolGraph, VolatilityConeChart } from "./charts";
|
|
23
23
|
import { coneWindowDate } from "./chart-model";
|
|
24
24
|
import type { RealizedVolEvidenceStatus } from "./evidence";
|
|
@@ -109,7 +109,7 @@ export function RealizedVolPane({ width, height, focused }: PaneProps) {
|
|
|
109
109
|
const refreshedCurrent = refreshedIv?.owner === ivLoader && refreshedIv.at > (loadedIv.updatedAt ?? 0) ? refreshedIv.data : null;
|
|
110
110
|
const iv = { ...loadedIv, data: refreshedCurrent ?? loadedIv.data };
|
|
111
111
|
ivDataRef.current = iv.data;
|
|
112
|
-
useLiveSessionRefresh(refreshIv, ATM_IV_REFRESH_MS, showIv && spotAvailable && !!iv.data?.reference && !loadedIv.loading,
|
|
112
|
+
const ivRefresh = useLiveSessionRefresh(refreshIv, ATM_IV_REFRESH_MS, showIv && spotAvailable && !!iv.data?.reference && !loadedIv.loading,
|
|
113
113
|
loadedIv.updatedAt);
|
|
114
114
|
const model = useMemo(() => history.data ? projectRealizedVolatility(history.data.history, {
|
|
115
115
|
symbol: history.data.symbol, estimator, windows, lookbackYears: Number(lookback) === 2 ? 2 : 1,
|
|
@@ -117,8 +117,8 @@ export function RealizedVolPane({ width, height, focused }: PaneProps) {
|
|
|
117
117
|
const chartInput = useMemo(() => model ? { history: model.history, rolling: model.series, windows: model.windows,
|
|
118
118
|
priceUnit: priceSeriesUnit(quote?.currency ?? "", quote?.instrumentType), iv: showIv ? iv.data?.reference ?? null : null } : null,
|
|
119
119
|
[model, quote?.currency, quote?.instrumentType, showIv, iv.data]);
|
|
120
|
-
const notices = [identityError, history.error, history.data?.error, iv.error, iv.data?.error,
|
|
121
|
-
...(model?.warnings ?? []), ...(showIv ? iv.data?.warnings ?? [] : []),
|
|
120
|
+
const notices = [identityError, history.error, history.data?.error, iv.error, ivRefresh.error, iv.data?.error,
|
|
121
|
+
...(model?.warnings ?? []), model?.historyNote, ...(showIv ? iv.data?.warnings ?? [] : []),
|
|
122
122
|
...(showIv && symbol && !spotAvailable ? ["Current ATM IV unavailable: underlying quote is missing or stale"] : []),
|
|
123
123
|
].filter((value): value is string => !!value);
|
|
124
124
|
usePaneNoticeFooter({ registrationId: "realized-vol-notices", notices: [...new Set(notices)], focused });
|
|
@@ -154,15 +154,17 @@ export function RealizedVolPane({ width, height, focused }: PaneProps) {
|
|
|
154
154
|
role: "report-notice", label: "Chart window",
|
|
155
155
|
getMetadata: () => ({ text: `Chart legend: ${legendWindow}-bar window`, key: "legendWindow", value: legendWindow }),
|
|
156
156
|
} : null);
|
|
157
|
+
const ivErrors = showIv ? [...new Set([iv.error, ivRefresh.error, iv.data?.error, !iv.data?.reference ? "Current ATM IV unavailable" : null]
|
|
158
|
+
.filter((value): value is string => !!value))] : [];
|
|
157
159
|
const evidence: RealizedVolEvidenceStatus = {
|
|
158
160
|
symbol: model?.symbol ?? symbol ?? "", view: view === "cone" ? "cone" : "graph", estimator, legendWindow,
|
|
159
|
-
windows, lookbackYears: Number(lookback) === 2 ? 2 : 1, showIv,
|
|
161
|
+
windows, lookbackYears: Number(lookback) === 2 ? 2 : 1, showIv, shortWindows: model?.shortWindows ?? [],
|
|
160
162
|
loading: history.loading || (showIv && iv.loading),
|
|
161
163
|
stale: history.data?.stale ?? false, source: history.data?.source ?? null,
|
|
162
164
|
asOf: model?.asOf?.toISOString() ?? null,
|
|
163
|
-
errors: [...new Set([history.error, history.data?.error, ...(model?.warnings ?? []),
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
errors: [...new Set([history.error, history.data?.error, ...(model?.warnings ?? []), ...ivErrors]
|
|
166
|
+
.filter((value): value is string => !!value))],
|
|
167
|
+
ivErrors,
|
|
166
168
|
currentIv: showIv && iv.data?.reference ? { value: iv.data.reference.value * 100,
|
|
167
169
|
date: iv.data.reference.date.toISOString(), label: iv.data.reference.label,
|
|
168
170
|
source: iv.data.reference.source, expiration: iv.data.reference.expiration } : null,
|
|
@@ -183,7 +185,7 @@ export function RealizedVolPane({ width, height, focused }: PaneProps) {
|
|
|
183
185
|
<QueryBar width={width} filters={[
|
|
184
186
|
{ id: "estimator", label: "Estimator", value: estimator, options: ESTIMATOR_OPTIONS, onChange: (value: string) => setEstimator(value as RealizedVolatilityEstimator) },
|
|
185
187
|
{ id: "lookback", label: "Lookback", value: String(lookback), options: [{ value: "1", label: "1Y" }, { value: "2", label: "2Y" }], onChange: setLookback },
|
|
186
|
-
]} meta={reference ? `ATM IV observed ${reference.date
|
|
188
|
+
]} meta={reference ? `ATM IV observed ${atmIvObservedLabel(reference.date)}` : "Annualized %"} />
|
|
187
189
|
{!symbol ? <EmptyState title="Select a ticker." /> : <PaneStatusBody subject="realized volatility" loading={history.loading && !model}
|
|
188
190
|
error={!model ? history.error ?? identityError ?? null : null} empty={!!model && !model.history.length}>
|
|
189
191
|
{model && view === "cone" ? <>
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "../../../api-client/types";
|
|
8
8
|
import { createPluginCache } from "../../../data/plugin-cache";
|
|
9
9
|
import { isAccessDenied } from "../../../api-client/errors";
|
|
10
|
+
import { quoteTradingExchange } from "../../../market-data/quotes/metadata";
|
|
10
11
|
import { cachedCloudResource, loadCloudResource } from "../shared/cloud-resource";
|
|
11
12
|
import {
|
|
12
13
|
buildRotation,
|
|
@@ -75,6 +76,10 @@ export function validateRotationHistory(
|
|
|
75
76
|
throw new Error("Invalid daily history.");
|
|
76
77
|
return response.data;
|
|
77
78
|
}
|
|
79
|
+
/** The venue a quote is for, as `quote` names it; empty when it names none. */
|
|
80
|
+
function listingVenue(quote: CloudQuotePayload | undefined): string {
|
|
81
|
+
return quote ? quoteTradingExchange(quote) : "";
|
|
82
|
+
}
|
|
78
83
|
export async function fetchRotation(
|
|
79
84
|
benchmark: RotationInstrument,
|
|
80
85
|
instruments: RotationInstrument[],
|
|
@@ -106,6 +111,16 @@ export async function fetchRotation(
|
|
|
106
111
|
if (isAccessDenied(error)) throw error;
|
|
107
112
|
quoteError = "Listing currencies unavailable.";
|
|
108
113
|
}
|
|
114
|
+
// A benchmark typed without its exchange (ACWI) is the listing its quote is for (ACWI:NASDAQ), as `quote`
|
|
115
|
+
// reads it: that venue's calendar picks the weekly closes.
|
|
116
|
+
const venue = benchmark.exchange
|
|
117
|
+
? ""
|
|
118
|
+
: listingVenue(quotes.get(rotationId(benchmark)));
|
|
119
|
+
const reference = venue ? { ...benchmark, exchange: venue } : benchmark;
|
|
120
|
+
const requested = unique.map((row) => ({
|
|
121
|
+
id: rotationId(row),
|
|
122
|
+
instrument: rotationId(row) === rotationId(benchmark) ? reference : row,
|
|
123
|
+
}));
|
|
109
124
|
const start = new Date(now);
|
|
110
125
|
start.setUTCFullYear(start.getUTCFullYear() - 3);
|
|
111
126
|
start.setUTCDate(1);
|
|
@@ -115,10 +130,9 @@ export async function fetchRotation(
|
|
|
115
130
|
let next = 0;
|
|
116
131
|
// Bound Cloud history requests; shared server caches batch upstream demand.
|
|
117
132
|
await Promise.all(
|
|
118
|
-
Array.from({ length: Math.min(4,
|
|
119
|
-
while (next <
|
|
120
|
-
const instrument =
|
|
121
|
-
id = rotationId(instrument),
|
|
133
|
+
Array.from({ length: Math.min(4, requested.length) }, async () => {
|
|
134
|
+
while (next < requested.length) {
|
|
135
|
+
const { id, instrument } = requested[next++]!,
|
|
122
136
|
currency = quotes.get(id)?.currency ?? null;
|
|
123
137
|
try {
|
|
124
138
|
const response = await client.getCloudHistory(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { fetchRotation } from "./client";
|
|
3
|
-
import { rotationInstruments, sectorRotationInstruments } from "./model";
|
|
3
|
+
import { DEFAULT_ROTATION_BENCHMARK, rotationInstruments, sectorRotationInstruments } from "./model";
|
|
4
4
|
|
|
5
5
|
/** Same rounding as the pane: ratios to 2 decimals, percentiles whole. */
|
|
6
6
|
const fixed = (value: unknown, digits: number) =>
|
|
@@ -21,7 +21,7 @@ export const rotationHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
21
21
|
key: "benchmark",
|
|
22
22
|
type: "string",
|
|
23
23
|
description: "Benchmark listing.",
|
|
24
|
-
defaultValue:
|
|
24
|
+
defaultValue: DEFAULT_ROTATION_BENCHMARK,
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
key: "trail",
|
|
@@ -44,7 +44,7 @@ export const rotationHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
44
44
|
},
|
|
45
45
|
async load(args, ctx) {
|
|
46
46
|
const references = rotationInstruments(
|
|
47
|
-
String(args.options.benchmark ??
|
|
47
|
+
String(args.options.benchmark ?? DEFAULT_ROTATION_BENCHMARK),
|
|
48
48
|
);
|
|
49
49
|
if (references.length !== 1) throw new Error("Choose one benchmark.");
|
|
50
50
|
const instruments = args.symbols.length
|
|
@@ -3,6 +3,7 @@ import type { PluginModule } from "../plugin-module";
|
|
|
3
3
|
import { rotationCache } from "./client";
|
|
4
4
|
import { rotationHeadless } from "./headless";
|
|
5
5
|
import { RelativeRotationPane } from "./pane";
|
|
6
|
+
import { DEFAULT_ROTATION_BENCHMARK } from "./model";
|
|
6
7
|
|
|
7
8
|
export const relativeRotationModule: PluginModule = {
|
|
8
9
|
panes: [
|
|
@@ -41,7 +42,7 @@ export const relativeRotationModule: PluginModule = {
|
|
|
41
42
|
key: "benchmark",
|
|
42
43
|
label: "Benchmark",
|
|
43
44
|
type: "text",
|
|
44
|
-
placeholder:
|
|
45
|
+
placeholder: DEFAULT_ROTATION_BENCHMARK,
|
|
45
46
|
},
|
|
46
47
|
{
|
|
47
48
|
key: "trail",
|