@gloom-sh/terminal 0.19.1 → 0.19.3
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 +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { classifyInstrumentType, isCryptoVenuePerpetual } from "../../tickers/instrument-kind";
|
|
2
|
+
import type { PriceBasis } from "../../types/instrument";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The unit an index level is in, as a chart's axis and series name it. A
|
|
@@ -28,11 +29,12 @@ export function quotePriceCurrency(quote: { currency?: string | null; instrument
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
|
-
* What a price chart
|
|
32
|
-
* otherwise its currency
|
|
32
|
+
* What a price chart reads in: the quote's price basis, points for an index
|
|
33
|
+
* level, otherwise its currency.
|
|
33
34
|
*/
|
|
34
|
-
export function priceSeriesUnit(currency: string, instrumentType: string | null | undefined): string {
|
|
35
|
-
|
|
35
|
+
export function priceSeriesUnit(currency: string, instrumentType: string | null | undefined, priceBasis?: PriceBasis): string {
|
|
36
|
+
if (priceBasis === "thirty-seconds") return "32nds";
|
|
37
|
+
return priceBasis === "points" || isIndexLevel(instrumentType) ? INDEX_POINTS_UNIT : currency;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
// EGX closes on Egypt's public holidays, often moved to the Thursday of their
|
|
22
22
|
// week (Armed Forces Day, Tuesday 6 Oct 2026, to Thursday 8 Oct, by the
|
|
23
23
|
// exchange's statement of 5 Oct). Its week is Sunday to Thursday.
|
|
24
|
+
// JSE closes on South Africa's public holidays, a Sunday one moving to the Monday (Women's Day, 9 Aug 2026):
|
|
25
|
+
// JSE Market Notice 380/25, JSE Markets Calendar 2026 (clientportal.jse.co.za), and the
|
|
26
|
+
// local government election day, Wed 4 Nov 2026, declared a public holiday on 15 Sep 2026 (gov.za).
|
|
24
27
|
const KRX_CLOSURES: Record<number, readonly string[]> = {
|
|
25
28
|
2026: [
|
|
26
29
|
"01-01", "02-16", "02-17", "02-18", "03-02", "05-01", "05-05", "05-25", "06-03",
|
|
@@ -79,6 +82,9 @@ export const PUBLISHED_APAC_CLOSURES: Readonly<Record<string, Record<number, rea
|
|
|
79
82
|
2026: ["01-01", "01-26", "04-03", "04-06", "06-08", "12-25", "12-28"],
|
|
80
83
|
2027: ["01-01", "01-26", "03-26", "03-29", "06-14", "12-27", "12-28"],
|
|
81
84
|
},
|
|
85
|
+
JSE: {
|
|
86
|
+
2026: ["01-01", "04-03", "04-06", "04-27", "05-01", "06-16", "08-10", "09-24", "11-04", "12-16", "12-25"],
|
|
87
|
+
},
|
|
82
88
|
EGX: {
|
|
83
89
|
2026: [
|
|
84
90
|
"01-01", "01-07", "01-29", "03-19", "03-22", "03-23", "04-12", "04-13", "05-07",
|
|
@@ -3,6 +3,7 @@ import { barsPerYear } from "./bars-per-year";
|
|
|
3
3
|
import {
|
|
4
4
|
mergePriceHistoryIntegrity,
|
|
5
5
|
pricePointIntegrity,
|
|
6
|
+
pricePointWicks,
|
|
6
7
|
type PriceHistoryIntegrity,
|
|
7
8
|
} from "../utils/price-history-integrity";
|
|
8
9
|
|
|
@@ -115,8 +116,9 @@ function observations(points: readonly PricePoint[]): Observation[] | null {
|
|
|
115
116
|
time,
|
|
116
117
|
close: positiveLog(point.close),
|
|
117
118
|
open: positiveLog(point.open),
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
// A wick too far from the bar's body to have traded is missing, not a range.
|
|
120
|
+
high: positiveLog(pricePointWicks(point).high),
|
|
121
|
+
low: positiveLog(pricePointWicks(point).low),
|
|
120
122
|
integrity: pricePointIntegrity(point),
|
|
121
123
|
}));
|
|
122
124
|
}
|
package/src/news/aggregator.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
normalizeNewsFeed,
|
|
18
18
|
normalizeNewsQuery,
|
|
19
19
|
} from "./news-model";
|
|
20
|
+
import { storyIdMatches } from "./story-id";
|
|
20
21
|
|
|
21
22
|
export { buildNewsQueryKey } from "./news-model";
|
|
22
23
|
|
|
@@ -343,16 +344,17 @@ export class NewsService {
|
|
|
343
344
|
const sources = this.enabledSources({ feed: "latest" })
|
|
344
345
|
.filter((source) => !!source.provider.fetchNewsStory);
|
|
345
346
|
|
|
347
|
+
// A short id (`6067684e`, as `news` prints it) names the story whose id starts with it.
|
|
346
348
|
const requestedRevisions = new Map<NewsQueryEntry, string>();
|
|
347
349
|
for (const entry of this.queries.values()) {
|
|
348
|
-
const article = entry.state.articles.find((article) => article.id
|
|
350
|
+
const article = entry.state.articles.find((article) => storyIdMatches(article.id, storyId));
|
|
349
351
|
if (article) requestedRevisions.set(entry, newsArticleRevision(article));
|
|
350
352
|
}
|
|
351
353
|
let failed = false;
|
|
352
354
|
let olderDetail: NewsArticle | null = null;
|
|
353
355
|
const commitDetail = (article: NewsArticle) => {
|
|
354
356
|
this.mergeStoryDetail(article, requestedRevisions);
|
|
355
|
-
return this.articles.find((current) => current.id ===
|
|
357
|
+
return this.articles.find((current) => current.id === article.id) ?? article;
|
|
356
358
|
};
|
|
357
359
|
for (const source of sources) {
|
|
358
360
|
try {
|
|
@@ -362,8 +364,9 @@ export class NewsService {
|
|
|
362
364
|
() => source.provider.fetchNewsStory?.(storyId) ?? Promise.resolve(null),
|
|
363
365
|
);
|
|
364
366
|
if (!article) continue;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
// The source answers a short id with the full id of the story it starts.
|
|
368
|
+
if (!storyIdMatches(article.id, storyId)) throw new Error("Story detail identity mismatch.");
|
|
369
|
+
const current = this.articles.find((current) => current.id === article.id);
|
|
367
370
|
if (current && article.publishedAt < current.publishedAt) {
|
|
368
371
|
if (!olderDetail || article.publishedAt > olderDetail.publishedAt) olderDetail = article;
|
|
369
372
|
continue;
|
package/src/news/news-model.ts
CHANGED
|
@@ -245,3 +245,14 @@ export function filterNewsArticlesForQuery(items: NewsArticle[], query: NewsQuer
|
|
|
245
245
|
// Page size is a fetch hint. Accumulated load-more results keep MAX_ARTICLES.
|
|
246
246
|
return filtered.slice(0, MAX_ARTICLES);
|
|
247
247
|
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* A story's summary as one line under its row in a text list (`news`,
|
|
251
|
+
* `fn CN`): its whitespace and line breaks run together, null when it has
|
|
252
|
+
* none or only repeats the headline.
|
|
253
|
+
*/
|
|
254
|
+
export function newsSummaryLine(article: Pick<NewsArticle, "title" | "summary">): string | null {
|
|
255
|
+
const summary = article.summary?.replace(/\s+/g, " ").trim();
|
|
256
|
+
if (!summary || summary.toLowerCase() === article.title.trim().toLowerCase()) return null;
|
|
257
|
+
return summary;
|
|
258
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Gloom Cloud story's short id: the first eight characters of its id
|
|
3
|
+
* (`6067684e` for `6067684e-2e13-41c5-a7cf-5052b3cefc07`), which `news`
|
|
4
|
+
* prints on each row and the story pane opens (`fn news-story-pane 6067684e`).
|
|
5
|
+
* Gloom Cloud opens the one story whose id starts with it.
|
|
6
|
+
*/
|
|
7
|
+
const SHORT_STORY_ID_LENGTH = 8;
|
|
8
|
+
|
|
9
|
+
const STORY_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
const SHORT_ID = /^[0-9a-f]{8}$/i;
|
|
11
|
+
|
|
12
|
+
/** The id a row prints: a Gloom Cloud story's short id, any other source's id as it is. */
|
|
13
|
+
export function shortStoryId(id: string): string {
|
|
14
|
+
return STORY_UUID.test(id) ? id.slice(0, SHORT_STORY_ID_LENGTH).toLowerCase() : id;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Whether `id` is the story `wanted` names, by its full id or its short one, in any case. */
|
|
18
|
+
export function storyIdMatches(id: string, wanted: string): boolean {
|
|
19
|
+
const typed = wanted.trim().toLowerCase();
|
|
20
|
+
const own = id.toLowerCase();
|
|
21
|
+
return own === typed || (SHORT_ID.test(typed) && STORY_UUID.test(own) && own.startsWith(typed));
|
|
22
|
+
}
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import { isPredictionMarketExchange, isUsListingExchange, parsePublicTickerKey } from "../utils/exchanges";
|
|
2
|
+
|
|
3
|
+
/** US over-the-counter venues: OTC Markets' tiers (OTC, OTCM, OTCQX...), the Pink market (PINX) and the Expert Market (EXPM). */
|
|
4
|
+
const OTC_VENUE = /^(?:OTC|PINX|PINK|PNK|EXPM)/;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A story's tag for a listing: the bare symbol on a US venue (or over the
|
|
8
|
+
* counter) and for a coin, the symbol and its venue elsewhere. PIK on the JSE
|
|
9
|
+
* is Pick n Pay, and bare PIK opens Kidpik on NASDAQ.
|
|
10
|
+
*/
|
|
1
11
|
function newsDisplayTickerSymbol(value: string | null | undefined): string | null {
|
|
2
12
|
const normalized = value?.trim().toUpperCase();
|
|
3
13
|
if (!normalized) return null;
|
|
4
|
-
|
|
14
|
+
const listing = parsePublicTickerKey(normalized);
|
|
15
|
+
const venue = listing.exchange ?? "";
|
|
16
|
+
if (!listing.symbol) return null;
|
|
17
|
+
if (isPredictionMarketExchange(venue)) return normalized;
|
|
18
|
+
return !venue || isUsListingExchange(venue) || OTC_VENUE.test(venue) || venue === "CRYPTO"
|
|
19
|
+
? listing.symbol
|
|
20
|
+
: `${listing.symbol}:${venue}`;
|
|
5
21
|
}
|
|
6
22
|
|
|
7
23
|
export function collectNewsDisplayTickers(
|
|
@@ -16,14 +16,17 @@ function getRuntimePlatform(): NodeJS.Platform {
|
|
|
16
16
|
export interface DesktopNotificationCommand {
|
|
17
17
|
command: string;
|
|
18
18
|
args: string[];
|
|
19
|
+
/** Added to the environment of the process, for text a command line must not carry. */
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface DesktopNotificationRunOptions {
|
|
24
|
+
env?: Record<string, string>;
|
|
25
|
+
onError?: (error: NodeJS.ErrnoException) => void;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
export interface DesktopNotificationRunner {
|
|
22
|
-
run(
|
|
23
|
-
command: string,
|
|
24
|
-
args: string[],
|
|
25
|
-
handlers?: { onError?: (error: NodeJS.ErrnoException) => void },
|
|
26
|
-
): void;
|
|
29
|
+
run(command: string, args: string[], options?: DesktopNotificationRunOptions): void;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export interface DesktopNotificationSink {
|
|
@@ -46,8 +49,14 @@ function escapeAppleScriptString(value: string): string {
|
|
|
46
49
|
.replace(/"/g, "\\\"")}"`;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const WINDOWS_NOTIFICATION_TITLE_ENV = "GLOOM_NOTIFICATION_TITLE";
|
|
53
|
+
const WINDOWS_NOTIFICATION_TEXT_ENV = "GLOOM_NOTIFICATION_TEXT";
|
|
54
|
+
/** The most of a title or a body handed to Windows; a balloon shows far less, and an environment value has a hard limit. */
|
|
55
|
+
const WINDOWS_NOTIFICATION_TEXT_LIMIT = 4_000;
|
|
56
|
+
|
|
57
|
+
/** NUL ends an environment value early, and spawning with one in it fails. */
|
|
58
|
+
function windowsEnvText(value: string): string {
|
|
59
|
+
return value.replaceAll("\0", "").slice(0, WINDOWS_NOTIFICATION_TEXT_LIMIT);
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
function mapLinuxUrgency(type: AppNotificationType | undefined): "low" | "normal" | "critical" {
|
|
@@ -105,6 +114,8 @@ export function buildDesktopNotificationCommand(
|
|
|
105
114
|
DEFAULT_NOTIFICATION_TITLE,
|
|
106
115
|
"--urgency",
|
|
107
116
|
mapLinuxUrgency(notification.type),
|
|
117
|
+
// Text that starts with `-` is the title or the body, not an option.
|
|
118
|
+
"--",
|
|
108
119
|
title,
|
|
109
120
|
...(linuxBody ? [linuxBody] : []),
|
|
110
121
|
],
|
|
@@ -112,14 +123,16 @@ export function buildDesktopNotificationCommand(
|
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
if (platform === "win32") {
|
|
115
|
-
|
|
126
|
+
// The script is the same for every notification. The title and the text
|
|
127
|
+
// reach it in the environment of the process, where PowerShell reads them
|
|
128
|
+
// as plain strings and never as code, whatever characters they hold.
|
|
116
129
|
const script = [
|
|
117
130
|
"Add-Type -AssemblyName System.Windows.Forms;",
|
|
118
131
|
"Add-Type -AssemblyName System.Drawing;",
|
|
119
132
|
"$notify = New-Object System.Windows.Forms.NotifyIcon;",
|
|
120
133
|
`$notify.Icon = [System.Drawing.SystemIcons]::${mapWindowsIcon(notification.type)};`,
|
|
121
|
-
`$notify.BalloonTipTitle = ${
|
|
122
|
-
`$notify.BalloonTipText = ${
|
|
134
|
+
`$notify.BalloonTipTitle = $env:${WINDOWS_NOTIFICATION_TITLE_ENV};`,
|
|
135
|
+
`$notify.BalloonTipText = $env:${WINDOWS_NOTIFICATION_TEXT_ENV};`,
|
|
123
136
|
"$notify.Visible = $true;",
|
|
124
137
|
"$notify.ShowBalloonTip(5000);",
|
|
125
138
|
"Start-Sleep -Seconds 6;",
|
|
@@ -128,6 +141,10 @@ export function buildDesktopNotificationCommand(
|
|
|
128
141
|
return {
|
|
129
142
|
command: "powershell.exe",
|
|
130
143
|
args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
|
|
144
|
+
env: {
|
|
145
|
+
[WINDOWS_NOTIFICATION_TITLE_ENV]: windowsEnvText(title),
|
|
146
|
+
[WINDOWS_NOTIFICATION_TEXT_ENV]: windowsEnvText(buildWindowsBody(notification)),
|
|
147
|
+
},
|
|
131
148
|
};
|
|
132
149
|
}
|
|
133
150
|
|
|
@@ -137,19 +154,22 @@ export function buildDesktopNotificationCommand(
|
|
|
137
154
|
function defaultDesktopNotificationRunner(
|
|
138
155
|
command: string,
|
|
139
156
|
args: string[],
|
|
140
|
-
|
|
157
|
+
options?: DesktopNotificationRunOptions,
|
|
141
158
|
): void {
|
|
142
159
|
if (typeof Bun !== "undefined" && typeof Bun.spawn === "function") {
|
|
143
160
|
try {
|
|
144
|
-
const child = Bun.spawn([command, ...args], {
|
|
161
|
+
const child = Bun.spawn([command, ...args], {
|
|
162
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
163
|
+
...(options?.env ? { env: { ...process.env, ...options.env } } : {}),
|
|
164
|
+
});
|
|
145
165
|
child.unref();
|
|
146
166
|
} catch (error) {
|
|
147
|
-
|
|
167
|
+
options?.onError?.(error as NodeJS.ErrnoException);
|
|
148
168
|
}
|
|
149
169
|
return;
|
|
150
170
|
}
|
|
151
171
|
|
|
152
|
-
|
|
172
|
+
options?.onError?.(Object.assign(new Error("Desktop notifications require a native process host."), {
|
|
153
173
|
code: "ENOSYS",
|
|
154
174
|
}) as NodeJS.ErrnoException);
|
|
155
175
|
}
|
|
@@ -203,6 +223,7 @@ export function createDesktopNotifier(
|
|
|
203
223
|
if (desktopCommand && disabledCommand !== desktopCommand.command) {
|
|
204
224
|
let requestFailed = false;
|
|
205
225
|
runner.run(desktopCommand.command, desktopCommand.args, {
|
|
226
|
+
env: desktopCommand.env,
|
|
206
227
|
onError: (error) => {
|
|
207
228
|
requestFailed = true;
|
|
208
229
|
if (error.code === "ENOENT") {
|
|
@@ -37,24 +37,28 @@ function readJson(path: string): Record<string, unknown> | null {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async function readCheckouts(): Promise<PluginCheckout[]> {
|
|
40
|
-
const { installedPluginDirectories,
|
|
40
|
+
const { installedPluginDirectories, readPluginRemoteAsync } = await installer();
|
|
41
41
|
const pluginsDir = getPluginsDir();
|
|
42
|
-
|
|
42
|
+
const checkouts: PluginCheckout[] = [];
|
|
43
|
+
// One git at a time: starting a process holds the thread for a moment, and
|
|
44
|
+
// starting them all at once adds those moments up into one pause.
|
|
45
|
+
for (const directory of installedPluginDirectories()) {
|
|
43
46
|
const dir = join(pluginsDir, directory);
|
|
44
47
|
const pkg = readJson(join(dir, "package.json"));
|
|
45
48
|
const dependencies = pkg?.dependencies;
|
|
46
|
-
|
|
49
|
+
checkouts.push({
|
|
47
50
|
directory,
|
|
48
51
|
id: readPluginManifest(dir).id,
|
|
49
|
-
repo: existsSync(join(dir, ".git")) ?
|
|
52
|
+
repo: existsSync(join(dir, ".git")) ? await readPluginRemoteAsync(dir) : null,
|
|
50
53
|
commit: readPluginCommit(dir),
|
|
51
54
|
version: typeof pkg?.version === "string" ? pkg.version : null,
|
|
52
55
|
linked: lstatSync(dir, { throwIfNoEntry: false })?.isSymbolicLink() === true,
|
|
53
56
|
needsGloomberb: checkPluginCompatibility(dir)?.needsGloomberb ?? null,
|
|
54
57
|
peers: installedPeerPlugins(dir, pluginsDir).map((peer) => peer.directory),
|
|
55
58
|
hasDependencies: !!dependencies && typeof dependencies === "object" && Object.keys(dependencies).length > 0,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return checkouts;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
function createNodeAutoUpdateDeps(manager: PluginManager): PluginAutoUpdateDeps {
|
|
@@ -157,7 +157,7 @@ export function PublicAnalyticsGroup({
|
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
159
|
* The desks chat shows beside your name, once What I Trade is on: pre-filled
|
|
160
|
-
* from your answer to "What do you trade?", coarse words only.
|
|
160
|
+
* from your answer to "What do you trade most?", coarse words only.
|
|
161
161
|
*/
|
|
162
162
|
export function DeskChoiceRow({
|
|
163
163
|
desks,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
|
-
import { AccountManagementPane } from "./pane";
|
|
3
2
|
|
|
4
3
|
export const accountManagementModule: PluginModule = {
|
|
5
4
|
panes: [{
|
|
@@ -7,8 +6,8 @@ export const accountManagementModule: PluginModule = {
|
|
|
7
6
|
name: "ACM",
|
|
8
7
|
icon: "A",
|
|
9
8
|
personalData: "account",
|
|
10
|
-
component: AccountManagementPane,
|
|
11
|
-
reportFreshness: { status: "not-a-feed", basis: "account data" },
|
|
9
|
+
get component(): typeof import("./pane").AccountManagementPane { const { AccountManagementPane } = require("./pane"); return AccountManagementPane; },
|
|
10
|
+
reportFreshness: { status: "not-a-feed", basis: "account data", local: true },
|
|
12
11
|
defaultPosition: "right",
|
|
13
12
|
defaultMode: "floating",
|
|
14
13
|
defaultFloatingSize: { width: 72, height: 36 },
|
|
@@ -44,7 +44,7 @@ export interface AccountDraft {
|
|
|
44
44
|
showLocalTime: boolean;
|
|
45
45
|
/** Chat shows what you trade: off until you turn it on. */
|
|
46
46
|
showDesks: boolean;
|
|
47
|
-
/** The desks to show, pre-filled from your answer to "What do you trade?". */
|
|
47
|
+
/** The desks to show, pre-filled from your answer to "What do you trade most?". */
|
|
48
48
|
desks: string[];
|
|
49
49
|
weeklyRoundupEnabled: boolean;
|
|
50
50
|
positionAlertsEnabled: boolean;
|
|
@@ -33,6 +33,7 @@ export const ALERT_ADD_FORMS: readonly AlertAddForm[] = [
|
|
|
33
33
|
{ name: "short interest", usage: "<symbol> short interest [increase|decrease] [<percent>%]" },
|
|
34
34
|
{ name: "analyst change", usage: "<symbol> analyst [upgrade|downgrade]" },
|
|
35
35
|
{ name: "hiring move", usage: "<symbol> hiring [accelerating|slowing]" },
|
|
36
|
+
{ name: "IPO", usage: "ipo <company or keyword>" },
|
|
36
37
|
{ name: "perpetual market", usage: "perps <market> <funding|oi|premium|closed-premium> <above|below> <percent>" },
|
|
37
38
|
{ name: "catalyst", usage: "catalyst [type <type> | agency <agency> | country <code>]" },
|
|
38
39
|
{ name: "catalyst", usage: "<symbol> catalyst" },
|
|
@@ -173,6 +174,17 @@ function perpsRule(words: readonly string[]): RuleAddRequest {
|
|
|
173
174
|
return { kind: "rule", event: "perps_threshold", symbol: null, listing: "any", values: { marketId, metric, direction, threshold } };
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
/**
|
|
178
|
+
* `ipo Stripe`, `ipo "Space Exploration"`: a company or keyword on the IPO calendar, followed until its deal
|
|
179
|
+
* is on the calendar, dated, priced and trading. Kept as typed, 2 to 80 characters, as the form takes it.
|
|
180
|
+
*/
|
|
181
|
+
function ipoRule(words: readonly string[]): RuleAddRequest {
|
|
182
|
+
const keyword = words.join(" ").trim().replace(/\s+/g, " ");
|
|
183
|
+
if (!keyword) throw new Error("Name the company or a keyword: gloom alerts add ipo Stripe.");
|
|
184
|
+
if (keyword.length < 2 || keyword.length > 80) throw new Error(`An IPO alert follows a company or keyword of 2 to 80 characters, not "${keyword}": gloom alerts add ipo Stripe.`);
|
|
185
|
+
return { kind: "rule", event: "ipo", symbol: null, listing: "any", values: { keyword } };
|
|
186
|
+
}
|
|
187
|
+
|
|
176
188
|
/** `catalyst`, `catalyst type clinical`, `catalyst agency FDA`, `catalyst country US`: the catalysts followed with no company. */
|
|
177
189
|
function catalystRule(words: readonly string[]): RuleAddRequest {
|
|
178
190
|
if (words.length === 0) return { kind: "rule", event: "catalyst-watched", symbol: null, listing: "any", values: {} };
|
|
@@ -192,7 +204,7 @@ function catalystRule(words: readonly string[]): RuleAddRequest {
|
|
|
192
204
|
* The forms of `alerts add` for the rest of what Add Event Alert offers:
|
|
193
205
|
* 52-week range, unusual volume, filings, insider trades, short interest,
|
|
194
206
|
* analyst changes and hiring on a symbol, perpetual markets, catalysts,
|
|
195
|
-
* Congress trades and 13F filings. Null for any other words; throws, naming
|
|
207
|
+
* IPOs, Congress trades and 13F filings. Null for any other words; throws, naming
|
|
196
208
|
* the form, when one of them is mistyped. A symbol that spells a form word
|
|
197
209
|
* (`PERPS above 10`) is still a price alert.
|
|
198
210
|
*/
|
|
@@ -202,6 +214,8 @@ export function parseRuleAddArgs(args: readonly string[]): RuleAddRequest | null
|
|
|
202
214
|
if (!CONDITION_WORDS.has(word(rest[0]))) {
|
|
203
215
|
if (lead === "perps" || lead === "perp") return perpsRule(rest);
|
|
204
216
|
if (lead === "catalyst" || lead === "catalysts") return catalystRule(rest);
|
|
217
|
+
// The IPO ETF keeps its other alerts written with its venue (IPO:NYSEARCA 52-week high).
|
|
218
|
+
if (lead === "ipo") return ipoRule(rest);
|
|
205
219
|
if (lead === "congress") {
|
|
206
220
|
const member = rest.join(" ").trim();
|
|
207
221
|
return { kind: "rule", event: member ? "congress-member" : "congress-watched", symbol: null, listing: "any", values: member ? { value: member } : {} };
|
|
@@ -17,6 +17,7 @@ import { usePluginAppActions, usePluginConfigState, usePluginState } from "../..
|
|
|
17
17
|
import {
|
|
18
18
|
CALENDAR_ALERTS_KEY,
|
|
19
19
|
EVENT_ALERTS_KEY,
|
|
20
|
+
IPO_ALERTS_KEY,
|
|
20
21
|
IV_LEVEL_ALERTS_KEY,
|
|
21
22
|
eventAlertStoreKey,
|
|
22
23
|
eventAlertTarget,
|
|
@@ -54,12 +55,13 @@ const columnsFor = (width: number, lastCheck: boolean, needsSignIn: boolean): Da
|
|
|
54
55
|
export function EventAlertsPane({ focused, width, height }: PaneProps) {
|
|
55
56
|
const [catalystStatus] = usePluginConfigState<string>(CATALYST_POLL_STATUS, "Catalyst alerts check every five minutes while the app is open");
|
|
56
57
|
const [json, setJson] = usePluginConfigState<string>(EVENT_ALERTS_KEY, "[]");
|
|
57
|
-
// IV levels
|
|
58
|
+
// IV levels, calendar events and IPOs are kept in their own lists, which released versions never read.
|
|
58
59
|
const [ivJson, setIvJson] = usePluginConfigState<string>(IV_LEVEL_ALERTS_KEY, "[]");
|
|
59
60
|
const [calendarJson, setCalendarJson] = usePluginConfigState<string>(CALENDAR_ALERTS_KEY, "[]");
|
|
61
|
+
const [ipoJson, setIpoJson] = usePluginConfigState<string>(IPO_ALERTS_KEY, "[]");
|
|
60
62
|
const { rules, error } = useMemo(
|
|
61
|
-
() => readEventAlertStores((key) => key === IV_LEVEL_ALERTS_KEY ? ivJson : key === CALENDAR_ALERTS_KEY ? calendarJson : json),
|
|
62
|
-
[json, ivJson, calendarJson],
|
|
63
|
+
() => readEventAlertStores((key) => key === IV_LEVEL_ALERTS_KEY ? ivJson : key === CALENDAR_ALERTS_KEY ? calendarJson : key === IPO_ALERTS_KEY ? ipoJson : json),
|
|
64
|
+
[json, ivJson, calendarJson, ipoJson],
|
|
63
65
|
);
|
|
64
66
|
// Their readings stay on this device.
|
|
65
67
|
const [savedChecks] = usePluginState<unknown>(IV_LEVEL_CHECKS_KEY, {});
|
|
@@ -70,9 +72,9 @@ export function EventAlertsPane({ focused, width, height }: PaneProps) {
|
|
|
70
72
|
const updateRules = useCallback(
|
|
71
73
|
(rule: EventAlertRule, update: (rules: EventAlertRule[]) => EventAlertRule[]) => {
|
|
72
74
|
const key = eventAlertStoreKey(rule);
|
|
73
|
-
(key === IV_LEVEL_ALERTS_KEY ? setIvJson : key === CALENDAR_ALERTS_KEY ? setCalendarJson : setJson)((current) => updateEventAlerts(current, update));
|
|
75
|
+
(key === IV_LEVEL_ALERTS_KEY ? setIvJson : key === CALENDAR_ALERTS_KEY ? setCalendarJson : key === IPO_ALERTS_KEY ? setIpoJson : setJson)((current) => updateEventAlerts(current, update));
|
|
74
76
|
},
|
|
75
|
-
[setJson, setIvJson, setCalendarJson],
|
|
77
|
+
[setJson, setIvJson, setCalendarJson, setIpoJson],
|
|
76
78
|
);
|
|
77
79
|
const [selected, setSelected] = useState(0);
|
|
78
80
|
const [sort, setSort] = useState<{ columnId: string; direction: SortDirection }>({ columnId: "created", direction: "desc" });
|
|
@@ -16,14 +16,22 @@ export const IV_LEVEL_ALERTS_KEY = "ivLevelAlerts";
|
|
|
16
16
|
* every version reads.
|
|
17
17
|
*/
|
|
18
18
|
export const CALENDAR_ALERTS_KEY = "calendarAlerts";
|
|
19
|
+
/**
|
|
20
|
+
* IPO rules, kept apart for the same reason. Gloom Cloud checks them, so it
|
|
21
|
+
* reads this list beside `eventAlerts`.
|
|
22
|
+
*/
|
|
23
|
+
export const IPO_ALERTS_KEY = "ipoAlerts";
|
|
19
24
|
export const MAX_EVENT_ALERTS = 40;
|
|
20
25
|
|
|
21
26
|
/** The config key a rule is kept under. */
|
|
22
27
|
export const eventAlertStoreKey = (rule: Pick<EventAlertRule, "kind">) =>
|
|
23
|
-
rule.kind === "iv_level" ? IV_LEVEL_ALERTS_KEY
|
|
28
|
+
rule.kind === "iv_level" ? IV_LEVEL_ALERTS_KEY
|
|
29
|
+
: rule.kind === "calendar_event" ? CALENDAR_ALERTS_KEY
|
|
30
|
+
: rule.kind === "ipo" ? IPO_ALERTS_KEY
|
|
31
|
+
: EVENT_ALERTS_KEY;
|
|
24
32
|
|
|
25
33
|
/** Every config key event alerts are kept under. */
|
|
26
|
-
export const EVENT_ALERT_STORE_KEYS = [EVENT_ALERTS_KEY, IV_LEVEL_ALERTS_KEY, CALENDAR_ALERTS_KEY] as const;
|
|
34
|
+
export const EVENT_ALERT_STORE_KEYS = [EVENT_ALERTS_KEY, IV_LEVEL_ALERTS_KEY, CALENDAR_ALERTS_KEY, IPO_ALERTS_KEY] as const;
|
|
27
35
|
type EventAlertStoreKey = (typeof EVENT_ALERT_STORE_KEYS)[number];
|
|
28
36
|
|
|
29
37
|
export interface EventAlertRule {
|
|
@@ -38,9 +38,8 @@ import {
|
|
|
38
38
|
parseAlertCommandValues,
|
|
39
39
|
parseAlertShortcutValues,
|
|
40
40
|
} from "./command";
|
|
41
|
-
import { POLL_INTERVAL_MS, POLL_SECONDS_KEY } from "./constants";
|
|
41
|
+
import { ALERTS_PLUGIN_ID, POLL_INTERVAL_MS, POLL_SECONDS_KEY } from "./constants";
|
|
42
42
|
import { alertHistoryLockNote, alertsHeadless } from "./headless";
|
|
43
|
-
import { AlertsPane } from "./pane";
|
|
44
43
|
import { alertsCliCommand } from "./cli";
|
|
45
44
|
import {
|
|
46
45
|
createQuoteErrorMessage,
|
|
@@ -81,7 +80,7 @@ function slashSpellsListing(symbol: string): boolean {
|
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
export const alertsPlugin: GloomPlugin = {
|
|
84
|
-
id:
|
|
83
|
+
id: ALERTS_PLUGIN_ID,
|
|
85
84
|
name: "Alerts",
|
|
86
85
|
version: "1.0.0",
|
|
87
86
|
description: "Price, valuation, daily technical and event alerts",
|
|
@@ -278,11 +277,12 @@ export const alertsPlugin: GloomPlugin = {
|
|
|
278
277
|
ctx.registerCommand({
|
|
279
278
|
id: "set-event-alert",
|
|
280
279
|
label: "Add Event Alert",
|
|
281
|
-
description: "Create an alert before earnings, CPI or FOMC, when earnings results are out, or for filings, news, IV levels, hiring and market moves",
|
|
280
|
+
description: "Create an alert before earnings, CPI or FOMC, when earnings results are out, or for filings, news, IPOs, IV levels, hiring and market moves",
|
|
282
281
|
keywords: [
|
|
283
282
|
"alert", "event", "congress", "13f", "fund", "member", "follow", "earnings", "cpi", "fomc", "days before",
|
|
284
283
|
"filing", "filings", "sec", "8-k", "news", "keyword", "analyst", "52-week", "unusual volume", "volume",
|
|
285
284
|
"short interest", "insider", "catalyst", "implied volatility", "iv spike", "iv level", "risk reversal", "skew", "options flow", "perps", "funding", "hiring", "jobs",
|
|
285
|
+
"ipo", "going public", "listing",
|
|
286
286
|
],
|
|
287
287
|
category: "data",
|
|
288
288
|
wizardLayout: "form",
|
|
@@ -463,7 +463,7 @@ export const alertsPlugin: GloomPlugin = {
|
|
|
463
463
|
reportLockNote: alertHistoryLockNote,
|
|
464
464
|
name: "Alerts",
|
|
465
465
|
icon: "A",
|
|
466
|
-
component: AlertsPane,
|
|
466
|
+
get component(): typeof import("./pane").AlertsPane { const { AlertsPane } = require("./pane"); return AlertsPane; },
|
|
467
467
|
defaultPosition: "right",
|
|
468
468
|
defaultMode: "floating",
|
|
469
469
|
defaultFloatingSize: { width: 82, height: 20 },
|
|
@@ -6,7 +6,7 @@ import type { EventAlertRule } from "./events";
|
|
|
6
6
|
export const MACRO_EVENT_NAMES: Record<MacroAlertEvent, string> = { cpi: "CPI release", fomc: "FOMC decision" };
|
|
7
7
|
/** CPI and FOMC and any other calendar release are one kind of alert to the reader, whichever list keeps the rule. */
|
|
8
8
|
export const RESEARCH_LABELS: Record<ResearchAlertKind, string> = {
|
|
9
|
-
perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", calendar_event: "Macro event", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", iv_level: "IV level", options_flow: "Options flow", hiring_move: "Hiring move",
|
|
9
|
+
perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", calendar_event: "Macro event", ipo: "IPO", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", iv_level: "IV level", options_flow: "Options flow", hiring_move: "Hiring move",
|
|
10
10
|
};
|
|
11
11
|
/** What an IV level rule follows, as the form, a list and a notification name it. */
|
|
12
12
|
export const IV_LEVEL_NAMES: Record<IvLevelMetric, string> = { atm: "30d ATM IV", rr: "30d 25D RR" };
|
|
@@ -46,6 +46,8 @@ export const researchWizardFields: Field[] = RESEARCH_FORM_KINDS.flatMap(kind =>
|
|
|
46
46
|
: kind === "macro_event" ? [{key:"event", label:"Release", type:"select", defaultValue:"cpi", options:MACRO_ALERT_EVENTS.map((value) => ({value, label:MACRO_EVENT_NAMES[value]}))}]
|
|
47
47
|
// HIRE covers listings worldwide; a non-US one is written with its venue.
|
|
48
48
|
: kind === "hiring_move" ? [{key:"symbol", label:"Symbol", type:"text", placeholder:"RIVN or ADYEN:AMS", required:true}]
|
|
49
|
+
// A deal on the IPO calendar has no listing to name yet: it is followed by its name.
|
|
50
|
+
: kind === "ipo" ? [{key:"keyword", label:"Company or keyword", type:"text", placeholder:"Stripe", required:true}]
|
|
49
51
|
: [{key:"symbol",label:kind === "news_keyword" ? "Symbol (optional)" : kind === "options_flow" ? "Symbol (blank: portfolio and watchlists)" : "Symbol",type:"text",placeholder:"AAPL", required:kind !== "news_keyword" && kind !== "options_flow"},
|
|
50
52
|
{key:"exchange",label:"US exchange",type:"select",defaultValue:"US",options:choices(["US","NASDAQ","NYSE","ARCA","AMEX","BATS","OTC"])}];
|
|
51
53
|
if (kind === "earnings_date") {
|
|
@@ -92,6 +94,7 @@ export function createResearchAlert(kind: string, values: Record<string,string>,
|
|
|
92
94
|
if (!value) throw new Error(kind === "options_flow"
|
|
93
95
|
? "Check the symbol and US exchange. The minimum premium is $50,000."
|
|
94
96
|
: kind === "hiring_move" ? "Check the symbol. Write a non-US listing with its exchange, as ADYEN:AMS."
|
|
97
|
+
: kind === "ipo" ? "Name a company or keyword of 2 to 80 characters, such as Stripe."
|
|
95
98
|
: kind === "iv_level"
|
|
96
99
|
? "Check the symbol and US exchange. An ATM IV level is 1 to 500 (%), a risk reversal -50 to 50 (vol pts)."
|
|
97
100
|
: kind === "iv_spike"
|
|
@@ -142,6 +145,7 @@ export function researchAlertDescription(rule: EventAlertRule): string {
|
|
|
142
145
|
case "news_keyword": return `${symbol} · ${config.keyword}`;
|
|
143
146
|
case "analyst_change": case "insider_trade": return `${symbol} · ${config.direction}`;
|
|
144
147
|
case "hiring_move": return `${symbol} · ${config.direction === "either" ? "accelerating or slowing" : config.direction}`;
|
|
148
|
+
case "ipo": return config.keyword ?? "";
|
|
145
149
|
case "fifty_two_week": return `${symbol} · 52w ${config.direction}`;
|
|
146
150
|
case "unusual_volume": return `${symbol} · ≥${config.threshold}× 20d`;
|
|
147
151
|
case "short_interest_change": return `${symbol} · ${config.direction} ≥${config.threshold}%`;
|
|
@@ -15,6 +15,7 @@ const RESEARCH_ALERT_KINDS = [
|
|
|
15
15
|
"options_flow",
|
|
16
16
|
"perps_threshold",
|
|
17
17
|
"hiring_move",
|
|
18
|
+
"ipo",
|
|
18
19
|
] as const
|
|
19
20
|
export type ResearchAlertKind = (typeof RESEARCH_ALERT_KINDS)[number]
|
|
20
21
|
export interface ResearchAlertConfig {
|
|
@@ -162,6 +163,8 @@ export function normalizeResearchRule(
|
|
|
162
163
|
: ""
|
|
163
164
|
if (!/^[A-Z0-9][A-Z0-9.:-]{0,31}$/.test(symbol)) return null
|
|
164
165
|
result.symbol = symbol
|
|
166
|
+
} else if (kind === "ipo") {
|
|
167
|
+
// A company or keyword looked for on the IPO calendar: no listing yet, so no symbol.
|
|
165
168
|
} else if (
|
|
166
169
|
(kind !== "news_keyword" && kind !== "options_flow") ||
|
|
167
170
|
input.symbol
|
|
@@ -253,6 +256,21 @@ export function normalizeResearchRule(
|
|
|
253
256
|
result.keyword = keyword
|
|
254
257
|
break
|
|
255
258
|
}
|
|
259
|
+
case "ipo": {
|
|
260
|
+
// Kept as typed, for the lists; Gloom Cloud matches it in any case.
|
|
261
|
+
const keyword =
|
|
262
|
+
typeof input.keyword === "string"
|
|
263
|
+
? input.keyword.trim().replace(/\s+/g, " ")
|
|
264
|
+
: ""
|
|
265
|
+
if (
|
|
266
|
+
keyword.length < 2 ||
|
|
267
|
+
keyword.length > 80 ||
|
|
268
|
+
/[\u0000-\u001f]/.test(keyword)
|
|
269
|
+
)
|
|
270
|
+
return null
|
|
271
|
+
result.keyword = keyword
|
|
272
|
+
break
|
|
273
|
+
}
|
|
256
274
|
case "analyst_change":
|
|
257
275
|
direction(["any", "upgrade", "downgrade"], "any")
|
|
258
276
|
break
|
|
@@ -36,7 +36,8 @@ const pane = (templateId: string, options?: PaneTemplateCreateOptions): AlertTar
|
|
|
36
36
|
* The one map from what an alert follows to what it opens, for every rule kind and every delivered alert:
|
|
37
37
|
* a perpetual opens PERP on its market; a dated event the pane its date comes from (ERN, CPI, WIRP, ECO); catalyst,
|
|
38
38
|
* Congress and 13F rules their calendar or list, on the company or fund they name; an IV level the symbol's
|
|
39
|
-
* OVDV, whose fits it reads; a hiring move HIRE on the company;
|
|
39
|
+
* OVDV, whose fits it reads; a hiring move HIRE on the company; an IPO the IPO calendar searched for its
|
|
40
|
+
* company or keyword; any other rule on a security
|
|
40
41
|
* that listing's DES. A rule on no security opens the pane it watches (SRCH for a news keyword, FLOW for flow
|
|
41
42
|
* on the user's lists, FIRST for breaking news). Null when there is nothing to open.
|
|
42
43
|
*/
|
|
@@ -58,6 +59,8 @@ export function alertTarget(subject: AlertSubject): AlertTarget | null {
|
|
|
58
59
|
case "breaking_news": if (!symbol) return pane("news-breaking-pane"); break;
|
|
59
60
|
// A hiring move opens HIRE on the company, where its Moves tab says why.
|
|
60
61
|
case "hiring_move": return symbol ? pane("hiring-pane", { symbol }) : null;
|
|
62
|
+
// An IPO opens the calendar searched for what the rule follows, its deal there whether it trades yet or not.
|
|
63
|
+
case "ipo": return pane("ipo-calendar-pane", subject.keyword ? { arg: subject.keyword } : undefined);
|
|
61
64
|
}
|
|
62
65
|
return symbol ? { kind: "description", symbol, ...(subject.exchange ? { exchange: subject.exchange } : {}) } : null;
|
|
63
66
|
}
|
|
@@ -6,7 +6,7 @@ export const attentionHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
6
6
|
options: [{ key: "tab", type: "enum", values: ATTENTION_TABS.map(({ value }) => ({ value })), defaultValue: "ranking", description: "Rankings, abnormal attention, groups, history or evidence." },
|
|
7
7
|
{ key: "window", type: "enum", values: WINDOWS.map(({ value }) => ({ value })), defaultValue: "now", description: "Latest completed publication hour, UTC day or trailing week." }],
|
|
8
8
|
discovery: { aliases: ["ATTN"], dataRequirements: ["Privacy-qualified published research counts"],
|
|
9
|
-
limitations: ["Pro dataset with three-row preview and latest history point.", "Suppressed hours are absent, never zero.", "
|
|
9
|
+
limitations: ["Pro dataset with three-row preview and latest history point.", "Suppressed hours are absent, never zero.", "Counts come from signed-in Gloom users and are not representative of all investors."] },
|
|
10
10
|
describe: "Gloom research attention (Pro)",
|
|
11
11
|
async load(args, ctx) {
|
|
12
12
|
const argument = Array.isArray(args.argument) ? args.argument.join(" ") : args.argument;
|
|
@@ -2,14 +2,12 @@ import type { PluginModule } from "../plugin-module";
|
|
|
2
2
|
import { attentionCache } from "./client";
|
|
3
3
|
import { attentionWindow } from "./model";
|
|
4
4
|
import { attentionHeadless } from "./headless";
|
|
5
|
-
import { TrendingPane } from "./trending";
|
|
6
|
-
import { AttentionPane } from "./pane";
|
|
7
5
|
import { ATTENTION_SERIES } from "./series";
|
|
8
6
|
import { ATTENTION_LISTED } from "./listing";
|
|
9
7
|
export const attentionModule: PluginModule = {
|
|
10
|
-
panes: [{ id: "attention", name: "Research Attention", icon: "A", component: AttentionPane, defaultPosition: "right", defaultMode: "floating",
|
|
8
|
+
panes: [{ id: "attention", name: "Research Attention", icon: "A", get component(): typeof import("./pane").AttentionPane { const { AttentionPane } = require("./pane"); return AttentionPane; }, defaultPosition: "right", defaultMode: "floating",
|
|
11
9
|
defaultFloatingSize: { width: 126, height: 32 }, tableExport: true, headless: attentionHeadless },
|
|
12
|
-
{ id: "attention-trending", reportFreshness: { status: "not-a-feed", basis: "hourly publication" }, name: "Gloom Trending", icon: "A", component: TrendingPane, defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 58, height: 12 } }],
|
|
10
|
+
{ id: "attention-trending", reportFreshness: { status: "not-a-feed", basis: "hourly publication" }, name: "Gloom Trending", icon: "A", get component(): typeof import("./trending").TrendingPane { const { TrendingPane } = require("./trending"); return TrendingPane; }, defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 58, height: 12 } }],
|
|
13
11
|
paneTemplates: [{ id: "attention-pane", paneId: "attention", label: "Research Attention", listed: ATTENTION_LISTED,
|
|
14
12
|
description: "Pro research attention: rankings, abnormal activity, sectors, countries and privacy-qualified hourly history.",
|
|
15
13
|
keywords: ["attention", "trending", "research", "activity"], shortcut: { prefix: "ATTN", argKind: "text", argPlaceholder: "TICKER", argOptional: true },
|