gloomberb 0.18.2 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -3,23 +3,43 @@ import { CLI_COMMAND_GROUPS } from "../../../cli/help";
3
3
  import { dryRunNote } from "../../../cli/helpers";
4
4
  import { requireArg, takeOption } from "../../../cli/commands/command-utils";
5
5
  import { requireQuotedListing } from "../../../cli/quote-answer";
6
- import { EXCHANGE_OPTION, listingReadNotes, requireListingArg } from "../../../cli/listing-arg";
6
+ import { EXCHANGE_OPTION, listingReadNotes, requireListingArg, usListingsOnlyMessage } from "../../../cli/listing-arg";
7
+ import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
7
8
  import { saveConfig } from "../../../data/config/store";
9
+ import { resumeStateKey } from "../../registry/plugin-state";
8
10
  import type { CliCommandDef } from "../../../types/plugin";
9
11
  import type { Quote } from "../../../types/financials";
10
12
  import { getActiveQuoteDisplay } from "../../../market-data/market/status";
11
13
  import { cliStyles } from "../../../utils/cli-output";
12
- import { ambiguousNumberMessage, meantThousandsDot, parsePastedNumber, thousandsDotReadings, type ThousandsDotReadings } from "../../../utils/decimal-comma";
14
+ import { canonicalExchange, isUsListingExchange } from "../../../utils/exchanges";
15
+ import { meantThousandsDot, parsePastedNumber, thousandsDotReadings, type ThousandsDotReadings } from "../../../utils/decimal-comma";
13
16
  import { newYorkToday } from "../earnings/board-model";
17
+ import { fetchEconCalendarFrom } from "../econ/calendar-source";
18
+ import { COUNTRY_FILTER_HINT, dateKey, loadCalendar, parseCountryFilter, type CountryFilter } from "../econ/calendar-model";
19
+ import { isAllDayEvent, type EconEvent } from "../econ/types";
20
+ import {
21
+ calendarEventLabel,
22
+ calendarNameWords,
23
+ expectsFigures,
24
+ macroEventNamed,
25
+ matchCalendarEvent,
26
+ releaseInstant,
27
+ releaseRowOn,
28
+ type CalendarEventChoice,
29
+ } from "./calendar-events";
14
30
  import { createAlert, rearmAlert, serializeAlerts } from "./alert-engine";
15
31
  import { ALERTS_KEY, ALERTS_PLUGIN_ID } from "./constants";
16
- import { EVENT_ALERTS_KEY, MAX_EVENT_ALERTS, eventAlertTarget, toggleEventAlert, type EventAlertRule } from "./events";
32
+ import { MAX_EVENT_ALERTS, eventAlertTarget, toggleEventAlert, writeEventAlertStores, type EventAlertRule } from "./events";
17
33
  import { alertKindLabel } from "./history";
18
- import { MAX_LEAD_DAYS, type MacroAlertEvent } from "./research-rules";
34
+ import { IV_LEVEL_CHECKS_KEY, readIvLevelChecks, rearmIvLevelAlert, type IvLevelCheck } from "./iv-level";
35
+ import { parseAlertPrice, readAlertPrice } from "./price";
36
+ import { farTargetMessage } from "./quotes";
37
+ import { MACRO_EVENT_NAMES, createResearchAlert, formatPremium, ivLevelUnitError, parseAmount, parseIvLevel, researchAlertDescription } from "./research-builder";
38
+ import { IV_LEVEL_RANGES, MAX_LEAD_DAYS, type IvLevelMetric, type MacroAlertEvent } from "./research-rules";
19
39
  import {
20
- MACRO_EVENT_CLI_NAMES,
21
40
  createScheduledAlert,
22
41
  eventDateUnavailableMessage,
42
+ followsRelease,
23
43
  leadStartDate,
24
44
  lookupScheduledDates,
25
45
  noUpcomingEventMessage,
@@ -31,6 +51,8 @@ import {
31
51
  scheduledEventName,
32
52
  scheduledPhase,
33
53
  scheduledTargetKey,
54
+ scheduledToday,
55
+ scheduledTriggerText,
34
56
  type ScheduledDateLookup,
35
57
  type ScheduledEventSources,
36
58
  type ScheduledEventTarget,
@@ -48,8 +70,11 @@ import type { AlertCondition, AlertRule } from "./types";
48
70
  */
49
71
  const PRICE_DELIVERY = "the app alerts while it is running; sign in with Cloud sync for email or phone alerts when it is closed";
50
72
 
51
- const ADD_USAGE = "Usage: gloomberb alerts add <symbol> <above|below|crosses> <price>, alerts add <symbol> earnings [--days <n>], or alerts add <CPI|FOMC> [--days <n>]";
73
+ const ADD_USAGE = "Usage: gloom alerts add <symbol> <above|below|crosses> <price>, alerts add <symbol> earnings [--days <n> | results], alerts add <calendar event> [--country <code>] [--days <n>], alerts add [symbol] news <keyword>, alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks], alerts add <symbol> <iv|rr> <above|below> <level>, or alerts add <OCC contract> iv spike <points>";
52
74
  const DAYS_RULE = `--days takes a whole number of days from 1 to ${MAX_LEAD_DAYS}; without it the alert goes off 1 day before.`;
75
+ /** The event form every refusal of `alerts add` names: any release in the economic calendar, CPI and FOMC among them. */
76
+ const EVENT_FORM = `a release in the economic calendar by name (alerts add "NBS Manufacturing PMI" --country CN, alerts add CN PMI, alerts add CPI --days 1)`;
77
+ const COUNTRY_RULE = "--country is for an alert on a calendar event, such as alerts add PMI --country CN.";
53
78
 
54
79
  /** What `alerts add` was asked for, before any listing or date is looked up. */
55
80
  export type AlertAddRequest =
@@ -58,48 +83,108 @@ export type AlertAddRequest =
58
83
  /** A price typed as 81.000, which the last price may show was 81000 with a dot between thousands. */
59
84
  thousandsDot?: ThousandsDotReadings & { text: string };
60
85
  }
61
- | { kind: "earnings"; symbol: string; leadDays: number }
62
- | { kind: "macro"; event: MacroAlertEvent; leadDays: number };
86
+ /** `results`: once the report's actual EPS is out, rather than `leadDays` before the date. */
87
+ | { kind: "earnings"; symbol: string; leadDays: number; results?: true }
88
+ | { kind: "macro"; event: MacroAlertEvent; leadDays: number }
89
+ /** A release in the economic calendar by name, matched against the calendar once it is read; `country` narrows it. */
90
+ | { kind: "release"; query: string; country: CountryFilter | null; leadDays: number }
91
+ /** Gloom Cloud's research rules, as Add Event Alert makes them; a news or flow rule without a symbol follows every name (news) or your lists (flow). */
92
+ | { kind: "news"; symbol: string | null; keyword: string }
93
+ | { kind: "flow"; symbol: string | null; premium: number; side: "any" | "calls" | "puts"; print: "any" | "sweep" | "block" }
94
+ /** The symbol's 30-day ATM IV (`atm`, in %) or 25-delta risk reversal (`rr`, in vol points) above or below a level, checked by the running app. */
95
+ | { kind: "iv_level"; symbol: string; metric: IvLevelMetric; direction: "above" | "below"; level: number }
96
+ /** One OCC contract's IV rising by `points`. */
97
+ | { kind: "iv_spike"; contract: string; points: number };
98
+
99
+ /** The `alerts add` forms Gloom Cloud's research rules take: they follow US listings. */
100
+ type ResearchAddRequest = Extract<AlertAddRequest, { kind: "news" | "flow" | "iv_level" | "iv_spike" }>;
63
101
 
64
102
  const CONDITIONS: readonly AlertCondition[] = ["above", "below", "crosses"];
65
103
 
66
104
  /** The first word of a price typed with a space between thousands and no quotes (`81 000`): one to three digits. */
67
105
  const LEADING_GROUP = /^[+-]?\d{1,3}$/;
68
106
 
107
+ /** `--country` as typed: a country code or the calendar's regions (`CN`, `uk`, `EU`); null when not given. Throws when it names none. */
108
+ function readCountryOption(country: string | null | undefined): CountryFilter | null {
109
+ if (country === undefined) return null;
110
+ if (country === null) throw new Error(`--country takes a two-letter country code, such as --country CN. ${COUNTRY_FILTER_HINT}`);
111
+ const parsed = parseCountryFilter(country);
112
+ if ("error" in parsed) throw new Error(`${parsed.error} ${COUNTRY_FILTER_HINT}`);
113
+ return parsed.filter === "all" ? null : parsed.filter;
114
+ }
115
+
69
116
  /**
70
- * Reads `alerts add` arguments: `<symbol> above 250`, `<symbol> earnings`, or
71
- * `CPI` / `FOMC`, with `--days` (already taken out of `args`, `null` when the
72
- * flag came without a value) for the event forms only, and `--exchange`, which
73
- * a command to type again keeps. Throws a message that names what the command
74
- * takes.
117
+ * A release named by its words (`NBS PMI`, `Industrial Profits`), a country
118
+ * code first standing for `--country` (`CN PMI`). CPI and FOMC, in the US or
119
+ * with no country, are the macro alerts whose dates come from their own
120
+ * calendars.
75
121
  */
76
- export function parseAlertAddArgs(args: readonly string[], days: string | null | undefined, exchange?: string | null): AlertAddRequest {
122
+ function releaseRequest(args: readonly string[], country: CountryFilter | null, leadDays: number): AlertAddRequest {
123
+ const words = args.join(" ").trim().split(/\s+/);
124
+ let query = words.join(" ");
125
+ if (!country && words.length >= 2 && /^[a-z]{2}$/i.test(words[0]!)) {
126
+ const parsed = parseCountryFilter(words[0]!);
127
+ if (!("error" in parsed) && parsed.filter !== "all") {
128
+ country = parsed.filter;
129
+ query = words.slice(1).join(" ");
130
+ }
131
+ }
132
+ const event = parseMacroEventName(query);
133
+ if (event && (!country || country === "US")) return { kind: "macro", event, leadDays };
134
+ return { kind: "release", query, country, leadDays };
135
+ }
136
+
137
+ /**
138
+ * Reads `alerts add` arguments: `<symbol> above 250`, `<symbol> earnings`,
139
+ * `<symbol> earnings results`, or a release in the economic calendar by name
140
+ * (`CPI`, `FOMC`, `NBS PMI`, `CN PMI`), with `--days` (already taken out of
141
+ * `args`, `null` when the flag came without a value) and `--country` for the
142
+ * event forms only, and `--exchange`, which a command to type again keeps.
143
+ * Throws a message that names what the command takes.
144
+ */
145
+ export function parseAlertAddArgs(
146
+ args: readonly string[],
147
+ days: string | null | undefined,
148
+ exchange?: string | null,
149
+ country?: string | null,
150
+ ): AlertAddRequest {
77
151
  const [first, second, third, ...rest] = args;
78
152
  if (!first) throw new Error(ADD_USAGE);
153
+ const countryFilter = readCountryOption(country);
154
+ const research = parseResearchAddArgs(args);
155
+ if (research) {
156
+ if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not news, flow or IV alerts.");
157
+ if (country !== undefined) throw new Error(COUNTRY_RULE);
158
+ return research;
159
+ }
79
160
  const leadDays = () => {
80
161
  const parsed = days === null ? null : parseLeadDays(days);
81
162
  if (parsed == null) throw new Error(DAYS_RULE);
82
163
  return parsed;
83
164
  };
84
165
  if (second === undefined) {
85
- const event = parseMacroEventName(first);
86
- if (event) return { kind: "macro", event, leadDays: leadDays() };
87
166
  if (first.toLowerCase() === "earnings") {
88
- throw new Error("Name the company: gloomberb alerts add <symbol> earnings, such as alerts add CRWV earnings --days 3.");
167
+ throw new Error("Name the company: gloom alerts add <symbol> earnings, such as alerts add CRWV earnings --days 3.");
89
168
  }
90
- throw new Error(
91
- `Unknown event "${first}". Event alerts take ${MACRO_EVENT_CLI_NAMES} (alerts add CPI --days 1) or a symbol with earnings (alerts add CRWV earnings --days 3); price alerts take a condition and a price (alerts add AAPL above 250).`,
92
- );
169
+ return releaseRequest(args, countryFilter, leadDays());
93
170
  }
171
+ if (second.toLowerCase() === "earnings" && country !== undefined) throw new Error(COUNTRY_RULE);
94
172
  if (second.toLowerCase() === "earnings") {
95
- if (third !== undefined) throw new Error(`Earnings alerts take a symbol and --days, not "${third}". ${ADD_USAGE}`);
173
+ if (third?.toLowerCase() === "results" && rest.length === 0) {
174
+ if (days !== undefined) throw new Error("An alert for results goes off once they are out and takes no --days.");
175
+ return { kind: "earnings", symbol: first, leadDays: 0, results: true };
176
+ }
177
+ if (third !== undefined) throw new Error(`Earnings alerts take a symbol and --days or results, not "${[third, ...rest].join(" ")}". ${ADD_USAGE}`);
96
178
  return { kind: "earnings", symbol: first, leadDays: leadDays() };
97
179
  }
98
180
  const condition = second.toLowerCase() as AlertCondition;
99
181
  if (!CONDITIONS.includes(condition)) {
100
- throw new Error(`Unknown alert "${second}". Use above, below or crosses with a price, or earnings with --days.`);
182
+ // Words with no price after them name a release (CN PMI, Industrial Profits); a price ends a mistyped price alert.
183
+ if (parseAlertPrice(args.at(-1)!).kind === "invalid") return releaseRequest(args, countryFilter, leadDays());
184
+ throw new Error(`Unknown alert "${second}". Use above, below or crosses with a price, earnings with --days, news with a keyword, flow, iv or rr with above or below and a level, iv spike on an OCC contract, or ${EVENT_FORM}.`);
101
185
  }
102
- if (days !== undefined) throw new Error("--days is for earnings and CPI or FOMC alerts, not price alerts.");
186
+ if (country !== undefined) throw new Error(COUNTRY_RULE);
187
+ if (days !== undefined) throw new Error("--days is for earnings and calendar event alerts, not price alerts.");
103
188
  if (third === undefined) throw new Error(ADD_USAGE);
104
189
  const retype = (price: number) => retypeCommand(first, condition, price, exchange);
105
190
  // `81 000` without quotes arrives as 81 and a stray 000; neither reading is taken for granted.
@@ -109,17 +194,122 @@ export function parseAlertAddArgs(args: readonly string[], days: string | null |
109
194
  throw new Error(`${[third, ...rest].join(" ")} could be ${spaced.value} (thousands) or ${typed} and a stray ${rest.join(" ")}; type ${retype(spaced.value)} or ${retype(typed)}`);
110
195
  }
111
196
  // A price pasted from a sheet (1.350,5, 1,350.5, 1 350,5) reads as it is written; `1,350` could be either, so it asks.
112
- const pasted = parsePastedNumber(third);
113
- if (pasted.kind === "ambiguous") throw new Error(ambiguousNumberMessage(third, pasted));
114
- if (pasted.kind === "invalid") throw new Error("Alert price must be a finite number.");
115
- if (rest.length > 0) throw new Error(`A price alert takes one price, not "${rest.join(" ")}" after ${third}; type ${retype(pasted.value)}`);
197
+ const price = readAlertPrice(third);
198
+ if (rest.length > 0) throw new Error(`A price alert takes one price, not "${rest.join(" ")}" after ${third}; type ${retype(price)}`);
116
199
  const thousandsDot = thousandsDotReadings(third);
117
- return { kind: "price", symbol: first, condition, price: pasted.value, ...(thousandsDot ? { thousandsDot: { ...thousandsDot, text: third.trim() } } : {}) };
200
+ return { kind: "price", symbol: first, condition, price, ...(thousandsDot ? { thousandsDot: { ...thousandsDot, text: third.trim() } } : {}) };
201
+ }
202
+
203
+ const OCC_CONTRACT = /^(?:O:)?[A-Z.]{1,6}\d{6}[CP]\d{8}$/i;
204
+ const LEVEL_DIRECTIONS = ["above", "below"] as const;
205
+
206
+ /** A level typed for an IV level rule, with its unit or without (`50`, `50%`, `-5.5 pts`), refused with its unit and range when it does not read. */
207
+ function readLevel(metric: IvLevelMetric, typed: string | undefined, symbol: string): number {
208
+ const word = metric === "atm" ? "iv" : "rr";
209
+ const example = metric === "atm" ? `alerts add ${symbol} iv above 50` : `alerts add ${symbol} rr below -5`;
210
+ if (!typed) throw new Error(`Name the level: gloom ${example}.`);
211
+ const unitError = ivLevelUnitError(metric, typed);
212
+ if (unitError) throw new Error(`${unitError}: gloom ${example}.`);
213
+ const level = parseIvLevel(typed);
214
+ const { min, max } = IV_LEVEL_RANGES[metric];
215
+ if (!Number.isFinite(level) || level < min || level > max) {
216
+ throw new Error(metric === "atm"
217
+ ? `An ${word} level is the 30-day ATM IV in percent, ${min} to ${max}, not "${typed}": gloom ${example}.`
218
+ : `An ${word} level is the 30-day 25-delta risk reversal in vol points, ${min} to ${max}, not "${typed}": gloom ${example}.`);
219
+ }
220
+ return level;
221
+ }
222
+
223
+ /**
224
+ * The research forms of `alerts add`, the ones Add Event Alert makes: `[symbol] news <keyword>`,
225
+ * `[symbol] flow [premium] [calls|puts] [sweeps|blocks]`, `<symbol> iv|rr <above|below> <level>` and
226
+ * `<OCC contract> iv spike <points>`. Null for any other form. A bare `news` or `flow` with no symbol
227
+ * follows every name or your lists; `NEWS above 10` is still a price alert on a symbol.
228
+ */
229
+ function parseResearchAddArgs(args: readonly string[]): ResearchAddRequest | null {
230
+ const [first = "", second, third, ...rest] = args;
231
+ const word = (value: string | undefined) => value?.toLowerCase() ?? "";
232
+ const isCondition = (value: string | undefined) => (CONDITIONS as readonly string[]).includes(word(value)) || word(value) === "earnings";
233
+ const lead = word(first);
234
+ const [symbol, kind, tail] = (lead === "news" || lead === "flow") && !isCondition(second)
235
+ ? [null, lead, args.slice(1)] as const
236
+ : [first, word(second), args.slice(2)] as const;
237
+ if (kind === "news") {
238
+ const keyword = tail.join(" ").trim();
239
+ if (!keyword) throw new Error(`Name a keyword or phrase: gloom alerts add ${symbol ?? "AAPL"} news buyback, or alerts add news "rate cut" for any company.`);
240
+ return { kind: "news", symbol, keyword };
241
+ }
242
+ if (kind === "flow") {
243
+ let premium = 1_000_000;
244
+ let side: "any" | "calls" | "puts" = "any";
245
+ let print: "any" | "sweep" | "block" = "any";
246
+ for (const token of tail) {
247
+ const lower = token.toLowerCase();
248
+ if (lower === "calls" || lower === "call") side = "calls";
249
+ else if (lower === "puts" || lower === "put") side = "puts";
250
+ else if (lower === "sweeps" || lower === "sweep") print = "sweep";
251
+ else if (lower === "blocks" || lower === "block") print = "block";
252
+ else if (Number.isFinite(parseAmount(token))) premium = parseAmount(token);
253
+ else throw new Error(`A flow alert takes a minimum premium (1m, $250k), calls or puts, and sweeps or blocks, not "${token}".`);
254
+ }
255
+ if (premium < 50_000) throw new Error(`The minimum premium is $50,000, not ${formatPremium(premium)}: Gloom Cloud records no smaller print.`);
256
+ return { kind: "flow", symbol, premium, side, print };
257
+ }
258
+ if (!symbol || (kind !== "iv" && kind !== "rr")) return null;
259
+ if (OCC_CONTRACT.test(symbol)) {
260
+ if (kind !== "iv" || word(third) !== "spike") {
261
+ throw new Error(`An option contract takes an IV spike in vol points: gloom alerts add ${symbol.toUpperCase()} iv spike 5. For a level, name the underlying: alerts add ${symbol.replace(/^O:/i, "").match(/^[A-Z.]+/i)![0]!.toUpperCase()} iv above 50.`);
262
+ }
263
+ // Vol points, with the unit or without it: `5`, `5 pts`.
264
+ const typed = rest.join(" ");
265
+ const value = typed && !/%/.test(typed) ? parseIvLevel(typed) : Number.NaN;
266
+ if (!Number.isFinite(value) || value < 0.1 || value > 100) {
267
+ throw new Error(`An IV spike is a rise of 0.1 to 100 vol points: gloom alerts add ${symbol.toUpperCase()} iv spike 5.`);
268
+ }
269
+ return { kind: "iv_spike", contract: symbol.toUpperCase(), points: value };
270
+ }
271
+ const metric: IvLevelMetric = kind === "iv" ? "atm" : "rr";
272
+ const direction = word(third);
273
+ if (!(LEVEL_DIRECTIONS as readonly string[]).includes(direction)) {
274
+ throw new Error(`${kind === "iv" ? "An iv" : "An rr"} alert takes above or below and a level: gloom alerts add ${symbol} ${kind} ${kind === "iv" ? "above 50" : "below -5"}${kind === "iv" ? ", or iv spike on one OCC contract" : ""}.`);
275
+ }
276
+ // The level, with its unit or without it: `50`, `50%`, `-5.5 pts`.
277
+ return { kind: "iv_level", symbol, metric, direction: direction as "above" | "below", level: readLevel(metric, rest.join(" "), symbol) };
118
278
  }
119
279
 
280
+ /** The Add Event Alert form's values for a research request, so the CLI makes the rule the dialog makes. */
281
+ function researchFormValues(request: ResearchAddRequest, exchange: string): { kind: string; values: Record<string, string> } {
282
+ switch (request.kind) {
283
+ case "news": return { kind: "news_keyword", values: { "news_keyword:keyword": request.keyword,
284
+ ...request.symbol ? { "news_keyword:symbol": request.symbol, "news_keyword:exchange": exchange } : {} } };
285
+ case "flow": return { kind: "options_flow", values: { "options_flow:threshold": String(request.premium),
286
+ "options_flow:direction": request.side, "options_flow:print": request.print,
287
+ ...request.symbol ? { "options_flow:symbol": request.symbol, "options_flow:exchange": exchange } : {} } };
288
+ case "iv_level": return { kind: "iv_level", values: { "iv_level:symbol": request.symbol, "iv_level:exchange": exchange,
289
+ "iv_level:metric": request.metric, "iv_level:direction": request.direction, "iv_level:threshold": String(request.level) } };
290
+ case "iv_spike": return { kind: "iv_spike", values: { "iv_spike:contract": request.contract, "iv_spike:threshold": String(request.points) } };
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Who checks a research rule and how it reaches you, as docs/usage.md states it: an IV level is read by the
296
+ * running app; the rest by Gloom Cloud for a signed-in account with Cloud sync, IV spikes and flow on Pro.
297
+ */
298
+ function researchDelivery(kind: ResearchAddRequest["kind"]): string {
299
+ if (kind === "iv_level") return "the app checks it every 15 minutes while it is running, on the same option fits as OVDV";
300
+ const plan = kind === "news" ? "" : " on Gloom Pro";
301
+ return `Gloom Cloud checks it for a signed-in account with Cloud sync${plan} and sends it to your phone`;
302
+ }
303
+
304
+ const isResearchRequest = (request: AlertAddRequest): request is ResearchAddRequest =>
305
+ request.kind === "news" || request.kind === "flow" || request.kind === "iv_level" || request.kind === "iv_spike";
306
+
307
+ /** The US venue a research rule keeps: one typed and known to it, else any US venue. */
308
+ const RESEARCH_EXCHANGES = new Set(["NASDAQ", "NYSE", "AMEX", "ARCA", "BATS", "OTC"]);
309
+
120
310
  /** `alerts add` as typed, with another price: what an error tells someone to type. */
121
311
  function retypeCommand(symbol: string, condition: AlertCondition, price: number, exchange: string | null | undefined): string {
122
- return `gloomberb alerts add ${symbol} ${condition} ${price}${exchange ? ` --exchange ${exchange}` : ""}`;
312
+ return `gloom alerts add ${symbol} ${condition} ${price}${exchange ? ` --exchange ${exchange}` : ""}`;
123
313
  }
124
314
 
125
315
  /**
@@ -159,19 +349,25 @@ function priceRow(alert: AlertRule): AlertListRow {
159
349
  return { ...alert, kind: "price", target: alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol };
160
350
  }
161
351
 
162
- function eventRow(rule: EventAlertRule, today: string, eventDate: string | null | undefined): AlertListRow {
352
+ function eventRow(rule: EventAlertRule, now: number, eventDate: string | null | undefined, check?: IvLevelCheck): AlertListRow {
163
353
  const scheduled = readScheduledRule(rule);
164
354
  if (!scheduled) {
165
- return { id: rule.id, kind: rule.kind, target: eventAlertTarget(rule), status: rule.status, createdAt: rule.createdAt };
355
+ const status = rule.status === "active" && rule.triggeredAt ? "triggered" : rule.status;
356
+ return { id: rule.id, kind: rule.kind, target: eventAlertTarget(rule), status, createdAt: rule.createdAt,
357
+ ...check ? { lastCheck: check } : {}, ...rule.triggeredAt ? { triggeredAt: rule.triggeredAt } : {} };
166
358
  }
167
359
  const date = eventDate ?? null;
168
360
  return {
169
361
  id: rule.id,
170
362
  kind: rule.kind,
171
- target: scheduled.target.kind === "earnings" ? scheduled.target.symbol : scheduled.target.event.toUpperCase(),
363
+ target: scheduled.target.kind === "earnings" ? scheduled.target.symbol
364
+ : scheduled.target.kind === "release" ? calendarEventLabel(scheduled.target) : scheduled.target.event.toUpperCase(),
172
365
  leadDays: scheduled.leadDays,
366
+ when: scheduled.trigger === "results" ? "results" : "before",
367
+ // CPI, FOMC and calendar releases also alert with their figures once they print.
368
+ ...followsRelease(scheduled.target) ? { onRelease: true } : {},
173
369
  eventDate: date,
174
- status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays, today),
370
+ status: scheduledAlertStatus({ ...rule, eventDate: date ?? undefined }, scheduled.leadDays, scheduledToday(scheduled.target, now), scheduled.trigger),
175
371
  createdAt: rule.createdAt,
176
372
  };
177
373
  }
@@ -186,7 +382,9 @@ function textRow(row: AlertListRow): Record<string, unknown> {
186
382
  target: row.target,
187
383
  trigger: price
188
384
  ? `${row.condition} ${row.targetPrice}`
189
- : typeof row.leadDays === "number" ? `${plural(row.leadDays, "day")} before` : "",
385
+ : typeof row.leadDays === "number"
386
+ ? scheduledTriggerText({ leadDays: row.leadDays, trigger: row.when === "results" ? "results" : "date", prints: row.onRelease === true })
387
+ : "",
190
388
  eventDate: typeof row.leadDays === "number" ? row.eventDate ?? "not known" : "",
191
389
  status: row.status,
192
390
  lastCheckedPrice: row.lastCheckedPrice,
@@ -197,7 +395,7 @@ function textRow(row: AlertListRow): Record<string, unknown> {
197
395
  /** The next event date of each scheduled rule: the saved one while it is still ahead, otherwise looked up. */
198
396
  async function listEventDates(
199
397
  rules: readonly EventAlertRule[],
200
- today: string,
398
+ now: number,
201
399
  lookup: (targets: ScheduledEventTarget[]) => Promise<Map<string, ScheduledDateLookup>>,
202
400
  ): Promise<{ dates: Map<string, string | null>; warnings: string[] }> {
203
401
  const dates = new Map<string, string | null>();
@@ -205,7 +403,7 @@ async function listEventDates(
205
403
  for (const rule of rules) {
206
404
  const scheduled = readScheduledRule(rule);
207
405
  if (!scheduled) continue;
208
- if (rule.eventDate && rule.eventDate >= today) dates.set(rule.id, rule.eventDate);
406
+ if (rule.eventDate && rule.eventDate >= scheduledToday(scheduled.target, now)) dates.set(rule.id, rule.eventDate);
209
407
  else stale.push({ rule, target: scheduled.target });
210
408
  }
211
409
  if (stale.length === 0) return { dates, warnings: [] };
@@ -219,6 +417,84 @@ async function listEventDates(
219
417
  return { dates, warnings: [...warnings] };
220
418
  }
221
419
 
420
+ /** How far back a release is looked for: a monthly one is named in the calendar a month back before its next date is listed. */
421
+ const CALENDAR_LOOKBACK_DAYS = 40;
422
+
423
+ /**
424
+ * The calendar a release is named from: the stored past five weeks and the
425
+ * listing ahead, or the listing alone from a server that keeps no past.
426
+ */
427
+ async function loadAlertCalendar(now: number): Promise<readonly EconEvent[]> {
428
+ const from = new Date(now - CALENDAR_LOOKBACK_DAYS * 86_400_000).toISOString().slice(0, 10);
429
+ const stored = await fetchEconCalendarFrom(from);
430
+ if (stored.events) return stored.events;
431
+ return (await loadCalendar()).data;
432
+ }
433
+
434
+ const quoted = (text: string) => (/\s/.test(text) ? `"${text}"` : text);
435
+ const weekday = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" });
436
+
437
+ /** "on Sat 2026-10-31 01:30 UTC", "on Mon 2026-10-26, all day": when a release is next listed. */
438
+ function releaseWhen(event: EconEvent): string {
439
+ const day = `on ${weekday.format(event.date)} ${dateKey(event.date)}`;
440
+ if (isAllDayEvent(event)) return `${day}, all day`;
441
+ return event.timeNote ? day : `${day} ${event.date.toISOString().slice(11, 16)} UTC`;
442
+ }
443
+
444
+ /** The release an alert was added for, as `--json` gives it. */
445
+ function releaseData(choice: CalendarEventChoice) {
446
+ return {
447
+ name: choice.name,
448
+ country: choice.country,
449
+ publisher: choice.publisher,
450
+ impact: choice.impact,
451
+ next: choice.next ? { at: choice.next.date.toISOString(), allDay: isAllDayEvent(choice.next) } : null,
452
+ };
453
+ }
454
+
455
+ const choiceList = (choices: readonly CalendarEventChoice[]) => choices.map((choice) => calendarEventLabel({ event: choice.name, country: choice.country }));
456
+
457
+ /** What a name read as, and what else it names in that country: said beside the alert added. */
458
+ function releaseReadNotes(query: string, choice: CalendarEventChoice, others: readonly CalendarEventChoice[]): string[] {
459
+ const notes: string[] = [];
460
+ if (calendarNameWords(query).join(" ") !== calendarNameWords(choice.name).join(" ")) {
461
+ notes.push(`Read "${query}" as ${calendarEventLabel({ event: choice.name, country: choice.country })}, ${choice.impact} impact.`);
462
+ }
463
+ if (others.length > 0) {
464
+ const shown = choiceList(others.slice(0, 4));
465
+ const more = others.length > 4 ? ` and ${others.length - 4} more` : "";
466
+ notes.push(`Also matching: ${shown.join(", ")}${more}. To follow one of them instead: alerts add ${quoted(others[0]!.name)} --country ${others[0]!.country}.`);
467
+ }
468
+ return notes;
469
+ }
470
+
471
+ /** Every form `alerts add` takes but a price alert's details, as a refusal ends. */
472
+ const ADD_FORMS = `Event alerts take ${EVENT_FORM} or a symbol with earnings (alerts add CRWV earnings --days 3); price alerts take a condition and a price (alerts add AAPL above 250).`;
473
+
474
+ /**
475
+ * Why a name could not be looked up. A single word (alerts add AAPL) may be
476
+ * a slip of another form as much as a release, so it also says what each
477
+ * form takes.
478
+ */
479
+ function calendarUnavailableMessage(query: string, country: CountryFilter | null, error: string): string {
480
+ if (country || /\s/.test(query.trim())) return eventDateUnavailableMessage({ kind: "release", event: query, country: country ?? "" }, error);
481
+ return `Unknown event "${query}": the economic calendar could not be loaded to look it up (${error}). ${ADD_FORMS}`;
482
+ }
483
+
484
+ /** The refusal of a name nothing in the calendar matches: the nearest names, and every form `alerts add` takes. */
485
+ function noReleaseMessage(query: string, country: CountryFilter | null, suggestions: readonly CalendarEventChoice[]): string {
486
+ const near = suggestions.length > 0 ? ` Did you mean ${choiceList(suggestions).join(", ")}?` : "";
487
+ return `No release in the economic calendar matches "${query}"${country ? ` in ${country.split(",").join(", ")}` : ""}.${near} `
488
+ + `${ADD_FORMS} gloom econ --country <code> lists the calendar.`;
489
+ }
490
+
491
+ /** The refusal of a name listed in several countries, with the best of each. */
492
+ function severalCountriesMessage(query: string, choices: readonly CalendarEventChoice[]): string {
493
+ const shown = choiceList(choices.slice(0, 6));
494
+ const more = choices.length > 6 ? ` and ${choices.length - 6} more` : "";
495
+ return `"${query}" is listed for several countries: ${shown.join(", ")}${more}. Name the country: alerts add ${quoted(query)} --country ${choices[0]!.country}.`;
496
+ }
497
+
222
498
  /** Looks up the next date for a new event alert, or fails saying why there is none. */
223
499
  async function requireEventDate(
224
500
  target: ScheduledEventTarget,
@@ -235,27 +511,39 @@ async function requireEventDate(
235
511
  export const alertsCliCommand: CliCommandDef = {
236
512
  name: "alerts",
237
513
  aliases: ["alert"],
238
- description: "List, add, and remove price alerts and alerts before earnings, CPI and FOMC",
514
+ description: "List, add, and remove price alerts, alerts before earnings and any economic calendar release (CPI, FOMC, NBS PMI), and news, options flow and IV alerts",
239
515
  help: {
240
516
  group: CLI_COMMAND_GROUPS.portfolios,
241
517
  usage: [
242
518
  "alerts list",
243
519
  "alerts add <symbol> <above|below|crosses> <price>",
244
- "alerts add <symbol> earnings [--days <n>]",
245
- "alerts add <CPI|FOMC> [--days <n>]",
520
+ "alerts add <symbol> earnings [--days <n> | results]",
521
+ "alerts add <calendar event> [--country <code>] [--days <n>]",
522
+ "alerts add [symbol] news <keyword>",
523
+ "alerts add [symbol] flow [premium] [calls|puts] [sweeps|blocks]",
524
+ "alerts add <symbol> <iv|rr> <above|below> <level>",
525
+ "alerts add <OCC contract> iv spike <points>",
246
526
  "alerts delete <id>",
247
527
  "alerts rearm <id>",
248
528
  ],
249
529
  options: [
250
530
  EXCHANGE_OPTION,
251
- { flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings, CPI and FOMC alerts` },
531
+ { flags: "--days <n>", description: `Days before the event the alert goes off, 1 to ${MAX_LEAD_DAYS} (default 1); earnings and calendar event alerts` },
532
+ { flags: "--country <code>", description: "The country of a calendar event, as econ --country takes it (CN, US, DE); a code before the name does the same (alerts add CN PMI)" },
252
533
  ],
253
534
  examples: [
254
535
  "alerts",
255
536
  "alerts add AAPL above 250",
256
537
  "alerts add SAN:EPA below 70",
257
538
  "alerts add CRWV earnings --days 3",
539
+ "alerts add CRWV earnings results",
258
540
  "alerts add CPI --days 1",
541
+ "alerts add \"NBS PMI\"",
542
+ "alerts add \"Industrial Profits\" --country CN --days 2",
543
+ "alerts add AAPL news buyback",
544
+ "alerts add NVDA flow 5m calls sweeps",
545
+ "alerts add IBIT iv above 50",
546
+ "alerts add SPY rr below -6",
259
547
  ],
260
548
  },
261
549
  execute: async (rawArgs, ctx) => {
@@ -264,61 +552,150 @@ export const alertsCliCommand: CliCommandDef = {
264
552
  const daysValue = takeOption(args, "--days");
265
553
  const days = daysFlag && daysValue === undefined ? null : daysValue;
266
554
  const exchangeOption = takeOption(args, "--exchange");
555
+ const countryFlag = rawArgs.some((arg) => arg === "--country" || arg.startsWith("--country="));
556
+ const countryValue = takeOption(args, "--country");
557
+ const country = countryFlag && countryValue === undefined ? null : countryValue;
267
558
  const action = args[0] ?? "list";
268
- const today = newYorkToday();
559
+ const now = Date.now();
560
+ const today = newYorkToday(new Date(now));
269
561
 
270
562
  const parseAdd = (): AlertAddRequest => {
271
563
  try {
272
- return parseAlertAddArgs(args.slice(1), days, exchangeOption);
564
+ return parseAlertAddArgs(args.slice(1), days, exchangeOption, country);
273
565
  } catch (error) {
274
566
  return ctx.fail(error instanceof Error ? error.message : String(error));
275
567
  }
276
568
  };
277
569
  const request = action === "add" ? parseAdd() : null;
278
- // A price alert is kept for the listing `quote` prints for the symbol, and for no symbol that quotes nothing.
279
- const priced = request?.kind === "price"
280
- ? await withMarketData(ctx, (market) => requireQuotedListing(request.symbol, exchangeOption, market, ctx, "alerts add"))
570
+ const research = request && isResearchRequest(request) ? request : null;
571
+ const listedSymbol = request?.kind === "price" ? request.symbol
572
+ : research && research.kind !== "iv_spike" ? research.symbol : null;
573
+ // A price alert is kept for the listing `quote` prints for the symbol, and for no symbol that quotes nothing;
574
+ // a news, flow or IV rule on a symbol follows that listing too, and only in the US, where their data is.
575
+ const priced = listedSymbol
576
+ ? await withMarketData(ctx, async (market) => {
577
+ const quoted = await requireQuotedListing(listedSymbol, exchangeOption, market, ctx, "alerts add");
578
+ if (research) {
579
+ const venue = quoted.listing.exchange || canonicalExchange(quoted.quote.listingExchangeName || quoted.quote.exchangeName);
580
+ if (!isUsListingExchange(venue)) {
581
+ const what = research.kind === "news" ? "News alerting" : research.kind === "flow" ? "Options flow" : "Options data";
582
+ const { message, details } = await usListingsOnlyMessage(what, quoted.listing, market,
583
+ (key) => `gloom alerts add ${key} ${args.slice(2).join(" ")}`);
584
+ ctx.fail(message, details);
585
+ }
586
+ }
587
+ return quoted;
588
+ })
281
589
  : null;
282
590
  const priceListing = priced?.listing ?? null;
283
591
  if (request?.kind === "price" && request.thousandsDot && priced) {
284
592
  const question = thousandsDotMessage(request, priced.listing.key, lastPrice(priced.quote), exchangeOption);
285
593
  if (question) ctx.fail(question);
286
594
  }
595
+ // A bare coin ticker read as a fund (BTC, the Grayscale trust) gets `quote`'s note naming the coin's pair.
596
+ const coinNote = priced && !priced.listing.exchange ? notTheCoinNote(priced.listing.symbol, priced.quote) : null;
597
+ if (request?.kind === "price" && priced) {
598
+ // A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
599
+ // Against the price the alert is checked on, as Add Alert measures it.
600
+ const far = farTargetMessage(priced.listing.key, request.price, priced.quote.price, coinNote);
601
+ if (far) ctx.fail(far);
602
+ }
287
603
  // What a typed listing was read as (7203:TSE as 7203:JPX, MXN as MXN:FWB2), said beside what was added.
288
- let read = priced?.notes ?? [];
604
+ let read = [...priced?.notes ?? [], ...coinNote ? [coinNote] : []];
289
605
  let newEventRule: EventAlertRule | null = null;
290
- if (request && request.kind !== "price") {
291
- const target: ScheduledEventTarget = request.kind === "macro"
292
- ? { kind: "macro", event: request.event }
293
- : await requireListingArg(request.symbol, exchangeOption, ctx)
606
+ let newResearchRule: EventAlertRule | null = null;
607
+ if (research) {
608
+ const typed = priceListing?.exchange ? canonicalExchange(priceListing.exchange) : "";
609
+ const { kind, values } = researchFormValues(
610
+ research.kind === "iv_spike" ? research : { ...research, symbol: priceListing?.symbol ?? null } as ResearchAddRequest,
611
+ RESEARCH_EXCHANGES.has(typed) ? typed : "US",
612
+ );
613
+ try {
614
+ newResearchRule = createResearchAlert(kind, values);
615
+ } catch (error) {
616
+ ctx.fail(error instanceof Error ? error.message : String(error));
617
+ }
618
+ }
619
+ // A release named from the calendar, with what the words were read as and what else they name.
620
+ let newRelease: CalendarEventChoice | null = null;
621
+ // CPI and FOMC, typed so or named by one of their calendar rows (CPI y/y, Fed Interest Rate Decision).
622
+ let macro: { event: MacroAlertEvent; leadDays: number } | null = request?.kind === "macro" ? request : null;
623
+ let calendar: readonly EconEvent[] | null = null;
624
+ if (request?.kind === "release") {
625
+ const events = await loadAlertCalendar(now).catch((error: unknown) =>
626
+ ctx.fail(calendarUnavailableMessage(request.query, request.country, error instanceof Error ? error.message : String(error))));
627
+ calendar = events;
628
+ const match = matchCalendarEvent(request.query, request.country, events, now);
629
+ if (match.kind !== "match") {
630
+ return ctx.fail(match.kind === "none"
631
+ ? noReleaseMessage(request.query, request.country, match.suggestions)
632
+ : severalCountriesMessage(request.query, match.choices));
633
+ }
634
+ const { choice, others } = match;
635
+ const macroEvent = macroEventNamed(choice);
636
+ if (macroEvent) {
637
+ macro = { event: macroEvent, leadDays: request.leadDays };
638
+ read = [`Read "${request.query}" as the ${MACRO_EVENT_NAMES[macroEvent]} alert, which follows ${calendarEventLabel({ event: choice.name, country: choice.country })}.`];
639
+ } else {
640
+ newRelease = choice;
641
+ read = releaseReadNotes(request.query, choice, others);
642
+ const target: ScheduledEventTarget = { kind: "release", event: choice.name, country: choice.country, ...(choice.publisher ? { publisher: choice.publisher } : {}) };
643
+ try {
644
+ newEventRule = createScheduledAlert(target, request.leadDays, choice.next ? dateKey(choice.next.date) : null);
645
+ } catch (error) {
646
+ ctx.fail(error instanceof Error ? error.message : String(error));
647
+ }
648
+ }
649
+ }
650
+ // When CPI or FOMC came out earlier today: its figures are not sent to an alert set after them.
651
+ let macroOutAt: number | null = null;
652
+ const earnings = request?.kind === "earnings" ? request : null;
653
+ if (macro || earnings) {
654
+ const target: ScheduledEventTarget = macro
655
+ ? { kind: "macro", event: macro.event }
656
+ : await requireListingArg(earnings!.symbol, exchangeOption, ctx)
294
657
  .then((listing) => {
295
658
  read = listingReadNotes(listing);
296
659
  return { kind: "earnings" as const, symbol: listing.symbol, exchange: listing.exchange };
297
660
  });
661
+ const lead = earnings?.results ? "results" as const : (macro ?? earnings!).leadDays;
298
662
  // A listing the rule cannot follow fails before any request.
299
663
  try {
300
- createScheduledAlert(target, request.leadDays, null);
664
+ createScheduledAlert(target, lead, null);
301
665
  } catch (error) {
302
666
  ctx.fail(error instanceof Error ? error.message : String(error));
303
667
  }
304
668
  const eventDate = target.kind === "earnings"
305
669
  ? await withMarketData(ctx, (market) => requireEventDate(target, scheduledEventSources(market.dataProvider), today, (message) => ctx.fail(message)))
306
670
  : await requireEventDate(target, scheduledEventSources(null), today, (message) => ctx.fail(message));
307
- newEventRule = createScheduledAlert(target, request.leadDays, eventDate);
671
+ newEventRule = createScheduledAlert(target, lead, eventDate);
672
+ if (target.kind === "macro" && eventDate === today) {
673
+ const events = calendar ?? await loadAlertCalendar(now).catch(() => {
674
+ // Intentional ignore: without the calendar the alert is added as before, only without saying today's release is out.
675
+ return null;
676
+ });
677
+ const row = events ? releaseRowOn(target, eventDate, events) : null;
678
+ const at = row ? releaseInstant(row) : null;
679
+ if (at != null && at <= now) macroOutAt = at;
680
+ }
308
681
  }
309
682
 
310
683
  if (action === "list") {
311
- const { alerts, events } = await withConfigData(ctx, (config) => readAlertStores(config.config));
312
- // Earnings dates come through market data; CPI and FOMC dates do not need it.
684
+ // An IV level's last reading is this device's, beside the rules.
685
+ const { alerts, alertsError, events, checks } = await withConfigData(ctx, (config) => ({
686
+ ...readAlertStores(config.config),
687
+ checks: readIvLevelChecks(config.persistence?.pluginState?.get(ALERTS_PLUGIN_ID, resumeStateKey(IV_LEVEL_CHECKS_KEY))?.value),
688
+ }));
689
+ // Earnings dates come through market data; CPI, FOMC and calendar dates do not need it.
313
690
  const lookup = (targets: ScheduledEventTarget[]) => targets.some((target) => target.kind === "earnings")
314
- ? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today))
315
- : lookupScheduledDates(targets, scheduledEventSources(null), today);
316
- const { dates, warnings } = await listEventDates(events.rules, today, lookup);
691
+ ? withMarketData(ctx, (market) => lookupScheduledDates(targets, scheduledEventSources(market.dataProvider), today, now))
692
+ : lookupScheduledDates(targets, scheduledEventSources(null), today, now);
693
+ const { dates, warnings } = await listEventDates(events.rules, now, lookup);
317
694
  const rows = [
318
695
  ...alerts.map(priceRow),
319
- ...events.rules.map((rule) => eventRow(rule, today, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate)),
696
+ ...events.rules.map((rule) => eventRow(rule, now, dates.has(rule.id) ? dates.get(rule.id) : rule.eventDate, checks[rule.id])),
320
697
  ];
321
- const allWarnings = [...(events.error ? [events.error] : []), ...warnings];
698
+ const allWarnings = [...(alertsError ? [alertsError] : []), ...(events.error ? [events.error] : []), ...warnings];
322
699
  ctx.printResult({ data: rows, ...(allWarnings.length ? { warnings: allWarnings } : {}) }, {
323
700
  rows: (data) => data.map(textRow),
324
701
  textColumns: [
@@ -331,14 +708,15 @@ export const alertsCliCommand: CliCommandDef = {
331
708
  { key: "lastCheckedPrice", header: "Last Price", align: "right", optional: true },
332
709
  { key: "triggeredAt", header: "Triggered", optional: true },
333
710
  ],
334
- empty: "No alerts. Add one with gloomberb alerts add AAPL above 250, alerts add CRWV earnings --days 3, or alerts add CPI --days 1.",
711
+ empty: "No alerts. Add one with gloom alerts add AAPL above 250, alerts add CRWV earnings --days 3, alerts add CPI --days 1, or alerts add CN PMI.",
335
712
  });
336
713
  return;
337
714
  }
338
715
 
339
716
  await withConfigData(ctx, async (config) => {
340
- const { pluginState, alerts, events } = readAlertStores(config.config);
717
+ const { pluginState, alerts, alertsError, events } = readAlertStores(config.config);
341
718
  const save = async (next: { alerts?: AlertRule[]; events?: EventAlertRule[] }) => {
719
+ if (next.alerts && alertsError) ctx.fail(alertsError);
342
720
  const nextConfig = {
343
721
  ...config.config,
344
722
  pluginConfig: {
@@ -346,7 +724,8 @@ export const alertsCliCommand: CliCommandDef = {
346
724
  [ALERTS_PLUGIN_ID]: {
347
725
  ...pluginState,
348
726
  ...(next.alerts ? { [ALERTS_KEY]: serializeAlerts(next.alerts) } : {}),
349
- ...(next.events ? { [EVENT_ALERTS_KEY]: JSON.stringify(next.events) } : {}),
727
+ // Each list keeps what it holds of kinds this version does not know; IV levels go in their own.
728
+ ...(next.events ? writeEventAlertStores(events.lists, next.events) : {}),
350
729
  },
351
730
  },
352
731
  };
@@ -369,30 +748,57 @@ export const alertsCliCommand: CliCommandDef = {
369
748
  });
370
749
  return;
371
750
  }
751
+ if (newResearchRule) {
752
+ const rule = newResearchRule;
753
+ const rules = requireEvents();
754
+ const existing = rules.find((entry) => entry.kind === rule.kind && entry.target === rule.target && entry.value.toLowerCase() === rule.value.toLowerCase());
755
+ if (existing) ctx.fail(`This alert already exists (ID ${existing.id}).`);
756
+ if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
757
+ await save({ events: [...rules, rule] });
758
+ const kind = research!.kind;
759
+ ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule }, ...(read.length > 0 ? { warnings: read } : {}) }, {
760
+ text: (data) => `Added alert ${data.alert.id}: ${alertKindLabel(rule.kind)} ${researchAlertDescription(rule)} (${researchDelivery(kind)}).${dryRunNote(data.dryRun)}`,
761
+ });
762
+ return;
763
+ }
372
764
  if (newEventRule) {
373
765
  const rule = newEventRule;
374
766
  const rules = requireEvents();
375
767
  const scheduled = readScheduledRule(rule)!;
376
768
  const name = scheduledEventName(scheduled.target);
377
769
  const existing = rules.find((entry) => entry.kind === rule.kind && entry.value.toLowerCase() === rule.value.toLowerCase());
378
- if (existing) ctx.fail(`An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}).`);
379
- if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloomberb alerts delete <id>.`);
770
+ if (existing) ctx.fail(scheduled.trigger === "results"
771
+ ? `An alert for ${name} results already exists (ID ${existing.id}).`
772
+ : `An alert ${plural(scheduled.leadDays, "day")} before ${name} already exists (ID ${existing.id}).`);
773
+ if (rules.length >= MAX_EVENT_ALERTS) ctx.fail(`Keep at most ${MAX_EVENT_ALERTS} event alerts. Delete one with gloom alerts delete <id>.`);
380
774
  await save({ events: [...rules, rule] });
381
- const eventDate = rule.eventDate!;
382
- const due = scheduledPhase(eventDate, scheduled.leadDays, undefined, today) === "due";
383
- const delivery = scheduled.target.kind === "macro" ? ", only while it is running" : " while it is running, or on your phone with Cloud sync";
384
- ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule }, ...(read.length > 0 ? { warnings: read } : {}) }, {
385
- // Delivery, as docs/usage.md states it: CPI and FOMC notify in the running app only; earnings also reach the phone.
386
- text: (data) => `Added alert ${data.alert.id}: ${name} on ${eventDate}, ${plural(scheduled.leadDays, "day")} before (${due
387
- ? "already reached, so the app alerts at its next check"
388
- : `the app alerts on ${leadStartDate(eventDate, scheduled.leadDays)}`}${delivery}).${dryRunNote(data.dryRun)}`,
775
+ const eventDate = rule.eventDate ?? null;
776
+ const due = !!eventDate && scheduledPhase(eventDate, scheduled.leadDays, undefined, scheduledToday(scheduled.target, now)) === "due";
777
+ const release = followsRelease(scheduled.target);
778
+ // A release that prints no figures (a plenum, a speech) alerts only before it.
779
+ const prints = release && (!newRelease?.next || expectsFigures(newRelease.next));
780
+ const delivery = release ? ", only while it is running" : " while it is running, or on your phone with Cloud sync";
781
+ const when = !eventDate ? null : newRelease?.next ? releaseWhen(newRelease.next) : `on ${eventDate}`;
782
+ const reminder = !eventDate
783
+ ? "the app alerts once the calendar lists its next date, about two weeks ahead"
784
+ : macroOutAt != null ? `it came out today at ${new Date(macroOutAt).toISOString().slice(11, 16)} UTC, so the app alerts before the next one`
785
+ : due ? "already reached, so the app alerts at its next check" : `the app alerts on ${leadStartDate(eventDate, scheduled.leadDays)}`;
786
+ ctx.printResult({ data: { changed: !ctx.cliOptions.dryRun, dryRun: ctx.cliOptions.dryRun, alert: rule,
787
+ ...newRelease ? { release: releaseData(newRelease) } : {} }, ...(read.length > 0 ? { warnings: read } : {}) }, {
788
+ // Delivery, as docs/usage.md states it: CPI, FOMC and calendar releases notify in the running app only; earnings also reach the phone,
789
+ // which Gloom Cloud alerts with the same results once the actual EPS is stored for a results alert.
790
+ text: (data) => scheduled.trigger === "results"
791
+ ? `Added alert ${data.alert.id}: ${name} on ${eventDate}, when results are out (the app alerts once the actual EPS is in while it is running, and with Cloud sync your phone gets the same alert).${dryRunNote(data.dryRun)}`
792
+ : `Added alert ${data.alert.id}: ${name}${when ? ` ${when}` : ""}, ${plural(scheduled.leadDays, "day")} before${prints ? " and when it prints" : ""} (${reminder}${
793
+ prints ? ", and with the actual, forecast and prior once out" : ""}${delivery}).${dryRunNote(data.dryRun)}`,
389
794
  });
390
795
  return;
391
796
  }
392
797
  if (action === "delete" || action === "rm") {
393
- const id = requireArg(args[1], "Usage: gloomberb alerts delete <id>", ctx);
798
+ const id = requireArg(args[1], "Usage: gloom alerts delete <id>", ctx);
394
799
  const isPrice = alerts.some((alert) => alert.id === id);
395
800
  const isEvent = !isPrice && events.rules.some((rule) => rule.id === id);
801
+ if (!isPrice && !isEvent && alertsError) ctx.fail(alertsError);
396
802
  if (isPrice) await save({ alerts: alerts.filter((alert) => alert.id !== id) });
397
803
  if (isEvent) await save({ events: requireEvents().filter((rule) => rule.id !== id) });
398
804
  const changed = isPrice || isEvent;
@@ -404,13 +810,16 @@ export const alertsCliCommand: CliCommandDef = {
404
810
  return;
405
811
  }
406
812
  if (action === "rearm") {
407
- const id = requireArg(args[1], "Usage: gloomberb alerts rearm <id>", ctx);
813
+ const id = requireArg(args[1], "Usage: gloom alerts rearm <id>", ctx);
408
814
  const isPrice = alerts.some((alert) => alert.id === id);
409
815
  const rule = isPrice ? null : events.rules.find((entry) => entry.id === id) ?? null;
816
+ if (!isPrice && !rule && alertsError) ctx.fail(alertsError);
410
817
  if (isPrice) await save({ alerts: alerts.map((alert) => alert.id === id ? rearmAlert(alert) : alert) });
411
818
  if (rule) {
412
819
  // A scheduled alert alerts again for its coming event; any other event alert resumes.
413
- const next = readScheduledRule(rule) ? rearmScheduledAlert(rule) : rule.status === "paused" ? toggleEventAlert(rule) : rule;
820
+ const next = readScheduledRule(rule) ? rearmScheduledAlert(rule)
821
+ : rule.kind === "iv_level" && rule.triggeredAt ? rearmIvLevelAlert(rule)
822
+ : rule.status === "paused" ? toggleEventAlert(rule) : rule;
414
823
  await save({ events: requireEvents().map((entry) => entry.id === id ? next : entry) });
415
824
  }
416
825
  const found = isPrice || !!rule;
@@ -421,7 +830,7 @@ export const alertsCliCommand: CliCommandDef = {
421
830
  });
422
831
  return;
423
832
  }
424
- ctx.fail("Usage: gloomberb alerts list|add|delete|rearm");
833
+ ctx.fail("Usage: gloom alerts list|add|delete|rearm");
425
834
  });
426
835
  },
427
836
  };