@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
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../../../components";
|
|
24
24
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
25
25
|
import {
|
|
26
|
+
useAssetData,
|
|
26
27
|
useDebouncedPluginPaneState,
|
|
27
28
|
usePaneSettingValue,
|
|
28
29
|
usePaneTitle,
|
|
@@ -34,6 +35,7 @@ import type { PaneProps } from "../../../types/plugin";
|
|
|
34
35
|
import { Box, type ScrollBoxRenderable } from "../../../ui";
|
|
35
36
|
import { isPlainArrowUp, stopSearchFocusNavigation } from "../../../utils/search-focus-navigation";
|
|
36
37
|
import { toneColor, withoutQuietColumns } from "../shared/research-cells";
|
|
38
|
+
import { looksLikeTicker, useTickerCompanySearch } from "../shared/ticker-company";
|
|
37
39
|
import { OpenFdaClient, parseOpenFdaDate } from "./client";
|
|
38
40
|
import {
|
|
39
41
|
activeFilters,
|
|
@@ -41,6 +43,7 @@ import {
|
|
|
41
43
|
FILTER_DEFAULT,
|
|
42
44
|
recordColumns,
|
|
43
45
|
recordTone,
|
|
46
|
+
termsCellText,
|
|
44
47
|
type ColumnId,
|
|
45
48
|
type FilterKey,
|
|
46
49
|
type FilterState,
|
|
@@ -75,6 +78,16 @@ const formatDate = (date: Date | null): string => (date ? date.toISOString().sli
|
|
|
75
78
|
|
|
76
79
|
const capitalize = (text: string): string => text.charAt(0).toUpperCase() + text.slice(1);
|
|
77
80
|
|
|
81
|
+
/** A search written as a ticker is, in capitals (`AZN`, `BRK.B`); `cat` or `azn` is a word to search for. */
|
|
82
|
+
const writtenAsTicker = (term: string): boolean => looksLikeTicker(term) && term === term.toUpperCase();
|
|
83
|
+
|
|
84
|
+
/** What a search that matched nothing points to, by what each dataset is searched on. */
|
|
85
|
+
const EMPTY_SEARCH_HINTS: Record<OpenFdaDataset, string> = {
|
|
86
|
+
drug: "Try the drug's brand or generic name…",
|
|
87
|
+
device: "Try the device or manufacturer name…",
|
|
88
|
+
recall: "Try the firm or product name…",
|
|
89
|
+
};
|
|
90
|
+
|
|
78
91
|
/** A generic's record lists every labeler; the cell counts them instead of naming one. */
|
|
79
92
|
function labelerText(record: DrugReport): string | null {
|
|
80
93
|
if (record.manufacturer) return record.manufacturer;
|
|
@@ -95,13 +108,19 @@ function cellFor(record: OpenFdaRecord, column: ColumnId, colors: ThemeColors):
|
|
|
95
108
|
return text ? { text, color: record.manufacturer ? colors.text : colors.textMuted } : missing;
|
|
96
109
|
}
|
|
97
110
|
if (column === "role") return toned(record.role ? capitalize(record.role) : null);
|
|
98
|
-
|
|
111
|
+
// The row names the first few; its export, like the record, keeps every one.
|
|
112
|
+
if (column === "reactions") {
|
|
113
|
+
return record.reactions.length > 0 ? { text: termsCellText(record.reactions), value: record.reactions.join(", "), color: colors.text } : missing;
|
|
114
|
+
}
|
|
99
115
|
if (column === "outcome") return toned(record.outcome);
|
|
100
116
|
break;
|
|
101
117
|
case "device":
|
|
102
118
|
if (column === "product") return { text: record.product, color: colors.textBright };
|
|
103
119
|
if (column === "manufacturer") return record.manufacturer ? { text: record.manufacturer, color: colors.text } : missing;
|
|
104
|
-
if (column === "problems")
|
|
120
|
+
if (column === "problems") {
|
|
121
|
+
return record.productProblems.length > 0
|
|
122
|
+
? { text: termsCellText(record.productProblems), value: record.productProblems.join(", "), color: colors.text } : missing;
|
|
123
|
+
}
|
|
105
124
|
if (column === "event") return toned(record.eventType);
|
|
106
125
|
break;
|
|
107
126
|
case "recall":
|
|
@@ -201,6 +220,7 @@ const isDataset = (value: unknown): value is OpenFdaDataset => DATASETS.some((en
|
|
|
201
220
|
|
|
202
221
|
export function OpenFdaPane({ width, height, focused }: PaneProps) {
|
|
203
222
|
const client = useMemo(() => new OpenFdaClient(), []);
|
|
223
|
+
const dataProvider = useAssetData();
|
|
204
224
|
const [storedQuery] = usePaneSettingValue("query", "");
|
|
205
225
|
const initialQuery = String(storedQuery ?? "").trim();
|
|
206
226
|
const [query, setQuery] = usePluginPaneState("query", initialQuery);
|
|
@@ -278,9 +298,25 @@ export function OpenFdaPane({ width, height, focused }: PaneProps) {
|
|
|
278
298
|
setOpenItemId(null);
|
|
279
299
|
}, [setFilterState, setOpenItemId, setSelectedId]);
|
|
280
300
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
301
|
+
// `FDA AZN`: FDA records name the firm and its products, never a symbol, so a
|
|
302
|
+
// ticker that finds nothing is searched as its company's name (AstraZeneca),
|
|
303
|
+
// as TRIAL reads one, and the search box then shows it. Only a term written in
|
|
304
|
+
// capitals, as a ticker is (the search box or `fn FDA AZN`): a word such as
|
|
305
|
+
// "cat" stays the search it is. Each term is looked up once.
|
|
306
|
+
const companySearch = useTickerCompanySearch(dataProvider, term, {
|
|
307
|
+
eligible: records.status === "loaded" && rows.length === 0 && !records.error && writtenAsTicker(term),
|
|
308
|
+
key: term,
|
|
309
|
+
onCompany: updateQuery,
|
|
310
|
+
});
|
|
311
|
+
const retryCompanySearch = companySearch.retry;
|
|
312
|
+
|
|
313
|
+
const loading = (records.loading && rows.length === 0) || companySearch.resolving;
|
|
314
|
+
const error = records.error?.message ?? companySearch.error;
|
|
315
|
+
const reload = useCallback(() => {
|
|
316
|
+
retryCompanySearch();
|
|
317
|
+
records.reload();
|
|
318
|
+
}, [records, retryCompanySearch]);
|
|
319
|
+
usePaneRefreshKey(reload, { focused, enabled: !searchFocused && !openItemId });
|
|
284
320
|
|
|
285
321
|
const linkRecord = openRecord ?? selected;
|
|
286
322
|
const info = useMemo<PaneFooterSegment[]>(() => [
|
|
@@ -420,7 +456,7 @@ export function OpenFdaPane({ width, height, focused }: PaneProps) {
|
|
|
420
456
|
resetScrollKey={`${dataset}:${term}:${filtersKey}`}
|
|
421
457
|
selectedTextOverridesCellColor
|
|
422
458
|
emptyStateTitle={term ? `No ${datasetLabel.toLowerCase()} match ${term}.` : `No ${datasetLabel.toLowerCase()}.`}
|
|
423
|
-
emptyStateHint="Press / to search…"
|
|
459
|
+
emptyStateHint={term ? EMPTY_SEARCH_HINTS[dataset] : "Press / to search…"}
|
|
424
460
|
/>
|
|
425
461
|
);
|
|
426
462
|
}
|
|
@@ -51,8 +51,26 @@ export interface ChainVolatilities {
|
|
|
51
51
|
/** Valuation instant the volatilities were solved at, reused for the Greeks. */
|
|
52
52
|
valuationTime: number;
|
|
53
53
|
byStrike: ReadonlyMap<number, number>;
|
|
54
|
+
/**
|
|
55
|
+
* Strikes in `byStrike` whose volatility was not solved: with no two-sided
|
|
56
|
+
* out-of-the-money quote to solve from, it is read off the neighbouring solved
|
|
57
|
+
* strikes (flat beyond the last of them). Shown marked, never as a solved figure.
|
|
58
|
+
*/
|
|
59
|
+
filled: ReadonlySet<number>;
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
/**
|
|
63
|
+
* How a chain's IV is solved, said under the chain in a report and in the pane's
|
|
64
|
+
* footer while a strike's IV is filled: marked "~", it is read off the
|
|
65
|
+
* neighbouring solved strikes, not solved from a quote.
|
|
66
|
+
*/
|
|
67
|
+
export const IV_FILLED_NOTE = "~ IV filled from neighbouring strikes";
|
|
68
|
+
export const IV_FILLED_SHORT = "~ IV filled";
|
|
69
|
+
export const IV_FILLED_DETAIL = "Each strike's IV is solved from the midpoint of its out-of-the-money option. A strike with no two-sided quote there "
|
|
70
|
+
+ "has its IV read off the nearest solved strikes, flat beyond the last one, and its cell leads with ~.";
|
|
71
|
+
/** The report's line on how IV was solved, with what "~" marks when a strike is filled. */
|
|
72
|
+
export const ivSolvedNote = (anyFilled: boolean) => `IV solved from quote mids${anyFilled ? ", ~ filled from neighbouring strikes" : ""}`;
|
|
73
|
+
|
|
56
74
|
function tightestQuotes(contracts: readonly OptionContract[], expiration: number): Map<number, OptionContract> {
|
|
57
75
|
const quotes = new Map<number, OptionContract>();
|
|
58
76
|
for (const contract of contracts) {
|
|
@@ -78,6 +96,7 @@ export function solveChainVolatilities(
|
|
|
78
96
|
): ChainVolatilities {
|
|
79
97
|
const valuationTime = optionQuoteValuationTime(chain, now);
|
|
80
98
|
const byStrike = new Map<number, number>();
|
|
99
|
+
const filled = new Set<number>();
|
|
81
100
|
// A venue that marks every contract (Deribit) states its volatility: the out-of-the-money side's at each strike.
|
|
82
101
|
if ([...chain.calls, ...chain.puts].some((contract) => contract.mark)) {
|
|
83
102
|
for (const [side, contracts] of [["call", chain.calls], ["put", chain.puts]] as const) {
|
|
@@ -88,9 +107,9 @@ export function solveChainVolatilities(
|
|
|
88
107
|
if (outOfTheMoney || !byStrike.has(contract.strike)) byStrike.set(contract.strike, mark.iv);
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
|
-
return { valuationTime, byStrike };
|
|
110
|
+
return { valuationTime, byStrike, filled };
|
|
92
111
|
}
|
|
93
|
-
if (!positive(spot)) return { valuationTime, byStrike };
|
|
112
|
+
if (!positive(spot)) return { valuationTime, byStrike, filled };
|
|
94
113
|
const rate = optionChainRate(chain);
|
|
95
114
|
const carry = Number.isFinite(dividendYield) ? dividendYield! : 0;
|
|
96
115
|
const expirations = new Set([...chain.calls, ...chain.puts].map((contract) => contract.expiration));
|
|
@@ -115,7 +134,8 @@ export function solveChainVolatilities(
|
|
|
115
134
|
? solve("call", calls.get(strike)) : solve("put", puts.get(strike)));
|
|
116
135
|
// Without a two-sided out-of-the-money quote a strike's time value sits inside
|
|
117
136
|
// the tick, and the in-the-money quote is intrinsic plus spread noise. Read its
|
|
118
|
-
// volatility off the neighbouring solved strikes so delta stays monotonic
|
|
137
|
+
// volatility off the neighbouring solved strikes so delta stays monotonic, and
|
|
138
|
+
// say it was filled: it is no market's volatility.
|
|
119
139
|
strikes.forEach((strike, index) => {
|
|
120
140
|
let volatility = solved[index];
|
|
121
141
|
if (volatility == null) {
|
|
@@ -128,10 +148,14 @@ export function solveChainVolatilities(
|
|
|
128
148
|
? low + (high - low) * (strike - strikes[below]!) / (strikes[above]! - strikes[below]!)
|
|
129
149
|
: low ?? high;
|
|
130
150
|
}
|
|
131
|
-
if (volatility != null)
|
|
151
|
+
if (volatility != null) {
|
|
152
|
+
byStrike.set(strike, volatility);
|
|
153
|
+
if (solved[index] == null) filled.add(strike);
|
|
154
|
+
else filled.delete(strike);
|
|
155
|
+
}
|
|
132
156
|
});
|
|
133
157
|
}
|
|
134
|
-
return { valuationTime, byStrike };
|
|
158
|
+
return { valuationTime, byStrike, filled };
|
|
135
159
|
}
|
|
136
160
|
|
|
137
161
|
function atmImpliedVolatility(volatilities: ChainVolatilities, spot: number | undefined): number | null {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { createTtlCache, type TtlCache } from "../../../data/ttl-cache";
|
|
2
3
|
import type { InstrumentRef } from "../../../market-data/request-types";
|
|
3
4
|
import type { QueryEntry } from "../../../market-data/result-types";
|
|
4
5
|
import { buildOptionsKey, resolveEntryData } from "../../../market-data/selectors";
|
|
@@ -15,6 +16,17 @@ const OPTIONS_LIVE_ANALYTICS_INTERVAL_MS = 1_000;
|
|
|
15
16
|
const LIVE_ANALYTICS_MAX_LOAD = 0.05;
|
|
16
17
|
/** The Treasury curve is daily; a refreshed chain reuses the one it already loaded. */
|
|
17
18
|
const TREASURY_REUSE_MS = 30 * 60_000;
|
|
19
|
+
const TREASURY_CURVE = "treasury";
|
|
20
|
+
|
|
21
|
+
interface Selection {
|
|
22
|
+
key: string | null;
|
|
23
|
+
cache: OptionsEnrichmentCache;
|
|
24
|
+
curve: TtlCache<YieldPoint[]>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const newSelection = (key: string | null): Selection => ({
|
|
28
|
+
key, cache: {}, curve: createTtlCache<YieldPoint[]>({ ttlMs: TREASURY_REUSE_MS }),
|
|
29
|
+
});
|
|
18
30
|
|
|
19
31
|
interface EnrichmentResult {
|
|
20
32
|
key: string;
|
|
@@ -59,9 +71,9 @@ export function useOptionsEnrichment(input: {
|
|
|
59
71
|
const current = useRef(input);
|
|
60
72
|
current.current = input;
|
|
61
73
|
// Per selection: the adjacent model and the Treasury curve outlive a chain refresh.
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
if (store.current.key !== key) store.current =
|
|
74
|
+
const [first] = useState(() => newSelection(key));
|
|
75
|
+
const store = useRef(first);
|
|
76
|
+
if (store.current.key !== key) store.current = newSelection(key);
|
|
65
77
|
const [result, setResult] = useState<EnrichmentResult | null>(null);
|
|
66
78
|
const resultRef = useRef(result);
|
|
67
79
|
resultRef.current = result;
|
|
@@ -71,7 +83,7 @@ export function useOptionsEnrichment(input: {
|
|
|
71
83
|
const request = current.current;
|
|
72
84
|
const identity = { key, entryKey, catalogueKey };
|
|
73
85
|
const selection = store.current;
|
|
74
|
-
const curve = selection.curve
|
|
86
|
+
const curve = selection.curve.peek(TREASURY_CURVE);
|
|
75
87
|
let projection: OptionsEnrichmentProjection | null = null;
|
|
76
88
|
// A refresh of the same expiry swaps in its analytics complete, never
|
|
77
89
|
// through a partial projection that would blank the strip for a moment.
|
|
@@ -93,7 +105,7 @@ export function useOptionsEnrichment(input: {
|
|
|
93
105
|
}).then((snapshot) => {
|
|
94
106
|
if (controller.signal.aborted) return;
|
|
95
107
|
if (projection && !projection.curveLoading && !projection.treasuryError && projection.curve?.length && !curve) {
|
|
96
|
-
selection.curve
|
|
108
|
+
selection.curve.set(TREASURY_CURVE, [...projection.curve]);
|
|
97
109
|
}
|
|
98
110
|
setResult({ ...identity, loading: false, carried: false, snapshot, projection, error: null });
|
|
99
111
|
}).catch((error: unknown) => {
|
|
@@ -163,10 +163,13 @@ export function findNearestStrikeIndex(strikes: number[], targetStrike: number):
|
|
|
163
163
|
return bestIndex;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
export function formatIv(value: number | undefined): string {
|
|
166
|
+
export function formatIv(value: number | undefined, filled = false): string {
|
|
167
167
|
// Providers send 0 for contracts with no implied volatility; "0.0%" would read
|
|
168
168
|
// as a real quote of zero vol.
|
|
169
169
|
if (value == null || !Number.isFinite(value) || value <= 0) return "\u2014";
|
|
170
|
+
// A filled figure leads with "~": it was read off the neighbouring strikes, not solved from a quote.
|
|
171
|
+
// Without the decimal it still fits the column at 100% and over.
|
|
172
|
+
if (filled) return value >= 0.9995 ? `~${Math.round(value * 100)}%` : `~${(value * 100).toFixed(1)}%`;
|
|
170
173
|
return `${(value * 100).toFixed(1)}%`;
|
|
171
174
|
}
|
|
172
175
|
|
|
@@ -305,7 +308,7 @@ function formatOptionContractCell(
|
|
|
305
308
|
case "openInterest":
|
|
306
309
|
return formatCompact(contract.openInterest, { fixedDecimals: true });
|
|
307
310
|
case "iv":
|
|
308
|
-
return formatIv(row.impliedVolatility);
|
|
311
|
+
return formatIv(row.impliedVolatility, row.impliedVolatilityFilled);
|
|
309
312
|
case "delta":
|
|
310
313
|
return formatGreek(greeks?.delta);
|
|
311
314
|
case "gamma":
|
|
@@ -32,6 +32,8 @@ export interface OptionTableRow {
|
|
|
32
32
|
put?: OptionContract;
|
|
33
33
|
/** Solved from quote midpoints; the call and put at a strike share it. */
|
|
34
34
|
impliedVolatility?: number;
|
|
35
|
+
/** `impliedVolatility` was filled from neighbouring strikes, not solved from a quote (see `ChainVolatilities.filled`). */
|
|
36
|
+
impliedVolatilityFilled?: boolean;
|
|
35
37
|
callGreeks?: OptionValuation;
|
|
36
38
|
putGreeks?: OptionValuation;
|
|
37
39
|
/** Time value as a share of spot per year (see `carry.ts`); null when it cannot be read. */
|
|
@@ -44,7 +44,7 @@ import type { OptionContract } from "../../../types/financials";
|
|
|
44
44
|
import { OPTIONS_CALCULATOR_TEMPLATE_ID } from "../options-calculator/model";
|
|
45
45
|
import { buildChainCalcParams, resolveCalcSide } from "./calc-seed";
|
|
46
46
|
import { useOptionsCatalogue } from "./expiry-catalogue";
|
|
47
|
-
import { calculateOptionGreeks, calculateOptionsSummary, solveChainVolatilities, type OptionsSummary } from "./analytics";
|
|
47
|
+
import { calculateOptionGreeks, calculateOptionsSummary, IV_FILLED_DETAIL, IV_FILLED_NOTE, IV_FILLED_SHORT, solveChainVolatilities, type OptionsSummary } from "./analytics";
|
|
48
48
|
import {
|
|
49
49
|
DEFAULT_OPTION_FIELD_IDS,
|
|
50
50
|
buildStrikeList,
|
|
@@ -578,6 +578,7 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
578
578
|
call,
|
|
579
579
|
put,
|
|
580
580
|
impliedVolatility: volatilities?.byStrike.get(strike),
|
|
581
|
+
impliedVolatilityFilled: volatilities?.filled.has(strike) ?? false,
|
|
581
582
|
callGreeks: volatilities ? calculateOptionGreeks(call, "call", greeksSpot, dividendYield, volatilities) : undefined,
|
|
582
583
|
putGreeks: volatilities ? calculateOptionGreeks(put, "put", greeksSpot, dividendYield, volatilities) : undefined,
|
|
583
584
|
callExtrinsicPerYear: call && volatilities ? optionCarry(call, "call", greeksSpot, volatilities.valuationTime)?.extrinsicPerYear ?? null : null,
|
|
@@ -622,9 +623,21 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
622
623
|
+ `${new Date(footerSpot.observedAt).toISOString().slice(11, 16)} UTC` : null,
|
|
623
624
|
strikeChain && !listedOpenInterest.loading ? openInterestAsOf?.date ? `OI ${openInterestAsOf.date.slice(5)}` : "OI undated" : null]
|
|
624
625
|
.filter(Boolean).join(" · ");
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
// How IV was solved is said while the chain shows a figure that was not: a strike with no two-sided
|
|
627
|
+
// out-of-the-money quote has its IV read off the neighbouring strikes, and its cell leads with "~".
|
|
628
|
+
const ivFilledShown = optionFieldIds.includes("iv") && rows.some((row) => row.impliedVolatilityFilled && row.impliedVolatility != null);
|
|
629
|
+
// A report of the pane (`gloom fn OMON`) says it too.
|
|
630
|
+
useRemoteUiNode(ivFilledShown ? {
|
|
631
|
+
role: "report-notice",
|
|
632
|
+
label: "IV filled",
|
|
633
|
+
getMetadata: () => ({ text: IV_FILLED_NOTE, key: "ivFilled", value: true }),
|
|
634
|
+
} : null);
|
|
635
|
+
usePaneFooter("options-basis", () => ({ info: [
|
|
636
|
+
...(basisText ? [{ id: "greeks-basis", parts: [{ text: basisText, tone: "muted" as const }],
|
|
637
|
+
variants: [[{ text: shortBasisText, tone: "muted" as const }], []] }] : []),
|
|
638
|
+
...(ivFilledShown ? [{ id: "iv-filled", parts: [{ text: IV_FILLED_NOTE, tone: "muted" as const }],
|
|
639
|
+
title: IV_FILLED_DETAIL, variants: [[{ text: IV_FILLED_SHORT, tone: "muted" as const }]] }] : []),
|
|
640
|
+
] }), [basisText, shortBasisText, ivFilledShown]);
|
|
628
641
|
// Figures that summarise the whole expiry move at most once a second, so a
|
|
629
642
|
// busy stream reads as a steady strip rather than flicker.
|
|
630
643
|
// A new expiry, a refreshed snapshot or the underlying price arriving or
|
|
@@ -42,6 +42,8 @@ export interface OpenInterestPayload {
|
|
|
42
42
|
tracked: boolean;
|
|
43
43
|
spot: number | null;
|
|
44
44
|
spotAsOf: string | null;
|
|
45
|
+
/** `close` when spot is a regular session's close at `spotAsOf`: a cash index outside its session. Absent from older servers. */
|
|
46
|
+
spotBasis?: "close";
|
|
45
47
|
delayed: boolean;
|
|
46
48
|
expiries: ExpiryOpenInterest[];
|
|
47
49
|
expiry: string | null;
|
|
@@ -82,6 +84,8 @@ export interface GammaPayload {
|
|
|
82
84
|
oiDate: string | null;
|
|
83
85
|
spot: number | null;
|
|
84
86
|
spotAsOf: string | null;
|
|
87
|
+
/** `close` when spot is a regular session's close at `spotAsOf`: a cash index outside its session. Absent from older servers. */
|
|
88
|
+
spotBasis?: "close";
|
|
85
89
|
delayed: boolean;
|
|
86
90
|
/** The one expiry measured, or null for all of them. */
|
|
87
91
|
expiry: string | null;
|
|
@@ -99,7 +103,14 @@ export interface GammaPayload {
|
|
|
99
103
|
strikes: GammaStrike[];
|
|
100
104
|
/** Each priced expiry's totals, from the same legs as `total`; absent from older servers. */
|
|
101
105
|
byExpiry?: GammaExpiry[];
|
|
106
|
+
/** When the quotes behind the volatilities were taken: the expiry's own, or the newest priced one's for all. */
|
|
102
107
|
quotesAsOf: string | null;
|
|
108
|
+
/**
|
|
109
|
+
* The instant gamma is evaluated at, which time to expiry runs from: now
|
|
110
|
+
* while the quotes are current, the spot's time with a closed session's
|
|
111
|
+
* quotes (before the open). Absent from older servers.
|
|
112
|
+
*/
|
|
113
|
+
gammaAsOf?: string | null;
|
|
103
114
|
warnings: string[];
|
|
104
115
|
/** The venue listing the options, when it is not the US exchanges: Deribit. */
|
|
105
116
|
venue?: string;
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
formatCountChange,
|
|
19
19
|
formatDistance,
|
|
20
20
|
formatGamma,
|
|
21
|
+
formatGammaTimes,
|
|
21
22
|
formatLevel,
|
|
22
23
|
formatPositioningSpot,
|
|
23
24
|
formatPayout,
|
|
@@ -44,15 +45,19 @@ const wall = (value: number | null, spot: number | null) =>
|
|
|
44
45
|
value == null ? "--" : `${formatStrikeLabel(value)}, ${formatDistance(value, spot)} vs spot`;
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
|
-
* What the report opens with: spot
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* What the report opens with: spot, the instant gamma is evaluated at and
|
|
49
|
+
* when the quotes behind its volatilities were taken, apart from the open
|
|
50
|
+
* interest session, then the figures the GEX tab shows. Exports keep each
|
|
51
|
+
* figure whole; the text says them as the pane does.
|
|
50
52
|
*/
|
|
51
53
|
function summary(openInterest: OpenInterestPayload, dealer: GammaPayload | null): { entries: HeadlessPaneEntry[]; textEntries: HeadlessPaneEntry[] } {
|
|
52
54
|
// Gamma is priced at its own read of spot; the figures and their distances use that one.
|
|
53
55
|
const spot = dealer?.spot ?? openInterest.spot;
|
|
54
56
|
const spotAsOf = dealer ? dealer.spotAsOf : openInterest.spotAsOf;
|
|
57
|
+
const spotBasis = dealer ? dealer.spotBasis : openInterest.spotBasis;
|
|
55
58
|
const venue = openInterest.venue;
|
|
59
|
+
// Older servers send no gamma time: their quote time stands alone, as it did.
|
|
60
|
+
const gammaTimes = dealer ? formatGammaTimes(dealer.gammaAsOf, dealer.quotesAsOf) : null;
|
|
56
61
|
// A venue that publishes open interest live (Deribit) dates it to the read, not to a session.
|
|
57
62
|
const oiAsOf = venue && openInterest.oiAsOf ? openInterest.oiAsOf : null;
|
|
58
63
|
const contract = openInterest.contract;
|
|
@@ -61,15 +66,20 @@ function summary(openInterest: OpenInterestPayload, dealer: GammaPayload | null)
|
|
|
61
66
|
...(venue ? [{ key: "venue", label: "Venue", value: venue }] : []),
|
|
62
67
|
{ key: "spot", label: "Spot", value: spot },
|
|
63
68
|
{ key: "spotAsOf", label: "Spot as of", value: spotAsOf },
|
|
64
|
-
|
|
69
|
+
// An index outside its session: spot is the close, spotAsOf its time.
|
|
70
|
+
...(spotBasis ? [{ key: "spotBasis", label: "Spot basis", value: spotBasis }] : []),
|
|
71
|
+
...(dealer && gammaTimes ? [{ key: "gammaAsOf", label: "Gamma at", value: dealer.gammaAsOf ?? null },
|
|
72
|
+
{ key: "quotesAsOf", label: "Vols from", value: dealer.quotesAsOf }]
|
|
73
|
+
: dealer ? [{ key: "quotesAsOf", label: "Quotes as of", value: dealer.quotesAsOf }] : []),
|
|
65
74
|
{ key: "oiDate", label: "Open interest as of", value: oiAsOf ?? openInterest.oiDate },
|
|
66
75
|
...(contract ? [{ key: "contract", label: "Contract", value: contract }] : []),
|
|
67
76
|
...(settles ? [{ key: "settles", label: "Expiries settle", value: settles }] : []),
|
|
68
77
|
];
|
|
69
78
|
const textEntries: HeadlessPaneEntry[] = [
|
|
70
79
|
...(venue ? [{ label: "Venue", value: venue }] : []),
|
|
71
|
-
{ label: "Spot", value: spot, formatted: formatPositioningSpot(spot, spotAsOf, venue) },
|
|
72
|
-
...(dealer ? [{ label: "
|
|
80
|
+
{ label: "Spot", value: spot, formatted: formatPositioningSpot(spot, spotAsOf, venue, spotBasis) },
|
|
81
|
+
...(dealer && gammaTimes ? [{ label: "Gamma at", value: dealer.gammaAsOf ?? null, formatted: gammaTimes }]
|
|
82
|
+
: dealer ? [{ label: "Quotes as of", value: dealer.quotesAsOf, formatted: time(dealer.quotesAsOf) }] : []),
|
|
73
83
|
{ label: "Open interest as of", value: oiAsOf ?? openInterest.oiDate, formatted: oiAsOf ? `${time(oiAsOf)}, live` : openInterest.oiDate ?? "--" },
|
|
74
84
|
...(contract ? [{ label: "Contract", value: contract, formatted: `${contract.size} ${contract.unit}; OI counts ${contract.unit}` }] : []),
|
|
75
85
|
...(settles ? [{ label: "Expiries settle", value: settles, formatted: `${settles}, the day each expiry names` }] : []),
|
|
@@ -227,13 +237,16 @@ export const optionsPositioningHeadless = (initialTab: PositioningTab): Headless
|
|
|
227
237
|
previousOiDate: openInterest.previousOiDate,
|
|
228
238
|
spot,
|
|
229
239
|
spotAsOf: openInterest.spotAsOf,
|
|
240
|
+
...(openInterest.spotBasis ? { spotBasis: openInterest.spotBasis } : {}),
|
|
230
241
|
delayed: openInterest.delayed,
|
|
231
242
|
...(openInterest.venue ? { venue: openInterest.venue, oiAsOf: openInterest.oiAsOf ?? null, contract: openInterest.contract ?? null } : {}),
|
|
232
243
|
...(dealer instanceof Error ? {} : {
|
|
233
|
-
gamma: { expiry: dealer.expiry ?? ALL_EXPIRIES, spot: dealer.spot, spotAsOf: dealer.spotAsOf,
|
|
244
|
+
gamma: { expiry: dealer.expiry ?? ALL_EXPIRIES, spot: dealer.spot, spotAsOf: dealer.spotAsOf,
|
|
245
|
+
...(dealer.spotBasis ? { spotBasis: dealer.spotBasis } : {}), total: dealer.total, band: dealer.band,
|
|
234
246
|
bandWithBoxes: dealer.bandWithBoxes ?? null, boxes: dealer.boxes ?? [], flip: dealer.flip,
|
|
235
247
|
callWall: walls?.callWall ?? null, putWall: walls?.putWall ?? null,
|
|
236
|
-
unit: "dollars of dealer delta per 1% move", assumption: "dealers long calls, short puts",
|
|
248
|
+
unit: "dollars of dealer delta per 1% move", assumption: "dealers long calls, short puts",
|
|
249
|
+
gammaAsOf: dealer.gammaAsOf ?? null, quotesAsOf: dealer.quotesAsOf },
|
|
237
250
|
}),
|
|
238
251
|
methodology: METHODOLOGY,
|
|
239
252
|
},
|
|
@@ -65,7 +65,7 @@ export const optionsPositioningModule: PluginModule = {
|
|
|
65
65
|
id: "options-positioning-gex",
|
|
66
66
|
label: "Dealer Gamma",
|
|
67
67
|
description: "Net dealer gamma by strike, its total, flip level, call and put walls and the range for the dealer assumption.",
|
|
68
|
-
keywords: ["gex", "gamma", "dealer gamma", "gamma exposure", "gamma flip", "call wall", "put wall"],
|
|
68
|
+
keywords: ["gex", "gamma", "dealer gamma", "gamma exposure", "gamma flip", "zero gamma", "gamma level", "call wall", "put wall", "0dte"],
|
|
69
69
|
shortcut: "GEX",
|
|
70
70
|
}, "gex"),
|
|
71
71
|
],
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { formatCompact, formatCompactAmount } from "../../../utils/format";
|
|
2
2
|
import { formatExpDate, formatStrikeLabel } from "../../../utils/options";
|
|
3
3
|
import { formatUtcTime } from "../../../utils/utc-time";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
formatModelSpotPrice,
|
|
6
|
+
formatSpotTime,
|
|
7
|
+
modelSpotLabel,
|
|
8
|
+
modelSpotStamp,
|
|
9
|
+
modelSpotStampVariants,
|
|
10
|
+
observedModelSpot,
|
|
11
|
+
type OptionModelSpot,
|
|
12
|
+
} from "../options/model-spot";
|
|
5
13
|
|
|
6
14
|
export type PositioningTab = "strikes" | "expiries" | "gex";
|
|
7
15
|
export const POSITIONING_TABS: { value: PositioningTab; label: string }[] = [
|
|
@@ -100,14 +108,27 @@ export function formatRatio(value: number | null | undefined): string {
|
|
|
100
108
|
export function formatLevel(value: number | null | undefined): string {
|
|
101
109
|
return value == null || !Number.isFinite(value) ? "--" : formatModelSpotPrice(value);
|
|
102
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The spot of a positioning answer as the options panes stamp theirs: the
|
|
113
|
+
* regular session's close when the server says so (a cash index outside its
|
|
114
|
+
* session, which has no extended hours), otherwise the session its time
|
|
115
|
+
* falls in.
|
|
116
|
+
*/
|
|
117
|
+
function positioningModelSpot(spot: number, at: number, basis?: "close"): OptionModelSpot {
|
|
118
|
+
return basis === "close"
|
|
119
|
+
? { price: spot, observedAt: at, valuationTime: at, basis: "close", session: null }
|
|
120
|
+
: observedModelSpot(spot, at);
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
/**
|
|
104
124
|
* The spot Gloom Cloud read the distances and dealer gamma at, stamped as
|
|
105
125
|
* every options pane stamps its spot (`modelSpotStamp`), as a stat cell
|
|
106
|
-
* carries it: `pre-market Wed 21 Oct 09:35 UTC`,
|
|
107
|
-
*
|
|
108
|
-
* the regular
|
|
126
|
+
* carries it: `pre-market Wed 21 Oct 09:35 UTC`, `close Tue 20 Oct` for an
|
|
127
|
+
* index outside its session, their shorter forms, then none, so the band
|
|
128
|
+
* keeps the columns it has in regular hours. Nothing for the regular
|
|
129
|
+
* session's price.
|
|
109
130
|
*/
|
|
110
|
-
export function positioningSpotDetail(spot: number | null | undefined, spotAsOf: string | null | undefined, venue?: string):
|
|
131
|
+
export function positioningSpotDetail(spot: number | null | undefined, spotAsOf: string | null | undefined, venue?: string, basis?: "close"):
|
|
111
132
|
{ detail?: string; detailVariants?: string[] } {
|
|
112
133
|
const at = spotAsOf ? Date.parse(spotAsOf) : Number.NaN;
|
|
113
134
|
if (spot == null || !Number.isFinite(at)) return {};
|
|
@@ -116,17 +137,62 @@ export function positioningSpotDetail(spot: number | null | undefined, spotAsOf:
|
|
|
116
137
|
const time = `${new Date(at).toISOString().slice(11, 16)} UTC`;
|
|
117
138
|
return { detail: `index ${time}`, detailVariants: [`index ${time}`, time, ""] };
|
|
118
139
|
}
|
|
119
|
-
const observed =
|
|
140
|
+
const observed = positioningModelSpot(spot, at, basis);
|
|
120
141
|
const stamp = modelSpotStamp(observed);
|
|
121
142
|
return stamp ? { detail: stamp, detailVariants: [...modelSpotStampVariants(observed), ""] } : {};
|
|
122
143
|
}
|
|
123
144
|
|
|
124
|
-
/** The spot as a report says it: `770.88 pre-market Wed 21 Oct 09:35 UTC`, `770.10 last Wed 21 Oct 14:05 UTC`. */
|
|
125
|
-
export function formatPositioningSpot(spot: number | null | undefined, spotAsOf: string | null | undefined, venue?: string): string {
|
|
145
|
+
/** The spot as a report says it: `770.88 pre-market Wed 21 Oct 09:35 UTC`, `770.10 last Wed 21 Oct 14:05 UTC`, `7,722.60 close Tue 27 Oct`. */
|
|
146
|
+
export function formatPositioningSpot(spot: number | null | undefined, spotAsOf: string | null | undefined, venue?: string, basis?: "close"): string {
|
|
126
147
|
const at = spotAsOf ? Date.parse(spotAsOf) : Number.NaN;
|
|
127
148
|
if (spot == null || !Number.isFinite(spot)) return "--";
|
|
128
149
|
if (venue) return Number.isFinite(at) ? `${formatLevel(spot)} ${venue} index ${formatUtcTime(new Date(at).toISOString())}` : formatLevel(spot);
|
|
129
|
-
return Number.isFinite(at) ? `${formatLevel(spot)} ${modelSpotLabel(
|
|
150
|
+
return Number.isFinite(at) ? `${formatLevel(spot)} ${modelSpotLabel(positioningModelSpot(spot, at, basis))}` : formatLevel(spot);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const instant = (value: string | null | undefined) => {
|
|
154
|
+
const at = value ? Date.parse(value) : Number.NaN;
|
|
155
|
+
return Number.isFinite(at) ? at : null;
|
|
156
|
+
};
|
|
157
|
+
/** `Wed 08:34 UTC`. */
|
|
158
|
+
const dayAndTime = (at: number) => formatSpotTime(at).replace(/^(\w{3}) \d{1,2} \w{3} /, "$1 ");
|
|
159
|
+
/** `08:34 UTC`. */
|
|
160
|
+
const timeOnly = (at: number) => `${new Date(at).toISOString().slice(11, 16)} UTC`;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* When dealer gamma is evaluated and when the quotes behind its volatilities
|
|
164
|
+
* were taken, as a report line: `Wed 28 Oct 08:34 UTC, vols from Tue 27 Oct
|
|
165
|
+
* 20:14 UTC`. Before the open that is the pre-market spot's time with the last
|
|
166
|
+
* session's volatilities. Null without a gamma time (an older server, which
|
|
167
|
+
* dates its quotes alone).
|
|
168
|
+
*/
|
|
169
|
+
export function formatGammaTimes(gammaAsOf: string | null | undefined, quotesAsOf: string | null | undefined): string | null {
|
|
170
|
+
const gamma = instant(gammaAsOf);
|
|
171
|
+
if (gamma == null) return null;
|
|
172
|
+
const quotes = instant(quotesAsOf);
|
|
173
|
+
return quotes == null ? formatSpotTime(gamma) : `${formatSpotTime(gamma)}, vols from ${formatSpotTime(quotes)}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The same as a GEX figure: `Gamma at Wed 08:34 UTC`, detail `vols from Tue
|
|
178
|
+
* 20:14 UTC` (the day left out when it is the gamma's), with shorter forms
|
|
179
|
+
* that keep the quote time. An older server's answer, with no gamma time,
|
|
180
|
+
* dates its quotes alone. Null with neither.
|
|
181
|
+
*/
|
|
182
|
+
export function gammaTimesFigure(gammaAsOf: string | null | undefined, quotesAsOf: string | null | undefined):
|
|
183
|
+
{ label: string; value: string; detail?: string; detailVariants?: string[] } | null {
|
|
184
|
+
const gamma = instant(gammaAsOf);
|
|
185
|
+
const quotes = instant(quotesAsOf);
|
|
186
|
+
if (gamma == null) return quotes == null ? null : { label: "Quotes as of", value: dayAndTime(quotes) };
|
|
187
|
+
if (quotes == null) return { label: "Gamma at", value: dayAndTime(gamma) };
|
|
188
|
+
const sameDay = new Date(gamma).toISOString().slice(0, 10) === new Date(quotes).toISOString().slice(0, 10);
|
|
189
|
+
const quoted = sameDay ? timeOnly(quotes) : dayAndTime(quotes);
|
|
190
|
+
return {
|
|
191
|
+
label: "Gamma at",
|
|
192
|
+
value: dayAndTime(gamma),
|
|
193
|
+
detail: `vols from ${quoted}`,
|
|
194
|
+
detailVariants: [`vols from ${quoted}`, `vols ${quoted}`, `vols ${quoted.replace(/ UTC$/, "")}`],
|
|
195
|
+
};
|
|
130
196
|
}
|
|
131
197
|
|
|
132
198
|
/** Where a level sits against spot: +0.6%. */
|
|
@@ -208,6 +274,9 @@ export function chartWindow<T extends { strike: number }>(
|
|
|
208
274
|
/** One full-width numeric domain the chart reads as time; strikes are never labelled as dates. */
|
|
209
275
|
const COORDINATE_SPAN = 1_000_000_000_000;
|
|
210
276
|
|
|
277
|
+
/** Where a chart date made by `toDate` sits across the plot, 0 to 1. */
|
|
278
|
+
export const axisRatioOfDate = (date: Date) => date.getTime() / COORDINATE_SPAN;
|
|
279
|
+
|
|
211
280
|
export interface NumericAxis {
|
|
212
281
|
/** Where x sits across the plot, 0 at the left edge and 1 at the right. */
|
|
213
282
|
ratio: (x: number) => number;
|