gloomberb 0.16.3 → 0.16.5
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 +14 -91
- package/package.json +7 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +42 -17
- package/src/account/upgrade-personalization.ts +57 -0
- package/src/account/upgrade-tickers.ts +124 -0
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/askg.ts +43 -0
- package/src/api-client/auth.ts +15 -0
- package/src/api-client/beneficial-owners.ts +4 -0
- package/src/api-client/chat.ts +19 -0
- package/src/api-client/company-kpis.ts +14 -2
- package/src/api-client/data.ts +58 -18
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +16 -1
- package/src/api-client/market-discovery.ts +10 -1
- package/src/api-client/normalizers.ts +27 -0
- package/src/api-client/paths.ts +95 -23
- package/src/api-client/request.ts +2 -1
- package/src/api-client/research-activity.ts +46 -10
- package/src/api-client/socket.ts +39 -8
- package/src/api-client/types.ts +74 -2
- package/src/api-client/upgrade-experiment.ts +90 -0
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +26 -2
- package/src/app/keybindings/actions.ts +10 -0
- package/src/app/pane-runtime/index.tsx +8 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
- package/src/app/pane-runtime/plugin-bindings.ts +65 -25
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +25 -4
- package/src/app/runtime/desktop-deeplink.ts +5 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +13 -7
- package/src/architecture/core-builtin-allowlist.json +61 -0
- package/src/brokers/signed-in/adapter.ts +33 -2
- package/src/brokers/signed-in/client.ts +10 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +34 -14
- package/src/brokers/signed-in/sign-in.ts +62 -5
- package/src/capabilities/chart-series.ts +11 -1
- package/src/capabilities/registry.ts +8 -1
- package/src/capabilities/types.ts +2 -0
- package/src/cli/commands/crypto-hints.ts +19 -4
- package/src/cli/commands/fred-series.ts +61 -0
- package/src/cli/commands/market.ts +744 -181
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +425 -0
- package/src/cli/commands/overview.ts +251 -40
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +109 -15
- package/src/cli/commands/system.ts +151 -24
- package/src/cli/commands/themes.ts +64 -0
- package/src/cli/commands/ticker.ts +149 -40
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +150 -17
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/errors.ts +25 -2
- package/src/cli/freshness.ts +22 -8
- package/src/cli/fx-pair.ts +57 -0
- package/src/cli/glossary.ts +429 -0
- package/src/cli/help.ts +14 -1
- package/src/cli/history-rows.ts +48 -7
- package/src/cli/index-roots.ts +58 -0
- package/src/cli/index.ts +29 -12
- package/src/cli/known-gaps.ts +27 -0
- package/src/cli/listing-arg.ts +595 -26
- package/src/cli/not-a-ticker.ts +44 -0
- package/src/cli/options.ts +50 -6
- package/src/cli/pane-functions/access-gate.ts +5 -0
- package/src/cli/pane-functions/capabilities.ts +11 -4
- package/src/cli/pane-functions/catalog.ts +48 -7
- package/src/cli/pane-functions/data.ts +48 -13
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +41 -19
- package/src/cli/pane-functions/freshness.ts +531 -32
- package/src/cli/pane-functions/function-help.ts +77 -25
- package/src/cli/pane-functions/headless.ts +50 -21
- package/src/cli/pane-functions/index.ts +66 -10
- package/src/cli/pane-functions/market-session.ts +147 -0
- package/src/cli/pane-functions/options.ts +77 -25
- package/src/cli/pane-functions/report-notices.ts +81 -0
- package/src/cli/pane-functions/report.ts +19 -8
- package/src/cli/pane-functions/resolver.ts +53 -18
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +199 -30
- package/src/cli/registry.ts +2 -1
- package/src/cli/report-tables.ts +3 -0
- package/src/cli/result.ts +53 -13
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scroll-truncation.ts +34 -0
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +9 -0
- package/src/cli/symbol-list.ts +172 -0
- package/src/cli/ticker-resolution.ts +223 -30
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +6 -2
- package/src/components/chart/composite/composite-chart.tsx +83 -10
- package/src/components/chart/composite/legend.tsx +88 -8
- package/src/components/chart/composite/panel-surface.tsx +44 -17
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/chart/composite/rasterizer.ts +64 -4
- package/src/components/chart/composite/series-cues.ts +71 -0
- package/src/components/chart/composite/text-renderer.ts +16 -8
- package/src/components/chart/composite/types.ts +3 -0
- package/src/components/command-bar/commands/direct/index.ts +9 -1
- package/src/components/command-bar/commands/plugin/items.ts +3 -1
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/command-items.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/root/results.ts +40 -10
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/root/search-providers.ts +97 -14
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/surface/index.tsx +18 -4
- package/src/components/command-bar/theme-picker.tsx +48 -13
- package/src/components/command-bar/workflow/ops.ts +5 -3
- package/src/components/command-bar/workflow/tickers.ts +23 -4
- package/src/components/data-table/view.tsx +28 -9
- package/src/components/layout/detached-pane-shell.tsx +9 -0
- package/src/components/layout/floating-pane.tsx +9 -2
- package/src/components/layout/header.tsx +20 -5
- package/src/components/layout/market-summary.ts +23 -9
- package/src/components/layout/pane/content.tsx +40 -29
- package/src/components/layout/pane/cycle-order.ts +2 -11
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/model.ts +35 -1
- package/src/components/layout/pane/header.tsx +26 -2
- package/src/components/layout/pane/index.tsx +12 -2
- package/src/components/layout/pane/sidebar.tsx +78 -10
- package/src/components/layout/pane/title.ts +1 -1
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/drag/index.ts +43 -9
- package/src/components/layout/shell/drag/live.ts +87 -6
- package/src/components/layout/shell/drag/runtime.ts +62 -20
- package/src/components/layout/shell/index.tsx +71 -12
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +33 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +216 -28
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/shortcuts.ts +5 -2
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-text.tsx +69 -23
- package/src/components/metric-treemap/heat-surface.tsx +37 -9
- package/src/components/metric-treemap/heat-tiles.ts +48 -17
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-frame.tsx +33 -10
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +50 -41
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +8 -4
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +24 -1
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +17 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +49 -0
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +20 -1
- package/src/components/ui/data-table/visible-range.ts +2 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +13 -2
- package/src/components/ui/index.ts +1 -1
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/core/connection-health.ts +1 -1
- package/src/core/state/app/layout-reducer.ts +89 -3
- package/src/core/state/app/state.ts +8 -5
- package/src/core/state/app/types.ts +13 -2
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/layout.ts +55 -21
- package/src/data/config/store/fold-panes.ts +149 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/node.ts +27 -6
- package/src/data/config/store/normalize.ts +44 -5
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7416 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +75 -5
- package/src/i18n/ja.ts +75 -5
- package/src/i18n/ko.ts +75 -5
- package/src/i18n/zh-cn.ts +75 -5
- package/src/i18n/zh-tw.ts +75 -5
- package/src/layout/desks.ts +2 -3
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/entries.ts +7 -1
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/market-data/coordinator/quotes.ts +2 -2
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +193 -30
- package/src/market-data/market/status.ts +59 -23
- package/src/market-data/options-alternatives.ts +69 -0
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +22 -2
- package/src/market-data/quotes/metadata.ts +8 -9
- package/src/market-data/quotes/resolution.ts +2 -0
- package/src/market-data/quotes/screener-live-quotes.ts +61 -9
- package/src/market-data/result-types.ts +2 -0
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +2 -56
- package/src/plugins/builtin/account-management/navigation.ts +1 -1
- package/src/plugins/builtin/account-management/pane.tsx +37 -8
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +41 -9
- package/src/plugins/builtin/alerts/pane.tsx +8 -0
- package/src/plugins/builtin/alerts/quotes.ts +3 -0
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +4 -2
- package/src/plugins/builtin/analytics/risk-headless.ts +33 -13
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +71 -12
- package/src/plugins/builtin/analytics/risk-pane.tsx +23 -12
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/pane.tsx +3 -1
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/backtest/pane.tsx +1 -1
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/broker-manager/model.ts +8 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +5 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +70 -13
- package/src/plugins/builtin/cds/headless.ts +116 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +193 -31
- package/src/plugins/builtin/cds/pane.tsx +56 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
- package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless.ts +8 -3
- package/src/plugins/builtin/chart-composer/index.tsx +9 -2
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/channels.ts +34 -36
- package/src/plugins/builtin/chat/command-bar.ts +138 -0
- package/src/plugins/builtin/chat/composer-commands.ts +21 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
- package/src/plugins/builtin/chat/content/footer.tsx +5 -3
- package/src/plugins/builtin/chat/content/index.tsx +21 -1
- package/src/plugins/builtin/chat/content/mentions.ts +2 -1
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/content/user-map.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +50 -0
- package/src/plugins/builtin/chat/conversations.ts +256 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +12 -6
- package/src/plugins/builtin/chat/ghost-user.ts +14 -0
- package/src/plugins/builtin/chat/index.tsx +40 -2
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
- package/src/plugins/builtin/chat/message/header.tsx +24 -15
- package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
- package/src/plugins/builtin/chat/message/render-state.ts +3 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +32 -2
- package/src/plugins/builtin/chat/pane.tsx +8 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +223 -23
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +33 -7
- package/src/plugins/builtin/cloud/askg/model.ts +156 -78
- package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
- package/src/plugins/builtin/cloud/askg/protocol.ts +50 -3
- package/src/plugins/builtin/cloud/askg/tool-display.ts +440 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +363 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +36 -1
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +734 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +239 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +162 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +192 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/comment-letters/client.ts +1 -3
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +19 -5
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/client.ts +23 -8
- package/src/plugins/builtin/company-kpis/headless.ts +3 -1
- package/src/plugins/builtin/company-kpis/pane.tsx +19 -9
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
- package/src/plugins/builtin/composite-plugins.ts +116 -67
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
- package/src/plugins/builtin/credit-boards/index.tsx +23 -21
- package/src/plugins/builtin/credit-boards/model.ts +0 -1
- package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
- package/src/plugins/builtin/credit-conditions/series.ts +26 -0
- package/src/plugins/builtin/credit-documents/headless.ts +31 -1
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/client.ts +37 -9
- package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
- package/src/plugins/builtin/dividend-yield/index.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/earnings-calls/data.ts +28 -7
- package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +42 -17
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +70 -22
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +30 -5
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/econ-statistics/view.ts +17 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/executives/data.ts +17 -6
- package/src/plugins/builtin/executives/pane.tsx +16 -7
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/pane.tsx +3 -1
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/filing-events/pane.tsx +22 -5
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +70 -9
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
- package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +64 -27
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +114 -20
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/index.tsx +18 -8
- package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +62 -14
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +42 -134
- package/src/plugins/builtin/holders/pane.tsx +14 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +27 -10
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/iv-history/charts.tsx +21 -7
- package/src/plugins/builtin/iv-history/client.ts +26 -6
- package/src/plugins/builtin/iv-history/format.ts +33 -2
- package/src/plugins/builtin/iv-history/headless.ts +27 -12
- package/src/plugins/builtin/iv-history/index.tsx +5 -3
- package/src/plugins/builtin/iv-history/model.ts +172 -20
- package/src/plugins/builtin/iv-history/pane.tsx +11 -3
- package/src/plugins/builtin/iv-history/screen-pane.tsx +45 -25
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +28 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/pane.tsx +1 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +111 -40
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
- package/src/plugins/builtin/layout-manager/index.tsx +9 -1
- package/src/plugins/builtin/macro-day/client.ts +68 -3
- package/src/plugins/builtin/macro-day/headless.ts +18 -12
- package/src/plugins/builtin/macro-day/index.tsx +3 -0
- package/src/plugins/builtin/macro-day/model.ts +66 -15
- package/src/plugins/builtin/macro-day/pane.tsx +34 -20
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +62 -44
- package/src/plugins/builtin/market-heatmap/data.ts +25 -5
- package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
- package/src/plugins/builtin/market-heatmap/model.ts +102 -3
- package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/headless.ts +31 -4
- package/src/plugins/builtin/market-movers/index.tsx +11 -3
- package/src/plugins/builtin/market-movers/model.ts +101 -17
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-movers/table.ts +50 -23
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +7 -2
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +3 -6
- package/src/plugins/builtin/mna/headless.ts +15 -3
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/news/headless.ts +9 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/openfda/client.ts +1 -1
- package/src/plugins/builtin/options/carry.ts +95 -0
- package/src/plugins/builtin/options/index.tsx +58 -5
- package/src/plugins/builtin/options/strike-window.ts +183 -0
- package/src/plugins/builtin/options/table.ts +55 -3
- package/src/plugins/builtin/options/types.ts +8 -1
- package/src/plugins/builtin/options/view.tsx +162 -13
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +5 -2
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +75 -18
- package/src/plugins/builtin/options-scenario/headless.ts +72 -24
- package/src/plugins/builtin/options-scenario/hedge.ts +285 -0
- package/src/plugins/builtin/options-scenario/index.tsx +9 -2
- package/src/plugins/builtin/options-scenario/model.ts +33 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
- package/src/plugins/builtin/pe-band/model.ts +2 -2
- package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
- package/src/plugins/builtin/perps/client.ts +40 -6
- package/src/plugins/builtin/perps/equity-row.tsx +33 -8
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +45 -30
- package/src/plugins/builtin/perps/index.tsx +18 -1
- package/src/plugins/builtin/perps/model.ts +32 -6
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
- package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +357 -39
- package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
- package/src/plugins/builtin/plugin-module.ts +90 -4
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +400 -99
- package/src/plugins/builtin/portfolio-list/cli/render.ts +281 -43
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +26 -16
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +70 -9
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +6 -2
- package/src/plugins/builtin/power/headless.ts +71 -7
- package/src/plugins/builtin/power/index.tsx +4 -1
- package/src/plugins/builtin/power/model.ts +72 -1
- package/src/plugins/builtin/power/pane.tsx +32 -9
- package/src/plugins/builtin/rate-path/headless.ts +11 -3
- package/src/plugins/builtin/realized-vol/headless.ts +8 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +7 -4
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
- package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
- package/src/plugins/builtin/risk-factors/data.ts +23 -9
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/sec/index.tsx +5 -5
- package/src/plugins/builtin/sectors/client.ts +10 -2
- package/src/plugins/builtin/sectors/headless.ts +35 -6
- package/src/plugins/builtin/sectors/index.tsx +18 -1
- package/src/plugins/builtin/sectors/sector-model.ts +150 -41
- package/src/plugins/builtin/series-inventory.ts +91 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +69 -0
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
- package/src/plugins/builtin/shared/members-menu.tsx +4 -4
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +59 -2
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/shared/ticker-request.ts +25 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
- package/src/plugins/builtin/shared/volatility/options.ts +1 -46
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +21 -4
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/surface.tsx +4 -4
- package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +1 -0
- package/src/plugins/builtin/short-volume/index.tsx +3 -10
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
- package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +1 -0
- package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
- package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
- package/src/plugins/builtin/treasury-auctions/headless.ts +56 -6
- package/src/plugins/builtin/treasury-auctions/index.tsx +12 -9
- package/src/plugins/builtin/treasury-auctions/model.ts +205 -30
- package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
- package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
- package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
- package/src/plugins/builtin/vol-surface/client.ts +6 -2
- package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
- package/src/plugins/builtin/vol-surface/headless.ts +125 -20
- package/src/plugins/builtin/vol-surface/model.ts +23 -11
- package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
- package/src/plugins/builtin/vol-surface/tables.ts +124 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-indices/client.ts +22 -4
- package/src/plugins/builtin/world-indices/headless.ts +54 -27
- package/src/plugins/builtin/world-indices/model.ts +4 -2
- package/src/plugins/builtin/world-indices/table.tsx +13 -2
- package/src/plugins/builtin/world-venue-map/geo-series.ts +27 -2
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +4 -2
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/catalog-backend.ts +16 -9
- package/src/plugins/catalog-browser.ts +129 -64
- package/src/plugins/catalog-manifest.ts +73 -40
- package/src/plugins/catalog-ui.ts +26 -6
- package/src/plugins/extracted-plugins.ts +7 -1
- package/src/plugins/ownership.ts +150 -7
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/registry/host-actions.ts +16 -0
- package/src/plugins/registry/index.ts +173 -28
- package/src/plugins/registry/plugin-state.ts +25 -1
- package/src/plugins/runtime/context.tsx +29 -0
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/react/input.ts +48 -1
- package/src/remote/app-host.tsx +25 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +126 -59
- package/src/remote/matching.ts +69 -0
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +63 -1
- package/src/remote/semantic-tree.tsx +14 -0
- package/src/remote/types.ts +32 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +27 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +21 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/box.tsx +4 -1
- package/src/renderers/dom/host/chart-surface.tsx +6 -2
- package/src/renderers/dom/host/drag-shield.ts +17 -6
- package/src/renderers/dom/host/live-frame.ts +14 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/size-observer.ts +26 -0
- package/src/renderers/dom/host/surface-3d.tsx +7 -2
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +74 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +18 -3
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +109 -5
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +6 -1
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +107 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +145 -0
- package/src/series/validate.ts +171 -0
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +26 -17
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -1
- package/src/sources/history-result.ts +1 -0
- package/src/sources/listing-symbols.ts +14 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +9 -5
- package/src/sources/provider-router/financial-routes.ts +12 -5
- package/src/sources/provider-router/financials.ts +55 -15
- package/src/sources/provider-router/history.ts +68 -34
- package/src/sources/provider-router/index.ts +1 -1
- package/src/sources/provider-router/primary.ts +23 -5
- package/src/sources/provider-router/search.ts +1 -0
- package/src/sources/sec-edgar.ts +68 -8
- package/src/sync/core-contributors.ts +5 -3
- package/src/test-support/cli-context.ts +2 -1
- package/src/test-support/data-provider.ts +34 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/themes.ts +149 -0
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +25 -0
- package/src/tickers/open-target.ts +3 -2
- package/src/tickers/search/index.ts +67 -9
- package/src/tickers/search/ranking.ts +78 -18
- package/src/tickers/search/types.ts +10 -2
- package/src/tickers/starter-symbols.ts +8 -0
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/resolve.ts +96 -30
- package/src/time-series/types.ts +4 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +67 -1
- package/src/types/data-provider.ts +5 -0
- package/src/types/financials.ts +52 -1
- package/src/types/headless.ts +72 -0
- package/src/types/instrument.ts +8 -0
- package/src/types/plugin.ts +68 -3
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/host.tsx +26 -4
- package/src/ui/index.tsx +2 -0
- package/src/ui/live-resize.ts +81 -0
- package/src/ui/live-stretch.ts +121 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/pane-fill.ts +11 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/cli-output.ts +21 -2
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +437 -52
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +120 -1
- package/src/utils/format.ts +17 -3
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +44 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history.ts +130 -40
- package/src/utils/rpc-timeout-error.ts +6 -2
- package/src/utils/sec-eps-basis.ts +178 -36
- package/src/utils/sec.ts +98 -40
- package/src/utils/throttled-fetch.ts +51 -22
- package/src/utils/utc-time.ts +51 -4
|
@@ -6,9 +6,12 @@ import { isPlainKey } from "../../../utils/keyboard";
|
|
|
6
6
|
import { formatCompact, formatPercentRaw } from "../../../utils/format";
|
|
7
7
|
import { formatMarketPrice, formatSignedMarketPrice, liveQuoteFormatOptions } from "../../../market-data/market/format";
|
|
8
8
|
import { formatExpDate, resolveOptionsTarget } from "../../../utils/options";
|
|
9
|
+
import { expiryIsoDate, findListedExpiry, formatDaysToExpiry, daysToExpiry, missingExpiryText, readOptionExpiration } from "../../../utils/option-expiry";
|
|
9
10
|
import { canonicalTickerKey } from "../../../utils/exchanges";
|
|
11
|
+
import { isOptionsUnavailableError, listedOptionsAlternative, optionsUnavailableTitle } from "../../../market-data/options-alternatives";
|
|
10
12
|
import { useChartQueries, useOptionsQuery, useResolvedEntryValue, useTickerFinancials } from "../../../market-data/hooks";
|
|
11
13
|
import {
|
|
14
|
+
Button,
|
|
12
15
|
DataTableView,
|
|
13
16
|
EmptyState,
|
|
14
17
|
usePaneFooter,
|
|
@@ -57,7 +60,7 @@ import {
|
|
|
57
60
|
import { useOptionsAccessFooter } from "./footer";
|
|
58
61
|
import { AnalyticsAsOfDialog, analyticsAsOfRows } from "./analytics-as-of";
|
|
59
62
|
import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
|
|
60
|
-
import { signedPositionDirection } from "
|
|
63
|
+
import { signedPositionDirection } from "../../../portfolio/position-metrics";
|
|
61
64
|
import { optionMarketReference } from "./market-reference";
|
|
62
65
|
import { useOptionsEnrichment } from "./enrichment";
|
|
63
66
|
import type { OptionsEnrichmentSnapshot } from "./enrichment-model";
|
|
@@ -67,6 +70,19 @@ import type { TickerFinancials } from "../../../types/financials";
|
|
|
67
70
|
import type { IvStats } from "../iv-history/client";
|
|
68
71
|
import { useIvRank } from "../iv-history/rank";
|
|
69
72
|
import { useOptionsSessionOpen, useThrottledValue } from "../shared/volatility/live-session";
|
|
73
|
+
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
74
|
+
import { optionCarry, optionCostOfSpot } from "./carry";
|
|
75
|
+
import {
|
|
76
|
+
ALL_STRIKES,
|
|
77
|
+
STRIKE_WINDOW_PRESETS,
|
|
78
|
+
STRIKE_WINDOW_SETTING,
|
|
79
|
+
applyStrikeWindow,
|
|
80
|
+
chainDeltasByStrike,
|
|
81
|
+
resolveStrikeWindow,
|
|
82
|
+
strikeWindowNotice,
|
|
83
|
+
strikeWindowShortLabel,
|
|
84
|
+
strikeWindowValue,
|
|
85
|
+
} from "./strike-window";
|
|
70
86
|
|
|
71
87
|
/** The summary strip and analytics recompute from live quotes at most this often. */
|
|
72
88
|
const OPTIONS_SUMMARY_THROTTLE_MS = 1_000;
|
|
@@ -76,6 +92,10 @@ const EXPIRY_NEXT_KEY = "]";
|
|
|
76
92
|
/** Switches the cursor row between its call and its put, as clicking either side does. */
|
|
77
93
|
const SIDE_KEY = "x";
|
|
78
94
|
|
|
95
|
+
function sentenceCase(text: string): string {
|
|
96
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
79
99
|
function formatRatio(value: number | null | undefined): string {
|
|
80
100
|
return value == null || !Number.isFinite(value) ? "--" : value.toFixed(2);
|
|
81
101
|
}
|
|
@@ -152,7 +172,8 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
152
172
|
[savedTicker, boundSymbol, fallbackExchange, fallbackCurrency]);
|
|
153
173
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
154
174
|
const liveStreaming = useLiveStreamingSetting();
|
|
155
|
-
|
|
175
|
+
// A number from the app, or the date or seconds `fn OMON --expiration` took as text.
|
|
176
|
+
const [seededSetting] = usePaneSettingValue<unknown>("expiration", undefined);
|
|
156
177
|
const [expirationTargetKey] = usePaneSettingValue<string | null>("expirationTargetKey", null);
|
|
157
178
|
const updatePaneSettings = useUpdatePaneSettings();
|
|
158
179
|
const [calcSide, setCalcSide] = useState<OptionSide | null>(null);
|
|
@@ -207,11 +228,18 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
207
228
|
const [chainRefreshMinutes] = usePaneSettingValue<string>("chainRefreshMinutes", "");
|
|
208
229
|
const [storedOptionFieldIds] = usePaneSettingValue<OptionFieldId[]>("optionColumnIds", DEFAULT_OPTION_FIELD_IDS);
|
|
209
230
|
const optionFieldIds = useMemo(() => resolveOptionFieldIds(storedOptionFieldIds), [storedOptionFieldIds]);
|
|
231
|
+
const [storedStrikeWindow] = usePaneSettingValue<unknown>(STRIKE_WINDOW_SETTING, ALL_STRIKES);
|
|
232
|
+
const strikeWindow = useMemo(() => resolveStrikeWindow(storedStrikeWindow), [storedStrikeWindow]);
|
|
233
|
+
const strikeWindowText = strikeWindowValue(strikeWindow);
|
|
210
234
|
const initialChainEntry = useOptionsQuery(baseRequest);
|
|
211
235
|
const initialChain = useResolvedEntryValue(initialChainEntry);
|
|
212
236
|
const initialExpiration = initialChain?.expirationDates.reduce((best, expiration) => (
|
|
213
237
|
parsed && Math.abs(expiration - parsed.expTs) < Math.abs(best - parsed.expTs) ? expiration : best
|
|
214
238
|
), initialChain.expirationDates[0]!);
|
|
239
|
+
// A typed date names a day; the catalogue's stamp for that day is the expiry.
|
|
240
|
+
const seededDate = readOptionExpiration(seededSetting);
|
|
241
|
+
const seededExpiration = seededDate == null ? undefined
|
|
242
|
+
: findListedExpiry(seededDate, initialChain?.expirationDates ?? []) ?? seededDate;
|
|
215
243
|
const selectedExpiration = expirationTargetKey == null || expirationTargetKey === selectionTargetKey
|
|
216
244
|
? seededExpiration ?? initialExpiration : initialExpiration;
|
|
217
245
|
const viewportKey = `${effectiveTicker}:${selectedExpiration ?? "initial"}`;
|
|
@@ -257,7 +285,24 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
257
285
|
const expirationPickRef = useRef<(value: string) => void>(() => {});
|
|
258
286
|
const { nativePaneChrome } = useUiCapabilities();
|
|
259
287
|
const selectedExpirationIndex = selectedExpiration == null ? -1 : expirationDates.indexOf(selectedExpiration);
|
|
260
|
-
const
|
|
288
|
+
const strikeWindowOptions = useMemo(() => {
|
|
289
|
+
const presets = STRIKE_WINDOW_PRESETS.map(({ value, label, short, description }) => ({ value, label, short, description }));
|
|
290
|
+
// A window typed on the command line that no preset matches still reads on the chip.
|
|
291
|
+
if (presets.some((preset) => preset.value === strikeWindowText)) return presets;
|
|
292
|
+
const label = strikeWindowShortLabel(resolveStrikeWindow(strikeWindowText));
|
|
293
|
+
return [...presets, { value: strikeWindowText, label, short: label, description: "Set from the command line." }];
|
|
294
|
+
}, [strikeWindowText]);
|
|
295
|
+
// The window chip leads: the expiry strip after it can run past the edge of the bar.
|
|
296
|
+
const queryFilters = useMemo<QueryBarFilter[]>(() => [{
|
|
297
|
+
id: "strikes",
|
|
298
|
+
label: "Strikes",
|
|
299
|
+
title: "Strikes",
|
|
300
|
+
value: strikeWindowText,
|
|
301
|
+
// Every strike is the default, so any window narrows the chain and the chip says so.
|
|
302
|
+
defaultValue: ALL_STRIKES,
|
|
303
|
+
options: strikeWindowOptions,
|
|
304
|
+
onChange: (value: string) => updatePaneSettings({ [STRIKE_WINDOW_SETTING]: value }),
|
|
305
|
+
}, {
|
|
261
306
|
id: "expiration",
|
|
262
307
|
label: "Exp",
|
|
263
308
|
inline: true,
|
|
@@ -272,7 +317,7 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
272
317
|
: index === selectedExpirationIndex + 1 ? EXPIRY_NEXT_KEY : undefined,
|
|
273
318
|
})),
|
|
274
319
|
onChange: (value: string) => expirationPickRef.current(value),
|
|
275
|
-
}], [expirationDates, selectedExpiration, selectedExpirationIndex]);
|
|
320
|
+
}], [expirationDates, selectedExpiration, selectedExpirationIndex, strikeWindowOptions, strikeWindowText, updatePaneSettings]);
|
|
276
321
|
// A scheduled refresh of a chain already on screen is quiet: the in-session
|
|
277
322
|
// cadence would otherwise blink the footer every few seconds.
|
|
278
323
|
const loading = (initialChainEntry?.phase === "loading" || initialChainEntry?.phase === "refreshing") && !chain
|
|
@@ -280,7 +325,10 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
280
325
|
|| (expirationChainEntry?.phase === "refreshing" && strikeChain === null);
|
|
281
326
|
// Refresh failures keep a ready entry with last-good data and an error.
|
|
282
327
|
// Surface that warning even when the cached chain is still usable.
|
|
283
|
-
|
|
328
|
+
// The body names the dates nearest the one asked for; the footer only flags it.
|
|
329
|
+
const unavailableExpiry = expirationUnavailable && selectedExpiration != null
|
|
330
|
+
? sentenceCase(missingExpiryText(selectedExpiration, availableExpirations)) : null;
|
|
331
|
+
const error = (expirationUnavailable ? `Expiry ${expiryIsoDate(selectedExpiration!)} unavailable` : null)
|
|
284
332
|
?? initialChainEntry?.error?.message ?? expirationChainEntry?.error?.message
|
|
285
333
|
?? (initialChainEntry?.phase === "error" || expirationChainEntry?.phase === "error"
|
|
286
334
|
? "Failed to load options" : null);
|
|
@@ -316,9 +364,76 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
316
364
|
|
|
317
365
|
useEffect(() => {
|
|
318
366
|
userSelectedStrikeRef.current = false;
|
|
319
|
-
}, [selectedExpiration]);
|
|
367
|
+
}, [selectedExpiration, strikeWindowText]);
|
|
320
368
|
|
|
321
|
-
const
|
|
369
|
+
const allStrikes = useMemo(() => strikeChain ? buildStrikeList(strikeChain) : [], [strikeChain]);
|
|
370
|
+
// The window is read once per chain snapshot, and again when a price first
|
|
371
|
+
// arrives, so streamed quotes never move strikes in or out under the cursor.
|
|
372
|
+
const underlyingPrice = underlying?.quote?.price;
|
|
373
|
+
const windowCenter = useMemo(
|
|
374
|
+
() => resolveDefaultStrikeTarget(parsed?.strike, underlyingPrice),
|
|
375
|
+
[strikeChain, parsed?.strike, underlyingPrice == null],
|
|
376
|
+
);
|
|
377
|
+
const windowSpot = useMemo(() => spot, [strikeChain, spot == null]);
|
|
378
|
+
const windowDeltas = useMemo(
|
|
379
|
+
() => strikeWindow.kind === "delta" && strikeChain ? chainDeltasByStrike(strikeChain, windowSpot, dividendYield) : null,
|
|
380
|
+
[dividendYield, strikeChain, strikeWindow.kind, windowSpot],
|
|
381
|
+
);
|
|
382
|
+
const strikeWindowResult = useMemo(
|
|
383
|
+
() => applyStrikeWindow(allStrikes, strikeWindow, { center: windowCenter, deltas: windowDeltas, keep: parsed?.strike ?? null }),
|
|
384
|
+
[allStrikes, parsed?.strike, strikeWindow, windowCenter, windowDeltas],
|
|
385
|
+
);
|
|
386
|
+
const strikes = strikeWindowResult.strikes;
|
|
387
|
+
usePaneNoticeFooter({ registrationId: "options-strike-window", focused,
|
|
388
|
+
notices: strikeWindowResult.fallback === "no-delta"
|
|
389
|
+
? ["The delta window needs a current underlying quote; showing every strike."]
|
|
390
|
+
: strikeWindowResult.fallback === "no-price" ? ["No price to centre the strike window on; showing every strike."] : [] });
|
|
391
|
+
// What the chain shows, in every renderer and in its reports: the expiry
|
|
392
|
+
// and its days left from the chain's as-of, beside the chain's own feed.
|
|
393
|
+
// Published ahead of the strike window, so a report names the expiry first.
|
|
394
|
+
const asOfChain = strikeChain ?? chain;
|
|
395
|
+
const chainAsOfMs = asOfChain?.asOf ? Date.parse(asOfChain.asOf) : Number.NaN;
|
|
396
|
+
const expiryAsOfMs = Number.isFinite(chainAsOfMs) ? chainAsOfMs : Date.now();
|
|
397
|
+
const expiryDate = selectedExpiration == null ? "" : expiryIsoDate(selectedExpiration);
|
|
398
|
+
const expiryDays = selectedExpiration == null ? "" : formatDaysToExpiry(selectedExpiration, expiryAsOfMs);
|
|
399
|
+
useRemoteUiNode(expiryDate && chain ? {
|
|
400
|
+
role: "report-notice",
|
|
401
|
+
label: "Expiry",
|
|
402
|
+
getMetadata: () => ({
|
|
403
|
+
text: `Expiry ${expiryDate} (${expiryDays})`,
|
|
404
|
+
key: "expiry",
|
|
405
|
+
value: { date: expiryDate, daysToExpiry: daysToExpiry(selectedExpiration!, expiryAsOfMs) },
|
|
406
|
+
}),
|
|
407
|
+
} : null);
|
|
408
|
+
// A refresh that failed keeps the last chain, which is then stale.
|
|
409
|
+
const asOfEntry = strikeChain == null ? null : strikeChain === expirationChain ? expirationChainEntry : initialChainEntry;
|
|
410
|
+
const chainDelay = asOfChain?.delayMinutes ?? 0;
|
|
411
|
+
const chainStatus = asOfEntry?.error != null ? "stale" as const
|
|
412
|
+
: asOfChain?.dataSource ?? (chainDelay > 0 ? "delayed" as const : undefined);
|
|
413
|
+
useRemoteUiNode(asOfChain ? {
|
|
414
|
+
role: "report-freshness",
|
|
415
|
+
label: "Chain freshness",
|
|
416
|
+
getMetadata: () => ({
|
|
417
|
+
...(asOfChain.asOf ? { asOf: asOfChain.asOf } : {}),
|
|
418
|
+
...(chainStatus ? { status: chainStatus } : {}),
|
|
419
|
+
...(chainDelay > 0 ? { delayMinutes: chainDelay } : {}),
|
|
420
|
+
}),
|
|
421
|
+
} : null);
|
|
422
|
+
// A report of the pane (`gloomberb fn OMON`) says how many strikes it lists of how many the expiry has.
|
|
423
|
+
useRemoteUiNode(strikeChain ? {
|
|
424
|
+
role: "report-notice",
|
|
425
|
+
label: "Strike window",
|
|
426
|
+
getMetadata: () => ({
|
|
427
|
+
text: strikeWindowNotice(strikeWindow, strikeWindowResult),
|
|
428
|
+
key: "strikes",
|
|
429
|
+
value: {
|
|
430
|
+
window: strikeWindowText,
|
|
431
|
+
shown: strikeWindowResult.strikes.length,
|
|
432
|
+
total: strikeWindowResult.total,
|
|
433
|
+
...(strikeWindowResult.fallback ? { fallback: strikeWindowResult.fallback } : {}),
|
|
434
|
+
},
|
|
435
|
+
}),
|
|
436
|
+
} : null);
|
|
322
437
|
const selectedStrikeIdx = selectedContract ? strikes.indexOf(selectedContract.strike) : strikeIdx;
|
|
323
438
|
// The snapshot's contracts decide which symbols stream; they do not change
|
|
324
439
|
// with the stream itself, so the subscription is stable between refreshes.
|
|
@@ -395,6 +510,9 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
395
510
|
impliedVolatility: volatilities?.byStrike.get(strike),
|
|
396
511
|
callGreeks: volatilities ? calculateOptionGreeks(call, "call", spot, dividendYield, volatilities) : undefined,
|
|
397
512
|
putGreeks: volatilities ? calculateOptionGreeks(put, "put", spot, dividendYield, volatilities) : undefined,
|
|
513
|
+
callExtrinsicPerYear: call && volatilities ? optionCarry(call, "call", spot, volatilities.valuationTime)?.extrinsicPerYear ?? null : null,
|
|
514
|
+
putExtrinsicPerYear: put && volatilities ? optionCarry(put, "put", spot, volatilities.valuationTime)?.extrinsicPerYear ?? null : null,
|
|
515
|
+
putCostOfSpot: put ? optionCostOfSpot(put, spot) : null,
|
|
398
516
|
isPositionStrike: !!parsed && strike === parsed.strike,
|
|
399
517
|
};
|
|
400
518
|
}), [callsByStrike, dividendYield, parsed, putsByStrike, spot, strikes, volatilities]);
|
|
@@ -643,9 +761,23 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
643
761
|
}, { enabled: focused, phase: "before" });
|
|
644
762
|
|
|
645
763
|
if (!ticker) {
|
|
646
|
-
return <EmptyState title="
|
|
764
|
+
return <EmptyState title="Select a ticker." />;
|
|
647
765
|
}
|
|
648
766
|
if (loading && !chain) return <Spinner label="Loading options chain..." />;
|
|
767
|
+
if (error && !chain && isOptionsUnavailableError(error)) {
|
|
768
|
+
// A future or commodity with no chain of its own: the listed fund whose options stand in, and OSA on it.
|
|
769
|
+
const alternative = listedOptionsAlternative(effectiveTicker);
|
|
770
|
+
return (
|
|
771
|
+
<EmptyState
|
|
772
|
+
title={optionsUnavailableTitle(effectiveTicker)}
|
|
773
|
+
hint={alternative ? `Nearest listed alternative: ${alternative.symbol} (${alternative.name}).` : "OSA builds strategy payoffs on a listed underlying."}
|
|
774
|
+
actions={alternative ? <>
|
|
775
|
+
<Button label={`${alternative.symbol} options`} onPress={() => createPaneFromTemplate("options-pane", { symbol: alternative.symbol })} />
|
|
776
|
+
<Button label={`OSA ${alternative.symbol}`} onPress={() => createPaneFromTemplate("options-scenario-pane", { symbol: alternative.symbol })} />
|
|
777
|
+
</> : undefined}
|
|
778
|
+
/>
|
|
779
|
+
);
|
|
780
|
+
}
|
|
649
781
|
if (error && !chain) return <EmptyState title="Options chain unavailable." message={error} />;
|
|
650
782
|
if (!chain || expirationDates.length === 0) {
|
|
651
783
|
return <EmptyState title={`No options available for ${effectiveTicker}.`} />;
|
|
@@ -656,7 +788,7 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
656
788
|
: 0;
|
|
657
789
|
// The root insets the terminal body, so the band gets the width inside it.
|
|
658
790
|
const statWidth = Math.max(1, width - (nativePaneChrome ? 0 : 2));
|
|
659
|
-
const
|
|
791
|
+
const figures = optionsSummaryItems({
|
|
660
792
|
spot: spotItem(underlying, isOpt ? undefined : ticker.metadata.assetCategory),
|
|
661
793
|
summary, enrichment, currency: underlying?.quote?.currency || ticker.metadata.currency || "",
|
|
662
794
|
ivRank: showIvRank ? { stats: ivRank } : null,
|
|
@@ -665,8 +797,23 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
665
797
|
// Slope or IVR detail is the widest cell and would otherwise halve the
|
|
666
798
|
// columns and push the chain down; the detail is cut short instead.
|
|
667
799
|
const statRowBudget = width >= 110 ? 3 : width >= 65 ? 4 : 6;
|
|
668
|
-
const
|
|
800
|
+
const figureColumns = Math.max(statGridColumns(figures, statWidth), Math.ceil(figures.length / statRowBudget));
|
|
801
|
+
// The expiry leads the band, since the strip above can scroll it out of
|
|
802
|
+
// view. It joins the figures as one more cell where the row budget leaves a
|
|
803
|
+
// cell wide enough for all of it, and otherwise takes a row of its own so no
|
|
804
|
+
// figure is squeezed. Sized for four-digit days, so the band does not
|
|
805
|
+
// reflow as the chain's as-of arrives or the expiry steps.
|
|
806
|
+
const expiryItem: StatItem = { id: "expiry", label: "Expiry", value: expiryDate, detail: `(${expiryDays})` };
|
|
807
|
+
const cellColumns = Math.max(figureColumns, Math.ceil((figures.length + 1) / statRowBudget));
|
|
808
|
+
const expiryCells = Math.max(...figures.map((item) => item.label.length), expiryItem.label.length) + 1
|
|
809
|
+
+ expiryDate.length + 2 + "(9999d)".length;
|
|
810
|
+
const expiryInline = Math.floor((statWidth - 2 * (cellColumns - 1)) / cellColumns) >= expiryCells;
|
|
811
|
+
const statColumns = expiryInline ? cellColumns : figureColumns;
|
|
812
|
+
const statItems = [expiryInline ? expiryItem : { ...expiryItem, wide: true }, ...figures];
|
|
669
813
|
const summaryRowCount = Math.min(statGridRows(statItems, statWidth, statColumns), Math.max(0, height - 6));
|
|
814
|
+
// A short band gives up figures from the end; the expiry stays.
|
|
815
|
+
const shownStatItems = summaryRowCount === 0 ? []
|
|
816
|
+
: [statItems[0]!, ...figures.slice(0, summaryRowCount * statColumns - (expiryInline ? 1 : statColumns))];
|
|
670
817
|
// No term here follows the selection or the load, so an empty cold-expiry
|
|
671
818
|
// response cannot resize the table: growing it during loading would turn a
|
|
672
819
|
// clamped scroll into apparent user navigation.
|
|
@@ -677,10 +824,10 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
677
824
|
|
|
678
825
|
return (
|
|
679
826
|
<Box flexDirection="column" flexGrow={1} paddingX={nativePaneChrome ? 0 : 1}>
|
|
680
|
-
<QueryBar width={Math.max(1, width - 2)} filters={
|
|
827
|
+
<QueryBar width={Math.max(1, width - 2)} filters={queryFilters} />
|
|
681
828
|
|
|
682
829
|
{summaryRowCount > 0 && (
|
|
683
|
-
<StatGrid items={
|
|
830
|
+
<StatGrid items={shownStatItems} width={statWidth} columns={statColumns} />
|
|
684
831
|
)}
|
|
685
832
|
|
|
686
833
|
{isOpt && parsed && (
|
|
@@ -721,7 +868,9 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
|
|
|
721
868
|
getItemKey={(row) => String(row.strike)}
|
|
722
869
|
renderCell={renderCell}
|
|
723
870
|
selectedTextOverridesCellColor
|
|
724
|
-
emptyStateTitle={error && !strikeChain ? "Selected expiration unavailable." : strikesLoading ? "Loading strikes..."
|
|
871
|
+
emptyStateTitle={unavailableExpiry ?? (error && !strikeChain ? "Selected expiration unavailable." : strikesLoading ? "Loading strikes..."
|
|
872
|
+
: strikeWindowResult.total > 0 ? "No strikes in this window." : "No strikes available.")}
|
|
873
|
+
reportEveryRow
|
|
725
874
|
rootWidth={Math.max(1, width - 2 + inset * 2)}
|
|
726
875
|
rootHeight={tableHeight}
|
|
727
876
|
columnGap={0}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { numberErrorMessage } from "../../../utils/decimal-comma";
|
|
1
2
|
import { DEFAULT_OPTION_CALC_DRAFT, updateOptionCalcDraft, type OptionCalcDraft } from "./model";
|
|
2
3
|
import type { CashDividend } from "./binomial";
|
|
3
4
|
|
|
@@ -5,7 +6,7 @@ const DECIMAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
|
5
6
|
|
|
6
7
|
function numberInput(value: unknown, label: string): number {
|
|
7
8
|
if (typeof value !== "number" && (typeof value !== "string" || !DECIMAL.test(value.trim()))) {
|
|
8
|
-
throw new Error(`${label} must be a finite number
|
|
9
|
+
throw new Error(numberErrorMessage(`${label} must be a finite number.`, value));
|
|
9
10
|
}
|
|
10
11
|
const result = Number(value);
|
|
11
12
|
if (!Number.isFinite(result)) throw new Error(`${label} must be a finite number.`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../api-client";
|
|
2
|
-
import {
|
|
2
|
+
import { usListingSymbol } from "../../../utils/sec";
|
|
3
3
|
|
|
4
4
|
export interface ExpiryOpenInterest {
|
|
5
5
|
date: string;
|
|
@@ -78,8 +78,12 @@ export interface GammaPayload {
|
|
|
78
78
|
|
|
79
79
|
export type OptionsPositioningApi = Pick<typeof apiClient, "optionsPositioning">;
|
|
80
80
|
|
|
81
|
-
/**
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* A US listing key ("SPY:ARCX") keeps only its symbol; an index keeps its
|
|
83
|
+
* caret (^SPX). A listing abroad is refused: its bare symbol's chain is
|
|
84
|
+
* another company's (SAN:EPA's would be Banco Santander's).
|
|
85
|
+
*/
|
|
86
|
+
export const positioningSymbol = (symbol: string) => usListingSymbol(symbol, "Options data");
|
|
83
87
|
|
|
84
88
|
const query = (symbol: string, expiry: string | null) =>
|
|
85
89
|
new URLSearchParams({ symbol: positioningSymbol(symbol), ...(expiry ? { expiry } : {}) }).toString();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
|
+
import { expirationOptionSeconds, expiryIsoDate, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER } from "../../../utils/option-expiry";
|
|
2
3
|
import { loadGamma, loadOpenInterest, positioningSymbol } from "./client";
|
|
3
4
|
import {
|
|
4
5
|
ALL_EXPIRIES,
|
|
@@ -25,6 +26,7 @@ export const optionsPositioningHeadless = (initialTab: PositioningTab): Headless
|
|
|
25
26
|
shape: "bundle",
|
|
26
27
|
freshness: { status: "not-a-feed", basis: "daily open interest", cadence: "daily" },
|
|
27
28
|
argument: { kind: "ticker", optional: true, description: "US option underlying, or SPX; SPY when omitted" },
|
|
29
|
+
usListingsOnly: "Options data",
|
|
28
30
|
describe: (args) => `${initialTab === "gex" ? "GEX" : "OPX"} ${args.symbols[0] ?? "SPY"}`,
|
|
29
31
|
discovery: {
|
|
30
32
|
aliases: initialTab === "gex" ? ["GEX"] : ["OPX", "MAXPAIN"],
|
|
@@ -39,8 +41,9 @@ export const optionsPositioningHeadless = (initialTab: PositioningTab): Headless
|
|
|
39
41
|
options: [
|
|
40
42
|
{ key: "tab", type: "enum", values: POSITIONING_TABS.map((tab) => ({ value: tab.value })), defaultValue: initialTab,
|
|
41
43
|
settingKey: "tab", description: "View the pane opens on: strikes, expiries or gex" },
|
|
42
|
-
{ key: "expiry", type: "string", settingKey: "expiry",
|
|
43
|
-
description:
|
|
44
|
+
{ key: "expiry", type: "string", settingKey: "expiry", placeholder: OPTION_EXPIRATION_PLACEHOLDER, example: "--expiry 2027-01-15",
|
|
45
|
+
description: `Expiry for the strikes and gamma, as ${OPTION_EXPIRATION_FORMAT}; the busiest near expiry when omitted (all expiries for gamma)`,
|
|
46
|
+
normalize: (value) => expiryIsoDate(expirationOptionSeconds(value, "expiry")) },
|
|
44
47
|
],
|
|
45
48
|
async load(args, ctx) {
|
|
46
49
|
const symbol = positioningSymbol(args.symbols[0] ?? "SPY");
|
|
@@ -34,13 +34,13 @@ import type { ResolvedSeries } from "../../../time-series/types";
|
|
|
34
34
|
import type { PaneProps } from "../../../types/plugin";
|
|
35
35
|
import { Box, Text } from "../../../ui";
|
|
36
36
|
import { formatCompact } from "../../../utils/format";
|
|
37
|
+
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
37
38
|
import { isPlainKey } from "../../../utils/keyboard";
|
|
38
39
|
import { nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
|
|
39
40
|
import { useCloudAccessFooter } from "../shared/cloud-upgrade";
|
|
40
41
|
import {
|
|
41
42
|
loadGamma,
|
|
42
43
|
loadOpenInterest,
|
|
43
|
-
positioningSymbol,
|
|
44
44
|
type ExpiryOpenInterest,
|
|
45
45
|
type GammaPayload,
|
|
46
46
|
type GammaStrike,
|
|
@@ -583,7 +583,7 @@ function OptionsPositioningView({ width, height, focused, symbol }: PaneProps &
|
|
|
583
583
|
return <Box width={width} height={height} flexDirection="column">
|
|
584
584
|
<PaneStatusBody loading={openInterest.loading && !data} error={!data ? openInterest.error : null}
|
|
585
585
|
empty={!!data && !data.expiries.length} subject="open interest"
|
|
586
|
-
emptyTitle={`No listed options for ${
|
|
586
|
+
emptyTitle={`No listed options for ${parsePublicTickerKey(symbol).symbol.replace(/^\^/, "")}.`}>
|
|
587
587
|
{data?.expiries.length ? <>
|
|
588
588
|
{tabStrip}
|
|
589
589
|
{content}
|
|
@@ -3,7 +3,7 @@ import { StaticChartSurface, type StaticChartOverlay } from "../../../components
|
|
|
3
3
|
import { Box, Text } from "../../../ui";
|
|
4
4
|
import { resolveChartPalette } from "../../../components/chart/core/palette";
|
|
5
5
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
6
|
-
import type
|
|
6
|
+
import { currencyLabel, scenarioValueUnit, type buildScenario } from "./model";
|
|
7
7
|
|
|
8
8
|
export function ScenarioPayoffChart({ scenario, width, height, focused = false }: {
|
|
9
9
|
scenario: ReturnType<typeof buildScenario>; width: number; height: number; focused?: boolean;
|
|
@@ -28,7 +28,7 @@ export function ScenarioPayoffChart({ scenario, width, height, focused = false }
|
|
|
28
28
|
<Box height={1} paddingX={1} flexDirection="row" gap={3}>
|
|
29
29
|
<Text fg={colors.warning}>{`${scenario.expiryRisk.reason ? "First expiry" : "Expiry"} ${date(scenario.expiryDate)}`}</Text>
|
|
30
30
|
<Text fg={colors.borderFocused}>{`Selected ${date(scenario.controls.date)}`}</Text>
|
|
31
|
-
<Text fg={colors.textDim}>{`P&L ${scenario.position.currency}`}</Text>
|
|
31
|
+
<Text fg={colors.textDim}>{`P&L ${currencyLabel(scenario.position.currency)} ${scenarioValueUnit(scenario.position)}`}</Text>
|
|
32
32
|
</Box>
|
|
33
33
|
<StaticChartSurface points={points} overlays={overlays} calendarSpaced
|
|
34
34
|
width={width} height={Math.max(2, height - 1)} colors={{ ...resolveChartPalette(colors), lineColor: colors.warning }}
|
|
@@ -5,10 +5,12 @@ import type { QueryEntry } from "../../../market-data/result-types";
|
|
|
5
5
|
import type { DataProvider } from "../../../types/data-provider";
|
|
6
6
|
import type { OptionsChain, Quote, TickerFinancials } from "../../../types/financials";
|
|
7
7
|
import { canonicalExchange, parsePublicTickerKey } from "../../../utils/exchanges";
|
|
8
|
+
import { numberErrorMessage } from "../../../utils/decimal-comma";
|
|
9
|
+
import { expiryIsoDate, findListedExpiry, missingExpiryText } from "../../../utils/option-expiry";
|
|
8
10
|
import { surfaceTreasuryRate } from "../vol-surface/model";
|
|
9
11
|
import { daysToExpiryFrom, extractImpliedForward, optionMid, solveImpliedVolatility } from "../shared/volatility";
|
|
10
12
|
import type { YieldPoint } from "../yield-curve/treasury-data";
|
|
11
|
-
import { parseLegs, validatePosition, type ScenarioControls, type ScenarioLeg, type ScenarioPosition } from "./model";
|
|
13
|
+
import { parseLegs, scenarioCurrency, validatePosition, type ScenarioControls, type ScenarioLeg, type ScenarioPosition } from "./model";
|
|
12
14
|
import { abortable, abortError } from "../../../utils/async-deadline";
|
|
13
15
|
import { errorMessage } from "../../../utils/errors";
|
|
14
16
|
|
|
@@ -26,6 +28,8 @@ export interface ScenarioMarketSnapshot {
|
|
|
26
28
|
underlyingQuote: Quote | null;
|
|
27
29
|
rateAsOf: string[];
|
|
28
30
|
warnings: string[];
|
|
31
|
+
/** Why the requested expiry has no chain, naming the listed dates nearest it. */
|
|
32
|
+
missingExpiry?: string;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export interface ScenarioLoaderDependencies {
|
|
@@ -99,15 +103,31 @@ export async function loadScenarioMarket(
|
|
|
99
103
|
const validDividend = symbolMatches(financialsSymbol, symbol) && !fundamentals?.stale && !financialsEntry?.error
|
|
100
104
|
&& typeof fundamentals?.dividendYield === "number" && Number.isFinite(fundamentals.dividendYield) && fundamentals.dividendYield >= 0;
|
|
101
105
|
if (!validDividend) warnings.push("Dividend yield unavailable; supply an explicit assumption");
|
|
102
|
-
|
|
106
|
+
let chainEntry = entry(chainResult, "Options chain");
|
|
103
107
|
let chain = chainEntry ? resolveEntryValue(chainEntry) : null;
|
|
104
108
|
if (chain && !symbolMatches(chain.underlyingSymbol, symbol)) {
|
|
105
109
|
warnings.push("Options chain does not match the requested ticker"); chain = null;
|
|
106
110
|
}
|
|
107
111
|
const expirationDates = [...new Set((chain?.expirationDates ?? []).filter((expiration) =>
|
|
108
112
|
positive(expiration) && daysToExpiryFrom(expiration, now) > 0))].sort((a, b) => a - b);
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
// A typed date is that day's UTC midnight; the chain's own stamp for the
|
|
114
|
+
// expiry is matched by calendar date, as OMON matches it.
|
|
115
|
+
const listedExpiration = request.expiration == null ? undefined : findListedExpiry(request.expiration, expirationDates);
|
|
116
|
+
const onExpiry = (value: OptionsChain, expiration: number) => [...value.calls, ...value.puts]
|
|
117
|
+
.every((contract) => expiryIsoDate(contract.expiration) === expiryIsoDate(expiration));
|
|
118
|
+
if (chain && listedExpiration != null && listedExpiration !== request.expiration && !chain.calls.length && !chain.puts.length) {
|
|
119
|
+
// A source that keys chains by its exact stamp answered the midnight request empty.
|
|
120
|
+
chainEntry = await abortable(dependencies.loadOptions({ instrument: request.instrument, expirationDate: listedExpiration }, options)
|
|
121
|
+
.catch((error: unknown) => { warnings.push(`Options chain: ${errorMessage(error)}`); return null; }), request.signal, CANCELLED);
|
|
122
|
+
if (request.signal?.aborted) throw abortError(CANCELLED);
|
|
123
|
+
if (chainEntry?.error) warnings.push(`Options chain: ${chainEntry.error.message}`);
|
|
124
|
+
chain = chainEntry ? resolveEntryValue(chainEntry) : null;
|
|
125
|
+
}
|
|
126
|
+
let missingExpiry: string | undefined;
|
|
127
|
+
if (chain && request.expiration != null && listedExpiration == null) {
|
|
128
|
+
missingExpiry = sentence(missingExpiryText(request.expiration, expirationDates, symbol));
|
|
129
|
+
warnings.push(missingExpiry); chain = null;
|
|
130
|
+
} else if (chain && listedExpiration != null && !onExpiry(chain, listedExpiration)) {
|
|
111
131
|
warnings.push("Selected expiration unavailable in the returned options chain"); chain = null;
|
|
112
132
|
}
|
|
113
133
|
if (!chain) warnings.push("Options chain unavailable");
|
|
@@ -117,15 +137,22 @@ export async function loadScenarioMarket(
|
|
|
117
137
|
if (curveResult.status === "rejected") warnings.push(`Treasury: ${errorMessage(curveResult.reason)}`);
|
|
118
138
|
const rate = surfaceTreasuryRate(curve, rateExpiration == null ? NaN : daysToExpiryFrom(rateExpiration, now) / 365);
|
|
119
139
|
warnings.push(...rate.warnings);
|
|
140
|
+
// A currency does not go stale with its quote: the listing's quote, its snapshot, or its contracts name it.
|
|
141
|
+
const currency = [symbolMatches(quote?.symbol, symbol) ? quote?.currency : undefined,
|
|
142
|
+
symbolMatches(financialsSymbol, symbol) ? financials?.quote?.currency : undefined,
|
|
143
|
+
chain?.calls[0]?.currency, chain?.puts[0]?.currency].find((value) => !!value?.trim())?.trim().toUpperCase() ?? "";
|
|
120
144
|
return {
|
|
121
|
-
symbol, exchange: request.instrument.exchange, spot: quoteCurrent ? quote!.price : null, currency
|
|
145
|
+
symbol, exchange: request.instrument.exchange, spot: quoteCurrent ? quote!.price : null, currency,
|
|
122
146
|
asOf: quoteCurrent && positive(quote!.lastUpdated) ? quote!.lastUpdated : now,
|
|
123
147
|
chain, expirationDates, rate: rate.rate, dividendYield: validDividend ? fundamentals!.dividendYield! : null,
|
|
124
148
|
source: quoteCurrent ? quoteEntry?.source ?? quote!.providerId ?? null : null,
|
|
125
149
|
underlyingQuote: quoteCurrent ? quote : null, rateAsOf: rate.asOf, warnings: [...new Set(warnings)],
|
|
150
|
+
...(missingExpiry ? { missingExpiry } : {}),
|
|
126
151
|
};
|
|
127
152
|
}
|
|
128
153
|
|
|
154
|
+
const sentence = (text: string) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
155
|
+
|
|
129
156
|
function supplied(value: unknown): boolean { return value != null && value !== ""; }
|
|
130
157
|
|
|
131
158
|
function numericSetting(settings: Record<string, unknown>, key: string): number | undefined {
|
|
@@ -133,7 +160,7 @@ function numericSetting(settings: Record<string, unknown>, key: string): number
|
|
|
133
160
|
if (!supplied(value)) return undefined;
|
|
134
161
|
if ((typeof value !== "string" && typeof value !== "number")
|
|
135
162
|
|| (typeof value === "string" && !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim()))
|
|
136
|
-
|| !Number.isFinite(Number(value))) throw new Error(`${key} must be a finite number
|
|
163
|
+
|| !Number.isFinite(Number(value))) throw new Error(numberErrorMessage(`${key} must be a finite number`, value));
|
|
137
164
|
return Number(value);
|
|
138
165
|
}
|
|
139
166
|
|
|
@@ -175,13 +202,18 @@ export function scenarioPositionFromSettings(
|
|
|
175
202
|
const dividendYield = dividendValue == null ? market?.dividendYield : dividendValue / 100;
|
|
176
203
|
const legText = typeof settings.legs === "string" ? settings.legs : "";
|
|
177
204
|
let legs = legText.trim() ? parseLegs(legText) : [];
|
|
205
|
+
const strike = numericSetting(settings, "strike");
|
|
206
|
+
if (strike != null && !(strike > 0)) throw new Error("strike must be positive");
|
|
207
|
+
if (strike != null && !supplied(settings.strategy)) throw new Error("strike picks the strike of --strategy; add --strategy put, vertical or straddle");
|
|
178
208
|
if (!legs.length && supplied(settings.strategy)) {
|
|
179
|
-
if (settings.strategy
|
|
180
|
-
if (!market?.chain || market.warnings.includes("Options chain is stale"))
|
|
209
|
+
if (!isScenarioStrategy(settings.strategy)) throw new Error("strategy must be vertical, straddle or put");
|
|
210
|
+
if (!market?.chain || market.warnings.includes("Options chain is stale")) {
|
|
211
|
+
throw new Error(market?.missingExpiry ?? "A current options chain is required to seed a strategy");
|
|
212
|
+
}
|
|
181
213
|
if (spot == null) throw new Error("A current underlying price or explicit --spot is required");
|
|
182
214
|
// Leg IVs are solved at the market's own spot and time, never at a what-if override.
|
|
183
215
|
const pricing = market.spot != null && rate != null ? { spot: market.spot, asOf: market.asOf, rate } : null;
|
|
184
|
-
const seeded = scenarioStrategyLegs(market.chain, spot, settings.strategy, pricing);
|
|
216
|
+
const seeded = scenarioStrategyLegs(market.chain, spot, settings.strategy, pricing, strike);
|
|
185
217
|
legs = seeded.legs;
|
|
186
218
|
// Mids priced off the parity forward are worth their cost only at the spot
|
|
187
219
|
// that forward implies on the scenario's own rate and dividend yield. The
|
|
@@ -198,7 +230,7 @@ export function scenarioPositionFromSettings(
|
|
|
198
230
|
if (dividendYield == null) throw new Error("Dividend yield unavailable; supply --dividend-yield as an annual percentage");
|
|
199
231
|
const position: ScenarioPosition = {
|
|
200
232
|
symbol, exchange: exchange ?? market?.exchange,
|
|
201
|
-
currency:
|
|
233
|
+
currency: scenarioCurrency(settings.currency, market?.currency, exchange ?? market?.exchange),
|
|
202
234
|
spot, rate, dividendYield, asOf: dateSetting(settings, "asOf") ?? market?.asOf ?? Date.now(), legs,
|
|
203
235
|
};
|
|
204
236
|
const error = validatePosition(position);
|
|
@@ -210,6 +242,9 @@ export function scenarioPositionFromSettings(
|
|
|
210
242
|
return position;
|
|
211
243
|
}
|
|
212
244
|
|
|
245
|
+
type ScenarioStrategy = "vertical" | "straddle" | "put";
|
|
246
|
+
const isScenarioStrategy = (value: unknown): value is ScenarioStrategy => value === "vertical" || value === "straddle" || value === "put";
|
|
247
|
+
|
|
213
248
|
interface StrategyPricing { spot: number; asOf: number; rate: number }
|
|
214
249
|
|
|
215
250
|
const usableVolatility = (value: unknown): value is number => positive(value) && value <= 5;
|
|
@@ -230,9 +265,13 @@ interface StrategyParity { forward: number; daysToExpiry: number }
|
|
|
230
265
|
* keeps a usable provider IV, and without one it is not eligible. `parity` is
|
|
231
266
|
* the forward a seeded leg was solved against, so the caller can start the
|
|
232
267
|
* scenario at the spot it implies.
|
|
268
|
+
*
|
|
269
|
+
* Each strategy is anchored at the strike nearest spot, or at `strike` when
|
|
270
|
+
* one is given: a long put (a protective put), a call vertical from the
|
|
271
|
+
* anchor to the next listed strike up, or a straddle at the anchor.
|
|
233
272
|
*/
|
|
234
273
|
function scenarioStrategyLegs(
|
|
235
|
-
chain: OptionsChain, spot: number, strategy:
|
|
274
|
+
chain: OptionsChain, spot: number, strategy: ScenarioStrategy, pricing: StrategyPricing | null, strike?: number,
|
|
236
275
|
): { legs: ScenarioLeg[]; parity: StrategyParity | null } {
|
|
237
276
|
const parity = new Map<number, StrategyParity | null>();
|
|
238
277
|
const parityFor = (expiration: number) => {
|
|
@@ -256,12 +295,30 @@ function scenarioStrategyLegs(
|
|
|
256
295
|
if (usableVolatility(volatility)) return [{ contract, mid, volatility, solved: true }];
|
|
257
296
|
return usableVolatility(contract.impliedVolatility) ? [{ contract, mid, volatility: contract.impliedVolatility, solved: false }] : [];
|
|
258
297
|
});
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
298
|
+
const nearest = (a: { contract: { strike: number } }, b: { contract: { strike: number } }) =>
|
|
299
|
+
Math.abs(a.contract.strike - spot) - Math.abs(b.contract.strike - spot);
|
|
300
|
+
const anchored = ({ contract }: { contract: { strike: number } }) => strike == null || Math.abs(contract.strike - strike) < 1e-9;
|
|
301
|
+
const calls = quoted(chain.calls, "call").sort(nearest);
|
|
302
|
+
const puts = quoted(chain.puts, "put").sort(nearest);
|
|
303
|
+
const missing = (candidates: { contract: { strike: number } }[]) => {
|
|
304
|
+
if (strike == null) return new Error("The selected chain has no complete quoted strategy with usable IV");
|
|
305
|
+
const listed = [...new Set(candidates.map(({ contract }) => contract.strike))]
|
|
306
|
+
.sort((a, b) => Math.abs(a - strike) - Math.abs(b - strike)).slice(0, 4).sort((a, b) => a - b);
|
|
307
|
+
return new Error(`The selected chain has no complete quoted ${strategy} at strike ${strike} with usable IV`
|
|
308
|
+
+ (listed.length ? `; nearest quoted strikes: ${listed.join(", ")}` : ""));
|
|
309
|
+
};
|
|
310
|
+
if (strategy === "put") {
|
|
311
|
+
const put = puts.find(anchored);
|
|
312
|
+
if (!put) throw missing(puts);
|
|
313
|
+
return { legs: [{ id: put.contract.contractSymbol, side: "put", quantity: 1, strike: put.contract.strike,
|
|
314
|
+
expiration: put.contract.expiration, price: put.mid, priceSource: "mid", volatility: put.volatility, multiplier: 100,
|
|
315
|
+
...(put.solved ? { volatilitySource: "mid" as const } : {}) }],
|
|
316
|
+
parity: put.solved ? parityFor(put.contract.expiration) : null };
|
|
317
|
+
}
|
|
318
|
+
const first = calls.find((entry) => anchored(entry) && (strategy === "vertical"
|
|
319
|
+
? calls.some(({ contract: other }) => other.expiration === entry.contract.expiration && other.strike > entry.contract.strike)
|
|
320
|
+
: puts.some(({ contract: put }) => put.expiration === entry.contract.expiration && put.strike === entry.contract.strike)));
|
|
321
|
+
if (!first) throw missing(calls);
|
|
265
322
|
const second = strategy === "vertical"
|
|
266
323
|
? calls.filter(({ contract }) => contract.expiration === first.contract.expiration && contract.strike > first.contract.strike)
|
|
267
324
|
.sort((a, b) => a.contract.strike - b.contract.strike)[0]!
|
|
@@ -270,7 +327,7 @@ function scenarioStrategyLegs(
|
|
|
270
327
|
const legs = [first, second].map(({ contract, mid, volatility, solved }, index): ScenarioLeg => ({
|
|
271
328
|
id: contract.contractSymbol, side: index === 1 && strategy === "straddle" ? "put" : "call",
|
|
272
329
|
quantity: index === 1 && strategy === "vertical" ? -1 : 1, strike: contract.strike,
|
|
273
|
-
expiration: contract.expiration, price: mid, volatility, multiplier: 100,
|
|
330
|
+
expiration: contract.expiration, price: mid, priceSource: "mid", volatility, multiplier: 100,
|
|
274
331
|
...(solved ? { volatilitySource: "mid" as const } : {}),
|
|
275
332
|
}));
|
|
276
333
|
return { legs, parity: first.solved || second.solved ? parityFor(first.contract.expiration) : null };
|