gloomberb 0.18.3 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/src/account/checkout-handoff.tsx +225 -0
- package/src/account/device-signin-dialog.tsx +2 -2
- package/src/account/device-signin.ts +2 -2
- package/src/account/upgrade-dialog.tsx +1 -1
- package/src/api-client/askg.ts +2 -2
- package/src/api-client/auth.ts +2 -2
- package/src/api-client/central-bank-rates.ts +6 -0
- package/src/api-client/collections.ts +5 -1
- package/src/api-client/cot.ts +2 -0
- package/src/api-client/crypto-etf-flows.ts +37 -0
- package/src/api-client/crypto-markets.ts +60 -0
- package/src/api-client/data.ts +84 -5
- package/src/api-client/doe.ts +15 -0
- package/src/api-client/equity-screener.ts +28 -0
- package/src/api-client/exchange-announcements.ts +38 -0
- package/src/api-client/fund-facts.ts +39 -0
- package/src/api-client/futures-curve.ts +9 -1
- package/src/api-client/hiring.ts +301 -0
- package/src/api-client/index.ts +22 -5
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +6 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +21 -4
- package/src/api-client/short-side-publications.ts +37 -0
- package/src/api-client/socket.ts +64 -4
- package/src/api-client/swap-curve.ts +35 -0
- package/src/api-client/themes.ts +7 -1
- package/src/api-client/types.ts +90 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/desktop-deeplink.ts +5 -5
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +13 -0
- package/src/app/star-prompt/status-line.tsx +2 -2
- package/src/architecture/core-builtin-allowlist.json +5 -2
- package/src/architecture/ui-literals-allowlist.json +3 -3
- package/src/architecture/ui-tokens-allowlist.json +1 -1
- package/src/brokers/broker-portfolio-sync.ts +37 -0
- package/src/brokers/directory.ts +11 -0
- package/src/brokers/logos.ts +3 -1
- package/src/brokers/signed-in/adapter.ts +304 -26
- package/src/brokers/signed-in/client.ts +14 -2
- package/src/brokers/signed-in/connect.ts +10 -2
- package/src/brokers/sync-broker-instance.ts +9 -1
- package/src/capabilities/chart-series.ts +22 -6
- package/src/cli/bank-section.ts +1 -1
- package/src/cli/base-converter.ts +23 -14
- package/src/cli/central-bank-events.ts +1 -1
- package/src/cli/command-options.ts +59 -4
- package/src/cli/commands/api.ts +9 -6
- package/src/cli/commands/broker.ts +2 -2
- package/src/cli/commands/command-utils.ts +2 -2
- package/src/cli/commands/crypto-hints.ts +7 -20
- package/src/cli/commands/fred-series.ts +4 -2
- package/src/cli/commands/keybindings.ts +2 -2
- package/src/cli/commands/market.ts +1117 -264
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +121 -48
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +64 -44
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +192 -47
- package/src/cli/context.ts +3 -2
- package/src/cli/desktop-pane-shot-view.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +1 -1
- package/src/cli/earnings-calendar.ts +4 -4
- package/src/cli/entry.ts +18 -1
- package/src/cli/errors.ts +20 -2
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +10 -4
- package/src/cli/freshness.ts +2 -8
- package/src/cli/fx-forward.ts +130 -0
- package/src/cli/fx-pair.ts +10 -9
- package/src/cli/glossary.ts +64 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +196 -43
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +118 -25
- package/src/cli/listing-arg.ts +300 -41
- package/src/cli/name-entries.ts +30 -18
- package/src/cli/native-smoke.ts +2 -0
- package/src/cli/not-a-ticker.ts +19 -9
- package/src/cli/opentui-libc.ts +5 -0
- package/src/cli/options.ts +12 -0
- package/src/cli/pane-functions/capabilities.ts +75 -20
- package/src/cli/pane-functions/catalog.ts +24 -23
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +10 -7
- package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
- package/src/cli/pane-functions/freshness.ts +174 -23
- package/src/cli/pane-functions/function-help.ts +66 -27
- package/src/cli/pane-functions/headless.ts +24 -11
- package/src/cli/pane-functions/index.ts +18 -16
- package/src/cli/pane-functions/options.ts +22 -2
- package/src/cli/pane-functions/report-notices.ts +12 -4
- package/src/cli/pane-functions/report.ts +5 -5
- package/src/cli/pane-functions/resolver.ts +47 -10
- package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
- package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
- package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
- package/src/cli/pane-functions/screenshot-payload.ts +453 -0
- package/src/cli/pane-functions/screenshot-render.ts +316 -0
- package/src/cli/pane-functions/screenshot-types.ts +162 -0
- package/src/cli/pane-functions/shot-options.ts +8 -3
- package/src/cli/peso-dollar-rates.ts +79 -0
- package/src/cli/quote-answer.ts +38 -3
- package/src/cli/report-tables.ts +61 -16
- package/src/cli/restore-plugins.ts +29 -14
- package/src/cli/result.ts +111 -22
- package/src/cli/series-search.ts +5 -5
- package/src/cli/statement-filings.ts +19 -0
- package/src/cli/suffixed-listing-rows.ts +49 -0
- package/src/cli/symbol-args.ts +50 -15
- package/src/cli/symbol-cells.ts +120 -0
- package/src/cli/symbol-list.ts +198 -68
- package/src/cli/symbol-rows.ts +131 -25
- package/src/cli/ticker-resolution.ts +108 -11
- package/src/cli/treasury-yields.ts +5 -5
- package/src/cli/types.ts +2 -0
- package/src/cli/uncovered-instrument.ts +3 -3
- package/src/cli/unknown-command.ts +6 -6
- package/src/components/chart/composite/chart-layout.ts +131 -0
- package/src/components/chart/composite/composite-chart.tsx +373 -16
- package/src/components/chart/composite/interactions.ts +7 -2
- package/src/components/chart/composite/legend.tsx +75 -3
- package/src/components/chart/composite/levels.ts +5 -0
- package/src/components/chart/composite/panel-gestures.ts +41 -0
- package/src/components/chart/composite/panel-labels.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +305 -11
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +16 -4
- package/src/components/chart/composite/text-renderer.ts +48 -21
- package/src/components/chart/composite/toolbar.tsx +52 -5
- package/src/components/chart/composite/types.ts +13 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
- package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
- package/src/components/chart/composite/use-composite-viewport.ts +12 -3
- package/src/components/chart/composite/value-scale.ts +149 -0
- package/src/components/chart/composite/volume-profile-paint.ts +6 -3
- package/src/components/chart/core/pointer.ts +2 -0
- package/src/components/chart-table/index.ts +2 -0
- package/src/components/chart-table/read.tsx +74 -0
- package/src/components/command-bar/commands/collection-list.ts +41 -19
- package/src/components/command-bar/commands/collection.ts +9 -3
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +3 -2
- package/src/components/command-bar/pane-templates/items.ts +59 -6
- package/src/components/command-bar/pane-templates/workflow.ts +10 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
- package/src/components/command-bar/routes/root/provider-search.ts +42 -11
- package/src/components/command-bar/routes/root/results.ts +18 -4
- package/src/components/command-bar/routes/root/runtime.ts +15 -3
- package/src/components/command-bar/routes/root/search-providers.ts +25 -2
- package/src/components/command-bar/routes/root/selection.ts +6 -1
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
- package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
- package/src/components/command-bar/selection-runtime.ts +50 -10
- package/src/components/command-bar/surface/index.tsx +24 -2
- package/src/components/command-bar/view-model.ts +7 -0
- package/src/components/command-bar/workflow/broker.ts +2 -2
- package/src/components/command-bar/workflow/collection-actions.ts +46 -7
- package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
- package/src/components/command-bar/workflow/fields.ts +8 -2
- package/src/components/command-bar/workflow/ops.ts +18 -4
- package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
- package/src/components/command-bar/workflow/submit.ts +3 -2
- package/src/components/command-bar/workflow/tickers.ts +5 -1
- package/src/components/company-logo.tsx +39 -0
- package/src/components/data-table/sections.ts +4 -2
- package/src/components/form-modal/confirm.tsx +2 -1
- package/src/components/form-modal/request.ts +5 -0
- package/src/components/form-modal/routes.ts +4 -1
- package/src/components/index.ts +12 -6
- package/src/components/layout/header.tsx +7 -6
- package/src/components/layout/market-summary.ts +9 -6
- package/src/components/layout/pane/capture-status.ts +1 -1
- package/src/components/layout/pane/footer/model.ts +11 -5
- package/src/components/layout/shell/chrome.ts +19 -6
- package/src/components/layout/shell/index.tsx +2 -1
- package/src/components/layout/shell/pane/layers.tsx +19 -1
- package/src/components/layout/status-bar.tsx +2 -2
- package/src/components/market-board/index.tsx +11 -3
- package/src/components/onboarding/onboarding-wizard.tsx +1 -0
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
- package/src/components/onboarding/wizard-broker-sync.ts +4 -1
- package/src/components/pane-settings-dialog/desktop.tsx +20 -7
- package/src/components/pane-settings-dialog/tui.tsx +17 -9
- package/src/components/pane-settings-dialog/value.ts +8 -1
- package/src/components/pane-settings-dialog.tsx +44 -13
- package/src/components/sign-in-code-panel.tsx +27 -19
- package/src/components/ui/column-grid.tsx +48 -4
- package/src/components/ui/country-flag-art.tsx +180 -0
- package/src/components/ui/country-flag.tsx +118 -12
- package/src/components/ui/data-table/opentui/index.tsx +61 -2
- package/src/components/ui/data-table/opentui/model.ts +109 -2
- package/src/components/ui/data-table/types.ts +1 -1
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/logo-mark.tsx +11 -2
- package/src/components/ui/query-bar.tsx +1 -1
- package/src/components/ui/row-notes.ts +79 -0
- package/src/core/state/app/layout.ts +4 -4
- package/src/data/cached-query.ts +8 -1
- package/src/data/config/chart-settings.ts +3 -3
- package/src/data/config/home.ts +10 -2
- package/src/data/config/layout.ts +4 -1
- package/src/data/config/store/node.ts +4 -0
- package/src/data/config/store/normalize.ts +13 -3
- package/src/data/config/store/portfolio-as-of-column.ts +2 -2
- package/src/data/json-storage.ts +2 -0
- package/src/data/saved-copies.ts +84 -0
- package/src/docs/snapshot.json +65 -32
- package/src/i18n/es.ts +29 -12
- package/src/i18n/ja.ts +29 -12
- package/src/i18n/ko.ts +29 -12
- package/src/i18n/zh-cn.ts +29 -12
- package/src/i18n/zh-tw.ts +29 -12
- package/src/layout/desks.ts +1 -1
- package/src/layout/launch-layout.ts +1 -1
- package/src/layout/pane-manager/floating-actions.ts +5 -3
- package/src/layout/pane-manager/floating.ts +43 -1
- package/src/layout/pane-manager/index.ts +2 -0
- package/src/layout/selected-ticker.ts +74 -0
- package/src/market-data/depositary-pair.ts +251 -0
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +124 -40
- package/src/market-data/fx-closes.ts +17 -2
- package/src/market-data/hooks.tsx +19 -11
- package/src/market-data/market/change-basis.ts +49 -7
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/trading-sessions.ts +13 -0
- package/src/market-data/market/year-to-date.ts +8 -13
- package/src/market-data/performance.ts +20 -14
- package/src/market-data/perpetual-premium.ts +32 -0
- package/src/market-data/published-apac-sessions.ts +2 -1
- package/src/market-data/published-futures-sessions.ts +69 -0
- package/src/market-data/quotes/contributions.ts +5 -1
- package/src/market-data/quotes/freshness.ts +42 -0
- package/src/market-data/quotes/intraday-bar.ts +37 -0
- package/src/market-data/quotes/regular-session-valuation.ts +13 -8
- package/src/market-data/quotes/resolution.ts +4 -2
- package/src/market-data/tax-equivalent.ts +1 -1
- package/src/market-data/ticker-price-returns.ts +3 -1
- package/src/news/aggregator.ts +5 -1
- package/src/news/news-model.ts +4 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/app-notifier.ts +1 -1
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +9 -8
- package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
- package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
- package/src/plugins/builtin/alerts/cli.ts +485 -76
- package/src/plugins/builtin/alerts/command.ts +5 -10
- package/src/plugins/builtin/alerts/event-rows.ts +9 -5
- package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
- package/src/plugins/builtin/alerts/events.ts +107 -5
- package/src/plugins/builtin/alerts/fired-log.ts +78 -0
- package/src/plugins/builtin/alerts/format.ts +2 -1
- package/src/plugins/builtin/alerts/headless.ts +100 -39
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +89 -0
- package/src/plugins/builtin/alerts/index.tsx +45 -18
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/pane.tsx +97 -29
- package/src/plugins/builtin/alerts/price.ts +35 -0
- package/src/plugins/builtin/alerts/quotes.ts +24 -0
- package/src/plugins/builtin/alerts/research-builder.ts +84 -11
- package/src/plugins/builtin/alerts/research-rules.ts +76 -3
- package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
- package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
- package/src/plugins/builtin/alerts/scheduled.ts +188 -29
- package/src/plugins/builtin/alerts/storage.ts +13 -10
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/analytics/index.tsx +8 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -1
- package/src/plugins/builtin/analytics/risk-book.ts +215 -0
- package/src/plugins/builtin/analytics/risk-client.ts +13 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
- package/src/plugins/builtin/analytics/risk-math.ts +6 -3
- package/src/plugins/builtin/analytics/risk-model.ts +297 -104
- package/src/plugins/builtin/analytics/risk-options.ts +17 -2
- package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
- package/src/plugins/builtin/attention/pane.tsx +6 -2
- package/src/plugins/builtin/awards/headless.ts +7 -3
- package/src/plugins/builtin/awards/model.ts +18 -0
- package/src/plugins/builtin/awards/pane.tsx +6 -3
- package/src/plugins/builtin/backtest/headless.ts +3 -3
- package/src/plugins/builtin/backtest/rules.ts +41 -5
- package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
- package/src/plugins/builtin/broker-manager/footer.ts +27 -11
- package/src/plugins/builtin/broker-manager/index.tsx +21 -12
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +12 -2
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
- package/src/plugins/builtin/broker-manager/table.ts +28 -12
- package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
- package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
- package/src/plugins/builtin/broker-trading/controller.ts +45 -13
- package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
- package/src/plugins/builtin/broker-trading/headless.ts +2 -1
- package/src/plugins/builtin/broker-trading/index.tsx +7 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
- package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
- package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
- package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
- package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
- package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
- package/src/plugins/builtin/buildout/cells.tsx +4 -0
- package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
- package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
- package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
- package/src/plugins/builtin/catalysts/detail.tsx +3 -2
- package/src/plugins/builtin/catalysts/index.tsx +1 -1
- package/src/plugins/builtin/catalysts/model.ts +49 -4
- package/src/plugins/builtin/catalysts/pane.tsx +6 -8
- package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
- package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
- package/src/plugins/builtin/changelog/index.tsx +1 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
- package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
- package/src/plugins/builtin/chart-composer/headless.ts +253 -35
- package/src/plugins/builtin/chart-composer/index.tsx +12 -2
- package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
- package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
- package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
- package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
- package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
- package/src/plugins/builtin/chart-composer/settings.ts +31 -19
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
- package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
- package/src/plugins/builtin/chat/attachments/model.ts +3 -1
- package/src/plugins/builtin/chat/content/index.tsx +1 -1
- package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/clinical-trials/client.ts +5 -2
- package/src/plugins/builtin/clinical-trials/model.ts +13 -4
- package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
- package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
- package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
- package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
- package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
- package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
- package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
- package/src/plugins/builtin/company-attention/building-model.ts +222 -0
- package/src/plugins/builtin/company-attention/building.tsx +172 -0
- package/src/plugins/builtin/company-attention/client.ts +117 -1
- package/src/plugins/builtin/company-attention/evidence.ts +16 -3
- package/src/plugins/builtin/company-attention/headless.ts +101 -5
- package/src/plugins/builtin/company-attention/index.tsx +6 -3
- package/src/plugins/builtin/company-attention/model.ts +33 -7
- package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
- package/src/plugins/builtin/company-attention/moves.tsx +159 -0
- package/src/plugins/builtin/company-attention/pane.tsx +79 -23
- package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
- package/src/plugins/builtin/company-attention/signals.tsx +160 -0
- package/src/plugins/builtin/company-kpis/headless.ts +3 -2
- package/src/plugins/builtin/company-kpis/model.ts +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/congress-trades/headless.ts +1 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
- package/src/plugins/builtin/correlation/headless.ts +5 -3
- package/src/plugins/builtin/cot/headless.ts +28 -6
- package/src/plugins/builtin/cot/model.ts +12 -0
- package/src/plugins/builtin/cot/pane.tsx +3 -2
- package/src/plugins/builtin/cpi/headless.ts +5 -1
- package/src/plugins/builtin/cpi/pane.tsx +11 -9
- package/src/plugins/builtin/credit-boards/model.ts +2 -0
- package/src/plugins/builtin/credit-documents/headless.ts +1 -1
- package/src/plugins/builtin/crypto-board/client.ts +86 -0
- package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
- package/src/plugins/builtin/crypto-board/defi.ts +239 -0
- package/src/plugins/builtin/crypto-board/headless.ts +117 -6
- package/src/plugins/builtin/crypto-board/index.tsx +8 -3
- package/src/plugins/builtin/crypto-board/model.ts +40 -8
- package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
- package/src/plugins/builtin/crypto-board/series.ts +19 -1
- package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
- package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
- package/src/plugins/builtin/debug/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/docs/index.tsx +1 -1
- package/src/plugins/builtin/docs/pane.tsx +2 -2
- package/src/plugins/builtin/docs/targets.ts +1 -1
- package/src/plugins/builtin/doe/client.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +12 -6
- package/src/plugins/builtin/doe/model.ts +43 -6
- package/src/plugins/builtin/doe/pane.tsx +18 -11
- package/src/plugins/builtin/earnings/board-model.ts +13 -6
- package/src/plugins/builtin/earnings/board-table.ts +4 -1
- package/src/plugins/builtin/earnings/board.tsx +253 -50
- package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
- package/src/plugins/builtin/earnings/calendar.tsx +338 -0
- package/src/plugins/builtin/earnings/client.ts +4 -3
- package/src/plugins/builtin/earnings/headless.ts +53 -15
- package/src/plugins/builtin/earnings/history-model.ts +53 -36
- package/src/plugins/builtin/earnings/index.tsx +15 -22
- package/src/plugins/builtin/earnings/model.ts +17 -12
- package/src/plugins/builtin/earnings/scope.ts +89 -0
- package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
- package/src/plugins/builtin/earnings-calls/data.ts +146 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
- package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
- package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
- package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +132 -20
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
- package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
- package/src/plugins/builtin/econ/index.tsx +112 -25
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +32 -1
- package/src/plugins/builtin/econ-statistics/client.ts +16 -3
- package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
- package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
- package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
- package/src/plugins/builtin/econ-statistics/view.ts +16 -1
- package/src/plugins/builtin/equity-screener/client.ts +21 -3
- package/src/plugins/builtin/equity-screener/headless.ts +68 -6
- package/src/plugins/builtin/equity-screener/live.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +6 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
- package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
- package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
- package/src/plugins/builtin/exposure/headless.ts +51 -6
- package/src/plugins/builtin/exposure/index.tsx +28 -2
- package/src/plugins/builtin/exposure/model.ts +7 -0
- package/src/plugins/builtin/exposure/pane.tsx +6 -1
- package/src/plugins/builtin/fear-greed/index.tsx +1 -1
- package/src/plugins/builtin/filing-events/index.tsx +1 -1
- package/src/plugins/builtin/futures/model.ts +10 -9
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +40 -9
- package/src/plugins/builtin/futures-curve/headless.ts +39 -17
- package/src/plugins/builtin/futures-curve/model.ts +258 -69
- package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
- package/src/plugins/builtin/fx-matrix/client.ts +15 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
- package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
- package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
- package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
- package/src/plugins/builtin/gpu/headless.ts +9 -4
- package/src/plugins/builtin/help/index.tsx +13 -3
- package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
- package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +13 -1
- package/src/plugins/builtin/holders/format.ts +28 -17
- package/src/plugins/builtin/holders/headless.ts +46 -7
- package/src/plugins/builtin/holders/index-holders.ts +264 -0
- package/src/plugins/builtin/holders/pane.tsx +79 -15
- package/src/plugins/builtin/holders/report-rows.ts +35 -7
- package/src/plugins/builtin/holders/table-model.ts +2 -1
- package/src/plugins/builtin/holders/treemap.tsx +2 -2
- package/src/plugins/builtin/insider/headless.ts +1 -1
- package/src/plugins/builtin/insider/signals-client.ts +1 -1
- package/src/plugins/builtin/insider/signals.tsx +1 -1
- package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
- package/src/plugins/builtin/iv-history/client.ts +20 -0
- package/src/plugins/builtin/iv-history/headless.ts +4 -4
- package/src/plugins/builtin/iv-history/index.tsx +1 -1
- package/src/plugins/builtin/iv-history/model.ts +13 -4
- package/src/plugins/builtin/iv-history/series.ts +3 -1
- package/src/plugins/builtin/jobs/lock-note.ts +1 -1
- package/src/plugins/builtin/jobs/model.ts +4 -15
- package/src/plugins/builtin/jobs/pane.tsx +7 -5
- package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
- package/src/plugins/builtin/jodi/pane.tsx +10 -4
- package/src/plugins/builtin/layout-manager/index.tsx +1 -0
- package/src/plugins/builtin/macro-day/headless.ts +20 -7
- package/src/plugins/builtin/macro-day/index.tsx +3 -2
- package/src/plugins/builtin/macro-day/model.ts +29 -8
- package/src/plugins/builtin/macro-day/pane.tsx +46 -10
- package/src/plugins/builtin/macro-day/releases.ts +55 -13
- package/src/plugins/builtin/market-heatmap/data.ts +9 -2
- package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
- package/src/plugins/builtin/market-heatmap/model.ts +10 -7
- package/src/plugins/builtin/market-movers/client.ts +33 -19
- package/src/plugins/builtin/market-movers/headless.ts +8 -2
- package/src/plugins/builtin/market-movers/index.tsx +16 -10
- package/src/plugins/builtin/market-movers/screener.ts +29 -8
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
- package/src/plugins/builtin/market-movers/session-table.ts +4 -2
- package/src/plugins/builtin/market-movers/table.ts +4 -1
- package/src/plugins/builtin/market-valuation/headless.ts +2 -0
- package/src/plugins/builtin/market-valuation/view.ts +5 -0
- package/src/plugins/builtin/members/headless.ts +6 -5
- package/src/plugins/builtin/members/pane.tsx +11 -4
- package/src/plugins/builtin/mna/client.ts +1 -0
- package/src/plugins/builtin/mna/detail.tsx +20 -32
- package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
- package/src/plugins/builtin/mna/distress/model.ts +3 -2
- package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
- package/src/plugins/builtin/mna/headless.ts +6 -13
- package/src/plugins/builtin/mna/model.ts +93 -15
- package/src/plugins/builtin/mna/pane.tsx +5 -4
- package/src/plugins/builtin/news/headless.ts +12 -6
- package/src/plugins/builtin/news/index.tsx +10 -1
- package/src/plugins/builtin/news/wire/index.tsx +3 -2
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
- package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
- package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
- package/src/plugins/builtin/notes/cli.ts +49 -14
- package/src/plugins/builtin/openfda/pane.tsx +1 -1
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/view.tsx +10 -10
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-scenario/client.ts +123 -28
- package/src/plugins/builtin/options-scenario/collar.ts +8 -6
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
- package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
- package/src/plugins/builtin/options-scenario/csp.ts +101 -21
- package/src/plugins/builtin/options-scenario/headless.ts +36 -15
- package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
- package/src/plugins/builtin/options-scenario/index.tsx +28 -3
- package/src/plugins/builtin/options-scenario/model.ts +13 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
- package/src/plugins/builtin/perps/equity-row.tsx +11 -3
- package/src/plugins/builtin/perps/freshness.ts +8 -5
- package/src/plugins/builtin/perps/headless.ts +3 -1
- package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
- package/src/plugins/builtin/perps/live-rows.ts +161 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +36 -17
- package/src/plugins/builtin/perps/venue-markets.ts +96 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
- package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
- package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
- package/src/plugins/builtin/portfolio-list/income.ts +13 -2
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
- package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
- package/src/plugins/builtin/power/client.ts +4 -1
- package/src/plugins/builtin/power/evidence.ts +22 -7
- package/src/plugins/builtin/power/gas.tsx +5 -1
- package/src/plugins/builtin/power/pane.tsx +8 -1
- package/src/plugins/builtin/power/prices.tsx +10 -3
- package/src/plugins/builtin/research/analyst-headless.ts +25 -8
- package/src/plugins/builtin/research/analyst-model.ts +68 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
- package/src/plugins/builtin/research/event-model.ts +3 -1
- package/src/plugins/builtin/research/event-sources.ts +12 -4
- package/src/plugins/builtin/research/index.tsx +2 -1
- package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
- package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
- package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
- package/src/plugins/builtin/risk-factors/index.tsx +2 -2
- package/src/plugins/builtin/scanner/lock-note.ts +1 -1
- package/src/plugins/builtin/sec/forms.ts +13 -0
- package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
- package/src/plugins/builtin/sec/fund-facts.ts +68 -8
- package/src/plugins/builtin/sec/headless.ts +85 -8
- package/src/plugins/builtin/sec/index.tsx +58 -12
- package/src/plugins/builtin/sectors/client.ts +2 -1
- package/src/plugins/builtin/sectors/sector-model.ts +11 -15
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
- package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
- package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
- package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
- package/src/plugins/builtin/shared/earnings-report.ts +3 -3
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
- package/src/plugins/builtin/shared/job-posting.ts +129 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
- package/src/plugins/builtin/shared/report-freshness.ts +3 -0
- package/src/plugins/builtin/shared/research-trading.tsx +21 -0
- package/src/plugins/builtin/shared/series-cache.ts +2 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
- package/src/plugins/builtin/short-interest/client.ts +2 -0
- package/src/plugins/builtin/short-interest/headless.ts +41 -12
- package/src/plugins/builtin/short-interest/index.tsx +23 -1
- package/src/plugins/builtin/short-interest/model.ts +70 -19
- package/src/plugins/builtin/short-interest/pane.tsx +19 -2
- package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
- package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
- package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
- package/src/plugins/builtin/short-interest/schedule.ts +35 -2
- package/src/plugins/builtin/short-interest/surface.tsx +11 -4
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
- package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
- package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
- package/src/plugins/builtin/supply-chain/model.ts +51 -3
- package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
- package/src/plugins/builtin/themes/headless.ts +8 -5
- package/src/plugins/builtin/themes/model.ts +14 -4
- package/src/plugins/builtin/themes/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/headless.ts +5 -5
- package/src/plugins/builtin/thirteenf/signals.ts +3 -1
- package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
- package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
- package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
- package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
- package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
- package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/client.ts +5 -4
- package/src/plugins/builtin/vol-surface/headless.ts +12 -3
- package/src/plugins/builtin/vol-surface/model.ts +4 -2
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
- package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
- package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
- package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
- package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +3 -26
- package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
- package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
- package/src/plugins/builtin/yield-curve/client.ts +25 -0
- package/src/plugins/builtin/yield-curve/curves.ts +7 -4
- package/src/plugins/builtin/yield-curve/headless.ts +38 -4
- package/src/plugins/builtin/yield-curve/history.ts +4 -3
- package/src/plugins/builtin/yield-curve/index.tsx +12 -6
- package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
- package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
- package/src/plugins/bundle.ts +1 -1
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/compat.ts +3 -3
- package/src/plugins/dependencies.ts +75 -14
- package/src/plugins/extracted-plugins.ts +132 -34
- package/src/plugins/host-link.ts +149 -34
- package/src/plugins/installer.ts +35 -10
- package/src/plugins/loader.ts +206 -38
- package/src/plugins/manager-node.ts +2 -2
- package/src/plugins/marketplace/deactivation.ts +22 -0
- package/src/plugins/marketplace/model.ts +2 -2
- package/src/plugins/marketplace/packs.ts +1 -1
- package/src/plugins/ownership.ts +1 -1
- package/src/plugins/peer-retry.ts +155 -0
- package/src/plugins/registry/contributions.ts +3 -1
- package/src/plugins/registry/index.ts +4 -3
- package/src/plugins/registry/plugin-state.ts +3 -0
- package/src/plugins/seed-notice.ts +20 -0
- package/src/plugins/shortcut-argument.ts +39 -0
- package/src/portfolio/allocation.ts +1 -1
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/column-values.ts +10 -8
- package/src/portfolio/cost-fx.ts +296 -19
- package/src/portfolio/manual-lots.ts +75 -0
- package/src/portfolio/mutations.ts +60 -22
- package/src/portfolio/suffixed-listings.ts +209 -0
- package/src/portfolio/totals-currency.ts +24 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/config-host.ts +3 -0
- package/src/renderers/browser/main.tsx +23 -3
- package/src/renderers/dom/boot-frame-page.ts +187 -0
- package/src/renderers/dom/boot-frame.ts +237 -0
- package/src/renderers/dom/build-assets.ts +46 -8
- package/src/renderers/dom/dom-ui-host.tsx +4 -29
- package/src/renderers/dom/host/ascii-text.ts +16 -2
- package/src/renderers/dom/host/box.tsx +43 -2
- package/src/renderers/dom/host/chart-surface.tsx +2 -0
- package/src/renderers/dom/host/hls.chunk.ts +3 -0
- package/src/renderers/dom/host/image-surface.tsx +49 -0
- package/src/renderers/dom/host/media-surface.tsx +24 -18
- package/src/renderers/dom/host/mouse.ts +30 -0
- package/src/renderers/dom/host/size-observer.ts +1 -1
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/styles.css +76 -0
- package/src/renderers/dom/test-utils.tsx +2 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
- package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +11 -1
- package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -1
- package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
- package/src/renderers/electrobun/shared/protocol.ts +6 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
- package/src/renderers/electrobun/view/main.tsx +30 -4
- package/src/renderers/electrobun/view/news-source.ts +5 -5
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/browser-open.ts +88 -0
- package/src/renderers/opentui/host.tsx +5 -14
- package/src/renderers/opentui/image/loader.ts +3 -2
- package/src/renderers/opentui/native-smoke.ts +10 -1
- package/src/renderers/opentui/pane-image/index.ts +2 -1
- package/src/renderers/opentui/render-lib.ts +253 -0
- package/src/renderers/opentui/render-timing-smoke.ts +1 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/share/layout-view.tsx +2 -2
- package/src/renderers/share/main.tsx +4 -4
- package/src/renderers/share/view.tsx +1 -1
- package/src/series/resolve.ts +22 -6
- package/src/series/standalone.ts +30 -2
- package/src/series/types.ts +8 -1
- package/src/shares/api.ts +4 -1
- package/src/shares/pane.ts +1 -1
- package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
- package/src/sources/gloomberb-cloud/index.ts +32 -16
- package/src/sources/gloomberb-cloud/news.ts +39 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
- package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
- package/src/sources/listing-symbols.ts +37 -0
- package/src/sources/provider-errors.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +21 -3
- package/src/sources/provider-router/financials.ts +15 -2
- package/src/sources/provider-router/history.ts +30 -8
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/controller.ts +1 -1
- package/src/sync/core-contributors.ts +46 -14
- package/src/team/collections-sync.ts +2 -1
- package/src/team/collections.ts +178 -17
- package/src/telemetry/crash-reports.ts +17 -4
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +3 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +76 -27
- package/src/tickers/search/ranking.ts +54 -4
- package/src/tickers/search/upsert.ts +29 -0
- package/src/tickers/terminal-codes.ts +416 -40
- package/src/time-series/bar-aggregation.ts +243 -0
- package/src/time-series/base-series.ts +307 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/fundamentals.ts +18 -3
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/leverage.ts +100 -0
- package/src/time-series/market-resolution.ts +4 -2
- package/src/time-series/market.ts +2 -1
- package/src/time-series/price-comparison.ts +175 -30
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +157 -0
- package/src/time-series/resolve.ts +126 -1577
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +208 -0
- package/src/time-series/spec.ts +11 -6
- package/src/time-series/studies.ts +109 -12
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/types.ts +29 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +14 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +104 -7
- package/src/types/headless.ts +76 -6
- package/src/types/instrument.ts +3 -0
- package/src/types/plugin.ts +65 -8
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker.ts +13 -0
- package/src/types/trading.ts +59 -1
- package/src/ui/ascii-font.ts +13 -1
- package/src/ui/host.tsx +6 -1
- package/src/updater/command-names.ts +175 -0
- package/src/updater/index.ts +10 -5
- package/src/utils/calendar-date.ts +32 -15
- package/src/utils/cash-runway.ts +241 -0
- package/src/utils/central-banks.ts +42 -0
- package/src/utils/cli-output.ts +259 -80
- package/src/utils/contract-month.ts +16 -0
- package/src/utils/crypto-pair.ts +61 -3
- package/src/utils/currency-units.ts +29 -0
- package/src/utils/datetime-format.ts +10 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/decimal-comma.ts +39 -6
- package/src/utils/exchange-announcements.ts +71 -0
- package/src/utils/exchange-rate-snapshot.ts +10 -0
- package/src/utils/financial-statements.ts +83 -4
- package/src/utils/format.ts +34 -16
- package/src/utils/fundamentals.ts +150 -34
- package/src/utils/fx-market-hours.ts +10 -1
- package/src/utils/latest-financial-period.ts +8 -0
- package/src/utils/linux-libc.ts +54 -0
- package/src/utils/market-capitalization.ts +111 -15
- package/src/utils/max-drawdown.ts +64 -0
- package/src/utils/network-failure.ts +83 -0
- package/src/utils/option-expiry.ts +139 -7
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +18 -8
- package/src/utils/price-window.ts +131 -0
- package/src/utils/release-date.ts +47 -0
- package/src/utils/sec.ts +3 -0
- package/src/utils/throttled-fetch.ts +11 -4
- package/src/utils/trailing-eps.ts +67 -0
- package/src/utils/typed-text.ts +21 -0
- package/src/utils/utc-time.ts +24 -1
- package/src/version.ts +6 -0
- package/src/cli/pane-functions/screenshot.ts +0 -2122
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Box } from "../../../ui";
|
|
2
|
-
import { Button, PaneStatusBody, PaneFooterScope, usePaneTabs } from "../../../components";
|
|
1
|
+
import { Box, type ScrollBoxRenderable } from "../../../ui";
|
|
2
|
+
import { Button, DetailScrollBody, KeyValueRow, Notice, PaneStatusBody, PaneFooterScope, usePaneTabs } from "../../../components";
|
|
3
3
|
import { usePluginPaneState } from "../../runtime";
|
|
4
4
|
import { EventAlertsPane } from "./events-pane";
|
|
5
5
|
import { AlertHistoryPane } from "./history-pane";
|
|
6
|
-
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore, type RefObject } from "react";
|
|
7
7
|
import {
|
|
8
8
|
confirmDialog,
|
|
9
|
-
|
|
9
|
+
DataTableStackView,
|
|
10
10
|
usePaneFooter,
|
|
11
11
|
type DataTableCell,
|
|
12
12
|
type DataTableColumn,
|
|
@@ -73,6 +73,39 @@ const ALERT_TABLE_CONTENT_WIDTH = ALERT_COLUMNS.reduce(
|
|
|
73
73
|
2,
|
|
74
74
|
);
|
|
75
75
|
|
|
76
|
+
const DETAIL_ROW = { labelWidth: 10, emphasis: false } as const;
|
|
77
|
+
|
|
78
|
+
/** "AAPL above 400.00": the rule, which titles its detail. */
|
|
79
|
+
function alertTitle(alert: AlertRule): string {
|
|
80
|
+
return `${alert.symbol} ${conditionLabel(alert.condition).toLowerCase()} ${formatAlertTargetPrice(alert)}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* One price alert in full: why it has no quote, when there is a reason, then
|
|
85
|
+
* the figures its row shortens. The title names the rule.
|
|
86
|
+
*/
|
|
87
|
+
function AlertDetail({ alert, scrollRef }: { alert: AlertRule; scrollRef: RefObject<ScrollBoxRenderable | null> }) {
|
|
88
|
+
const reason = alert.lastCheckError
|
|
89
|
+
? alert.lastCheckError.includes(alert.symbol) ? alert.lastCheckError : `${alert.symbol}: ${alert.lastCheckError}`
|
|
90
|
+
: null;
|
|
91
|
+
return (
|
|
92
|
+
<DetailScrollBody ref={scrollRef} resetScrollKey={alert.id}>
|
|
93
|
+
<Box flexDirection="column">
|
|
94
|
+
{reason ? <Box paddingBottom={1}><Notice tone="warning">{reason}</Notice></Box> : null}
|
|
95
|
+
<KeyValueRow {...DETAIL_ROW} label="State" value={alert.status === "triggered" ? "Triggered" : "Active"} tone={alert.status === "triggered" ? "positive" : "muted"} />
|
|
96
|
+
{/* With no quote the reason above says why; the price and its distance wait for one. */}
|
|
97
|
+
{reason ? null : <KeyValueRow {...DETAIL_ROW} label="Current" value={formatCurrentPrice(alert, 24)} />}
|
|
98
|
+
{reason ? null : <KeyValueRow {...DETAIL_ROW} label="Away" value={formatAlertDistance(alert)} />}
|
|
99
|
+
{reason
|
|
100
|
+
? alert.lastCheckedAt ? <KeyValueRow {...DETAIL_ROW} label="Checked" value={relativeTime(alert.lastCheckedAt)} tone="muted" /> : null
|
|
101
|
+
: <KeyValueRow {...DETAIL_ROW} label="Quote" value={formatQuoteChecked(alert)} tone="muted" />}
|
|
102
|
+
{alert.triggeredAt ? <KeyValueRow {...DETAIL_ROW} label="Alerted" value={relativeTime(alert.triggeredAt)} /> : null}
|
|
103
|
+
<KeyValueRow {...DETAIL_ROW} label="Armed" value={relativeTime(alert.armedAt ?? alert.createdAt)} tone="muted" />
|
|
104
|
+
</Box>
|
|
105
|
+
</DetailScrollBody>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
76
109
|
export function AlertsPane(props: PaneProps) {
|
|
77
110
|
const [tab, setTab] = usePluginPaneState<string>("tab", "prices");
|
|
78
111
|
// A missed price alert opened from the Unread list selects its rule on Prices.
|
|
@@ -107,14 +140,18 @@ export function AlertsPane(props: PaneProps) {
|
|
|
107
140
|
}
|
|
108
141
|
|
|
109
142
|
function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
110
|
-
const [alertsJson, setAlertsJson] = usePluginConfigState<
|
|
111
|
-
const { openPluginCommandWorkflow } = usePluginAppActions();
|
|
143
|
+
const [alertsJson, setAlertsJson] = usePluginConfigState<unknown>(ALERTS_KEY, "[]");
|
|
144
|
+
const { openPluginCommandWorkflow, notify } = usePluginAppActions();
|
|
112
145
|
const dialog = useDialog();
|
|
113
146
|
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
147
|
+
const [detailOpen, setDetailOpen] = useState(false);
|
|
148
|
+
// The alert the open detail shows, by id, so it never becomes whichever alert takes its row.
|
|
149
|
+
const detailAlertId = useRef<string | null>(null);
|
|
150
|
+
const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
114
151
|
const showHorizontalScrollbar = ALERT_TABLE_CONTENT_WIDTH > width;
|
|
115
152
|
const storeError = useMemo(() => readAlertsStoreError(alertsJson), [alertsJson]);
|
|
116
153
|
|
|
117
|
-
const { alerts, rows
|
|
154
|
+
const { alerts, rows } = useMemo(() => {
|
|
118
155
|
const parsed = deserializeAlerts(alertsJson);
|
|
119
156
|
const activeAlerts = parsed.filter((a) => a.status === "active");
|
|
120
157
|
const triggeredAlerts = parsed
|
|
@@ -124,7 +161,6 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
124
161
|
return {
|
|
125
162
|
alerts: parsed,
|
|
126
163
|
rows: [...activeAlerts, ...triggeredAlerts],
|
|
127
|
-
quoteError: parsed.find((alert) => alert.lastCheckError)?.lastCheckError ?? null,
|
|
128
164
|
};
|
|
129
165
|
}, [alertsJson]);
|
|
130
166
|
|
|
@@ -158,12 +194,16 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
158
194
|
return quote ? { ...alert, ...quoteAlertFields(quote, quote.receivedAt) } : alert;
|
|
159
195
|
}), [liveEntries, rows]);
|
|
160
196
|
|
|
161
|
-
const savePaneAlerts = useCallback((next:
|
|
162
|
-
|
|
197
|
+
const savePaneAlerts = useCallback((next: (current: AlertRule[]) => AlertRule[]): string | null => {
|
|
198
|
+
let error: string | null = null;
|
|
199
|
+
setAlertsJson((currentJson: unknown) => {
|
|
200
|
+
error = readAlertsStoreError(currentJson);
|
|
201
|
+
// The config hook skips persistence when the updater returns the unchanged value.
|
|
202
|
+
if (error) return currentJson;
|
|
163
203
|
const current = deserializeAlerts(currentJson);
|
|
164
|
-
|
|
165
|
-
return serializeAlerts(resolved);
|
|
204
|
+
return serializeAlerts(next(current));
|
|
166
205
|
});
|
|
206
|
+
return error;
|
|
167
207
|
}, [setAlertsJson]);
|
|
168
208
|
|
|
169
209
|
const deleteAlert = useCallback(async (id: string) => {
|
|
@@ -175,15 +215,20 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
175
215
|
confirmLabel: "Delete",
|
|
176
216
|
});
|
|
177
217
|
if (!confirmed) return;
|
|
178
|
-
savePaneAlerts(
|
|
218
|
+
const error = savePaneAlerts((current) => current.filter((a) => a.id !== id));
|
|
219
|
+
if (error) {
|
|
220
|
+
notify({ body: error, type: "error" });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
179
223
|
setSelectedIdx((prev) => Math.max(0, Math.min(prev, rows.length - 2)));
|
|
180
|
-
}, [alerts, dialog, rows.length, savePaneAlerts]);
|
|
224
|
+
}, [alerts, dialog, notify, rows.length, savePaneAlerts]);
|
|
181
225
|
|
|
182
226
|
const rearmAlert = useCallback((id: string) => {
|
|
183
|
-
savePaneAlerts(
|
|
184
|
-
|
|
227
|
+
const error = savePaneAlerts(
|
|
228
|
+
(current) => current.map((a) => (a.id === id ? rebuildAlert(a) : a)),
|
|
185
229
|
);
|
|
186
|
-
|
|
230
|
+
if (error) notify({ body: error, type: "error" });
|
|
231
|
+
}, [notify, savePaneAlerts]);
|
|
187
232
|
|
|
188
233
|
const startAddAlert = useCallback(() => {
|
|
189
234
|
openPluginCommandWorkflow("set-alert");
|
|
@@ -195,6 +240,9 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
195
240
|
}, [deleteAlert, rows, selectedIdx]);
|
|
196
241
|
|
|
197
242
|
const selectedRule = rows[selectedIdx];
|
|
243
|
+
// A quote one alert could not get is said on its row (Current and Quote),
|
|
244
|
+
// and in full in its detail.
|
|
245
|
+
const selectedShown = displayRows[selectedIdx];
|
|
198
246
|
const selectedTarget = useMemo(() => selectedRule ? alertTarget(priceRuleSubject(selectedRule)) : null, [selectedRule]);
|
|
199
247
|
const openTarget = useOpenAlertTarget();
|
|
200
248
|
|
|
@@ -222,11 +270,12 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
222
270
|
onApply={async (value) => {
|
|
223
271
|
const parsed = parseAlertCommandValues({ shortcut: value });
|
|
224
272
|
if (!parsed) throw new Error("Use SYMBOL above|below|crosses PRICE.");
|
|
225
|
-
savePaneAlerts((current) => current.map((alert) => (
|
|
273
|
+
const error = savePaneAlerts((current) => current.map((alert) => (
|
|
226
274
|
alert.id === selected.id
|
|
227
275
|
? editAlert(alert, parsed.symbol, parsed.condition, parsed.price)
|
|
228
276
|
: alert
|
|
229
277
|
)));
|
|
278
|
+
if (error) throw new Error(error);
|
|
230
279
|
}}
|
|
231
280
|
/>
|
|
232
281
|
),
|
|
@@ -236,12 +285,11 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
236
285
|
// Quotes come from the plugin's single background poll, which writes into the
|
|
237
286
|
// same persisted store, so the pane never fetches on its own.
|
|
238
287
|
|
|
288
|
+
// A quote one alert could not get shows on that alert's row (Current and Quote), not here.
|
|
239
289
|
usePaneFooter("alerts", () => ({
|
|
240
290
|
info: storeError
|
|
241
291
|
? [{ id: "store-error", parts: [{ text: storeError, tone: "warning" as const }] }]
|
|
242
|
-
:
|
|
243
|
-
? [{ id: "quote-error", parts: [{ text: quoteError, tone: "warning" as const }] }]
|
|
244
|
-
: [],
|
|
292
|
+
: [],
|
|
245
293
|
hints: [
|
|
246
294
|
{ id: "add", key: "a", label: "dd alert", onPress: startAddAlert },
|
|
247
295
|
...alertViewHints(selectedTarget, openTarget),
|
|
@@ -271,7 +319,6 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
271
319
|
openTarget,
|
|
272
320
|
rows,
|
|
273
321
|
selectedIdx,
|
|
274
|
-
quoteError,
|
|
275
322
|
rows.length,
|
|
276
323
|
startAddAlert,
|
|
277
324
|
storeError,
|
|
@@ -281,6 +328,20 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
281
328
|
setSelectedIdx((prev) => (rows.length === 0 ? 0 : Math.min(prev, rows.length - 1)));
|
|
282
329
|
}, [rows.length]);
|
|
283
330
|
|
|
331
|
+
// While the detail is open, a change to the list keeps it on its alert: it follows the alert to its new row (re-armed
|
|
332
|
+
// or triggered, it changes group) and goes back to the list once the alert is deleted, from here or elsewhere.
|
|
333
|
+
// Declared before the effect below, so a delete is checked against the alert shown, not the row that takes its place.
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
if (!detailOpen) return;
|
|
336
|
+
const index = rows.findIndex((row) => row.id === detailAlertId.current);
|
|
337
|
+
if (index < 0) setDetailOpen(false);
|
|
338
|
+
else setSelectedIdx(index);
|
|
339
|
+
}, [detailOpen, rows]);
|
|
340
|
+
// A new selection while it is open (an alert focused from Unread) is the alert it shows from then on.
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
if (detailOpen) detailAlertId.current = rows[selectedIdx]?.id ?? null;
|
|
343
|
+
}, [detailOpen, selectedIdx]);
|
|
344
|
+
|
|
284
345
|
// The footer hints bind a, e, d and m; `n` is a second key for adding.
|
|
285
346
|
const handleTableKeyDown = useCallback((event: DataTableKeyEvent) => {
|
|
286
347
|
if (!isPlainKey(event, "n")) return false;
|
|
@@ -322,7 +383,7 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
322
383
|
case "quote":
|
|
323
384
|
return {
|
|
324
385
|
text: formatQuoteChecked(alert),
|
|
325
|
-
color: colors.textDim,
|
|
386
|
+
color: alert.lastCheckError ? colors.negative : colors.textDim,
|
|
326
387
|
};
|
|
327
388
|
case "triggered":
|
|
328
389
|
return {
|
|
@@ -333,8 +394,13 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
333
394
|
}, []);
|
|
334
395
|
|
|
335
396
|
return (
|
|
336
|
-
<
|
|
397
|
+
<DataTableStackView<AlertRule, AlertColumn>
|
|
337
398
|
focused={focused}
|
|
399
|
+
detailOpen={detailOpen && !!selectedShown}
|
|
400
|
+
onBack={() => setDetailOpen(false)}
|
|
401
|
+
detailTitle={selectedShown ? alertTitle(selectedShown) : undefined}
|
|
402
|
+
detailContent={selectedShown ? <AlertDetail alert={selectedShown} scrollRef={detailScrollRef} /> : null}
|
|
403
|
+
detailScrollRef={detailScrollRef}
|
|
338
404
|
selection={{
|
|
339
405
|
kind: "index",
|
|
340
406
|
selectedIndex: selectedIdx,
|
|
@@ -349,16 +415,18 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
349
415
|
sortColumnId={null}
|
|
350
416
|
sortDirection="asc"
|
|
351
417
|
getItemKey={(alert) => alert.id}
|
|
418
|
+
// Enter opens the alert; `m` re-arms a triggered one.
|
|
352
419
|
onActivate={(alert) => {
|
|
353
|
-
|
|
420
|
+
detailAlertId.current = alert.id;
|
|
421
|
+
setDetailOpen(true);
|
|
354
422
|
}}
|
|
355
423
|
renderCell={renderCell}
|
|
356
424
|
selectedTextOverridesCellColor
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
emptyContent={
|
|
425
|
+
status={displayRows.length === 0 && storeError ? { error: storeError, subject: "price alerts" } : undefined}
|
|
426
|
+
emptyStateTitle="No alerts"
|
|
427
|
+
emptyContent={
|
|
360
428
|
<PaneStatusBody empty emptyTitle="No alerts" actions={<Button label="Add alert" compact onPress={startAddAlert} />} />
|
|
361
|
-
|
|
429
|
+
}
|
|
362
430
|
showHorizontalScrollbar={showHorizontalScrollbar}
|
|
363
431
|
/>
|
|
364
432
|
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ambiguousNumberMessage, parsePastedNumber, type PastedNumber } from "../../../utils/decimal-comma";
|
|
2
|
+
|
|
3
|
+
const PRICE_SCALES = { k: 1e3, m: 1e6, b: 1e9 } as const;
|
|
4
|
+
/** A price typed with a scale after it: 81k, 1.5m. */
|
|
5
|
+
const SCALED_PRICE = /^([+-]?\d[\d.,]*)\s*([kmb])$/i;
|
|
6
|
+
|
|
7
|
+
/** What to type for an alert price, as the error for one that reads as none names it. */
|
|
8
|
+
const PRICE_FORMAT = "type 81000 or 81k";
|
|
9
|
+
|
|
10
|
+
const scaled = (value: number, scale: number) => Number((value * scale).toPrecision(15));
|
|
11
|
+
|
|
12
|
+
/** A price as `parsePastedNumber` reads it, and `81k` is 81000: the scale applies to either reading of a comma and three digits. */
|
|
13
|
+
export function parseAlertPrice(text: string): PastedNumber {
|
|
14
|
+
const match = SCALED_PRICE.exec(text.trim());
|
|
15
|
+
if (!match) return parsePastedNumber(text);
|
|
16
|
+
const scale = PRICE_SCALES[match[2]!.toLowerCase() as keyof typeof PRICE_SCALES];
|
|
17
|
+
const base = parsePastedNumber(match[1]);
|
|
18
|
+
if (base.kind === "number") return { kind: "number", value: scaled(base.value, scale) };
|
|
19
|
+
if (base.kind === "ambiguous") return { kind: "ambiguous", thousands: scaled(base.thousands, scale), decimal: scaled(base.decimal, scale) };
|
|
20
|
+
return base;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* An alert price as every way of adding an alert reads it (`parseAlertPrice`):
|
|
25
|
+
* the number, or an error that names what to type. A comma before exactly
|
|
26
|
+
* three digits (`81,000`) could be thousands or a decimal comma, so it throws
|
|
27
|
+
* naming both readings rather than storing either; text that is no number
|
|
28
|
+
* throws with an example.
|
|
29
|
+
*/
|
|
30
|
+
export function readAlertPrice(text: string): number {
|
|
31
|
+
const price = parseAlertPrice(text);
|
|
32
|
+
if (price.kind === "ambiguous") throw new Error(ambiguousNumberMessage(text, price));
|
|
33
|
+
if (price.kind === "invalid") throw new Error(`Alert price must be a number, got "${text.trim()}"; ${PRICE_FORMAT}.`);
|
|
34
|
+
return price.value;
|
|
35
|
+
}
|
|
@@ -66,3 +66,27 @@ export function quoteErrorAlertFields(
|
|
|
66
66
|
lastCheckError: error,
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
/** How many times the last price a target may be, either way, before it reads as a typo or another instrument's price. */
|
|
71
|
+
const MAX_TARGET_RATIO = 10;
|
|
72
|
+
|
|
73
|
+
/** A price as typed or quoted, never in exponent form: 85,000, 37.35, 0.0000001234. */
|
|
74
|
+
const priceText = (value: number) => value.toLocaleString("en-US", Math.abs(value) >= 1
|
|
75
|
+
? { maximumFractionDigits: 2 }
|
|
76
|
+
: { maximumSignificantDigits: 4 });
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Why a price target is refused: more than ten times the last price, or under
|
|
80
|
+
* a tenth of it, which is a slip or another instrument's price (bitcoin's,
|
|
81
|
+
* typed on the BTC fund). `coinNote` is the note `quote` gives a bare coin
|
|
82
|
+
* ticker on a listing that is not the coin, which says what to use instead.
|
|
83
|
+
* Null when the target is near enough, or there is no positive last price to
|
|
84
|
+
* measure it against.
|
|
85
|
+
*/
|
|
86
|
+
export function farTargetMessage(symbol: string, target: number, last: number | null | undefined, coinNote: string | null): string | null {
|
|
87
|
+
if (last == null || !Number.isFinite(last) || last <= 0) return null;
|
|
88
|
+
const above = target > last * MAX_TARGET_RATIO;
|
|
89
|
+
if (!above && target >= last / MAX_TARGET_RATIO) return null;
|
|
90
|
+
const how = above ? `more than ${MAX_TARGET_RATIO} times` : `under 1/${MAX_TARGET_RATIO} of`;
|
|
91
|
+
return `A target of ${priceText(target)} is ${how} ${symbol}'s last price of ${priceText(last)}. ${coinNote ?? "Check the price and the symbol."}`;
|
|
92
|
+
}
|
|
@@ -1,51 +1,102 @@
|
|
|
1
1
|
import type { GloomPlugin } from "../../../types/plugin";
|
|
2
|
-
import { DEFAULT_LEAD_DAYS, isResearchAlertKind, MACRO_ALERT_EVENTS, normalizeResearchRule, readResearchRule, RESEARCH_ALERT_FORMS, type MacroAlertEvent, type ResearchAlertKind } from "./research-rules";
|
|
2
|
+
import { DEFAULT_LEAD_DAYS, isResearchAlertKind, MACRO_ALERT_EVENTS, normalizeResearchRule, readResearchRule, RESEARCH_ALERT_FORMS, type IvLevelMetric, type MacroAlertEvent, type ResearchAlertKind } from "./research-rules";
|
|
3
3
|
import type { EventAlertRule } from "./events";
|
|
4
4
|
|
|
5
5
|
/** What a macro event is called in a rule, a list and a notification. */
|
|
6
6
|
export const MACRO_EVENT_NAMES: Record<MacroAlertEvent, string> = { cpi: "CPI release", fomc: "FOMC decision" };
|
|
7
|
+
/** CPI and FOMC and any other calendar release are one kind of alert to the reader, whichever list keeps the rule. */
|
|
7
8
|
export const RESEARCH_LABELS: Record<ResearchAlertKind, string> = {
|
|
8
|
-
perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", options_flow: "Options flow",
|
|
9
|
+
perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", calendar_event: "Macro event", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", iv_level: "IV level", options_flow: "Options flow", hiring_move: "Hiring move",
|
|
9
10
|
};
|
|
11
|
+
/** What an IV level rule follows, as the form, a list and a notification name it. */
|
|
12
|
+
export const IV_LEVEL_NAMES: Record<IvLevelMetric, string> = { atm: "30d ATM IV", rr: "30d 25D RR" };
|
|
13
|
+
/** An IV level in its unit: an ATM IV in percent, a risk reversal in signed vol points. */
|
|
14
|
+
export function formatIvLevel(metric: IvLevelMetric, value: number): string {
|
|
15
|
+
const rounded = Number(value.toFixed(2));
|
|
16
|
+
return metric === "atm" ? `${rounded}%` : `${rounded > 0 ? "+" : ""}${rounded} pts`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* An OCC contract that has not expired, for the IV spike form's example: an
|
|
20
|
+
* AAPL call on the monthly expiry (the third Friday) two months from `now`.
|
|
21
|
+
*/
|
|
22
|
+
function occContractExample(now = new Date()): string {
|
|
23
|
+
const month = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 2, 1));
|
|
24
|
+
const thirdFriday = 1 + ((5 - month.getUTCDay() + 7) % 7) + 14;
|
|
25
|
+
const two = (value: number) => String(value).padStart(2, "0");
|
|
26
|
+
return `AAPL${two(month.getUTCFullYear() % 100)}${two(month.getUTCMonth() + 1)}${two(thirdFriday)}C00300000`;
|
|
27
|
+
}
|
|
10
28
|
type Command = Parameters<Parameters<NonNullable<GloomPlugin["setup"]>>[0]["registerCommand"]>[0];
|
|
11
29
|
type Field = NonNullable<Command["wizard"]>[number];
|
|
30
|
+
/** An earnings alert goes off days before the report, or once its results are out. */
|
|
31
|
+
const EARNINGS_ALERT_WHEN = [{ value: "before", label: "Days before the date" }, { value: "results", label: "When results are out" }];
|
|
12
32
|
const choices = (values: string[]) => values.map(value => ({label: value.replaceAll("_", " "), value}));
|
|
13
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The kinds Add Event Alert offers. A calendar event is added by name from the
|
|
35
|
+
* shell (`alerts add "NBS PMI"`), which matches it against the calendar; the
|
|
36
|
+
* form keeps its CPI and FOMC choice under Macro event.
|
|
37
|
+
*/
|
|
38
|
+
export const RESEARCH_FORM_KINDS = (Object.keys(RESEARCH_LABELS) as ResearchAlertKind[]).filter((kind) => kind !== "calendar_event");
|
|
39
|
+
export const researchWizardFields: Field[] = RESEARCH_FORM_KINDS.flatMap(kind => {
|
|
14
40
|
const fields: Field[] = kind === "perps_threshold" ? [
|
|
15
41
|
{ key: "marketId", label: "Market", type: "text", placeholder: "hyperliquid:default:BTC", required: true },
|
|
16
42
|
{ key: "metric", label: "Metric", type: "select", defaultValue: "funding8h", options: [{ value: "funding8h", label: "Funding / 8h" }, { value: "oiChange24h", label: "OI change / 24h" }, { value: "premium", label: "Premium vs oracle" }, { value: "closedMarketPremium", label: "Closed-market premium" }] },
|
|
17
43
|
{ key: "direction", label: "Crossing", type: "select", defaultValue: "above", options: choices(["above", "below"]) },
|
|
18
44
|
{ key: "threshold", label: "Threshold (%)", type: "number", defaultValue: "0.1" },
|
|
19
|
-
] : kind === "iv_spike" ? [{key:"contract", label:"OCC contract", type:"text", placeholder:
|
|
45
|
+
] : kind === "iv_spike" ? [{key:"contract", label:"OCC contract", type:"text", placeholder:occContractExample(), required:true}]
|
|
20
46
|
: kind === "macro_event" ? [{key:"event", label:"Release", type:"select", defaultValue:"cpi", options:MACRO_ALERT_EVENTS.map((value) => ({value, label:MACRO_EVENT_NAMES[value]}))}]
|
|
47
|
+
// HIRE covers listings worldwide; a non-US one is written with its venue.
|
|
48
|
+
: kind === "hiring_move" ? [{key:"symbol", label:"Symbol", type:"text", placeholder:"RIVN or ADYEN:AMS", required:true}]
|
|
21
49
|
: [{key:"symbol",label:kind === "news_keyword" ? "Symbol (optional)" : kind === "options_flow" ? "Symbol (blank: portfolio and watchlists)" : "Symbol",type:"text",placeholder:"AAPL", required:kind !== "news_keyword" && kind !== "options_flow"},
|
|
22
50
|
{key:"exchange",label:"US exchange",type:"select",defaultValue:"US",options:choices(["US","NASDAQ","NYSE","ARCA","AMEX","BATS","OTC"])}];
|
|
23
|
-
if (kind === "earnings_date")
|
|
51
|
+
if (kind === "earnings_date") {
|
|
52
|
+
fields.push({key:"when",label:"Alert",type:"select",defaultValue:"before",options:EARNINGS_ALERT_WHEN});
|
|
53
|
+
fields.push({key:"leadDays",label:"Days before earnings",type:"number",defaultValue:String(DEFAULT_LEAD_DAYS)});
|
|
54
|
+
}
|
|
24
55
|
if (kind === "macro_event") fields.push({key:"leadDays",label:"Days before",type:"number",defaultValue:String(DEFAULT_LEAD_DAYS)});
|
|
25
56
|
if (kind === "filing_type") fields.push({key:"form",label:"Filing type",type:"select",defaultValue:"8-K",options:choices([...RESEARCH_ALERT_FORMS])});
|
|
26
57
|
if (kind === "news_keyword") fields.push({key:"keyword",label:"Keyword or phrase",type:"text",required:true,placeholder:"buyback"});
|
|
27
|
-
|
|
58
|
+
if (kind === "iv_level") fields.push({key:"metric",label:"Measure",type:"select",defaultValue:"atm",options:[{value:"atm",label:"30d ATM IV (%)"},{value:"rr",label:"30d 25D risk reversal (vol pts)"}]});
|
|
59
|
+
const directions: Partial<Record<ResearchAlertKind, string[]>> = {iv_level:["above","below"],analyst_change:["any","upgrade","downgrade"],fifty_two_week:["high","low"],short_interest_change:["either","increase","decrease"],insider_trade:["either","buy","sell"],options_flow:["any","calls","puts"],hiring_move:["either","accelerating","slowing"]};
|
|
28
60
|
const direction = directions[kind as ResearchAlertKind];
|
|
29
61
|
if (direction) fields.push({key:"direction",label:kind === "options_flow" ? "Calls or puts" : "Direction",type:"select",defaultValue:direction[0],options:choices(direction)});
|
|
30
62
|
if (kind === "options_flow") {
|
|
31
63
|
fields.push({key:"threshold",label:"Minimum premium ($)",type:"number",defaultValue:"1000000"});
|
|
32
64
|
fields.push({key:"print",label:"Print type",type:"select",defaultValue:"any",options:[{label:"any print",value:"any"},{label:"sweeps only",value:"sweep"},{label:"blocks only",value:"block"}]});
|
|
33
65
|
}
|
|
66
|
+
if (kind === "iv_level") fields.push({key:"threshold",label:"Level (IV %, RR vol pts)",type:"number",placeholder:"35",required:true});
|
|
34
67
|
if (kind === "unusual_volume" || kind === "short_interest_change" || kind === "iv_spike") fields.push({key:"threshold",label:kind === "unusual_volume" ? "Volume / prior 20-session average" : kind === "iv_spike" ? "IV rise (percentage points)" : "Settlement change (%)",type:"number",defaultValue:kind === "unusual_volume" ? "2" : kind === "iv_spike" ? "5" : "10"});
|
|
35
|
-
return fields.map(field => ({...field, key:`${kind}:${field.key}`, dependsOn:
|
|
68
|
+
return fields.map(field => ({...field, key:`${kind}:${field.key}`, dependsOn: kind === "earnings_date" && field.key === "leadDays"
|
|
69
|
+
// The lead time is for an alert before the date, not one waiting for results.
|
|
70
|
+
? [{key:"event",value:kind}, {key:"earnings_date:when",value:"before"}]
|
|
71
|
+
: {key:"event",value:kind}}));
|
|
36
72
|
});
|
|
73
|
+
/** The Add Event Alert form filled in for an earnings alert on `symbol` that goes off once its results are out, as ERN opens it. */
|
|
74
|
+
export function earningsResultsAlertValues(symbol: string): Record<string, string> {
|
|
75
|
+
return { event: "earnings_date", "earnings_date:symbol": symbol, "earnings_date:when": "results" };
|
|
76
|
+
}
|
|
37
77
|
export function createResearchAlert(kind: string, values: Record<string,string>, now = Date.now()): EventAlertRule {
|
|
38
78
|
if (!isResearchAlertKind(kind)) throw new Error("Choose an alert event.");
|
|
39
79
|
const input: Record<string,unknown> = {version:1};
|
|
40
80
|
for (const [key,value] of Object.entries(values)) if (key.startsWith(`${kind}:`)) {
|
|
41
81
|
const field = key.slice(kind.length+1);
|
|
42
82
|
if (kind === "perps_threshold" && field === "threshold" && value.trim()) input[field] = Number(value) / 100;
|
|
83
|
+
// A level reads as typed, signed (a risk reversal is often below zero) and with or without its unit.
|
|
84
|
+
else if (kind === "iv_level" && field === "threshold" && value.trim()) {
|
|
85
|
+
const unitError = ivLevelUnitError(values["iv_level:metric"] === "rr" ? "rr" : "atm", value);
|
|
86
|
+
if (unitError) throw new Error(`${unitError}.`);
|
|
87
|
+
input[field] = parseIvLevel(value);
|
|
88
|
+
}
|
|
43
89
|
else if (value.trim()) input[field] = field === "threshold" || field === "leadDays" ? parseAmount(value) : value;
|
|
44
90
|
}
|
|
45
91
|
const value = normalizeResearchRule(kind,input);
|
|
46
92
|
if (!value) throw new Error(kind === "options_flow"
|
|
47
93
|
? "Check the symbol and US exchange. The minimum premium is $50,000."
|
|
48
|
-
: "Check the symbol
|
|
94
|
+
: kind === "hiring_move" ? "Check the symbol. Write a non-US listing with its exchange, as ADYEN:AMS."
|
|
95
|
+
: kind === "iv_level"
|
|
96
|
+
? "Check the symbol and US exchange. An ATM IV level is 1 to 500 (%), a risk reversal -50 to 50 (vol pts)."
|
|
97
|
+
: kind === "iv_spike"
|
|
98
|
+
? `Use a valid OCC contract, such as ${occContractExample()}.`
|
|
99
|
+
: "Check the symbol, US exchange and event fields.");
|
|
49
100
|
return {id:crypto.randomUUID(),kind,target:"rule",value,createdAt:now,status:"active"};
|
|
50
101
|
}
|
|
51
102
|
/** "2.5", "1,000,000", "$250k" and "1.5m" all read as numbers; anything else is NaN. */
|
|
@@ -55,11 +106,27 @@ export function parseAmount(value: string): number {
|
|
|
55
106
|
const scale = { k: 1e3, m: 1e6, b: 1e9 }[match[2]?.toLowerCase() as "k" | "m" | "b"] ?? 1;
|
|
56
107
|
return Number(match[1]!.replace(/[,_]/g, "")) * scale;
|
|
57
108
|
}
|
|
58
|
-
|
|
109
|
+
const IV_LEVEL_PATTERN = /^([+-]?\d+(?:\.\d+)?)\s*(%|pts?|points?|vol pts?|vol points?)?$/i;
|
|
110
|
+
/** "35", "35%", "-4.5", "+2 pts" and "-5.5 vol points" read as numbers; anything else is NaN. */
|
|
111
|
+
export function parseIvLevel(value: string): number {
|
|
112
|
+
const match = IV_LEVEL_PATTERN.exec(value.trim());
|
|
113
|
+
return match ? Number(match[1]) : Number.NaN;
|
|
114
|
+
}
|
|
115
|
+
/** Why a level typed with a unit does not suit its measure (`5 pts` for an ATM IV, `-5%` for a risk reversal); null when it does or has none. */
|
|
116
|
+
export function ivLevelUnitError(metric: IvLevelMetric, value: string): string | null {
|
|
117
|
+
const unit = IV_LEVEL_PATTERN.exec(value.trim())?.[2];
|
|
118
|
+
if (!unit) return null;
|
|
119
|
+
const percent = unit === "%";
|
|
120
|
+
if (metric === "atm" && !percent) return `An ATM IV level is in %, not vol points ("${value.trim()}")`;
|
|
121
|
+
if (metric === "rr" && percent) return `A risk reversal is in vol points, not % ("${value.trim()}")`;
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/** $1M, $250K, $1.5B, and $500 under a thousand. */
|
|
59
125
|
export function formatPremium(value: number): string {
|
|
60
126
|
if (value >= 1e9) return `$${Number((value / 1e9).toFixed(1))}B`;
|
|
61
127
|
if (value >= 1e6) return `$${Number((value / 1e6).toFixed(1))}M`;
|
|
62
|
-
return `$${
|
|
128
|
+
if (value >= 1e3) return `$${Number((value / 1e3).toFixed(1))}K`;
|
|
129
|
+
return `$${Number(Math.max(0, value).toFixed(2))}`;
|
|
63
130
|
}
|
|
64
131
|
export function researchAlertDescription(rule: EventAlertRule): string {
|
|
65
132
|
if (!isResearchAlertKind(rule.kind)) return rule.value;
|
|
@@ -68,15 +135,21 @@ export function researchAlertDescription(rule: EventAlertRule): string {
|
|
|
68
135
|
const symbol = config.symbol ?? "All symbols";
|
|
69
136
|
switch(rule.kind) {
|
|
70
137
|
case "perps_threshold": return `${config.marketId} · ${config.metric} ${config.direction} ${((config.threshold ?? 0) * 100).toFixed(3)}%`;
|
|
71
|
-
case "earnings_date": return `${symbol} · ${config.leadDays}d before`;
|
|
138
|
+
case "earnings_date": return `${symbol} · ${config.when === "results" ? "results" : `${config.leadDays}d before`}`;
|
|
72
139
|
case "macro_event": return `${MACRO_EVENT_NAMES[config.event as MacroAlertEvent] ?? config.event} · ${config.leadDays}d before`;
|
|
140
|
+
case "calendar_event": return `${config.event} (${config.country}) · ${config.leadDays}d before`;
|
|
73
141
|
case "filing_type": return `${symbol} · ${config.form}`;
|
|
74
142
|
case "news_keyword": return `${symbol} · ${config.keyword}`;
|
|
75
143
|
case "analyst_change": case "insider_trade": return `${symbol} · ${config.direction}`;
|
|
144
|
+
case "hiring_move": return `${symbol} · ${config.direction === "either" ? "accelerating or slowing" : config.direction}`;
|
|
76
145
|
case "fifty_two_week": return `${symbol} · 52w ${config.direction}`;
|
|
77
146
|
case "unusual_volume": return `${symbol} · ≥${config.threshold}× 20d`;
|
|
78
147
|
case "short_interest_change": return `${symbol} · ${config.direction} ≥${config.threshold}%`;
|
|
79
148
|
case "iv_spike": return `${config.contract} · +${config.threshold}pt`;
|
|
149
|
+
case "iv_level": {
|
|
150
|
+
const metric = config.metric as IvLevelMetric;
|
|
151
|
+
return `${symbol} · ${IV_LEVEL_NAMES[metric]} ${config.direction} ${formatIvLevel(metric, config.threshold ?? 0)}`;
|
|
152
|
+
}
|
|
80
153
|
case "options_flow": return [
|
|
81
154
|
config.symbol ?? "Portfolio and watchlists",
|
|
82
155
|
`≥${formatPremium(config.threshold ?? 0)}`,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const RESEARCH_ALERT_KINDS = [
|
|
3
3
|
"earnings_date",
|
|
4
4
|
"macro_event",
|
|
5
|
+
"calendar_event",
|
|
5
6
|
"filing_type",
|
|
6
7
|
"news_keyword",
|
|
7
8
|
"analyst_change",
|
|
@@ -10,8 +11,10 @@ const RESEARCH_ALERT_KINDS = [
|
|
|
10
11
|
"short_interest_change",
|
|
11
12
|
"insider_trade",
|
|
12
13
|
"iv_spike",
|
|
14
|
+
"iv_level",
|
|
13
15
|
"options_flow",
|
|
14
16
|
"perps_threshold",
|
|
17
|
+
"hiring_move",
|
|
15
18
|
] as const
|
|
16
19
|
export type ResearchAlertKind = (typeof RESEARCH_ALERT_KINDS)[number]
|
|
17
20
|
export interface ResearchAlertConfig {
|
|
@@ -26,10 +29,32 @@ export interface ResearchAlertConfig {
|
|
|
26
29
|
contract?: string
|
|
27
30
|
/** Options flow: which prints count (any, sweep or block). */
|
|
28
31
|
marketId?: string
|
|
29
|
-
|
|
32
|
+
/** Perpetuals: the market figure. IV level: `atm`, the 30-day ATM IV, or `rr`, the 30-day 25-delta risk reversal. */
|
|
33
|
+
metric?: "funding8h" | "oiChange24h" | "premium" | "closedMarketPremium" | IvLevelMetric
|
|
30
34
|
print?: string
|
|
31
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Macro event: which scheduled release (`MACRO_ALERT_EVENTS`). Calendar
|
|
37
|
+
* event: the release's name as the economic calendar lists it.
|
|
38
|
+
*/
|
|
32
39
|
event?: string
|
|
40
|
+
/** Calendar event: the country the calendar lists the release for (CN, US, DE for a German release). */
|
|
41
|
+
country?: string
|
|
42
|
+
/** Calendar event: who publishes it, when the calendar names them, which tells apart releases of one name. */
|
|
43
|
+
publisher?: string
|
|
44
|
+
/**
|
|
45
|
+
* Earnings: `results` goes off when the report's actual EPS is out, rather
|
|
46
|
+
* than `leadDays` before the date. Such a rule keeps a lead of 0, so a
|
|
47
|
+
* server that reads only the lead alerts on the day instead.
|
|
48
|
+
*/
|
|
49
|
+
when?: "results"
|
|
50
|
+
}
|
|
51
|
+
/** What an IV level rule follows on its symbol's surface: 30-day ATM IV (in %) or 30-day 25-delta risk reversal (in vol points). */
|
|
52
|
+
const IV_LEVEL_METRICS = ["atm", "rr"] as const
|
|
53
|
+
export type IvLevelMetric = (typeof IV_LEVEL_METRICS)[number]
|
|
54
|
+
/** The levels an IV level rule takes: an ATM IV in percent, a risk reversal in vol points either side of zero. */
|
|
55
|
+
export const IV_LEVEL_RANGES: Record<IvLevelMetric, { min: number; max: number }> = {
|
|
56
|
+
atm: { min: 1, max: 500 },
|
|
57
|
+
rr: { min: -50, max: 50 },
|
|
33
58
|
}
|
|
34
59
|
/** Days before a scheduled event (earnings, a macro release) an alert fires when none is given, and the most it takes. */
|
|
35
60
|
export const DEFAULT_LEAD_DAYS = 1
|
|
@@ -39,6 +64,14 @@ export const MACRO_ALERT_EVENTS = ["cpi", "fomc"] as const
|
|
|
39
64
|
export type MacroAlertEvent = (typeof MACRO_ALERT_EVENTS)[number]
|
|
40
65
|
export const isMacroAlertEvent = (value: unknown): value is MacroAlertEvent =>
|
|
41
66
|
MACRO_ALERT_EVENTS.includes(value as MacroAlertEvent)
|
|
67
|
+
/** The longest calendar event name or publisher a rule keeps. */
|
|
68
|
+
const MAX_CALENDAR_TEXT = 120
|
|
69
|
+
/** A calendar event's name or publisher as a rule keeps it: trimmed, single-spaced, printable; empty when it is none. */
|
|
70
|
+
const calendarText = (value: unknown): string => {
|
|
71
|
+
if (typeof value !== "string") return ""
|
|
72
|
+
const text = value.trim().replace(/\s+/g, " ")
|
|
73
|
+
return text.length <= MAX_CALENDAR_TEXT && !/[\u0000-\u001f]/.test(text) ? text : ""
|
|
74
|
+
}
|
|
42
75
|
export const RESEARCH_ALERT_FORMS = [
|
|
43
76
|
"8-K",
|
|
44
77
|
"10-K",
|
|
@@ -98,6 +131,14 @@ export function normalizeResearchRule(
|
|
|
98
131
|
const event = typeof input.event === "string" ? input.event.trim().toLowerCase() : ""
|
|
99
132
|
if (!isMacroAlertEvent(event)) return null
|
|
100
133
|
result.event = event
|
|
134
|
+
} else if (kind === "calendar_event") {
|
|
135
|
+
const event = calendarText(input.event)
|
|
136
|
+
const country = typeof input.country === "string" ? input.country.trim().toUpperCase() : ""
|
|
137
|
+
if (!event || !/^[A-Z]{2}$/.test(country)) return null
|
|
138
|
+
result.event = event
|
|
139
|
+
result.country = country
|
|
140
|
+
const publisher = calendarText(input.publisher)
|
|
141
|
+
if (publisher) result.publisher = publisher
|
|
101
142
|
} else if (kind === "perps_threshold") {
|
|
102
143
|
const marketId = typeof input.marketId === "string" ? input.marketId.trim() : "";
|
|
103
144
|
const metric = input.metric;
|
|
@@ -113,6 +154,14 @@ export function normalizeResearchRule(
|
|
|
113
154
|
result.symbol = identity.symbol
|
|
114
155
|
result.exchange = "US"
|
|
115
156
|
result.contract = identity.contract
|
|
157
|
+
} else if (kind === "hiring_move") {
|
|
158
|
+
// A HIRE company: any listing, a world one with its venue (ADYEN:AMS).
|
|
159
|
+
const symbol =
|
|
160
|
+
typeof input.symbol === "string"
|
|
161
|
+
? input.symbol.trim().toUpperCase()
|
|
162
|
+
: ""
|
|
163
|
+
if (!/^[A-Z0-9][A-Z0-9.:-]{0,31}$/.test(symbol)) return null
|
|
164
|
+
result.symbol = symbol
|
|
116
165
|
} else if (
|
|
117
166
|
(kind !== "news_keyword" && kind !== "options_flow") ||
|
|
118
167
|
input.symbol
|
|
@@ -162,8 +211,17 @@ export function normalizeResearchRule(
|
|
|
162
211
|
numeric("threshold", 0.001, -100, 100);
|
|
163
212
|
direction(["above", "below"], "above");
|
|
164
213
|
break;
|
|
165
|
-
case "earnings_date":
|
|
214
|
+
case "earnings_date": {
|
|
215
|
+
const when = input.when ?? "before"
|
|
216
|
+
if (when === "results") {
|
|
217
|
+
result.leadDays = 0
|
|
218
|
+
result.when = "results"
|
|
219
|
+
} else if (when === "before") numeric("leadDays", DEFAULT_LEAD_DAYS, 0, MAX_LEAD_DAYS, true)
|
|
220
|
+
else throw new Error()
|
|
221
|
+
break
|
|
222
|
+
}
|
|
166
223
|
case "macro_event":
|
|
224
|
+
case "calendar_event":
|
|
167
225
|
numeric("leadDays", DEFAULT_LEAD_DAYS, 0, MAX_LEAD_DAYS, true)
|
|
168
226
|
break
|
|
169
227
|
case "filing_type": {
|
|
@@ -211,9 +269,24 @@ export function normalizeResearchRule(
|
|
|
211
269
|
case "insider_trade":
|
|
212
270
|
direction(["buy", "sell", "either"], "either")
|
|
213
271
|
break
|
|
272
|
+
case "hiring_move":
|
|
273
|
+
direction(["either", "accelerating", "slowing"], "either")
|
|
274
|
+
break
|
|
214
275
|
case "iv_spike":
|
|
215
276
|
numeric("threshold", 5, 0.1, 100)
|
|
216
277
|
break
|
|
278
|
+
case "iv_level": {
|
|
279
|
+
const metric = input.metric ?? "atm"
|
|
280
|
+
if (typeof metric !== "string" || !IV_LEVEL_METRICS.includes(metric as IvLevelMetric))
|
|
281
|
+
throw new Error()
|
|
282
|
+
result.metric = metric as IvLevelMetric
|
|
283
|
+
direction(["above", "below"], "above")
|
|
284
|
+
const range = IV_LEVEL_RANGES[metric as IvLevelMetric]
|
|
285
|
+
// No default: a level is what the rule is about.
|
|
286
|
+
if (input.threshold == null) throw new Error()
|
|
287
|
+
numeric("threshold", 0, range.min, range.max)
|
|
288
|
+
break
|
|
289
|
+
}
|
|
217
290
|
case "options_flow": {
|
|
218
291
|
// Premium in dollars; Cloud records nothing under $50K.
|
|
219
292
|
numeric("threshold", 1_000_000, 50_000, 1_000_000_000)
|