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
|
@@ -3,23 +3,43 @@ import { CLI_COMMAND_GROUPS } from "../../../cli/help";
|
|
|
3
3
|
import { dryRunNote } from "../../../cli/helpers";
|
|
4
4
|
import { requireArg, takeOption } from "../../../cli/commands/command-utils";
|
|
5
5
|
import { requireQuotedListing } from "../../../cli/quote-answer";
|
|
6
|
-
import { EXCHANGE_OPTION, listingReadNotes, requireListingArg } from "../../../cli/listing-arg";
|
|
6
|
+
import { EXCHANGE_OPTION, listingReadNotes, requireListingArg, usListingsOnlyMessage } from "../../../cli/listing-arg";
|
|
7
|
+
import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
|
|
7
8
|
import { saveConfig } from "../../../data/config/store";
|
|
9
|
+
import { resumeStateKey } from "../../registry/plugin-state";
|
|
8
10
|
import type { CliCommandDef } from "../../../types/plugin";
|
|
9
11
|
import type { Quote } from "../../../types/financials";
|
|
10
12
|
import { getActiveQuoteDisplay } from "../../../market-data/market/status";
|
|
11
13
|
import { cliStyles } from "../../../utils/cli-output";
|
|
12
|
-
import {
|
|
14
|
+
import { canonicalExchange, isUsListingExchange } from "../../../utils/exchanges";
|
|
15
|
+
import { meantThousandsDot, parsePastedNumber, thousandsDotReadings, type ThousandsDotReadings } from "../../../utils/decimal-comma";
|
|
13
16
|
import { newYorkToday } from "../earnings/board-model";
|
|
17
|
+
import { fetchEconCalendarFrom } from "../econ/calendar-source";
|
|
18
|
+
import { COUNTRY_FILTER_HINT, dateKey, loadCalendar, parseCountryFilter, type CountryFilter } from "../econ/calendar-model";
|
|
19
|
+
import { isAllDayEvent, type EconEvent } from "../econ/types";
|
|
20
|
+
import {
|
|
21
|
+
calendarEventLabel,
|
|
22
|
+
calendarNameWords,
|
|
23
|
+
expectsFigures,
|
|
24
|
+
macroEventNamed,
|
|
25
|
+
matchCalendarEvent,
|
|
26
|
+
releaseInstant,
|
|
27
|
+
releaseRowOn,
|
|
28
|
+
type CalendarEventChoice,
|
|
29
|
+
} from "./calendar-events";
|
|
14
30
|
import { createAlert, rearmAlert, serializeAlerts } from "./alert-engine";
|
|
15
31
|
import { ALERTS_KEY, ALERTS_PLUGIN_ID } from "./constants";
|
|
16
|
-
import {
|
|
32
|
+
import { MAX_EVENT_ALERTS, eventAlertTarget, toggleEventAlert, writeEventAlertStores, type EventAlertRule } from "./events";
|
|
17
33
|
import { alertKindLabel } from "./history";
|
|
18
|
-
import {
|
|
34
|
+
import { IV_LEVEL_CHECKS_KEY, readIvLevelChecks, rearmIvLevelAlert, type IvLevelCheck } from "./iv-level";
|
|
35
|
+
import { parseAlertPrice, readAlertPrice } from "./price";
|
|
36
|
+
import { farTargetMessage } from "./quotes";
|
|
37
|
+
import { MACRO_EVENT_NAMES, createResearchAlert, formatPremium, ivLevelUnitError, parseAmount, parseIvLevel, researchAlertDescription } from "./research-builder";
|
|
38
|
+
import { IV_LEVEL_RANGES, MAX_LEAD_DAYS, type IvLevelMetric, type MacroAlertEvent } from "./research-rules";
|
|
19
39
|
import {
|
|
20
|
-
MACRO_EVENT_CLI_NAMES,
|
|
21
40
|
createScheduledAlert,
|
|
22
41
|
eventDateUnavailableMessage,
|
|
42
|
+
followsRelease,
|
|
23
43
|
leadStartDate,
|
|
24
44
|
lookupScheduledDates,
|
|
25
45
|
noUpcomingEventMessage,
|
|
@@ -31,6 +51,8 @@ import {
|
|
|
31
51
|
scheduledEventName,
|
|
32
52
|
scheduledPhase,
|
|
33
53
|
scheduledTargetKey,
|
|
54
|
+
scheduledToday,
|
|
55
|
+
scheduledTriggerText,
|
|
34
56
|
type ScheduledDateLookup,
|
|
35
57
|
type ScheduledEventSources,
|
|
36
58
|
type ScheduledEventTarget,
|
|
@@ -48,8 +70,11 @@ import type { AlertCondition, AlertRule } from "./types";
|
|
|
48
70
|
*/
|
|
49
71
|
const PRICE_DELIVERY = "the app alerts while it is running; sign in with Cloud sync for email or phone alerts when it is closed";
|
|
50
72
|
|
|
51
|
-
const ADD_USAGE = "Usage:
|
|
73
|
+
const ADD_USAGE = "Usage: gloom alerts add <symbol> <above|below|crosses> <price>, alerts add <symbol> earnings [--days <n> | results], alerts add <calendar event> [--country <code>] [--days <n>], alerts add [symbol] news <keyword>, alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks], alerts add <symbol> <iv|rr> <above|below> <level>, or alerts add <OCC contract> iv spike <points>";
|
|
52
74
|
const DAYS_RULE = `--days takes a whole number of days from 1 to ${MAX_LEAD_DAYS}; without it the alert goes off 1 day before.`;
|
|
75
|
+
/** The event form every refusal of `alerts add` names: any release in the economic calendar, CPI and FOMC among them. */
|
|
76
|
+
const EVENT_FORM = `a release in the economic calendar by name (alerts add "NBS Manufacturing PMI" --country CN, alerts add CN PMI, alerts add CPI --days 1)`;
|
|
77
|
+
const COUNTRY_RULE = "--country is for an alert on a calendar event, such as alerts add PMI --country CN.";
|
|
53
78
|
|
|
54
79
|
/** What `alerts add` was asked for, before any listing or date is looked up. */
|
|
55
80
|
export type AlertAddRequest =
|
|
@@ -58,48 +83,108 @@ export type AlertAddRequest =
|
|
|
58
83
|
/** A price typed as 81.000, which the last price may show was 81000 with a dot between thousands. */
|
|
59
84
|
thousandsDot?: ThousandsDotReadings & { text: string };
|
|
60
85
|
}
|
|
61
|
-
|
|
62
|
-
| { kind: "
|
|
86
|
+
/** `results`: once the report's actual EPS is out, rather than `leadDays` before the date. */
|
|
87
|
+
| { kind: "earnings"; symbol: string; leadDays: number; results?: true }
|
|
88
|
+
| { kind: "macro"; event: MacroAlertEvent; leadDays: number }
|
|
89
|
+
/** A release in the economic calendar by name, matched against the calendar once it is read; `country` narrows it. */
|
|
90
|
+
| { kind: "release"; query: string; country: CountryFilter | null; leadDays: number }
|
|
91
|
+
/** Gloom Cloud's research rules, as Add Event Alert makes them; a news or flow rule without a symbol follows every name (news) or your lists (flow). */
|
|
92
|
+
| { kind: "news"; symbol: string | null; keyword: string }
|
|
93
|
+
| { kind: "flow"; symbol: string | null; premium: number; side: "any" | "calls" | "puts"; print: "any" | "sweep" | "block" }
|
|
94
|
+
/** The symbol's 30-day ATM IV (`atm`, in %) or 25-delta risk reversal (`rr`, in vol points) above or below a level, checked by the running app. */
|
|
95
|
+
| { kind: "iv_level"; symbol: string; metric: IvLevelMetric; direction: "above" | "below"; level: number }
|
|
96
|
+
/** One OCC contract's IV rising by `points`. */
|
|
97
|
+
| { kind: "iv_spike"; contract: string; points: number };
|
|
98
|
+
|
|
99
|
+
/** The `alerts add` forms Gloom Cloud's research rules take: they follow US listings. */
|
|
100
|
+
type ResearchAddRequest = Extract<AlertAddRequest, { kind: "news" | "flow" | "iv_level" | "iv_spike" }>;
|
|
63
101
|
|
|
64
102
|
const CONDITIONS: readonly AlertCondition[] = ["above", "below", "crosses"];
|
|
65
103
|
|
|
66
104
|
/** The first word of a price typed with a space between thousands and no quotes (`81 000`): one to three digits. */
|
|
67
105
|
const LEADING_GROUP = /^[+-]?\d{1,3}$/;
|
|
68
106
|
|
|
107
|
+
/** `--country` as typed: a country code or the calendar's regions (`CN`, `uk`, `EU`); null when not given. Throws when it names none. */
|
|
108
|
+
function readCountryOption(country: string | null | undefined): CountryFilter | null {
|
|
109
|
+
if (country === undefined) return null;
|
|
110
|
+
if (country === null) throw new Error(`--country takes a two-letter country code, such as --country CN. ${COUNTRY_FILTER_HINT}`);
|
|
111
|
+
const parsed = parseCountryFilter(country);
|
|
112
|
+
if ("error" in parsed) throw new Error(`${parsed.error} ${COUNTRY_FILTER_HINT}`);
|
|
113
|
+
return parsed.filter === "all" ? null : parsed.filter;
|
|
114
|
+
}
|
|
115
|
+
|
|
69
116
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* takes.
|
|
117
|
+
* A release named by its words (`NBS PMI`, `Industrial Profits`), a country
|
|
118
|
+
* code first standing for `--country` (`CN PMI`). CPI and FOMC, in the US or
|
|
119
|
+
* with no country, are the macro alerts whose dates come from their own
|
|
120
|
+
* calendars.
|
|
75
121
|
*/
|
|
76
|
-
|
|
122
|
+
function releaseRequest(args: readonly string[], country: CountryFilter | null, leadDays: number): AlertAddRequest {
|
|
123
|
+
const words = args.join(" ").trim().split(/\s+/);
|
|
124
|
+
let query = words.join(" ");
|
|
125
|
+
if (!country && words.length >= 2 && /^[a-z]{2}$/i.test(words[0]!)) {
|
|
126
|
+
const parsed = parseCountryFilter(words[0]!);
|
|
127
|
+
if (!("error" in parsed) && parsed.filter !== "all") {
|
|
128
|
+
country = parsed.filter;
|
|
129
|
+
query = words.slice(1).join(" ");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const event = parseMacroEventName(query);
|
|
133
|
+
if (event && (!country || country === "US")) return { kind: "macro", event, leadDays };
|
|
134
|
+
return { kind: "release", query, country, leadDays };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Reads `alerts add` arguments: `<symbol> above 250`, `<symbol> earnings`,
|
|
139
|
+
* `<symbol> earnings results`, or a release in the economic calendar by name
|
|
140
|
+
* (`CPI`, `FOMC`, `NBS PMI`, `CN PMI`), with `--days` (already taken out of
|
|
141
|
+
* `args`, `null` when the flag came without a value) and `--country` for the
|
|
142
|
+
* event forms only, and `--exchange`, which a command to type again keeps.
|
|
143
|
+
* Throws a message that names what the command takes.
|
|
144
|
+
*/
|
|
145
|
+
export function parseAlertAddArgs(
|
|
146
|
+
args: readonly string[],
|
|
147
|
+
days: string | null | undefined,
|
|
148
|
+
exchange?: string | null,
|
|
149
|
+
country?: string | null,
|
|
150
|
+
): AlertAddRequest {
|
|
77
151
|
const [first, second, third, ...rest] = args;
|
|
78
152
|
if (!first) throw new Error(ADD_USAGE);
|
|
153
|
+
const countryFilter = readCountryOption(country);
|
|
154
|
+
const research = parseResearchAddArgs(args);
|
|
155
|
+
if (research) {
|
|
156
|
+
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not news, flow or IV alerts.");
|
|
157
|
+
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
158
|
+
return research;
|
|
159
|
+
}
|
|
79
160
|
const leadDays = () => {
|
|
80
161
|
const parsed = days === null ? null : parseLeadDays(days);
|
|
81
162
|
if (parsed == null) throw new Error(DAYS_RULE);
|
|
82
163
|
return parsed;
|
|
83
164
|
};
|
|
84
165
|
if (second === undefined) {
|
|
85
|
-
const event = parseMacroEventName(first);
|
|
86
|
-
if (event) return { kind: "macro", event, leadDays: leadDays() };
|
|
87
166
|
if (first.toLowerCase() === "earnings") {
|
|
88
|
-
throw new Error("Name the company:
|
|
167
|
+
throw new Error("Name the company: gloom alerts add <symbol> earnings, such as alerts add CRWV earnings --days 3.");
|
|
89
168
|
}
|
|
90
|
-
|
|
91
|
-
`Unknown event "${first}". Event alerts take ${MACRO_EVENT_CLI_NAMES} (alerts add CPI --days 1) or a symbol with earnings (alerts add CRWV earnings --days 3); price alerts take a condition and a price (alerts add AAPL above 250).`,
|
|
92
|
-
);
|
|
169
|
+
return releaseRequest(args, countryFilter, leadDays());
|
|
93
170
|
}
|
|
171
|
+
if (second.toLowerCase() === "earnings" && country !== undefined) throw new Error(COUNTRY_RULE);
|
|
94
172
|
if (second.toLowerCase() === "earnings") {
|
|
95
|
-
if (third
|
|
173
|
+
if (third?.toLowerCase() === "results" && rest.length === 0) {
|
|
174
|
+
if (days !== undefined) throw new Error("An alert for results goes off once they are out and takes no --days.");
|
|
175
|
+
return { kind: "earnings", symbol: first, leadDays: 0, results: true };
|
|
176
|
+
}
|
|
177
|
+
if (third !== undefined) throw new Error(`Earnings alerts take a symbol and --days or results, not "${[third, ...rest].join(" ")}". ${ADD_USAGE}`);
|
|
96
178
|
return { kind: "earnings", symbol: first, leadDays: leadDays() };
|
|
97
179
|
}
|
|
98
180
|
const condition = second.toLowerCase() as AlertCondition;
|
|
99
181
|
if (!CONDITIONS.includes(condition)) {
|
|
100
|
-
|
|
182
|
+
// Words with no price after them name a release (CN PMI, Industrial Profits); a price ends a mistyped price alert.
|
|
183
|
+
if (parseAlertPrice(args.at(-1)!).kind === "invalid") return releaseRequest(args, countryFilter, leadDays());
|
|
184
|
+
throw new Error(`Unknown alert "${second}". Use above, below or crosses with a price, earnings with --days, news with a keyword, flow, iv or rr with above or below and a level, iv spike on an OCC contract, or ${EVENT_FORM}.`);
|
|
101
185
|
}
|
|
102
|
-
if (
|
|
186
|
+
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
187
|
+
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not price alerts.");
|
|
103
188
|
if (third === undefined) throw new Error(ADD_USAGE);
|
|
104
189
|
const retype = (price: number) => retypeCommand(first, condition, price, exchange);
|
|
105
190
|
// `81 000` without quotes arrives as 81 and a stray 000; neither reading is taken for granted.
|
|
@@ -109,17 +194,122 @@ export function parseAlertAddArgs(args: readonly string[], days: string | null |
|
|
|
109
194
|
throw new Error(`${[third, ...rest].join(" ")} could be ${spaced.value} (thousands) or ${typed} and a stray ${rest.join(" ")}; type ${retype(spaced.value)} or ${retype(typed)}`);
|
|
110
195
|
}
|
|
111
196
|
// A price pasted from a sheet (1.350,5, 1,350.5, 1 350,5) reads as it is written; `1,350` could be either, so it asks.
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
114
|
-
if (pasted.kind === "invalid") throw new Error("Alert price must be a finite number.");
|
|
115
|
-
if (rest.length > 0) throw new Error(`A price alert takes one price, not "${rest.join(" ")}" after ${third}; type ${retype(pasted.value)}`);
|
|
197
|
+
const price = readAlertPrice(third);
|
|
198
|
+
if (rest.length > 0) throw new Error(`A price alert takes one price, not "${rest.join(" ")}" after ${third}; type ${retype(price)}`);
|
|
116
199
|
const thousandsDot = thousandsDotReadings(third);
|
|
117
|
-
return { kind: "price", symbol: first, condition, price
|
|
200
|
+
return { kind: "price", symbol: first, condition, price, ...(thousandsDot ? { thousandsDot: { ...thousandsDot, text: third.trim() } } : {}) };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const OCC_CONTRACT = /^(?:O:)?[A-Z.]{1,6}\d{6}[CP]\d{8}$/i;
|
|
204
|
+
const LEVEL_DIRECTIONS = ["above", "below"] as const;
|
|
205
|
+
|
|
206
|
+
/** A level typed for an IV level rule, with its unit or without (`50`, `50%`, `-5.5 pts`), refused with its unit and range when it does not read. */
|
|
207
|
+
function readLevel(metric: IvLevelMetric, typed: string | undefined, symbol: string): number {
|
|
208
|
+
const word = metric === "atm" ? "iv" : "rr";
|
|
209
|
+
const example = metric === "atm" ? `alerts add ${symbol} iv above 50` : `alerts add ${symbol} rr below -5`;
|
|
210
|
+
if (!typed) throw new Error(`Name the level: gloom ${example}.`);
|
|
211
|
+
const unitError = ivLevelUnitError(metric, typed);
|
|
212
|
+
if (unitError) throw new Error(`${unitError}: gloom ${example}.`);
|
|
213
|
+
const level = parseIvLevel(typed);
|
|
214
|
+
const { min, max } = IV_LEVEL_RANGES[metric];
|
|
215
|
+
if (!Number.isFinite(level) || level < min || level > max) {
|
|
216
|
+
throw new Error(metric === "atm"
|
|
217
|
+
? `An ${word} level is the 30-day ATM IV in percent, ${min} to ${max}, not "${typed}": gloom ${example}.`
|
|
218
|
+
: `An ${word} level is the 30-day 25-delta risk reversal in vol points, ${min} to ${max}, not "${typed}": gloom ${example}.`);
|
|
219
|
+
}
|
|
220
|
+
return level;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The research forms of `alerts add`, the ones Add Event Alert makes: `[symbol] news <keyword>`,
|
|
225
|
+
* `[symbol] flow [premium] [calls|puts] [sweeps|blocks]`, `<symbol> iv|rr <above|below> <level>` and
|
|
226
|
+
* `<OCC contract> iv spike <points>`. Null for any other form. A bare `news` or `flow` with no symbol
|
|
227
|
+
* follows every name or your lists; `NEWS above 10` is still a price alert on a symbol.
|
|
228
|
+
*/
|
|
229
|
+
function parseResearchAddArgs(args: readonly string[]): ResearchAddRequest | null {
|
|
230
|
+
const [first = "", second, third, ...rest] = args;
|
|
231
|
+
const word = (value: string | undefined) => value?.toLowerCase() ?? "";
|
|
232
|
+
const isCondition = (value: string | undefined) => (CONDITIONS as readonly string[]).includes(word(value)) || word(value) === "earnings";
|
|
233
|
+
const lead = word(first);
|
|
234
|
+
const [symbol, kind, tail] = (lead === "news" || lead === "flow") && !isCondition(second)
|
|
235
|
+
? [null, lead, args.slice(1)] as const
|
|
236
|
+
: [first, word(second), args.slice(2)] as const;
|
|
237
|
+
if (kind === "news") {
|
|
238
|
+
const keyword = tail.join(" ").trim();
|
|
239
|
+
if (!keyword) throw new Error(`Name a keyword or phrase: gloom alerts add ${symbol ?? "AAPL"} news buyback, or alerts add news "rate cut" for any company.`);
|
|
240
|
+
return { kind: "news", symbol, keyword };
|
|
241
|
+
}
|
|
242
|
+
if (kind === "flow") {
|
|
243
|
+
let premium = 1_000_000;
|
|
244
|
+
let side: "any" | "calls" | "puts" = "any";
|
|
245
|
+
let print: "any" | "sweep" | "block" = "any";
|
|
246
|
+
for (const token of tail) {
|
|
247
|
+
const lower = token.toLowerCase();
|
|
248
|
+
if (lower === "calls" || lower === "call") side = "calls";
|
|
249
|
+
else if (lower === "puts" || lower === "put") side = "puts";
|
|
250
|
+
else if (lower === "sweeps" || lower === "sweep") print = "sweep";
|
|
251
|
+
else if (lower === "blocks" || lower === "block") print = "block";
|
|
252
|
+
else if (Number.isFinite(parseAmount(token))) premium = parseAmount(token);
|
|
253
|
+
else throw new Error(`A flow alert takes a minimum premium (1m, $250k), calls or puts, and sweeps or blocks, not "${token}".`);
|
|
254
|
+
}
|
|
255
|
+
if (premium < 50_000) throw new Error(`The minimum premium is $50,000, not ${formatPremium(premium)}: Gloom Cloud records no smaller print.`);
|
|
256
|
+
return { kind: "flow", symbol, premium, side, print };
|
|
257
|
+
}
|
|
258
|
+
if (!symbol || (kind !== "iv" && kind !== "rr")) return null;
|
|
259
|
+
if (OCC_CONTRACT.test(symbol)) {
|
|
260
|
+
if (kind !== "iv" || word(third) !== "spike") {
|
|
261
|
+
throw new Error(`An option contract takes an IV spike in vol points: gloom alerts add ${symbol.toUpperCase()} iv spike 5. For a level, name the underlying: alerts add ${symbol.replace(/^O:/i, "").match(/^[A-Z.]+/i)![0]!.toUpperCase()} iv above 50.`);
|
|
262
|
+
}
|
|
263
|
+
// Vol points, with the unit or without it: `5`, `5 pts`.
|
|
264
|
+
const typed = rest.join(" ");
|
|
265
|
+
const value = typed && !/%/.test(typed) ? parseIvLevel(typed) : Number.NaN;
|
|
266
|
+
if (!Number.isFinite(value) || value < 0.1 || value > 100) {
|
|
267
|
+
throw new Error(`An IV spike is a rise of 0.1 to 100 vol points: gloom alerts add ${symbol.toUpperCase()} iv spike 5.`);
|
|
268
|
+
}
|
|
269
|
+
return { kind: "iv_spike", contract: symbol.toUpperCase(), points: value };
|
|
270
|
+
}
|
|
271
|
+
const metric: IvLevelMetric = kind === "iv" ? "atm" : "rr";
|
|
272
|
+
const direction = word(third);
|
|
273
|
+
if (!(LEVEL_DIRECTIONS as readonly string[]).includes(direction)) {
|
|
274
|
+
throw new Error(`${kind === "iv" ? "An iv" : "An rr"} alert takes above or below and a level: gloom alerts add ${symbol} ${kind} ${kind === "iv" ? "above 50" : "below -5"}${kind === "iv" ? ", or iv spike on one OCC contract" : ""}.`);
|
|
275
|
+
}
|
|
276
|
+
// The level, with its unit or without it: `50`, `50%`, `-5.5 pts`.
|
|
277
|
+
return { kind: "iv_level", symbol, metric, direction: direction as "above" | "below", level: readLevel(metric, rest.join(" "), symbol) };
|
|
118
278
|
}
|
|
119
279
|
|
|
280
|
+
/** The Add Event Alert form's values for a research request, so the CLI makes the rule the dialog makes. */
|
|
281
|
+
function researchFormValues(request: ResearchAddRequest, exchange: string): { kind: string; values: Record<string, string> } {
|
|
282
|
+
switch (request.kind) {
|
|
283
|
+
case "news": return { kind: "news_keyword", values: { "news_keyword:keyword": request.keyword,
|
|
284
|
+
...request.symbol ? { "news_keyword:symbol": request.symbol, "news_keyword:exchange": exchange } : {} } };
|
|
285
|
+
case "flow": return { kind: "options_flow", values: { "options_flow:threshold": String(request.premium),
|
|
286
|
+
"options_flow:direction": request.side, "options_flow:print": request.print,
|
|
287
|
+
...request.symbol ? { "options_flow:symbol": request.symbol, "options_flow:exchange": exchange } : {} } };
|
|
288
|
+
case "iv_level": return { kind: "iv_level", values: { "iv_level:symbol": request.symbol, "iv_level:exchange": exchange,
|
|
289
|
+
"iv_level:metric": request.metric, "iv_level:direction": request.direction, "iv_level:threshold": String(request.level) } };
|
|
290
|
+
case "iv_spike": return { kind: "iv_spike", values: { "iv_spike:contract": request.contract, "iv_spike:threshold": String(request.points) } };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Who checks a research rule and how it reaches you, as docs/usage.md states it: an IV level is read by the
|
|
296
|
+
* running app; the rest by Gloom Cloud for a signed-in account with Cloud sync, IV spikes and flow on Pro.
|
|
297
|
+
*/
|
|
298
|
+
function researchDelivery(kind: ResearchAddRequest["kind"]): string {
|
|
299
|
+
if (kind === "iv_level") return "the app checks it every 15 minutes while it is running, on the same option fits as OVDV";
|
|
300
|
+
const plan = kind === "news" ? "" : " on Gloom Pro";
|
|
301
|
+
return `Gloom Cloud checks it for a signed-in account with Cloud sync${plan} and sends it to your phone`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const isResearchRequest = (request: AlertAddRequest): request is ResearchAddRequest =>
|
|
305
|
+
request.kind === "news" || request.kind === "flow" || request.kind === "iv_level" || request.kind === "iv_spike";
|
|
306
|
+
|
|
307
|
+
/** The US venue a research rule keeps: one typed and known to it, else any US venue. */
|
|
308
|
+
const RESEARCH_EXCHANGES = new Set(["NASDAQ", "NYSE", "AMEX", "ARCA", "BATS", "OTC"]);
|
|
309
|
+
|
|
120
310
|
/** `alerts add` as typed, with another price: what an error tells someone to type. */
|
|
121
311
|
function retypeCommand(symbol: string, condition: AlertCondition, price: number, exchange: string | null | undefined): string {
|
|
122
|
-
return `
|
|
312
|
+
return `gloom alerts add ${symbol} ${condition} ${price}${exchange ? ` --exchange ${exchange}` : ""}`;
|
|
123
313
|
}
|
|
124
314
|
|
|
125
315
|
/**
|
|
@@ -159,19 +349,25 @@ function priceRow(alert: AlertRule): AlertListRow {
|
|
|
159
349
|
return { ...alert, kind: "price", target: alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol };
|
|
160
350
|
}
|
|
161
351
|
|
|
162
|
-
function eventRow(rule: EventAlertRule,
|
|
352
|
+
function eventRow(rule: EventAlertRule, now: number, eventDate: string | null | undefined, check?: IvLevelCheck): AlertListRow {
|
|
163
353
|
const scheduled = readScheduledRule(rule);
|
|
164
354
|
if (!scheduled) {
|
|
165
|
-
|
|
355
|
+
const status = rule.status === "active" && rule.triggeredAt ? "triggered" : rule.status;
|
|
356
|
+
return { id: rule.id, kind: rule.kind, target: eventAlertTarget(rule), status, createdAt: rule.createdAt,
|
|
357
|
+
...check ? { lastCheck: check } : {}, ...rule.triggeredAt ? { triggeredAt: rule.triggeredAt } : {} };
|
|
166
358
|
}
|
|
167
359
|
const date = eventDate ?? null;
|
|
168
360
|
return {
|
|
169
361
|
id: rule.id,
|
|
170
362
|
kind: rule.kind,
|
|
171
|
-
target: scheduled.target.kind === "earnings" ? scheduled.target.symbol
|
|
363
|
+
target: scheduled.target.kind === "earnings" ? scheduled.target.symbol
|
|
364
|
+
: scheduled.target.kind === "release" ? calendarEventLabel(scheduled.target) : scheduled.target.event.toUpperCase(),
|
|
172
365
|
leadDays: scheduled.leadDays,
|
|
366
|
+
when: scheduled.trigger === "results" ? "results" : "before",
|
|
367
|
+
// CPI, FOMC and calendar releases also alert with their figures once they print.
|
|
368
|
+
...followsRelease(scheduled.target) ? { onRelease: true } : {},
|
|
173
369
|
eventDate: date,
|
|
174
|
-
status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays,
|
|
370
|
+
status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays, scheduledToday(scheduled.target, now), scheduled.trigger),
|
|
175
371
|
createdAt: rule.createdAt,
|
|
176
372
|
};
|
|
177
373
|
}
|
|
@@ -186,7 +382,9 @@ function textRow(row: AlertListRow): Record<string, unknown> {
|
|
|
186
382
|
target: row.target,
|
|
187
383
|
trigger: price
|
|
188
384
|
? `${row.condition} ${row.targetPrice}`
|
|
189
|
-
: typeof row.leadDays === "number"
|
|
385
|
+
: typeof row.leadDays === "number"
|
|
386
|
+
? scheduledTriggerText({ leadDays: row.leadDays, trigger: row.when === "results" ? "results" : "date", prints: row.onRelease === true })
|
|
387
|
+
: "",
|
|
190
388
|
eventDate: typeof row.leadDays === "number" ? row.eventDate ?? "not known" : "",
|
|
191
389
|
status: row.status,
|
|
192
390
|
lastCheckedPrice: row.lastCheckedPrice,
|
|
@@ -197,7 +395,7 @@ function textRow(row: AlertListRow): Record<string, unknown> {
|
|
|
197
395
|
/** The next event date of each scheduled rule: the saved one while it is still ahead, otherwise looked up. */
|
|
198
396
|
async function listEventDates(
|
|
199
397
|
rules: readonly EventAlertRule[],
|
|
200
|
-
|
|
398
|
+
now: number,
|
|
201
399
|
lookup: (targets: ScheduledEventTarget[]) => Promise<Map<string, ScheduledDateLookup>>,
|
|
202
400
|
): Promise<{ dates: Map<string, string | null>; warnings: string[] }> {
|
|
203
401
|
const dates = new Map<string, string | null>();
|
|
@@ -205,7 +403,7 @@ async function listEventDates(
|
|
|
205
403
|
for (const rule of rules) {
|
|
206
404
|
const scheduled = readScheduledRule(rule);
|
|
207
405
|
if (!scheduled) continue;
|
|
208
|
-
if (rule.eventDate && rule.eventDate >=
|
|
406
|
+
if (rule.eventDate && rule.eventDate >= scheduledToday(scheduled.target, now)) dates.set(rule.id, rule.eventDate);
|
|
209
407
|
else stale.push({ rule, target: scheduled.target });
|
|
210
408
|
}
|
|
211
409
|
if (stale.length === 0) return { dates, warnings: [] };
|
|
@@ -219,6 +417,84 @@ async function listEventDates(
|
|
|
219
417
|
return { dates, warnings: [...warnings] };
|
|
220
418
|
}
|
|
221
419
|
|
|
420
|
+
/** How far back a release is looked for: a monthly one is named in the calendar a month back before its next date is listed. */
|
|
421
|
+
const CALENDAR_LOOKBACK_DAYS = 40;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The calendar a release is named from: the stored past five weeks and the
|
|
425
|
+
* listing ahead, or the listing alone from a server that keeps no past.
|
|
426
|
+
*/
|
|
427
|
+
async function loadAlertCalendar(now: number): Promise<readonly EconEvent[]> {
|
|
428
|
+
const from = new Date(now - CALENDAR_LOOKBACK_DAYS * 86_400_000).toISOString().slice(0, 10);
|
|
429
|
+
const stored = await fetchEconCalendarFrom(from);
|
|
430
|
+
if (stored.events) return stored.events;
|
|
431
|
+
return (await loadCalendar()).data;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const quoted = (text: string) => (/\s/.test(text) ? `"${text}"` : text);
|
|
435
|
+
const weekday = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" });
|
|
436
|
+
|
|
437
|
+
/** "on Sat 2026-10-31 01:30 UTC", "on Mon 2026-10-26, all day": when a release is next listed. */
|
|
438
|
+
function releaseWhen(event: EconEvent): string {
|
|
439
|
+
const day = `on ${weekday.format(event.date)} ${dateKey(event.date)}`;
|
|
440
|
+
if (isAllDayEvent(event)) return `${day}, all day`;
|
|
441
|
+
return event.timeNote ? day : `${day} ${event.date.toISOString().slice(11, 16)} UTC`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** The release an alert was added for, as `--json` gives it. */
|
|
445
|
+
function releaseData(choice: CalendarEventChoice) {
|
|
446
|
+
return {
|
|
447
|
+
name: choice.name,
|
|
448
|
+
country: choice.country,
|
|
449
|
+
publisher: choice.publisher,
|
|
450
|
+
impact: choice.impact,
|
|
451
|
+
next: choice.next ? { at: choice.next.date.toISOString(), allDay: isAllDayEvent(choice.next) } : null,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const choiceList = (choices: readonly CalendarEventChoice[]) => choices.map((choice) => calendarEventLabel({ event: choice.name, country: choice.country }));
|
|
456
|
+
|
|
457
|
+
/** What a name read as, and what else it names in that country: said beside the alert added. */
|
|
458
|
+
function releaseReadNotes(query: string, choice: CalendarEventChoice, others: readonly CalendarEventChoice[]): string[] {
|
|
459
|
+
const notes: string[] = [];
|
|
460
|
+
if (calendarNameWords(query).join(" ") !== calendarNameWords(choice.name).join(" ")) {
|
|
461
|
+
notes.push(`Read "${query}" as ${calendarEventLabel({ event: choice.name, country: choice.country })}, ${choice.impact} impact.`);
|
|
462
|
+
}
|
|
463
|
+
if (others.length > 0) {
|
|
464
|
+
const shown = choiceList(others.slice(0, 4));
|
|
465
|
+
const more = others.length > 4 ? ` and ${others.length - 4} more` : "";
|
|
466
|
+
notes.push(`Also matching: ${shown.join(", ")}${more}. To follow one of them instead: alerts add ${quoted(others[0]!.name)} --country ${others[0]!.country}.`);
|
|
467
|
+
}
|
|
468
|
+
return notes;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Every form `alerts add` takes but a price alert's details, as a refusal ends. */
|
|
472
|
+
const ADD_FORMS = `Event alerts take ${EVENT_FORM} or a symbol with earnings (alerts add CRWV earnings --days 3); price alerts take a condition and a price (alerts add AAPL above 250).`;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Why a name could not be looked up. A single word (alerts add AAPL) may be
|
|
476
|
+
* a slip of another form as much as a release, so it also says what each
|
|
477
|
+
* form takes.
|
|
478
|
+
*/
|
|
479
|
+
function calendarUnavailableMessage(query: string, country: CountryFilter | null, error: string): string {
|
|
480
|
+
if (country || /\s/.test(query.trim())) return eventDateUnavailableMessage({ kind: "release", event: query, country: country ?? "" }, error);
|
|
481
|
+
return `Unknown event "${query}": the economic calendar could not be loaded to look it up (${error}). ${ADD_FORMS}`;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** The refusal of a name nothing in the calendar matches: the nearest names, and every form `alerts add` takes. */
|
|
485
|
+
function noReleaseMessage(query: string, country: CountryFilter | null, suggestions: readonly CalendarEventChoice[]): string {
|
|
486
|
+
const near = suggestions.length > 0 ? ` Did you mean ${choiceList(suggestions).join(", ")}?` : "";
|
|
487
|
+
return `No release in the economic calendar matches "${query}"${country ? ` in ${country.split(",").join(", ")}` : ""}.${near} `
|
|
488
|
+
+ `${ADD_FORMS} gloom econ --country <code> lists the calendar.`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** The refusal of a name listed in several countries, with the best of each. */
|
|
492
|
+
function severalCountriesMessage(query: string, choices: readonly CalendarEventChoice[]): string {
|
|
493
|
+
const shown = choiceList(choices.slice(0, 6));
|
|
494
|
+
const more = choices.length > 6 ? ` and ${choices.length - 6} more` : "";
|
|
495
|
+
return `"${query}" is listed for several countries: ${shown.join(", ")}${more}. Name the country: alerts add ${quoted(query)} --country ${choices[0]!.country}.`;
|
|
496
|
+
}
|
|
497
|
+
|
|
222
498
|
/** Looks up the next date for a new event alert, or fails saying why there is none. */
|
|
223
499
|
async function requireEventDate(
|
|
224
500
|
target: ScheduledEventTarget,
|
|
@@ -235,27 +511,39 @@ async function requireEventDate(
|
|
|
235
511
|
export const alertsCliCommand: CliCommandDef = {
|
|
236
512
|
name: "alerts",
|
|
237
513
|
aliases: ["alert"],
|
|
238
|
-
description: "List, add, and remove price alerts
|
|
514
|
+
description: "List, add, and remove price alerts, alerts before earnings and any economic calendar release (CPI, FOMC, NBS PMI), and news, options flow and IV alerts",
|
|
239
515
|
help: {
|
|
240
516
|
group: CLI_COMMAND_GROUPS.portfolios,
|
|
241
517
|
usage: [
|
|
242
518
|
"alerts list",
|
|
243
519
|
"alerts add <symbol> <above|below|crosses> <price>",
|
|
244
|
-
"alerts add <symbol> earnings [--days <n>]",
|
|
245
|
-
"alerts add <
|
|
520
|
+
"alerts add <symbol> earnings [--days <n> | results]",
|
|
521
|
+
"alerts add <calendar event> [--country <code>] [--days <n>]",
|
|
522
|
+
"alerts add [symbol] news <keyword>",
|
|
523
|
+
"alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks]",
|
|
524
|
+
"alerts add <symbol> <iv|rr> <above|below> <level>",
|
|
525
|
+
"alerts add <OCC contract> iv spike <points>",
|
|
246
526
|
"alerts delete <id>",
|
|
247
527
|
"alerts rearm <id>",
|
|
248
528
|
],
|
|
249
529
|
options: [
|
|
250
530
|
EXCHANGE_OPTION,
|
|
251
|
-
{ flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings
|
|
531
|
+
{ flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings and calendar event alerts` },
|
|
532
|
+
{ flags: "--country <code>", description: "The country of a calendar event, as econ --country takes it (CN, US, DE); a code before the name does the same (alerts add CN PMI)" },
|
|
252
533
|
],
|
|
253
534
|
examples: [
|
|
254
535
|
"alerts",
|
|
255
536
|
"alerts add AAPL above 250",
|
|
256
537
|
"alerts add SAN:EPA below 70",
|
|
257
538
|
"alerts add CRWV earnings --days 3",
|
|
539
|
+
"alerts add CRWV earnings results",
|
|
258
540
|
"alerts add CPI --days 1",
|
|
541
|
+
"alerts add \"NBS PMI\"",
|
|
542
|
+
"alerts add \"Industrial Profits\" --country CN --days 2",
|
|
543
|
+
"alerts add AAPL news buyback",
|
|
544
|
+
"alerts add NVDA flow 5m calls sweeps",
|
|
545
|
+
"alerts add IBIT iv above 50",
|
|
546
|
+
"alerts add SPY rr below -6",
|
|
259
547
|
],
|
|
260
548
|
},
|
|
261
549
|
execute: async (rawArgs, ctx) => {
|
|
@@ -264,61 +552,150 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
264
552
|
const daysValue = takeOption(args, "--days");
|
|
265
553
|
const days = daysFlag && daysValue === undefined ? null : daysValue;
|
|
266
554
|
const exchangeOption = takeOption(args, "--exchange");
|
|
555
|
+
const countryFlag = rawArgs.some((arg) => arg === "--country" || arg.startsWith("--country="));
|
|
556
|
+
const countryValue = takeOption(args, "--country");
|
|
557
|
+
const country = countryFlag && countryValue === undefined ? null : countryValue;
|
|
267
558
|
const action = args[0] ?? "list";
|
|
268
|
-
const
|
|
559
|
+
const now = Date.now();
|
|
560
|
+
const today = newYorkToday(new Date(now));
|
|
269
561
|
|
|
270
562
|
const parseAdd = (): AlertAddRequest => {
|
|
271
563
|
try {
|
|
272
|
-
return parseAlertAddArgs(args.slice(1), days, exchangeOption);
|
|
564
|
+
return parseAlertAddArgs(args.slice(1), days, exchangeOption, country);
|
|
273
565
|
} catch (error) {
|
|
274
566
|
return ctx.fail(error instanceof Error ? error.message : String(error));
|
|
275
567
|
}
|
|
276
568
|
};
|
|
277
569
|
const request = action === "add" ? parseAdd() : null;
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
570
|
+
const research = request && isResearchRequest(request) ? request : null;
|
|
571
|
+
const listedSymbol = request?.kind === "price" ? request.symbol
|
|
572
|
+
: research && research.kind !== "iv_spike" ? research.symbol : null;
|
|
573
|
+
// A price alert is kept for the listing `quote` prints for the symbol, and for no symbol that quotes nothing;
|
|
574
|
+
// a news, flow or IV rule on a symbol follows that listing too, and only in the US, where their data is.
|
|
575
|
+
const priced = listedSymbol
|
|
576
|
+
? await withMarketData(ctx, async (market) => {
|
|
577
|
+
const quoted = await requireQuotedListing(listedSymbol, exchangeOption, market, ctx, "alerts add");
|
|
578
|
+
if (research) {
|
|
579
|
+
const venue = quoted.listing.exchange || canonicalExchange(quoted.quote.listingExchangeName || quoted.quote.exchangeName);
|
|
580
|
+
if (!isUsListingExchange(venue)) {
|
|
581
|
+
const what = research.kind === "news" ? "News alerting" : research.kind === "flow" ? "Options flow" : "Options data";
|
|
582
|
+
const { message, details } = await usListingsOnlyMessage(what, quoted.listing, market,
|
|
583
|
+
(key) => `gloom alerts add ${key} ${args.slice(2).join(" ")}`);
|
|
584
|
+
ctx.fail(message, details);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return quoted;
|
|
588
|
+
})
|
|
281
589
|
: null;
|
|
282
590
|
const priceListing = priced?.listing ?? null;
|
|
283
591
|
if (request?.kind === "price" && request.thousandsDot && priced) {
|
|
284
592
|
const question = thousandsDotMessage(request, priced.listing.key, lastPrice(priced.quote), exchangeOption);
|
|
285
593
|
if (question) ctx.fail(question);
|
|
286
594
|
}
|
|
595
|
+
// A bare coin ticker read as a fund (BTC, the Grayscale trust) gets `quote`'s note naming the coin's pair.
|
|
596
|
+
const coinNote = priced && !priced.listing.exchange ? notTheCoinNote(priced.listing.symbol, priced.quote) : null;
|
|
597
|
+
if (request?.kind === "price" && priced) {
|
|
598
|
+
// A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
|
|
599
|
+
// Against the price the alert is checked on, as Add Alert measures it.
|
|
600
|
+
const far = farTargetMessage(priced.listing.key, request.price, priced.quote.price, coinNote);
|
|
601
|
+
if (far) ctx.fail(far);
|
|
602
|
+
}
|
|
287
603
|
// What a typed listing was read as (7203:TSE as 7203:JPX, MXN as MXN:FWB2), said beside what was added.
|
|
288
|
-
let read = priced?.notes ?? [];
|
|
604
|
+
let read = [...priced?.notes ?? [], ...coinNote ? [coinNote] : []];
|
|
289
605
|
let newEventRule: EventAlertRule | null = null;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
606
|
+
let newResearchRule: EventAlertRule | null = null;
|
|
607
|
+
if (research) {
|
|
608
|
+
const typed = priceListing?.exchange ? canonicalExchange(priceListing.exchange) : "";
|
|
609
|
+
const { kind, values } = researchFormValues(
|
|
610
|
+
research.kind === "iv_spike" ? research : { ...research, symbol: priceListing?.symbol ?? null } as ResearchAddRequest,
|
|
611
|
+
RESEARCH_EXCHANGES.has(typed) ? typed : "US",
|
|
612
|
+
);
|
|
613
|
+
try {
|
|
614
|
+
newResearchRule = createResearchAlert(kind, values);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// A release named from the calendar, with what the words were read as and what else they name.
|
|
620
|
+
let newRelease: CalendarEventChoice | null = null;
|
|
621
|
+
// CPI and FOMC, typed so or named by one of their calendar rows (CPI y/y, Fed Interest Rate Decision).
|
|
622
|
+
let macro: { event: MacroAlertEvent; leadDays: number } | null = request?.kind === "macro" ? request : null;
|
|
623
|
+
let calendar: readonly EconEvent[] | null = null;
|
|
624
|
+
if (request?.kind === "release") {
|
|
625
|
+
const events = await loadAlertCalendar(now).catch((error: unknown) =>
|
|
626
|
+
ctx.fail(calendarUnavailableMessage(request.query, request.country, error instanceof Error ? error.message : String(error))));
|
|
627
|
+
calendar = events;
|
|
628
|
+
const match = matchCalendarEvent(request.query, request.country, events, now);
|
|
629
|
+
if (match.kind !== "match") {
|
|
630
|
+
return ctx.fail(match.kind === "none"
|
|
631
|
+
? noReleaseMessage(request.query, request.country, match.suggestions)
|
|
632
|
+
: severalCountriesMessage(request.query, match.choices));
|
|
633
|
+
}
|
|
634
|
+
const { choice, others } = match;
|
|
635
|
+
const macroEvent = macroEventNamed(choice);
|
|
636
|
+
if (macroEvent) {
|
|
637
|
+
macro = { event: macroEvent, leadDays: request.leadDays };
|
|
638
|
+
read = [`Read "${request.query}" as the ${MACRO_EVENT_NAMES[macroEvent]} alert, which follows ${calendarEventLabel({ event: choice.name, country: choice.country })}.`];
|
|
639
|
+
} else {
|
|
640
|
+
newRelease = choice;
|
|
641
|
+
read = releaseReadNotes(request.query, choice, others);
|
|
642
|
+
const target: ScheduledEventTarget = { kind: "release", event: choice.name, country: choice.country, ...(choice.publisher ? { publisher: choice.publisher } : {}) };
|
|
643
|
+
try {
|
|
644
|
+
newEventRule = createScheduledAlert(target, request.leadDays, choice.next ? dateKey(choice.next.date) : null);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// When CPI or FOMC came out earlier today: its figures are not sent to an alert set after them.
|
|
651
|
+
let macroOutAt: number | null = null;
|
|
652
|
+
const earnings = request?.kind === "earnings" ? request : null;
|
|
653
|
+
if (macro || earnings) {
|
|
654
|
+
const target: ScheduledEventTarget = macro
|
|
655
|
+
? { kind: "macro", event: macro.event }
|
|
656
|
+
: await requireListingArg(earnings!.symbol, exchangeOption, ctx)
|
|
294
657
|
.then((listing) => {
|
|
295
658
|
read = listingReadNotes(listing);
|
|
296
659
|
return { kind: "earnings" as const, symbol: listing.symbol, exchange: listing.exchange };
|
|
297
660
|
});
|
|
661
|
+
const lead = earnings?.results ? "results" as const : (macro ?? earnings!).leadDays;
|
|
298
662
|
// A listing the rule cannot follow fails before any request.
|
|
299
663
|
try {
|
|
300
|
-
createScheduledAlert(target,
|
|
664
|
+
createScheduledAlert(target, lead, null);
|
|
301
665
|
} catch (error) {
|
|
302
666
|
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
303
667
|
}
|
|
304
668
|
const eventDate = target.kind === "earnings"
|
|
305
669
|
? await withMarketData(ctx, (market) => requireEventDate(target, scheduledEventSources(market.dataProvider), today, (message) => ctx.fail(message)))
|
|
306
670
|
: await requireEventDate(target, scheduledEventSources(null), today, (message) => ctx.fail(message));
|
|
307
|
-
newEventRule = createScheduledAlert(target,
|
|
671
|
+
newEventRule = createScheduledAlert(target, lead, eventDate);
|
|
672
|
+
if (target.kind === "macro" && eventDate === today) {
|
|
673
|
+
const events = calendar ?? await loadAlertCalendar(now).catch(() => {
|
|
674
|
+
// Intentional ignore: without the calendar the alert is added as before, only without saying today's release is out.
|
|
675
|
+
return null;
|
|
676
|
+
});
|
|
677
|
+
const row = events ? releaseRowOn(target, eventDate, events) : null;
|
|
678
|
+
const at = row ? releaseInstant(row) : null;
|
|
679
|
+
if (at != null && at <= now) macroOutAt = at;
|
|
680
|
+
}
|
|
308
681
|
}
|
|
309
682
|
|
|
310
683
|
if (action === "list") {
|
|
311
|
-
|
|
312
|
-
|
|
684
|
+
// An IV level's last reading is this device's, beside the rules.
|
|
685
|
+
const { alerts, alertsError, events, checks } = await withConfigData(ctx, (config) => ({
|
|
686
|
+
...readAlertStores(config.config),
|
|
687
|
+
checks: readIvLevelChecks(config.persistence?.pluginState?.get(ALERTS_PLUGIN_ID, resumeStateKey(IV_LEVEL_CHECKS_KEY))?.value),
|
|
688
|
+
}));
|
|
689
|
+
// Earnings dates come through market data; CPI, FOMC and calendar dates do not need it.
|
|
313
690
|
const lookup = (targets: ScheduledEventTarget[]) => targets.some((target) => target.kind === "earnings")
|
|
314
|
-
? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today))
|
|
315
|
-
: lookupScheduledDates(targets, scheduledEventSources(null), today);
|
|
316
|
-
const { dates, warnings } = await listEventDates(events.rules,
|
|
691
|
+
? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today, now))
|
|
692
|
+
: lookupScheduledDates(targets, scheduledEventSources(null), today, now);
|
|
693
|
+
const { dates, warnings } = await listEventDates(events.rules, now, lookup);
|
|
317
694
|
const rows = [
|
|
318
695
|
...alerts.map(priceRow),
|
|
319
|
-
...events.rules.map((rule) => eventRow(rule,
|
|
696
|
+
...events.rules.map((rule) => eventRow(rule, now, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate, checks[rule.id])),
|
|
320
697
|
];
|
|
321
|
-
const allWarnings = [...(events.error ? [events.error] : []), ...warnings];
|
|
698
|
+
const allWarnings = [...(alertsError ? [alertsError] : []), ...(events.error ? [events.error] : []), ...warnings];
|
|
322
699
|
ctx.printResult({ data: rows, ...(allWarnings.length ? { warnings: allWarnings } : {}) }, {
|
|
323
700
|
rows: (data) => data.map(textRow),
|
|
324
701
|
textColumns: [
|
|
@@ -331,14 +708,15 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
331
708
|
{ key: "lastCheckedPrice", header: "Last Price", align: "right", optional: true },
|
|
332
709
|
{ key: "triggeredAt", header: "Triggered", optional: true },
|
|
333
710
|
],
|
|
334
|
-
empty: "No alerts. Add one with
|
|
711
|
+
empty: "No alerts. Add one with gloom alerts add AAPL above 250, alerts add CRWV earnings --days 3, alerts add CPI --days 1, or alerts add CN PMI.",
|
|
335
712
|
});
|
|
336
713
|
return;
|
|
337
714
|
}
|
|
338
715
|
|
|
339
716
|
await withConfigData(ctx, async (config) => {
|
|
340
|
-
const { pluginState, alerts, events } = readAlertStores(config.config);
|
|
717
|
+
const { pluginState, alerts, alertsError, events } = readAlertStores(config.config);
|
|
341
718
|
const save = async (next: { alerts?: AlertRule[]; events?: EventAlertRule[] }) => {
|
|
719
|
+
if (next.alerts && alertsError) ctx.fail(alertsError);
|
|
342
720
|
const nextConfig = {
|
|
343
721
|
...config.config,
|
|
344
722
|
pluginConfig: {
|
|
@@ -346,7 +724,8 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
346
724
|
[ALERTS_PLUGIN_ID]: {
|
|
347
725
|
...pluginState,
|
|
348
726
|
...(next.alerts ? { [ALERTS_KEY]: serializeAlerts(next.alerts) } : {}),
|
|
349
|
-
|
|
727
|
+
// Each list keeps what it holds of kinds this version does not know; IV levels go in their own.
|
|
728
|
+
...(next.events ? writeEventAlertStores(events.lists, next.events) : {}),
|
|
350
729
|
},
|
|
351
730
|
},
|
|
352
731
|
};
|
|
@@ -369,30 +748,57 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
369
748
|
});
|
|
370
749
|
return;
|
|
371
750
|
}
|
|
751
|
+
if (newResearchRule) {
|
|
752
|
+
const rule = newResearchRule;
|
|
753
|
+
const rules = requireEvents();
|
|
754
|
+
const existing = rules.find((entry) => entry.kind === rule.kind && entry.target === rule.target && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
755
|
+
if (existing) ctx.fail(`This alert already exists (ID ${existing.id}).`);
|
|
756
|
+
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
757
|
+
await save({ events: [...rules, rule] });
|
|
758
|
+
const kind = research!.kind;
|
|
759
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
760
|
+
text: (data) => `Added alert ${data.alert.id}: ${alertKindLabel(rule.kind)} ${researchAlertDescription(rule)} (${researchDelivery(kind)}).${dryRunNote(data.dryRun)}`,
|
|
761
|
+
});
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
372
764
|
if (newEventRule) {
|
|
373
765
|
const rule = newEventRule;
|
|
374
766
|
const rules = requireEvents();
|
|
375
767
|
const scheduled = readScheduledRule(rule)!;
|
|
376
768
|
const name = scheduledEventName(scheduled.target);
|
|
377
769
|
const existing = rules.find((entry) => entry.kind === rule.kind && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
378
|
-
if (existing) ctx.fail(
|
|
379
|
-
|
|
770
|
+
if (existing) ctx.fail(scheduled.trigger === "results"
|
|
771
|
+
? `An alert for ${name} results already exists (ID ${existing.id}).`
|
|
772
|
+
: `An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}).`);
|
|
773
|
+
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
380
774
|
await save({ events: [...rules, rule] });
|
|
381
|
-
const eventDate = rule.eventDate
|
|
382
|
-
const due = scheduledPhase(eventDate, scheduled.leadDays, undefined,
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
775
|
+
const eventDate = rule.eventDate ?? null;
|
|
776
|
+
const due = !!eventDate && scheduledPhase(eventDate, scheduled.leadDays, undefined, scheduledToday(scheduled.target, now)) === "due";
|
|
777
|
+
const release = followsRelease(scheduled.target);
|
|
778
|
+
// A release that prints no figures (a plenum, a speech) alerts only before it.
|
|
779
|
+
const prints = release && (!newRelease?.next || expectsFigures(newRelease.next));
|
|
780
|
+
const delivery = release ? ", only while it is running" : " while it is running, or on your phone with Cloud sync";
|
|
781
|
+
const when = !eventDate ? null : newRelease?.next ? releaseWhen(newRelease.next) : `on ${eventDate}`;
|
|
782
|
+
const reminder = !eventDate
|
|
783
|
+
? "the app alerts once the calendar lists its next date, about two weeks ahead"
|
|
784
|
+
: macroOutAt != null ? `it came out today at ${new Date(macroOutAt).toISOString().slice(11, 16)} UTC, so the app alerts before the next one`
|
|
785
|
+
: due ? "already reached, so the app alerts at its next check" : `the app alerts on ${leadStartDate(eventDate, scheduled.leadDays)}`;
|
|
786
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule,
|
|
787
|
+
...newRelease ? { release: releaseData(newRelease) } : {} }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
788
|
+
// Delivery, as docs/usage.md states it: CPI, FOMC and calendar releases notify in the running app only; earnings also reach the phone,
|
|
789
|
+
// which Gloom Cloud alerts with the same results once the actual EPS is stored for a results alert.
|
|
790
|
+
text: (data) => scheduled.trigger === "results"
|
|
791
|
+
? `Added alert ${data.alert.id}: ${name} on ${eventDate}, when results are out (the app alerts once the actual EPS is in while it is running, and with Cloud sync your phone gets the same alert).${dryRunNote(data.dryRun)}`
|
|
792
|
+
: `Added alert ${data.alert.id}: ${name}${when ? ` ${when}` : ""}, ${plural(scheduled.leadDays, "day")} before${prints ? " and when it prints" : ""} (${reminder}${
|
|
793
|
+
prints ? ", and with the actual, forecast and prior once out" : ""}${delivery}).${dryRunNote(data.dryRun)}`,
|
|
389
794
|
});
|
|
390
795
|
return;
|
|
391
796
|
}
|
|
392
797
|
if (action === "delete" || action === "rm") {
|
|
393
|
-
const id = requireArg(args[1], "Usage:
|
|
798
|
+
const id = requireArg(args[1], "Usage: gloom alerts delete <id>", ctx);
|
|
394
799
|
const isPrice = alerts.some((alert) => alert.id === id);
|
|
395
800
|
const isEvent = !isPrice && events.rules.some((rule) => rule.id === id);
|
|
801
|
+
if (!isPrice && !isEvent && alertsError) ctx.fail(alertsError);
|
|
396
802
|
if (isPrice) await save({ alerts: alerts.filter((alert) => alert.id !== id) });
|
|
397
803
|
if (isEvent) await save({ events: requireEvents().filter((rule) => rule.id !== id) });
|
|
398
804
|
const changed = isPrice || isEvent;
|
|
@@ -404,13 +810,16 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
404
810
|
return;
|
|
405
811
|
}
|
|
406
812
|
if (action === "rearm") {
|
|
407
|
-
const id = requireArg(args[1], "Usage:
|
|
813
|
+
const id = requireArg(args[1], "Usage: gloom alerts rearm <id>", ctx);
|
|
408
814
|
const isPrice = alerts.some((alert) => alert.id === id);
|
|
409
815
|
const rule = isPrice ? null : events.rules.find((entry) => entry.id === id) ?? null;
|
|
816
|
+
if (!isPrice && !rule && alertsError) ctx.fail(alertsError);
|
|
410
817
|
if (isPrice) await save({ alerts: alerts.map((alert) => alert.id === id ? rearmAlert(alert) : alert) });
|
|
411
818
|
if (rule) {
|
|
412
819
|
// A scheduled alert alerts again for its coming event; any other event alert resumes.
|
|
413
|
-
const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
|
|
820
|
+
const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
|
|
821
|
+
: rule.kind === "iv_level" && rule.triggeredAt ? rearmIvLevelAlert(rule)
|
|
822
|
+
: rule.status === "paused" ? toggleEventAlert(rule) : rule;
|
|
414
823
|
await save({ events: requireEvents().map((entry) => entry.id === id ? next : entry) });
|
|
415
824
|
}
|
|
416
825
|
const found = isPrice || !!rule;
|
|
@@ -421,7 +830,7 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
421
830
|
});
|
|
422
831
|
return;
|
|
423
832
|
}
|
|
424
|
-
ctx.fail("Usage:
|
|
833
|
+
ctx.fail("Usage: gloom alerts list|add|delete|rearm");
|
|
425
834
|
});
|
|
426
835
|
},
|
|
427
836
|
};
|