gloomberb 0.18.2 → 0.18.4
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 +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/patches/electrobun@1.18.1.patch +106 -5
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +99 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +24 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +26 -7
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/telemetry.ts +17 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/global-shortcuts.ts +1 -16
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/runtime/update.ts +28 -8
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/pane-tabs-allowlist.json +65 -0
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1189 -296
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +272 -70
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +20 -14
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +32 -18
- package/src/cli/issuer-membership.ts +103 -0
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +315 -43
- package/src/cli/name-entries.ts +73 -22
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +77 -20
- package/src/cli/pane-functions/catalog.ts +40 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +12 -8
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +48 -16
- package/src/cli/pane-functions/index.ts +82 -21
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +65 -14
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +190 -27
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +122 -25
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +226 -60
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +387 -21
- package/src/components/chart/composite/drawing-store.ts +26 -6
- package/src/components/chart/composite/interactions.ts +9 -3
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +316 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +19 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +32 -6
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/data-table/view.tsx +23 -3
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +13 -6
- package/src/components/layout/detached-pane-shell.tsx +1 -0
- package/src/components/layout/floating-pane.tsx +1 -0
- package/src/components/layout/header.tsx +234 -60
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/pane/header.tsx +10 -3
- package/src/components/layout/pane/index.tsx +1 -0
- package/src/components/layout/pane/pane-tabs.tsx +9 -1
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/components/ui/stat-grid.tsx +28 -3
- package/src/components/ui/status.tsx +3 -1
- package/src/core/app-services.ts +4 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +51 -12
- package/src/i18n/ja.ts +51 -12
- package/src/i18n/ko.ts +51 -12
- package/src/i18n/zh-cn.ts +51 -12
- package/src/i18n/zh-tw.ts +51 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +108 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +77 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
- package/src/plugins/builtin/attention/pane.tsx +22 -16
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +12 -9
- package/src/plugins/builtin/backtest/export.ts +1 -1
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/client.ts +2 -2
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +9 -11
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
- package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +89 -31
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +7 -4
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
- package/src/plugins/builtin/correlation/settings.ts +1 -0
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +7 -5
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +19 -12
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +77 -5
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +34 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +16 -10
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/board.tsx +4 -1
- package/src/plugins/builtin/gpu/changes.tsx +10 -10
- package/src/plugins/builtin/gpu/equities.tsx +4 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/gpu/history.tsx +4 -2
- package/src/plugins/builtin/gpu/pane.tsx +5 -3
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +15 -2
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +48 -8
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/base-values.ts +11 -7
- package/src/plugins/builtin/insider/client.ts +3 -0
- package/src/plugins/builtin/insider/headless.ts +20 -7
- package/src/plugins/builtin/insider/index.tsx +17 -8
- package/src/plugins/builtin/insider/issuer.ts +34 -4
- package/src/plugins/builtin/insider/signals-client.ts +3 -3
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/insider/table-model.ts +35 -13
- package/src/plugins/builtin/insider/table.tsx +5 -2
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/events.ts +145 -0
- package/src/plugins/builtin/iv-history/format.ts +3 -0
- package/src/plugins/builtin/iv-history/headless.ts +91 -7
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +14 -8
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +36 -21
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +33 -11
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +50 -15
- package/src/plugins/builtin/notes/export.ts +17 -6
- package/src/plugins/builtin/notes/files.ts +16 -5
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/enrichment-model.ts +10 -9
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/model-spot.ts +70 -4
- package/src/plugins/builtin/options/view.tsx +51 -23
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
- package/src/plugins/builtin/options-positioning/headless.ts +11 -3
- package/src/plugins/builtin/options-positioning/model.ts +24 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
- package/src/plugins/builtin/options-scenario/client.ts +142 -29
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +49 -21
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +6 -3
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +44 -32
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/seasonality/client.ts +2 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +6 -4
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +23 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/shared/volatility/options.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/social-mentions/client.ts +2 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +28 -7
- package/src/plugins/builtin/vol-surface/headless.ts +43 -15
- package/src/plugins/builtin/vol-surface/model.ts +34 -12
- package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
- package/src/plugins/builtin/vol-surface/stored.ts +34 -3
- package/src/plugins/builtin/vol-surface/tables.ts +14 -0
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/desktop/controls.tsx +1 -1
- package/src/renderers/dom/desktop/query-bar.tsx +2 -0
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/host/tabs.tsx +16 -5
- package/src/renderers/dom/host/top-surface.ts +127 -0
- package/src/renderers/dom/styles.css +115 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +9 -7
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +8 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +39 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/index.ts +16 -0
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +21 -4
- package/src/telemetry/update-failures-node.ts +59 -0
- package/src/telemetry/update-failures.ts +307 -0
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +215 -36
- package/src/tickers/company-name.ts +43 -0
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/listing-fate.ts +30 -0
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +20 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +83 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/issuer-listings.ts +28 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/failure.ts +163 -0
- package/src/updater/index.ts +97 -28
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-pair.ts +26 -0
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +71 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- package/src/cli/pane-functions/screenshot.ts +0 -2122
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { DataTableView, PaneStatusBody, QueryBar, usePaneFooter, usePaneNoticeFooter, type DataTableColumn, type DataTableKeyEvent } from "../../../components";
|
|
3
|
+
import { useAsyncResource } from "../../../react/async-resource";
|
|
4
|
+
import { useAutoRefresh } from "../../../react/auto-refresh";
|
|
5
|
+
import { usePaneSettingValue, usePluginAppActions } from "../../../public/react";
|
|
6
|
+
import { useThemeColors } from "../../../theme/theme-context";
|
|
7
|
+
import type { PaneProps } from "../../../types/plugin";
|
|
8
|
+
import { Box } from "../../../ui";
|
|
9
|
+
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
10
|
+
import { compareSortValues, nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
|
|
11
|
+
import { useAssetData } from "../../runtime";
|
|
12
|
+
import { createScenarioDependencies } from "./client";
|
|
13
|
+
import { cspScreenNotes, cspScreenReportRow, type CspScreenReportRow } from "./csp-screen-headless";
|
|
14
|
+
import { CSP_SCREEN_DEFAULTS, CSP_SCREEN_LIMIT, cspScreenCriteriaText, cspTargetDate, loadCspScreen, readCspScreenCriteria, type CspScreenRow } from "./csp-screen";
|
|
15
|
+
import { formatHedgePrice } from "./hedge";
|
|
16
|
+
|
|
17
|
+
type ScreenRow = CspScreenReportRow & { source: CspScreenRow };
|
|
18
|
+
|
|
19
|
+
const COLUMNS: DataTableColumn[] = [
|
|
20
|
+
{ id: "rank", label: "#", width: 3, align: "right" },
|
|
21
|
+
{ id: "symbol", label: "Symbol", width: 7, align: "left" },
|
|
22
|
+
{ id: "expiry", label: "Expiry", width: 10, align: "right" },
|
|
23
|
+
{ id: "days", label: "DTE", width: 4, align: "right" },
|
|
24
|
+
{ id: "strike", label: "Strike", width: 8, align: "right" },
|
|
25
|
+
{ id: "bid", label: "Bid", width: 7, align: "right" },
|
|
26
|
+
{ id: "ask", label: "Ask", width: 7, align: "right" },
|
|
27
|
+
{ id: "spread", label: "Spread", width: 11, align: "right" },
|
|
28
|
+
{ id: "delta", label: "Delta", width: 6, align: "right" },
|
|
29
|
+
{ id: "otm", label: "OTM", width: 6, align: "right" },
|
|
30
|
+
{ id: "midYield", label: "Mid yield/yr", width: 12, align: "right" },
|
|
31
|
+
{ id: "bidYield", label: "Bid yield/yr", width: 12, align: "right" },
|
|
32
|
+
{ id: "ivRank", label: "IVR", width: 4, align: "right" },
|
|
33
|
+
{ id: "earnings", label: "Earnings", width: 17, align: "left" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const DELTA_OPTIONS = ["0.10", "0.15", "0.20", "0.25", "0.30", "0.35", "0.40", "0.50"];
|
|
37
|
+
const DAYS_OPTIONS = ["7", "14", "21", "30", "45", "60", "90"];
|
|
38
|
+
const SYMBOLS_FIELD_WIDTH = 24;
|
|
39
|
+
/** Symbols apply once typing pauses, so a half-typed ticker does not start a screen. */
|
|
40
|
+
const SYMBOLS_DEBOUNCE_MS = 700;
|
|
41
|
+
|
|
42
|
+
const symbolList = (text: string) => [...new Set(text.split(/[\s,;]+/).map((symbol) => symbol.trim().toUpperCase()).filter(Boolean))];
|
|
43
|
+
const percentText = (value: unknown, digits: number) => typeof value === "number" ? `${value.toFixed(digits)}%` : "--";
|
|
44
|
+
const priceText = (value: unknown) => typeof value === "number" ? formatHedgePrice(value) : "--";
|
|
45
|
+
/** A column's value to sort on: earnings by date, a flag as a number. */
|
|
46
|
+
function sortValue(row: CspScreenReportRow, columnId: string): string | number | null {
|
|
47
|
+
if (columnId === "earnings") return row.earningsDate;
|
|
48
|
+
const value = row[columnId as keyof CspScreenReportRow];
|
|
49
|
+
return typeof value === "boolean" ? Number(value) : value;
|
|
50
|
+
}
|
|
51
|
+
/** A select's options with the current value kept when it is not a preset (a delta typed on the command line). */
|
|
52
|
+
const withCurrent = (presets: readonly string[], current: string, label: (value: string) => string) =>
|
|
53
|
+
[...new Set([...presets, current])].sort((a, b) => Number(a) - Number(b)).map((value) => ({ value, label: label(value) }));
|
|
54
|
+
|
|
55
|
+
export function CspScreenPane({ width, height, focused }: PaneProps) {
|
|
56
|
+
const colors = useThemeColors();
|
|
57
|
+
const [symbolsText, setSymbolsText] = usePaneSettingValue<string>("symbols", "");
|
|
58
|
+
const [delta, setDelta] = usePaneSettingValue<string | number>("delta", "");
|
|
59
|
+
const [days, setDays] = usePaneSettingValue<string | number>("days", "");
|
|
60
|
+
const [expiration, setExpiration] = usePaneSettingValue<string | number>("expiration", "");
|
|
61
|
+
const [maxSpread] = usePaneSettingValue<string | number>("maxSpread", "");
|
|
62
|
+
const [symbolsActive, setSymbolsActive] = useState(false);
|
|
63
|
+
const { createPaneFromTemplate } = usePluginAppActions();
|
|
64
|
+
const assetData = useAssetData();
|
|
65
|
+
const typed = useMemo(() => symbolList(String(symbolsText ?? "")), [symbolsText]);
|
|
66
|
+
// The screen reads the first names it can rank; the footer names the rest.
|
|
67
|
+
const symbols = useMemo(() => typed.slice(0, CSP_SCREEN_LIMIT), [typed]);
|
|
68
|
+
const leftOut = typed.slice(CSP_SCREEN_LIMIT);
|
|
69
|
+
const criteria = useMemo(() => {
|
|
70
|
+
try { return { value: readCspScreenCriteria({ delta, days, expiration, maxSpread }), error: null }; }
|
|
71
|
+
catch (error) { return { value: null, error: error instanceof Error ? error.message : String(error) }; }
|
|
72
|
+
}, [delta, days, expiration, maxSpread]);
|
|
73
|
+
const controller = useRef<AbortController | null>(null);
|
|
74
|
+
const key = JSON.stringify([symbols, criteria.value]);
|
|
75
|
+
const loader = useCallback(async () => {
|
|
76
|
+
controller.current?.abort();
|
|
77
|
+
const current = new AbortController();
|
|
78
|
+
controller.current = current;
|
|
79
|
+
return loadCspScreen(symbols.map((symbol) => parsePublicTickerKey(symbol)), criteria.value!,
|
|
80
|
+
{ scenario: createScenarioDependencies(), earnings: assetData }, current.signal);
|
|
81
|
+
}, [key, assetData]);
|
|
82
|
+
const resource = useAsyncResource(symbols.length && criteria.value ? loader : null);
|
|
83
|
+
useEffect(() => () => controller.current?.abort(), [loader]);
|
|
84
|
+
useAutoRefresh(resource.updatedAt, resource.load);
|
|
85
|
+
const [sort, setSort] = useState<{ columnId: string; direction: SortDirection }>({ columnId: "rank", direction: "asc" });
|
|
86
|
+
const rows = useMemo<ScreenRow[]>(() => {
|
|
87
|
+
const flat = (resource.data?.rows ?? []).map((row) => ({ ...cspScreenReportRow(row), source: row }));
|
|
88
|
+
// Unranked names stay under the ranked ones whichever way rank sorts.
|
|
89
|
+
return flat.sort((left, right) => sort.columnId === "rank"
|
|
90
|
+
? compareSortValues(left.rank ?? Number.POSITIVE_INFINITY, right.rank ?? Number.POSITIVE_INFINITY, sort.direction)
|
|
91
|
+
: compareSortValues(sortValue(left, sort.columnId), sortValue(right, sort.columnId), sort.direction));
|
|
92
|
+
}, [resource.data, sort]);
|
|
93
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
94
|
+
const screen = resource.data;
|
|
95
|
+
const notices = [criteria.error, resource.data ? null : resource.error,
|
|
96
|
+
leftOut.length ? `Ranks the first ${CSP_SCREEN_LIMIT} symbols; ${leftOut.length} left out: ${leftOut.join(", ")}` : null,
|
|
97
|
+
...(screen?.ivRankError ? [`IV rank unavailable: ${screen.ivRankError}`] : []),
|
|
98
|
+
...(screen?.rows ?? []).flatMap((row) => row.note ? [`${row.symbol}: ${row.note}`] : []),
|
|
99
|
+
...(screen ? cspScreenNotes(screen).slice(1) : [])].filter((value): value is string => !!value);
|
|
100
|
+
usePaneNoticeFooter({ registrationId: "csp-screen-notices", notices, focused });
|
|
101
|
+
// The bar names the delta and the expiry; the footer says only what changes.
|
|
102
|
+
usePaneFooter("csp-screen", () => ({ info: [
|
|
103
|
+
...(resource.loading ? [{ id: "loading", parts: [{ text: "loading", tone: "muted" as const }] }] : []),
|
|
104
|
+
] }), [resource.loading]);
|
|
105
|
+
const criteriaText = criteria.value ? cspScreenCriteriaText(criteria.value, Date.now()) : "";
|
|
106
|
+
// Enter opens the name's put in OSA, at the strike and expiry the screen read.
|
|
107
|
+
const openScenario = (row: ScreenRow) => {
|
|
108
|
+
const csp = row.source.csp;
|
|
109
|
+
if (!csp) return;
|
|
110
|
+
createPaneFromTemplate("options-scenario-pane", { symbol: row.symbol, values: { strategy: "csp", strike: String(csp.strike), expiration: csp.expiry } });
|
|
111
|
+
};
|
|
112
|
+
const handleKey = (event: DataTableKeyEvent): boolean => {
|
|
113
|
+
if (event.ctrl || event.alt || event.meta || event.name !== "r") return false;
|
|
114
|
+
void resource.reload();
|
|
115
|
+
event.preventDefault?.(); event.stopPropagation?.(); return true;
|
|
116
|
+
};
|
|
117
|
+
const cell = (row: ScreenRow, id: string): { text: string; color?: string } => {
|
|
118
|
+
switch (id) {
|
|
119
|
+
case "rank": return { text: row.rank == null ? "" : String(row.rank), color: colors.textDim };
|
|
120
|
+
case "symbol": return { text: row.symbol, color: colors.textBright };
|
|
121
|
+
case "expiry": return { text: String(row.expiry ?? "--"), color: colors.textDim };
|
|
122
|
+
case "days": return { text: row.days == null ? "--" : String(row.days) };
|
|
123
|
+
case "strike": return { text: row.strike == null ? "--" : String(row.strike) };
|
|
124
|
+
case "bid": return { text: priceText(row.bid) };
|
|
125
|
+
case "ask": return { text: priceText(row.ask) };
|
|
126
|
+
case "spread": return { text: `${percentText(row.spread, 1)}${row.wide ? " wide" : ""}`, color: row.wide ? colors.warning : undefined };
|
|
127
|
+
case "delta": return { text: typeof row.delta === "number" ? row.delta.toFixed(2) : "--" };
|
|
128
|
+
case "otm": return { text: percentText(row.otm, 1) };
|
|
129
|
+
case "midYield": return { text: percentText(row.midYield, 2), color: colors.textDim };
|
|
130
|
+
case "bidYield": return { text: percentText(row.bidYield, 2), color: colors.positive };
|
|
131
|
+
case "ivRank": return { text: typeof row.ivRank === "number" ? row.ivRank.toFixed(0) : "--" };
|
|
132
|
+
case "earnings": return { text: String(row.earnings), color: row.earningsBeforeExpiry === true ? colors.warning : colors.textDim };
|
|
133
|
+
default: return { text: "" };
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const deltaValue = criteria.value ? criteria.value.delta.toFixed(2) : CSP_SCREEN_DEFAULTS.delta.toFixed(2);
|
|
137
|
+
const daysValue = String(criteria.value?.days ?? CSP_SCREEN_DEFAULTS.days);
|
|
138
|
+
// A date typed on the command line is the expiry's choice until a number of days is picked.
|
|
139
|
+
const typedDate = criteria.value?.expiration != null ? cspTargetDate(criteria.value, Date.now()) : null;
|
|
140
|
+
return <Box width={width} height={height} flexDirection="column" overflow="hidden">
|
|
141
|
+
<QueryBar width={width} filters={[
|
|
142
|
+
{ id: "symbols", kind: "text" as const, label: "Symbols", value: String(symbolsText ?? ""), placeholder: "JPM, GS, KO",
|
|
143
|
+
width: SYMBOLS_FIELD_WIDTH, debounceMs: SYMBOLS_DEBOUNCE_MS, focused, active: symbolsActive, onActiveChange: setSymbolsActive,
|
|
144
|
+
onChange: setSymbolsText },
|
|
145
|
+
{ id: "delta", label: "Delta", value: deltaValue, defaultValue: CSP_SCREEN_DEFAULTS.delta.toFixed(2),
|
|
146
|
+
options: withCurrent(DELTA_OPTIONS, deltaValue, (value) => value), onChange: (value: string) => setDelta(value) },
|
|
147
|
+
{ id: "expiry", label: "Expiry", value: typedDate ?? daysValue, defaultValue: String(CSP_SCREEN_DEFAULTS.days),
|
|
148
|
+
options: [...withCurrent(DAYS_OPTIONS, daysValue, (value) => `${value} days`), ...(typedDate ? [{ value: typedDate, label: typedDate }] : [])],
|
|
149
|
+
onChange: (value: string) => { if (value === typedDate) return; setExpiration(""); setDays(value); } },
|
|
150
|
+
]} />
|
|
151
|
+
<PaneStatusBody subject="cash-secured puts" loading={resource.loading && !resource.data}
|
|
152
|
+
error={criteria.error && !criteria.value ? criteria.error : !resource.data ? resource.error : null}>
|
|
153
|
+
<DataTableView<ScreenRow> focused={focused && !symbolsActive} columns={COLUMNS} items={rows} rootWidth={width}
|
|
154
|
+
rootHeight={Math.max(2, height - 1)}
|
|
155
|
+
getItemKey={(row) => row.symbol} sortColumnId={sort.columnId} sortDirection={sort.direction}
|
|
156
|
+
emptyStateTitle={symbols.length ? "No puts to rank." : "Type symbols to rank their cash-secured puts."}
|
|
157
|
+
onHeaderClick={(id) => setSort((current) => nextHeaderSort(current, id, { firstDirection: id === "rank" || id === "symbol" ? "asc" : "desc" }))}
|
|
158
|
+
selection={{ kind: "id", selectedId: selected ?? rows[0]?.symbol ?? "", getId: (row) => row.symbol, onChange: (id) => setSelected(id) }}
|
|
159
|
+
onActivate={openScenario} onRootKeyDown={handleKey}
|
|
160
|
+
getExportMetadata={() => [["puts", criteriaText || undefined], ["ranked by", "annualised yield on cash at the bid"],
|
|
161
|
+
["wide", criteria.value ? `spread over ${criteria.value.maxSpreadPercent}% of the mid` : undefined]]}
|
|
162
|
+
renderCell={(row, column) => cell(row, column.id)} />
|
|
163
|
+
</PaneStatusBody>
|
|
164
|
+
</Box>;
|
|
165
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { InstrumentRef } from "../../../market-data/request-types";
|
|
2
|
+
import type { DataProvider } from "../../../types/data-provider";
|
|
3
|
+
import type { HeadlessPaneFreshness } from "../../../types/plugin";
|
|
4
|
+
import { mapWithConcurrency } from "../../../utils/concurrency";
|
|
5
|
+
import { numberErrorMessage, wholeNumberOptionMessage } from "../../../utils/decimal-comma";
|
|
6
|
+
import { errorMessage } from "../../../utils/errors";
|
|
7
|
+
import { expirationHint, expiredExpiryText, expiryIsoDate, isLiveOptionExpiry, readOptionExpiration } from "../../../utils/option-expiry";
|
|
8
|
+
import { zonedDateKey } from "../../../utils/zoned-date-time";
|
|
9
|
+
import { ivRankReason, loadIvScreen, type ImpliedVolatilityApi, type IvScreenRow } from "../iv-history/client";
|
|
10
|
+
import { quoteObservation } from "../shared/report-freshness";
|
|
11
|
+
import { loadScenarioMarket, scenarioPositionFromSettings, scenarioSpotBasis, type ScenarioLoaderDependencies, type ScenarioMarketSnapshot } from "./client";
|
|
12
|
+
import { cashSecuredPut, loadNextEarningsDates, type CashSecuredPut } from "./csp";
|
|
13
|
+
import { seededLegQuotes } from "./hedge";
|
|
14
|
+
import { scenarioValue } from "./model";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A cash-secured put screen (docs/research-data.md#cash-secured-put-screen):
|
|
18
|
+
* for each name, the put OSA's `--strategy csp --delta` seed sells on the
|
|
19
|
+
* listed expiry nearest the target, read by the same `cashSecuredPut`, ranked
|
|
20
|
+
* by its annualised yield at the bid.
|
|
21
|
+
*/
|
|
22
|
+
export interface CspScreenCriteria {
|
|
23
|
+
/** The absolute put delta each name's put is nearest, such as 0.30. */
|
|
24
|
+
delta: number;
|
|
25
|
+
/** Calendar days out the expiry is nearest, when no date is given. */
|
|
26
|
+
days: number;
|
|
27
|
+
/** The date the expiry is nearest, in Unix seconds, in place of `days`; null when none is given. */
|
|
28
|
+
expiration: number | null;
|
|
29
|
+
/** A spread over this percent of the midpoint marks the put wide. */
|
|
30
|
+
maxSpreadPercent: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const CSP_SCREEN_DEFAULTS: Readonly<Omit<CspScreenCriteria, "expiration">> = { delta: 0.3, days: 30, maxSpreadPercent: 10 };
|
|
34
|
+
/** The most names one screen reads: each is two chain requests. */
|
|
35
|
+
export const CSP_SCREEN_LIMIT = 20;
|
|
36
|
+
const DAY_MS = 86_400_000;
|
|
37
|
+
const CONCURRENCY = 4;
|
|
38
|
+
|
|
39
|
+
export interface CspScreenRow {
|
|
40
|
+
symbol: string;
|
|
41
|
+
/** 1 for the highest yield at the bid; null for a name with no put to rank. */
|
|
42
|
+
rank: number | null;
|
|
43
|
+
csp: CashSecuredPut | null;
|
|
44
|
+
/** The put's delta per share, negative, at the IV its midpoint implies. */
|
|
45
|
+
delta: number | null;
|
|
46
|
+
/** 30-day IV rank, 0 to 100, against the prior 52 weeks; null when IV history has none. */
|
|
47
|
+
ivRank: number | null;
|
|
48
|
+
/** Its spread is over the screen's `maxSpreadPercent` of the midpoint. */
|
|
49
|
+
wideSpread: boolean;
|
|
50
|
+
/** Why the name has no put to rank. */
|
|
51
|
+
note: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CspScreen {
|
|
55
|
+
rows: CspScreenRow[];
|
|
56
|
+
criteria: CspScreenCriteria;
|
|
57
|
+
/** Names IV history has no rank for, with why when it can say, such as `XYZ (no options history)`; a bare symbol has no reason to give. */
|
|
58
|
+
missingRanks: string[];
|
|
59
|
+
/** The IV rank request failed for every name. */
|
|
60
|
+
ivRankError: string | null;
|
|
61
|
+
freshness: HeadlessPaneFreshness;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const supplied = (value: unknown) => value != null && value !== "";
|
|
65
|
+
|
|
66
|
+
function readNumber(value: unknown, flag: string, example: string): number {
|
|
67
|
+
const text = typeof value === "number" ? String(value) : typeof value === "string" ? value.trim().replace(/%$/, "") : "";
|
|
68
|
+
const number = Number(text);
|
|
69
|
+
if (!text || !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text) || !Number.isFinite(number)) {
|
|
70
|
+
throw new Error(numberErrorMessage(`Invalid --${flag} "${String(value)}". Use ${example}.`, text));
|
|
71
|
+
}
|
|
72
|
+
return number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `--days`: whole calendar days. A decimal is told the whole numbers beside it, never a decimal to type. */
|
|
76
|
+
function readDays(value: unknown): number {
|
|
77
|
+
const decimal = wholeNumberOptionMessage("--days", "days", value);
|
|
78
|
+
if (decimal) throw new Error(decimal);
|
|
79
|
+
const days = readNumber(value, "days", "whole calendar days, such as 30");
|
|
80
|
+
if (!Number.isInteger(days) || days < 1) throw new Error(`Invalid --days "${String(value)}". Use whole calendar days, such as 30.`);
|
|
81
|
+
return days;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The screen's criteria from `fn CSP` options or the pane's settings, each default where not set. */
|
|
85
|
+
export function readCspScreenCriteria(options: Record<string, unknown>): CspScreenCriteria {
|
|
86
|
+
const delta = supplied(options.delta) ? Math.abs(readNumber(options.delta, "delta", "a put's delta between 0 and 1, such as 0.30")) : CSP_SCREEN_DEFAULTS.delta;
|
|
87
|
+
if (!(delta > 0 && delta < 1)) throw new Error(`Invalid --delta "${String(options.delta)}". Use a put's delta between 0 and 1, such as 0.30.`);
|
|
88
|
+
const days = supplied(options.days) ? readDays(options.days) : CSP_SCREEN_DEFAULTS.days;
|
|
89
|
+
const maxSpreadPercent = supplied(options.maxSpread)
|
|
90
|
+
? readNumber(options.maxSpread, "max-spread", "a percent of the midpoint above 0, such as 10") : CSP_SCREEN_DEFAULTS.maxSpreadPercent;
|
|
91
|
+
if (!(maxSpreadPercent > 0)) throw new Error(`Invalid --max-spread "${String(options.maxSpread)}". Use a percent of the midpoint above 0, such as 10.`);
|
|
92
|
+
const expiration = supplied(options.expiration) ? readOptionExpiration(options.expiration) : null;
|
|
93
|
+
if (supplied(options.expiration) && expiration == null) throw new Error(`Invalid --expiration "${String(options.expiration)}". Use a date such as 2026-11-20.${expirationHint(String(options.expiration))}`);
|
|
94
|
+
if (expiration != null && supplied(options.days)) throw new Error("--days and --expiration each pick the expiry; give one of them");
|
|
95
|
+
return { delta, days, expiration, maxSpreadPercent };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The date each name's expiry is nearest, YYYY-MM-DD: the typed date, else `days` out from today in New York. */
|
|
99
|
+
export function cspTargetDate(criteria: Pick<CspScreenCriteria, "days" | "expiration">, now: number): string {
|
|
100
|
+
if (criteria.expiration != null) return expiryIsoDate(criteria.expiration);
|
|
101
|
+
return new Date(Date.parse(`${zonedDateKey(now, "America/New_York")}T00:00:00Z`) + criteria.days * DAY_MS).toISOString().slice(0, 10);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The listed expiry whose date is nearest `target` (YYYY-MM-DD), the earlier on a tie; undefined when none is listed. */
|
|
105
|
+
export function nearestListedExpiry(expirations: readonly number[], target: string): number | undefined {
|
|
106
|
+
const targetTime = Date.parse(`${target}T00:00:00Z`);
|
|
107
|
+
let best: { expiration: number; distance: number } | undefined;
|
|
108
|
+
for (const expiration of [...expirations].sort((a, b) => a - b)) {
|
|
109
|
+
const distance = Math.abs(Date.parse(`${expiryIsoDate(expiration)}T00:00:00Z`) - targetTime);
|
|
110
|
+
if (Number.isFinite(distance) && (!best || distance < best.distance)) best = { expiration, distance };
|
|
111
|
+
}
|
|
112
|
+
return best?.expiration;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const bidYield = (row: CspScreenRow) => row.csp?.quote?.bidAnnualizedYield ?? null;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Highest annualised yield at the bid first; a wide spread is ranked with the
|
|
119
|
+
* rest and flagged, never left out. A put with no yield to rank (it expires
|
|
120
|
+
* today) follows, then the names with no put, by symbol.
|
|
121
|
+
*/
|
|
122
|
+
export function rankCspScreen(rows: readonly Omit<CspScreenRow, "rank">[]): CspScreenRow[] {
|
|
123
|
+
const tier = (row: Omit<CspScreenRow, "rank">) => bidYield(row as CspScreenRow) != null ? 0 : row.csp ? 1 : 2;
|
|
124
|
+
const sorted = [...rows].sort((left, right) => tier(left) - tier(right)
|
|
125
|
+
|| (bidYield(right as CspScreenRow) ?? 0) - (bidYield(left as CspScreenRow) ?? 0)
|
|
126
|
+
|| left.symbol.localeCompare(right.symbol));
|
|
127
|
+
let rank = 0;
|
|
128
|
+
return sorted.map((row) => ({ ...row, rank: tier(row) === 0 ? ++rank : null }));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Whether a put's spread is over the screen's limit. */
|
|
132
|
+
function cspWideSpread(csp: CashSecuredPut | null, maxSpreadPercent: number): boolean {
|
|
133
|
+
return csp?.quote != null && csp.quote.spreadOfMid * 100 > maxSpreadPercent;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface CspScreenDependencies {
|
|
137
|
+
scenario: ScenarioLoaderDependencies;
|
|
138
|
+
/** The earnings calendar's source. */
|
|
139
|
+
earnings: DataProvider | null | undefined;
|
|
140
|
+
/** Gloom Cloud's IV history; the app's client when omitted. */
|
|
141
|
+
ivApi?: ImpliedVolatilityApi;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface ScreenedPut { csp: CashSecuredPut; delta: number | null; market: ScenarioMarketSnapshot }
|
|
145
|
+
|
|
146
|
+
/** One name's put: OSA's csp seed at `--delta` on the listed expiry nearest the target, read as OSA reads it. */
|
|
147
|
+
async function screenPut(
|
|
148
|
+
instrument: InstrumentRef, criteria: CspScreenCriteria, dependencies: ScenarioLoaderDependencies, target: string,
|
|
149
|
+
earningsDate: string | null, signal: AbortSignal | undefined,
|
|
150
|
+
): Promise<ScreenedPut> {
|
|
151
|
+
const first = await loadScenarioMarket({ instrument, signal }, dependencies);
|
|
152
|
+
// A typed date whose last session has closed is not stood in for by the next one: it says so, and names it.
|
|
153
|
+
const now = dependencies.now?.() ?? Date.now();
|
|
154
|
+
if (criteria.expiration != null && !isLiveOptionExpiry(criteria.expiration, now, instrument.exchange)) {
|
|
155
|
+
throw new Error(`Expiry ${expiryIsoDate(criteria.expiration)} `
|
|
156
|
+
+ expiredExpiryText(criteria.expiration, first.expirationDates, now, { exchange: instrument.exchange, flag: "expiration" }));
|
|
157
|
+
}
|
|
158
|
+
const expiration = nearestListedExpiry(first.expirationDates, target);
|
|
159
|
+
if (expiration == null) {
|
|
160
|
+
throw new Error(first.warnings.find((warning) => warning.startsWith("Options chain")) ?? "No listed option expiries");
|
|
161
|
+
}
|
|
162
|
+
const loaded = first.chain ? [...first.chain.puts, ...first.chain.calls][0]?.expiration : undefined;
|
|
163
|
+
const market = loaded != null && expiryIsoDate(loaded) === expiryIsoDate(expiration)
|
|
164
|
+
? first : await loadScenarioMarket({ instrument, expiration, signal }, dependencies);
|
|
165
|
+
const settings = { symbol: instrument.symbol, exchange: instrument.exchange, strategy: "csp", delta: criteria.delta };
|
|
166
|
+
const position = scenarioPositionFromSettings(settings, market);
|
|
167
|
+
const leg = position?.legs[0];
|
|
168
|
+
if (!position || !leg) throw new Error("No put to sell on this chain");
|
|
169
|
+
const quote = seededLegQuotes(position, market.chain).get(leg.id);
|
|
170
|
+
const csp = cashSecuredPut(position, scenarioSpotBasis(settings, position, market), earningsDate, quote);
|
|
171
|
+
if (!csp) throw new Error("No put to sell on this chain");
|
|
172
|
+
const shares = leg.quantity * leg.multiplier;
|
|
173
|
+
const delta = scenarioValue(position, position.spot, position.asOf).delta / shares;
|
|
174
|
+
return { csp, delta: Number.isFinite(delta) ? delta : null, market };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function loadIvRanks(symbols: readonly string[], signal: AbortSignal | undefined, api: ImpliedVolatilityApi | undefined) {
|
|
178
|
+
try {
|
|
179
|
+
const payload = await loadIvScreen(symbols, { signal }, api);
|
|
180
|
+
return { rows: new Map<string, IvScreenRow>(payload.rows.map((row) => [row.symbol.toUpperCase(), row])), error: null };
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (signal?.aborted) throw error;
|
|
183
|
+
return { rows: new Map<string, IvScreenRow>(), error: errorMessage(error) ?? "no response" };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* How current the screen is: each name's underlying quote dates its row, and
|
|
189
|
+
* the chains' feed (delayed when any is) says how far behind they run.
|
|
190
|
+
*/
|
|
191
|
+
function screenFreshness(markets: readonly ScenarioMarketSnapshot[], now: number): HeadlessPaneFreshness {
|
|
192
|
+
const chains = markets.flatMap((market) => market.chain ? [market.chain] : []);
|
|
193
|
+
const delayed = chains.some((chain) => chain.dataSource === "delayed");
|
|
194
|
+
const live = chains.length > 0 && chains.every((chain) => chain.dataSource === "live");
|
|
195
|
+
const delayMinutes = Math.max(0, ...chains.map((chain) => chain.delayMinutes ?? 0));
|
|
196
|
+
const observations = markets.flatMap((market) => market.underlyingQuote ? [{ ...quoteObservation(market.underlyingQuote, now) }] : []);
|
|
197
|
+
return {
|
|
198
|
+
...(delayed ? { status: "delayed" as const } : live ? { status: "live" as const } : {}),
|
|
199
|
+
...(delayed && delayMinutes ? { delayMinutes } : {}),
|
|
200
|
+
...(observations.length ? { observations } : markets[0] ? { asOf: markets[0].asOf } : {}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Each name's cash-secured put, ranked: one IV rank request, one earnings
|
|
206
|
+
* calendar request and one Treasury curve for all of them.
|
|
207
|
+
*/
|
|
208
|
+
export async function loadCspScreen(
|
|
209
|
+
instruments: readonly InstrumentRef[], criteria: CspScreenCriteria, dependencies: CspScreenDependencies, signal?: AbortSignal,
|
|
210
|
+
): Promise<CspScreen> {
|
|
211
|
+
const now = dependencies.scenario.now?.() ?? Date.now();
|
|
212
|
+
// Every name reads the same curve, up to twice (the catalogue, then its expiry): asked for once, when the first name needs it.
|
|
213
|
+
let curve: ReturnType<ScenarioLoaderDependencies["loadYieldCurve"]> | null = null;
|
|
214
|
+
const scenario: ScenarioLoaderDependencies = { ...dependencies.scenario, loadYieldCurve: () => curve ??= dependencies.scenario.loadYieldCurve() };
|
|
215
|
+
const symbols = instruments.map((instrument) => instrument.symbol.trim().toUpperCase());
|
|
216
|
+
const [ivRanks, earnings] = await Promise.all([
|
|
217
|
+
loadIvRanks(symbols, signal, dependencies.ivApi),
|
|
218
|
+
loadNextEarningsDates(dependencies.earnings, symbols, zonedDateKey(now, "America/New_York")),
|
|
219
|
+
]);
|
|
220
|
+
signal?.throwIfAborted();
|
|
221
|
+
const target = cspTargetDate(criteria, now);
|
|
222
|
+
const markets: ScenarioMarketSnapshot[] = [];
|
|
223
|
+
const missingRanks: string[] = [];
|
|
224
|
+
const rows = await mapWithConcurrency(instruments, CONCURRENCY, async (instrument, index): Promise<Omit<CspScreenRow, "rank">> => {
|
|
225
|
+
const symbol = symbols[index]!;
|
|
226
|
+
const ivRow = ivRanks.rows.get(symbol);
|
|
227
|
+
const rank = ivRow?.iv30?.rank;
|
|
228
|
+
const ivRank = rank != null && Number.isFinite(rank) ? rank : null;
|
|
229
|
+
try {
|
|
230
|
+
const screened = await screenPut(instrument, criteria, scenario, target, earnings.get(symbol) ?? null, signal);
|
|
231
|
+
markets.push(screened.market);
|
|
232
|
+
if (ivRank == null && !ivRanks.error) {
|
|
233
|
+
const reason = ivRankReason(ivRow);
|
|
234
|
+
missingRanks.push(reason ? `${symbol} (${reason})` : symbol);
|
|
235
|
+
}
|
|
236
|
+
return { symbol, csp: screened.csp, delta: screened.delta, ivRank, wideSpread: cspWideSpread(screened.csp, criteria.maxSpreadPercent), note: null };
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (signal?.aborted) throw error;
|
|
239
|
+
return { symbol, csp: null, delta: null, ivRank, wideSpread: false, note: errorMessage(error) ?? "No put to sell" };
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return { rows: rankCspScreen(rows), criteria, missingRanks: missingRanks.sort(), ivRankError: ivRanks.error,
|
|
243
|
+
freshness: screenFreshness(markets, now) };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** `nearest 0.30 delta · expiry nearest 2026-11-21 (30 days)`: what each name's put is. */
|
|
247
|
+
export function cspScreenCriteriaText(criteria: CspScreenCriteria, now: number): string {
|
|
248
|
+
return `nearest ${criteria.delta.toFixed(2)} delta · expiry nearest ${cspTargetDate(criteria, now)}`
|
|
249
|
+
+ (criteria.expiration == null ? ` (${criteria.days} days)` : "");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** `before 2026-11-04`, `after 2027-01-19`, `unknown`: whether the next report falls before the put expires, and its date. */
|
|
253
|
+
export function cspEarningsText(csp: CashSecuredPut | null): string {
|
|
254
|
+
if (!csp) return "--";
|
|
255
|
+
const { beforeExpiry, date } = csp.earnings;
|
|
256
|
+
return beforeExpiry == null ? "unknown" : `${beforeExpiry ? "before" : "after"} ${date}`;
|
|
257
|
+
}
|
|
258
|
+
|
|
@@ -2,8 +2,11 @@ import type { StatItem } from "../../../components";
|
|
|
2
2
|
import type { DataProvider } from "../../../types/data-provider";
|
|
3
3
|
import type { HeadlessPaneEntry } from "../../../types/headless";
|
|
4
4
|
import { loadEarningsCalendar } from "../earnings/data/cache";
|
|
5
|
+
import { optionSpread } from "../options/market-reference";
|
|
6
|
+
import { newYorkDay, resolveListingEvents, upcomingReportDate } from "../shared/earnings-calendar";
|
|
7
|
+
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
5
8
|
import { formatHedgePrice } from "./hedge";
|
|
6
|
-
import { UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
|
|
9
|
+
import { amountUnit, exportPercent, UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* A cash-secured put (docs/research-data.md#cash-secured-put): one short put
|
|
@@ -15,6 +18,9 @@ import { UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
|
|
|
15
18
|
* seed enters at the quote midpoint
|
|
16
19
|
* - yield on cash = premium / cash secured
|
|
17
20
|
* - annualised yield = yield on cash x 365 / days to expiry (simple, not compounded)
|
|
21
|
+
* - the same premium and yields at the bid, the price a sell order fills at
|
|
22
|
+
* without crossing the spread, when the put has a two-sided quote
|
|
23
|
+
* - spread = ask - bid, and as a percent of the midpoint
|
|
18
24
|
* - out of the money = (spot - strike) / spot, against the scenario's spot
|
|
19
25
|
* - break-even = strike - entry price
|
|
20
26
|
* - days to expiry = calendar days from the valuation date in New York to the expiry date
|
|
@@ -38,6 +44,8 @@ export interface CashSecuredPut {
|
|
|
38
44
|
yieldOnCash: number;
|
|
39
45
|
/** Null on the expiry day, when there is no day to annualise over. */
|
|
40
46
|
annualizedYield: number | null;
|
|
47
|
+
/** The put's two-sided quote and what selling at its bid yields; null without one. */
|
|
48
|
+
quote: CspQuote | null;
|
|
41
49
|
daysToExpiry: number;
|
|
42
50
|
/** Valuation date in New York, YYYY-MM-DD. */
|
|
43
51
|
valuationDate: string;
|
|
@@ -50,6 +58,20 @@ export interface CashSecuredPut {
|
|
|
50
58
|
earnings: CspEarnings;
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
interface CspQuote {
|
|
62
|
+
bid: number;
|
|
63
|
+
ask: number;
|
|
64
|
+
/** ask - bid, per share. */
|
|
65
|
+
spread: number;
|
|
66
|
+
/** The spread over the midpoint, as a fraction. */
|
|
67
|
+
spreadOfMid: number;
|
|
68
|
+
/** Premium at the bid, for every contract. */
|
|
69
|
+
bidPremium: number;
|
|
70
|
+
bidYieldOnCash: number;
|
|
71
|
+
/** Null on the expiry day. */
|
|
72
|
+
bidAnnualizedYield: number | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
53
75
|
export interface CspEarnings {
|
|
54
76
|
/** The next report date, YYYY-MM-DD, or null when none is published. */
|
|
55
77
|
date: string | null;
|
|
@@ -71,12 +93,22 @@ export function earningsBeforeExpiry(earningsDate: string | null | undefined, va
|
|
|
71
93
|
return { date: earningsDate, beforeExpiry: earningsDate <= expiry };
|
|
72
94
|
}
|
|
73
95
|
|
|
96
|
+
/** What selling at `price` per share earns on the cash secured: the one yield calculation, at the entry price and at the bid alike. */
|
|
97
|
+
function putYield(price: number, cashSecured: number, sharesSold: number, daysToExpiry: number) {
|
|
98
|
+
const premium = Math.round(price * sharesSold * 1e6) / 1e6;
|
|
99
|
+
const yieldOnCash = premium / cashSecured;
|
|
100
|
+
return { premium, yieldOnCash, annualizedYield: daysToExpiry > 0 ? yieldOnCash * 365 / daysToExpiry : null };
|
|
101
|
+
}
|
|
102
|
+
|
|
74
103
|
/**
|
|
75
104
|
* The position as a cash-secured put, or null when it is not one short put.
|
|
76
|
-
* `spotBasis` names the spot the out-of-the-money distance is read against
|
|
105
|
+
* `spotBasis` names the spot the out-of-the-money distance is read against;
|
|
106
|
+
* `quote` is the put's bid and ask, for a put seeded from the chain
|
|
107
|
+
* (`seededLegQuotes`).
|
|
77
108
|
*/
|
|
78
109
|
export function cashSecuredPut(
|
|
79
110
|
position: ScenarioPosition, spotBasis: string, earningsDate: string | null | undefined,
|
|
111
|
+
quote?: { bid: number; ask: number } | null,
|
|
80
112
|
): CashSecuredPut | null {
|
|
81
113
|
const [leg] = position.legs;
|
|
82
114
|
if (position.legs.length !== 1 || !leg || leg.side !== "put" || leg.quantity >= 0 || position.underlying) return null;
|
|
@@ -85,12 +117,19 @@ export function cashSecuredPut(
|
|
|
85
117
|
const valuationDate = newYorkCalendarDate(position.asOf);
|
|
86
118
|
const daysToExpiry = Math.max(0, Math.round((Date.parse(`${expiry}T00:00:00Z`) - Date.parse(`${valuationDate}T00:00:00Z`)) / DAY_MS));
|
|
87
119
|
const cashSecured = leg.strike * leg.multiplier * contracts;
|
|
88
|
-
const
|
|
89
|
-
const
|
|
120
|
+
const sharesSold = leg.multiplier * contracts;
|
|
121
|
+
const atEntry = putYield(leg.price, cashSecured, sharesSold, daysToExpiry);
|
|
122
|
+
const spread = quote ? optionSpread(quote) : null;
|
|
123
|
+
let bidQuote: CspQuote | null = null;
|
|
124
|
+
if (quote && spread?.kind === "two-sided") {
|
|
125
|
+
const atBid = putYield(quote.bid, cashSecured, sharesSold, daysToExpiry);
|
|
126
|
+
bidQuote = { bid: quote.bid, ask: quote.ask, spread: spread.spread, spreadOfMid: spread.percentOfMid / 100,
|
|
127
|
+
bidPremium: atBid.premium, bidYieldOnCash: atBid.yieldOnCash, bidAnnualizedYield: atBid.annualizedYield };
|
|
128
|
+
}
|
|
90
129
|
return {
|
|
91
130
|
currency: position.currency, strike: leg.strike, expiry, contracts, multiplier: leg.multiplier,
|
|
92
131
|
price: leg.price, priceBasis: leg.priceSource === "mid" ? "mid" : "entry",
|
|
93
|
-
cashSecured,
|
|
132
|
+
cashSecured, ...atEntry, quote: bidQuote,
|
|
94
133
|
daysToExpiry, valuationDate, spot: position.spot, spotBasis,
|
|
95
134
|
outOfTheMoney: position.spot > 0 ? (position.spot - leg.strike) / position.spot : null,
|
|
96
135
|
breakEven: leg.strike - leg.price,
|
|
@@ -99,15 +138,31 @@ export function cashSecuredPut(
|
|
|
99
138
|
}
|
|
100
139
|
|
|
101
140
|
/**
|
|
102
|
-
* The next report date
|
|
103
|
-
* calendar
|
|
141
|
+
* The next report date, YYYY-MM-DD, as every earnings surface resolves it
|
|
142
|
+
* (shared/earnings-calendar): the calendar's, or once that date has passed
|
|
143
|
+
* the next one on record; null when there is none or the calendar cannot be
|
|
144
|
+
* read.
|
|
104
145
|
*/
|
|
105
|
-
export async function loadNextEarningsDate(provider: DataProvider | null | undefined, symbol: string): Promise<string | null> {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
146
|
+
export async function loadNextEarningsDate(provider: DataProvider | null | undefined, symbol: string, today = newYorkDay()): Promise<string | null> {
|
|
147
|
+
return (await loadNextEarningsDates(provider, [symbol], today)).get(symbol.trim().toUpperCase()) ?? null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The next report date of each symbol, by its upper-case symbol, from one calendar request; a symbol with none is left out. */
|
|
151
|
+
export async function loadNextEarningsDates(
|
|
152
|
+
provider: DataProvider | null | undefined, symbols: readonly string[], today = newYorkDay(),
|
|
153
|
+
): Promise<Map<string, string>> {
|
|
154
|
+
const keys = [...new Set(symbols.map((symbol) => symbol.trim().toUpperCase()))];
|
|
155
|
+
const result = await loadEarningsCalendar(provider, keys).catch(() => null);
|
|
156
|
+
if (!provider || !result?.events.length) return new Map();
|
|
157
|
+
const listings = keys.map((key) => {
|
|
158
|
+
const listing = parsePublicTickerKey(key);
|
|
159
|
+
return { key, symbol: listing.symbol, exchange: listing.exchange ?? "" };
|
|
160
|
+
});
|
|
161
|
+
const resolved = await resolveListingEvents(provider, listings, result.events, today);
|
|
162
|
+
return new Map(keys.flatMap((key) => {
|
|
163
|
+
const date = upcomingReportDate(resolved.get(key));
|
|
164
|
+
return date ? [[key, date] as const] : [];
|
|
165
|
+
}));
|
|
111
166
|
}
|
|
112
167
|
|
|
113
168
|
const money = (value: number) => value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
@@ -129,22 +184,40 @@ function earningsText(earnings: CspEarnings): string {
|
|
|
129
184
|
|
|
130
185
|
const priceWord = (csp: CashSecuredPut) => csp.priceBasis === "mid" ? "quote mid" : "entry price";
|
|
131
186
|
|
|
132
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* The report's Cash-secured put section. Each figure's `value` is in its
|
|
189
|
+
* `unit`, every digit (a percent as a percent), so an export writes the number
|
|
190
|
+
* and its unit; `formatted` is what text output prints.
|
|
191
|
+
*/
|
|
133
192
|
export function cspReportEntries(csp: CashSecuredPut): HeadlessPaneEntry[] {
|
|
134
193
|
const unit = unitOf(csp);
|
|
194
|
+
const amount = { unit: amountUnit(csp.currency) };
|
|
195
|
+
const perShare = `${amount.unit} per share`;
|
|
196
|
+
const quote = csp.quote;
|
|
135
197
|
return [
|
|
136
|
-
{ key: "cashSecured", label: "Cash secured", value: csp.cashSecured,
|
|
198
|
+
{ key: "cashSecured", label: "Cash secured", value: csp.cashSecured, ...amount,
|
|
137
199
|
formatted: `${money(csp.cashSecured)}${unit} (${strikeText(csp.strike)} x ${csp.multiplier} x ${csp.contracts})` },
|
|
138
|
-
{ key: "premium", label: "Premium received", value: csp.premium,
|
|
200
|
+
{ key: "premium", label: "Premium received", value: csp.premium, ...amount,
|
|
139
201
|
formatted: `${money(csp.premium)}${unit} (${formatHedgePrice(csp.price)} per share, ${priceWord(csp)})` },
|
|
140
|
-
{ key: "yieldOnCash", label: "Yield on cash", value: csp.yieldOnCash,
|
|
202
|
+
{ key: "yieldOnCash", label: "Yield on cash", value: exportPercent(csp.yieldOnCash), unit: "%",
|
|
141
203
|
formatted: `${percent(csp.yieldOnCash)} (${money(csp.premium)} / ${money(csp.cashSecured)})` },
|
|
142
|
-
{ key: "annualizedYield", label: "Annualised yield", value: csp.annualizedYield,
|
|
204
|
+
{ key: "annualizedYield", label: "Annualised yield", value: exportPercent(csp.annualizedYield), unit: "%",
|
|
143
205
|
formatted: csp.annualizedYield == null ? "-- (expires today)" : `${percent(csp.annualizedYield)} (yield on cash x 365 / ${csp.daysToExpiry})` },
|
|
144
|
-
|
|
145
|
-
|
|
206
|
+
...(quote ? [
|
|
207
|
+
{ key: "bid", label: "Bid", value: quote.bid, unit: perShare, formatted: `${formatHedgePrice(quote.bid)} per share` },
|
|
208
|
+
{ key: "ask", label: "Ask", value: quote.ask, unit: perShare, formatted: `${formatHedgePrice(quote.ask)} per share` },
|
|
209
|
+
{ key: "spread", label: "Spread", value: exportPercent(quote.spreadOfMid), unit: "% of mid",
|
|
210
|
+
formatted: `${formatHedgePrice(quote.spread)} per share (${percent(quote.spreadOfMid, 1)} of the mid)` },
|
|
211
|
+
{ key: "bidPremium", label: "Premium at the bid", value: quote.bidPremium, ...amount,
|
|
212
|
+
formatted: `${money(quote.bidPremium)}${unit} (${formatHedgePrice(quote.bid)} x ${csp.multiplier} x ${csp.contracts})` },
|
|
213
|
+
{ key: "bidAnnualizedYield", label: "Annualised yield at the bid", value: exportPercent(quote.bidAnnualizedYield), unit: "%",
|
|
214
|
+
formatted: quote.bidAnnualizedYield == null ? "-- (expires today)"
|
|
215
|
+
: `${percent(quote.bidAnnualizedYield)} (${percent(quote.bidYieldOnCash)} on cash x 365 / ${csp.daysToExpiry})` },
|
|
216
|
+
] : []),
|
|
217
|
+
{ key: "outOfTheMoney", label: "Out of the money", value: exportPercent(csp.outOfTheMoney), unit: "%", formatted: otmText(csp) },
|
|
218
|
+
{ key: "breakEven", label: "Break-even", value: csp.breakEven, unit: perShare,
|
|
146
219
|
formatted: `${formatHedgePrice(csp.breakEven)} per share (${strikeText(csp.strike)} - ${formatHedgePrice(csp.price)})` },
|
|
147
|
-
{ key: "daysToExpiry", label: "Days to expiry", value: csp.daysToExpiry,
|
|
220
|
+
{ key: "daysToExpiry", label: "Days to expiry", value: csp.daysToExpiry, unit: "days",
|
|
148
221
|
formatted: `${csp.daysToExpiry} (${csp.valuationDate} to ${csp.expiry})` },
|
|
149
222
|
{ key: "earningsBeforeExpiry", label: "Earnings before expiry", value: csp.earnings.beforeExpiry, formatted: earningsText(csp.earnings) },
|
|
150
223
|
];
|
|
@@ -153,11 +226,18 @@ export function cspReportEntries(csp: CashSecuredPut): HeadlessPaneEntry[] {
|
|
|
153
226
|
/** The pane's figures: the same numbers as the report, sized for a StatGrid. */
|
|
154
227
|
export function cspStatItems(csp: CashSecuredPut): StatItem[] {
|
|
155
228
|
const earnings = csp.earnings;
|
|
229
|
+
const quote = csp.quote;
|
|
156
230
|
return [
|
|
157
231
|
{ id: "csp-cash", label: "Cash secured", value: money(csp.cashSecured), detail: `${strikeText(csp.strike)} x ${csp.multiplier * csp.contracts}` },
|
|
158
232
|
{ id: "csp-premium", label: "Premium", value: money(csp.premium), detail: `${priceWord(csp)} ${formatHedgePrice(csp.price)}` },
|
|
159
233
|
{ id: "csp-yield", label: "Yield", value: percent(csp.yieldOnCash),
|
|
160
234
|
detail: csp.annualizedYield == null ? "expires today" : `${percent(csp.annualizedYield)} a year` },
|
|
235
|
+
...(quote ? [
|
|
236
|
+
{ id: "csp-bid", label: "Bid / ask", value: `${formatHedgePrice(quote.bid)} / ${formatHedgePrice(quote.ask)}`,
|
|
237
|
+
detail: `spread ${percent(quote.spreadOfMid, 1)} of mid` },
|
|
238
|
+
{ id: "csp-bid-yield", label: "At the bid", value: quote.bidAnnualizedYield == null ? "--" : percent(quote.bidAnnualizedYield),
|
|
239
|
+
detail: quote.bidAnnualizedYield == null ? "expires today" : "a year" },
|
|
240
|
+
] : []),
|
|
161
241
|
{ id: "csp-otm", label: "OTM", value: csp.outOfTheMoney == null ? "--" : percent(csp.outOfTheMoney, 1),
|
|
162
242
|
detail: `vs ${money(csp.spot)} ${csp.spotBasis === "implied by option quotes" ? "implied" : csp.spotBasis}` },
|
|
163
243
|
{ id: "csp-break-even", label: "Break-even", value: formatHedgePrice(csp.breakEven), detail: "per share" },
|