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
|
@@ -1,38 +1,67 @@
|
|
|
1
1
|
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
2
2
|
import { parsePublicTickerKey } from "../../../utils/exchanges";
|
|
3
|
+
import {
|
|
4
|
+
expirationOptionSeconds, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER, readOptionExpiration,
|
|
5
|
+
} from "../../../utils/option-expiry";
|
|
3
6
|
import { resolveHeadlessInstrument } from "../shared/headless-market-data";
|
|
4
7
|
import {
|
|
5
8
|
createScenarioDependencies, loadScenarioMarket, scenarioControlsFromSettings, scenarioPositionFromSettings,
|
|
6
9
|
type ScenarioMarketSnapshot,
|
|
7
10
|
} from "./client";
|
|
8
|
-
import { buildScenario, parseLegs } from "./model";
|
|
11
|
+
import { buildScenario, currencyLabel, parseLegs, scenarioValueUnit } from "./model";
|
|
12
|
+
import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget } from "./hedge";
|
|
9
13
|
|
|
10
14
|
// Float noise at a flat origin or a breakeven prints as 0.00, not -0.00.
|
|
11
15
|
const money = (value: unknown) => typeof value === "number" ? (Math.abs(value) < 0.005 ? 0 : value).toFixed(2) : "--";
|
|
16
|
+
const percent = (value: unknown) => typeof value === "number" ? `${(value * 100).toFixed(2)}%` : "--";
|
|
12
17
|
const supplied = (value: unknown) => value != null && value !== "";
|
|
18
|
+
// A seeded entry is a quote midpoint, often to the half cent (5.495), so it keeps up to four decimals.
|
|
19
|
+
const premium = (value: unknown) => typeof value === "number"
|
|
20
|
+
? value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 4, useGrouping: false }) : "--";
|
|
21
|
+
/** Entry prices and spot are quoted per share of the underlying. */
|
|
22
|
+
const PRICE_UNIT = "per share";
|
|
23
|
+
/** The Valuation entries, in model order, with the unit each is in. */
|
|
24
|
+
const VALUATION_LABELS: Record<string, (currency: string) => string> = {
|
|
25
|
+
price: () => "Value", pnl: () => "P&L", delta: () => "Delta (shares)", gamma: () => "Gamma (shares per 1.00 move)",
|
|
26
|
+
thetaPerDay: (currency) => `Theta (${currency} per day)`, vegaPerPoint: (currency) => `Vega (${currency} per vol point)`,
|
|
27
|
+
rhoPerPoint: (currency) => `Rho (${currency} per rate point)`,
|
|
28
|
+
};
|
|
13
29
|
|
|
14
30
|
export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
15
31
|
shape: "bundle", argument: { kind: "ticker", description: "Underlying ticker" },
|
|
32
|
+
usListingsOnly: "Options data",
|
|
16
33
|
describe: (args) => `OSA ${args.symbols[0] ?? ""}`,
|
|
34
|
+
description: "A typed or chain-seeded options position valued across spot, date and volatility: its legs, value and Greeks, expiry risk and a "
|
|
35
|
+
+ "scenario P&L grid. With --nav and --budget-bps, how many contracts a hedge budget buys, the premium spent, the notional protected and "
|
|
36
|
+
+ "coverage of an equity sleeve.",
|
|
17
37
|
discovery: { screenshotReadiness: "ready", limitations: ["European scenario values do not model early assignment or exercise."] },
|
|
18
38
|
options: [
|
|
19
39
|
{ key: "tab", type: "enum", values: ["payoff", "grid", "legs"].map((value) => ({ value })), defaultValue: "payoff",
|
|
20
40
|
description: "Initial view", pluginState: { pluginId: "ticker-research", key: "activeTabId" } },
|
|
21
|
-
{ key: "legs", type: "string", description: "Semicolon-separated side,strike,YYYY-MM-DD,signed quantity,entry price,IV percent[,multiplier]" },
|
|
22
|
-
{ key: "strategy", type: "enum", values: [{ value: "vertical" }, { value: "straddle" }
|
|
23
|
-
|
|
41
|
+
{ key: "legs", type: "string", description: "Semicolon-separated side,strike,YYYY-MM-DD,signed quantity,entry price per share,IV percent[,multiplier]" },
|
|
42
|
+
{ key: "strategy", type: "enum", values: [{ value: "vertical" }, { value: "straddle" }, { value: "put" }],
|
|
43
|
+
description: "Build a position from current two-sided chain quotes: a call vertical, a straddle, or one long put (a protective put)" },
|
|
44
|
+
{ key: "strike", type: "string", description: "The --strategy strike: the put, the vertical's long call or the straddle. Nearest the money when omitted" },
|
|
45
|
+
{ key: "expiration", type: "string", placeholder: OPTION_EXPIRATION_PLACEHOLDER, example: "--strategy vertical --expiration 2027-01-15",
|
|
46
|
+
description: `Chain expiry for --strategy, as ${OPTION_EXPIRATION_FORMAT}`, normalize: (value) => expirationOptionSeconds(value) },
|
|
24
47
|
{ key: "spot", type: "string", description: "Underlying price override, per share" },
|
|
25
48
|
{ key: "rate", type: "string", description: "Continuously compounded annual risk-free rate, percent" },
|
|
26
49
|
{ key: "dividendYield", aliases: ["dividend-yield"], type: "string", description: "Continuous annual dividend yield, percent" },
|
|
27
|
-
{ key: "currency", type: "string", description: "
|
|
50
|
+
{ key: "currency", type: "string", description: "Currency of the inputs, such as USD; the underlying's when omitted" },
|
|
28
51
|
{ key: "asOf", aliases: ["as-of"], type: "string", description: "Position valuation timestamp: UTC ISO or YYYY-MM-DD" },
|
|
29
52
|
{ key: "date", type: "string", description: "Scenario date: UTC ISO or YYYY-MM-DD" },
|
|
30
53
|
{ key: "volShift", aliases: ["vol-shift"], type: "string", defaultValue: "0", description: "Parallel volatility shift, percentage points" },
|
|
31
54
|
{ key: "spotRange", aliases: ["spot-range"], type: "string", defaultValue: "30", description: "Spot grid range above and below current spot, percent" },
|
|
55
|
+
{ 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" },
|
|
56
|
+
{ key: "budgetBps", aliases: ["budget-bps"], type: "string", example: "--strategy put --strike 700 --expiration 2027-01-15 --nav 100m --budget-bps 50",
|
|
57
|
+
description: "Hedge budget in basis points of --nav (50 is 0.5%): the report says how many of the position it buys, the premium, and the notional protected" },
|
|
58
|
+
{ key: "sleeve", aliases: ["equity-sleeve"], type: "string", description: "Equity sleeve the hedge protects, for coverage = notional / sleeve. Same amount forms as --nav" },
|
|
32
59
|
],
|
|
33
60
|
async load(args, ctx) {
|
|
34
61
|
const symbol = args.symbols[0]!;
|
|
35
62
|
const settings: Record<string, unknown> = { ...ctx.settings, ...args.options, symbol };
|
|
63
|
+
// Bad hedge inputs fail before any market request.
|
|
64
|
+
hedgeInputsFromSettings(settings);
|
|
36
65
|
const suppliedInputs = !!settings.legs && ["spot", "rate", "dividendYield"].every((key) => settings[key] != null && settings[key] !== "");
|
|
37
66
|
let market: ScenarioMarketSnapshot;
|
|
38
67
|
if (suppliedInputs) {
|
|
@@ -42,8 +71,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
42
71
|
} else {
|
|
43
72
|
const instrument = await resolveHeadlessInstrument(ctx, symbol);
|
|
44
73
|
const typedExpiries = typeof settings.legs === "string" && settings.legs.trim() ? parseLegs(settings.legs).map((leg) => leg.expiration) : [];
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
const expiration = readOptionExpiration(args.options.expiration);
|
|
75
|
+
market = await loadScenarioMarket({ instrument, expiration: expiration ?? undefined,
|
|
47
76
|
rateExpiration: typedExpiries.length ? Math.min(...typedExpiries) : undefined, signal: ctx.signal,
|
|
48
77
|
}, createScenarioDependencies(ctx.marketData, ctx.apiClient));
|
|
49
78
|
}
|
|
@@ -51,13 +80,21 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
51
80
|
const position = scenarioPositionFromSettings(settings, market);
|
|
52
81
|
const controls = position ? scenarioControlsFromSettings(settings, position) : null;
|
|
53
82
|
const scenario = position && controls ? buildScenario(position, controls) : null;
|
|
54
|
-
const
|
|
55
|
-
|
|
83
|
+
const hedge = scenarioHedgeBudget(settings, position, market);
|
|
84
|
+
// A typed rate or yield stands in for the source that could not supply one.
|
|
85
|
+
const marketWarnings = market.warnings.filter((warning) => !(supplied(settings.dividendYield) && warning.startsWith("Dividend yield unavailable"))
|
|
86
|
+
&& !(supplied(settings.rate) && warning.startsWith("Treasury")));
|
|
87
|
+
const errors = scenario ? [...new Set([...marketWarnings, ...scenario.warnings])]
|
|
88
|
+
: [...marketWarnings, "No position supplied; add --legs or select --strategy"];
|
|
56
89
|
const midVolatility = !!position?.legs.some((leg) => leg.volatilitySource === "mid");
|
|
57
90
|
// A seeded strategy starts at the spot its quote mids imply; the last print is reported beside it.
|
|
58
91
|
const lastPrint = midVolatility && !supplied(settings.spot) && market.spot != null && money(market.spot) !== money(position!.spot)
|
|
59
92
|
? market.spot : null;
|
|
60
93
|
const chain = market.chain;
|
|
94
|
+
const currency = position ? currencyLabel(position.currency) : null;
|
|
95
|
+
// Value, P&L, risk and the grid add every contract; entry prices and spot are per share.
|
|
96
|
+
const valueUnit = position ? scenarioValueUnit(position) : null;
|
|
97
|
+
const per = `${currency} ${valueUnit}`;
|
|
61
98
|
return {
|
|
62
99
|
freshness: suppliedInputs
|
|
63
100
|
? { source: "Your inputs", status: "not-a-feed", basis: "scenario" }
|
|
@@ -66,33 +103,44 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
66
103
|
sections: scenario ? [
|
|
67
104
|
{ title: "Position", columns: [
|
|
68
105
|
{ key: "side", header: "Side" }, { key: "strike", header: "Strike" }, { key: "expiry", header: "Expiry" },
|
|
69
|
-
{ key: "quantity", header: "
|
|
106
|
+
{ key: "quantity", header: "Contracts" }, { key: "price", header: "Entry/share", format: premium },
|
|
70
107
|
{ key: "volatility", header: "IV", format: (value) => typeof value === "number" ? `${(value * 100).toFixed(2)}%` : "--" },
|
|
71
108
|
...(midVolatility ? [{ key: "volatilityFrom", header: "IV from" }] : []),
|
|
72
109
|
{ key: "multiplier", header: "Multiplier" },
|
|
73
110
|
], rows: position!.legs.map((leg) => ({ ...leg, expiry: new Date(leg.expiration * 1000).toISOString().slice(0, 10),
|
|
74
111
|
...(midVolatility ? { volatilityFrom: leg.volatilitySource === "mid" ? "quote mid" : "provider" } : {}) })) },
|
|
75
|
-
|
|
76
|
-
{ key: "spot", label: "
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{ label: "
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
{ title: "Inputs", entries: [
|
|
113
|
+
{ key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot,
|
|
114
|
+
formatted: `${money(position!.spot)} ${PRICE_UNIT}` },
|
|
115
|
+
...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint, formatted: `${money(lastPrint)} ${PRICE_UNIT}` }] : []),
|
|
116
|
+
{ key: "rate", label: "Rate", value: position!.rate, formatted: percent(position!.rate) },
|
|
117
|
+
{ key: "dividendYield", label: "Dividend yield", value: position!.dividendYield, formatted: percent(position!.dividendYield) },
|
|
118
|
+
{ key: "currency", label: "Currency", value: position!.currency, formatted: currency! },
|
|
119
|
+
] },
|
|
120
|
+
...(hedge ? [{ title: "Hedge budget", entries: hedgeReportEntries(hedge) }] : []),
|
|
121
|
+
{ title: `Valuation (${per})`, entries: Object.entries(scenario.valuation).map(([key, value]) => ({ key,
|
|
122
|
+
label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value) })) },
|
|
123
|
+
{ title: `Expiry risk (${per})`, entries: [
|
|
124
|
+
{ key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens,
|
|
125
|
+
formatted: scenario.expiryRisk.breakevens.map(money).join(", ") || "none" },
|
|
126
|
+
{ key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
|
|
127
|
+
{ key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
|
|
128
|
+
...(scenario.expiryRisk.reason ? [{ key: "limitation", label: "Limitation", value: scenario.expiryRisk.reason }] : []),
|
|
85
129
|
] },
|
|
86
|
-
{ title:
|
|
87
|
-
{ key: "spot", header: "Spot"
|
|
130
|
+
{ title: `Scenario grid (P&L, ${per})`, columns: [
|
|
131
|
+
{ key: "spot", header: "Spot/share", format: money },
|
|
132
|
+
{ key: "move", header: "Move", format: (value) => typeof value === "number" && Number.isFinite(value) ? `${(value * 100).toFixed(1)}%` : "--" },
|
|
88
133
|
...scenario.dates.map((date, index) => ({ key: `date${index}`, header: new Date(date).toISOString().slice(0, 10), format: money })),
|
|
89
134
|
], rows: scenario.grid.map((row) => ({ spot: row.spot, move: row.move,
|
|
90
135
|
...Object.fromEntries(row.values.map((value, index) => [`date${index}`, value])) })) },
|
|
91
136
|
] : [],
|
|
92
137
|
complete: scenario != null && errors.length === 0,
|
|
93
138
|
unavailableSymbols: scenario ? [] : [symbol], errors,
|
|
94
|
-
metadata: { scenario, position, controls, market, inputSource: suppliedInputs ? "user" : "market",
|
|
95
|
-
unit: position?.currency || "currency unspecified",
|
|
139
|
+
metadata: { scenario, position, controls, market, ...(hedge ? { hedge } : {}), inputSource: suppliedInputs ? "user" : "market",
|
|
140
|
+
unit: position?.currency || "currency unspecified",
|
|
141
|
+
// Leg entry prices and spot are per share; value, P&L, risk, Greeks in currency and the grid are for valueUnit.
|
|
142
|
+
...(position ? { units: { currency: position.currency, price: PRICE_UNIT, value: valueUnit } } : {}),
|
|
143
|
+
methodology: "docs/research-data.md#options-scenario-analysis" },
|
|
96
144
|
};
|
|
97
145
|
},
|
|
98
146
|
};
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { StatItem } from "../../../components";
|
|
2
|
+
import type { OptionsChain } from "../../../types/financials";
|
|
3
|
+
import type { HeadlessPaneEntry } from "../../../types/headless";
|
|
4
|
+
import { numberErrorMessage } from "../../../utils/decimal-comma";
|
|
5
|
+
import { formatCompact } from "../../../utils/format";
|
|
6
|
+
import { UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hedge budget sizing (docs/research-data.md#options-scenario-analysis): how
|
|
10
|
+
* many of the position as entered a budget of NAV buys, what that spends, and
|
|
11
|
+
* how much of the underlying it protects.
|
|
12
|
+
*
|
|
13
|
+
* The position is scaled as a whole, in sets of its smallest whole ratio (a
|
|
14
|
+
* put, a 1x1 spread, a 2x1 ratio), so the hedge keeps its shape. A set costs
|
|
15
|
+
* its net debit: each leg's price times its signed contracts and multiplier.
|
|
16
|
+
* The price is the entry price as entered, except for a leg a strategy seed
|
|
17
|
+
* entered at its contract's midpoint (`priceSource: "mid"`, its id the
|
|
18
|
+
* contract's symbol in the loaded chain), which buys at the ask and sells at
|
|
19
|
+
* the bid, so a seeded hedge never spends past its budget.
|
|
20
|
+
* The protecting legs are the long puts, or the long calls when the position
|
|
21
|
+
* holds no long put; notional protected is their contracts times multiplier
|
|
22
|
+
* times spot.
|
|
23
|
+
*/
|
|
24
|
+
export interface HedgeBudgetInputs {
|
|
25
|
+
/** Portfolio NAV, in the position's currency. */
|
|
26
|
+
nav: number;
|
|
27
|
+
/** The budget as basis points of NAV: 50 is 0.5%. */
|
|
28
|
+
budgetBps: number;
|
|
29
|
+
/** The equity sleeve the hedge protects; coverage is notional over it. */
|
|
30
|
+
sleeve: number | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** How the set was priced: the legs as entered, chain quotes (long at the ask, short at the bid), or some of each. */
|
|
34
|
+
type HedgePriceBasis = "entry" | "quote" | "mixed";
|
|
35
|
+
|
|
36
|
+
export interface HedgeBudget extends HedgeBudgetInputs {
|
|
37
|
+
currency: string;
|
|
38
|
+
/** NAV times budget bps. */
|
|
39
|
+
budget: number;
|
|
40
|
+
basis: HedgePriceBasis;
|
|
41
|
+
/** True for a single long leg: the price is that leg's own, not a net debit. */
|
|
42
|
+
singleLeg: boolean;
|
|
43
|
+
/** Per underlying unit of one protecting contract; null when the legs' multipliers differ. */
|
|
44
|
+
sizingPrice: number | null;
|
|
45
|
+
/** What one protecting contract costs, all legs of its set included. */
|
|
46
|
+
costPerContract: number;
|
|
47
|
+
/** Units of the underlying one protecting contract covers. */
|
|
48
|
+
multiplier: number;
|
|
49
|
+
/** Protecting contracts bought: whole sets, rounded down, times the protecting contracts in a set. */
|
|
50
|
+
contracts: number;
|
|
51
|
+
/** Premium spent: whole sets times what a set costs. */
|
|
52
|
+
premium: number;
|
|
53
|
+
/** Premium over NAV in basis points: what the budget actually spends. */
|
|
54
|
+
premiumBps: number;
|
|
55
|
+
/** The underlying price notional is read at. */
|
|
56
|
+
spot: number;
|
|
57
|
+
/** Protecting contracts times multiplier times spot. */
|
|
58
|
+
notional: number;
|
|
59
|
+
/** Notional over NAV, as a fraction. */
|
|
60
|
+
notionalOfNav: number;
|
|
61
|
+
/** Notional over the sleeve, as a fraction; null without a sleeve. */
|
|
62
|
+
coverage: number | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const supplied = (value: unknown) => value != null && value !== "";
|
|
66
|
+
|
|
67
|
+
const AMOUNT_SCALES: Record<string, number> = { k: 1e3, m: 1e6, mm: 1e6, mn: 1e6, b: 1e9, bn: 1e9, t: 1e12, tn: 1e12 };
|
|
68
|
+
const AMOUNT = /^\$?\s*(\d{1,3}(?:,\d{3})+|\d+)(\.\d+)?\s*([a-z]{1,2})?$/i;
|
|
69
|
+
const AMOUNT_FORMS = "such as 100m, 2.5bn, 250k, 1,000,000 or $100m";
|
|
70
|
+
|
|
71
|
+
/** `100m`, `2.5bn`, `250k`, `1,000,000` and `$100m` as a positive amount; anything else names the flag and the forms. */
|
|
72
|
+
export function parseHedgeAmount(value: unknown, flag: string): number {
|
|
73
|
+
const text = typeof value === "number" ? String(value) : typeof value === "string" ? value.trim() : "";
|
|
74
|
+
if (/^-/.test(text)) throw new Error(`--${flag} must be greater than zero.`);
|
|
75
|
+
const match = AMOUNT.exec(text);
|
|
76
|
+
const scale = match ? match[3] ? AMOUNT_SCALES[match[3].toLowerCase()] : 1 : undefined;
|
|
77
|
+
const amount = match && scale ? Number(`${match[1]!.replaceAll(",", "")}${match[2] ?? ""}`) * scale : Number.NaN;
|
|
78
|
+
if (!Number.isFinite(amount)) throw new Error(`--${flag} must be an amount ${AMOUNT_FORMS}; got "${text}".`);
|
|
79
|
+
if (!(amount > 0)) throw new Error(`--${flag} must be greater than zero.`);
|
|
80
|
+
return amount;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseBudgetBps(value: unknown): number {
|
|
84
|
+
const text = typeof value === "number" ? String(value) : typeof value === "string" ? value.trim() : "";
|
|
85
|
+
const bps = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(text) ? Number(text) : Number.NaN;
|
|
86
|
+
if (!Number.isFinite(bps)) throw new Error(numberErrorMessage(`--budget-bps must be a number of basis points, such as 50 for 0.5% of NAV; got "${text}".`, text));
|
|
87
|
+
if (!(bps > 0)) throw new Error("--budget-bps must be greater than zero.");
|
|
88
|
+
if (bps > 10_000) throw new Error("--budget-bps must be at most 10,000 (all of NAV).");
|
|
89
|
+
return bps;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `--nav`, `--budget-bps` and `--sleeve` as pane settings or report options.
|
|
94
|
+
* Null when none is set; each needs the others it depends on.
|
|
95
|
+
*/
|
|
96
|
+
export function hedgeInputsFromSettings(settings: Record<string, unknown>): HedgeBudgetInputs | null {
|
|
97
|
+
const hasNav = supplied(settings.nav);
|
|
98
|
+
const hasBudget = supplied(settings.budgetBps);
|
|
99
|
+
const hasSleeve = supplied(settings.sleeve);
|
|
100
|
+
if (!hasNav && !hasBudget) {
|
|
101
|
+
if (hasSleeve) throw new Error("--sleeve needs --nav and --budget-bps: coverage is the notional a hedge budget buys over the sleeve.");
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (!hasBudget) throw new Error("--nav needs --budget-bps: the share of NAV to spend, in basis points (50 is 0.5%).");
|
|
105
|
+
if (!hasNav) throw new Error("--budget-bps needs --nav: the portfolio value the budget is a share of, such as 100m.");
|
|
106
|
+
return { nav: parseHedgeAmount(settings.nav, "nav"), budgetBps: parseBudgetBps(settings.budgetBps),
|
|
107
|
+
sleeve: hasSleeve ? parseHedgeAmount(settings.sleeve, "sleeve") : null };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function gcd(left: number, right: number): number {
|
|
111
|
+
return right === 0 ? left : gcd(right, left % right);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Float noise off a cent-quoted premium times a multiplier, so 5.495 x 100 is 549.5. */
|
|
115
|
+
const clean = (value: number) => Math.round(value * 1e6) / 1e6;
|
|
116
|
+
|
|
117
|
+
export interface HedgeQuote { bid: number; ask: number }
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The bid and ask of each leg a strategy seeded from the chain, by leg id: the
|
|
121
|
+
* chain's, or the leg's streamed quote when the pane follows the market.
|
|
122
|
+
*/
|
|
123
|
+
function hedgeLegQuotes(
|
|
124
|
+
position: ScenarioPosition, chain: OptionsChain | null | undefined, liveQuotes?: ReadonlyMap<string, HedgeQuote>,
|
|
125
|
+
): Map<string, HedgeQuote> {
|
|
126
|
+
const contracts = new Map([...(chain?.calls ?? []), ...(chain?.puts ?? [])].map((contract) => [contract.contractSymbol, contract]));
|
|
127
|
+
const quotes = new Map<string, HedgeQuote>();
|
|
128
|
+
for (const leg of position.legs) {
|
|
129
|
+
const contract = leg.priceSource === "mid" ? contracts.get(leg.id) : undefined;
|
|
130
|
+
if (contract && contract.strike === leg.strike && contract.expiration === leg.expiration
|
|
131
|
+
&& contract.bid > 0 && contract.ask >= contract.bid && Number.isFinite(contract.ask)) {
|
|
132
|
+
const live = liveQuotes?.get(leg.id);
|
|
133
|
+
quotes.set(leg.id, live && live.bid > 0 && live.ask >= live.bid ? live : { bid: contract.bid, ask: contract.ask });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return quotes;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The underlying price notional is read at: the last price when the scenario
|
|
141
|
+
* starts at a spot the option quotes imply (a seeded strategy) and no spot was
|
|
142
|
+
* typed, otherwise the scenario's own spot.
|
|
143
|
+
*/
|
|
144
|
+
function hedgeSpot(position: ScenarioPosition, marketSpot: number | null | undefined, spotTyped: boolean): number {
|
|
145
|
+
const quoteImplied = position.legs.some((leg) => leg.volatilitySource === "mid");
|
|
146
|
+
return quoteImplied && !spotTyped && marketSpot != null && Number.isFinite(marketSpot) && marketSpot > 0 ? marketSpot : position.spot;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const money = (value: number) => value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
150
|
+
|
|
151
|
+
export function sizeHedgeBudget(
|
|
152
|
+
position: ScenarioPosition,
|
|
153
|
+
inputs: HedgeBudgetInputs,
|
|
154
|
+
{ quotes = new Map(), spot = position.spot }: { quotes?: ReadonlyMap<string, HedgeQuote>; spot?: number } = {},
|
|
155
|
+
): HedgeBudget {
|
|
156
|
+
const longs = position.legs.filter((leg) => leg.quantity > 0);
|
|
157
|
+
if (!longs.length) throw new Error("A hedge budget buys options; this position has no long option.");
|
|
158
|
+
const step = position.legs.reduce((divisor, leg) => gcd(divisor, Math.abs(leg.quantity)), 0);
|
|
159
|
+
const protecting = longs.some((leg) => leg.side === "put") ? longs.filter((leg) => leg.side === "put") : longs;
|
|
160
|
+
const priced = position.legs.map((leg) => {
|
|
161
|
+
const quote = quotes.get(leg.id);
|
|
162
|
+
return { leg, quoted: !!quote, price: quote ? leg.quantity > 0 ? quote.ask : quote.bid : leg.price };
|
|
163
|
+
});
|
|
164
|
+
const quotedCount = priced.filter((entry) => entry.quoted).length;
|
|
165
|
+
const basis: HedgePriceBasis = quotedCount === 0 ? "entry" : quotedCount === priced.length ? "quote" : "mixed";
|
|
166
|
+
const setCost = clean(priced.reduce((sum, { leg, price }) => sum + price * leg.quantity / step * leg.multiplier, 0));
|
|
167
|
+
const currency = position.currency === UNKNOWN_CURRENCY ? "" : ` ${position.currency}`;
|
|
168
|
+
if (!(setCost > 0)) {
|
|
169
|
+
throw new Error(setCost < 0
|
|
170
|
+
? `This position takes in a net credit of ${money(-setCost)}${currency} per set, so a budget does not size it; a hedge budget buys a net debit.`
|
|
171
|
+
: "This position costs nothing at these prices, so a budget does not size it.");
|
|
172
|
+
}
|
|
173
|
+
const contractsPerSet = protecting.reduce((sum, leg) => sum + leg.quantity / step, 0);
|
|
174
|
+
const unitsPerSet = protecting.reduce((sum, leg) => sum + leg.quantity / step * leg.multiplier, 0);
|
|
175
|
+
const multipliers = new Set(position.legs.map((leg) => leg.multiplier));
|
|
176
|
+
const multiplier = unitsPerSet / contractsPerSet;
|
|
177
|
+
const costPerContract = setCost / contractsPerSet;
|
|
178
|
+
const sizingPrice = multipliers.size === 1 ? clean(costPerContract / multiplier) : null;
|
|
179
|
+
const budget = clean(inputs.nav * inputs.budgetBps / 10_000);
|
|
180
|
+
// A budget that is an exact multiple of the set's cost buys that many sets, despite float division.
|
|
181
|
+
const sets = Math.floor(budget / setCost * (1 + 1e-12));
|
|
182
|
+
if (sets < 1) {
|
|
183
|
+
const single = position.legs.length === 1 && contractsPerSet === 1;
|
|
184
|
+
const at = single && sizingPrice != null ? ` at ${describePrice(basis, true, sizingPrice)}` : "";
|
|
185
|
+
throw new Error(`A ${formatBps(inputs.budgetBps)} budget of ${money(inputs.nav)}${currency} NAV is ${money(budget)}${currency}; `
|
|
186
|
+
+ `one ${single ? "contract" : "set of the position"} costs ${money(setCost)}${currency}${at}.`);
|
|
187
|
+
}
|
|
188
|
+
const contracts = sets * contractsPerSet;
|
|
189
|
+
const premium = clean(sets * setCost);
|
|
190
|
+
const notional = sets * unitsPerSet * spot;
|
|
191
|
+
return { ...inputs, currency: position.currency, budget, basis, singleLeg: position.legs.length === 1, sizingPrice,
|
|
192
|
+
costPerContract, multiplier, contracts, premium, premiumBps: premium / inputs.nav * 10_000, spot, notional,
|
|
193
|
+
notionalOfNav: notional / inputs.nav, coverage: inputs.sleeve == null ? null : notional / inputs.sleeve };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Hedge sizing for a scenario, read the same way by the report and the pane:
|
|
198
|
+
* null without `--nav` and `--budget-bps`; throws on a bad input or a
|
|
199
|
+
* position a budget cannot size.
|
|
200
|
+
*/
|
|
201
|
+
export function scenarioHedgeBudget(
|
|
202
|
+
settings: Record<string, unknown>,
|
|
203
|
+
position: ScenarioPosition | null,
|
|
204
|
+
market: { chain?: OptionsChain | null; spot?: number | null; liveQuotes?: ReadonlyMap<string, HedgeQuote> } | null | undefined,
|
|
205
|
+
): HedgeBudget | null {
|
|
206
|
+
const inputs = hedgeInputsFromSettings(settings);
|
|
207
|
+
if (!inputs) return null;
|
|
208
|
+
if (!position?.legs.length) throw new Error("A hedge budget needs a position; add --legs or select --strategy.");
|
|
209
|
+
return sizeHedgeBudget(position, inputs, { quotes: hedgeLegQuotes(position, market?.chain, market?.liveQuotes),
|
|
210
|
+
spot: hedgeSpot(position, market?.spot, supplied(settings.spot)) });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** `50 bps`, `12.5 bps`. */
|
|
214
|
+
function formatBps(value: number): string {
|
|
215
|
+
return `${Number(value.toFixed(2))} bps`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** A per-unit premium with the decimals it was quoted to: 5.51, 5.495. */
|
|
219
|
+
export function formatHedgePrice(value: number): string {
|
|
220
|
+
return value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 4 });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** The price a budget is sized at, and where it came from: `5.51 per share, the ask`, `2.30 per share net debit at entry prices`. */
|
|
224
|
+
function describePrice(basis: HedgePriceBasis, singleLeg: boolean, price: number): string {
|
|
225
|
+
const value = `${formatHedgePrice(price)} per share`;
|
|
226
|
+
if (singleLeg) return basis === "quote" ? `${value}, the ask` : `${value}, the entry price`;
|
|
227
|
+
return basis === "quote" ? `${value} net debit: long legs at the ask, short at the bid`
|
|
228
|
+
: basis === "mixed" ? `${value} net debit: quoted legs at the ask or bid, the rest at entry`
|
|
229
|
+
: `${value} net debit at entry prices`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** The price basis in a word or two, for a figure's detail: `ask 5.51`, `entry 5.495`, `net debit 2.30`. */
|
|
233
|
+
function shortPrice(hedge: HedgeBudget): string {
|
|
234
|
+
if (hedge.sizingPrice == null) return `${money(hedge.costPerContract)} each`;
|
|
235
|
+
const word = !hedge.singleLeg ? "net debit" : hedge.basis === "quote" ? "ask" : "entry";
|
|
236
|
+
return `${word} ${formatHedgePrice(hedge.sizingPrice)}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const percent = (fraction: number) => `${(fraction * 100).toFixed(1)}%`;
|
|
240
|
+
|
|
241
|
+
/** `551.00 USD (5.51 x 100)`: what one protecting contract costs, and the price behind it. */
|
|
242
|
+
function costText(cost: number, sizingPrice: number | null, multiplier: number, unit: string): string {
|
|
243
|
+
return `${money(cost)}${unit}${sizingPrice == null ? "" : ` (${formatHedgePrice(sizingPrice)} x ${multiplier})`}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** The report's Hedge budget section; `value` keeps full precision, `formatted` is what text output prints. */
|
|
247
|
+
export function hedgeReportEntries(hedge: HedgeBudget): HeadlessPaneEntry[] {
|
|
248
|
+
const unit = hedge.currency && hedge.currency !== UNKNOWN_CURRENCY ? ` ${hedge.currency}` : "";
|
|
249
|
+
return [
|
|
250
|
+
{ key: "nav", label: "NAV", value: hedge.nav, formatted: `${money(hedge.nav)}${unit}` },
|
|
251
|
+
{ key: "budget", label: "Budget", value: hedge.budget, formatted: `${money(hedge.budget)}${unit} (${formatBps(hedge.budgetBps)} of NAV)` },
|
|
252
|
+
{ key: "sizingPrice", label: "Sizing price", value: hedge.sizingPrice,
|
|
253
|
+
formatted: hedge.sizingPrice == null ? "--" : describePrice(hedge.basis, hedge.singleLeg, hedge.sizingPrice) },
|
|
254
|
+
{ key: "costPerContract", label: "Cost per contract", value: hedge.costPerContract, formatted: costText(hedge.costPerContract, hedge.sizingPrice, hedge.multiplier, unit) },
|
|
255
|
+
{ key: "contracts", label: "Contracts", value: hedge.contracts, formatted: hedge.contracts.toLocaleString("en-US") },
|
|
256
|
+
{ key: "premium", label: "Premium spent", value: hedge.premium, formatted: `${money(hedge.premium)}${unit}` },
|
|
257
|
+
{ key: "premiumBps", label: "Premium / NAV", value: hedge.premiumBps, formatted: `${formatBps(hedge.premiumBps)} (${(hedge.premiumBps / 100).toFixed(2)}% of NAV)` },
|
|
258
|
+
{ key: "notional", label: "Notional protected", value: hedge.notional,
|
|
259
|
+
formatted: `${money(hedge.notional)}${unit} (${hedge.contracts.toLocaleString("en-US")} x ${hedge.multiplier} x ${formatHedgePrice(hedge.spot)})` },
|
|
260
|
+
{ key: "notionalOfNav", label: "Notional / NAV", value: hedge.notionalOfNav, formatted: percent(hedge.notionalOfNav) },
|
|
261
|
+
hedge.coverage == null || hedge.sleeve == null
|
|
262
|
+
? { key: "coverage", label: "Coverage", value: null, formatted: "No equity sleeve given; add --sleeve <amount> to read notional against it" }
|
|
263
|
+
: { key: "coverage", label: "Coverage", value: hedge.coverage, formatted: `${percent(hedge.coverage)} of a ${money(hedge.sleeve)}${unit} sleeve` },
|
|
264
|
+
];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** `100M`, `70.62M`: an amount short enough for a figure. */
|
|
268
|
+
const short = (value: number) => formatCompact(value);
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The pane's Hedge budget figures: the same numbers as the report, sized for a
|
|
272
|
+
* StatGrid. The currency is the P&L figure's, so it is not repeated here.
|
|
273
|
+
*/
|
|
274
|
+
export function hedgeStatItems(hedge: HedgeBudget): StatItem[] {
|
|
275
|
+
return [
|
|
276
|
+
{ id: "hedge-budget", label: "Hedge budget", value: money(hedge.budget), detail: `${formatBps(hedge.budgetBps)} of ${short(hedge.nav)}` },
|
|
277
|
+
{ id: "hedge-contracts", label: "Contracts", value: hedge.contracts.toLocaleString("en-US"), detail: shortPrice(hedge) },
|
|
278
|
+
{ id: "hedge-premium", label: "Premium", value: money(hedge.premium), detail: `${formatBps(hedge.premiumBps)} of NAV` },
|
|
279
|
+
{ id: "hedge-notional", label: "Notional", value: short(hedge.notional),
|
|
280
|
+
detail: `${percent(hedge.notionalOfNav)} of NAV at ${formatHedgePrice(hedge.spot)}` },
|
|
281
|
+
hedge.coverage == null || hedge.sleeve == null
|
|
282
|
+
? { id: "hedge-coverage", label: "Coverage", value: "--", detail: "no sleeve set" }
|
|
283
|
+
: { id: "hedge-coverage", label: "Coverage", value: percent(hedge.coverage), detail: `of ${short(hedge.sleeve)} sleeve` },
|
|
284
|
+
];
|
|
285
|
+
}
|
|
@@ -3,7 +3,8 @@ import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
|
3
3
|
import { OptionsScenarioPane } from "./pane";
|
|
4
4
|
import { optionsScenarioHeadless } from "./headless";
|
|
5
5
|
|
|
6
|
-
const scenarioSettingKeys = ["legs", "strategy", "expiration", "spot", "rate", "dividendYield", "currency", "asOf", "date", "volShift",
|
|
6
|
+
const scenarioSettingKeys = ["legs", "strategy", "strike", "expiration", "spot", "rate", "dividendYield", "currency", "asOf", "date", "volShift",
|
|
7
|
+
"spotRange", "seedLeg", "nav", "budgetBps", "sleeve"];
|
|
7
8
|
|
|
8
9
|
export const optionsScenarioModule: PluginModule = {
|
|
9
10
|
panes: [{
|
|
@@ -14,13 +15,19 @@ export const optionsScenarioModule: PluginModule = {
|
|
|
14
15
|
title: "Options Scenario Settings",
|
|
15
16
|
fields: [
|
|
16
17
|
{ key: "spotRange", label: "Spot grid range (%)", type: "text" },
|
|
18
|
+
{ key: "nav", label: "Hedge NAV", type: "text", placeholder: "100m",
|
|
19
|
+
description: "Portfolio NAV a hedge budget is a share of, such as 100m, 2.5bn or 1,000,000. Set with a budget to size the position." },
|
|
20
|
+
{ key: "budgetBps", label: "Hedge budget (bps)", type: "text", placeholder: "50",
|
|
21
|
+
description: "Share of NAV to spend on the position, in basis points: 50 is 0.5%." },
|
|
22
|
+
{ key: "sleeve", label: "Equity sleeve", type: "text", placeholder: "100m",
|
|
23
|
+
description: "The equity the hedge protects, for coverage: notional protected over the sleeve." },
|
|
17
24
|
],
|
|
18
25
|
},
|
|
19
26
|
}],
|
|
20
27
|
paneTemplates: [createTickerSurfacePaneTemplate({
|
|
21
28
|
id: "options-scenario-pane", paneId: "options-scenario", label: "Options Scenario",
|
|
22
29
|
description: "Build multi-leg positions and compare scenario P&L, payoff and aggregate Greeks.",
|
|
23
|
-
keywords: ["osa", "options", "scenario", "payoff", "strategy", "greeks"],
|
|
30
|
+
keywords: ["osa", "options", "scenario", "payoff", "strategy", "greeks", "hedge", "hedging", "collar", "protective put", "put spread"],
|
|
24
31
|
shortcut: "OSA", publicShare: true,
|
|
25
32
|
settings: (symbol, _context, options) => ({ symbol, ...Object.fromEntries(scenarioSettingKeys
|
|
26
33
|
.filter((key) => options?.values?.[key] != null).map((key) => [key, options!.values![key]])) }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { daysToExpiryFrom, valueOption, type OptionValuation } from "../shared/volatility";
|
|
2
2
|
import { isFiniteNumber } from "../../../utils/guards";
|
|
3
|
+
import { isUsListingExchange } from "../../../utils/exchanges";
|
|
3
4
|
|
|
4
5
|
const DAY_MS = 86_400_000;
|
|
5
6
|
const MAX_DATE_MS = Date.UTC(9999, 11, 31);
|
|
@@ -19,6 +20,12 @@ export interface ScenarioLeg {
|
|
|
19
20
|
volatility: number;
|
|
20
21
|
/** "mid" when the IV was solved from the quote midpoint rather than taken from the provider. */
|
|
21
22
|
volatilitySource?: "mid";
|
|
23
|
+
/**
|
|
24
|
+
* "mid" when a strategy seed entered the leg at the midpoint of the chain
|
|
25
|
+
* contract its id names; a hedge budget then buys it at the ask. Saving the
|
|
26
|
+
* leg from the editor drops it: the price is then as typed.
|
|
27
|
+
*/
|
|
28
|
+
priceSource?: "mid";
|
|
22
29
|
multiplier: number;
|
|
23
30
|
}
|
|
24
31
|
|
|
@@ -129,6 +136,32 @@ export function validateScenarioInputs(position: ScenarioPosition): string | nul
|
|
|
129
136
|
return null;
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
/** The stored stand-in for a currency nothing names; shown as `currency unknown`. */
|
|
140
|
+
export const UNKNOWN_CURRENCY = "UNKNOWN";
|
|
141
|
+
export const currencyLabel = (currency: string) => currency === UNKNOWN_CURRENCY ? "currency unknown" : currency;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The position's currency: the one typed, else the underlying's quote
|
|
145
|
+
* currency, else USD for a US listing (a bare symbol is one, as Gloom Cloud
|
|
146
|
+
* lists only US options). A listing abroad with no quote stays unknown.
|
|
147
|
+
*/
|
|
148
|
+
export function scenarioCurrency(typed: unknown, quoted: string | null | undefined, exchange: string | undefined): string {
|
|
149
|
+
const explicit = typed == null ? "" : String(typed).trim().toUpperCase();
|
|
150
|
+
if (explicit) return explicit;
|
|
151
|
+
if (quoted?.trim()) return quoted.trim().toUpperCase();
|
|
152
|
+
return !exchange || isUsListingExchange(exchange) ? "USD" : UNKNOWN_CURRENCY;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* What the position's money figures (value, P&L, risk, Greeks in currency) are
|
|
157
|
+
* for: they add every contract times its multiplier, while entry prices and
|
|
158
|
+
* spot are per share. One contract reads `per contract`.
|
|
159
|
+
*/
|
|
160
|
+
export function scenarioValueUnit(position: Pick<ScenarioPosition, "legs">): string {
|
|
161
|
+
const contracts = position.legs.reduce((sum, leg) => sum + Math.abs(leg.quantity), 0);
|
|
162
|
+
return contracts === 1 ? "per contract" : `for ${contracts.toLocaleString("en-US")} contracts`;
|
|
163
|
+
}
|
|
164
|
+
|
|
132
165
|
/** Also validates restored persistence values at the model boundary. */
|
|
133
166
|
export function validatePosition(position: ScenarioPosition): string | null {
|
|
134
167
|
return validateScenarioInputs(position) ?? (position.legs.length ? null : "Add at least one option leg.");
|