@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
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
Quote,
|
|
13
13
|
TickerFinancials,
|
|
14
14
|
} from "../../types/financials";
|
|
15
|
-
import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, quoteFormatOptions, resolveAssetDisplayKind, withCurrencyMinorDigits } from "../../market-data/market/format";
|
|
15
|
+
import { currencyPriceMinDigits, formatMarketPrice, formatMarketPriceWithCurrency, penceFigure, quoteFormatOptions, resolveAssetDisplayKind, withCurrencyMinorDigits } from "../../market-data/market/format";
|
|
16
16
|
import { INDEX_POINTS_EXPORT_UNIT, isIndexLevel, reportedQuoteVolume } from "../../market-data/market/index-level";
|
|
17
17
|
import { changeBasisJson, changeBasisLabel, changeBasisNote, quoteChangeBasis, quotePreviousCloseDate, quoteSettleText } from "../../market-data/market/change-basis";
|
|
18
18
|
import {
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
import { cliArrow, cliStyles, colorBySign, renderSection, renderStats } from "../../utils/cli-output";
|
|
42
42
|
import { filledPeriodsNote, stalePeriodNote, statementHistoryNotes } from "../../utils/financial-statements";
|
|
43
43
|
import { formatPerShareNumber } from "../../utils/reported-money";
|
|
44
|
+
import { isCurrencyPairListing } from "../../utils/fx-market-hours";
|
|
44
45
|
import {
|
|
45
46
|
analystTargetCurrency,
|
|
46
47
|
formatAnalystPrice,
|
|
@@ -58,7 +59,7 @@ import {
|
|
|
58
59
|
} from "../../market-data/options-alternatives";
|
|
59
60
|
import {
|
|
60
61
|
atReportTtmRate, cliFuturesContract, futuresContractMetrics, exportedDividendFundamentals, fundamentalsReportTables, loadDividendDistributions,
|
|
61
|
-
|
|
62
|
+
loadReportFundFacts, renderFundamentalsReport, writesCurrencyCodes,
|
|
62
63
|
} from "./ticker";
|
|
63
64
|
import { createBaseConverter } from "../base-converter";
|
|
64
65
|
import { loadStatementFilings } from "../statement-filings";
|
|
@@ -75,6 +76,7 @@ import {
|
|
|
75
76
|
loadOptionModelInputs,
|
|
76
77
|
loadOptionsChainOpenInterest,
|
|
77
78
|
chainCoinCurrency,
|
|
79
|
+
chainHasFilledIv,
|
|
78
80
|
formatCoinPremiumCell,
|
|
79
81
|
formatOptionDeltaCell,
|
|
80
82
|
formatOptionDollarCell,
|
|
@@ -90,6 +92,7 @@ import {
|
|
|
90
92
|
optionsBasisFields,
|
|
91
93
|
optionsBasisFootnote,
|
|
92
94
|
parseOptionExpiration,
|
|
95
|
+
takeExpirationOption,
|
|
93
96
|
valueChainAtModelSpot,
|
|
94
97
|
type OptionsChainBasis,
|
|
95
98
|
} from "./options-chain";
|
|
@@ -139,6 +142,7 @@ import {
|
|
|
139
142
|
historyPriceDecimals,
|
|
140
143
|
historyRows,
|
|
141
144
|
historyUnit,
|
|
145
|
+
quotedPriceDivisor,
|
|
142
146
|
historyVolume,
|
|
143
147
|
historyVolumeText,
|
|
144
148
|
type HistoryRow,
|
|
@@ -153,7 +157,7 @@ import {
|
|
|
153
157
|
symbolListNotes,
|
|
154
158
|
type HistoryListRow,
|
|
155
159
|
} from "../history-export";
|
|
156
|
-
import { isCliFailure } from "../errors";
|
|
160
|
+
import { fail, isCliFailure, NO_DATA_CODE } from "../errors";
|
|
157
161
|
import { mapWithConcurrency } from "../../utils/concurrency";
|
|
158
162
|
import { MISSING_VOLUME_TEXT } from "../../utils/price-history-integrity";
|
|
159
163
|
import { CRYPTO_BOARD_HINT, quoteNotes } from "./crypto-hints";
|
|
@@ -215,6 +219,7 @@ import {
|
|
|
215
219
|
type ResolvedEarningsEvent,
|
|
216
220
|
} from "../../plugins/builtin/shared/earnings-calendar";
|
|
217
221
|
import {
|
|
222
|
+
coinListingNotes,
|
|
218
223
|
EXCHANGE_OPTION,
|
|
219
224
|
listingHeading,
|
|
220
225
|
listingIdentity,
|
|
@@ -303,6 +308,7 @@ import {
|
|
|
303
308
|
unvaluedHoldersNote,
|
|
304
309
|
} from "../../plugins/builtin/holders/report-rows";
|
|
305
310
|
import { fetchBeneficialOwners } from "../../plugins/builtin/holders/beneficial-client";
|
|
311
|
+
import { fundHoldersAnswer } from "../../plugins/builtin/holders/fund-holders";
|
|
306
312
|
import { centralBankEventsMessage } from "../central-bank-events";
|
|
307
313
|
import { classSharesAsOf, classSharesToday } from "../../plugins/builtin/holders/client";
|
|
308
314
|
import {
|
|
@@ -465,7 +471,8 @@ const QUOTE_LEAD_COLUMNS = [
|
|
|
465
471
|
row.error && !row.price
|
|
466
472
|
// A typo is not an outage: say what is wrong with the symbol itself.
|
|
467
473
|
? isNotATickerMessage(row.error) ? cliStyles.warning("not a ticker") : cliStyles.danger("unavailable")
|
|
468
|
-
:
|
|
474
|
+
// A London line adds the pence it trades in, as the exchange and RNS write it, muted as a table cell's note is: £6.308 630.8p.
|
|
475
|
+
: `${yieldLevelCell(row.price, row)}${row.pence ? ` ${cliStyles.muted(row.pence)}` : ""}`
|
|
469
476
|
),
|
|
470
477
|
},
|
|
471
478
|
{
|
|
@@ -729,10 +736,12 @@ function quoteRows(results: QuoteCliRecord[], now = Date.now()) {
|
|
|
729
736
|
// A price in cents says what it is per, as the contract terms word it: 314.95¢/lb.
|
|
730
737
|
const per = display.price != null ? futuresPricePer(result.futuresContract, quote?.currency) : "";
|
|
731
738
|
const previousClose = display.price != null && display.change != null ? display.price - display.change : quote?.previousClose;
|
|
739
|
+
const last = price(display.price);
|
|
732
740
|
return {
|
|
733
741
|
symbol: result.target.symbol,
|
|
734
742
|
name: quote?.name || result.listing?.name || "",
|
|
735
|
-
price: `${
|
|
743
|
+
price: `${last}${per}`,
|
|
744
|
+
pence: penceFigure(display.price, quote, /\.(\d+)/.exec(last)?.[1]?.length ?? 0),
|
|
736
745
|
rawPrice: display.price,
|
|
737
746
|
priceBasis: quote?.priceBasis ?? null,
|
|
738
747
|
instrumentType: quote?.instrumentType ?? null,
|
|
@@ -1102,7 +1111,7 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
|
|
|
1102
1111
|
return pair ? [`${row.target.symbol}: ${depositaryPairText(pair, isReceiptSide({ quote: row.quote }))}.`] : [];
|
|
1103
1112
|
}),
|
|
1104
1113
|
// A cell shows only "unavailable", and an exported row only its status, so stderr names each failure; the JSON rows carry each reason.
|
|
1105
|
-
...ctx.cliOptions.format !== "json" ? quoteNotes(data
|
|
1114
|
+
...ctx.cliOptions.format !== "json" ? quoteNotes(data) : [],
|
|
1106
1115
|
];
|
|
1107
1116
|
const columns = commandName === "compare" ? compareColumns() : quoteColumns();
|
|
1108
1117
|
const basisNotes = changeBasisNotes(data, now);
|
|
@@ -1117,7 +1126,8 @@ async function runQuote(rawArgs: string[], ctx: Parameters<CliCommandDef["execut
|
|
|
1117
1126
|
// close does not read as a contradiction of DGS10 or GC for the same day.
|
|
1118
1127
|
// Dated by the quotes as served, which say whether one is the saved copy.
|
|
1119
1128
|
freshness: withSavedCopy(
|
|
1120
|
-
|
|
1129
|
+
// A table across markets (US and London lines) dates each market's session and says when each opens.
|
|
1130
|
+
quotesFreshness(data.map((row) => row.quote), { datesEachVenue: true, ...yieldsOnly ? { schedule: `Cboe close ${CBOE_YIELD_INDEX_CLOSE}` } : {} }),
|
|
1121
1131
|
answers.map(({ result }) => result.quote),
|
|
1122
1132
|
),
|
|
1123
1133
|
}, {
|
|
@@ -1399,7 +1409,11 @@ async function loadListingHistory(
|
|
|
1399
1409
|
};
|
|
1400
1410
|
// A coin's volume is traded value in its quote currency: said in every row and on the status line.
|
|
1401
1411
|
const volume = historyVolume(unit, traded);
|
|
1402
|
-
|
|
1412
|
+
// Spot FX has no exchange to count its trades: a source's 0 is no volume, printed as missing.
|
|
1413
|
+
const pair = quote?.instrumentType === "CURRENCY" || isCurrencyPairListing(symbol, exchange);
|
|
1414
|
+
// A London line's bars were priced in pence: cleaned there, and printed to the pence decimals `quote` shows.
|
|
1415
|
+
const priceDivisor = quotedPriceDivisor(quote);
|
|
1416
|
+
const rows = historyRows(pair ? points.map(({ volume: _volume, ...point }) => point) : points, resolution, unit.currency, unit.unit, volume, priceDivisor);
|
|
1403
1417
|
// Bars stop arriving on the listing's own trading days, not New York's.
|
|
1404
1418
|
const venue = quote?.listingExchangeName || exchange || undefined;
|
|
1405
1419
|
const served = range === "YTD" && !window ? rows.slice(Math.max(0, yearToDateStartIndex(rows.map((row) => row.date), rows[0]?.interval, venue))) : rows;
|
|
@@ -1450,7 +1464,7 @@ async function loadListingHistory(
|
|
|
1450
1464
|
].flat();
|
|
1451
1465
|
return {
|
|
1452
1466
|
listing, identity, unit, data, resolution, interval, intraday, volume, freshness, notes,
|
|
1453
|
-
decimals: historyPriceDecimals(data, listing.saved?.metadata.assetCategory),
|
|
1467
|
+
decimals: historyPriceDecimals(data, listing.saved?.metadata.assetCategory, priceDivisor),
|
|
1454
1468
|
};
|
|
1455
1469
|
}
|
|
1456
1470
|
|
|
@@ -1720,6 +1734,22 @@ const STATEMENT_COLUMNS: CliResultColumn<ReturnType<typeof financialStatementRow
|
|
|
1720
1734
|
{ key: "currency", header: "Cur" },
|
|
1721
1735
|
];
|
|
1722
1736
|
|
|
1737
|
+
/** A symbol as typed in capitals (`NEE`, `BP:LSE`, `7203.T`), never a word of a company's name. */
|
|
1738
|
+
const TYPED_SYMBOL = /^[A-Z0-9][A-Z0-9.:^=&-]{0,15}$/;
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* The symbols of a command given several where it takes one: `NEE FSLR` as
|
|
1742
|
+
* two words, or as one (`"NEE FSLR"`, `NEE,FSLR`). One word with spaces is
|
|
1743
|
+
* a list only when every part is a symbol as typed, so a company's name
|
|
1744
|
+
* (`"Bank of America"`) is still searched for. Null for one symbol.
|
|
1745
|
+
*/
|
|
1746
|
+
export function comparedSymbols(args: readonly string[]): string[] | null {
|
|
1747
|
+
const words = args.flatMap((arg) => arg.split(/[\s,]+/)).filter(Boolean);
|
|
1748
|
+
if (words.length < 2) return null;
|
|
1749
|
+
if (args.length > 1 || args.some((arg) => arg.includes(","))) return words;
|
|
1750
|
+
return words.every((word) => TYPED_SYMBOL.test(word)) ? words : null;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1723
1753
|
async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1], view: FinancialsView) {
|
|
1724
1754
|
const args = [...rawArgs];
|
|
1725
1755
|
const exchangeOption = takeOption(args, "--exchange");
|
|
@@ -1744,6 +1774,12 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
1744
1774
|
const tabular = ctx.cliOptions.format === "csv" || ctx.cliOptions.format === "ndjson";
|
|
1745
1775
|
if (sectionFlag && !tabular) ctx.fail("--section picks one table of --csv or --ndjson output.");
|
|
1746
1776
|
if (sectionFlag && !section?.trim()) ctx.fail("--section needs a section title or number.");
|
|
1777
|
+
// Several symbols are a comparison, which RV draws side by side.
|
|
1778
|
+
const compared = view === "valuation" ? comparedSymbols(args) : null;
|
|
1779
|
+
if (compared) {
|
|
1780
|
+
ctx.fail(`valuation takes one symbol; got ${compared.join(" ")}.`,
|
|
1781
|
+
`To compare their valuations side by side: gloom fn RV ${compared.join(",")}\nUsage: gloom valuation <symbol>`);
|
|
1782
|
+
}
|
|
1747
1783
|
const raw = requireOneArg(args, `${commandName} <symbol>`, "symbol", ctx);
|
|
1748
1784
|
const selectTables = (tables: CliReportTables) => {
|
|
1749
1785
|
try {
|
|
@@ -1765,8 +1801,8 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
1765
1801
|
// declared distribution it annualizes (not in JSON), and one whose dividend stopped says so; read beside the listing notes.
|
|
1766
1802
|
const distributions = view === "statements" ? null : loadDividendDistributions(
|
|
1767
1803
|
market.dataProvider, listing.request, financials, { declared: ctx.cliOptions.format !== "json" });
|
|
1768
|
-
// A
|
|
1769
|
-
const
|
|
1804
|
+
// A fund's facts: its bond figures beside its distribution yield, and whether fn ETF has any to point to.
|
|
1805
|
+
const fundFacts = view === "statements" ? null : loadReportFundFacts(listing.request.symbol,
|
|
1770
1806
|
financials.quote?.tradingExchangeName || listing.request.exchange || financials.quote?.listingExchangeName || financials.quote?.exchangeName,
|
|
1771
1807
|
financials, listing.saved);
|
|
1772
1808
|
const treasury = rates ? loadTreasuryTenors() : null;
|
|
@@ -1795,13 +1831,14 @@ async function runFinancials(rawArgs: string[], ctx: Parameters<CliCommandDef["e
|
|
|
1795
1831
|
loadBankFigures(market.statementSources, listing.request, financials),
|
|
1796
1832
|
])
|
|
1797
1833
|
: [null, { bank: null, note: null }];
|
|
1798
|
-
const
|
|
1834
|
+
const facts = await fundFacts;
|
|
1835
|
+
const bond = facts?.bond ?? null;
|
|
1799
1836
|
// On the SEC yield when the sponsor publishes one, else the distribution yield the line above shows.
|
|
1800
1837
|
const taxEquivalent = rates ? analyzeTaxEquivalent({
|
|
1801
1838
|
symbol: identity.symbol || listing.key, bond, rates, treasury: await treasury,
|
|
1802
1839
|
distribution: distributionBasis(priced.fundamentals?.dividendYield, priced.fundamentals?.dividendYieldBasis === "trailing" ? "trailing" : "forward"),
|
|
1803
1840
|
}) : null;
|
|
1804
|
-
const report = { ...data, distributions: await distributions, statementFilings, bank: bank.bank, bankNote: bank.note, bondFund: bond, taxEquivalent,
|
|
1841
|
+
const report = { ...data, distributions: await distributions, statementFilings, bank: bank.bank, bankNote: bank.note, bondFund: bond, fundFacts: facts, taxEquivalent,
|
|
1805
1842
|
// A base currency other than USD reads every amount with its code, as `ticker` does.
|
|
1806
1843
|
currencyCodes: writesCurrencyCodes(market.config) };
|
|
1807
1844
|
ctx.printResult({
|
|
@@ -2043,7 +2080,8 @@ async function runNews(rawArgs: string[], ctx: Parameters<CliCommandDef["execute
|
|
|
2043
2080
|
return;
|
|
2044
2081
|
}
|
|
2045
2082
|
const empty = emptyMessage(false);
|
|
2046
|
-
|
|
2083
|
+
// A bare coin ticker read as another listing (ETH, the Grayscale fund) says where the coin is, as `quote` does.
|
|
2084
|
+
const read = listing ? [...listingReadNotes(listing), ...coinListingNotes(listing, quote)] : [];
|
|
2047
2085
|
// A word that is also the company's name ("gold" for Gold.com Inc.) says once which ticker it found and how
|
|
2048
2086
|
// to search the stories that mention it instead, when those stories are mostly about something else.
|
|
2049
2087
|
const typed = args[0]?.trim() ?? "";
|
|
@@ -2401,14 +2439,20 @@ async function runHolders(
|
|
|
2401
2439
|
// --tail counts back from the end of the rows loaded, so it loads at least as many as it asks for.
|
|
2402
2440
|
const tail = ctx.cliOptions.tail;
|
|
2403
2441
|
const indexLimit = Math.min(ctx.cliOptions.limit ?? Math.max(tail ?? 0, HOLDERS_DEFAULT_ROWS), HOLDER_ROWS_MAX);
|
|
2442
|
+
const quoted = loadListingQuote(market.dataProvider, listing);
|
|
2404
2443
|
const [source, quote, form4, index] = await Promise.all([
|
|
2405
2444
|
loadForListing(
|
|
2406
2445
|
listing, market, ctx,
|
|
2407
2446
|
() => market.dataProvider.getHolders(listing.request.symbol, listing.request.exchange),
|
|
2408
2447
|
(found) => found.holders.length === 0,
|
|
2409
2448
|
{ command: commandName, noun: "holder data" },
|
|
2410
|
-
)
|
|
2411
|
-
|
|
2449
|
+
).catch(async (error: unknown) => {
|
|
2450
|
+
// A fund has no ownership breakdown: say so, and what reads it, rather than the source's NO_DATA.
|
|
2451
|
+
const fund = await fundHoldersAnswer(listing.symbol, error, await quoted, apiClient);
|
|
2452
|
+
if (fund) fail(fund.message, fund.details.join("\n") || undefined, NO_DATA_CODE);
|
|
2453
|
+
throw error;
|
|
2454
|
+
}),
|
|
2455
|
+
quoted,
|
|
2412
2456
|
insiderCommand ? loadForm4Report(market, listing) : null,
|
|
2413
2457
|
indexTicker ? loadIndexedHolders(indexTicker, indexLimit).catch(logFailure("cli.commands-market", "Read the 13F holder index failed", null)) : null,
|
|
2414
2458
|
]);
|
|
@@ -2616,12 +2660,13 @@ async function runEvents(rawArgs: string[], ctx: Parameters<CliCommandDef["execu
|
|
|
2616
2660
|
async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["execute"]>[1]) {
|
|
2617
2661
|
const args = [...rawArgs];
|
|
2618
2662
|
const leapsOptions = takeLeapsOptions(args);
|
|
2619
|
-
const
|
|
2663
|
+
const expirationOption = takeExpirationOption(args);
|
|
2664
|
+
const { value: expiration, flag: expirationFlag } = expirationOption;
|
|
2665
|
+
if (expirationOption.both) ctx.fail("--expiry is --expiration; give one of them.", `Usage: gloom ${OPTIONS_USAGE}`);
|
|
2620
2666
|
const exchangeOption = takeOption(args, "--exchange");
|
|
2621
2667
|
if (leapsOptions.leaps) {
|
|
2622
2668
|
// With --leaps, --expiration is a window of one expiry.
|
|
2623
|
-
const
|
|
2624
|
-
const criteria = parseLeapsCriteria({ ...leapsOptions.raw, ...expirationGiven ? { "--expiration": expiration ?? "" } : {} });
|
|
2669
|
+
const criteria = parseLeapsCriteria({ ...leapsOptions.raw, ...expirationOption.given ? { "--expiration": expiration ?? "" } : {} });
|
|
2625
2670
|
if ("error" in criteria) ctx.fail(criteria.error, `Usage: gloom ${LEAPS_USAGE}`);
|
|
2626
2671
|
const entries = normalizeSymbols(args);
|
|
2627
2672
|
if (entries.length === 0) ctx.fail("--leaps needs at least one symbol.", `Usage: gloom ${LEAPS_USAGE}`);
|
|
@@ -2633,11 +2678,11 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2633
2678
|
}
|
|
2634
2679
|
const raw = requireOneArg(args, OPTIONS_USAGE, "symbol", ctx);
|
|
2635
2680
|
let expirationDate: number | undefined;
|
|
2636
|
-
if (
|
|
2681
|
+
if (expirationOption.given) {
|
|
2637
2682
|
const parsed = expiration == null ? null : parseOptionExpiration(expiration);
|
|
2638
2683
|
if (parsed == null) {
|
|
2639
2684
|
ctx.fail(
|
|
2640
|
-
expiration == null ?
|
|
2685
|
+
expiration == null ? `${expirationFlag} needs a date.` : `Invalid ${expirationFlag} "${expiration}".`,
|
|
2641
2686
|
`Use ${OPTION_EXPIRATION_FORMAT}.${expiration == null ? "" : expirationHint(expiration)}\nUsage: gloom ${OPTIONS_USAGE}`,
|
|
2642
2687
|
);
|
|
2643
2688
|
}
|
|
@@ -2669,7 +2714,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2669
2714
|
const listed = await readChain(undefined).then(({ chain }) => chain.expirationDates,
|
|
2670
2715
|
logFailure("cli-market", "Read available option expirations failed", []));
|
|
2671
2716
|
ctx.fail(`expiry ${expiryIsoDate(expirationDate)} for ${listing.key} `
|
|
2672
|
-
+ expiredExpiryText(expirationDate, listed, now, { exchange: expiryVenue, flag:
|
|
2717
|
+
+ expiredExpiryText(expirationDate, listed, now, { exchange: expiryVenue, flag: expirationFlag.slice(2) }),
|
|
2673
2718
|
`Run gloom options ${listing.key} to list the live expiries.`);
|
|
2674
2719
|
}
|
|
2675
2720
|
const { result, chain: read } = await loadForListing(listing, market, ctx, async () => {
|
|
@@ -2711,7 +2756,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2711
2756
|
// IV and delta are valued at one spot and instant, named under the table and on every exported row.
|
|
2712
2757
|
const { chain, spot: modelSpot } = valueChainAtModelSpot(loaded, inputs, Date.now(), identity.exchange);
|
|
2713
2758
|
const basis: OptionsChainBasis = { spot: modelSpot, dividendYield: inputs.dividendYield ?? null,
|
|
2714
|
-
openInterest: await loadOptionsChainOpenInterest(symbol, chain) };
|
|
2759
|
+
openInterest: await loadOptionsChainOpenInterest(symbol, chain), ivFilled: chainHasFilledIv(chain) };
|
|
2715
2760
|
const basisFields = optionsBasisFields(basis);
|
|
2716
2761
|
const currency = [...chain.calls, ...chain.puts].find((contract) => contract.currency?.trim())?.currency.trim().toUpperCase()
|
|
2717
2762
|
?? quote?.currency ?? null;
|
|
@@ -2783,6 +2828,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2783
2828
|
{ key: "bid", header: "Bid" },
|
|
2784
2829
|
{ key: "ask", header: "Ask" },
|
|
2785
2830
|
{ key: "iv", header: "IV (annualized decimal)" },
|
|
2831
|
+
{ key: "ivSource", header: "IV source" },
|
|
2786
2832
|
{ key: "delta", header: "Delta (decimal)" },
|
|
2787
2833
|
{ key: "volume", header: "Vol" },
|
|
2788
2834
|
{ key: "openInterest", header: "OI" },
|
|
@@ -2810,7 +2856,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2810
2856
|
{ key: "coinAsk", header: `Ask ${coin}`, align: "right", format: formatCoinPremiumCell },
|
|
2811
2857
|
{ key: "coinMark", header: `Mark ${coin}`, align: "right", format: formatCoinPremiumCell },
|
|
2812
2858
|
{ key: "mark", header: "Mark $", align: "right", format: formatOptionDollarCell },
|
|
2813
|
-
{ key: "iv", header: "IV", align: "right", format: formatOptionIvCell },
|
|
2859
|
+
{ key: "iv", header: "IV", align: "right", format: (value, row) => formatOptionIvCell(value, row) },
|
|
2814
2860
|
{ key: "delta", header: "Delta", align: "right", format: formatOptionDeltaCell },
|
|
2815
2861
|
{ key: "gamma", header: "Gamma", align: "right", format: formatOptionGammaCell, optional: true, dropPriority: 3 },
|
|
2816
2862
|
{ key: "vega", header: "Vega $", align: "right", format: formatOptionDollarCell, optional: true, dropPriority: 2 },
|
|
@@ -2826,7 +2872,7 @@ async function runOptions(rawArgs: string[], ctx: Parameters<CliCommandDef["exec
|
|
|
2826
2872
|
{ key: "last", header: "Last", align: "right" },
|
|
2827
2873
|
{ key: "bid", header: "Bid", align: "right", format: (_value, row) => formatOptionQuoteCell(row, "bid") },
|
|
2828
2874
|
{ key: "ask", header: "Ask", align: "right", format: (_value, row) => formatOptionQuoteCell(row, "ask") },
|
|
2829
|
-
{ key: "iv", header: "IV", align: "right", format: formatOptionIvCell },
|
|
2875
|
+
{ key: "iv", header: "IV", align: "right", format: (value, row) => formatOptionIvCell(value, row) },
|
|
2830
2876
|
{ key: "delta", header: "Delta", align: "right", format: formatOptionDeltaCell },
|
|
2831
2877
|
{ key: "volume", header: "Vol", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
2832
2878
|
{ key: "openInterest", header: "OI", align: "right", format: formatCountCell, optional: true, dropPriority: 1 },
|
|
@@ -3250,7 +3296,7 @@ export const marketDataCliCommands: BuiltinCliCommandDef[] = [
|
|
|
3250
3296
|
usage: [OPTIONS_USAGE, LEAPS_USAGE],
|
|
3251
3297
|
options: [
|
|
3252
3298
|
{
|
|
3253
|
-
flags: `--expiration <${OPTION_EXPIRATION_PLACEHOLDER}>`,
|
|
3299
|
+
flags: `--expiration <${OPTION_EXPIRATION_PLACEHOLDER}>, --expiry <${OPTION_EXPIRATION_PLACEHOLDER}>`,
|
|
3254
3300
|
description: "Expiration as a date (2028-01-21) or Unix seconds; defaults to the nearest one, and lists the others. With --leaps: rank that expiry alone",
|
|
3255
3301
|
},
|
|
3256
3302
|
EXCHANGE_OPTION,
|
|
@@ -4,7 +4,7 @@ import type { DataProvider } from "../../types/data-provider";
|
|
|
4
4
|
import type { CliListing } from "../listing-arg";
|
|
5
5
|
import { cliStyles, cliTerminalWidth, wrapText } from "../../utils/cli-output";
|
|
6
6
|
import { isFiniteNumber } from "../../utils/guards";
|
|
7
|
-
import { calculateOptionGreeks, solveChainVolatilities } from "../../plugins/builtin/options/analytics";
|
|
7
|
+
import { calculateOptionGreeks, ivSolvedNote, solveChainVolatilities } from "../../plugins/builtin/options/analytics";
|
|
8
8
|
import { optionQuoteSide } from "../../plugins/builtin/options/market-reference";
|
|
9
9
|
import {
|
|
10
10
|
formatModelSpotPrice, modelSpotLabel, optionModelSpot, type OptionModelSpot,
|
|
@@ -16,9 +16,30 @@ import { DEFAULT_OPTION_CALC_DRAFT } from "../../plugins/builtin/options-calcula
|
|
|
16
16
|
import { expiryIsoDate, OPTION_EXPIRATION_PLACEHOLDER, parseOptionExpiration, settlementStampTime } from "../../utils/option-expiry";
|
|
17
17
|
import { loadModelDividendYield } from "../../market-data/model-dividend-yield";
|
|
18
18
|
import { formatUtcTime } from "../../utils/utc-time";
|
|
19
|
+
import { takeOption } from "./command-utils";
|
|
19
20
|
|
|
20
21
|
export const OPTIONS_USAGE = `options <symbol> [--expiration <${OPTION_EXPIRATION_PLACEHOLDER}>]`;
|
|
21
22
|
|
|
23
|
+
/** `--expiration` as `options` names it, and `--expiry` as `fn GEX` and `fn OPX` do. */
|
|
24
|
+
const OPTIONS_EXPIRATION_FLAGS = ["--expiration", "--expiry"] as const;
|
|
25
|
+
type OptionsExpirationFlag = (typeof OPTIONS_EXPIRATION_FLAGS)[number];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Takes the expiry option out of `args`, under either spelling: its value,
|
|
29
|
+
* the flag as typed (for messages), whether it was given at all (a flag with
|
|
30
|
+
* no value is given), and whether both spellings were.
|
|
31
|
+
*/
|
|
32
|
+
export function takeExpirationOption(args: string[]): {
|
|
33
|
+
value: string | undefined;
|
|
34
|
+
flag: OptionsExpirationFlag;
|
|
35
|
+
given: boolean;
|
|
36
|
+
both: boolean;
|
|
37
|
+
} {
|
|
38
|
+
const typed = OPTIONS_EXPIRATION_FLAGS.filter((flag) => args.some((arg) => arg === flag || arg.startsWith(`${flag}=`)));
|
|
39
|
+
const [expiration, expiry] = OPTIONS_EXPIRATION_FLAGS.map((flag) => takeOption(args, flag));
|
|
40
|
+
return { value: expiration ?? expiry, flag: typed.length === 1 ? typed[0]! : "--expiration", given: typed.length > 0, both: typed.length > 1 };
|
|
41
|
+
}
|
|
42
|
+
|
|
22
43
|
export { parseOptionExpiration };
|
|
23
44
|
|
|
24
45
|
/** Whether the chain holds contracts of the requested expiry, matched by UTC calendar date as the chain stamps them. */
|
|
@@ -63,6 +84,8 @@ export interface OptionModelInputs {
|
|
|
63
84
|
interface OptionModelFigures {
|
|
64
85
|
/** Annualized volatility as a fraction, solved from the chain's quotes. */
|
|
65
86
|
iv: number | null;
|
|
87
|
+
/** `iv` was read off the neighbouring strikes: this strike has no two-sided out-of-the-money quote to solve from. */
|
|
88
|
+
ivFilled?: boolean;
|
|
66
89
|
delta: number | null;
|
|
67
90
|
}
|
|
68
91
|
|
|
@@ -73,6 +96,7 @@ export const OPTION_MODEL_RATE = DEFAULT_OPTION_CALC_DRAFT.rate;
|
|
|
73
96
|
export const MARKED_OPTION_FIELDS = {
|
|
74
97
|
iv: "The venue's mark implied volatility (decimal), one per contract. Delta and the other greeks under mark are the venue's too: "
|
|
75
98
|
+ "Black-76 at the expiry's forward with no discounting, to its settlement.",
|
|
99
|
+
ivSource: "mark: iv is the venue's own, never solved or filled here.",
|
|
76
100
|
bid: "Prices in USD are the coin prices (coin) at the expiry's forward (mark.forward), the venue's own model price.",
|
|
77
101
|
mark: "price in USD; iv; delta and gamma per 1.00 move in the underlying; vega per volatility point and theta per calendar day, in USD.",
|
|
78
102
|
} as const;
|
|
@@ -91,6 +115,8 @@ export function chainCoinCurrency(chain: OptionsChain): string | null {
|
|
|
91
115
|
export const OPTION_VOLATILITY_FIELDS = {
|
|
92
116
|
iv: "Annualized volatility (decimal) solved from the quote midpoint, one per strike from its out-of-the-money side, at the "
|
|
93
117
|
+ "spot, valuation time and rate in metadata.model. Delta here and dealer gamma (GEX) are valued with it.",
|
|
118
|
+
ivSource: "solved: iv comes from the strike's out-of-the-money quote midpoint. filled: that side has no two-sided quote, so iv is read off the "
|
|
119
|
+
+ "nearest solved strikes (flat beyond the last one); it is no market's volatility, and delta is valued with it all the same.",
|
|
94
120
|
impliedVolatility: "The data provider's own reported implied volatility, passed through unchanged. Delta does not use it; "
|
|
95
121
|
+ "GEX falls back to it only at a strike with no two-sided quote on either side.",
|
|
96
122
|
} as const;
|
|
@@ -116,8 +142,9 @@ function optionModelFigures(chain: OptionsChain, inputs: OptionModelInputs): Map
|
|
|
116
142
|
for (const contract of contracts) {
|
|
117
143
|
const volatility = volatilities.byStrike.get(contract.strike);
|
|
118
144
|
const delta = calculateOptionGreeks(contract, side, inputs.spot, inputs.dividendYield, volatilities)?.delta;
|
|
145
|
+
const iv = volatility != null && Number.isFinite(volatility) && volatility > 0 ? volatility : null;
|
|
119
146
|
figures.set(contract, {
|
|
120
|
-
iv:
|
|
147
|
+
iv, ivFilled: iv != null && volatilities.filled.has(contract.strike),
|
|
121
148
|
delta: delta != null && Number.isFinite(delta) ? delta : null,
|
|
122
149
|
});
|
|
123
150
|
}
|
|
@@ -167,7 +194,8 @@ export async function loadOptionModelInputs(
|
|
|
167
194
|
/** The chain with each contract's modeled `iv` and `delta` added; every provider field is kept. */
|
|
168
195
|
export function chainWithModelFigures(chain: OptionsChain, inputs: OptionModelInputs): OptionsChain {
|
|
169
196
|
const figures = optionModelFigures(chain, inputs);
|
|
170
|
-
const add = (contract: OptionContract) => ({ ...contract, iv: figures.get(contract)?.iv ?? null,
|
|
197
|
+
const add = (contract: OptionContract) => ({ ...contract, iv: figures.get(contract)?.iv ?? null, ivFilled: figures.get(contract)?.ivFilled ?? false,
|
|
198
|
+
delta: figures.get(contract)?.delta ?? null });
|
|
171
199
|
return { ...chain, calls: chain.calls.map(add), puts: chain.puts.map(add) };
|
|
172
200
|
}
|
|
173
201
|
|
|
@@ -180,6 +208,13 @@ export interface OptionsChainBasis {
|
|
|
180
208
|
spot: OptionModelSpot | null;
|
|
181
209
|
dividendYield: number | null;
|
|
182
210
|
openInterest: OpenInterestAsOf | null;
|
|
211
|
+
/** A strike's IV is filled from its neighbours (`chainHasFilledIv`), which the closing line says. */
|
|
212
|
+
ivFilled?: boolean;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Whether any contract's IV was read off the neighbouring strikes (see `ChainVolatilities.filled`). */
|
|
216
|
+
export function chainHasFilledIv(chain: OptionsChain): boolean {
|
|
217
|
+
return [...chain.calls, ...chain.puts].some((contract) => (contract as OptionContract & OptionModelFigures).ivFilled === true);
|
|
183
218
|
}
|
|
184
219
|
|
|
185
220
|
/** The chain with `iv` and `delta` valued at the spot `optionModelSpot` picks for it, and that spot. */
|
|
@@ -207,7 +242,8 @@ export async function loadOptionsChainOpenInterest(symbol: string, chain: Option
|
|
|
207
242
|
}
|
|
208
243
|
|
|
209
244
|
/**
|
|
210
|
-
* The line under the chain: `Greeks at spot 89.79 (close Mon 12 Oct 20:00 UTC), dividend yield 2.34%, rate 4% ·
|
|
245
|
+
* The line under the chain: `Greeks at spot 89.79 (close Mon 12 Oct 20:00 UTC), dividend yield 2.34%, rate 4% · IV solved from quote mids ·
|
|
246
|
+
* OI as of Fri 9 Oct`, with `, ~ filled from neighbouring strikes` after the IV when a strike has no quote to solve from.
|
|
211
247
|
* An open interest whose date cannot be established says why. A chain its
|
|
212
248
|
* venue marks says whose marks they are: `Deribit marks: IV and greeks at
|
|
213
249
|
* each expiry's forward, rate 0%; $ is the BTC price at the forward · OI live, Thu 22 Oct 22:42 UTC`.
|
|
@@ -230,6 +266,7 @@ export function optionsBasisFootnote(basis: OptionsChainBasis, chain?: OptionsCh
|
|
|
230
266
|
// An unknown yield is valued as none, and says so rather than read as a stated 0%.
|
|
231
267
|
+ `dividend yield ${basis.dividendYield == null ? "unknown (valued at 0%)" : `${Number((basis.dividendYield * 100).toFixed(2))}%`}, `
|
|
232
268
|
+ `rate ${Number((OPTION_MODEL_RATE * 100).toFixed(2))}%`);
|
|
269
|
+
parts.push(ivSolvedNote(basis.ivFilled === true));
|
|
233
270
|
}
|
|
234
271
|
const oi = basis.openInterest;
|
|
235
272
|
parts.push(oi?.date ? openInterestAsOfLabel(oi)
|
|
@@ -266,6 +303,8 @@ export function optionRows(chain: OptionsChain) {
|
|
|
266
303
|
volume: contract.volume,
|
|
267
304
|
openInterest: contract.openInterest,
|
|
268
305
|
iv: modeled.iv ?? null,
|
|
306
|
+
// Solved from the strike's quote, or filled from its neighbours when it has none to solve from; a venue's own mark is neither.
|
|
307
|
+
ivSource: modeled.iv == null ? null : contract.mark ? "mark" : modeled.ivFilled ? "filled" : "solved",
|
|
269
308
|
delta: modeled.delta ?? null,
|
|
270
309
|
expiration: expiryIsoDate(contract.expiration),
|
|
271
310
|
// A venue's marks and coin prices (Deribit), beside the dollar ones.
|
|
@@ -301,9 +340,11 @@ export function formatOptionQuoteCell(row: Record<string, unknown>, side: "bid"
|
|
|
301
340
|
return quote == null ? "—" : String(quote);
|
|
302
341
|
}
|
|
303
342
|
|
|
304
|
-
/** Implied volatility as a percent; a dash when it could not be solved, never 0.0
|
|
305
|
-
export function formatOptionIvCell(value: unknown): string {
|
|
306
|
-
|
|
343
|
+
/** Implied volatility as a percent; a dash when it could not be solved, never 0.0%, and a leading "~" when it was filled from neighbouring strikes. */
|
|
344
|
+
export function formatOptionIvCell(value: unknown, row?: Record<string, unknown>): string {
|
|
345
|
+
if (!isFiniteNumber(value) || !(value > 0)) return "—";
|
|
346
|
+
const text = `${(value * 100).toFixed(1)}%`;
|
|
347
|
+
return row?.ivSource === "filled" ? `~${text}` : text;
|
|
307
348
|
}
|
|
308
349
|
|
|
309
350
|
/** Delta to two places, signed for puts; a dash when it could not be valued, and no "-0.00" for a far wing. */
|
|
@@ -281,6 +281,8 @@ export type LeapsRow = {
|
|
|
281
281
|
// The fields below are on a row only when `--columns` asked for them.
|
|
282
282
|
/** `iv`: the implied volatility the contract's delta is valued at, percent a year. */
|
|
283
283
|
ivPercent?: number | null;
|
|
284
|
+
/** `iv`: `filled` when that strike has no two-sided out-of-the-money quote to solve from and its IV is read off the neighbouring strikes. */
|
|
285
|
+
ivSource?: "solved" | "filled" | null;
|
|
284
286
|
/** `ivr`: the underlying's 30-day IV rank against its 52 weeks, 0 to 100, and the close it ranks. */
|
|
285
287
|
ivRank?: number | null;
|
|
286
288
|
ivRankAsOf?: string | null;
|
|
@@ -369,7 +371,10 @@ function screenLeapsChain(
|
|
|
369
371
|
dataSource: chain.dataSource ?? null,
|
|
370
372
|
delayMinutes: chain.delayMinutes ?? null,
|
|
371
373
|
};
|
|
372
|
-
if (wants.has("iv"))
|
|
374
|
+
if (wants.has("iv")) {
|
|
375
|
+
row.ivPercent = contract.iv != null && Number.isFinite(contract.iv) ? round(contract.iv * 100, 2) : null;
|
|
376
|
+
row.ivSource = row.ivPercent == null ? null : (contract as { ivFilled?: boolean }).ivFilled ? "filled" : "solved";
|
|
377
|
+
}
|
|
373
378
|
// A two-sided quote passed the spread cap, so the midpoint is there.
|
|
374
379
|
const mid = optionMid(contract)!;
|
|
375
380
|
if (wants.has("cost")) row.costPerContract = round(mid * SHARES_PER_CONTRACT, 2);
|
|
@@ -694,7 +699,7 @@ const LEAPS_COLUMNS: CliResultColumn<LeapsRow>[] = [
|
|
|
694
699
|
|
|
695
700
|
/** What each `--columns` name adds to the exports, after Extrinsic/yr %. */
|
|
696
701
|
const EXTRA_EXPORT_COLUMNS: Record<LeapsExtraColumn, CliResultColumn<LeapsRow>[]> = {
|
|
697
|
-
iv: [{ key: "ivPercent", header: "IV %", align: "right", format: formatPercent }],
|
|
702
|
+
iv: [{ key: "ivPercent", header: "IV %", align: "right", format: formatPercent }, { key: "ivSource", header: "IV source" }],
|
|
698
703
|
ivr: [{ key: "ivRank", header: "IV Rank", align: "right" }, { key: "ivRankAsOf", header: "IV Rank As Of" }],
|
|
699
704
|
cost: [{ key: "costPerContract", header: "Cost/Contract (mid x 100)", align: "right" }],
|
|
700
705
|
breakeven: [
|
|
@@ -717,7 +722,8 @@ const LEAPS_TEXT_COLUMNS: CliResultColumn<LeapsRow>[] = TEXT_COLUMN_KEYS.map((ke
|
|
|
717
722
|
|
|
718
723
|
/** The text table's columns for a contract's own `--columns` figures; the underlying's go in a table of their own above. */
|
|
719
724
|
const EXTRA_TEXT_COLUMNS: Partial<Record<LeapsExtraColumn, CliResultColumn<LeapsRow>[]>> = {
|
|
720
|
-
iv: [{ key: "ivPercent", header: "IV", align: "right", format:
|
|
725
|
+
iv: [{ key: "ivPercent", header: "IV", align: "right", format: (value, row) => row.ivSource === "filled" && typeof value === "number" && Number.isFinite(value)
|
|
726
|
+
? `~${value.toFixed(1)}%` : formatPercent(value) }],
|
|
721
727
|
"carry-net": [{ key: "carryNetPerYearPercent", header: "Net/yr", align: "right", format: formatPercent }],
|
|
722
728
|
cost: [{ key: "costPerContract", header: "Cost", align: "right", format: formatWhole }],
|
|
723
729
|
breakeven: [
|