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
@@ -1,5 +1,9 @@
1
1
  import { startCatalystAlerts } from "./catalysts";
2
- import { RESEARCH_LABELS, researchWizardFields, createResearchAlert } from "./research-builder";
2
+ import { notTheCoinNote } from "../../../cli/commands/crypto-hints";
3
+ import { createFiredAlertRecorder, firedAlertTime, quoteSourceWords } from "./fired-log";
4
+ import { checkIvLevelAlertsNow, startIvLevelAlerts } from "./iv-level";
5
+ import { logFailure } from "../../../utils/log-failure";
6
+ import { RESEARCH_FORM_KINDS, RESEARCH_LABELS, researchWizardFields, createResearchAlert } from "./research-builder";
3
7
  import { newYorkToday } from "../earnings/board-model";
4
8
  import { checkScheduledEventAlertsNow, startScheduledEventAlerts } from "./scheduled-check";
5
9
  import { scheduledEventSources } from "./scheduled-sources";
@@ -12,7 +16,7 @@ import {
12
16
  scheduledTargetKey,
13
17
  } from "./scheduled";
14
18
  import { isResearchAlertKind, MAX_LEAD_DAYS } from "./research-rules";
15
- import { EVENT_ALERTS_KEY, EVENT_ALERT_TEMPLATES, MAX_EVENT_ALERTS, createEventAlert, readEventAlerts } from "./events";
19
+ import { EVENT_ALERT_TEMPLATES, MAX_EVENT_ALERTS, createEventAlert, eventAlertStoreKey, readEventAlertStores, updateEventAlerts } from "./events";
16
20
  import { formatMarketPrice } from "../../../market-data/market/format";
17
21
  import { settleTypedListingKey } from "../../../tickers/listing-venues";
18
22
  import type { Quote } from "../../../types/financials";
@@ -32,6 +36,7 @@ import { AlertsPane } from "./pane";
32
36
  import { alertsCliCommand } from "./cli";
33
37
  import {
34
38
  createQuoteErrorMessage,
39
+ farTargetMessage,
35
40
  quoteAlertFields,
36
41
  quoteErrorAlertFields,
37
42
  resolveAlertQuote,
@@ -45,6 +50,7 @@ import type { AlertRule } from "./types";
45
50
 
46
51
  let catalystAlertsStop: (() => void) | null = null;
47
52
  let scheduledAlertsStop: (() => void) | null = null;
53
+ let ivLevelAlertsStop: (() => void) | null = null;
48
54
  let pollGeneration = 0;
49
55
  let pollTimer: ReturnType<typeof setTimeout> | null = null;
50
56
  let alertStream: AlertQuoteStream | null = null;
@@ -66,11 +72,22 @@ export const alertsPlugin: GloomPlugin = {
66
72
 
67
73
  setup(ctx) {
68
74
  const generation = ++pollGeneration;
75
+ // Every alert the app fires is kept on this device, for History and `fn ALRT --tab history`.
76
+ const recordFired = createFiredAlertRecorder(ctx.resume, logFailure("alerts.fired-log", "Record fired alert failed"));
69
77
  catalystAlertsStop?.();
70
78
  catalystAlertsStop = startCatalystAlerts(ctx);
71
79
  scheduledAlertsStop?.();
72
- scheduledAlertsStop = startScheduledEventAlerts(ctx, scheduledEventSources(ctx.marketData));
73
- const notifyTriggered = (alert: AlertRule, price: number) => {
80
+ scheduledAlertsStop = startScheduledEventAlerts(ctx, scheduledEventSources(ctx.marketData), Date.now, recordFired);
81
+ ivLevelAlertsStop?.();
82
+ ivLevelAlertsStop = startIvLevelAlerts(ctx, recordFired);
83
+ const notifyTriggered = (alert: AlertRule, quote: Quote, firedAt: number) => {
84
+ const price = quote.price;
85
+ recordFired({
86
+ kind: "price", ruleId: alert.id, rule: `${alert.exchange ? `${alert.symbol}:${alert.exchange}` : alert.symbol} ${alert.condition} ${alert.targetPrice}`,
87
+ firedAt, value: price, ...(quote.currency ? { unit: quote.currency } : {}),
88
+ quoteAt: firedAlertTime(quote.lastUpdated), source: quoteSourceWords(quote.dataSource),
89
+ symbol: alert.symbol, ...(alert.exchange ? { exchange: alert.exchange } : {}),
90
+ });
74
91
  ctx.notify({
75
92
  body: `${formatAlertDescription(alert)} triggered at ${price}`,
76
93
  type: "success",
@@ -110,7 +127,7 @@ export const alertsPlugin: GloomPlugin = {
110
127
  alert.status = "triggered";
111
128
  alert.triggeredAt = now;
112
129
  ctx.log.info("stream: TRIGGERED", { symbol: alert.symbol, price: quote.price });
113
- notifyTriggered(alert, quote.price);
130
+ notifyTriggered(alert, quote, now);
114
131
  persist = true;
115
132
  }
116
133
  streamedBaselines.set(alert.id, quote.price);
@@ -119,7 +136,7 @@ export const alertsPlugin: GloomPlugin = {
119
136
  }
120
137
  if (!persist && !(touched && now - lastStreamPersistAt >= STREAM_PERSIST_MS)) return;
121
138
  lastStreamPersistAt = now;
122
- saveAlerts(ctx, alerts);
139
+ void saveAlerts(ctx, alerts).catch(logFailure("alerts.stream", "Save streamed price alerts failed"));
123
140
  if (persist) alertStream?.sync();
124
141
  };
125
142
  alertStream?.dispose();
@@ -168,6 +185,9 @@ export const alertsPlugin: GloomPlugin = {
168
185
  const symbol = (await settleTypedListingKey(input.symbol, { store: ctx.tickerRepository, dataProvider: ctx.marketData })).toUpperCase();
169
186
 
170
187
  const quote = await resolveAlertQuote(ctx.marketData, symbol);
188
+ // A target ten times away is a slip or another instrument's price, such as bitcoin's on the BTC fund.
189
+ const far = farTargetMessage(symbol, input.price, quote.price, symbol.includes(":") ? null : notTheCoinNote(symbol, quote));
190
+ if (far) throw new Error(far);
171
191
 
172
192
  const alert = {
173
193
  ...createAlert(quote.symbol || symbol, input.condition, input.price),
@@ -175,7 +195,7 @@ export const alertsPlugin: GloomPlugin = {
175
195
  };
176
196
  const existing = loadAlerts(ctx);
177
197
  existing.push(alert);
178
- saveAlerts(ctx, existing);
198
+ await saveAlerts(ctx, existing);
179
199
  alertStream?.sync();
180
200
 
181
201
  ctx.notify({
@@ -188,11 +208,11 @@ export const alertsPlugin: GloomPlugin = {
188
208
  ctx.registerCommand({
189
209
  id: "set-event-alert",
190
210
  label: "Add Event Alert",
191
- description: "Create an alert before earnings, CPI or FOMC, or for filings, news and market moves",
211
+ description: "Create an alert before earnings, CPI or FOMC, when earnings results are out, or for filings, news, IV levels, hiring and market moves",
192
212
  keywords: [
193
213
  "alert", "event", "congress", "13f", "fund", "member", "follow", "earnings", "cpi", "fomc", "days before",
194
214
  "filing", "filings", "sec", "8-k", "news", "keyword", "analyst", "52-week", "unusual volume", "volume",
195
- "short interest", "insider", "catalyst", "implied volatility", "iv spike", "options flow", "perps", "funding",
215
+ "short interest", "insider", "catalyst", "implied volatility", "iv spike", "iv level", "risk reversal", "skew", "options flow", "perps", "funding", "hiring", "jobs",
196
216
  ],
197
217
  category: "data",
198
218
  wizardLayout: "form",
@@ -202,7 +222,7 @@ export const alertsPlugin: GloomPlugin = {
202
222
  label: "Event",
203
223
  type: "select",
204
224
  defaultValue: "congress-watched",
205
- options: [...Object.entries(RESEARCH_LABELS).map(([value,label]) => ({value,label})), ...EVENT_ALERT_TEMPLATES.map((entry) => ({ label: entry.label, value: entry.id }))],
225
+ options: [...RESEARCH_FORM_KINDS.map((value) => ({ value, label: RESEARCH_LABELS[value] })), ...EVENT_ALERT_TEMPLATES.map((entry) => ({ label: entry.label, value: entry.id }))],
206
226
  },
207
227
  ...EVENT_ALERT_TEMPLATES.filter((entry) => entry.field != null).map((entry) => ({
208
228
  key: entry.id,
@@ -216,13 +236,15 @@ export const alertsPlugin: GloomPlugin = {
216
236
  ],
217
237
  async execute(values = {}) {
218
238
  const event = values?.event || "congress-watched";
219
- const initial = readEventAlerts(ctx.configState.get<string>(EVENT_ALERTS_KEY) || "[]");
239
+ const readAll = () => readEventAlertStores((key) => ctx.configState.get<string>(key));
240
+ const initial = readAll();
220
241
  if (initial.error) throw new Error(initial.error);
221
242
  if (initial.rules.length >= MAX_EVENT_ALERTS)
222
243
  throw new Error("Keep at most 40 event alerts.");
223
244
  // Earnings and macro events alert before a date: one has to be known, or the rule would never go off.
224
245
  const scheduled = event === "earnings_date" || event === "macro_event";
225
- if (scheduled && parseLeadDays(values[`${event}:leadDays`]) == null)
246
+ const awaitsResults = event === "earnings_date" && values["earnings_date:when"] === "results";
247
+ if (scheduled && !awaitsResults && parseLeadDays(values[`${event}:leadDays`]) == null)
226
248
  throw new Error(`Days before must be a whole number from 1 to ${MAX_LEAD_DAYS}.`);
227
249
  let rule = isResearchAlertKind(event) ? createResearchAlert(event, values) : createEventAlert(event, values?.[event] || "");
228
250
  const target = scheduled ? readScheduledRule(rule)?.target : null;
@@ -234,7 +256,7 @@ export const alertsPlugin: GloomPlugin = {
234
256
  rule = { ...rule, eventDate: lookup.date };
235
257
  }
236
258
  // Read again: the lookup took a while and the rules may have changed meanwhile.
237
- const result = scheduled ? readEventAlerts(ctx.configState.get<string>(EVENT_ALERTS_KEY) || "[]") : initial;
259
+ const result = scheduled ? readAll() : initial;
238
260
  if (result.error) throw new Error(result.error);
239
261
  if (
240
262
  result.rules.some(
@@ -245,9 +267,12 @@ export const alertsPlugin: GloomPlugin = {
245
267
  )
246
268
  )
247
269
  throw new Error("This event is already followed.");
248
- await ctx.configState.set(EVENT_ALERTS_KEY, JSON.stringify([...result.rules, rule]));
270
+ // An IV level goes in its own list, which released versions never read.
271
+ const key = eventAlertStoreKey(rule);
272
+ await ctx.configState.set(key, updateEventAlerts(ctx.configState.get<string>(key), (rules) => [...rules, rule]));
249
273
  ctx.notify({ body: "Event alert saved.", type: "success" });
250
274
  if (scheduled) checkScheduledEventAlertsNow();
275
+ if (rule.kind === "iv_level") checkIvLevelAlertsNow();
251
276
  },
252
277
  });
253
278
 
@@ -294,7 +319,7 @@ export const alertsPlugin: GloomPlugin = {
294
319
  alert.status = "triggered";
295
320
  alert.triggeredAt = Date.now();
296
321
  ctx.log.info("poll: TRIGGERED", { symbol: alert.symbol, price: quote.price });
297
- notifyTriggered(alert, quote.price);
322
+ notifyTriggered(alert, quote, alert.triggeredAt);
298
323
  }
299
324
  streamedBaselines.delete(alert.id);
300
325
  Object.assign(alert, quoteAlertFields(quote));
@@ -302,7 +327,7 @@ export const alertsPlugin: GloomPlugin = {
302
327
  }
303
328
 
304
329
  if (changed) {
305
- saveAlerts(ctx, alerts);
330
+ await saveAlerts(ctx, alerts);
306
331
  }
307
332
  };
308
333
 
@@ -313,11 +338,11 @@ export const alertsPlugin: GloomPlugin = {
313
338
  const seconds = Number(ctx.paneSettings?.get<string>("alerts", POLL_SECONDS_KEY));
314
339
  const delay = Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : POLL_INTERVAL_MS;
315
340
  pollTimer = setTimeout(() => {
316
- void poll().finally(scheduleNextPoll);
341
+ void poll().catch(logFailure("alerts.poll", "Check price alerts failed")).finally(scheduleNextPoll);
317
342
  }, delay);
318
343
  };
319
344
 
320
- void poll().finally(scheduleNextPoll);
345
+ void poll().catch(logFailure("alerts.poll", "Check price alerts failed")).finally(scheduleNextPoll);
321
346
 
322
347
  ctx.registerPane({
323
348
  id: "alerts",
@@ -369,6 +394,8 @@ export const alertsPlugin: GloomPlugin = {
369
394
  catalystAlertsStop = null;
370
395
  scheduledAlertsStop?.();
371
396
  scheduledAlertsStop = null;
397
+ ivLevelAlertsStop?.();
398
+ ivLevelAlertsStop = null;
372
399
  pollGeneration += 1;
373
400
  alertStream?.dispose();
374
401
  alertStream = null;
@@ -0,0 +1,365 @@
1
+ import type { DataProvider } from "../../../types/data-provider";
2
+ import type { GloomPluginContext } from "../../../types/plugin";
3
+ import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
4
+ import { withDeadline } from "../../../utils/async-deadline";
5
+ import { errorMessage } from "../../../utils/errors";
6
+ import { createSurfaceDependencies, loadVolatilitySurface, withReusedYieldCurve, type SurfaceLoaderDependencies } from "../vol-surface/client";
7
+ import { buildSurfaceGrid, DEFAULT_SURFACE_SETTINGS, type SurfaceExpiry, type SurfaceSnapshot } from "../vol-surface/model";
8
+ import { IV_LEVEL_ALERTS_KEY, readEventAlerts, updateEventAlerts, type EventAlertRule } from "./events";
9
+ import { type FiredAlertInput } from "./fired-log";
10
+ import { formatIvLevel, IV_LEVEL_NAMES, researchAlertDescription } from "./research-builder";
11
+ import { readResearchRule, type IvLevelMetric, type ResearchAlertConfig } from "./research-rules";
12
+
13
+ /** OVDV's 1M fixed tenor: the constant maturity an IV level rule reads. */
14
+ const IV_LEVEL_TENOR = { label: "1M", years: 30 / 365 };
15
+ /** OVDV's delta axis at that tenor: the 25-delta put, ATM (the delta-neutral strike) and the 25-delta call. */
16
+ const IV_LEVEL_DELTAS = [-0.25, 0, 0.25] as const;
17
+ /**
18
+ * A tenor with listed expiries on one side only is carried flat from the
19
+ * nearest (OVDV marks it E); an IV level takes that only from an expiry this
20
+ * close to 30 days.
21
+ */
22
+ const MAX_EXTRAPOLATION_DAYS = 10;
23
+ /** Option quotes move through the session; readings are taken this often while the app runs. */
24
+ const CHECK_INTERVAL_MS = 15 * 60_000;
25
+ const FIRST_CHECK_DELAY_MS = 8_000;
26
+ /** A symbol's option chains that have not all arrived by then are given up on until the next check. */
27
+ const LOAD_TIMEOUT_MS = 60_000;
28
+
29
+ /**
30
+ * The device's last reading of each IV level rule, by rule id, in plugin
31
+ * resume state: never synced, so a reading every 15 minutes writes nothing
32
+ * another device pulls, and a pull never takes one away.
33
+ */
34
+ export const IV_LEVEL_CHECKS_KEY = "ivLevelChecks";
35
+
36
+ /** One check of an IV level rule: the level in the rule's unit, and the option quotes it was read from. */
37
+ export interface IvLevelCheck {
38
+ checkedAt: number;
39
+ /** % IV for an ATM rule, vol points for a risk reversal; null when the check found none. */
40
+ value: number | null;
41
+ /** When the option quotes behind the reading were observed. */
42
+ quoteAt?: string;
43
+ /** How current those quotes are: `real-time`, `15 min delayed`. */
44
+ source?: string;
45
+ /** Why there was no reading. */
46
+ error?: string;
47
+ }
48
+
49
+ const isText = (value: unknown): value is string => typeof value === "string" && value.length > 0 && value.length <= 300;
50
+
51
+ /** The saved readings that read; anything else is dropped, and the next check writes it again. */
52
+ export function readIvLevelChecks(value: unknown): Record<string, IvLevelCheck> {
53
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
54
+ const checks: Record<string, IvLevelCheck> = {};
55
+ for (const [id, raw] of Object.entries(value as Record<string, unknown>)) {
56
+ if (!raw || typeof raw !== "object") continue;
57
+ const check = raw as Record<string, unknown>;
58
+ if (!Number.isFinite(check.checkedAt) || (check.value !== null && !Number.isFinite(check.value))) continue;
59
+ checks[id] = {
60
+ checkedAt: check.checkedAt as number,
61
+ value: check.value as number | null,
62
+ ...(isText(check.quoteAt) ? { quoteAt: check.quoteAt } : {}),
63
+ ...(isText(check.source) ? { source: check.source } : {}),
64
+ ...(isText(check.error) ? { error: check.error } : {}),
65
+ };
66
+ }
67
+ return checks;
68
+ }
69
+
70
+ /**
71
+ * An IV level rule's symbol's 30-day ATM IV and 25-delta risk reversal, in
72
+ * decimals: the 1M row of OVDV's Table on the delta axis with fixed tenors
73
+ * (ATM, 25dP and 25dC), solved at 30 days on the same fits. Null with `error`
74
+ * when the chain gives none.
75
+ */
76
+ export interface IvLevelReading {
77
+ atm: number | null;
78
+ rr: number | null;
79
+ /** When the newest option quotes behind it were observed. */
80
+ quoteAt: string | null;
81
+ /** How current those quotes are: `real-time`, `15 min delayed`, `delayed`. */
82
+ source: string | null;
83
+ error?: string;
84
+ }
85
+
86
+ /** How current option quotes are, worded as reports word a feed. */
87
+ function feedWords(expiries: readonly Pick<SurfaceExpiry, "dataSource" | "delayMinutes">[]): string | null {
88
+ if (expiries.length === 0) return null;
89
+ if (expiries.every((expiry) => expiry.dataSource === "live")) return "real-time";
90
+ const delay = Math.max(0, ...expiries.map((expiry) => expiry.delayMinutes ?? 0));
91
+ if (expiries.some((expiry) => expiry.dataSource === "delayed")) return delay > 0 ? `${delay} min delayed` : "delayed";
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * The 30-day reading of a surface as OVDV loads it: its own fixed-tenor solve
97
+ * (`buildSurfaceGrid`), so the figure is the one OVDV shows, with the time and
98
+ * feed of the expiries either side of 30 days that it comes from.
99
+ */
100
+ export function ivLevelReading(snapshot: Pick<SurfaceSnapshot, "spot" | "expiries" | "failures">): IvLevelReading {
101
+ const grid = buildSurfaceGrid(snapshot as SurfaceSnapshot, {
102
+ axis: "delta", tenors: "fixed", fixedTenors: [IV_LEVEL_TENOR], coordinates: IV_LEVEL_DELTAS,
103
+ });
104
+ const row = grid.rows[0];
105
+ // The expiries the solve stands on: the ones either side of 30 days, or the nearest when there is one side only.
106
+ const available = snapshot.expiries
107
+ .filter((expiry) => expiry.fit && expiry.forward != null && expiry.rate != null && expiry.dividendYield != null)
108
+ .sort((a, b) => a.years - b.years);
109
+ const right = available.findIndex((expiry) => expiry.years >= IV_LEVEL_TENOR.years);
110
+ const used = right < 0 ? available.slice(-1) : right === 0 ? available.slice(0, 1) : available.slice(right - 1, right + 1);
111
+ const tooFar = !!row?.extrapolated && used.every((expiry) => Math.abs(expiry.years - IV_LEVEL_TENOR.years) * 365 > MAX_EXTRAPOLATION_DAYS);
112
+ const [put, atm, call] = tooFar || !row ? [null, null, null] : row.cells.map((cell) => cell.volatility);
113
+ const rr = put != null && call != null ? call - put : null;
114
+ const quoteAt = used.map((expiry) => expiry.asOf).filter((value): value is string => !!value).sort().at(-1) ?? null;
115
+ const error = atm == null && rr == null
116
+ ? snapshot.failures.find((failure) => failure.message)?.message ?? snapshot.expiries.find((expiry) => expiry.error)?.error
117
+ ?? "No clean option quotes near 30 days"
118
+ : undefined;
119
+ return { atm: atm ?? null, rr, quoteAt, source: feedWords(used), ...(error ? { error } : {}) };
120
+ }
121
+
122
+ /** The symbol's listing as an option chain is requested: a research rule's `US` names no venue. */
123
+ const instrumentOf = (config: Pick<ResearchAlertConfig, "symbol" | "exchange">) => ({
124
+ symbol: config.symbol!,
125
+ ...(config.exchange && config.exchange !== "US" ? { exchange: config.exchange } : {}),
126
+ });
127
+
128
+ /** The symbol's surface as OVDV loads it by default: the same expiries, settings and spot. */
129
+ async function loadIvLevelReading(
130
+ config: Pick<ResearchAlertConfig, "symbol" | "exchange">,
131
+ marketData: Pick<DataProvider, "getQuote">,
132
+ dependencies: SurfaceLoaderDependencies,
133
+ signal: AbortSignal,
134
+ ): Promise<IvLevelReading> {
135
+ const instrument = instrumentOf(config);
136
+ const quote = await marketData.getQuote(instrument.symbol, instrument.exchange);
137
+ if (!quote || !(quote.price > 0) || !Number.isFinite(quote.price) || quote.stale) throw new Error(`No current price for ${instrument.symbol}`);
138
+ const snapshot = await loadVolatilitySurface({
139
+ instrument, spot: quote.price, spotAsOf: quote.lastUpdated, quote, settings: DEFAULT_SURFACE_SETTINGS, signal,
140
+ }, dependencies);
141
+ return ivLevelReading(snapshot);
142
+ }
143
+
144
+ /** The reading an IV level rule follows, in its unit: % IV for ATM, vol points for a risk reversal. */
145
+ function ivLevelValue(reading: IvLevelReading, metric: IvLevelMetric): number | null {
146
+ const value = metric === "atm" ? reading.atm : reading.rr;
147
+ return value == null ? null : Number((value * 100).toFixed(2));
148
+ }
149
+
150
+ /** Whether a reading is past the rule's level, above it or below it, as a price alert's `above` and `below` are. */
151
+ function ivLevelReached(config: Pick<ResearchAlertConfig, "direction" | "threshold">, value: number | null): boolean {
152
+ if (value == null || config.threshold == null) return false;
153
+ return config.direction === "below" ? value < config.threshold : value > config.threshold;
154
+ }
155
+
156
+ /** A rule's check from a reading, or from the reason there was none. */
157
+ function ivLevelCheck(reading: IvLevelReading | Error, metric: IvLevelMetric, checkedAt: number): IvLevelCheck {
158
+ if (reading instanceof Error) return { checkedAt, value: null, error: reading.message };
159
+ const value = ivLevelValue(reading, metric);
160
+ return {
161
+ checkedAt,
162
+ value,
163
+ ...(reading.quoteAt ? { quoteAt: reading.quoteAt } : {}),
164
+ ...(reading.source ? { source: reading.source } : {}),
165
+ ...(value == null ? { error: reading.error ?? (metric === "rr" ? "No 25-delta quotes near 30 days" : "No ATM quotes near 30 days") } : {}),
166
+ };
167
+ }
168
+
169
+ /** "IBIT 30d ATM IV 52.31% is above 50%". */
170
+ function ivLevelMessage(config: ResearchAlertConfig, value: number): string {
171
+ const metric = config.metric as IvLevelMetric;
172
+ return `${config.symbol} ${IV_LEVEL_NAMES[metric]} ${formatIvLevel(metric, value)} is ${config.direction} ${formatIvLevel(metric, config.threshold ?? 0)}`;
173
+ }
174
+
175
+ /**
176
+ * An IV level rule's last check, for its row: "52.31% · quotes 10-22 20:14 UTC, 15 min delayed", the reason
177
+ * there was no reading, or that the running app has not checked it yet.
178
+ */
179
+ export function ivLevelCheckText(rule: Pick<EventAlertRule, "value">, check: IvLevelCheck | undefined): string {
180
+ if (!check) return "Not checked yet";
181
+ const metric = readResearchRule("iv_level", rule.value)?.metric as IvLevelMetric | undefined;
182
+ if (check.value == null || !metric) return check.error ?? "No reading";
183
+ const quoted = check.quoteAt ? new Date(check.quoteAt) : null;
184
+ const when = quoted && Number.isFinite(quoted.getTime())
185
+ ? `quotes ${quoted.toISOString().slice(5, 16).replace("T", " ")} UTC` : null;
186
+ return [formatIvLevel(metric, check.value), [when, check.source].filter(Boolean).join(", ")].filter(Boolean).join(" · ");
187
+ }
188
+
189
+ /** A rule that went off arms again; its last reading stays until the next check. */
190
+ export function rearmIvLevelAlert(rule: EventAlertRule): EventAlertRule {
191
+ const { triggeredAt: _triggered, ...rest } = rule;
192
+ return rest;
193
+ }
194
+
195
+ let checkNow: (() => void) | null = null;
196
+
197
+ /** Check at once, after a rule was added or re-armed, rather than at the next interval. */
198
+ export function checkIvLevelAlertsNow(): void {
199
+ checkNow?.();
200
+ }
201
+
202
+ export interface IvLevelAlertOptions {
203
+ /** One symbol's reading; it is given up on when `signal` aborts. */
204
+ load?: (config: ResearchAlertConfig, signal: AbortSignal) => Promise<IvLevelReading>;
205
+ now?: () => number;
206
+ /** How long one symbol's reading may take; about a minute. */
207
+ timeoutMs?: number;
208
+ }
209
+
210
+ /**
211
+ * Takes a reading for every active IV level rule while the app runs, one per
212
+ * symbol, and keeps it on the device for the Events tab. A rule whose reading
213
+ * is past its level, above or below it as a price alert's is (so one added or
214
+ * re-armed already past it goes off at that check), is saved as Alerted first
215
+ * and then notified, once: a save that fails tells no one, so the next check
216
+ * cannot tell twice. The rule then reads Alerted until it is re-armed. Synced
217
+ * config changes only then. With no such rules it requests nothing.
218
+ */
219
+ export function startIvLevelAlerts(
220
+ ctx: Pick<GloomPluginContext, "configState" | "resume" | "notify" | "showPane" | "log" | "marketData">,
221
+ record: (entry: FiredAlertInput) => void,
222
+ options: IvLevelAlertOptions = {},
223
+ ): () => void {
224
+ const now = options.now ?? Date.now;
225
+ const timeoutMs = options.timeoutMs ?? LOAD_TIMEOUT_MS;
226
+ let dependencies: SurfaceLoaderDependencies | null = null;
227
+ // OVDV's chains, through the coordinator the panes share, so a symbol open in OVDV is read once.
228
+ const load = options.load ?? ((config: ResearchAlertConfig, signal: AbortSignal) => {
229
+ dependencies ??= withReusedYieldCurve(createSurfaceDependencies(getSharedMarketDataCoordinator() ? undefined : ctx.marketData));
230
+ return loadIvLevelReading(config, ctx.marketData, dependencies, signal);
231
+ });
232
+ let stopped = false;
233
+ let running = false;
234
+ let rerun = false;
235
+ let timer: ReturnType<typeof setTimeout> | null = null;
236
+ const read = () => readEventAlerts(ctx.configState.get<string>(IV_LEVEL_ALERTS_KEY) ?? "[]");
237
+ const watched = (rule: EventAlertRule) => rule.kind === "iv_level" && rule.status === "active" && !rule.triggeredAt;
238
+ const configOf = (rule: EventAlertRule) => readResearchRule("iv_level", rule.value);
239
+ const keyOf = (config: ResearchAlertConfig) => `${config.symbol}:${config.exchange ?? ""}`;
240
+
241
+ /** One symbol's reading, or why there was none: a failure, or no answer within `timeoutMs`. */
242
+ const readingFor = async (config: ResearchAlertConfig): Promise<IvLevelReading | Error> => {
243
+ const abort = new AbortController();
244
+ try {
245
+ return await withDeadline(load(config, abort.signal), timeoutMs,
246
+ `Option quotes for ${config.symbol} did not arrive within ${timeoutMs >= 1000 ? `${Math.round(timeoutMs / 1000)} s` : `${timeoutMs} ms`}`,
247
+ () => abort.abort());
248
+ } catch (failure) {
249
+ ctx.log.warn("IV level unavailable", { symbol: config.symbol, error: errorMessage(failure) });
250
+ return new Error(errorMessage(failure));
251
+ }
252
+ };
253
+
254
+ const check = async () => {
255
+ const { rules, error } = read();
256
+ if (error) return;
257
+ const symbols = new Map<string, ResearchAlertConfig>();
258
+ for (const rule of rules.filter(watched)) {
259
+ const config = configOf(rule);
260
+ if (config) symbols.set(keyOf(config), config);
261
+ }
262
+ if (symbols.size === 0) return;
263
+ const readings = new Map<string, IvLevelReading | Error>();
264
+ // One symbol at a time: each reads OVDV's option chains.
265
+ for (const [key, config] of symbols) {
266
+ if (stopped) return;
267
+ readings.set(key, await readingFor(config));
268
+ }
269
+ if (stopped) return;
270
+ // Read again: the pane or a sync may have changed the rules while the chains loaded.
271
+ const current = read();
272
+ if (current.error) return;
273
+ const checkedAt = now();
274
+ const saved = readIvLevelChecks(ctx.resume.getState(IV_LEVEL_CHECKS_KEY));
275
+ const checks: Record<string, IvLevelCheck> = {};
276
+ const reached = new Map<string, { config: ResearchAlertConfig; check: IvLevelCheck }>();
277
+ for (const rule of current.rules) {
278
+ const config = watched(rule) ? configOf(rule) : null;
279
+ const reading = config ? readings.get(keyOf(config)) : undefined;
280
+ if (!config || !reading) {
281
+ // A rule not read this time keeps its last reading; a deleted one's goes.
282
+ if (saved[rule.id]) checks[rule.id] = saved[rule.id]!;
283
+ continue;
284
+ }
285
+ const result = ivLevelCheck(reading, config.metric as IvLevelMetric, checkedAt);
286
+ checks[rule.id] = result;
287
+ if (ivLevelReached(config, result.value)) reached.set(rule.id, { config, check: result });
288
+ }
289
+ try {
290
+ ctx.resume.setState(IV_LEVEL_CHECKS_KEY, checks);
291
+ } catch (failure) {
292
+ ctx.log.warn("IV level readings not kept", { error: errorMessage(failure) });
293
+ }
294
+ if (reached.size === 0 || stopped) return;
295
+
296
+ // Alerted on every device before anyone is told; only rules still watching when it is saved go off.
297
+ const fired: EventAlertRule[] = [];
298
+ const next = updateEventAlerts(ctx.configState.get<string>(IV_LEVEL_ALERTS_KEY), (latest) => latest.map((rule) => {
299
+ if (!reached.has(rule.id) || !watched(rule)) return rule;
300
+ fired.push(rule);
301
+ return { ...rule, triggeredAt: checkedAt };
302
+ }));
303
+ if (fired.length === 0) return;
304
+ try {
305
+ await ctx.configState.set(IV_LEVEL_ALERTS_KEY, next);
306
+ } catch (failure) {
307
+ ctx.log.warn("IV level alert not saved, so not sent", { error: errorMessage(failure) });
308
+ return;
309
+ }
310
+ for (const rule of fired) {
311
+ const { config, check: result } = reached.get(rule.id)!;
312
+ const metric = config.metric as IvLevelMetric;
313
+ ctx.notify({
314
+ body: ivLevelMessage(config, result.value!),
315
+ type: "success",
316
+ desktop: "always",
317
+ persistent: true,
318
+ sound: "Glass",
319
+ action: { label: "Open", onClick: () => ctx.showPane("alerts") },
320
+ });
321
+ record({
322
+ kind: "iv_level", ruleId: rule.id, rule: researchAlertDescription(rule), firedAt: checkedAt,
323
+ value: result.value, unit: metric === "atm" ? "%" : "vol pts",
324
+ quoteAt: result.quoteAt ?? null, source: result.source ?? null,
325
+ symbol: config.symbol, ...(config.exchange && config.exchange !== "US" ? { exchange: config.exchange } : {}),
326
+ });
327
+ }
328
+ };
329
+
330
+ const run = async () => {
331
+ if (stopped) return;
332
+ if (running) {
333
+ rerun = true;
334
+ return;
335
+ }
336
+ running = true;
337
+ if (timer) clearTimeout(timer);
338
+ timer = null;
339
+ try {
340
+ await check();
341
+ } catch (failure) {
342
+ if (!stopped) ctx.log.warn("IV level alerts check failed", { error: errorMessage(failure) });
343
+ } finally {
344
+ running = false;
345
+ if (!stopped) {
346
+ if (rerun) {
347
+ rerun = false;
348
+ void run();
349
+ } else {
350
+ timer = setTimeout(() => void run(), CHECK_INTERVAL_MS);
351
+ }
352
+ }
353
+ }
354
+ };
355
+
356
+ timer = setTimeout(() => void run(), FIRST_CHECK_DELAY_MS);
357
+ const trigger = () => void run();
358
+ checkNow = trigger;
359
+ return () => {
360
+ stopped = true;
361
+ if (timer) clearTimeout(timer);
362
+ timer = null;
363
+ if (checkNow === trigger) checkNow = null;
364
+ };
365
+ }
@@ -42,14 +42,14 @@ export function levelAlertCondition(level: number, currentPrice: number | null):
42
42
  * duplicate, and a store that does not parse is left alone.
43
43
  */
44
44
  export function addLevelAlert(
45
- storeJson: string | null | undefined,
45
+ storeJson: unknown,
46
46
  target: AlertTarget,
47
47
  level: number,
48
48
  currentPrice: number | null,
49
49
  ): { json: string; alert: AlertRule; created: boolean } | { error: string } {
50
- const json = storeJson ?? "[]";
51
- const error = readAlertsStoreError(json);
50
+ const error = readAlertsStoreError(storeJson);
52
51
  if (error) return { error };
52
+ const json = typeof storeJson === "string" ? storeJson : "[]";
53
53
  const alerts = deserializeAlerts(json);
54
54
  const existing = alerts.find((alert) => (
55
55
  alert.status === "active" && alert.targetPrice === level && watches(alert, target)