gloomberb 0.18.3 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -1,12 +1,12 @@
1
- import { Box } from "../../../ui";
2
- import { Button, PaneStatusBody, PaneFooterScope, usePaneTabs } from "../../../components";
1
+ import { Box, type ScrollBoxRenderable } from "../../../ui";
2
+ import { Button, DetailScrollBody, KeyValueRow, Notice, PaneStatusBody, PaneFooterScope, usePaneTabs } from "../../../components";
3
3
  import { usePluginPaneState } from "../../runtime";
4
4
  import { EventAlertsPane } from "./events-pane";
5
5
  import { AlertHistoryPane } from "./history-pane";
6
- import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore, type RefObject } from "react";
7
7
  import {
8
8
  confirmDialog,
9
- DataTableView,
9
+ DataTableStackView,
10
10
  usePaneFooter,
11
11
  type DataTableCell,
12
12
  type DataTableColumn,
@@ -73,6 +73,39 @@ const ALERT_TABLE_CONTENT_WIDTH = ALERT_COLUMNS.reduce(
73
73
  2,
74
74
  );
75
75
 
76
+ const DETAIL_ROW = { labelWidth: 10, emphasis: false } as const;
77
+
78
+ /** "AAPL above 400.00": the rule, which titles its detail. */
79
+ function alertTitle(alert: AlertRule): string {
80
+ return `${alert.symbol} ${conditionLabel(alert.condition).toLowerCase()} ${formatAlertTargetPrice(alert)}`;
81
+ }
82
+
83
+ /**
84
+ * One price alert in full: why it has no quote, when there is a reason, then
85
+ * the figures its row shortens. The title names the rule.
86
+ */
87
+ function AlertDetail({ alert, scrollRef }: { alert: AlertRule; scrollRef: RefObject<ScrollBoxRenderable | null> }) {
88
+ const reason = alert.lastCheckError
89
+ ? alert.lastCheckError.includes(alert.symbol) ? alert.lastCheckError : `${alert.symbol}: ${alert.lastCheckError}`
90
+ : null;
91
+ return (
92
+ <DetailScrollBody ref={scrollRef} resetScrollKey={alert.id}>
93
+ <Box flexDirection="column">
94
+ {reason ? <Box paddingBottom={1}><Notice tone="warning">{reason}</Notice></Box> : null}
95
+ <KeyValueRow {...DETAIL_ROW} label="State" value={alert.status === "triggered" ? "Triggered" : "Active"} tone={alert.status === "triggered" ? "positive" : "muted"} />
96
+ {/* With no quote the reason above says why; the price and its distance wait for one. */}
97
+ {reason ? null : <KeyValueRow {...DETAIL_ROW} label="Current" value={formatCurrentPrice(alert, 24)} />}
98
+ {reason ? null : <KeyValueRow {...DETAIL_ROW} label="Away" value={formatAlertDistance(alert)} />}
99
+ {reason
100
+ ? alert.lastCheckedAt ? <KeyValueRow {...DETAIL_ROW} label="Checked" value={relativeTime(alert.lastCheckedAt)} tone="muted" /> : null
101
+ : <KeyValueRow {...DETAIL_ROW} label="Quote" value={formatQuoteChecked(alert)} tone="muted" />}
102
+ {alert.triggeredAt ? <KeyValueRow {...DETAIL_ROW} label="Alerted" value={relativeTime(alert.triggeredAt)} /> : null}
103
+ <KeyValueRow {...DETAIL_ROW} label="Armed" value={relativeTime(alert.armedAt ?? alert.createdAt)} tone="muted" />
104
+ </Box>
105
+ </DetailScrollBody>
106
+ );
107
+ }
108
+
76
109
  export function AlertsPane(props: PaneProps) {
77
110
  const [tab, setTab] = usePluginPaneState<string>("tab", "prices");
78
111
  // A missed price alert opened from the Unread list selects its rule on Prices.
@@ -107,14 +140,18 @@ export function AlertsPane(props: PaneProps) {
107
140
  }
108
141
 
109
142
  function PriceAlertsPane({ focused, width, height }: PaneProps) {
110
- const [alertsJson, setAlertsJson] = usePluginConfigState<string>(ALERTS_KEY, "[]");
111
- const { openPluginCommandWorkflow } = usePluginAppActions();
143
+ const [alertsJson, setAlertsJson] = usePluginConfigState<unknown>(ALERTS_KEY, "[]");
144
+ const { openPluginCommandWorkflow, notify } = usePluginAppActions();
112
145
  const dialog = useDialog();
113
146
  const [selectedIdx, setSelectedIdx] = useState(0);
147
+ const [detailOpen, setDetailOpen] = useState(false);
148
+ // The alert the open detail shows, by id, so it never becomes whichever alert takes its row.
149
+ const detailAlertId = useRef<string | null>(null);
150
+ const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
114
151
  const showHorizontalScrollbar = ALERT_TABLE_CONTENT_WIDTH > width;
115
152
  const storeError = useMemo(() => readAlertsStoreError(alertsJson), [alertsJson]);
116
153
 
117
- const { alerts, rows, quoteError } = useMemo(() => {
154
+ const { alerts, rows } = useMemo(() => {
118
155
  const parsed = deserializeAlerts(alertsJson);
119
156
  const activeAlerts = parsed.filter((a) => a.status === "active");
120
157
  const triggeredAlerts = parsed
@@ -124,7 +161,6 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
124
161
  return {
125
162
  alerts: parsed,
126
163
  rows: [...activeAlerts, ...triggeredAlerts],
127
- quoteError: parsed.find((alert) => alert.lastCheckError)?.lastCheckError ?? null,
128
164
  };
129
165
  }, [alertsJson]);
130
166
 
@@ -158,12 +194,16 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
158
194
  return quote ? { ...alert, ...quoteAlertFields(quote, quote.receivedAt) } : alert;
159
195
  }), [liveEntries, rows]);
160
196
 
161
- const savePaneAlerts = useCallback((next: AlertRule[] | ((current: AlertRule[]) => AlertRule[])) => {
162
- setAlertsJson((currentJson) => {
197
+ const savePaneAlerts = useCallback((next: (current: AlertRule[]) => AlertRule[]): string | null => {
198
+ let error: string | null = null;
199
+ setAlertsJson((currentJson: unknown) => {
200
+ error = readAlertsStoreError(currentJson);
201
+ // The config hook skips persistence when the updater returns the unchanged value.
202
+ if (error) return currentJson;
163
203
  const current = deserializeAlerts(currentJson);
164
- const resolved = typeof next === "function" ? next(current) : next;
165
- return serializeAlerts(resolved);
204
+ return serializeAlerts(next(current));
166
205
  });
206
+ return error;
167
207
  }, [setAlertsJson]);
168
208
 
169
209
  const deleteAlert = useCallback(async (id: string) => {
@@ -175,15 +215,20 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
175
215
  confirmLabel: "Delete",
176
216
  });
177
217
  if (!confirmed) return;
178
- savePaneAlerts(alerts.filter((a) => a.id !== id));
218
+ const error = savePaneAlerts((current) => current.filter((a) => a.id !== id));
219
+ if (error) {
220
+ notify({ body: error, type: "error" });
221
+ return;
222
+ }
179
223
  setSelectedIdx((prev) => Math.max(0, Math.min(prev, rows.length - 2)));
180
- }, [alerts, dialog, rows.length, savePaneAlerts]);
224
+ }, [alerts, dialog, notify, rows.length, savePaneAlerts]);
181
225
 
182
226
  const rearmAlert = useCallback((id: string) => {
183
- savePaneAlerts(
184
- alerts.map((a) => (a.id === id ? rebuildAlert(a) : a)),
227
+ const error = savePaneAlerts(
228
+ (current) => current.map((a) => (a.id === id ? rebuildAlert(a) : a)),
185
229
  );
186
- }, [alerts, savePaneAlerts]);
230
+ if (error) notify({ body: error, type: "error" });
231
+ }, [notify, savePaneAlerts]);
187
232
 
188
233
  const startAddAlert = useCallback(() => {
189
234
  openPluginCommandWorkflow("set-alert");
@@ -195,6 +240,9 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
195
240
  }, [deleteAlert, rows, selectedIdx]);
196
241
 
197
242
  const selectedRule = rows[selectedIdx];
243
+ // A quote one alert could not get is said on its row (Current and Quote),
244
+ // and in full in its detail.
245
+ const selectedShown = displayRows[selectedIdx];
198
246
  const selectedTarget = useMemo(() => selectedRule ? alertTarget(priceRuleSubject(selectedRule)) : null, [selectedRule]);
199
247
  const openTarget = useOpenAlertTarget();
200
248
 
@@ -222,11 +270,12 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
222
270
  onApply={async (value) => {
223
271
  const parsed = parseAlertCommandValues({ shortcut: value });
224
272
  if (!parsed) throw new Error("Use SYMBOL above|below|crosses PRICE.");
225
- savePaneAlerts((current) => current.map((alert) => (
273
+ const error = savePaneAlerts((current) => current.map((alert) => (
226
274
  alert.id === selected.id
227
275
  ? editAlert(alert, parsed.symbol, parsed.condition, parsed.price)
228
276
  : alert
229
277
  )));
278
+ if (error) throw new Error(error);
230
279
  }}
231
280
  />
232
281
  ),
@@ -236,12 +285,11 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
236
285
  // Quotes come from the plugin's single background poll, which writes into the
237
286
  // same persisted store, so the pane never fetches on its own.
238
287
 
288
+ // A quote one alert could not get shows on that alert's row (Current and Quote), not here.
239
289
  usePaneFooter("alerts", () => ({
240
290
  info: storeError
241
291
  ? [{ id: "store-error", parts: [{ text: storeError, tone: "warning" as const }] }]
242
- : quoteError
243
- ? [{ id: "quote-error", parts: [{ text: quoteError, tone: "warning" as const }] }]
244
- : [],
292
+ : [],
245
293
  hints: [
246
294
  { id: "add", key: "a", label: "dd alert", onPress: startAddAlert },
247
295
  ...alertViewHints(selectedTarget, openTarget),
@@ -271,7 +319,6 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
271
319
  openTarget,
272
320
  rows,
273
321
  selectedIdx,
274
- quoteError,
275
322
  rows.length,
276
323
  startAddAlert,
277
324
  storeError,
@@ -281,6 +328,20 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
281
328
  setSelectedIdx((prev) => (rows.length === 0 ? 0 : Math.min(prev, rows.length - 1)));
282
329
  }, [rows.length]);
283
330
 
331
+ // While the detail is open, a change to the list keeps it on its alert: it follows the alert to its new row (re-armed
332
+ // or triggered, it changes group) and goes back to the list once the alert is deleted, from here or elsewhere.
333
+ // Declared before the effect below, so a delete is checked against the alert shown, not the row that takes its place.
334
+ useEffect(() => {
335
+ if (!detailOpen) return;
336
+ const index = rows.findIndex((row) => row.id === detailAlertId.current);
337
+ if (index < 0) setDetailOpen(false);
338
+ else setSelectedIdx(index);
339
+ }, [detailOpen, rows]);
340
+ // A new selection while it is open (an alert focused from Unread) is the alert it shows from then on.
341
+ useEffect(() => {
342
+ if (detailOpen) detailAlertId.current = rows[selectedIdx]?.id ?? null;
343
+ }, [detailOpen, selectedIdx]);
344
+
284
345
  // The footer hints bind a, e, d and m; `n` is a second key for adding.
285
346
  const handleTableKeyDown = useCallback((event: DataTableKeyEvent) => {
286
347
  if (!isPlainKey(event, "n")) return false;
@@ -322,7 +383,7 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
322
383
  case "quote":
323
384
  return {
324
385
  text: formatQuoteChecked(alert),
325
- color: colors.textDim,
386
+ color: alert.lastCheckError ? colors.negative : colors.textDim,
326
387
  };
327
388
  case "triggered":
328
389
  return {
@@ -333,8 +394,13 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
333
394
  }, []);
334
395
 
335
396
  return (
336
- <DataTableView<AlertRule, AlertColumn>
397
+ <DataTableStackView<AlertRule, AlertColumn>
337
398
  focused={focused}
399
+ detailOpen={detailOpen && !!selectedShown}
400
+ onBack={() => setDetailOpen(false)}
401
+ detailTitle={selectedShown ? alertTitle(selectedShown) : undefined}
402
+ detailContent={selectedShown ? <AlertDetail alert={selectedShown} scrollRef={detailScrollRef} /> : null}
403
+ detailScrollRef={detailScrollRef}
338
404
  selection={{
339
405
  kind: "index",
340
406
  selectedIndex: selectedIdx,
@@ -349,16 +415,18 @@ function PriceAlertsPane({ focused, width, height }: PaneProps) {
349
415
  sortColumnId={null}
350
416
  sortDirection="asc"
351
417
  getItemKey={(alert) => alert.id}
418
+ // Enter opens the alert; `m` re-arms a triggered one.
352
419
  onActivate={(alert) => {
353
- if (alert.status === "triggered") rearmAlert(alert.id);
420
+ detailAlertId.current = alert.id;
421
+ setDetailOpen(true);
354
422
  }}
355
423
  renderCell={renderCell}
356
424
  selectedTextOverridesCellColor
357
- emptyStateTitle="Saved alerts could not be read."
358
- emptyStateHint={storeError ?? undefined}
359
- emptyContent={storeError ? undefined : (
425
+ status={displayRows.length === 0 && storeError ? { error: storeError, subject: "price alerts" } : undefined}
426
+ emptyStateTitle="No alerts"
427
+ emptyContent={
360
428
  <PaneStatusBody empty emptyTitle="No alerts" actions={<Button label="Add alert" compact onPress={startAddAlert} />} />
361
- )}
429
+ }
362
430
  showHorizontalScrollbar={showHorizontalScrollbar}
363
431
  />
364
432
  );
@@ -0,0 +1,35 @@
1
+ import { ambiguousNumberMessage, parsePastedNumber, type PastedNumber } from "../../../utils/decimal-comma";
2
+
3
+ const PRICE_SCALES = { k: 1e3, m: 1e6, b: 1e9 } as const;
4
+ /** A price typed with a scale after it: 81k, 1.5m. */
5
+ const SCALED_PRICE = /^([+-]?\d[\d.,]*)\s*([kmb])$/i;
6
+
7
+ /** What to type for an alert price, as the error for one that reads as none names it. */
8
+ const PRICE_FORMAT = "type 81000 or 81k";
9
+
10
+ const scaled = (value: number, scale: number) => Number((value * scale).toPrecision(15));
11
+
12
+ /** A price as `parsePastedNumber` reads it, and `81k` is 81000: the scale applies to either reading of a comma and three digits. */
13
+ export function parseAlertPrice(text: string): PastedNumber {
14
+ const match = SCALED_PRICE.exec(text.trim());
15
+ if (!match) return parsePastedNumber(text);
16
+ const scale = PRICE_SCALES[match[2]!.toLowerCase() as keyof typeof PRICE_SCALES];
17
+ const base = parsePastedNumber(match[1]);
18
+ if (base.kind === "number") return { kind: "number", value: scaled(base.value, scale) };
19
+ if (base.kind === "ambiguous") return { kind: "ambiguous", thousands: scaled(base.thousands, scale), decimal: scaled(base.decimal, scale) };
20
+ return base;
21
+ }
22
+
23
+ /**
24
+ * An alert price as every way of adding an alert reads it (`parseAlertPrice`):
25
+ * the number, or an error that names what to type. A comma before exactly
26
+ * three digits (`81,000`) could be thousands or a decimal comma, so it throws
27
+ * naming both readings rather than storing either; text that is no number
28
+ * throws with an example.
29
+ */
30
+ export function readAlertPrice(text: string): number {
31
+ const price = parseAlertPrice(text);
32
+ if (price.kind === "ambiguous") throw new Error(ambiguousNumberMessage(text, price));
33
+ if (price.kind === "invalid") throw new Error(`Alert price must be a number, got "${text.trim()}"; ${PRICE_FORMAT}.`);
34
+ return price.value;
35
+ }
@@ -66,3 +66,27 @@ export function quoteErrorAlertFields(
66
66
  lastCheckError: error,
67
67
  };
68
68
  }
69
+
70
+ /** How many times the last price a target may be, either way, before it reads as a typo or another instrument's price. */
71
+ const MAX_TARGET_RATIO = 10;
72
+
73
+ /** A price as typed or quoted, never in exponent form: 85,000, 37.35, 0.0000001234. */
74
+ const priceText = (value: number) => value.toLocaleString("en-US", Math.abs(value) >= 1
75
+ ? { maximumFractionDigits: 2 }
76
+ : { maximumSignificantDigits: 4 });
77
+
78
+ /**
79
+ * Why a price target is refused: more than ten times the last price, or under
80
+ * a tenth of it, which is a slip or another instrument's price (bitcoin's,
81
+ * typed on the BTC fund). `coinNote` is the note `quote` gives a bare coin
82
+ * ticker on a listing that is not the coin, which says what to use instead.
83
+ * Null when the target is near enough, or there is no positive last price to
84
+ * measure it against.
85
+ */
86
+ export function farTargetMessage(symbol: string, target: number, last: number | null | undefined, coinNote: string | null): string | null {
87
+ if (last == null || !Number.isFinite(last) || last <= 0) return null;
88
+ const above = target > last * MAX_TARGET_RATIO;
89
+ if (!above && target >= last / MAX_TARGET_RATIO) return null;
90
+ const how = above ? `more than ${MAX_TARGET_RATIO} times` : `under 1/${MAX_TARGET_RATIO} of`;
91
+ return `A target of ${priceText(target)} is ${how} ${symbol}'s last price of ${priceText(last)}. ${coinNote ?? "Check the price and the symbol."}`;
92
+ }
@@ -1,51 +1,102 @@
1
1
  import type { GloomPlugin } from "../../../types/plugin";
2
- import { DEFAULT_LEAD_DAYS, isResearchAlertKind, MACRO_ALERT_EVENTS, normalizeResearchRule, readResearchRule, RESEARCH_ALERT_FORMS, type MacroAlertEvent, type ResearchAlertKind } from "./research-rules";
2
+ import { DEFAULT_LEAD_DAYS, isResearchAlertKind, MACRO_ALERT_EVENTS, normalizeResearchRule, readResearchRule, RESEARCH_ALERT_FORMS, type IvLevelMetric, type MacroAlertEvent, type ResearchAlertKind } from "./research-rules";
3
3
  import type { EventAlertRule } from "./events";
4
4
 
5
5
  /** What a macro event is called in a rule, a list and a notification. */
6
6
  export const MACRO_EVENT_NAMES: Record<MacroAlertEvent, string> = { cpi: "CPI release", fomc: "FOMC decision" };
7
+ /** CPI and FOMC and any other calendar release are one kind of alert to the reader, whichever list keeps the rule. */
7
8
  export const RESEARCH_LABELS: Record<ResearchAlertKind, string> = {
8
- perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", options_flow: "Options flow",
9
+ perps_threshold: "Perpetual threshold", earnings_date: "Earnings date", macro_event: "Macro event", calendar_event: "Macro event", filing_type: "SEC filing", news_keyword: "News keyword", analyst_change: "Analyst change", fifty_two_week: "52-week range", unusual_volume: "Unusual volume", short_interest_change: "Short interest", insider_trade: "Insider trade", iv_spike: "IV spike", iv_level: "IV level", options_flow: "Options flow", hiring_move: "Hiring move",
9
10
  };
11
+ /** What an IV level rule follows, as the form, a list and a notification name it. */
12
+ export const IV_LEVEL_NAMES: Record<IvLevelMetric, string> = { atm: "30d ATM IV", rr: "30d 25D RR" };
13
+ /** An IV level in its unit: an ATM IV in percent, a risk reversal in signed vol points. */
14
+ export function formatIvLevel(metric: IvLevelMetric, value: number): string {
15
+ const rounded = Number(value.toFixed(2));
16
+ return metric === "atm" ? `${rounded}%` : `${rounded > 0 ? "+" : ""}${rounded} pts`;
17
+ }
18
+ /**
19
+ * An OCC contract that has not expired, for the IV spike form's example: an
20
+ * AAPL call on the monthly expiry (the third Friday) two months from `now`.
21
+ */
22
+ function occContractExample(now = new Date()): string {
23
+ const month = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 2, 1));
24
+ const thirdFriday = 1 + ((5 - month.getUTCDay() + 7) % 7) + 14;
25
+ const two = (value: number) => String(value).padStart(2, "0");
26
+ return `AAPL${two(month.getUTCFullYear() % 100)}${two(month.getUTCMonth() + 1)}${two(thirdFriday)}C00300000`;
27
+ }
10
28
  type Command = Parameters<Parameters<NonNullable<GloomPlugin["setup"]>>[0]["registerCommand"]>[0];
11
29
  type Field = NonNullable<Command["wizard"]>[number];
30
+ /** An earnings alert goes off days before the report, or once its results are out. */
31
+ const EARNINGS_ALERT_WHEN = [{ value: "before", label: "Days before the date" }, { value: "results", label: "When results are out" }];
12
32
  const choices = (values: string[]) => values.map(value => ({label: value.replaceAll("_", " "), value}));
13
- export const researchWizardFields: Field[] = Object.keys(RESEARCH_LABELS).flatMap(kind => {
33
+ /**
34
+ * The kinds Add Event Alert offers. A calendar event is added by name from the
35
+ * shell (`alerts add "NBS PMI"`), which matches it against the calendar; the
36
+ * form keeps its CPI and FOMC choice under Macro event.
37
+ */
38
+ export const RESEARCH_FORM_KINDS = (Object.keys(RESEARCH_LABELS) as ResearchAlertKind[]).filter((kind) => kind !== "calendar_event");
39
+ export const researchWizardFields: Field[] = RESEARCH_FORM_KINDS.flatMap(kind => {
14
40
  const fields: Field[] = kind === "perps_threshold" ? [
15
41
  { key: "marketId", label: "Market", type: "text", placeholder: "hyperliquid:default:BTC", required: true },
16
42
  { key: "metric", label: "Metric", type: "select", defaultValue: "funding8h", options: [{ value: "funding8h", label: "Funding / 8h" }, { value: "oiChange24h", label: "OI change / 24h" }, { value: "premium", label: "Premium vs oracle" }, { value: "closedMarketPremium", label: "Closed-market premium" }] },
17
43
  { key: "direction", label: "Crossing", type: "select", defaultValue: "above", options: choices(["above", "below"]) },
18
44
  { key: "threshold", label: "Threshold (%)", type: "number", defaultValue: "0.1" },
19
- ] : kind === "iv_spike" ? [{key:"contract", label:"OCC contract", type:"text", placeholder:"AAPL261016C00300000", required:true}]
45
+ ] : kind === "iv_spike" ? [{key:"contract", label:"OCC contract", type:"text", placeholder:occContractExample(), required:true}]
20
46
  : kind === "macro_event" ? [{key:"event", label:"Release", type:"select", defaultValue:"cpi", options:MACRO_ALERT_EVENTS.map((value) => ({value, label:MACRO_EVENT_NAMES[value]}))}]
47
+ // HIRE covers listings worldwide; a non-US one is written with its venue.
48
+ : kind === "hiring_move" ? [{key:"symbol", label:"Symbol", type:"text", placeholder:"RIVN or ADYEN:AMS", required:true}]
21
49
  : [{key:"symbol",label:kind === "news_keyword" ? "Symbol (optional)" : kind === "options_flow" ? "Symbol (blank: portfolio and watchlists)" : "Symbol",type:"text",placeholder:"AAPL", required:kind !== "news_keyword" && kind !== "options_flow"},
22
50
  {key:"exchange",label:"US exchange",type:"select",defaultValue:"US",options:choices(["US","NASDAQ","NYSE","ARCA","AMEX","BATS","OTC"])}];
23
- if (kind === "earnings_date") fields.push({key:"leadDays",label:"Days before earnings",type:"number",defaultValue:String(DEFAULT_LEAD_DAYS)});
51
+ if (kind === "earnings_date") {
52
+ fields.push({key:"when",label:"Alert",type:"select",defaultValue:"before",options:EARNINGS_ALERT_WHEN});
53
+ fields.push({key:"leadDays",label:"Days before earnings",type:"number",defaultValue:String(DEFAULT_LEAD_DAYS)});
54
+ }
24
55
  if (kind === "macro_event") fields.push({key:"leadDays",label:"Days before",type:"number",defaultValue:String(DEFAULT_LEAD_DAYS)});
25
56
  if (kind === "filing_type") fields.push({key:"form",label:"Filing type",type:"select",defaultValue:"8-K",options:choices([...RESEARCH_ALERT_FORMS])});
26
57
  if (kind === "news_keyword") fields.push({key:"keyword",label:"Keyword or phrase",type:"text",required:true,placeholder:"buyback"});
27
- const directions: Partial<Record<ResearchAlertKind, string[]>> = {analyst_change:["any","upgrade","downgrade"],fifty_two_week:["high","low"],short_interest_change:["either","increase","decrease"],insider_trade:["either","buy","sell"],options_flow:["any","calls","puts"]};
58
+ if (kind === "iv_level") fields.push({key:"metric",label:"Measure",type:"select",defaultValue:"atm",options:[{value:"atm",label:"30d ATM IV (%)"},{value:"rr",label:"30d 25D risk reversal (vol pts)"}]});
59
+ const directions: Partial<Record<ResearchAlertKind, string[]>> = {iv_level:["above","below"],analyst_change:["any","upgrade","downgrade"],fifty_two_week:["high","low"],short_interest_change:["either","increase","decrease"],insider_trade:["either","buy","sell"],options_flow:["any","calls","puts"],hiring_move:["either","accelerating","slowing"]};
28
60
  const direction = directions[kind as ResearchAlertKind];
29
61
  if (direction) fields.push({key:"direction",label:kind === "options_flow" ? "Calls or puts" : "Direction",type:"select",defaultValue:direction[0],options:choices(direction)});
30
62
  if (kind === "options_flow") {
31
63
  fields.push({key:"threshold",label:"Minimum premium ($)",type:"number",defaultValue:"1000000"});
32
64
  fields.push({key:"print",label:"Print type",type:"select",defaultValue:"any",options:[{label:"any print",value:"any"},{label:"sweeps only",value:"sweep"},{label:"blocks only",value:"block"}]});
33
65
  }
66
+ if (kind === "iv_level") fields.push({key:"threshold",label:"Level (IV %, RR vol pts)",type:"number",placeholder:"35",required:true});
34
67
  if (kind === "unusual_volume" || kind === "short_interest_change" || kind === "iv_spike") fields.push({key:"threshold",label:kind === "unusual_volume" ? "Volume / prior 20-session average" : kind === "iv_spike" ? "IV rise (percentage points)" : "Settlement change (%)",type:"number",defaultValue:kind === "unusual_volume" ? "2" : kind === "iv_spike" ? "5" : "10"});
35
- return fields.map(field => ({...field, key:`${kind}:${field.key}`, dependsOn:{key:"event",value:kind}}));
68
+ return fields.map(field => ({...field, key:`${kind}:${field.key}`, dependsOn: kind === "earnings_date" && field.key === "leadDays"
69
+ // The lead time is for an alert before the date, not one waiting for results.
70
+ ? [{key:"event",value:kind}, {key:"earnings_date:when",value:"before"}]
71
+ : {key:"event",value:kind}}));
36
72
  });
73
+ /** The Add Event Alert form filled in for an earnings alert on `symbol` that goes off once its results are out, as ERN opens it. */
74
+ export function earningsResultsAlertValues(symbol: string): Record<string, string> {
75
+ return { event: "earnings_date", "earnings_date:symbol": symbol, "earnings_date:when": "results" };
76
+ }
37
77
  export function createResearchAlert(kind: string, values: Record<string,string>, now = Date.now()): EventAlertRule {
38
78
  if (!isResearchAlertKind(kind)) throw new Error("Choose an alert event.");
39
79
  const input: Record<string,unknown> = {version:1};
40
80
  for (const [key,value] of Object.entries(values)) if (key.startsWith(`${kind}:`)) {
41
81
  const field = key.slice(kind.length+1);
42
82
  if (kind === "perps_threshold" && field === "threshold" && value.trim()) input[field] = Number(value) / 100;
83
+ // A level reads as typed, signed (a risk reversal is often below zero) and with or without its unit.
84
+ else if (kind === "iv_level" && field === "threshold" && value.trim()) {
85
+ const unitError = ivLevelUnitError(values["iv_level:metric"] === "rr" ? "rr" : "atm", value);
86
+ if (unitError) throw new Error(`${unitError}.`);
87
+ input[field] = parseIvLevel(value);
88
+ }
43
89
  else if (value.trim()) input[field] = field === "threshold" || field === "leadDays" ? parseAmount(value) : value;
44
90
  }
45
91
  const value = normalizeResearchRule(kind,input);
46
92
  if (!value) throw new Error(kind === "options_flow"
47
93
  ? "Check the symbol and US exchange. The minimum premium is $50,000."
48
- : "Check the symbol, US exchange and event fields. Use a valid OCC contract for IV alerts.");
94
+ : kind === "hiring_move" ? "Check the symbol. Write a non-US listing with its exchange, as ADYEN:AMS."
95
+ : kind === "iv_level"
96
+ ? "Check the symbol and US exchange. An ATM IV level is 1 to 500 (%), a risk reversal -50 to 50 (vol pts)."
97
+ : kind === "iv_spike"
98
+ ? `Use a valid OCC contract, such as ${occContractExample()}.`
99
+ : "Check the symbol, US exchange and event fields.");
49
100
  return {id:crypto.randomUUID(),kind,target:"rule",value,createdAt:now,status:"active"};
50
101
  }
51
102
  /** "2.5", "1,000,000", "$250k" and "1.5m" all read as numbers; anything else is NaN. */
@@ -55,11 +106,27 @@ export function parseAmount(value: string): number {
55
106
  const scale = { k: 1e3, m: 1e6, b: 1e9 }[match[2]?.toLowerCase() as "k" | "m" | "b"] ?? 1;
56
107
  return Number(match[1]!.replace(/[,_]/g, "")) * scale;
57
108
  }
58
- /** $1M, $250K, $1.5B. */
109
+ const IV_LEVEL_PATTERN = /^([+-]?\d+(?:\.\d+)?)\s*(%|pts?|points?|vol pts?|vol points?)?$/i;
110
+ /** "35", "35%", "-4.5", "+2 pts" and "-5.5 vol points" read as numbers; anything else is NaN. */
111
+ export function parseIvLevel(value: string): number {
112
+ const match = IV_LEVEL_PATTERN.exec(value.trim());
113
+ return match ? Number(match[1]) : Number.NaN;
114
+ }
115
+ /** Why a level typed with a unit does not suit its measure (`5 pts` for an ATM IV, `-5%` for a risk reversal); null when it does or has none. */
116
+ export function ivLevelUnitError(metric: IvLevelMetric, value: string): string | null {
117
+ const unit = IV_LEVEL_PATTERN.exec(value.trim())?.[2];
118
+ if (!unit) return null;
119
+ const percent = unit === "%";
120
+ if (metric === "atm" && !percent) return `An ATM IV level is in %, not vol points ("${value.trim()}")`;
121
+ if (metric === "rr" && percent) return `A risk reversal is in vol points, not % ("${value.trim()}")`;
122
+ return null;
123
+ }
124
+ /** $1M, $250K, $1.5B, and $500 under a thousand. */
59
125
  export function formatPremium(value: number): string {
60
126
  if (value >= 1e9) return `$${Number((value / 1e9).toFixed(1))}B`;
61
127
  if (value >= 1e6) return `$${Number((value / 1e6).toFixed(1))}M`;
62
- return `$${Math.round(value / 1e3)}K`;
128
+ if (value >= 1e3) return `$${Number((value / 1e3).toFixed(1))}K`;
129
+ return `$${Number(Math.max(0, value).toFixed(2))}`;
63
130
  }
64
131
  export function researchAlertDescription(rule: EventAlertRule): string {
65
132
  if (!isResearchAlertKind(rule.kind)) return rule.value;
@@ -68,15 +135,21 @@ export function researchAlertDescription(rule: EventAlertRule): string {
68
135
  const symbol = config.symbol ?? "All symbols";
69
136
  switch(rule.kind) {
70
137
  case "perps_threshold": return `${config.marketId} · ${config.metric} ${config.direction} ${((config.threshold ?? 0) * 100).toFixed(3)}%`;
71
- case "earnings_date": return `${symbol} · ${config.leadDays}d before`;
138
+ case "earnings_date": return `${symbol} · ${config.when === "results" ? "results" : `${config.leadDays}d before`}`;
72
139
  case "macro_event": return `${MACRO_EVENT_NAMES[config.event as MacroAlertEvent] ?? config.event} · ${config.leadDays}d before`;
140
+ case "calendar_event": return `${config.event} (${config.country}) · ${config.leadDays}d before`;
73
141
  case "filing_type": return `${symbol} · ${config.form}`;
74
142
  case "news_keyword": return `${symbol} · ${config.keyword}`;
75
143
  case "analyst_change": case "insider_trade": return `${symbol} · ${config.direction}`;
144
+ case "hiring_move": return `${symbol} · ${config.direction === "either" ? "accelerating or slowing" : config.direction}`;
76
145
  case "fifty_two_week": return `${symbol} · 52w ${config.direction}`;
77
146
  case "unusual_volume": return `${symbol} · ≥${config.threshold}× 20d`;
78
147
  case "short_interest_change": return `${symbol} · ${config.direction} ≥${config.threshold}%`;
79
148
  case "iv_spike": return `${config.contract} · +${config.threshold}pt`;
149
+ case "iv_level": {
150
+ const metric = config.metric as IvLevelMetric;
151
+ return `${symbol} · ${IV_LEVEL_NAMES[metric]} ${config.direction} ${formatIvLevel(metric, config.threshold ?? 0)}`;
152
+ }
80
153
  case "options_flow": return [
81
154
  config.symbol ?? "Portfolio and watchlists",
82
155
  `≥${formatPremium(config.threshold ?? 0)}`,
@@ -2,6 +2,7 @@
2
2
  const RESEARCH_ALERT_KINDS = [
3
3
  "earnings_date",
4
4
  "macro_event",
5
+ "calendar_event",
5
6
  "filing_type",
6
7
  "news_keyword",
7
8
  "analyst_change",
@@ -10,8 +11,10 @@ const RESEARCH_ALERT_KINDS = [
10
11
  "short_interest_change",
11
12
  "insider_trade",
12
13
  "iv_spike",
14
+ "iv_level",
13
15
  "options_flow",
14
16
  "perps_threshold",
17
+ "hiring_move",
15
18
  ] as const
16
19
  export type ResearchAlertKind = (typeof RESEARCH_ALERT_KINDS)[number]
17
20
  export interface ResearchAlertConfig {
@@ -26,10 +29,32 @@ export interface ResearchAlertConfig {
26
29
  contract?: string
27
30
  /** Options flow: which prints count (any, sweep or block). */
28
31
  marketId?: string
29
- metric?: "funding8h" | "oiChange24h" | "premium" | "closedMarketPremium"
32
+ /** Perpetuals: the market figure. IV level: `atm`, the 30-day ATM IV, or `rr`, the 30-day 25-delta risk reversal. */
33
+ metric?: "funding8h" | "oiChange24h" | "premium" | "closedMarketPremium" | IvLevelMetric
30
34
  print?: string
31
- /** Macro event: which scheduled release (`MACRO_ALERT_EVENTS`). */
35
+ /**
36
+ * Macro event: which scheduled release (`MACRO_ALERT_EVENTS`). Calendar
37
+ * event: the release's name as the economic calendar lists it.
38
+ */
32
39
  event?: string
40
+ /** Calendar event: the country the calendar lists the release for (CN, US, DE for a German release). */
41
+ country?: string
42
+ /** Calendar event: who publishes it, when the calendar names them, which tells apart releases of one name. */
43
+ publisher?: string
44
+ /**
45
+ * Earnings: `results` goes off when the report's actual EPS is out, rather
46
+ * than `leadDays` before the date. Such a rule keeps a lead of 0, so a
47
+ * server that reads only the lead alerts on the day instead.
48
+ */
49
+ when?: "results"
50
+ }
51
+ /** What an IV level rule follows on its symbol's surface: 30-day ATM IV (in %) or 30-day 25-delta risk reversal (in vol points). */
52
+ const IV_LEVEL_METRICS = ["atm", "rr"] as const
53
+ export type IvLevelMetric = (typeof IV_LEVEL_METRICS)[number]
54
+ /** The levels an IV level rule takes: an ATM IV in percent, a risk reversal in vol points either side of zero. */
55
+ export const IV_LEVEL_RANGES: Record<IvLevelMetric, { min: number; max: number }> = {
56
+ atm: { min: 1, max: 500 },
57
+ rr: { min: -50, max: 50 },
33
58
  }
34
59
  /** Days before a scheduled event (earnings, a macro release) an alert fires when none is given, and the most it takes. */
35
60
  export const DEFAULT_LEAD_DAYS = 1
@@ -39,6 +64,14 @@ export const MACRO_ALERT_EVENTS = ["cpi", "fomc"] as const
39
64
  export type MacroAlertEvent = (typeof MACRO_ALERT_EVENTS)[number]
40
65
  export const isMacroAlertEvent = (value: unknown): value is MacroAlertEvent =>
41
66
  MACRO_ALERT_EVENTS.includes(value as MacroAlertEvent)
67
+ /** The longest calendar event name or publisher a rule keeps. */
68
+ const MAX_CALENDAR_TEXT = 120
69
+ /** A calendar event's name or publisher as a rule keeps it: trimmed, single-spaced, printable; empty when it is none. */
70
+ const calendarText = (value: unknown): string => {
71
+ if (typeof value !== "string") return ""
72
+ const text = value.trim().replace(/\s+/g, " ")
73
+ return text.length <= MAX_CALENDAR_TEXT && !/[\u0000-\u001f]/.test(text) ? text : ""
74
+ }
42
75
  export const RESEARCH_ALERT_FORMS = [
43
76
  "8-K",
44
77
  "10-K",
@@ -98,6 +131,14 @@ export function normalizeResearchRule(
98
131
  const event = typeof input.event === "string" ? input.event.trim().toLowerCase() : ""
99
132
  if (!isMacroAlertEvent(event)) return null
100
133
  result.event = event
134
+ } else if (kind === "calendar_event") {
135
+ const event = calendarText(input.event)
136
+ const country = typeof input.country === "string" ? input.country.trim().toUpperCase() : ""
137
+ if (!event || !/^[A-Z]{2}$/.test(country)) return null
138
+ result.event = event
139
+ result.country = country
140
+ const publisher = calendarText(input.publisher)
141
+ if (publisher) result.publisher = publisher
101
142
  } else if (kind === "perps_threshold") {
102
143
  const marketId = typeof input.marketId === "string" ? input.marketId.trim() : "";
103
144
  const metric = input.metric;
@@ -113,6 +154,14 @@ export function normalizeResearchRule(
113
154
  result.symbol = identity.symbol
114
155
  result.exchange = "US"
115
156
  result.contract = identity.contract
157
+ } else if (kind === "hiring_move") {
158
+ // A HIRE company: any listing, a world one with its venue (ADYEN:AMS).
159
+ const symbol =
160
+ typeof input.symbol === "string"
161
+ ? input.symbol.trim().toUpperCase()
162
+ : ""
163
+ if (!/^[A-Z0-9][A-Z0-9.:-]{0,31}$/.test(symbol)) return null
164
+ result.symbol = symbol
116
165
  } else if (
117
166
  (kind !== "news_keyword" && kind !== "options_flow") ||
118
167
  input.symbol
@@ -162,8 +211,17 @@ export function normalizeResearchRule(
162
211
  numeric("threshold", 0.001, -100, 100);
163
212
  direction(["above", "below"], "above");
164
213
  break;
165
- case "earnings_date":
214
+ case "earnings_date": {
215
+ const when = input.when ?? "before"
216
+ if (when === "results") {
217
+ result.leadDays = 0
218
+ result.when = "results"
219
+ } else if (when === "before") numeric("leadDays", DEFAULT_LEAD_DAYS, 0, MAX_LEAD_DAYS, true)
220
+ else throw new Error()
221
+ break
222
+ }
166
223
  case "macro_event":
224
+ case "calendar_event":
167
225
  numeric("leadDays", DEFAULT_LEAD_DAYS, 0, MAX_LEAD_DAYS, true)
168
226
  break
169
227
  case "filing_type": {
@@ -211,9 +269,24 @@ export function normalizeResearchRule(
211
269
  case "insider_trade":
212
270
  direction(["buy", "sell", "either"], "either")
213
271
  break
272
+ case "hiring_move":
273
+ direction(["either", "accelerating", "slowing"], "either")
274
+ break
214
275
  case "iv_spike":
215
276
  numeric("threshold", 5, 0.1, 100)
216
277
  break
278
+ case "iv_level": {
279
+ const metric = input.metric ?? "atm"
280
+ if (typeof metric !== "string" || !IV_LEVEL_METRICS.includes(metric as IvLevelMetric))
281
+ throw new Error()
282
+ result.metric = metric as IvLevelMetric
283
+ direction(["above", "below"], "above")
284
+ const range = IV_LEVEL_RANGES[metric as IvLevelMetric]
285
+ // No default: a level is what the rule is about.
286
+ if (input.threshold == null) throw new Error()
287
+ numeric("threshold", 0, range.min, range.max)
288
+ break
289
+ }
217
290
  case "options_flow": {
218
291
  // Premium in dollars; Cloud records nothing under $50K.
219
292
  numeric("threshold", 1_000_000, 50_000, 1_000_000_000)