@gloom-sh/terminal 0.18.5 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +140 -7
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +17 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +11 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/types.ts +70 -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/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 +72 -26
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +61 -12
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +55 -34
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +8 -0
- package/src/cli/glossary.ts +25 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +180 -52
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +86 -3
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +49 -14
- 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/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/pane-templates/items.ts +3 -2
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- 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 +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/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/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 +71 -137
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +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/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +336 -77
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- 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 +24 -6
- 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/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/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- 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/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +6 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +36 -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 +1 -0
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +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/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +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 +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +7 -5
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/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 +27 -3
- 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 +2 -1
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/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/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/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 +2 -2
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- 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/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 +12 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/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.ts +44 -3
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/terminal-codes.ts +19 -0
- 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/financials.ts +32 -1
- package/src/types/headless.ts +21 -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 +44 -0
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -264,6 +264,7 @@ function HelpPane({ focused, width, height }: PaneProps) {
|
|
|
264
264
|
entry("zoom-tool", ["Shift+Z", "Shift+Drag"], "Pick the zoom tool, then drag a time range."),
|
|
265
265
|
entry("ruler", ["Shift+M", "Alt+Drag"], "Pick the ruler, then drag to measure a move."),
|
|
266
266
|
entry("draw", ["Shift+D", "Shift+P"], "Draw a trend line or a freehand shape."),
|
|
267
|
+
entry("shapes", ["Shift+J", "Shift+B", "Shift+F"], "Draw a horizontal ray, a rectangle or a Fib retracement."),
|
|
267
268
|
entry("place", ["Enter", "Arrows"], "With a tool picked, Enter starts it at the cursor, the arrows move its end, and Enter finishes it."),
|
|
268
269
|
entry("drawings", ["[", "]"], "With a drawing tool picked, pick a drawing."),
|
|
269
270
|
entry("colour", ["c", "Backspace"], "Cycle the drawing colour, or delete the picked drawing (the last one while a drawing tool is picked)."),
|
|
@@ -18,7 +18,7 @@ export function resolveWindowTemplates(registry: SharedRegistry): HelpShortcutEn
|
|
|
18
18
|
const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
|
|
19
19
|
|
|
20
20
|
return [...registry.paneTemplates.values()]
|
|
21
|
-
.filter((template) => template.shortcut)
|
|
21
|
+
.filter((template) => template.shortcut && template.listed !== false)
|
|
22
22
|
.filter((template) => {
|
|
23
23
|
const pluginId = registry.getPaneTemplatePluginId?.(template.id);
|
|
24
24
|
return !pluginId || !disabledPlugins.has(pluginId);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Quote } from "../../../types/financials";
|
|
2
|
+
import { quoteTradingExchange } from "../../../market-data/quotes/metadata";
|
|
3
|
+
import { isUsListingExchange } from "../../../utils/exchanges";
|
|
4
|
+
import { isFundType } from "../../../utils/sec";
|
|
5
|
+
import { isNoSourceAnswer } from "../../../sources/provider-errors";
|
|
6
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
7
|
+
import { fetchFunds } from "../members/client";
|
|
8
|
+
import { coveredFund } from "../members/model";
|
|
9
|
+
|
|
10
|
+
type FundQuote = Pick<Quote, "instrumentType" | "tradingExchangeName" | "listingExchangeName" | "exchangeName">;
|
|
11
|
+
|
|
12
|
+
/** What a holder report says for a fund in place of its holders: the reason, and the commands that read the fund. */
|
|
13
|
+
export interface FundHoldersAnswer {
|
|
14
|
+
message: string;
|
|
15
|
+
/** One command a line, each for what it reads; empty for a fund abroad, which those do not cover. */
|
|
16
|
+
details: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What `holders`, `13f` and `fn HDS` say for a fund no holder source has an
|
|
21
|
+
* ownership breakdown of (XLE, SPY, EWJ), in place of the source's bare
|
|
22
|
+
* NO_DATA: that it is a fund, and for a US-listed one the commands that read
|
|
23
|
+
* it, each only where it runs: the 13F filers that hold it (`fn 13F`), what it
|
|
24
|
+
* holds where `fn MEMB` covers it, and its fund facts (`fn ETF`). Null for a
|
|
25
|
+
* listing that is not a fund, or a failure that is no answer (a timeout).
|
|
26
|
+
*/
|
|
27
|
+
export async function fundHoldersAnswer(
|
|
28
|
+
symbol: string,
|
|
29
|
+
error: unknown,
|
|
30
|
+
quote: FundQuote | null | undefined,
|
|
31
|
+
client: NonNullable<Parameters<typeof fetchFunds>[0]>,
|
|
32
|
+
): Promise<FundHoldersAnswer | null> {
|
|
33
|
+
if (!isNoSourceAnswer(error) || !quote || !isFundType(quote.instrumentType)) return null;
|
|
34
|
+
const fund = symbol.split(":")[0]!.trim().toUpperCase();
|
|
35
|
+
const message = `${fund} is a fund: there is no ownership breakdown for funds.`;
|
|
36
|
+
if (!isUsListingExchange(quoteTradingExchange(quote))) return { message, details: [] };
|
|
37
|
+
// A covered-fund list that cannot be read names no fn MEMB, which may not run for this fund.
|
|
38
|
+
const funds = await fetchFunds(client).then((payload) => payload.funds)
|
|
39
|
+
.catch(logFailure("holders.fund-holders", "Read the funds fn MEMB covers failed", null));
|
|
40
|
+
const covered = !!funds && !!coveredFund(funds, fund);
|
|
41
|
+
// Shares of an exchange-traded fund are 13F securities; a mutual fund's are not.
|
|
42
|
+
const traded = !/MUTUAL/i.test(quote.instrumentType ?? "");
|
|
43
|
+
return {
|
|
44
|
+
message,
|
|
45
|
+
details: [
|
|
46
|
+
...traded ? [`13F filers that hold it: gloom fn 13F ${fund}`] : [],
|
|
47
|
+
...covered ? [`What it holds: gloom fn MEMB ${fund}`] : [],
|
|
48
|
+
`Fund facts: gloom fn ETF ${fund}`,
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
holderReportUnits,
|
|
34
34
|
} from "./report-rows";
|
|
35
35
|
import { REPORT_ROWS } from "../shared/headless-options";
|
|
36
|
+
import { fundHoldersAnswer } from "./fund-holders";
|
|
36
37
|
import {
|
|
37
38
|
HOLDER_ROWS_MAX,
|
|
38
39
|
indexedHoldersPartialNote,
|
|
@@ -224,11 +225,27 @@ export function createHoldersHeadless(
|
|
|
224
225
|
.catch(logFailure("holders.headless", "Resolve the listing's venue for the 13F holder index failed", null)))?.exchange ?? "";
|
|
225
226
|
const indexTicker = indexHolderTicker(symbol, exchange);
|
|
226
227
|
const [snapshot, index] = await Promise.all([
|
|
227
|
-
dependencies.loadSnapshot(symbol, args, ctx)
|
|
228
|
+
dependencies.loadSnapshot(symbol, args, ctx).catch(async (error: unknown) => {
|
|
229
|
+
const quote = await ctx.marketData.getQuote(symbol)
|
|
230
|
+
.catch(logFailure("holders.headless", "Read the quote of a listing with no holders failed", null));
|
|
231
|
+
const fund = await fundHoldersAnswer(symbol, error, quote, ctx.apiClient);
|
|
232
|
+
if (fund) return fund;
|
|
233
|
+
throw error;
|
|
234
|
+
}),
|
|
228
235
|
indexTicker
|
|
229
236
|
? dependencies.loadIndexedHolders(indexTicker, limit, ctx).catch(logFailure("holders.headless", "Load 13F holder index failed", null))
|
|
230
237
|
: null,
|
|
231
238
|
]);
|
|
239
|
+
// A fund has no ownership breakdown: the report says so in place of rows, with what reads it, and fails as no data.
|
|
240
|
+
if ("message" in snapshot) {
|
|
241
|
+
return {
|
|
242
|
+
columns: HOLDER_REPORT_COLUMNS,
|
|
243
|
+
rows: [],
|
|
244
|
+
empty: [snapshot.message, ...snapshot.details].join("\n"),
|
|
245
|
+
unavailableSymbols: [symbol],
|
|
246
|
+
metadata: { symbol },
|
|
247
|
+
};
|
|
248
|
+
}
|
|
232
249
|
const { marketCap } = snapshot;
|
|
233
250
|
const indexed = !!index?.rows.length && indexHolderTicker(symbol, exchange, snapshot.data) != null;
|
|
234
251
|
const partialNote = indexed ? indexedHoldersPartialNote(index, limit) : null;
|
|
@@ -5,10 +5,18 @@ import type { ChartRequest, InstrumentRef } from "../../../market-data/request-t
|
|
|
5
5
|
import type { QueryEntry } from "../../../market-data/result-types";
|
|
6
6
|
import type { DataProvider } from "../../../types/data-provider";
|
|
7
7
|
import type { PricePoint } from "../../../types/financials";
|
|
8
|
-
import {
|
|
8
|
+
import { realizedVolatilityCadenceIssue, rollingRealizedVolatility } from "../shared/volatility";
|
|
9
9
|
import { NO_FATE, type ListingFate } from "../../../tickers/listing-fate";
|
|
10
10
|
|
|
11
11
|
export type IvMethod = "quote-mid" | "trade-close";
|
|
12
|
+
/**
|
|
13
|
+
* Which prices a trade-close reading was solved from: each option's closing
|
|
14
|
+
* print, kept because it sat inside the closing two-sided quote (`print`), the
|
|
15
|
+
* quote midpoint, used where a print sat outside it (`mid`), or both
|
|
16
|
+
* (`mixed`). Absent when no closing quote was stored to check the prints
|
|
17
|
+
* against, as for most history before the checks began.
|
|
18
|
+
*/
|
|
19
|
+
export type IvCloseBasis = "print" | "mid" | "mixed";
|
|
12
20
|
export type IvCoverageStatus = "ready" | "backfilling" | "queued" | "unavailable";
|
|
13
21
|
export interface IvPoint {
|
|
14
22
|
sessionDate: string;
|
|
@@ -27,11 +35,14 @@ export interface IvPoint {
|
|
|
27
35
|
*/
|
|
28
36
|
put25_30?: number | null;
|
|
29
37
|
call25_30?: number | null;
|
|
38
|
+
/** Prices a trade close was solved from; see `IvCloseBasis`. Absent from older servers. */
|
|
39
|
+
basis?: IvCloseBasis | null;
|
|
30
40
|
}
|
|
31
41
|
export interface IvStats {
|
|
32
42
|
value: number;
|
|
33
43
|
date: string;
|
|
34
44
|
method: IvMethod;
|
|
45
|
+
basis?: IvCloseBasis | null;
|
|
35
46
|
rank: number | null;
|
|
36
47
|
percentile: number | null;
|
|
37
48
|
low: number | null;
|
|
@@ -54,6 +65,7 @@ interface IvReading {
|
|
|
54
65
|
iv90: number | null;
|
|
55
66
|
iv180: number | null;
|
|
56
67
|
iv365: number | null;
|
|
68
|
+
basis?: IvCloseBasis | null;
|
|
57
69
|
}
|
|
58
70
|
export interface IvHistoryPayload {
|
|
59
71
|
version: 1;
|
|
@@ -155,6 +167,37 @@ export function createHvDependencies(marketData?: DataProvider): HvDependencies
|
|
|
155
167
|
/** Screen rows with no reading whose symbol's fate is looked up; a long list of them is a backfill, not a lookup. */
|
|
156
168
|
const MAX_FATE_LOOKUPS = 10;
|
|
157
169
|
|
|
170
|
+
/**
|
|
171
|
+
* A symbol's realized volatility over one window: the newest, and after every
|
|
172
|
+
* session's close, so a reading is divided by the HV of its own date and not
|
|
173
|
+
* of today.
|
|
174
|
+
*/
|
|
175
|
+
export interface ScreenHv {
|
|
176
|
+
/** HV over the newest window of closes. */
|
|
177
|
+
value: number | null;
|
|
178
|
+
/** Date of the newest daily bar: the newest session the symbol's prices reach. */
|
|
179
|
+
date: string | null;
|
|
180
|
+
/** HV after each session's close, oldest first; sessions without a full window are left out. */
|
|
181
|
+
series: ReadonlyArray<readonly [date: string, value: number]>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const barDay = (value: Date) => new Date(value).toISOString().slice(0, 10);
|
|
185
|
+
|
|
186
|
+
/** `ScreenHv` from daily closes; a provider that fell back to weekly or intraday bars would misstate daily HV, so it has none. */
|
|
187
|
+
export function screenHv(history: readonly PricePoint[], window: number): ScreenHv {
|
|
188
|
+
if (realizedVolatilityCadenceIssue(history)) return { value: null, date: null, series: [] };
|
|
189
|
+
const rolling = rollingRealizedVolatility(history, { windows: [window] });
|
|
190
|
+
const series = rolling.flatMap((point) => {
|
|
191
|
+
const value = point.values[window];
|
|
192
|
+
return value == null ? [] : [[barDay(point.date), value] as const];
|
|
193
|
+
});
|
|
194
|
+
const newest = history.reduce<string | null>((latest, point) => {
|
|
195
|
+
const date = barDay(point.date);
|
|
196
|
+
return latest == null || date > latest ? date : latest;
|
|
197
|
+
}, null);
|
|
198
|
+
return { value: rolling.at(-1)?.values[window] ?? null, date: newest, series };
|
|
199
|
+
}
|
|
200
|
+
|
|
158
201
|
/**
|
|
159
202
|
* HV for a screen's symbols (`loadRealizedVolatilities`), started at once,
|
|
160
203
|
* and what became of the ones its rows have no reading for, at most
|
|
@@ -166,11 +209,11 @@ const MAX_FATE_LOOKUPS = 10;
|
|
|
166
209
|
*/
|
|
167
210
|
export async function loadScreenVolatilities(
|
|
168
211
|
instruments: readonly InstrumentRef[], rows: Promise<readonly IvScreenRow[]>, window: number,
|
|
169
|
-
options: { signal?: AbortSignal; onValue?: (symbol: string, value:
|
|
212
|
+
options: { signal?: AbortSignal; onValue?: (symbol: string, value: ScreenHv | null) => void; onFates?: (fates: ReadonlyMap<string, ListingFate>) => void } = {},
|
|
170
213
|
dependencies: HvDependencies = createHvDependencies(),
|
|
171
|
-
): Promise<{ hv: Map<string,
|
|
214
|
+
): Promise<{ hv: Map<string, ScreenHv | null>; fates: Map<string, ListingFate> }> {
|
|
172
215
|
const moved = new Set<string>();
|
|
173
|
-
const onValue = options.onValue && ((symbol: string, value:
|
|
216
|
+
const onValue = options.onValue && ((symbol: string, value: ScreenHv | null) => { if (!moved.has(symbol)) options.onValue!(symbol, value); });
|
|
174
217
|
const [hv, fates] = await Promise.all([
|
|
175
218
|
loadRealizedVolatilities(instruments, window, { ...options, onValue }, dependencies),
|
|
176
219
|
rows.then(async (screened) => {
|
|
@@ -189,20 +232,18 @@ export async function loadScreenVolatilities(
|
|
|
189
232
|
|
|
190
233
|
/** Close-to-close HV over one window for each symbol from one year of daily closes, four at a time. */
|
|
191
234
|
async function loadRealizedVolatilities(
|
|
192
|
-
instruments: readonly InstrumentRef[], window: number, options: { signal?: AbortSignal; onValue?: (symbol: string, value:
|
|
235
|
+
instruments: readonly InstrumentRef[], window: number, options: { signal?: AbortSignal; onValue?: (symbol: string, value: ScreenHv | null) => void } = {},
|
|
193
236
|
dependencies: HvDependencies = createHvDependencies(),
|
|
194
|
-
): Promise<Map<string,
|
|
195
|
-
const result = new Map<string,
|
|
237
|
+
): Promise<Map<string, ScreenHv | null>> {
|
|
238
|
+
const result = new Map<string, ScreenHv | null>();
|
|
196
239
|
let next = 0;
|
|
197
240
|
const worker = async () => {
|
|
198
241
|
while (next < instruments.length && !options.signal?.aborted) {
|
|
199
242
|
const instrument = instruments[next++]!;
|
|
200
|
-
let value:
|
|
243
|
+
let value: ScreenHv | null = null;
|
|
201
244
|
try {
|
|
202
245
|
const entry = await dependencies.loadChart({ instrument, bufferRange: "1Y", granularity: "resolution", resolution: "1d" });
|
|
203
|
-
|
|
204
|
-
// A provider that fell back to weekly or intraday bars would misstate daily HV.
|
|
205
|
-
value = realizedVolatilityCadenceIssue(history) ? null : realizedVolatility(history, window);
|
|
246
|
+
value = screenHv(resolveEntryValue(entry) ?? [], window);
|
|
206
247
|
} catch { value = null; }
|
|
207
248
|
result.set(instrument.symbol, value);
|
|
208
249
|
options.onValue?.(instrument.symbol, value);
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
import type { IvMethod } from "./client";
|
|
2
|
-
import type
|
|
1
|
+
import type { IvCloseBasis, IvMethod } from "./client";
|
|
2
|
+
import { readingSource, type IvStatRow, type IvStatUnit, type RichCheap, type RichCheapDates } from "./model";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* VCA's sign conventions, and what VIX is beside an at-the-money IV30, one
|
|
6
|
-
* line each: under the table, in the shot and in the report's notes. The
|
|
7
|
-
* is the stored 25-delta
|
|
8
|
-
* the latest quote capture
|
|
6
|
+
* line each: under the table, in the shot and in the report's notes. The risk
|
|
7
|
+
* reversal is the stored 25-delta call and put IVs at a 30-day constant
|
|
8
|
+
* maturity, from the latest quote capture, signed as OVDV signs it; both
|
|
9
|
+
* differences are decimal IV shown in points. Its line is there only while a
|
|
10
|
+
* risk reversal column is.
|
|
9
11
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
export function vcaDefinitions({ skew }: { skew: boolean }): string[] {
|
|
13
|
+
return [
|
|
14
|
+
...(skew ? ["25D RR: 30-day 25-delta call IV minus 25-delta put IV, in vol points, as OVDV's risk reversal; positive means calls are richer."] : []),
|
|
15
|
+
"30-90: IV30 minus IV90. IV/HV: IV30 over the HV20 of the same date.",
|
|
16
|
+
"VIX is Cboe's 30-day S&P 500 implied vol from the whole SPX strike strip, skew included; IV30 here is at the money, so VIX usually reads higher.",
|
|
17
|
+
];
|
|
18
|
+
}
|
|
14
19
|
|
|
15
20
|
/** Words for implied volatility that are no function's code: `fn IVOL` names HIVG and OVDV, which list them as `suggestFor`. */
|
|
16
21
|
export const IMPLIED_VOL_WORDS = ["IVOL", "IV", "OVOL", "IMPVOL", "IMPLIEDVOL"];
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
|
-
* A skew column's header names its sign
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* A skew column's header names its sign. The risk reversal is call minus put
|
|
25
|
+
* wherever it is shown (OVDV, VCA, HIVG's rr25d), so positive means calls are
|
|
26
|
+
* richer; HIVG's skew25d keeps the put-minus-call sign it was first exported
|
|
27
|
+
* with, the negative of that. "rr25d call-put (pts)": the stem a script keys
|
|
28
|
+
* on, the sign, and vol points in the unit's usual place, as in "IV30 (%)".
|
|
29
|
+
* No comma, so a CSV header needs no quoting.
|
|
24
30
|
*/
|
|
25
31
|
export const skewHeader = (stem: string, sign: string) => `${stem} ${sign} (pts)`;
|
|
26
32
|
|
|
@@ -43,22 +49,29 @@ export function formatStat(value: number | null | undefined, unit: IvStatUnit):
|
|
|
43
49
|
/**
|
|
44
50
|
* How a stored reading was taken. A quote capture is the option quote mids
|
|
45
51
|
* from the minutes before the close, taken once a session and never updated:
|
|
46
|
-
* it is not a live feed, so it is not called live.
|
|
52
|
+
* it is not a live feed, so it is not called live. A close solved from quote
|
|
53
|
+
* midpoints, where its prints were off the market, says so.
|
|
54
|
+
*/
|
|
55
|
+
export function statSource(row: Pick<IvStatRow, "method"> & { basis?: IvCloseBasis | null }): string {
|
|
56
|
+
return row.method === "prices" ? "closes" : row.method ? readingSource(row.method, row.basis) : "";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* "2026-10-09 quote": the session a reading belongs to and how it was taken.
|
|
60
|
+
* A stale reading says so instead, "2026-05-18 close, stale", and in the short
|
|
61
|
+
* form "05-18 stale".
|
|
47
62
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
export function readingLabel(date: string, method: IvMethod | null | undefined, short = false,
|
|
64
|
+
extra: { basis?: IvCloseBasis | null; stale?: boolean } = {}): string {
|
|
65
|
+
if (extra.stale) return short ? `${shortDate(date)} stale` : `${date} ${readingSource(method, extra.basis)}, stale`;
|
|
66
|
+
return `${short ? shortDate(date) : date} ${readingSource(method, extra.basis)}`;
|
|
51
67
|
}
|
|
52
|
-
/** "2026-10-09 quote": the session a reading belongs to and how it was taken. */
|
|
53
|
-
export const readingLabel = (date: string, method: IvMethod | null | undefined, short = false) =>
|
|
54
|
-
`${short ? shortDate(date) : date} ${readingSource(method)}`;
|
|
55
68
|
/**
|
|
56
69
|
* The dates every row agrees on, said once: the reading's, and the trade close
|
|
57
70
|
* its rank is measured on. `short` is for a footer with little room: month and
|
|
58
71
|
* day, and no window.
|
|
59
72
|
*/
|
|
60
73
|
export function sharedDates({ reading, rank }: Pick<RichCheapDates, "reading" | "rank">, short = false): string[] {
|
|
61
|
-
if (reading?.method === "trade-close" && reading.date === rank) return [readingLabel(reading.date, reading.method, short)];
|
|
62
|
-
return [reading && `IV30 ${readingLabel(reading.date, reading.method, short)}`,
|
|
74
|
+
if (reading?.method === "trade-close" && reading.date === rank) return [readingLabel(reading.date, reading.method, short, { basis: reading.basis })];
|
|
75
|
+
return [reading && `IV30 ${readingLabel(reading.date, reading.method, short, { basis: reading.basis })}`,
|
|
63
76
|
rank && (short ? `IVR/IVP ${shortDate(rank)} close` : `IVR/IVP vs 52 weeks to ${rank} close`)].filter((part): part is string => !!part);
|
|
64
77
|
}
|
|
@@ -2,8 +2,8 @@ import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
|
2
2
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
3
3
|
import { createRealizedVolatilityDependencies, loadRealizedVolatilityHistory } from "../realized-vol/client";
|
|
4
4
|
import { createHvDependencies, loadIvHistory, loadIvScreen, loadScreenVolatilities } from "./client";
|
|
5
|
-
import { formatPoints, formatRank, formatSpot, formatStat, formatVol, IMPLIED_VOL_WORDS, readingLabel, sharedDates, skewHeader,
|
|
6
|
-
import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, type RichCheapRow, richCheapDates, screenCoverage, VCA_LIMIT, VCA_PRESETS } from "./model";
|
|
5
|
+
import { formatPoints, formatRank, formatSpot, formatStat, formatVol, IMPLIED_VOL_WORDS, readingLabel, sharedDates, skewHeader, statSource, vcaDefinitions, verdictLabel } from "./format";
|
|
6
|
+
import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, type RichCheapRow, richCheapDates, screenCoverage, screenStaleNote, VCA_LIMIT, VCA_PRESETS } from "./model";
|
|
7
7
|
import { vcaUniverse } from "./universe";
|
|
8
8
|
import { offsetText, releaseOffsetRows } from "./events";
|
|
9
9
|
import { loadMacroReleases } from "../macro-day/client";
|
|
@@ -16,6 +16,7 @@ import type { HeadlessPaneContext } from "../../../types/headless";
|
|
|
16
16
|
import { zonedDateKey } from "../../../utils/zoned-date-time";
|
|
17
17
|
import { listingFateNotes, listingFateReport, loadListingFate, MovedSymbolError } from "../../../tickers/listing-fate";
|
|
18
18
|
import { startTickerChangeLookup } from "../../../tickers/ticker-changes";
|
|
19
|
+
import { fxVolatilityRefusal } from "../../../market-data/options-alternatives";
|
|
19
20
|
|
|
20
21
|
const METHODOLOGY = "docs/research-data.md#implied-volatility-history";
|
|
21
22
|
const percent = (value: unknown) => typeof value === "number" ? formatVol(value) : "--";
|
|
@@ -24,6 +25,7 @@ const points = (value: unknown) => typeof value === "number" ? formatPoints(valu
|
|
|
24
25
|
export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
25
26
|
shape: "bundle", argument: { kind: "ticker", description: "US option underlying" },
|
|
26
27
|
usListingsOnly: "Implied volatility history",
|
|
28
|
+
refuseSymbol: fxVolatilityRefusal("HIVG"),
|
|
27
29
|
freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date", oldest: null },
|
|
28
30
|
describe: (args) => `HIVG ${args.symbols[0] ?? ""}`,
|
|
29
31
|
discovery: { aliases: ["HIVG"], suggestFor: IMPLIED_VOL_WORDS, screenshotReadiness: "live-dom", dataRequirements: ["Cloud stored implied volatility", "Daily price history"],
|
|
@@ -79,7 +81,7 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
79
81
|
// With nothing served, the fate is why: no "not a ticker" or missing-history errors beside it.
|
|
80
82
|
const { notes: fateNotes, errors } = listingFateReport(fate, { served },
|
|
81
83
|
[prices.error, ...model.warnings].filter((value): value is string => !!value));
|
|
82
|
-
const notes = [...fateNotes, ...[model.oneYearNote, model.skewNote].filter((note): note is string => !!note)];
|
|
84
|
+
const notes = [...fateNotes, ...[model.oneYearNote, model.skewNote, model.staleNote].filter((note): note is string => !!note)];
|
|
83
85
|
if (event && releases) {
|
|
84
86
|
const lookback = String(args.options.lookback) as IvLookback;
|
|
85
87
|
const label = MACRO_EVENT_LABELS[event];
|
|
@@ -117,18 +119,27 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
117
119
|
releaseSource: MACRO_RELEASE_SOURCES[event], unit: "decimal annualized volatility", methodology: METHODOLOGY },
|
|
118
120
|
};
|
|
119
121
|
}
|
|
122
|
+
// The text report dates a stale measure "stale"; exports keep the bare date and say it in `stale`.
|
|
123
|
+
const statColumns = [
|
|
124
|
+
{ key: "label", header: "Measure" },
|
|
125
|
+
{ key: "value", header: "Current", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
126
|
+
{ key: "date", header: "As of" }, { key: "method", header: "Method" },
|
|
127
|
+
{ key: "low", header: "52w low", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
128
|
+
{ key: "high", header: "52w high", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
129
|
+
{ key: "rank", header: "Rank", format: (value: unknown) => formatRank(value as number | null) },
|
|
130
|
+
{ key: "percentile", header: "Pctl", format: (value: unknown) => formatRank(value as number | null) },
|
|
131
|
+
{ key: "samples", header: "Sessions" },
|
|
132
|
+
];
|
|
133
|
+
const statTextColumns = statColumns.map((column) => column.key === "date"
|
|
134
|
+
? { ...column, textFormat: (value: unknown, row: unknown) => typeof value !== "string" ? "-" : (row as IvStatRow).stale ? `${value} stale` : value }
|
|
135
|
+
: column.key === "method"
|
|
136
|
+
// The wording the pane's source column uses; exports keep the raw method and carry the basis.
|
|
137
|
+
? { ...column, textFormat: (value: unknown, row: unknown) => typeof value !== "string" ? "-" : statSource(row as IvStatRow) }
|
|
138
|
+
: column);
|
|
120
139
|
return {
|
|
121
140
|
sections: [
|
|
122
|
-
{ title: "Statistics", columns: [
|
|
123
|
-
|
|
124
|
-
{ key: "value", header: "Current", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
125
|
-
{ key: "date", header: "As of" }, { key: "method", header: "Method" },
|
|
126
|
-
{ key: "low", header: "52w low", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
127
|
-
{ key: "high", header: "52w high", format: (value: unknown, row?: unknown) => formatStat(value as number | null, (row as IvStatRow).unit) },
|
|
128
|
-
{ key: "rank", header: "Rank", format: (value: unknown) => formatRank(value as number | null) },
|
|
129
|
-
{ key: "percentile", header: "Pctl", format: (value: unknown) => formatRank(value as number | null) },
|
|
130
|
-
{ key: "samples", header: "Sessions" },
|
|
131
|
-
], rows: model.stats.map((row) => ({ ...row })) },
|
|
141
|
+
{ title: "Statistics", columns: [...statColumns, { key: "basis", header: "Basis" }, { key: "stale", header: "Stale" }], textColumns: statTextColumns,
|
|
142
|
+
rows: model.stats.map((row) => ({ ...row })) },
|
|
132
143
|
// Exports always carry the skew columns, so a script's header never moves; the text table drops them when none has a value.
|
|
133
144
|
{ title: "Daily history", columns: [...historyColumns, ...skewColumns], rows,
|
|
134
145
|
...(rows.some((row) => row.skew25d != null) ? {} : { textColumns: historyColumns }) },
|
|
@@ -151,6 +162,7 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
151
162
|
freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date" },
|
|
152
163
|
argument: { kind: "symbol-list", optional: true, maximum: VCA_LIMIT, description: "US option underlyings; defaults to index and sector ETFs." },
|
|
153
164
|
usListingsOnly: "Implied volatility",
|
|
165
|
+
refuseSymbol: fxVolatilityRefusal("VCA"),
|
|
154
166
|
options: [{ key: "preset", type: "enum", values: [{ value: "etfs" }, { value: "megacaps" }], defaultValue: "etfs", description: "Preset when no symbols are given" }],
|
|
155
167
|
describe: "Volatility rich/cheap",
|
|
156
168
|
// The pane names its universe `scope`; symbols typed open their own (custom), which a preset does not replace.
|
|
@@ -180,14 +192,15 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
180
192
|
// A listing that stopped trading has its row's note, not a line saying it has no options history.
|
|
181
193
|
const coverage = screenCoverage(rows, { explained: new Set([...noted].filter(([, entry]) => entry.fate.endedOn).map(([symbol]) => symbol)) });
|
|
182
194
|
const dates = richCheapDates(rows);
|
|
183
|
-
const hasSkew = rows.some((row) => row.
|
|
195
|
+
const hasSkew = rows.some((row) => row.riskReversal != null);
|
|
184
196
|
const hasIv1y = rows.some((row) => row.iv1y != null), hasIv1yRank = rows.some((row) => row.iv1yPercentile != null);
|
|
185
197
|
const dated = (value: unknown) => typeof value === "string" ? value : "--";
|
|
186
198
|
return {
|
|
187
199
|
sections: [{ title: ["Rich/cheap", universe.label, ...sharedDates(dates)].join(" · "), columns: [
|
|
188
200
|
{ key: "symbol", header: "Symbol" }, { key: "iv30", header: "IV30", format: percent },
|
|
189
201
|
...(dates.reading ? [] : [{ key: "date", header: "As of", format: (value: unknown, row?: unknown) =>
|
|
190
|
-
typeof value === "string" ? readingLabel(value, (row as RichCheapRow).method
|
|
202
|
+
typeof value === "string" ? readingLabel(value, (row as RichCheapRow).method, false,
|
|
203
|
+
{ basis: (row as RichCheapRow).basis, stale: (row as RichCheapRow).stale }) : "--" }]),
|
|
191
204
|
// The underlying price each reading was taken at, on the date the title or the As of column gives.
|
|
192
205
|
{ key: "spot", header: "Spot", format: (value: unknown) => typeof value === "number" ? formatSpot(value) : "--" },
|
|
193
206
|
...(dates.rankApart ? [{ key: "rankDate", header: "Rank as of", format: (value: unknown) => typeof value === "string" ? `${value} close` : "--" }] : []),
|
|
@@ -197,16 +210,18 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
197
210
|
...(hasIv1y ? [{ key: "iv1y", header: "IV1Y", format: percent }] : []),
|
|
198
211
|
...(hasIv1yRank ? [{ key: "iv1yPercentile", header: "IVP1Y", format: (value: unknown) => formatRank(value as number | null) }] : []),
|
|
199
212
|
{ key: "termSlope", header: "30-90", format: (value: unknown) => formatPoints(value as number | null) },
|
|
200
|
-
...(hasSkew ? [{ key: "
|
|
201
|
-
...(hasSkew && dates.skewApart ? [{ key: "skewDate", header: "
|
|
213
|
+
...(hasSkew ? [{ key: "riskReversal", header: skewHeader("25D RR", "call-put"), format: (value: unknown) => formatPoints(value as number | null) }] : []),
|
|
214
|
+
...(hasSkew && dates.skewApart ? [{ key: "skewDate", header: "RR as of", format: dated }] : []),
|
|
202
215
|
{ key: "hv", header: "HV20", format: percent },
|
|
216
|
+
// Said only where an HV is not the one of its IV's date: a stale reading's, or one a few sessions off.
|
|
217
|
+
...(dates.hvApart ? [{ key: "hvDate", header: "HV as of", format: dated }] : []),
|
|
203
218
|
{ key: "ivHv", header: "IV/HV", format: (value: unknown) => typeof value === "number" ? value.toFixed(2) : "--" },
|
|
204
219
|
...(noted.size > 0 ? [{ key: "note", header: "Note", underRow: true }] : []),
|
|
205
220
|
], rows: rows.map((row) => ({ ...row, ...noted.has(row.symbol) ? { note: noted.get(row.symbol)!.note } : {} })) }],
|
|
206
221
|
// A symbol with no options history is final; only history still to come leaves the screen incomplete.
|
|
207
222
|
complete: !coverage.pending.length && !universe.error, unavailableSymbols: coverage.missing,
|
|
208
223
|
errors: [universe.error].filter((value): value is string => !!value),
|
|
209
|
-
notes: [...coverage.notes, ...
|
|
224
|
+
notes: [...coverage.notes, ...[screenStaleNote(rows)].filter((note): note is string => !!note), ...vcaDefinitions({ skew: hasSkew })],
|
|
210
225
|
metadata: { presets: VCA_PRESETS, methodology: METHODOLOGY },
|
|
211
226
|
};
|
|
212
227
|
},
|
|
@@ -32,13 +32,13 @@ export const ivHistoryModule: PluginModule = {
|
|
|
32
32
|
{ ...createTickerSurfacePaneTemplate({
|
|
33
33
|
id: "iv-history-pane", paneId: "iv-history", label: "Implied Volatility History",
|
|
34
34
|
description: "30-day, 90-day and one-year ATM implied volatility since 2024 against realized, with IV rank and percentile.",
|
|
35
|
-
keywords: ["hivg", "implied", "volatility", "history", "iv rank", "ivr", "percentile", "vrp", "iv1y", "leaps"], shortcut: "HIVG", publicShare: true,
|
|
35
|
+
keywords: ["hivg", "implied", "volatility", "vol", "history", "iv rank", "ivr", "percentile", "vrp", "iv1y", "leaps"], shortcut: "HIVG", publicShare: true,
|
|
36
36
|
}), headless: ivHistoryHeadless },
|
|
37
37
|
{
|
|
38
38
|
id: "iv-screen-pane", paneId: "iv-screen", label: "Volatility Rich/Cheap",
|
|
39
39
|
description: "IV rank, percentile, one-year IV, term slope, skew and IV/HV across a list, ranked rich to cheap.",
|
|
40
40
|
// Where a put seller finds rich premium; CSP and OSA own the exact put-selling phrases and rank first on them.
|
|
41
|
-
keywords: ["vca", "rich", "cheap", "iv rank", "implied volatility", "screen", "cash-secured puts", "selling puts", "short-put candidates",
|
|
41
|
+
keywords: ["vca", "vol", "rich", "cheap", "iv rank", "implied volatility", "screen", "cash-secured puts", "selling puts", "short-put candidates",
|
|
42
42
|
"csp candidates", "wheel candidates"],
|
|
43
43
|
shortcut: { prefix: "VCA", argKind: "ticker-list" as const, argOptional: true }, headless: ivScreenHeadless,
|
|
44
44
|
createInstance: (_context, options) => ({
|