gloomberb 0.18.3 → 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/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 +84 -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 +22 -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 +21 -4
- 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/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- 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/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- 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 +1117 -264
- 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 +192 -47
- 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 +19 -13
- 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 +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- 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 +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- 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 +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- 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 +47 -10
- 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 +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- 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 +198 -68
- 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 +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- 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 +305 -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 +13 -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/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/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 +12 -6
- package/src/components/layout/header.tsx +7 -6
- 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/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/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 +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -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 +29 -19
- 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 +107 -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 +76 -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 +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- 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 +6 -3
- 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 +22 -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 +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- 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 +25 -12
- 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/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 +6 -8
- 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 +127 -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 +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- 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 +79 -23
- 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 +1 -1
- 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 +5 -3
- 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 +3 -2
- 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/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/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 +18 -11
- 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 +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- 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 +6 -1
- 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/headless.ts +9 -4
- 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 +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- 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/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- 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/headless.ts +4 -4
- 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 +10 -4
- 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 +33 -19
- 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 +29 -8
- 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 +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- 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 +36 -15
- 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 +9 -5
- 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 +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- 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 +4 -1
- 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 +8 -1
- 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/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -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 +2 -1
- 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-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 +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.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/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 +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- 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/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/styles.css +76 -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/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -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/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 +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- 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 +1 -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 +32 -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/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 +17 -4
- 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 +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- 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 +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -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/index.ts +10 -5
- 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-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 +3 -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,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" },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
3
|
+
import { DATE_OPTION_EXAMPLES } from "../../../utils/calendar-date";
|
|
3
4
|
import {
|
|
4
5
|
expirationOptionSeconds, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER, readOptionExpiration,
|
|
5
6
|
} from "../../../utils/option-expiry";
|
|
@@ -11,8 +12,8 @@ import {
|
|
|
11
12
|
import { scenarioFreshness } from "./freshness";
|
|
12
13
|
import { cashSecuredPut, cspReportEntries, loadNextEarningsDate } from "./csp";
|
|
13
14
|
import { formatSpotTime } from "../options/model-spot";
|
|
14
|
-
import { buildScenario, currencyLabel, parseLegs, scenarioValueUnit } from "./model";
|
|
15
|
-
import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget } from "./hedge";
|
|
15
|
+
import { amountUnit, buildScenario, currencyLabel, exportPercent, parseLegs, scenarioValueUnit } from "./model";
|
|
16
|
+
import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget, seededLegQuotes } from "./hedge";
|
|
16
17
|
import { collarReportEntries, scenarioCollar } from "./collar";
|
|
17
18
|
import { modelDividendYieldNote } from "../../../market-data/model-dividend-yield";
|
|
18
19
|
import { listingFateEmpty, loadListingFate } from "../../../tickers/listing-fate";
|
|
@@ -32,6 +33,12 @@ const VALUATION_LABELS: Record<string, (currency: string) => string> = {
|
|
|
32
33
|
thetaPerDay: (currency) => `Theta (${currency} per day)`, vegaPerPoint: (currency) => `Vega (${currency} per vol point)`,
|
|
33
34
|
rhoPerPoint: (currency) => `Rho (${currency} per rate point)`,
|
|
34
35
|
};
|
|
36
|
+
/** The unit an export names beside each Valuation figure: `per` is the currency for the position (`USD per contract`). */
|
|
37
|
+
const VALUATION_UNITS: Record<string, (currency: string, per: string) => string> = {
|
|
38
|
+
price: (_currency, per) => per, pnl: (_currency, per) => per, delta: () => "shares", gamma: () => "shares per 1.00 move",
|
|
39
|
+
thetaPerDay: (currency) => `${currency} per day`, vegaPerPoint: (currency) => `${currency} per vol point`,
|
|
40
|
+
rhoPerPoint: (currency) => `${currency} per rate point`,
|
|
41
|
+
};
|
|
35
42
|
|
|
36
43
|
export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
37
44
|
shape: "bundle", argument: { kind: "ticker", description: "Underlying ticker" },
|
|
@@ -40,7 +47,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
40
47
|
description: "A typed or chain-seeded options position valued across spot, date and volatility: its legs, value and Greeks, expiry risk and a "
|
|
41
48
|
+ "scenario P&L grid. With --nav and --budget-bps, how many contracts a hedge budget buys, the premium spent, the notional protected and "
|
|
42
49
|
+ "coverage of an equity sleeve. --strategy csp sells a cash-secured put: cash secured, premium, yield on cash, annualised yield, "
|
|
43
|
-
+ "distance out of the money, break-even, days to expiry and whether earnings fall
|
|
50
|
+
+ "the bid, ask and spread with the yield at the bid, distance out of the money, break-even, days to expiry and whether earnings fall "
|
|
51
|
+
+ "before expiry; CSP ranks the same put across several names. --strategy collar buys a put and sells "
|
|
44
52
|
+ "a call against the shares they cover: net premium, floor, cap and breakevens of the hedged position.",
|
|
45
53
|
discovery: { screenshotReadiness: "ready", limitations: ["European scenario values do not model early assignment or exercise."] },
|
|
46
54
|
options: [
|
|
@@ -53,6 +61,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
53
61
|
+ "csp, one short put sold at its midpoint with the cash to buy the shares set aside (a cash-secured put), "
|
|
54
62
|
+ "or collar, one long put and one short call on the same expiry with the shares they cover" },
|
|
55
63
|
{ key: "strike", type: "string", description: "The --strategy strike: the put, the vertical's long call or the straddle. Nearest the money when omitted" },
|
|
64
|
+
{ key: "delta", type: "string", placeholder: "0.30", example: "--strategy csp --delta 0.30 --expiration 2026-11-20",
|
|
65
|
+
description: "The --strategy csp or put put by its delta instead of --strike: the one whose delta is nearest this, as a put's delta reads without its sign" },
|
|
56
66
|
{ key: "putStrike", aliases: ["put-strike"], type: "string",
|
|
57
67
|
description: "The --strategy collar put bought. The quoted strike nearest 10% below spot when omitted" },
|
|
58
68
|
{ key: "callStrike", aliases: ["call-strike"], type: "string",
|
|
@@ -67,8 +77,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
67
77
|
{ key: "rate", type: "string", description: "Continuously compounded annual risk-free rate, percent" },
|
|
68
78
|
{ key: "dividendYield", aliases: ["dividend-yield"], type: "string", description: "Continuous annual dividend yield, percent" },
|
|
69
79
|
{ key: "currency", type: "string", description: "Currency of the inputs, such as USD; the underlying's when omitted" },
|
|
70
|
-
{ key: "asOf", aliases: ["as-of"], type: "string", description:
|
|
71
|
-
{ key: "date", type: "string", description:
|
|
80
|
+
{ key: "asOf", aliases: ["as-of"], type: "string", description: `Position valuation date, as ${DATE_OPTION_EXAMPLES}, or a UTC ISO timestamp` },
|
|
81
|
+
{ key: "date", type: "string", description: `Scenario date, as ${DATE_OPTION_EXAMPLES}, or a UTC ISO timestamp` },
|
|
72
82
|
{ key: "volShift", aliases: ["vol-shift"], type: "string", defaultValue: "0", description: "Parallel volatility shift, percentage points" },
|
|
73
83
|
{ key: "spotRange", aliases: ["spot-range"], type: "string", defaultValue: "30", description: "Spot grid range above and below current spot, percent" },
|
|
74
84
|
{ key: "nav", type: "string", description: "Portfolio NAV for a hedge budget, such as 100m, 2.5bn, 250k, 1,000,000 or $100m. Needs --budget-bps" },
|
|
@@ -78,6 +88,11 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
78
88
|
],
|
|
79
89
|
async load(args, ctx) {
|
|
80
90
|
const symbol = args.symbols[0]!;
|
|
91
|
+
// Several names to sell puts on are a screen, not one scenario.
|
|
92
|
+
const names = args.rawArgument.split(/[\s,;]+/).filter(Boolean);
|
|
93
|
+
if (names.length > 1 && args.options.strategy === "csp") {
|
|
94
|
+
throw new Error(`OSA values one underlying. To rank cash-secured puts across ${names.join(", ")}: gloom fn CSP ${names.join(" ")}`);
|
|
95
|
+
}
|
|
81
96
|
const settings: Record<string, unknown> = { ...ctx.settings, ...args.options, symbol };
|
|
82
97
|
// Bad hedge inputs fail before any market request.
|
|
83
98
|
hedgeInputsFromSettings(settings);
|
|
@@ -118,7 +133,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
118
133
|
const lastPrint = midVolatility && !supplied(settings.spot) && market.spot != null && money(market.spot) !== money(position!.spot)
|
|
119
134
|
? market.spot : null;
|
|
120
135
|
const spotBasis = position ? scenarioSpotBasis(settings, position, market) : null;
|
|
121
|
-
const csp = position && settings.strategy === "csp"
|
|
136
|
+
const csp = position && settings.strategy === "csp"
|
|
137
|
+
? cashSecuredPut(position, spotBasis!, earningsDate, seededLegQuotes(position, market.chain).get(position.legs[0]?.id ?? "")) : null;
|
|
122
138
|
// A collar seeded, or one typed with the shares it hedges.
|
|
123
139
|
const collar = position && (settings.strategy === "collar" || position.underlying) ? scenarioCollar(position, spotBasis!) : null;
|
|
124
140
|
const lastSession = market.underlyingQuote && spotBasis === "implied by option quotes"
|
|
@@ -129,6 +145,10 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
129
145
|
// Value, P&L, risk and the grid add every contract; entry prices and spot are per share.
|
|
130
146
|
const valueUnit = position ? scenarioValueUnit(position) : null;
|
|
131
147
|
const per = `${currency} ${valueUnit}`;
|
|
148
|
+
// What an export names beside each number: the currency (or `currency unknown`) and what it is for.
|
|
149
|
+
const exportCurrency = position ? amountUnit(position.currency) : "";
|
|
150
|
+
const exportPer = `${exportCurrency} ${valueUnit}`;
|
|
151
|
+
const perShare = `${exportCurrency} ${PRICE_UNIT}`;
|
|
132
152
|
return {
|
|
133
153
|
freshness: scenarioFreshness(market, suppliedInputs),
|
|
134
154
|
sections: scenario ? [
|
|
@@ -141,16 +161,16 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
141
161
|
], rows: position!.legs.map((leg) => ({ ...leg, expiry: new Date(leg.expiration * 1000).toISOString().slice(0, 10),
|
|
142
162
|
...(midVolatility ? { volatilityFrom: leg.volatilitySource === "mid" ? "quote mid" : "provider" } : {}) })) },
|
|
143
163
|
{ title: "Inputs", entries: [
|
|
144
|
-
{ key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot,
|
|
164
|
+
{ key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot, unit: perShare,
|
|
145
165
|
formatted: `${money(position!.spot)} ${PRICE_UNIT}${lastPrint == null && spotBasis !== "last price" ? ` (${scenarioSpotStamp(spotBasis!, position!.asOf)})` : ""}` },
|
|
146
|
-
...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint,
|
|
166
|
+
...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint, unit: perShare,
|
|
147
167
|
formatted: `${money(lastPrint)} ${PRICE_UNIT}${lastSession && lastSession !== "last price" ? ` (${scenarioSpotStamp(lastSession, market.asOf)})` : ""}` }] : []),
|
|
148
|
-
...(position!.underlying ? [{ key: "shares", label: "Shares held", value: position!.underlying.quantity,
|
|
168
|
+
...(position!.underlying ? [{ key: "shares", label: "Shares held", value: position!.underlying.quantity, unit: "shares",
|
|
149
169
|
formatted: `${position!.underlying.quantity.toLocaleString("en-US")} at ${money(position!.underlying.price)} ${PRICE_UNIT}` }] : []),
|
|
150
170
|
{ key: "asOf", label: "Valued at", value: new Date(position!.asOf).toISOString(), formatted: formatSpotTime(position!.asOf) },
|
|
151
|
-
{ key: "rate", label: "Rate", value: position!.rate, formatted: percent(position!.rate) },
|
|
171
|
+
{ key: "rate", label: "Rate", value: exportPercent(position!.rate), unit: "%", formatted: percent(position!.rate) },
|
|
152
172
|
// A yield read as 0 because no dividend was paid, or the dividend stopped, says so; a stated or typed one is the figure alone.
|
|
153
|
-
{ key: "dividendYield", label: "Dividend yield", value: position!.dividendYield, formatted: percent(position!.dividendYield)
|
|
173
|
+
{ key: "dividendYield", label: "Dividend yield", value: exportPercent(position!.dividendYield), unit: "%", formatted: percent(position!.dividendYield)
|
|
154
174
|
+ (dividendNote ? ` (${dividendNote})` : "") },
|
|
155
175
|
{ key: "currency", label: "Currency", value: position!.currency, formatted: currency! },
|
|
156
176
|
] },
|
|
@@ -158,12 +178,13 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
158
178
|
...(csp ? [{ title: "Cash-secured put", entries: cspReportEntries(csp) }] : []),
|
|
159
179
|
...(collar ? [{ title: "Collar", entries: collarReportEntries(collar) }] : []),
|
|
160
180
|
{ title: `Valuation (${per})`, entries: Object.entries(scenario.valuation).map(([key, value]) => ({ key,
|
|
161
|
-
label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value)
|
|
181
|
+
label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value),
|
|
182
|
+
...(VALUATION_UNITS[key] ? { unit: VALUATION_UNITS[key]!(exportCurrency, exportPer) } : {}) })) },
|
|
162
183
|
{ title: `Expiry risk (${per})`, entries: [
|
|
163
|
-
{ key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens,
|
|
184
|
+
{ key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens, unit: perShare,
|
|
164
185
|
formatted: scenario.expiryRisk.breakevens.map(money).join(", ") || "none" },
|
|
165
|
-
{ key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
|
|
166
|
-
{ key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
|
|
186
|
+
{ key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, unit: exportPer, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
|
|
187
|
+
{ key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, unit: exportPer, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
|
|
167
188
|
...(scenario.expiryRisk.reason ? [{ key: "limitation", label: "Limitation", value: scenario.expiryRisk.reason }] : []),
|
|
168
189
|
] },
|
|
169
190
|
{ title: `Scenario grid (P&L, ${per})`, columns: [
|