gloomberb 0.18.3 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/README.zh-CN.md +11 -11
- package/bin/{gloomberb → gloom} +3 -1
- package/package.json +6 -2
- package/patches/electrobun@1.18.1.patch +32 -1
- package/src/account/auth-model.ts +7 -3
- 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 +11 -6
- package/src/api-client/catalysts.ts +2 -1
- 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 +98 -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 +182 -16
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +14 -0
- package/src/api-client/members.ts +18 -0
- package/src/api-client/mna.ts +6 -0
- package/src/api-client/paths.ts +18 -1
- package/src/api-client/request.ts +224 -26
- package/src/api-client/research-activity.ts +3 -10
- 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 +96 -2
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -2
- package/src/app/runtime/broker-import.ts +43 -26
- 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 +9 -2
- package/src/cli/commands/keybindings.ts +25 -2
- package/src/cli/commands/market.ts +1177 -278
- package/src/cli/commands/news-hints.ts +2 -2
- package/src/cli/commands/news-search.ts +59 -6
- package/src/cli/commands/options-chain.ts +59 -3
- package/src/cli/commands/options-leaps.ts +41 -26
- package/src/cli/commands/overview.ts +222 -61
- package/src/cli/commands/plugins.ts +9 -9
- package/src/cli/commands/remote.ts +13 -10
- package/src/cli/commands/search.ts +82 -46
- package/src/cli/commands/system.ts +27 -19
- package/src/cli/commands/themes.ts +1 -1
- package/src/cli/commands/ticker.ts +194 -48
- 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 +21 -3
- package/src/cli/exit-codes.ts +1 -1
- package/src/cli/export-source.ts +38 -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 +113 -20
- package/src/cli/help.ts +19 -13
- package/src/cli/history-export.ts +126 -0
- package/src/cli/history-rows.ts +226 -50
- package/src/cli/index-roots.ts +5 -45
- package/src/cli/index.ts +24 -18
- package/src/cli/known-gaps.ts +123 -27
- package/src/cli/listing-arg.ts +316 -46
- 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 +193 -30
- package/src/cli/pane-functions/function-help.ts +76 -30
- package/src/cli/pane-functions/headless.ts +59 -12
- package/src/cli/pane-functions/index.ts +28 -19
- package/src/cli/pane-functions/market-session.ts +15 -5
- 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 +10 -6
- package/src/cli/pane-functions/resolver.ts +64 -11
- 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 +45 -4
- package/src/cli/report-tables.ts +75 -19
- package/src/cli/restore-plugins.ts +42 -15
- package/src/cli/result.ts +164 -23
- 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 +395 -20
- package/src/components/chart/composite/format.ts +27 -0
- 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 +7 -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-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +314 -13
- package/src/components/chart/composite/price-axis-labels.tsx +27 -3
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +44 -19
- package/src/components/chart/composite/scene.ts +20 -4
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- 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 +17 -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/panel/prompt-binding.ts +0 -8
- 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/alert.ts +1 -1
- 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 +33 -46
- 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/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +38 -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-frame.tsx +1 -1
- 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/portfolio-step/positions-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 +10 -2
- 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 +6 -4
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- 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 +123 -0
- package/src/docs/snapshot.json +422 -112
- package/src/i18n/es.ts +30 -13
- package/src/i18n/ja.ts +30 -13
- package/src/i18n/ko.ts +30 -13
- package/src/i18n/zh-cn.ts +30 -13
- package/src/i18n/zh-tw.ts +30 -13
- 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/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- 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 +81 -11
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/freshness.ts +34 -5
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/period-return.ts +163 -0
- package/src/market-data/market/status.ts +16 -4
- 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/options-alternatives.ts +60 -0
- 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 +25 -3
- 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 +7 -2
- package/src/market-data/selectors.ts +3 -0
- 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/notifications/notification-log.ts +6 -4
- 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 +34 -20
- package/src/plugins/builtin/alerts/calendar-events.ts +429 -0
- package/src/plugins/builtin/alerts/cli.ts +533 -85
- package/src/plugins/builtin/alerts/command.ts +12 -10
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- 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 +119 -0
- package/src/plugins/builtin/alerts/format.ts +10 -3
- package/src/plugins/builtin/alerts/headless.ts +107 -41
- package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
- package/src/plugins/builtin/alerts/history-rows.ts +92 -0
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +107 -30
- package/src/plugins/builtin/alerts/iv-level.ts +365 -0
- package/src/plugins/builtin/alerts/levels.ts +3 -3
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +61 -0
- package/src/plugins/builtin/alerts/metric-data.ts +70 -0
- package/src/plugins/builtin/alerts/metric-model.ts +175 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +130 -49
- 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 +38 -12
- package/src/plugins/builtin/alerts/targets.ts +12 -3
- package/src/plugins/builtin/alerts/types.ts +3 -0
- 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 +13 -8
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -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 +24 -14
- package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
- package/src/plugins/builtin/broker-manager/model.ts +18 -3
- 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 +66 -24
- 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 +19 -1
- package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
- package/src/plugins/builtin/broker-trading/order-feed.ts +83 -10
- 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 +111 -24
- 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 +2 -2
- 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 +51 -5
- 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 +4 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +3 -2
- package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +7 -10
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -3
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -0
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +9 -4
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +42 -29
- package/src/plugins/builtin/chart-composer/headless.ts +266 -37
- 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 +144 -30
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- 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 +31 -28
- 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 +44 -31
- 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/command-suggestions.ts +92 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +51 -22
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +265 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +9 -2
- package/src/plugins/builtin/chat/people.ts +1 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -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/plugin.tsx +1 -1
- 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 +144 -27
- package/src/plugins/builtin/econ/detail-view.tsx +48 -10
- package/src/plugins/builtin/econ/figures.ts +72 -0
- 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 +144 -27
- package/src/plugins/builtin/econ/print-watch.ts +2 -1
- package/src/plugins/builtin/econ/types.ts +34 -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 +87 -28
- 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 +104 -0
- package/src/plugins/builtin/econ-statistics/view.ts +19 -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 +47 -12
- 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/charts.tsx +2 -2
- 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 +20 -1
- package/src/plugins/builtin/exposure/pane.tsx +10 -5
- 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/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +12 -10
- package/src/plugins/builtin/futures/returns.ts +8 -36
- package/src/plugins/builtin/futures/table.tsx +3 -3
- 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 +20 -8
- package/src/plugins/builtin/fx-matrix/headless.ts +37 -16
- package/src/plugins/builtin/fx-matrix/index.tsx +25 -13
- package/src/plugins/builtin/fx-matrix/live-legs.ts +34 -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/help/keybindings-editor.tsx +18 -14
- 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 +7 -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 +51 -13
- 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/footer.ts +2 -2
- 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/breadth-pane.tsx +81 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +24 -10
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +31 -14
- 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/analytics.ts +26 -2
- package/src/plugins/builtin/options/carry.ts +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -1
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +1 -7
- package/src/plugins/builtin/options/view.tsx +30 -13
- package/src/plugins/builtin/options-calculator/headless.ts +46 -7
- package/src/plugins/builtin/options-positioning/client.ts +19 -0
- package/src/plugins/builtin/options-positioning/headless.ts +54 -14
- package/src/plugins/builtin/options-positioning/index.tsx +3 -3
- package/src/plugins/builtin/options-positioning/model.ts +47 -8
- package/src/plugins/builtin/options-positioning/pane.tsx +68 -38
- 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 +14 -9
- 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 +168 -0
- package/src/plugins/builtin/perps/markets-section.tsx +154 -0
- package/src/plugins/builtin/perps/pane.tsx +49 -26
- 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 +54 -22
- 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 +7 -4
- 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/index.tsx +11 -8
- 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/rate-path/pane.tsx +2 -2
- 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 +80 -13
- 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/feed.ts +2 -2
- package/src/plugins/builtin/scanner/index.ts +24 -1
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +114 -0
- package/src/plugins/builtin/scanner/live-model.ts +351 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- 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 +3 -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/macro-releases.ts +65 -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/scanner-capture.ts +3 -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/shared/use-quote-board.ts +8 -2
- package/src/plugins/builtin/shared/volatility/pricing.ts +7 -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 +11 -6
- package/src/plugins/builtin/themes/model.ts +15 -5
- package/src/plugins/builtin/themes/pane.tsx +27 -8
- 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 +14 -4
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +7 -4
- package/src/plugins/builtin/vol-surface/headless.ts +71 -13
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +26 -9
- package/src/plugins/builtin/vol-surface/pane.tsx +44 -7
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/model.ts +5 -10
- package/src/plugins/builtin/volatility/pane.tsx +2 -2
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- 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 +15 -9
- 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 +15 -13
- 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 +15 -0
- package/src/public/time-series.ts +11 -8
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +5 -55
- package/src/renderers/browser/main.tsx +30 -60
- 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/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +56 -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/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +70 -3
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
- package/src/renderers/electrobun/bun/index.ts +36 -2
- 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/app-name.ts +17 -0
- 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/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/config-host.ts +3 -0
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -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/opentui/toast-host.tsx +71 -43
- 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 +16 -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/batches.ts +10 -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 +58 -15
- package/src/sources/provider-router/index.ts +146 -10
- package/src/sources/provider-router/primary.ts +1 -1
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/bootstrap.ts +23 -0
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/hooks/inline-tickers.ts +30 -5
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +67 -17
- package/src/sync/core-contributors.ts +46 -14
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- 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/telemetry/usage-counts.ts +0 -18
- package/src/test-support/builtin-panes.ts +1 -1
- package/src/theme/colors.ts +11 -30
- package/src/theme/ui-tokens.ts +37 -1
- package/src/tickers/company-name-listing.ts +210 -33
- package/src/tickers/company-name.ts +59 -0
- package/src/tickers/index-roots.ts +47 -0
- package/src/tickers/instrument-label.ts +5 -1
- package/src/tickers/listing-venues.ts +23 -0
- package/src/tickers/metadata.ts +10 -0
- package/src/tickers/perpetual-listing.ts +5 -2
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/home-listing.ts +75 -0
- package/src/tickers/search/index.ts +111 -33
- package/src/tickers/search/ranking.ts +64 -5
- package/src/tickers/search/types.ts +5 -0
- 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 +315 -0
- package/src/time-series/chart-bounds.ts +349 -0
- package/src/time-series/chart-seed.ts +142 -0
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +5 -0
- package/src/time-series/field-catalog.ts +1 -3
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +173 -10
- package/src/time-series/history-acquisition.ts +499 -0
- package/src/time-series/hooks.ts +1 -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/price-units.ts +72 -2
- package/src/time-series/range.ts +9 -1
- package/src/time-series/resolution.ts +174 -16
- package/src/time-series/resolve-context.ts +161 -0
- package/src/time-series/resolve.ts +185 -1587
- package/src/time-series/series-name.ts +38 -0
- package/src/time-series/series-presentation.ts +242 -0
- package/src/time-series/spec.ts +13 -7
- package/src/time-series/studies.ts +377 -82
- package/src/time-series/tail-reconcile.ts +121 -0
- package/src/time-series/transforms.ts +1 -0
- package/src/time-series/types.ts +34 -2
- package/src/time-series/use-chart-resolution.ts +4 -5
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/broker.ts +10 -0
- package/src/types/config.ts +14 -0
- package/src/types/data-provider.ts +23 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/exchange-rate.ts +6 -0
- package/src/types/financials.ts +145 -8
- package/src/types/headless.ts +117 -9
- package/src/types/instrument.ts +6 -0
- package/src/types/plugin.ts +66 -9
- 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 +180 -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 +89 -5
- 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 +86 -0
- package/src/utils/option-expiry.ts +186 -7
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-earnings.ts +11 -5
- package/src/utils/price-history.ts +23 -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
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -1,25 +1,50 @@
|
|
|
1
|
+
import { metricDescription } from "./metric-model";
|
|
2
|
+
import { parseMetricAlert, type MetricAlertInput } from "./metric-command";
|
|
3
|
+
import { metricReadings } from "./metric-data";
|
|
1
4
|
import { withConfigData, withMarketData } from "../../../cli/scoped-context";
|
|
2
5
|
import { CLI_COMMAND_GROUPS } from "../../../cli/help";
|
|
3
6
|
import { dryRunNote } from "../../../cli/helpers";
|
|
4
7
|
import { requireArg, takeOption } from "../../../cli/commands/command-utils";
|
|
5
8
|
import { requireQuotedListing } from "../../../cli/quote-answer";
|
|
6
|
-
import { EXCHANGE_OPTION, listingReadNotes, requireListingArg } from "../../../cli/listing-arg";
|
|
9
|
+
import { EXCHANGE_OPTION, listingReadNotes, requireListingArg, usListingsOnlyMessage } from "../../../cli/listing-arg";
|
|
10
|
+
import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
|
|
7
11
|
import { saveConfig } from "../../../data/config/store";
|
|
12
|
+
import { resumeStateKey } from "../../registry/plugin-state";
|
|
8
13
|
import type { CliCommandDef } from "../../../types/plugin";
|
|
9
14
|
import type { Quote } from "../../../types/financials";
|
|
10
15
|
import { getActiveQuoteDisplay } from "../../../market-data/market/status";
|
|
11
16
|
import { cliStyles } from "../../../utils/cli-output";
|
|
12
|
-
import {
|
|
17
|
+
import { canonicalExchange, isUsListingExchange } from "../../../utils/exchanges";
|
|
18
|
+
import { meantThousandsDot, parsePastedNumber, thousandsDotReadings, type ThousandsDotReadings } from "../../../utils/decimal-comma";
|
|
13
19
|
import { newYorkToday } from "../earnings/board-model";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
20
|
+
import { fetchEconCalendarFrom } from "../econ/calendar-source";
|
|
21
|
+
import { COUNTRY_FILTER_HINT, dateKey, loadCalendar, parseCountryFilter, type CountryFilter } from "../econ/calendar-model";
|
|
22
|
+
import { isAllDayEvent, type EconEvent } from "../econ/types";
|
|
23
|
+
import {
|
|
24
|
+
calendarEventLabel,
|
|
25
|
+
calendarNameWords,
|
|
26
|
+
expectsFigures,
|
|
27
|
+
macroEventNamed,
|
|
28
|
+
matchCalendarEvent,
|
|
29
|
+
releaseInstant,
|
|
30
|
+
releaseRenameNote,
|
|
31
|
+
releaseRowOn,
|
|
32
|
+
type CalendarEventChoice,
|
|
33
|
+
type ReleaseRename,
|
|
34
|
+
} from "./calendar-events";
|
|
35
|
+
import { createAlert, rearmAlert } from "./alert-engine";
|
|
36
|
+
import { ALERTS_PLUGIN_ID } from "./constants";
|
|
37
|
+
import { MAX_EVENT_ALERTS, eventAlertTarget, toggleEventAlert, writeEventAlertStores, type EventAlertRule } from "./events";
|
|
17
38
|
import { alertKindLabel } from "./history";
|
|
18
|
-
import {
|
|
39
|
+
import { IV_LEVEL_CHECKS_KEY, readIvLevelChecks, rearmIvLevelAlert, type IvLevelCheck } from "./iv-level";
|
|
40
|
+
import { parseAlertPrice, readAlertPrice } from "./price";
|
|
41
|
+
import { farTargetMessage } from "./quotes";
|
|
42
|
+
import { MACRO_EVENT_NAMES, createResearchAlert, formatPremium, ivLevelUnitError, parseAmount, parseIvLevel, researchAlertDescription } from "./research-builder";
|
|
43
|
+
import { IV_LEVEL_RANGES, MAX_LEAD_DAYS, type IvLevelMetric, type MacroAlertEvent } from "./research-rules";
|
|
19
44
|
import {
|
|
20
|
-
MACRO_EVENT_CLI_NAMES,
|
|
21
45
|
createScheduledAlert,
|
|
22
46
|
eventDateUnavailableMessage,
|
|
47
|
+
followsRelease,
|
|
23
48
|
leadStartDate,
|
|
24
49
|
lookupScheduledDates,
|
|
25
50
|
noUpcomingEventMessage,
|
|
@@ -31,12 +56,14 @@ import {
|
|
|
31
56
|
scheduledEventName,
|
|
32
57
|
scheduledPhase,
|
|
33
58
|
scheduledTargetKey,
|
|
59
|
+
scheduledToday,
|
|
60
|
+
scheduledTriggerText,
|
|
34
61
|
type ScheduledDateLookup,
|
|
35
62
|
type ScheduledEventSources,
|
|
36
63
|
type ScheduledEventTarget,
|
|
37
64
|
} from "./scheduled";
|
|
38
65
|
import { scheduledEventSources } from "./scheduled-sources";
|
|
39
|
-
import { readAlertStores } from "./storage";
|
|
66
|
+
import { readAlertStores, writeLevelAlertStores } from "./storage";
|
|
40
67
|
import type { AlertCondition, AlertRule } from "./types";
|
|
41
68
|
|
|
42
69
|
/**
|
|
@@ -48,58 +75,127 @@ import type { AlertCondition, AlertRule } from "./types";
|
|
|
48
75
|
*/
|
|
49
76
|
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
77
|
|
|
51
|
-
const ADD_USAGE = "Usage:
|
|
78
|
+
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
79
|
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.`;
|
|
80
|
+
/** The event form every refusal of `alerts add` names: any release in the economic calendar, CPI and FOMC among them. */
|
|
81
|
+
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)`;
|
|
82
|
+
const COUNTRY_RULE = "--country is for an alert on a calendar event, such as alerts add PMI --country CN.";
|
|
53
83
|
|
|
54
84
|
/** What `alerts add` was asked for, before any listing or date is looked up. */
|
|
55
85
|
export type AlertAddRequest =
|
|
86
|
+
| ({ kind: "metric" } & MetricAlertInput)
|
|
56
87
|
| {
|
|
57
88
|
kind: "price"; symbol: string; condition: AlertCondition; price: number;
|
|
58
89
|
/** A price typed as 81.000, which the last price may show was 81000 with a dot between thousands. */
|
|
59
90
|
thousandsDot?: ThousandsDotReadings & { text: string };
|
|
60
91
|
}
|
|
61
|
-
|
|
62
|
-
| { kind: "
|
|
92
|
+
/** `results`: once the report's actual EPS is out, rather than `leadDays` before the date. */
|
|
93
|
+
| { kind: "earnings"; symbol: string; leadDays: number; results?: true }
|
|
94
|
+
| { kind: "macro"; event: MacroAlertEvent; leadDays: number }
|
|
95
|
+
/** A release in the economic calendar by name, matched against the calendar once it is read; `country` narrows it. */
|
|
96
|
+
| { kind: "release"; query: string; country: CountryFilter | null; leadDays: number }
|
|
97
|
+
/** 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). */
|
|
98
|
+
| { kind: "news"; symbol: string | null; keyword: string }
|
|
99
|
+
| { kind: "flow"; symbol: string | null; premium: number; side: "any" | "calls" | "puts"; print: "any" | "sweep" | "block" }
|
|
100
|
+
/** 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. */
|
|
101
|
+
| { kind: "iv_level"; symbol: string; metric: IvLevelMetric; direction: "above" | "below"; level: number }
|
|
102
|
+
/** One OCC contract's IV rising by `points`. */
|
|
103
|
+
| { kind: "iv_spike"; contract: string; points: number };
|
|
104
|
+
|
|
105
|
+
/** The `alerts add` forms Gloom Cloud's research rules take: they follow US listings. */
|
|
106
|
+
type ResearchAddRequest = Extract<AlertAddRequest, { kind: "news" | "flow" | "iv_level" | "iv_spike" }>;
|
|
63
107
|
|
|
64
108
|
const CONDITIONS: readonly AlertCondition[] = ["above", "below", "crosses"];
|
|
65
109
|
|
|
66
110
|
/** The first word of a price typed with a space between thousands and no quotes (`81 000`): one to three digits. */
|
|
67
111
|
const LEADING_GROUP = /^[+-]?\d{1,3}$/;
|
|
68
112
|
|
|
113
|
+
/** `--country` as typed: a country code or the calendar's regions (`CN`, `uk`, `EU`); null when not given. Throws when it names none. */
|
|
114
|
+
function readCountryOption(country: string | null | undefined): CountryFilter | null {
|
|
115
|
+
if (country === undefined) return null;
|
|
116
|
+
if (country === null) throw new Error(`--country takes a two-letter country code, such as --country CN. ${COUNTRY_FILTER_HINT}`);
|
|
117
|
+
const parsed = parseCountryFilter(country);
|
|
118
|
+
if ("error" in parsed) throw new Error(`${parsed.error} ${COUNTRY_FILTER_HINT}`);
|
|
119
|
+
return parsed.filter === "all" ? null : parsed.filter;
|
|
120
|
+
}
|
|
121
|
+
|
|
69
122
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* takes.
|
|
123
|
+
* A release named by its words (`NBS PMI`, `Industrial Profits`), a country
|
|
124
|
+
* code first standing for `--country` (`CN PMI`). CPI and FOMC, in the US or
|
|
125
|
+
* with no country, are the macro alerts whose dates come from their own
|
|
126
|
+
* calendars.
|
|
75
127
|
*/
|
|
76
|
-
|
|
128
|
+
function releaseRequest(args: readonly string[], country: CountryFilter | null, leadDays: number): AlertAddRequest {
|
|
129
|
+
const words = args.join(" ").trim().split(/\s+/);
|
|
130
|
+
let query = words.join(" ");
|
|
131
|
+
if (!country && words.length >= 2 && /^[a-z]{2}$/i.test(words[0]!)) {
|
|
132
|
+
const parsed = parseCountryFilter(words[0]!);
|
|
133
|
+
if (!("error" in parsed) && parsed.filter !== "all") {
|
|
134
|
+
country = parsed.filter;
|
|
135
|
+
query = words.slice(1).join(" ");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const event = parseMacroEventName(query);
|
|
139
|
+
if (event && (!country || country === "US")) return { kind: "macro", event, leadDays };
|
|
140
|
+
return { kind: "release", query, country, leadDays };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Reads `alerts add` arguments: `<symbol> above 250`, `<symbol> earnings`,
|
|
145
|
+
* `<symbol> earnings results`, or a release in the economic calendar by name
|
|
146
|
+
* (`CPI`, `FOMC`, `NBS PMI`, `CN PMI`), with `--days` (already taken out of
|
|
147
|
+
* `args`, `null` when the flag came without a value) and `--country` for the
|
|
148
|
+
* event forms only, and `--exchange`, which a command to type again keeps.
|
|
149
|
+
* Throws a message that names what the command takes.
|
|
150
|
+
*/
|
|
151
|
+
export function parseAlertAddArgs(
|
|
152
|
+
args: readonly string[],
|
|
153
|
+
days: string | null | undefined,
|
|
154
|
+
exchange?: string | null,
|
|
155
|
+
country?: string | null,
|
|
156
|
+
): AlertAddRequest {
|
|
77
157
|
const [first, second, third, ...rest] = args;
|
|
78
158
|
if (!first) throw new Error(ADD_USAGE);
|
|
159
|
+
const countryFilter = readCountryOption(country);
|
|
160
|
+
const metric = parseMetricAlert(args.join(" "));
|
|
161
|
+
if (metric) {
|
|
162
|
+
if (days !== undefined || country !== undefined) throw new Error("Valuation and daily technical alerts take neither --days nor --country.");
|
|
163
|
+
return { kind: "metric", ...metric };
|
|
164
|
+
}
|
|
165
|
+
const research = parseResearchAddArgs(args);
|
|
166
|
+
if (research) {
|
|
167
|
+
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not news, flow or IV alerts.");
|
|
168
|
+
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
169
|
+
return research;
|
|
170
|
+
}
|
|
79
171
|
const leadDays = () => {
|
|
80
172
|
const parsed = days === null ? null : parseLeadDays(days);
|
|
81
173
|
if (parsed == null) throw new Error(DAYS_RULE);
|
|
82
174
|
return parsed;
|
|
83
175
|
};
|
|
84
176
|
if (second === undefined) {
|
|
85
|
-
const event = parseMacroEventName(first);
|
|
86
|
-
if (event) return { kind: "macro", event, leadDays: leadDays() };
|
|
87
177
|
if (first.toLowerCase() === "earnings") {
|
|
88
|
-
throw new Error("Name the company:
|
|
178
|
+
throw new Error("Name the company: gloom alerts add <symbol> earnings, such as alerts add CRWV earnings --days 3.");
|
|
89
179
|
}
|
|
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
|
-
);
|
|
180
|
+
return releaseRequest(args, countryFilter, leadDays());
|
|
93
181
|
}
|
|
182
|
+
if (second.toLowerCase() === "earnings" && country !== undefined) throw new Error(COUNTRY_RULE);
|
|
94
183
|
if (second.toLowerCase() === "earnings") {
|
|
95
|
-
if (third
|
|
184
|
+
if (third?.toLowerCase() === "results" && rest.length === 0) {
|
|
185
|
+
if (days !== undefined) throw new Error("An alert for results goes off once they are out and takes no --days.");
|
|
186
|
+
return { kind: "earnings", symbol: first, leadDays: 0, results: true };
|
|
187
|
+
}
|
|
188
|
+
if (third !== undefined) throw new Error(`Earnings alerts take a symbol and --days or results, not "${[third, ...rest].join(" ")}". ${ADD_USAGE}`);
|
|
96
189
|
return { kind: "earnings", symbol: first, leadDays: leadDays() };
|
|
97
190
|
}
|
|
98
191
|
const condition = second.toLowerCase() as AlertCondition;
|
|
99
192
|
if (!CONDITIONS.includes(condition)) {
|
|
100
|
-
|
|
193
|
+
// Words with no price after them name a release (CN PMI, Industrial Profits); a price ends a mistyped price alert.
|
|
194
|
+
if (parseAlertPrice(args.at(-1)!).kind === "invalid") return releaseRequest(args, countryFilter, leadDays());
|
|
195
|
+
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
196
|
}
|
|
102
|
-
if (
|
|
197
|
+
if (country !== undefined) throw new Error(COUNTRY_RULE);
|
|
198
|
+
if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not price alerts.");
|
|
103
199
|
if (third === undefined) throw new Error(ADD_USAGE);
|
|
104
200
|
const retype = (price: number) => retypeCommand(first, condition, price, exchange);
|
|
105
201
|
// `81 000` without quotes arrives as 81 and a stray 000; neither reading is taken for granted.
|
|
@@ -109,17 +205,122 @@ export function parseAlertAddArgs(args: readonly string[], days: string | null |
|
|
|
109
205
|
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
206
|
}
|
|
111
207
|
// 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)}`);
|
|
208
|
+
const price = readAlertPrice(third);
|
|
209
|
+
if (rest.length > 0) throw new Error(`A price alert takes one price, not "${rest.join(" ")}" after ${third}; type ${retype(price)}`);
|
|
116
210
|
const thousandsDot = thousandsDotReadings(third);
|
|
117
|
-
return { kind: "price", symbol: first, condition, price
|
|
211
|
+
return { kind: "price", symbol: first, condition, price, ...(thousandsDot ? { thousandsDot: { ...thousandsDot, text: third.trim() } } : {}) };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const OCC_CONTRACT = /^(?:O:)?[A-Z.]{1,6}\d{6}[CP]\d{8}$/i;
|
|
215
|
+
const LEVEL_DIRECTIONS = ["above", "below"] as const;
|
|
216
|
+
|
|
217
|
+
/** 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. */
|
|
218
|
+
function readLevel(metric: IvLevelMetric, typed: string | undefined, symbol: string): number {
|
|
219
|
+
const word = metric === "atm" ? "iv" : "rr";
|
|
220
|
+
const example = metric === "atm" ? `alerts add ${symbol} iv above 50` : `alerts add ${symbol} rr below -5`;
|
|
221
|
+
if (!typed) throw new Error(`Name the level: gloom ${example}.`);
|
|
222
|
+
const unitError = ivLevelUnitError(metric, typed);
|
|
223
|
+
if (unitError) throw new Error(`${unitError}: gloom ${example}.`);
|
|
224
|
+
const level = parseIvLevel(typed);
|
|
225
|
+
const { min, max } = IV_LEVEL_RANGES[metric];
|
|
226
|
+
if (!Number.isFinite(level) || level < min || level > max) {
|
|
227
|
+
throw new Error(metric === "atm"
|
|
228
|
+
? `An ${word} level is the 30-day ATM IV in percent, ${min} to ${max}, not "${typed}": gloom ${example}.`
|
|
229
|
+
: `An ${word} level is the 30-day 25-delta risk reversal in vol points, ${min} to ${max}, not "${typed}": gloom ${example}.`);
|
|
230
|
+
}
|
|
231
|
+
return level;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The research forms of `alerts add`, the ones Add Event Alert makes: `[symbol] news <keyword>`,
|
|
236
|
+
* `[symbol] flow [premium] [calls|puts] [sweeps|blocks]`, `<symbol> iv|rr <above|below> <level>` and
|
|
237
|
+
* `<OCC contract> iv spike <points>`. Null for any other form. A bare `news` or `flow` with no symbol
|
|
238
|
+
* follows every name or your lists; `NEWS above 10` is still a price alert on a symbol.
|
|
239
|
+
*/
|
|
240
|
+
function parseResearchAddArgs(args: readonly string[]): ResearchAddRequest | null {
|
|
241
|
+
const [first = "", second, third, ...rest] = args;
|
|
242
|
+
const word = (value: string | undefined) => value?.toLowerCase() ?? "";
|
|
243
|
+
const isCondition = (value: string | undefined) => (CONDITIONS as readonly string[]).includes(word(value)) || word(value) === "earnings";
|
|
244
|
+
const lead = word(first);
|
|
245
|
+
const [symbol, kind, tail] = (lead === "news" || lead === "flow") && !isCondition(second)
|
|
246
|
+
? [null, lead, args.slice(1)] as const
|
|
247
|
+
: [first, word(second), args.slice(2)] as const;
|
|
248
|
+
if (kind === "news") {
|
|
249
|
+
const keyword = tail.join(" ").trim();
|
|
250
|
+
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.`);
|
|
251
|
+
return { kind: "news", symbol, keyword };
|
|
252
|
+
}
|
|
253
|
+
if (kind === "flow") {
|
|
254
|
+
let premium = 1_000_000;
|
|
255
|
+
let side: "any" | "calls" | "puts" = "any";
|
|
256
|
+
let print: "any" | "sweep" | "block" = "any";
|
|
257
|
+
for (const token of tail) {
|
|
258
|
+
const lower = token.toLowerCase();
|
|
259
|
+
if (lower === "calls" || lower === "call") side = "calls";
|
|
260
|
+
else if (lower === "puts" || lower === "put") side = "puts";
|
|
261
|
+
else if (lower === "sweeps" || lower === "sweep") print = "sweep";
|
|
262
|
+
else if (lower === "blocks" || lower === "block") print = "block";
|
|
263
|
+
else if (Number.isFinite(parseAmount(token))) premium = parseAmount(token);
|
|
264
|
+
else throw new Error(`A flow alert takes a minimum premium (1m, $250k), calls or puts, and sweeps or blocks, not "${token}".`);
|
|
265
|
+
}
|
|
266
|
+
if (premium < 50_000) throw new Error(`The minimum premium is $50,000, not ${formatPremium(premium)}: Gloom Cloud records no smaller print.`);
|
|
267
|
+
return { kind: "flow", symbol, premium, side, print };
|
|
268
|
+
}
|
|
269
|
+
if (!symbol || (kind !== "iv" && kind !== "rr")) return null;
|
|
270
|
+
if (OCC_CONTRACT.test(symbol)) {
|
|
271
|
+
if (kind !== "iv" || word(third) !== "spike") {
|
|
272
|
+
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.`);
|
|
273
|
+
}
|
|
274
|
+
// Vol points, with the unit or without it: `5`, `5 pts`.
|
|
275
|
+
const typed = rest.join(" ");
|
|
276
|
+
const value = typed && !/%/.test(typed) ? parseIvLevel(typed) : Number.NaN;
|
|
277
|
+
if (!Number.isFinite(value) || value < 0.1 || value > 100) {
|
|
278
|
+
throw new Error(`An IV spike is a rise of 0.1 to 100 vol points: gloom alerts add ${symbol.toUpperCase()} iv spike 5.`);
|
|
279
|
+
}
|
|
280
|
+
return { kind: "iv_spike", contract: symbol.toUpperCase(), points: value };
|
|
281
|
+
}
|
|
282
|
+
const metric: IvLevelMetric = kind === "iv" ? "atm" : "rr";
|
|
283
|
+
const direction = word(third);
|
|
284
|
+
if (!(LEVEL_DIRECTIONS as readonly string[]).includes(direction)) {
|
|
285
|
+
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" : ""}.`);
|
|
286
|
+
}
|
|
287
|
+
// The level, with its unit or without it: `50`, `50%`, `-5.5 pts`.
|
|
288
|
+
return { kind: "iv_level", symbol, metric, direction: direction as "above" | "below", level: readLevel(metric, rest.join(" "), symbol) };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** The Add Event Alert form's values for a research request, so the CLI makes the rule the dialog makes. */
|
|
292
|
+
function researchFormValues(request: ResearchAddRequest, exchange: string): { kind: string; values: Record<string, string> } {
|
|
293
|
+
switch (request.kind) {
|
|
294
|
+
case "news": return { kind: "news_keyword", values: { "news_keyword:keyword": request.keyword,
|
|
295
|
+
...request.symbol ? { "news_keyword:symbol": request.symbol, "news_keyword:exchange": exchange } : {} } };
|
|
296
|
+
case "flow": return { kind: "options_flow", values: { "options_flow:threshold": String(request.premium),
|
|
297
|
+
"options_flow:direction": request.side, "options_flow:print": request.print,
|
|
298
|
+
...request.symbol ? { "options_flow:symbol": request.symbol, "options_flow:exchange": exchange } : {} } };
|
|
299
|
+
case "iv_level": return { kind: "iv_level", values: { "iv_level:symbol": request.symbol, "iv_level:exchange": exchange,
|
|
300
|
+
"iv_level:metric": request.metric, "iv_level:direction": request.direction, "iv_level:threshold": String(request.level) } };
|
|
301
|
+
case "iv_spike": return { kind: "iv_spike", values: { "iv_spike:contract": request.contract, "iv_spike:threshold": String(request.points) } };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Who checks a research rule and how it reaches you, as docs/usage.md states it: an IV level is read by the
|
|
307
|
+
* running app; the rest by Gloom Cloud for a signed-in account with Cloud sync, IV spikes and flow on Pro.
|
|
308
|
+
*/
|
|
309
|
+
function researchDelivery(kind: ResearchAddRequest["kind"]): string {
|
|
310
|
+
if (kind === "iv_level") return "the app checks it every 15 minutes while it is running, on the same option fits as OVDV";
|
|
311
|
+
const plan = kind === "news" ? "" : " on Gloom Pro";
|
|
312
|
+
return `Gloom Cloud checks it for a signed-in account with Cloud sync${plan} and sends it to your phone`;
|
|
118
313
|
}
|
|
119
314
|
|
|
315
|
+
const isResearchRequest = (request: AlertAddRequest): request is ResearchAddRequest =>
|
|
316
|
+
request.kind === "news" || request.kind === "flow" || request.kind === "iv_level" || request.kind === "iv_spike";
|
|
317
|
+
|
|
318
|
+
/** The US venue a research rule keeps: one typed and known to it, else any US venue. */
|
|
319
|
+
const RESEARCH_EXCHANGES = new Set(["NASDAQ", "NYSE", "AMEX", "ARCA", "BATS", "OTC"]);
|
|
320
|
+
|
|
120
321
|
/** `alerts add` as typed, with another price: what an error tells someone to type. */
|
|
121
322
|
function retypeCommand(symbol: string, condition: AlertCondition, price: number, exchange: string | null | undefined): string {
|
|
122
|
-
return `
|
|
323
|
+
return `gloom alerts add ${symbol} ${condition} ${price}${exchange ? ` --exchange ${exchange}` : ""}`;
|
|
123
324
|
}
|
|
124
325
|
|
|
125
326
|
/**
|
|
@@ -156,22 +357,28 @@ type AlertListRow = Record<string, unknown> & {
|
|
|
156
357
|
};
|
|
157
358
|
|
|
158
359
|
function priceRow(alert: AlertRule): AlertListRow {
|
|
159
|
-
return { ...alert, kind: "price", target: alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol };
|
|
360
|
+
return { ...alert, kind: alert.metric?.kind ?? "price", rule: alert.metric ? metricDescription(alert) : undefined, target: alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol };
|
|
160
361
|
}
|
|
161
362
|
|
|
162
|
-
function eventRow(rule: EventAlertRule,
|
|
363
|
+
function eventRow(rule: EventAlertRule, now: number, eventDate: string | null | undefined, check?: IvLevelCheck): AlertListRow {
|
|
163
364
|
const scheduled = readScheduledRule(rule);
|
|
164
365
|
if (!scheduled) {
|
|
165
|
-
|
|
366
|
+
const status = rule.status === "active" && rule.triggeredAt ? "triggered" : rule.status;
|
|
367
|
+
return { id: rule.id, kind: rule.kind, target: eventAlertTarget(rule), status, createdAt: rule.createdAt,
|
|
368
|
+
...check ? { lastCheck: check } : {}, ...rule.triggeredAt ? { triggeredAt: rule.triggeredAt } : {} };
|
|
166
369
|
}
|
|
167
370
|
const date = eventDate ?? null;
|
|
168
371
|
return {
|
|
169
372
|
id: rule.id,
|
|
170
373
|
kind: rule.kind,
|
|
171
|
-
target: scheduled.target.kind === "earnings" ? scheduled.target.symbol
|
|
374
|
+
target: scheduled.target.kind === "earnings" ? scheduled.target.symbol
|
|
375
|
+
: scheduled.target.kind === "release" ? calendarEventLabel(scheduled.target) : scheduled.target.event.toUpperCase(),
|
|
172
376
|
leadDays: scheduled.leadDays,
|
|
377
|
+
when: scheduled.trigger === "results" ? "results" : "before",
|
|
378
|
+
// CPI, FOMC and calendar releases also alert with their figures once they print.
|
|
379
|
+
...followsRelease(scheduled.target) ? { onRelease: true } : {},
|
|
173
380
|
eventDate: date,
|
|
174
|
-
status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays,
|
|
381
|
+
status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays, scheduledToday(scheduled.target, now), scheduled.trigger),
|
|
175
382
|
createdAt: rule.createdAt,
|
|
176
383
|
};
|
|
177
384
|
}
|
|
@@ -184,9 +391,11 @@ function textRow(row: AlertListRow): Record<string, unknown> {
|
|
|
184
391
|
id: row.id,
|
|
185
392
|
kind: price ? "Price" : alertKindLabel(row.kind),
|
|
186
393
|
target: row.target,
|
|
187
|
-
trigger: price
|
|
394
|
+
trigger: row.rule ?? (price
|
|
188
395
|
? `${row.condition} ${row.targetPrice}`
|
|
189
|
-
: typeof row.leadDays === "number"
|
|
396
|
+
: typeof row.leadDays === "number"
|
|
397
|
+
? scheduledTriggerText({ leadDays: row.leadDays, trigger: row.when === "results" ? "results" : "date", prints: row.onRelease === true })
|
|
398
|
+
: ""),
|
|
190
399
|
eventDate: typeof row.leadDays === "number" ? row.eventDate ?? "not known" : "",
|
|
191
400
|
status: row.status,
|
|
192
401
|
lastCheckedPrice: row.lastCheckedPrice,
|
|
@@ -197,7 +406,7 @@ function textRow(row: AlertListRow): Record<string, unknown> {
|
|
|
197
406
|
/** The next event date of each scheduled rule: the saved one while it is still ahead, otherwise looked up. */
|
|
198
407
|
async function listEventDates(
|
|
199
408
|
rules: readonly EventAlertRule[],
|
|
200
|
-
|
|
409
|
+
now: number,
|
|
201
410
|
lookup: (targets: ScheduledEventTarget[]) => Promise<Map<string, ScheduledDateLookup>>,
|
|
202
411
|
): Promise<{ dates: Map<string, string | null>; warnings: string[] }> {
|
|
203
412
|
const dates = new Map<string, string | null>();
|
|
@@ -205,7 +414,7 @@ async function listEventDates(
|
|
|
205
414
|
for (const rule of rules) {
|
|
206
415
|
const scheduled = readScheduledRule(rule);
|
|
207
416
|
if (!scheduled) continue;
|
|
208
|
-
if (rule.eventDate && rule.eventDate >=
|
|
417
|
+
if (rule.eventDate && rule.eventDate >= scheduledToday(scheduled.target, now)) dates.set(rule.id, rule.eventDate);
|
|
209
418
|
else stale.push({ rule, target: scheduled.target });
|
|
210
419
|
}
|
|
211
420
|
if (stale.length === 0) return { dates, warnings: [] };
|
|
@@ -219,6 +428,96 @@ async function listEventDates(
|
|
|
219
428
|
return { dates, warnings: [...warnings] };
|
|
220
429
|
}
|
|
221
430
|
|
|
431
|
+
/** 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. */
|
|
432
|
+
const CALENDAR_LOOKBACK_DAYS = 40;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* The calendar a release is named from: the stored past five weeks and the
|
|
436
|
+
* listing ahead, or the listing alone from a server that keeps no past.
|
|
437
|
+
*/
|
|
438
|
+
async function loadAlertCalendar(now: number): Promise<readonly EconEvent[]> {
|
|
439
|
+
const from = new Date(now - CALENDAR_LOOKBACK_DAYS * 86_400_000).toISOString().slice(0, 10);
|
|
440
|
+
const stored = await fetchEconCalendarFrom(from);
|
|
441
|
+
if (stored.events) return stored.events;
|
|
442
|
+
return (await loadCalendar()).data;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const quoted = (text: string) => (/\s/.test(text) ? `"${text}"` : text);
|
|
446
|
+
const weekday = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" });
|
|
447
|
+
|
|
448
|
+
/** "on Sat 2026-10-31 01:30 UTC", "on Mon 2026-10-26, all day": when a release is next listed. */
|
|
449
|
+
function releaseWhen(event: EconEvent): string {
|
|
450
|
+
const day = `on ${weekday.format(event.date)} ${dateKey(event.date)}`;
|
|
451
|
+
if (isAllDayEvent(event)) return `${day}, all day`;
|
|
452
|
+
return event.timeNote ? day : `${day} ${event.date.toISOString().slice(11, 16)} UTC`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** The release an alert was added for, as `--json` gives it. */
|
|
456
|
+
function releaseData(choice: CalendarEventChoice) {
|
|
457
|
+
return {
|
|
458
|
+
name: choice.name,
|
|
459
|
+
country: choice.country,
|
|
460
|
+
publisher: choice.publisher,
|
|
461
|
+
impact: choice.impact,
|
|
462
|
+
next: choice.next ? { at: choice.next.date.toISOString(), allDay: isAllDayEvent(choice.next) } : null,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const choiceList = (choices: readonly CalendarEventChoice[]) => choices.map((choice) => calendarEventLabel({ event: choice.name, country: choice.country }));
|
|
467
|
+
|
|
468
|
+
/** What a name read as, and what else it names in that country: said beside the alert added. */
|
|
469
|
+
function releaseReadNotes(
|
|
470
|
+
query: string,
|
|
471
|
+
choice: CalendarEventChoice,
|
|
472
|
+
others: readonly CalendarEventChoice[],
|
|
473
|
+
renamed?: ReleaseRename,
|
|
474
|
+
): string[] {
|
|
475
|
+
const notes: string[] = [];
|
|
476
|
+
if (calendarNameWords(query).join(" ") !== calendarNameWords(choice.name).join(" ")) {
|
|
477
|
+
notes.push(`Read "${query}" as ${calendarEventLabel({ event: choice.name, country: choice.country })}, ${choice.impact} impact.`
|
|
478
|
+
+ (renamed ? ` ${releaseRenameNote(renamed)}` : ""));
|
|
479
|
+
}
|
|
480
|
+
if (others.length > 0) {
|
|
481
|
+
const shown = choiceList(others.slice(0, 4));
|
|
482
|
+
const more = others.length > 4 ? ` and ${others.length - 4} more` : "";
|
|
483
|
+
notes.push(`Also matching: ${shown.join(", ")}${more}. To follow one of them instead: alerts add ${quoted(others[0]!.name)} --country ${others[0]!.country}.`);
|
|
484
|
+
}
|
|
485
|
+
return notes;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** Every form `alerts add` takes but a price alert's details, as a refusal ends. */
|
|
489
|
+
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).`;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Why a name could not be looked up. A single word (alerts add AAPL) may be
|
|
493
|
+
* a slip of another form as much as a release, so it also says what each
|
|
494
|
+
* form takes.
|
|
495
|
+
*/
|
|
496
|
+
function calendarUnavailableMessage(query: string, country: CountryFilter | null, error: string): string {
|
|
497
|
+
if (country || /\s/.test(query.trim())) return eventDateUnavailableMessage({ kind: "release", event: query, country: country ?? "" }, error);
|
|
498
|
+
return `Unknown event "${query}": the economic calendar could not be loaded to look it up (${error}). ${ADD_FORMS}`;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** The refusal of a name nothing in the calendar matches: the nearest names, and every form `alerts add` takes. */
|
|
502
|
+
function noReleaseMessage(
|
|
503
|
+
query: string,
|
|
504
|
+
country: CountryFilter | null,
|
|
505
|
+
suggestions: readonly CalendarEventChoice[],
|
|
506
|
+
renamed?: ReleaseRename,
|
|
507
|
+
): string {
|
|
508
|
+
const near = suggestions.length > 0 ? ` Did you mean ${choiceList(suggestions).join(", ")}?` : "";
|
|
509
|
+
return `No release in the economic calendar matches "${query}"${country ? ` in ${country.split(",").join(", ")}` : ""}.`
|
|
510
|
+
+ `${renamed ? ` ${releaseRenameNote(renamed)}` : ""}${near} `
|
|
511
|
+
+ `${ADD_FORMS} gloom econ --country <code> lists the calendar.`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** The refusal of a name listed in several countries, with the best of each. */
|
|
515
|
+
function severalCountriesMessage(query: string, choices: readonly CalendarEventChoice[]): string {
|
|
516
|
+
const shown = choiceList(choices.slice(0, 6));
|
|
517
|
+
const more = choices.length > 6 ? ` and ${choices.length - 6} more` : "";
|
|
518
|
+
return `"${query}" is listed for several countries: ${shown.join(", ")}${more}. Name the country: alerts add ${quoted(query)} --country ${choices[0]!.country}.`;
|
|
519
|
+
}
|
|
520
|
+
|
|
222
521
|
/** Looks up the next date for a new event alert, or fails saying why there is none. */
|
|
223
522
|
async function requireEventDate(
|
|
224
523
|
target: ScheduledEventTarget,
|
|
@@ -235,27 +534,47 @@ async function requireEventDate(
|
|
|
235
534
|
export const alertsCliCommand: CliCommandDef = {
|
|
236
535
|
name: "alerts",
|
|
237
536
|
aliases: ["alert"],
|
|
238
|
-
description: "List, add, and remove price
|
|
537
|
+
description: "List, add, and remove price, valuation and daily technical alerts, alerts before earnings and any economic calendar release (CPI, FOMC, NBS PMI), and news, options flow and IV alerts",
|
|
239
538
|
help: {
|
|
240
539
|
group: CLI_COMMAND_GROUPS.portfolios,
|
|
241
540
|
usage: [
|
|
242
541
|
"alerts list",
|
|
243
542
|
"alerts add <symbol> <above|below|crosses> <price>",
|
|
244
|
-
"alerts add <symbol>
|
|
245
|
-
"alerts add <
|
|
543
|
+
"alerts add <symbol> <pe|ps|rsi(14)> <above|below> <level>",
|
|
544
|
+
"alerts add <symbol> ma(50) crosses <above|below> ma(200)",
|
|
545
|
+
"alerts add <symbol> closes <above|below> ma(200)",
|
|
546
|
+
"alerts add <symbol> earnings [--days <n> | results]",
|
|
547
|
+
"alerts add <calendar event> [--country <code>] [--days <n>]",
|
|
548
|
+
"alerts add [symbol] news <keyword>",
|
|
549
|
+
"alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks]",
|
|
550
|
+
"alerts add <symbol> <iv|rr> <above|below> <level>",
|
|
551
|
+
"alerts add <OCC contract> iv spike <points>",
|
|
246
552
|
"alerts delete <id>",
|
|
247
553
|
"alerts rearm <id>",
|
|
248
554
|
],
|
|
249
555
|
options: [
|
|
250
556
|
EXCHANGE_OPTION,
|
|
251
|
-
{ flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings
|
|
557
|
+
{ flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings and calendar event alerts` },
|
|
558
|
+
{ 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
559
|
],
|
|
253
560
|
examples: [
|
|
254
561
|
"alerts",
|
|
255
562
|
"alerts add AAPL above 250",
|
|
563
|
+
"alerts add AAPL pe below 25",
|
|
564
|
+
"alerts add AAPL ps above 8",
|
|
565
|
+
'alerts add NVDA "rsi(14)" under 30',
|
|
566
|
+
'alerts add SPY "ma(50)" crosses above "ma(200)"',
|
|
567
|
+
'alerts add TSLA closes below "ma(200)"',
|
|
256
568
|
"alerts add SAN:EPA below 70",
|
|
257
569
|
"alerts add CRWV earnings --days 3",
|
|
570
|
+
"alerts add CRWV earnings results",
|
|
258
571
|
"alerts add CPI --days 1",
|
|
572
|
+
"alerts add \"NBS PMI\"",
|
|
573
|
+
"alerts add \"Industrial Profits\" --country CN --days 2",
|
|
574
|
+
"alerts add AAPL news buyback",
|
|
575
|
+
"alerts add NVDA flow 5m calls sweeps",
|
|
576
|
+
"alerts add IBIT iv above 50",
|
|
577
|
+
"alerts add SPY rr below -6",
|
|
259
578
|
],
|
|
260
579
|
},
|
|
261
580
|
execute: async (rawArgs, ctx) => {
|
|
@@ -264,61 +583,156 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
264
583
|
const daysValue = takeOption(args, "--days");
|
|
265
584
|
const days = daysFlag && daysValue === undefined ? null : daysValue;
|
|
266
585
|
const exchangeOption = takeOption(args, "--exchange");
|
|
586
|
+
const countryFlag = rawArgs.some((arg) => arg === "--country" || arg.startsWith("--country="));
|
|
587
|
+
const countryValue = takeOption(args, "--country");
|
|
588
|
+
const country = countryFlag && countryValue === undefined ? null : countryValue;
|
|
267
589
|
const action = args[0] ?? "list";
|
|
268
|
-
const
|
|
590
|
+
const now = Date.now();
|
|
591
|
+
const today = newYorkToday(new Date(now));
|
|
269
592
|
|
|
270
593
|
const parseAdd = (): AlertAddRequest => {
|
|
271
594
|
try {
|
|
272
|
-
return parseAlertAddArgs(args.slice(1), days, exchangeOption);
|
|
595
|
+
return parseAlertAddArgs(args.slice(1), days, exchangeOption, country);
|
|
273
596
|
} catch (error) {
|
|
274
597
|
return ctx.fail(error instanceof Error ? error.message : String(error));
|
|
275
598
|
}
|
|
276
599
|
};
|
|
277
600
|
const request = action === "add" ? parseAdd() : null;
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
601
|
+
const research = request && isResearchRequest(request) ? request : null;
|
|
602
|
+
const listedSymbol = (request?.kind === "price" || request?.kind === "metric") ? request.symbol
|
|
603
|
+
: research && research.kind !== "iv_spike" ? research.symbol : null;
|
|
604
|
+
// A price alert is kept for the listing `quote` prints for the symbol, and for no symbol that quotes nothing;
|
|
605
|
+
// a news, flow or IV rule on a symbol follows that listing too, and only in the US, where their data is.
|
|
606
|
+
const priced = listedSymbol
|
|
607
|
+
? await withMarketData(ctx, async (market) => {
|
|
608
|
+
const quoted = await requireQuotedListing(listedSymbol, exchangeOption, market, ctx, "alerts add");
|
|
609
|
+
if (research) {
|
|
610
|
+
const venue = quoted.listing.exchange || canonicalExchange(quoted.quote.listingExchangeName || quoted.quote.exchangeName);
|
|
611
|
+
if (!isUsListingExchange(venue)) {
|
|
612
|
+
const what = research.kind === "news" ? "News alerting" : research.kind === "flow" ? "Options flow" : "Options data";
|
|
613
|
+
const { message, details } = await usListingsOnlyMessage(what, quoted.listing, market,
|
|
614
|
+
(key) => `gloom alerts add ${key} ${args.slice(2).join(" ")}`);
|
|
615
|
+
ctx.fail(message, details);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return quoted;
|
|
619
|
+
})
|
|
281
620
|
: null;
|
|
282
621
|
const priceListing = priced?.listing ?? null;
|
|
622
|
+
if (request?.kind === "metric" && priced) {
|
|
623
|
+
const exchange = canonicalExchange(priced.listing.exchange || priced.quote.listingExchangeName || priced.quote.exchangeName);
|
|
624
|
+
const preview = { ...createAlert(priced.listing.symbol, request.condition, request.price, exchange), metric: request.metric };
|
|
625
|
+
await withMarketData(ctx, market => metricReadings(market.dataProvider)(preview)).catch(error => ctx.fail(error instanceof Error ? error.message : String(error)));
|
|
626
|
+
priced.listing.exchange = exchange;
|
|
627
|
+
}
|
|
283
628
|
if (request?.kind === "price" && request.thousandsDot && priced) {
|
|
284
629
|
const question = thousandsDotMessage(request, priced.listing.key, lastPrice(priced.quote), exchangeOption);
|
|
285
630
|
if (question) ctx.fail(question);
|
|
286
631
|
}
|
|
632
|
+
// A bare coin ticker read as a fund (BTC, the Grayscale trust) gets `quote`'s note naming the coin's pair.
|
|
633
|
+
const coinNote = priced && !priced.listing.exchange ? notTheCoinNote(priced.listing.symbol, priced.quote) : null;
|
|
634
|
+
if (request?.kind === "price" && priced) {
|
|
635
|
+
// A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
|
|
636
|
+
// Against the price the alert is checked on, as Add Alert measures it.
|
|
637
|
+
const far = farTargetMessage(priced.listing.key, request.price, priced.quote.price, coinNote);
|
|
638
|
+
if (far) ctx.fail(far);
|
|
639
|
+
}
|
|
287
640
|
// 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 ?? [];
|
|
641
|
+
let read = [...priced?.notes ?? [], ...coinNote ? [coinNote] : []];
|
|
289
642
|
let newEventRule: EventAlertRule | null = null;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
643
|
+
let newResearchRule: EventAlertRule | null = null;
|
|
644
|
+
if (research) {
|
|
645
|
+
const typed = priceListing?.exchange ? canonicalExchange(priceListing.exchange) : "";
|
|
646
|
+
const { kind, values } = researchFormValues(
|
|
647
|
+
research.kind === "iv_spike" ? research : { ...research, symbol: priceListing?.symbol ?? null } as ResearchAddRequest,
|
|
648
|
+
RESEARCH_EXCHANGES.has(typed) ? typed : "US",
|
|
649
|
+
);
|
|
650
|
+
try {
|
|
651
|
+
newResearchRule = createResearchAlert(kind, values);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// A release named from the calendar, with what the words were read as and what else they name.
|
|
657
|
+
let newRelease: CalendarEventChoice | null = null;
|
|
658
|
+
// CPI and FOMC, typed so or named by one of their calendar rows (CPI y/y, Fed Interest Rate Decision).
|
|
659
|
+
let macro: { event: MacroAlertEvent; leadDays: number } | null = request?.kind === "macro" ? request : null;
|
|
660
|
+
let calendar: readonly EconEvent[] | null = null;
|
|
661
|
+
if (request?.kind === "release") {
|
|
662
|
+
const events = await loadAlertCalendar(now).catch((error: unknown) =>
|
|
663
|
+
ctx.fail(calendarUnavailableMessage(request.query, request.country, error instanceof Error ? error.message : String(error))));
|
|
664
|
+
calendar = events;
|
|
665
|
+
const match = matchCalendarEvent(request.query, request.country, events, now);
|
|
666
|
+
if (match.kind !== "match") {
|
|
667
|
+
return ctx.fail(match.kind === "none"
|
|
668
|
+
? noReleaseMessage(request.query, request.country, match.suggestions, match.renamed)
|
|
669
|
+
: severalCountriesMessage(request.query, match.choices));
|
|
670
|
+
}
|
|
671
|
+
const { choice, others } = match;
|
|
672
|
+
const macroEvent = macroEventNamed(choice);
|
|
673
|
+
if (macroEvent) {
|
|
674
|
+
macro = { event: macroEvent, leadDays: request.leadDays };
|
|
675
|
+
read = [`Read "${request.query}" as the ${MACRO_EVENT_NAMES[macroEvent]} alert, which follows ${calendarEventLabel({ event: choice.name, country: choice.country })}.`];
|
|
676
|
+
} else {
|
|
677
|
+
newRelease = choice;
|
|
678
|
+
read = releaseReadNotes(request.query, choice, others, match.renamed);
|
|
679
|
+
const target: ScheduledEventTarget = { kind: "release", event: choice.name, country: choice.country, ...(choice.publisher ? { publisher: choice.publisher } : {}) };
|
|
680
|
+
try {
|
|
681
|
+
newEventRule = createScheduledAlert(target, request.leadDays, choice.next ? dateKey(choice.next.date) : null);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
// When CPI or FOMC came out earlier today: its figures are not sent to an alert set after them.
|
|
688
|
+
let macroOutAt: number | null = null;
|
|
689
|
+
const earnings = request?.kind === "earnings" ? request : null;
|
|
690
|
+
if (macro || earnings) {
|
|
691
|
+
const target: ScheduledEventTarget = macro
|
|
692
|
+
? { kind: "macro", event: macro.event }
|
|
693
|
+
: await requireListingArg(earnings!.symbol, exchangeOption, ctx)
|
|
294
694
|
.then((listing) => {
|
|
295
695
|
read = listingReadNotes(listing);
|
|
296
696
|
return { kind: "earnings" as const, symbol: listing.symbol, exchange: listing.exchange };
|
|
297
697
|
});
|
|
698
|
+
const lead = earnings?.results ? "results" as const : (macro ?? earnings!).leadDays;
|
|
298
699
|
// A listing the rule cannot follow fails before any request.
|
|
299
700
|
try {
|
|
300
|
-
createScheduledAlert(target,
|
|
701
|
+
createScheduledAlert(target, lead, null);
|
|
301
702
|
} catch (error) {
|
|
302
703
|
ctx.fail(error instanceof Error ? error.message : String(error));
|
|
303
704
|
}
|
|
304
705
|
const eventDate = target.kind === "earnings"
|
|
305
706
|
? await withMarketData(ctx, (market) => requireEventDate(target, scheduledEventSources(market.dataProvider), today, (message) => ctx.fail(message)))
|
|
306
707
|
: await requireEventDate(target, scheduledEventSources(null), today, (message) => ctx.fail(message));
|
|
307
|
-
newEventRule = createScheduledAlert(target,
|
|
708
|
+
newEventRule = createScheduledAlert(target, lead, eventDate);
|
|
709
|
+
if (target.kind === "macro" && eventDate === today) {
|
|
710
|
+
const events = calendar ?? await loadAlertCalendar(now).catch(() => {
|
|
711
|
+
// Intentional ignore: without the calendar the alert is added as before, only without saying today's release is out.
|
|
712
|
+
return null;
|
|
713
|
+
});
|
|
714
|
+
const row = events ? releaseRowOn(target, eventDate, events) : null;
|
|
715
|
+
const at = row ? releaseInstant(row) : null;
|
|
716
|
+
if (at != null && at <= now) macroOutAt = at;
|
|
717
|
+
}
|
|
308
718
|
}
|
|
309
719
|
|
|
310
720
|
if (action === "list") {
|
|
311
|
-
|
|
312
|
-
|
|
721
|
+
// An IV level's last reading is this device's, beside the rules.
|
|
722
|
+
const { alerts, alertsError, events, checks } = await withConfigData(ctx, (config) => ({
|
|
723
|
+
...readAlertStores(config.config),
|
|
724
|
+
checks: readIvLevelChecks(config.persistence?.pluginState?.get(ALERTS_PLUGIN_ID, resumeStateKey(IV_LEVEL_CHECKS_KEY))?.value),
|
|
725
|
+
}));
|
|
726
|
+
// Earnings dates come through market data; CPI, FOMC and calendar dates do not need it.
|
|
313
727
|
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,
|
|
728
|
+
? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today, now))
|
|
729
|
+
: lookupScheduledDates(targets, scheduledEventSources(null), today, now);
|
|
730
|
+
const { dates, warnings } = await listEventDates(events.rules, now, lookup);
|
|
317
731
|
const rows = [
|
|
318
732
|
...alerts.map(priceRow),
|
|
319
|
-
...events.rules.map((rule) => eventRow(rule,
|
|
733
|
+
...events.rules.map((rule) => eventRow(rule, now, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate, checks[rule.id])),
|
|
320
734
|
];
|
|
321
|
-
const allWarnings = [...(events.error ? [events.error] : []), ...warnings];
|
|
735
|
+
const allWarnings = [...(alertsError ? [alertsError] : []), ...(events.error ? [events.error] : []), ...warnings];
|
|
322
736
|
ctx.printResult({ data: rows, ...(allWarnings.length ? { warnings: allWarnings } : {}) }, {
|
|
323
737
|
rows: (data) => data.map(textRow),
|
|
324
738
|
textColumns: [
|
|
@@ -331,22 +745,26 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
331
745
|
{ key: "lastCheckedPrice", header: "Last Price", align: "right", optional: true },
|
|
332
746
|
{ key: "triggeredAt", header: "Triggered", optional: true },
|
|
333
747
|
],
|
|
334
|
-
empty: "No alerts. Add one with
|
|
748
|
+
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
749
|
});
|
|
336
750
|
return;
|
|
337
751
|
}
|
|
338
752
|
|
|
339
753
|
await withConfigData(ctx, async (config) => {
|
|
340
|
-
const { pluginState, alerts, events } = readAlertStores(config.config);
|
|
754
|
+
const { pluginState, alerts, alertsError, levelErrors, events } = readAlertStores(config.config);
|
|
341
755
|
const save = async (next: { alerts?: AlertRule[]; events?: EventAlertRule[] }) => {
|
|
756
|
+
if (next.alerts) for (const [key, value] of Object.entries(writeLevelAlertStores(next.alerts))) {
|
|
757
|
+
if (levelErrors[key] && value !== "[]") ctx.fail(levelErrors[key]!);
|
|
758
|
+
}
|
|
342
759
|
const nextConfig = {
|
|
343
760
|
...config.config,
|
|
344
761
|
pluginConfig: {
|
|
345
762
|
...config.config.pluginConfig,
|
|
346
763
|
[ALERTS_PLUGIN_ID]: {
|
|
347
764
|
...pluginState,
|
|
348
|
-
...(next.alerts ?
|
|
349
|
-
|
|
765
|
+
...(next.alerts ? Object.fromEntries(Object.entries(writeLevelAlertStores(next.alerts)).filter(([key]) => !levelErrors[key])) : {}),
|
|
766
|
+
// Each list keeps what it holds of kinds this version does not know; IV levels go in their own.
|
|
767
|
+
...(next.events ? writeEventAlertStores(events.lists, next.events) : {}),
|
|
350
768
|
},
|
|
351
769
|
},
|
|
352
770
|
};
|
|
@@ -358,14 +776,27 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
358
776
|
return events.rules;
|
|
359
777
|
};
|
|
360
778
|
|
|
361
|
-
if (request?.kind === "price") {
|
|
779
|
+
if (request?.kind === "price" || request?.kind === "metric") {
|
|
362
780
|
const { condition, price } = request;
|
|
363
781
|
const symbol = requireArg(priceListing?.key, ADD_USAGE, ctx);
|
|
364
|
-
const alert = createAlert(symbol, condition, price, priceListing?.exchange);
|
|
782
|
+
const alert = { ...createAlert(symbol, condition, price, priceListing?.exchange), ...(request.kind === "metric" ? { metric: request.metric } : {}) };
|
|
365
783
|
await save({ alerts: [...alerts, alert] });
|
|
366
784
|
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
367
785
|
// The delivery, as docs/usage.md states it, before any full stop, which after the price would read as part of it (below 81.).
|
|
368
|
-
text: (data) => `Added alert ${data.alert.id}: ${symbol} ${condition} ${price} (${PRICE_DELIVERY}).${dryRunNote(data.dryRun)}`,
|
|
786
|
+
text: (data) => `Added alert ${data.alert.id}: ${data.alert.metric ? metricDescription(data.alert) : `${symbol} ${condition} ${price}`} (${PRICE_DELIVERY}).${dryRunNote(data.dryRun)}`,
|
|
787
|
+
});
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (newResearchRule) {
|
|
791
|
+
const rule = newResearchRule;
|
|
792
|
+
const rules = requireEvents();
|
|
793
|
+
const existing = rules.find((entry) => entry.kind === rule.kind && entry.target === rule.target && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
794
|
+
if (existing) ctx.fail(`This alert already exists (ID ${existing.id}).`);
|
|
795
|
+
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
796
|
+
await save({ events: [...rules, rule] });
|
|
797
|
+
const kind = research!.kind;
|
|
798
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
799
|
+
text: (data) => `Added alert ${data.alert.id}: ${alertKindLabel(rule.kind)} ${researchAlertDescription(rule)} (${researchDelivery(kind)}).${dryRunNote(data.dryRun)}`,
|
|
369
800
|
});
|
|
370
801
|
return;
|
|
371
802
|
}
|
|
@@ -375,24 +806,38 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
375
806
|
const scheduled = readScheduledRule(rule)!;
|
|
376
807
|
const name = scheduledEventName(scheduled.target);
|
|
377
808
|
const existing = rules.find((entry) => entry.kind === rule.kind && entry.value.toLowerCase() === rule.value.toLowerCase());
|
|
378
|
-
if (existing) ctx.fail(
|
|
379
|
-
|
|
809
|
+
if (existing) ctx.fail(scheduled.trigger === "results"
|
|
810
|
+
? `An alert for ${name} results already exists (ID ${existing.id}).`
|
|
811
|
+
: `An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}).`);
|
|
812
|
+
if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
|
|
380
813
|
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
|
-
|
|
814
|
+
const eventDate = rule.eventDate ?? null;
|
|
815
|
+
const due = !!eventDate && scheduledPhase(eventDate, scheduled.leadDays, undefined, scheduledToday(scheduled.target, now)) === "due";
|
|
816
|
+
const release = followsRelease(scheduled.target);
|
|
817
|
+
// A release that prints no figures (a plenum, a speech) alerts only before it.
|
|
818
|
+
const prints = release && (!newRelease?.next || expectsFigures(newRelease.next));
|
|
819
|
+
const delivery = release ? ", only while it is running" : " while it is running, or on your phone with Cloud sync";
|
|
820
|
+
const when = !eventDate ? null : newRelease?.next ? releaseWhen(newRelease.next) : `on ${eventDate}`;
|
|
821
|
+
const reminder = !eventDate
|
|
822
|
+
? "the app alerts once the calendar lists its next date, about two weeks ahead"
|
|
823
|
+
: macroOutAt != null ? `it came out today at ${new Date(macroOutAt).toISOString().slice(11, 16)} UTC, so the app alerts before the next one`
|
|
824
|
+
: due ? "already reached, so the app alerts at its next check" : `the app alerts on ${leadStartDate(eventDate, scheduled.leadDays)}`;
|
|
825
|
+
ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule,
|
|
826
|
+
...newRelease ? { release: releaseData(newRelease) } : {} }, ...(read.length > 0 ? { warnings: read } : {}) }, {
|
|
827
|
+
// Delivery, as docs/usage.md states it: CPI, FOMC and calendar releases notify in the running app only; earnings also reach the phone,
|
|
828
|
+
// which Gloom Cloud alerts with the same results once the actual EPS is stored for a results alert.
|
|
829
|
+
text: (data) => scheduled.trigger === "results"
|
|
830
|
+
? `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)}`
|
|
831
|
+
: `Added alert ${data.alert.id}: ${name}${when ? ` ${when}` : ""}, ${plural(scheduled.leadDays, "day")} before${prints ? " and when it prints" : ""} (${reminder}${
|
|
832
|
+
prints ? ", and with the actual, forecast and prior once out" : ""}${delivery}).${dryRunNote(data.dryRun)}`,
|
|
389
833
|
});
|
|
390
834
|
return;
|
|
391
835
|
}
|
|
392
836
|
if (action === "delete" || action === "rm") {
|
|
393
|
-
const id = requireArg(args[1], "Usage:
|
|
837
|
+
const id = requireArg(args[1], "Usage: gloom alerts delete <id>", ctx);
|
|
394
838
|
const isPrice = alerts.some((alert) => alert.id === id);
|
|
395
839
|
const isEvent = !isPrice && events.rules.some((rule) => rule.id === id);
|
|
840
|
+
if (!isPrice && !isEvent && alertsError) ctx.fail(alertsError);
|
|
396
841
|
if (isPrice) await save({ alerts: alerts.filter((alert) => alert.id !== id) });
|
|
397
842
|
if (isEvent) await save({ events: requireEvents().filter((rule) => rule.id !== id) });
|
|
398
843
|
const changed = isPrice || isEvent;
|
|
@@ -404,13 +849,16 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
404
849
|
return;
|
|
405
850
|
}
|
|
406
851
|
if (action === "rearm") {
|
|
407
|
-
const id = requireArg(args[1], "Usage:
|
|
852
|
+
const id = requireArg(args[1], "Usage: gloom alerts rearm <id>", ctx);
|
|
408
853
|
const isPrice = alerts.some((alert) => alert.id === id);
|
|
409
854
|
const rule = isPrice ? null : events.rules.find((entry) => entry.id === id) ?? null;
|
|
855
|
+
if (!isPrice && !rule && alertsError) ctx.fail(alertsError);
|
|
410
856
|
if (isPrice) await save({ alerts: alerts.map((alert) => alert.id === id ? rearmAlert(alert) : alert) });
|
|
411
857
|
if (rule) {
|
|
412
858
|
// A scheduled alert alerts again for its coming event; any other event alert resumes.
|
|
413
|
-
const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
|
|
859
|
+
const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
|
|
860
|
+
: rule.kind === "iv_level" && rule.triggeredAt ? rearmIvLevelAlert(rule)
|
|
861
|
+
: rule.status === "paused" ? toggleEventAlert(rule) : rule;
|
|
414
862
|
await save({ events: requireEvents().map((entry) => entry.id === id ? next : entry) });
|
|
415
863
|
}
|
|
416
864
|
const found = isPrice || !!rule;
|
|
@@ -421,7 +869,7 @@ export const alertsCliCommand: CliCommandDef = {
|
|
|
421
869
|
});
|
|
422
870
|
return;
|
|
423
871
|
}
|
|
424
|
-
ctx.fail("Usage:
|
|
872
|
+
ctx.fail("Usage: gloom alerts list|add|delete|rearm");
|
|
425
873
|
});
|
|
426
874
|
},
|
|
427
875
|
};
|