gloomberb 0.18.2 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -0,0 +1,165 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { DataTableView, PaneStatusBody, QueryBar, usePaneFooter, usePaneNoticeFooter, type DataTableColumn, type DataTableKeyEvent } from "../../../components";
3
+ import { useAsyncResource } from "../../../react/async-resource";
4
+ import { useAutoRefresh } from "../../../react/auto-refresh";
5
+ import { usePaneSettingValue, usePluginAppActions } from "../../../public/react";
6
+ import { useThemeColors } from "../../../theme/theme-context";
7
+ import type { PaneProps } from "../../../types/plugin";
8
+ import { Box } from "../../../ui";
9
+ import { parsePublicTickerKey } from "../../../utils/exchanges";
10
+ import { compareSortValues, nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
11
+ import { useAssetData } from "../../runtime";
12
+ import { createScenarioDependencies } from "./client";
13
+ import { cspScreenNotes, cspScreenReportRow, type CspScreenReportRow } from "./csp-screen-headless";
14
+ import { CSP_SCREEN_DEFAULTS, CSP_SCREEN_LIMIT, cspScreenCriteriaText, cspTargetDate, loadCspScreen, readCspScreenCriteria, type CspScreenRow } from "./csp-screen";
15
+ import { formatHedgePrice } from "./hedge";
16
+
17
+ type ScreenRow = CspScreenReportRow & { source: CspScreenRow };
18
+
19
+ const COLUMNS: DataTableColumn[] = [
20
+ { id: "rank", label: "#", width: 3, align: "right" },
21
+ { id: "symbol", label: "Symbol", width: 7, align: "left" },
22
+ { id: "expiry", label: "Expiry", width: 10, align: "right" },
23
+ { id: "days", label: "DTE", width: 4, align: "right" },
24
+ { id: "strike", label: "Strike", width: 8, align: "right" },
25
+ { id: "bid", label: "Bid", width: 7, align: "right" },
26
+ { id: "ask", label: "Ask", width: 7, align: "right" },
27
+ { id: "spread", label: "Spread", width: 11, align: "right" },
28
+ { id: "delta", label: "Delta", width: 6, align: "right" },
29
+ { id: "otm", label: "OTM", width: 6, align: "right" },
30
+ { id: "midYield", label: "Mid yield/yr", width: 12, align: "right" },
31
+ { id: "bidYield", label: "Bid yield/yr", width: 12, align: "right" },
32
+ { id: "ivRank", label: "IVR", width: 4, align: "right" },
33
+ { id: "earnings", label: "Earnings", width: 17, align: "left" },
34
+ ];
35
+
36
+ const DELTA_OPTIONS = ["0.10", "0.15", "0.20", "0.25", "0.30", "0.35", "0.40", "0.50"];
37
+ const DAYS_OPTIONS = ["7", "14", "21", "30", "45", "60", "90"];
38
+ const SYMBOLS_FIELD_WIDTH = 24;
39
+ /** Symbols apply once typing pauses, so a half-typed ticker does not start a screen. */
40
+ const SYMBOLS_DEBOUNCE_MS = 700;
41
+
42
+ const symbolList = (text: string) => [...new Set(text.split(/[\s,;]+/).map((symbol) => symbol.trim().toUpperCase()).filter(Boolean))];
43
+ const percentText = (value: unknown, digits: number) => typeof value === "number" ? `${value.toFixed(digits)}%` : "--";
44
+ const priceText = (value: unknown) => typeof value === "number" ? formatHedgePrice(value) : "--";
45
+ /** A column's value to sort on: earnings by date, a flag as a number. */
46
+ function sortValue(row: CspScreenReportRow, columnId: string): string | number | null {
47
+ if (columnId === "earnings") return row.earningsDate;
48
+ const value = row[columnId as keyof CspScreenReportRow];
49
+ return typeof value === "boolean" ? Number(value) : value;
50
+ }
51
+ /** A select's options with the current value kept when it is not a preset (a delta typed on the command line). */
52
+ const withCurrent = (presets: readonly string[], current: string, label: (value: string) => string) =>
53
+ [...new Set([...presets, current])].sort((a, b) => Number(a) - Number(b)).map((value) => ({ value, label: label(value) }));
54
+
55
+ export function CspScreenPane({ width, height, focused }: PaneProps) {
56
+ const colors = useThemeColors();
57
+ const [symbolsText, setSymbolsText] = usePaneSettingValue<string>("symbols", "");
58
+ const [delta, setDelta] = usePaneSettingValue<string | number>("delta", "");
59
+ const [days, setDays] = usePaneSettingValue<string | number>("days", "");
60
+ const [expiration, setExpiration] = usePaneSettingValue<string | number>("expiration", "");
61
+ const [maxSpread] = usePaneSettingValue<string | number>("maxSpread", "");
62
+ const [symbolsActive, setSymbolsActive] = useState(false);
63
+ const { createPaneFromTemplate } = usePluginAppActions();
64
+ const assetData = useAssetData();
65
+ const typed = useMemo(() => symbolList(String(symbolsText ?? "")), [symbolsText]);
66
+ // The screen reads the first names it can rank; the footer names the rest.
67
+ const symbols = useMemo(() => typed.slice(0, CSP_SCREEN_LIMIT), [typed]);
68
+ const leftOut = typed.slice(CSP_SCREEN_LIMIT);
69
+ const criteria = useMemo(() => {
70
+ try { return { value: readCspScreenCriteria({ delta, days, expiration, maxSpread }), error: null }; }
71
+ catch (error) { return { value: null, error: error instanceof Error ? error.message : String(error) }; }
72
+ }, [delta, days, expiration, maxSpread]);
73
+ const controller = useRef<AbortController | null>(null);
74
+ const key = JSON.stringify([symbols, criteria.value]);
75
+ const loader = useCallback(async () => {
76
+ controller.current?.abort();
77
+ const current = new AbortController();
78
+ controller.current = current;
79
+ return loadCspScreen(symbols.map((symbol) => parsePublicTickerKey(symbol)), criteria.value!,
80
+ { scenario: createScenarioDependencies(), earnings: assetData }, current.signal);
81
+ }, [key, assetData]);
82
+ const resource = useAsyncResource(symbols.length && criteria.value ? loader : null);
83
+ useEffect(() => () => controller.current?.abort(), [loader]);
84
+ useAutoRefresh(resource.updatedAt, resource.load);
85
+ const [sort, setSort] = useState<{ columnId: string; direction: SortDirection }>({ columnId: "rank", direction: "asc" });
86
+ const rows = useMemo<ScreenRow[]>(() => {
87
+ const flat = (resource.data?.rows ?? []).map((row) => ({ ...cspScreenReportRow(row), source: row }));
88
+ // Unranked names stay under the ranked ones whichever way rank sorts.
89
+ return flat.sort((left, right) => sort.columnId === "rank"
90
+ ? compareSortValues(left.rank ?? Number.POSITIVE_INFINITY, right.rank ?? Number.POSITIVE_INFINITY, sort.direction)
91
+ : compareSortValues(sortValue(left, sort.columnId), sortValue(right, sort.columnId), sort.direction));
92
+ }, [resource.data, sort]);
93
+ const [selected, setSelected] = useState<string | null>(null);
94
+ const screen = resource.data;
95
+ const notices = [criteria.error, resource.data ? null : resource.error,
96
+ leftOut.length ? `Ranks the first ${CSP_SCREEN_LIMIT} symbols; ${leftOut.length} left out: ${leftOut.join(", ")}` : null,
97
+ ...(screen?.ivRankError ? [`IV rank unavailable: ${screen.ivRankError}`] : []),
98
+ ...(screen?.rows ?? []).flatMap((row) => row.note ? [`${row.symbol}: ${row.note}`] : []),
99
+ ...(screen ? cspScreenNotes(screen).slice(1) : [])].filter((value): value is string => !!value);
100
+ usePaneNoticeFooter({ registrationId: "csp-screen-notices", notices, focused });
101
+ // The bar names the delta and the expiry; the footer says only what changes.
102
+ usePaneFooter("csp-screen", () => ({ info: [
103
+ ...(resource.loading ? [{ id: "loading", parts: [{ text: "loading", tone: "muted" as const }] }] : []),
104
+ ] }), [resource.loading]);
105
+ const criteriaText = criteria.value ? cspScreenCriteriaText(criteria.value, Date.now()) : "";
106
+ // Enter opens the name's put in OSA, at the strike and expiry the screen read.
107
+ const openScenario = (row: ScreenRow) => {
108
+ const csp = row.source.csp;
109
+ if (!csp) return;
110
+ createPaneFromTemplate("options-scenario-pane", { symbol: row.symbol, values: { strategy: "csp", strike: String(csp.strike), expiration: csp.expiry } });
111
+ };
112
+ const handleKey = (event: DataTableKeyEvent): boolean => {
113
+ if (event.ctrl || event.alt || event.meta || event.name !== "r") return false;
114
+ void resource.reload();
115
+ event.preventDefault?.(); event.stopPropagation?.(); return true;
116
+ };
117
+ const cell = (row: ScreenRow, id: string): { text: string; color?: string } => {
118
+ switch (id) {
119
+ case "rank": return { text: row.rank == null ? "" : String(row.rank), color: colors.textDim };
120
+ case "symbol": return { text: row.symbol, color: colors.textBright };
121
+ case "expiry": return { text: String(row.expiry ?? "--"), color: colors.textDim };
122
+ case "days": return { text: row.days == null ? "--" : String(row.days) };
123
+ case "strike": return { text: row.strike == null ? "--" : String(row.strike) };
124
+ case "bid": return { text: priceText(row.bid) };
125
+ case "ask": return { text: priceText(row.ask) };
126
+ case "spread": return { text: `${percentText(row.spread, 1)}${row.wide ? " wide" : ""}`, color: row.wide ? colors.warning : undefined };
127
+ case "delta": return { text: typeof row.delta === "number" ? row.delta.toFixed(2) : "--" };
128
+ case "otm": return { text: percentText(row.otm, 1) };
129
+ case "midYield": return { text: percentText(row.midYield, 2), color: colors.textDim };
130
+ case "bidYield": return { text: percentText(row.bidYield, 2), color: colors.positive };
131
+ case "ivRank": return { text: typeof row.ivRank === "number" ? row.ivRank.toFixed(0) : "--" };
132
+ case "earnings": return { text: String(row.earnings), color: row.earningsBeforeExpiry === true ? colors.warning : colors.textDim };
133
+ default: return { text: "" };
134
+ }
135
+ };
136
+ const deltaValue = criteria.value ? criteria.value.delta.toFixed(2) : CSP_SCREEN_DEFAULTS.delta.toFixed(2);
137
+ const daysValue = String(criteria.value?.days ?? CSP_SCREEN_DEFAULTS.days);
138
+ // A date typed on the command line is the expiry's choice until a number of days is picked.
139
+ const typedDate = criteria.value?.expiration != null ? cspTargetDate(criteria.value, Date.now()) : null;
140
+ return <Box width={width} height={height} flexDirection="column" overflow="hidden">
141
+ <QueryBar width={width} filters={[
142
+ { id: "symbols", kind: "text" as const, label: "Symbols", value: String(symbolsText ?? ""), placeholder: "JPM, GS, KO",
143
+ width: SYMBOLS_FIELD_WIDTH, debounceMs: SYMBOLS_DEBOUNCE_MS, focused, active: symbolsActive, onActiveChange: setSymbolsActive,
144
+ onChange: setSymbolsText },
145
+ { id: "delta", label: "Delta", value: deltaValue, defaultValue: CSP_SCREEN_DEFAULTS.delta.toFixed(2),
146
+ options: withCurrent(DELTA_OPTIONS, deltaValue, (value) => value), onChange: (value: string) => setDelta(value) },
147
+ { id: "expiry", label: "Expiry", value: typedDate ?? daysValue, defaultValue: String(CSP_SCREEN_DEFAULTS.days),
148
+ options: [...withCurrent(DAYS_OPTIONS, daysValue, (value) => `${value} days`), ...(typedDate ? [{ value: typedDate, label: typedDate }] : [])],
149
+ onChange: (value: string) => { if (value === typedDate) return; setExpiration(""); setDays(value); } },
150
+ ]} />
151
+ <PaneStatusBody subject="cash-secured puts" loading={resource.loading && !resource.data}
152
+ error={criteria.error && !criteria.value ? criteria.error : !resource.data ? resource.error : null}>
153
+ <DataTableView<ScreenRow> focused={focused && !symbolsActive} columns={COLUMNS} items={rows} rootWidth={width}
154
+ rootHeight={Math.max(2, height - 1)}
155
+ getItemKey={(row) => row.symbol} sortColumnId={sort.columnId} sortDirection={sort.direction}
156
+ emptyStateTitle={symbols.length ? "No puts to rank." : "Type symbols to rank their cash-secured puts."}
157
+ onHeaderClick={(id) => setSort((current) => nextHeaderSort(current, id, { firstDirection: id === "rank" || id === "symbol" ? "asc" : "desc" }))}
158
+ selection={{ kind: "id", selectedId: selected ?? rows[0]?.symbol ?? "", getId: (row) => row.symbol, onChange: (id) => setSelected(id) }}
159
+ onActivate={openScenario} onRootKeyDown={handleKey}
160
+ getExportMetadata={() => [["puts", criteriaText || undefined], ["ranked by", "annualised yield on cash at the bid"],
161
+ ["wide", criteria.value ? `spread over ${criteria.value.maxSpreadPercent}% of the mid` : undefined]]}
162
+ renderCell={(row, column) => cell(row, column.id)} />
163
+ </PaneStatusBody>
164
+ </Box>;
165
+ }
@@ -0,0 +1,258 @@
1
+ import type { InstrumentRef } from "../../../market-data/request-types";
2
+ import type { DataProvider } from "../../../types/data-provider";
3
+ import type { HeadlessPaneFreshness } from "../../../types/plugin";
4
+ import { mapWithConcurrency } from "../../../utils/concurrency";
5
+ import { numberErrorMessage, wholeNumberOptionMessage } from "../../../utils/decimal-comma";
6
+ import { errorMessage } from "../../../utils/errors";
7
+ import { expirationHint, expiredExpiryText, expiryIsoDate, isLiveOptionExpiry, readOptionExpiration } from "../../../utils/option-expiry";
8
+ import { zonedDateKey } from "../../../utils/zoned-date-time";
9
+ import { ivRankReason, loadIvScreen, type ImpliedVolatilityApi, type IvScreenRow } from "../iv-history/client";
10
+ import { quoteObservation } from "../shared/report-freshness";
11
+ import { loadScenarioMarket, scenarioPositionFromSettings, scenarioSpotBasis, type ScenarioLoaderDependencies, type ScenarioMarketSnapshot } from "./client";
12
+ import { cashSecuredPut, loadNextEarningsDates, type CashSecuredPut } from "./csp";
13
+ import { seededLegQuotes } from "./hedge";
14
+ import { scenarioValue } from "./model";
15
+
16
+ /**
17
+ * A cash-secured put screen (docs/research-data.md#cash-secured-put-screen):
18
+ * for each name, the put OSA's `--strategy csp --delta` seed sells on the
19
+ * listed expiry nearest the target, read by the same `cashSecuredPut`, ranked
20
+ * by its annualised yield at the bid.
21
+ */
22
+ export interface CspScreenCriteria {
23
+ /** The absolute put delta each name's put is nearest, such as 0.30. */
24
+ delta: number;
25
+ /** Calendar days out the expiry is nearest, when no date is given. */
26
+ days: number;
27
+ /** The date the expiry is nearest, in Unix seconds, in place of `days`; null when none is given. */
28
+ expiration: number | null;
29
+ /** A spread over this percent of the midpoint marks the put wide. */
30
+ maxSpreadPercent: number;
31
+ }
32
+
33
+ export const CSP_SCREEN_DEFAULTS: Readonly<Omit<CspScreenCriteria, "expiration">> = { delta: 0.3, days: 30, maxSpreadPercent: 10 };
34
+ /** The most names one screen reads: each is two chain requests. */
35
+ export const CSP_SCREEN_LIMIT = 20;
36
+ const DAY_MS = 86_400_000;
37
+ const CONCURRENCY = 4;
38
+
39
+ export interface CspScreenRow {
40
+ symbol: string;
41
+ /** 1 for the highest yield at the bid; null for a name with no put to rank. */
42
+ rank: number | null;
43
+ csp: CashSecuredPut | null;
44
+ /** The put's delta per share, negative, at the IV its midpoint implies. */
45
+ delta: number | null;
46
+ /** 30-day IV rank, 0 to 100, against the prior 52 weeks; null when IV history has none. */
47
+ ivRank: number | null;
48
+ /** Its spread is over the screen's `maxSpreadPercent` of the midpoint. */
49
+ wideSpread: boolean;
50
+ /** Why the name has no put to rank. */
51
+ note: string | null;
52
+ }
53
+
54
+ export interface CspScreen {
55
+ rows: CspScreenRow[];
56
+ criteria: CspScreenCriteria;
57
+ /** Names IV history has no rank for, with why when it can say, such as `XYZ (no options history)`; a bare symbol has no reason to give. */
58
+ missingRanks: string[];
59
+ /** The IV rank request failed for every name. */
60
+ ivRankError: string | null;
61
+ freshness: HeadlessPaneFreshness;
62
+ }
63
+
64
+ const supplied = (value: unknown) => value != null && value !== "";
65
+
66
+ function readNumber(value: unknown, flag: string, example: string): number {
67
+ const text = typeof value === "number" ? String(value) : typeof value === "string" ? value.trim().replace(/%$/, "") : "";
68
+ const number = Number(text);
69
+ if (!text || !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text) || !Number.isFinite(number)) {
70
+ throw new Error(numberErrorMessage(`Invalid --${flag} "${String(value)}". Use ${example}.`, text));
71
+ }
72
+ return number;
73
+ }
74
+
75
+ /** `--days`: whole calendar days. A decimal is told the whole numbers beside it, never a decimal to type. */
76
+ function readDays(value: unknown): number {
77
+ const decimal = wholeNumberOptionMessage("--days", "days", value);
78
+ if (decimal) throw new Error(decimal);
79
+ const days = readNumber(value, "days", "whole calendar days, such as 30");
80
+ if (!Number.isInteger(days) || days < 1) throw new Error(`Invalid --days "${String(value)}". Use whole calendar days, such as 30.`);
81
+ return days;
82
+ }
83
+
84
+ /** The screen's criteria from `fn CSP` options or the pane's settings, each default where not set. */
85
+ export function readCspScreenCriteria(options: Record<string, unknown>): CspScreenCriteria {
86
+ const delta = supplied(options.delta) ? Math.abs(readNumber(options.delta, "delta", "a put's delta between 0 and 1, such as 0.30")) : CSP_SCREEN_DEFAULTS.delta;
87
+ if (!(delta > 0 && delta < 1)) throw new Error(`Invalid --delta "${String(options.delta)}". Use a put's delta between 0 and 1, such as 0.30.`);
88
+ const days = supplied(options.days) ? readDays(options.days) : CSP_SCREEN_DEFAULTS.days;
89
+ const maxSpreadPercent = supplied(options.maxSpread)
90
+ ? readNumber(options.maxSpread, "max-spread", "a percent of the midpoint above 0, such as 10") : CSP_SCREEN_DEFAULTS.maxSpreadPercent;
91
+ if (!(maxSpreadPercent > 0)) throw new Error(`Invalid --max-spread "${String(options.maxSpread)}". Use a percent of the midpoint above 0, such as 10.`);
92
+ const expiration = supplied(options.expiration) ? readOptionExpiration(options.expiration) : null;
93
+ if (supplied(options.expiration) && expiration == null) throw new Error(`Invalid --expiration "${String(options.expiration)}". Use a date such as 2026-11-20.${expirationHint(String(options.expiration))}`);
94
+ if (expiration != null && supplied(options.days)) throw new Error("--days and --expiration each pick the expiry; give one of them");
95
+ return { delta, days, expiration, maxSpreadPercent };
96
+ }
97
+
98
+ /** The date each name's expiry is nearest, YYYY-MM-DD: the typed date, else `days` out from today in New York. */
99
+ export function cspTargetDate(criteria: Pick<CspScreenCriteria, "days" | "expiration">, now: number): string {
100
+ if (criteria.expiration != null) return expiryIsoDate(criteria.expiration);
101
+ return new Date(Date.parse(`${zonedDateKey(now, "America/New_York")}T00:00:00Z`) + criteria.days * DAY_MS).toISOString().slice(0, 10);
102
+ }
103
+
104
+ /** The listed expiry whose date is nearest `target` (YYYY-MM-DD), the earlier on a tie; undefined when none is listed. */
105
+ export function nearestListedExpiry(expirations: readonly number[], target: string): number | undefined {
106
+ const targetTime = Date.parse(`${target}T00:00:00Z`);
107
+ let best: { expiration: number; distance: number } | undefined;
108
+ for (const expiration of [...expirations].sort((a, b) => a - b)) {
109
+ const distance = Math.abs(Date.parse(`${expiryIsoDate(expiration)}T00:00:00Z`) - targetTime);
110
+ if (Number.isFinite(distance) && (!best || distance < best.distance)) best = { expiration, distance };
111
+ }
112
+ return best?.expiration;
113
+ }
114
+
115
+ const bidYield = (row: CspScreenRow) => row.csp?.quote?.bidAnnualizedYield ?? null;
116
+
117
+ /**
118
+ * Highest annualised yield at the bid first; a wide spread is ranked with the
119
+ * rest and flagged, never left out. A put with no yield to rank (it expires
120
+ * today) follows, then the names with no put, by symbol.
121
+ */
122
+ export function rankCspScreen(rows: readonly Omit<CspScreenRow, "rank">[]): CspScreenRow[] {
123
+ const tier = (row: Omit<CspScreenRow, "rank">) => bidYield(row as CspScreenRow) != null ? 0 : row.csp ? 1 : 2;
124
+ const sorted = [...rows].sort((left, right) => tier(left) - tier(right)
125
+ || (bidYield(right as CspScreenRow) ?? 0) - (bidYield(left as CspScreenRow) ?? 0)
126
+ || left.symbol.localeCompare(right.symbol));
127
+ let rank = 0;
128
+ return sorted.map((row) => ({ ...row, rank: tier(row) === 0 ? ++rank : null }));
129
+ }
130
+
131
+ /** Whether a put's spread is over the screen's limit. */
132
+ function cspWideSpread(csp: CashSecuredPut | null, maxSpreadPercent: number): boolean {
133
+ return csp?.quote != null && csp.quote.spreadOfMid * 100 > maxSpreadPercent;
134
+ }
135
+
136
+ export interface CspScreenDependencies {
137
+ scenario: ScenarioLoaderDependencies;
138
+ /** The earnings calendar's source. */
139
+ earnings: DataProvider | null | undefined;
140
+ /** Gloom Cloud's IV history; the app's client when omitted. */
141
+ ivApi?: ImpliedVolatilityApi;
142
+ }
143
+
144
+ interface ScreenedPut { csp: CashSecuredPut; delta: number | null; market: ScenarioMarketSnapshot }
145
+
146
+ /** One name's put: OSA's csp seed at `--delta` on the listed expiry nearest the target, read as OSA reads it. */
147
+ async function screenPut(
148
+ instrument: InstrumentRef, criteria: CspScreenCriteria, dependencies: ScenarioLoaderDependencies, target: string,
149
+ earningsDate: string | null, signal: AbortSignal | undefined,
150
+ ): Promise<ScreenedPut> {
151
+ const first = await loadScenarioMarket({ instrument, signal }, dependencies);
152
+ // A typed date whose last session has closed is not stood in for by the next one: it says so, and names it.
153
+ const now = dependencies.now?.() ?? Date.now();
154
+ if (criteria.expiration != null && !isLiveOptionExpiry(criteria.expiration, now, instrument.exchange)) {
155
+ throw new Error(`Expiry ${expiryIsoDate(criteria.expiration)} `
156
+ + expiredExpiryText(criteria.expiration, first.expirationDates, now, { exchange: instrument.exchange, flag: "expiration" }));
157
+ }
158
+ const expiration = nearestListedExpiry(first.expirationDates, target);
159
+ if (expiration == null) {
160
+ throw new Error(first.warnings.find((warning) => warning.startsWith("Options chain")) ?? "No listed option expiries");
161
+ }
162
+ const loaded = first.chain ? [...first.chain.puts, ...first.chain.calls][0]?.expiration : undefined;
163
+ const market = loaded != null && expiryIsoDate(loaded) === expiryIsoDate(expiration)
164
+ ? first : await loadScenarioMarket({ instrument, expiration, signal }, dependencies);
165
+ const settings = { symbol: instrument.symbol, exchange: instrument.exchange, strategy: "csp", delta: criteria.delta };
166
+ const position = scenarioPositionFromSettings(settings, market);
167
+ const leg = position?.legs[0];
168
+ if (!position || !leg) throw new Error("No put to sell on this chain");
169
+ const quote = seededLegQuotes(position, market.chain).get(leg.id);
170
+ const csp = cashSecuredPut(position, scenarioSpotBasis(settings, position, market), earningsDate, quote);
171
+ if (!csp) throw new Error("No put to sell on this chain");
172
+ const shares = leg.quantity * leg.multiplier;
173
+ const delta = scenarioValue(position, position.spot, position.asOf).delta / shares;
174
+ return { csp, delta: Number.isFinite(delta) ? delta : null, market };
175
+ }
176
+
177
+ async function loadIvRanks(symbols: readonly string[], signal: AbortSignal | undefined, api: ImpliedVolatilityApi | undefined) {
178
+ try {
179
+ const payload = await loadIvScreen(symbols, { signal }, api);
180
+ return { rows: new Map<string, IvScreenRow>(payload.rows.map((row) => [row.symbol.toUpperCase(), row])), error: null };
181
+ } catch (error) {
182
+ if (signal?.aborted) throw error;
183
+ return { rows: new Map<string, IvScreenRow>(), error: errorMessage(error) ?? "no response" };
184
+ }
185
+ }
186
+
187
+ /**
188
+ * How current the screen is: each name's underlying quote dates its row, and
189
+ * the chains' feed (delayed when any is) says how far behind they run.
190
+ */
191
+ function screenFreshness(markets: readonly ScenarioMarketSnapshot[], now: number): HeadlessPaneFreshness {
192
+ const chains = markets.flatMap((market) => market.chain ? [market.chain] : []);
193
+ const delayed = chains.some((chain) => chain.dataSource === "delayed");
194
+ const live = chains.length > 0 && chains.every((chain) => chain.dataSource === "live");
195
+ const delayMinutes = Math.max(0, ...chains.map((chain) => chain.delayMinutes ?? 0));
196
+ const observations = markets.flatMap((market) => market.underlyingQuote ? [{ ...quoteObservation(market.underlyingQuote, now) }] : []);
197
+ return {
198
+ ...(delayed ? { status: "delayed" as const } : live ? { status: "live" as const } : {}),
199
+ ...(delayed && delayMinutes ? { delayMinutes } : {}),
200
+ ...(observations.length ? { observations } : markets[0] ? { asOf: markets[0].asOf } : {}),
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Each name's cash-secured put, ranked: one IV rank request, one earnings
206
+ * calendar request and one Treasury curve for all of them.
207
+ */
208
+ export async function loadCspScreen(
209
+ instruments: readonly InstrumentRef[], criteria: CspScreenCriteria, dependencies: CspScreenDependencies, signal?: AbortSignal,
210
+ ): Promise<CspScreen> {
211
+ const now = dependencies.scenario.now?.() ?? Date.now();
212
+ // Every name reads the same curve, up to twice (the catalogue, then its expiry): asked for once, when the first name needs it.
213
+ let curve: ReturnType<ScenarioLoaderDependencies["loadYieldCurve"]> | null = null;
214
+ const scenario: ScenarioLoaderDependencies = { ...dependencies.scenario, loadYieldCurve: () => curve ??= dependencies.scenario.loadYieldCurve() };
215
+ const symbols = instruments.map((instrument) => instrument.symbol.trim().toUpperCase());
216
+ const [ivRanks, earnings] = await Promise.all([
217
+ loadIvRanks(symbols, signal, dependencies.ivApi),
218
+ loadNextEarningsDates(dependencies.earnings, symbols, zonedDateKey(now, "America/New_York")),
219
+ ]);
220
+ signal?.throwIfAborted();
221
+ const target = cspTargetDate(criteria, now);
222
+ const markets: ScenarioMarketSnapshot[] = [];
223
+ const missingRanks: string[] = [];
224
+ const rows = await mapWithConcurrency(instruments, CONCURRENCY, async (instrument, index): Promise<Omit<CspScreenRow, "rank">> => {
225
+ const symbol = symbols[index]!;
226
+ const ivRow = ivRanks.rows.get(symbol);
227
+ const rank = ivRow?.iv30?.rank;
228
+ const ivRank = rank != null && Number.isFinite(rank) ? rank : null;
229
+ try {
230
+ const screened = await screenPut(instrument, criteria, scenario, target, earnings.get(symbol) ?? null, signal);
231
+ markets.push(screened.market);
232
+ if (ivRank == null && !ivRanks.error) {
233
+ const reason = ivRankReason(ivRow);
234
+ missingRanks.push(reason ? `${symbol} (${reason})` : symbol);
235
+ }
236
+ return { symbol, csp: screened.csp, delta: screened.delta, ivRank, wideSpread: cspWideSpread(screened.csp, criteria.maxSpreadPercent), note: null };
237
+ } catch (error) {
238
+ if (signal?.aborted) throw error;
239
+ return { symbol, csp: null, delta: null, ivRank, wideSpread: false, note: errorMessage(error) ?? "No put to sell" };
240
+ }
241
+ });
242
+ return { rows: rankCspScreen(rows), criteria, missingRanks: missingRanks.sort(), ivRankError: ivRanks.error,
243
+ freshness: screenFreshness(markets, now) };
244
+ }
245
+
246
+ /** `nearest 0.30 delta · expiry nearest 2026-11-21 (30 days)`: what each name's put is. */
247
+ export function cspScreenCriteriaText(criteria: CspScreenCriteria, now: number): string {
248
+ return `nearest ${criteria.delta.toFixed(2)} delta · expiry nearest ${cspTargetDate(criteria, now)}`
249
+ + (criteria.expiration == null ? ` (${criteria.days} days)` : "");
250
+ }
251
+
252
+ /** `before 2026-11-04`, `after 2027-01-19`, `unknown`: whether the next report falls before the put expires, and its date. */
253
+ export function cspEarningsText(csp: CashSecuredPut | null): string {
254
+ if (!csp) return "--";
255
+ const { beforeExpiry, date } = csp.earnings;
256
+ return beforeExpiry == null ? "unknown" : `${beforeExpiry ? "before" : "after"} ${date}`;
257
+ }
258
+
@@ -2,8 +2,11 @@ import type { StatItem } from "../../../components";
2
2
  import type { DataProvider } from "../../../types/data-provider";
3
3
  import type { HeadlessPaneEntry } from "../../../types/headless";
4
4
  import { loadEarningsCalendar } from "../earnings/data/cache";
5
+ import { optionSpread } from "../options/market-reference";
6
+ import { newYorkDay, resolveListingEvents, upcomingReportDate } from "../shared/earnings-calendar";
7
+ import { parsePublicTickerKey } from "../../../utils/exchanges";
5
8
  import { formatHedgePrice } from "./hedge";
6
- import { UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
9
+ import { amountUnit, exportPercent, UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
7
10
 
8
11
  /**
9
12
  * A cash-secured put (docs/research-data.md#cash-secured-put): one short put
@@ -15,6 +18,9 @@ import { UNKNOWN_CURRENCY, type ScenarioPosition } from "./model";
15
18
  * seed enters at the quote midpoint
16
19
  * - yield on cash = premium / cash secured
17
20
  * - annualised yield = yield on cash x 365 / days to expiry (simple, not compounded)
21
+ * - the same premium and yields at the bid, the price a sell order fills at
22
+ * without crossing the spread, when the put has a two-sided quote
23
+ * - spread = ask - bid, and as a percent of the midpoint
18
24
  * - out of the money = (spot - strike) / spot, against the scenario's spot
19
25
  * - break-even = strike - entry price
20
26
  * - days to expiry = calendar days from the valuation date in New York to the expiry date
@@ -38,6 +44,8 @@ export interface CashSecuredPut {
38
44
  yieldOnCash: number;
39
45
  /** Null on the expiry day, when there is no day to annualise over. */
40
46
  annualizedYield: number | null;
47
+ /** The put's two-sided quote and what selling at its bid yields; null without one. */
48
+ quote: CspQuote | null;
41
49
  daysToExpiry: number;
42
50
  /** Valuation date in New York, YYYY-MM-DD. */
43
51
  valuationDate: string;
@@ -50,6 +58,20 @@ export interface CashSecuredPut {
50
58
  earnings: CspEarnings;
51
59
  }
52
60
 
61
+ interface CspQuote {
62
+ bid: number;
63
+ ask: number;
64
+ /** ask - bid, per share. */
65
+ spread: number;
66
+ /** The spread over the midpoint, as a fraction. */
67
+ spreadOfMid: number;
68
+ /** Premium at the bid, for every contract. */
69
+ bidPremium: number;
70
+ bidYieldOnCash: number;
71
+ /** Null on the expiry day. */
72
+ bidAnnualizedYield: number | null;
73
+ }
74
+
53
75
  export interface CspEarnings {
54
76
  /** The next report date, YYYY-MM-DD, or null when none is published. */
55
77
  date: string | null;
@@ -71,12 +93,22 @@ export function earningsBeforeExpiry(earningsDate: string | null | undefined, va
71
93
  return { date: earningsDate, beforeExpiry: earningsDate <= expiry };
72
94
  }
73
95
 
96
+ /** What selling at `price` per share earns on the cash secured: the one yield calculation, at the entry price and at the bid alike. */
97
+ function putYield(price: number, cashSecured: number, sharesSold: number, daysToExpiry: number) {
98
+ const premium = Math.round(price * sharesSold * 1e6) / 1e6;
99
+ const yieldOnCash = premium / cashSecured;
100
+ return { premium, yieldOnCash, annualizedYield: daysToExpiry > 0 ? yieldOnCash * 365 / daysToExpiry : null };
101
+ }
102
+
74
103
  /**
75
104
  * The position as a cash-secured put, or null when it is not one short put.
76
- * `spotBasis` names the spot the out-of-the-money distance is read against.
105
+ * `spotBasis` names the spot the out-of-the-money distance is read against;
106
+ * `quote` is the put's bid and ask, for a put seeded from the chain
107
+ * (`seededLegQuotes`).
77
108
  */
78
109
  export function cashSecuredPut(
79
110
  position: ScenarioPosition, spotBasis: string, earningsDate: string | null | undefined,
111
+ quote?: { bid: number; ask: number } | null,
80
112
  ): CashSecuredPut | null {
81
113
  const [leg] = position.legs;
82
114
  if (position.legs.length !== 1 || !leg || leg.side !== "put" || leg.quantity >= 0 || position.underlying) return null;
@@ -85,12 +117,19 @@ export function cashSecuredPut(
85
117
  const valuationDate = newYorkCalendarDate(position.asOf);
86
118
  const daysToExpiry = Math.max(0, Math.round((Date.parse(`${expiry}T00:00:00Z`) - Date.parse(`${valuationDate}T00:00:00Z`)) / DAY_MS));
87
119
  const cashSecured = leg.strike * leg.multiplier * contracts;
88
- const premium = Math.round(leg.price * leg.multiplier * contracts * 1e6) / 1e6;
89
- const yieldOnCash = premium / cashSecured;
120
+ const sharesSold = leg.multiplier * contracts;
121
+ const atEntry = putYield(leg.price, cashSecured, sharesSold, daysToExpiry);
122
+ const spread = quote ? optionSpread(quote) : null;
123
+ let bidQuote: CspQuote | null = null;
124
+ if (quote && spread?.kind === "two-sided") {
125
+ const atBid = putYield(quote.bid, cashSecured, sharesSold, daysToExpiry);
126
+ bidQuote = { bid: quote.bid, ask: quote.ask, spread: spread.spread, spreadOfMid: spread.percentOfMid / 100,
127
+ bidPremium: atBid.premium, bidYieldOnCash: atBid.yieldOnCash, bidAnnualizedYield: atBid.annualizedYield };
128
+ }
90
129
  return {
91
130
  currency: position.currency, strike: leg.strike, expiry, contracts, multiplier: leg.multiplier,
92
131
  price: leg.price, priceBasis: leg.priceSource === "mid" ? "mid" : "entry",
93
- cashSecured, premium, yieldOnCash, annualizedYield: daysToExpiry > 0 ? yieldOnCash * 365 / daysToExpiry : null,
132
+ cashSecured, ...atEntry, quote: bidQuote,
94
133
  daysToExpiry, valuationDate, spot: position.spot, spotBasis,
95
134
  outOfTheMoney: position.spot > 0 ? (position.spot - leg.strike) / position.spot : null,
96
135
  breakEven: leg.strike - leg.price,
@@ -99,15 +138,31 @@ export function cashSecuredPut(
99
138
  }
100
139
 
101
140
  /**
102
- * The next report date from the earnings calendar, YYYY-MM-DD; null when the
103
- * calendar has none for the symbol or cannot be read.
141
+ * The next report date, YYYY-MM-DD, as every earnings surface resolves it
142
+ * (shared/earnings-calendar): the calendar's, or once that date has passed
143
+ * the next one on record; null when there is none or the calendar cannot be
144
+ * read.
104
145
  */
105
- export async function loadNextEarningsDate(provider: DataProvider | null | undefined, symbol: string): Promise<string | null> {
106
- const key = symbol.trim().toUpperCase();
107
- const result = await loadEarningsCalendar(provider, [key]).catch(() => null);
108
- const event = result?.events.find((entry) => entry.symbol.trim().toUpperCase() === key);
109
- const time = event?.earningsDate.getTime();
110
- return time != null && Number.isFinite(time) ? new Date(time).toISOString().slice(0, 10) : null;
146
+ export async function loadNextEarningsDate(provider: DataProvider | null | undefined, symbol: string, today = newYorkDay()): Promise<string | null> {
147
+ return (await loadNextEarningsDates(provider, [symbol], today)).get(symbol.trim().toUpperCase()) ?? null;
148
+ }
149
+
150
+ /** The next report date of each symbol, by its upper-case symbol, from one calendar request; a symbol with none is left out. */
151
+ export async function loadNextEarningsDates(
152
+ provider: DataProvider | null | undefined, symbols: readonly string[], today = newYorkDay(),
153
+ ): Promise<Map<string, string>> {
154
+ const keys = [...new Set(symbols.map((symbol) => symbol.trim().toUpperCase()))];
155
+ const result = await loadEarningsCalendar(provider, keys).catch(() => null);
156
+ if (!provider || !result?.events.length) return new Map();
157
+ const listings = keys.map((key) => {
158
+ const listing = parsePublicTickerKey(key);
159
+ return { key, symbol: listing.symbol, exchange: listing.exchange ?? "" };
160
+ });
161
+ const resolved = await resolveListingEvents(provider, listings, result.events, today);
162
+ return new Map(keys.flatMap((key) => {
163
+ const date = upcomingReportDate(resolved.get(key));
164
+ return date ? [[key, date] as const] : [];
165
+ }));
111
166
  }
112
167
 
113
168
  const money = (value: number) => value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
@@ -129,22 +184,40 @@ function earningsText(earnings: CspEarnings): string {
129
184
 
130
185
  const priceWord = (csp: CashSecuredPut) => csp.priceBasis === "mid" ? "quote mid" : "entry price";
131
186
 
132
- /** The report's Cash-secured put section; `value` keeps full precision, `formatted` is what text output prints. */
187
+ /**
188
+ * The report's Cash-secured put section. Each figure's `value` is in its
189
+ * `unit`, every digit (a percent as a percent), so an export writes the number
190
+ * and its unit; `formatted` is what text output prints.
191
+ */
133
192
  export function cspReportEntries(csp: CashSecuredPut): HeadlessPaneEntry[] {
134
193
  const unit = unitOf(csp);
194
+ const amount = { unit: amountUnit(csp.currency) };
195
+ const perShare = `${amount.unit} per share`;
196
+ const quote = csp.quote;
135
197
  return [
136
- { key: "cashSecured", label: "Cash secured", value: csp.cashSecured,
198
+ { key: "cashSecured", label: "Cash secured", value: csp.cashSecured, ...amount,
137
199
  formatted: `${money(csp.cashSecured)}${unit} (${strikeText(csp.strike)} x ${csp.multiplier} x ${csp.contracts})` },
138
- { key: "premium", label: "Premium received", value: csp.premium,
200
+ { key: "premium", label: "Premium received", value: csp.premium, ...amount,
139
201
  formatted: `${money(csp.premium)}${unit} (${formatHedgePrice(csp.price)} per share, ${priceWord(csp)})` },
140
- { key: "yieldOnCash", label: "Yield on cash", value: csp.yieldOnCash,
202
+ { key: "yieldOnCash", label: "Yield on cash", value: exportPercent(csp.yieldOnCash), unit: "%",
141
203
  formatted: `${percent(csp.yieldOnCash)} (${money(csp.premium)} / ${money(csp.cashSecured)})` },
142
- { key: "annualizedYield", label: "Annualised yield", value: csp.annualizedYield,
204
+ { key: "annualizedYield", label: "Annualised yield", value: exportPercent(csp.annualizedYield), unit: "%",
143
205
  formatted: csp.annualizedYield == null ? "-- (expires today)" : `${percent(csp.annualizedYield)} (yield on cash x 365 / ${csp.daysToExpiry})` },
144
- { key: "outOfTheMoney", label: "Out of the money", value: csp.outOfTheMoney, formatted: otmText(csp) },
145
- { key: "breakEven", label: "Break-even", value: csp.breakEven,
206
+ ...(quote ? [
207
+ { key: "bid", label: "Bid", value: quote.bid, unit: perShare, formatted: `${formatHedgePrice(quote.bid)} per share` },
208
+ { key: "ask", label: "Ask", value: quote.ask, unit: perShare, formatted: `${formatHedgePrice(quote.ask)} per share` },
209
+ { key: "spread", label: "Spread", value: exportPercent(quote.spreadOfMid), unit: "% of mid",
210
+ formatted: `${formatHedgePrice(quote.spread)} per share (${percent(quote.spreadOfMid, 1)} of the mid)` },
211
+ { key: "bidPremium", label: "Premium at the bid", value: quote.bidPremium, ...amount,
212
+ formatted: `${money(quote.bidPremium)}${unit} (${formatHedgePrice(quote.bid)} x ${csp.multiplier} x ${csp.contracts})` },
213
+ { key: "bidAnnualizedYield", label: "Annualised yield at the bid", value: exportPercent(quote.bidAnnualizedYield), unit: "%",
214
+ formatted: quote.bidAnnualizedYield == null ? "-- (expires today)"
215
+ : `${percent(quote.bidAnnualizedYield)} (${percent(quote.bidYieldOnCash)} on cash x 365 / ${csp.daysToExpiry})` },
216
+ ] : []),
217
+ { key: "outOfTheMoney", label: "Out of the money", value: exportPercent(csp.outOfTheMoney), unit: "%", formatted: otmText(csp) },
218
+ { key: "breakEven", label: "Break-even", value: csp.breakEven, unit: perShare,
146
219
  formatted: `${formatHedgePrice(csp.breakEven)} per share (${strikeText(csp.strike)} - ${formatHedgePrice(csp.price)})` },
147
- { key: "daysToExpiry", label: "Days to expiry", value: csp.daysToExpiry,
220
+ { key: "daysToExpiry", label: "Days to expiry", value: csp.daysToExpiry, unit: "days",
148
221
  formatted: `${csp.daysToExpiry} (${csp.valuationDate} to ${csp.expiry})` },
149
222
  { key: "earningsBeforeExpiry", label: "Earnings before expiry", value: csp.earnings.beforeExpiry, formatted: earningsText(csp.earnings) },
150
223
  ];
@@ -153,11 +226,18 @@ export function cspReportEntries(csp: CashSecuredPut): HeadlessPaneEntry[] {
153
226
  /** The pane's figures: the same numbers as the report, sized for a StatGrid. */
154
227
  export function cspStatItems(csp: CashSecuredPut): StatItem[] {
155
228
  const earnings = csp.earnings;
229
+ const quote = csp.quote;
156
230
  return [
157
231
  { id: "csp-cash", label: "Cash secured", value: money(csp.cashSecured), detail: `${strikeText(csp.strike)} x ${csp.multiplier * csp.contracts}` },
158
232
  { id: "csp-premium", label: "Premium", value: money(csp.premium), detail: `${priceWord(csp)} ${formatHedgePrice(csp.price)}` },
159
233
  { id: "csp-yield", label: "Yield", value: percent(csp.yieldOnCash),
160
234
  detail: csp.annualizedYield == null ? "expires today" : `${percent(csp.annualizedYield)} a year` },
235
+ ...(quote ? [
236
+ { id: "csp-bid", label: "Bid / ask", value: `${formatHedgePrice(quote.bid)} / ${formatHedgePrice(quote.ask)}`,
237
+ detail: `spread ${percent(quote.spreadOfMid, 1)} of mid` },
238
+ { id: "csp-bid-yield", label: "At the bid", value: quote.bidAnnualizedYield == null ? "--" : percent(quote.bidAnnualizedYield),
239
+ detail: quote.bidAnnualizedYield == null ? "expires today" : "a year" },
240
+ ] : []),
161
241
  { id: "csp-otm", label: "OTM", value: csp.outOfTheMoney == null ? "--" : percent(csp.outOfTheMoney, 1),
162
242
  detail: `vs ${money(csp.spot)} ${csp.spotBasis === "implied by option quotes" ? "implied" : csp.spotBasis}` },
163
243
  { id: "csp-break-even", label: "Break-even", value: formatHedgePrice(csp.breakEven), detail: "per share" },