gloomberb 0.18.3 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (886) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -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" },
@@ -1,5 +1,6 @@
1
1
  import type { HeadlessPaneDefinition } from "../../../types/plugin";
2
2
  import { parsePublicTickerKey } from "../../../utils/exchanges";
3
+ import { DATE_OPTION_EXAMPLES } from "../../../utils/calendar-date";
3
4
  import {
4
5
  expirationOptionSeconds, OPTION_EXPIRATION_FORMAT, OPTION_EXPIRATION_PLACEHOLDER, readOptionExpiration,
5
6
  } from "../../../utils/option-expiry";
@@ -11,8 +12,8 @@ import {
11
12
  import { scenarioFreshness } from "./freshness";
12
13
  import { cashSecuredPut, cspReportEntries, loadNextEarningsDate } from "./csp";
13
14
  import { formatSpotTime } from "../options/model-spot";
14
- import { buildScenario, currencyLabel, parseLegs, scenarioValueUnit } from "./model";
15
- import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget } from "./hedge";
15
+ import { amountUnit, buildScenario, currencyLabel, exportPercent, parseLegs, scenarioValueUnit } from "./model";
16
+ import { hedgeInputsFromSettings, hedgeReportEntries, scenarioHedgeBudget, seededLegQuotes } from "./hedge";
16
17
  import { collarReportEntries, scenarioCollar } from "./collar";
17
18
  import { modelDividendYieldNote } from "../../../market-data/model-dividend-yield";
18
19
  import { listingFateEmpty, loadListingFate } from "../../../tickers/listing-fate";
@@ -32,6 +33,12 @@ const VALUATION_LABELS: Record<string, (currency: string) => string> = {
32
33
  thetaPerDay: (currency) => `Theta (${currency} per day)`, vegaPerPoint: (currency) => `Vega (${currency} per vol point)`,
33
34
  rhoPerPoint: (currency) => `Rho (${currency} per rate point)`,
34
35
  };
36
+ /** The unit an export names beside each Valuation figure: `per` is the currency for the position (`USD per contract`). */
37
+ const VALUATION_UNITS: Record<string, (currency: string, per: string) => string> = {
38
+ price: (_currency, per) => per, pnl: (_currency, per) => per, delta: () => "shares", gamma: () => "shares per 1.00 move",
39
+ thetaPerDay: (currency) => `${currency} per day`, vegaPerPoint: (currency) => `${currency} per vol point`,
40
+ rhoPerPoint: (currency) => `${currency} per rate point`,
41
+ };
35
42
 
36
43
  export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
37
44
  shape: "bundle", argument: { kind: "ticker", description: "Underlying ticker" },
@@ -40,7 +47,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
40
47
  description: "A typed or chain-seeded options position valued across spot, date and volatility: its legs, value and Greeks, expiry risk and a "
41
48
  + "scenario P&L grid. With --nav and --budget-bps, how many contracts a hedge budget buys, the premium spent, the notional protected and "
42
49
  + "coverage of an equity sleeve. --strategy csp sells a cash-secured put: cash secured, premium, yield on cash, annualised yield, "
43
- + "distance out of the money, break-even, days to expiry and whether earnings fall before expiry. --strategy collar buys a put and sells "
50
+ + "the bid, ask and spread with the yield at the bid, distance out of the money, break-even, days to expiry and whether earnings fall "
51
+ + "before expiry; CSP ranks the same put across several names. --strategy collar buys a put and sells "
44
52
  + "a call against the shares they cover: net premium, floor, cap and breakevens of the hedged position.",
45
53
  discovery: { screenshotReadiness: "ready", limitations: ["European scenario values do not model early assignment or exercise."] },
46
54
  options: [
@@ -53,6 +61,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
53
61
  + "csp, one short put sold at its midpoint with the cash to buy the shares set aside (a cash-secured put), "
54
62
  + "or collar, one long put and one short call on the same expiry with the shares they cover" },
55
63
  { key: "strike", type: "string", description: "The --strategy strike: the put, the vertical's long call or the straddle. Nearest the money when omitted" },
64
+ { key: "delta", type: "string", placeholder: "0.30", example: "--strategy csp --delta 0.30 --expiration 2026-11-20",
65
+ description: "The --strategy csp or put put by its delta instead of --strike: the one whose delta is nearest this, as a put's delta reads without its sign" },
56
66
  { key: "putStrike", aliases: ["put-strike"], type: "string",
57
67
  description: "The --strategy collar put bought. The quoted strike nearest 10% below spot when omitted" },
58
68
  { key: "callStrike", aliases: ["call-strike"], type: "string",
@@ -67,8 +77,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
67
77
  { key: "rate", type: "string", description: "Continuously compounded annual risk-free rate, percent" },
68
78
  { key: "dividendYield", aliases: ["dividend-yield"], type: "string", description: "Continuous annual dividend yield, percent" },
69
79
  { key: "currency", type: "string", description: "Currency of the inputs, such as USD; the underlying's when omitted" },
70
- { key: "asOf", aliases: ["as-of"], type: "string", description: "Position valuation timestamp: UTC ISO or YYYY-MM-DD" },
71
- { key: "date", type: "string", description: "Scenario date: UTC ISO or YYYY-MM-DD" },
80
+ { key: "asOf", aliases: ["as-of"], type: "string", description: `Position valuation date, as ${DATE_OPTION_EXAMPLES}, or a UTC ISO timestamp` },
81
+ { key: "date", type: "string", description: `Scenario date, as ${DATE_OPTION_EXAMPLES}, or a UTC ISO timestamp` },
72
82
  { key: "volShift", aliases: ["vol-shift"], type: "string", defaultValue: "0", description: "Parallel volatility shift, percentage points" },
73
83
  { key: "spotRange", aliases: ["spot-range"], type: "string", defaultValue: "30", description: "Spot grid range above and below current spot, percent" },
74
84
  { key: "nav", type: "string", description: "Portfolio NAV for a hedge budget, such as 100m, 2.5bn, 250k, 1,000,000 or $100m. Needs --budget-bps" },
@@ -78,6 +88,11 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
78
88
  ],
79
89
  async load(args, ctx) {
80
90
  const symbol = args.symbols[0]!;
91
+ // Several names to sell puts on are a screen, not one scenario.
92
+ const names = args.rawArgument.split(/[\s,;]+/).filter(Boolean);
93
+ if (names.length > 1 && args.options.strategy === "csp") {
94
+ throw new Error(`OSA values one underlying. To rank cash-secured puts across ${names.join(", ")}: gloom fn CSP ${names.join(" ")}`);
95
+ }
81
96
  const settings: Record<string, unknown> = { ...ctx.settings, ...args.options, symbol };
82
97
  // Bad hedge inputs fail before any market request.
83
98
  hedgeInputsFromSettings(settings);
@@ -118,7 +133,8 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
118
133
  const lastPrint = midVolatility && !supplied(settings.spot) && market.spot != null && money(market.spot) !== money(position!.spot)
119
134
  ? market.spot : null;
120
135
  const spotBasis = position ? scenarioSpotBasis(settings, position, market) : null;
121
- const csp = position && settings.strategy === "csp" ? cashSecuredPut(position, spotBasis!, earningsDate) : null;
136
+ const csp = position && settings.strategy === "csp"
137
+ ? cashSecuredPut(position, spotBasis!, earningsDate, seededLegQuotes(position, market.chain).get(position.legs[0]?.id ?? "")) : null;
122
138
  // A collar seeded, or one typed with the shares it hedges.
123
139
  const collar = position && (settings.strategy === "collar" || position.underlying) ? scenarioCollar(position, spotBasis!) : null;
124
140
  const lastSession = market.underlyingQuote && spotBasis === "implied by option quotes"
@@ -129,6 +145,10 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
129
145
  // Value, P&L, risk and the grid add every contract; entry prices and spot are per share.
130
146
  const valueUnit = position ? scenarioValueUnit(position) : null;
131
147
  const per = `${currency} ${valueUnit}`;
148
+ // What an export names beside each number: the currency (or `currency unknown`) and what it is for.
149
+ const exportCurrency = position ? amountUnit(position.currency) : "";
150
+ const exportPer = `${exportCurrency} ${valueUnit}`;
151
+ const perShare = `${exportCurrency} ${PRICE_UNIT}`;
132
152
  return {
133
153
  freshness: scenarioFreshness(market, suppliedInputs),
134
154
  sections: scenario ? [
@@ -141,16 +161,16 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
141
161
  ], rows: position!.legs.map((leg) => ({ ...leg, expiry: new Date(leg.expiration * 1000).toISOString().slice(0, 10),
142
162
  ...(midVolatility ? { volatilityFrom: leg.volatilitySource === "mid" ? "quote mid" : "provider" } : {}) })) },
143
163
  { title: "Inputs", entries: [
144
- { key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot,
164
+ { key: "spot", label: lastPrint != null ? "Spot implied by option quotes" : "Spot", value: position!.spot, unit: perShare,
145
165
  formatted: `${money(position!.spot)} ${PRICE_UNIT}${lastPrint == null && spotBasis !== "last price" ? ` (${scenarioSpotStamp(spotBasis!, position!.asOf)})` : ""}` },
146
- ...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint,
166
+ ...(lastPrint != null ? [{ key: "last", label: "Last price", value: lastPrint, unit: perShare,
147
167
  formatted: `${money(lastPrint)} ${PRICE_UNIT}${lastSession && lastSession !== "last price" ? ` (${scenarioSpotStamp(lastSession, market.asOf)})` : ""}` }] : []),
148
- ...(position!.underlying ? [{ key: "shares", label: "Shares held", value: position!.underlying.quantity,
168
+ ...(position!.underlying ? [{ key: "shares", label: "Shares held", value: position!.underlying.quantity, unit: "shares",
149
169
  formatted: `${position!.underlying.quantity.toLocaleString("en-US")} at ${money(position!.underlying.price)} ${PRICE_UNIT}` }] : []),
150
170
  { key: "asOf", label: "Valued at", value: new Date(position!.asOf).toISOString(), formatted: formatSpotTime(position!.asOf) },
151
- { key: "rate", label: "Rate", value: position!.rate, formatted: percent(position!.rate) },
171
+ { key: "rate", label: "Rate", value: exportPercent(position!.rate), unit: "%", formatted: percent(position!.rate) },
152
172
  // A yield read as 0 because no dividend was paid, or the dividend stopped, says so; a stated or typed one is the figure alone.
153
- { key: "dividendYield", label: "Dividend yield", value: position!.dividendYield, formatted: percent(position!.dividendYield)
173
+ { key: "dividendYield", label: "Dividend yield", value: exportPercent(position!.dividendYield), unit: "%", formatted: percent(position!.dividendYield)
154
174
  + (dividendNote ? ` (${dividendNote})` : "") },
155
175
  { key: "currency", label: "Currency", value: position!.currency, formatted: currency! },
156
176
  ] },
@@ -158,12 +178,13 @@ export const optionsScenarioHeadless: HeadlessPaneDefinition<"bundle"> = {
158
178
  ...(csp ? [{ title: "Cash-secured put", entries: cspReportEntries(csp) }] : []),
159
179
  ...(collar ? [{ title: "Collar", entries: collarReportEntries(collar) }] : []),
160
180
  { title: `Valuation (${per})`, entries: Object.entries(scenario.valuation).map(([key, value]) => ({ key,
161
- label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value) })) },
181
+ label: VALUATION_LABELS[key]?.(currency!) ?? key, value, formatted: money(value),
182
+ ...(VALUATION_UNITS[key] ? { unit: VALUATION_UNITS[key]!(exportCurrency, exportPer) } : {}) })) },
162
183
  { title: `Expiry risk (${per})`, entries: [
163
- { key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens,
184
+ { key: "breakevens", label: "Breakevens (spot)", value: scenario.expiryRisk.breakevens, unit: perShare,
164
185
  formatted: scenario.expiryRisk.breakevens.map(money).join(", ") || "none" },
165
- { key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
166
- { key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
186
+ { key: "maxProfit", label: "Max profit", value: scenario.expiryRisk.maxProfit, unit: exportPer, formatted: scenario.expiryRisk.unlimitedProfit ? "Unlimited" : money(scenario.expiryRisk.maxProfit) },
187
+ { key: "maxLoss", label: "Max loss", value: scenario.expiryRisk.maxLoss, unit: exportPer, formatted: scenario.expiryRisk.unlimitedLoss ? "Unlimited" : money(scenario.expiryRisk.maxLoss) },
167
188
  ...(scenario.expiryRisk.reason ? [{ key: "limitation", label: "Limitation", value: scenario.expiryRisk.reason }] : []),
168
189
  ] },
169
190
  { title: `Scenario grid (P&L, ${per})`, columns: [