gloomberb 0.16.2 → 0.16.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 (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -12,6 +12,7 @@ import {
12
12
  formatMaybePercent,
13
13
  formatMoneyCompact,
14
14
  formatSignedCompact,
15
+ holderValueCurrency,
15
16
  resolveHolderOwnershipPercent,
16
17
  } from "./format";
17
18
  import { buildRows, sortRows } from "./table-model";
@@ -19,6 +20,8 @@ import type { HolderData } from "../../../types/financials";
19
20
  import type { HolderColumnId } from "./types";
20
21
  import { SEC_FILINGS } from "../shared/report-freshness";
21
22
  import {
23
+ holderShareBasisMarker,
24
+ holderShareBasisNote,
22
25
  holderValueBasis,
23
26
  holdersHeaderLine,
24
27
  moneyColumnHeader,
@@ -28,8 +31,11 @@ import {
28
31
 
29
32
  /** Columns a source fills only when it reports the quarter's change. */
30
33
  const CHANGE_COLUMN_KEYS = new Set(["changeShares", "changePercent"]);
34
+ /** Shown only when some row is held as depositary receipts, as on a home line abroad. */
35
+ const BASIS_COLUMN_KEY = "shareBasis";
31
36
  import type { BeneficialOwnersPayload } from "../../../api-client/beneficial-owners";
32
37
  import { fetchBeneficialOwners, type BeneficialOwnersRequest } from "./beneficial-client";
38
+ import { resolveHeadlessIssuerListing } from "../shared/headless-market-data";
33
39
  import type { BeneficialColumnId } from "./beneficial-model";
34
40
  import {
35
41
  BENEFICIAL_REPORT_COLUMNS,
@@ -60,6 +66,11 @@ const HOLDER_COLUMNS: HeadlessPaneColumn[] = [
60
66
  align: "right",
61
67
  format: (value) => value == null ? "-" : formatCompact(Number(value)),
62
68
  },
69
+ {
70
+ key: BASIS_COLUMN_KEY,
71
+ header: "Basis",
72
+ format: (_value, row) => holderShareBasisMarker(row),
73
+ },
63
74
  {
64
75
  key: "changeShares",
65
76
  header: "Chg",
@@ -126,7 +137,11 @@ export interface HoldersHeadlessDependencies {
126
137
 
127
138
  const defaultDependencies: HoldersHeadlessDependencies = {
128
139
  loadSnapshot: (symbol, _args, ctx) => loadHolderSnapshot(ctx.marketData, symbol),
129
- loadBeneficialOwners: (symbol, request, ctx) => fetchBeneficialOwners(symbol, request, {
140
+ // A listing abroad names its venue and company, so it never reads a US namesake's owners.
141
+ loadBeneficialOwners: async (symbol, request, ctx) => fetchBeneficialOwners(symbol, {
142
+ ...request,
143
+ listing: await resolveHeadlessIssuerListing(ctx, symbol),
144
+ }, {
130
145
  client: ctx.apiClient,
131
146
  signal: ctx.signal,
132
147
  }),
@@ -238,6 +253,8 @@ export function createHoldersHeadless(
238
253
  }
239
254
  const { data, marketCap } = await dependencies.loadSnapshot(symbol, args, ctx);
240
255
  const currency = data.currency ?? "USD";
256
+ // A London line's values can be dollars from the 13F filings; they are labelled and formatted as such.
257
+ const valueCurrency = holderValueCurrency(data, currency)!;
241
258
  const sorted = sortRows(buildRows(data), {
242
259
  columnId: SORT_COLUMNS[String(args.options.sort)] ?? "value",
243
260
  direction: args.options.order === "asc" ? "asc" : "desc",
@@ -253,19 +270,23 @@ export function createHoldersHeadless(
253
270
  changePercent: row.changePercent ?? null,
254
271
  percentHeld: resolveHolderOwnershipPercent(row, marketCap) ?? null,
255
272
  reportDate: row.reportDate ?? null,
256
- currency,
273
+ currency: valueCurrency,
274
+ shareBasis: row.shareBasis ?? null,
257
275
  }));
258
276
  const total = data.summary?.institutionsCount ?? null;
259
277
  const changeReported = rows.some((row) => row.changeShares != null || row.changePercent != null);
260
278
  const reportDate = sharedReportDate(rows);
261
- const valueBasis = holderValueBasis(reportDate);
279
+ const valueBasis = holderValueBasis(reportDate, data.valueBasis, valueCurrency);
262
280
  const positionsBasis = rows.length > 0 ? nonUsHolderCaveat(data.exchange, currency) : null;
281
+ const shareBasisNote = holderShareBasisNote(data, rows);
282
+ const markedRows = rows.some((row) => holderShareBasisMarker(row));
263
283
  // The title names the ticker and the closing line carries the as-of date.
264
284
  const header = holdersHeaderLine({
265
285
  name: data.name,
266
286
  symbol: data.symbol && data.symbol !== symbol ? data.symbol : null,
267
287
  exchange: data.exchange,
268
- currency,
288
+ currency: valueCurrency,
289
+ listingCurrency: data.currency,
269
290
  shown: rows.length,
270
291
  reported: sorted.length,
271
292
  total,
@@ -278,13 +299,18 @@ export function createHoldersHeadless(
278
299
  // A change column that is empty on every row says nothing; the line above explains it once.
279
300
  columns: HOLDER_COLUMNS
280
301
  .filter((column) => changeReported || !CHANGE_COLUMN_KEYS.has(column.key))
281
- .map((column) => column.key === "value" ? { ...column, header: moneyColumnHeader(column.header, currency) } : column),
302
+ .filter((column) => markedRows || column.key !== BASIS_COLUMN_KEY)
303
+ .map((column) => column.key === "value" ? { ...column, header: moneyColumnHeader(column.header, valueCurrency) } : column),
282
304
  rows,
283
305
  metadata: {
284
306
  symbol: data.symbol || symbol,
285
307
  name: data.name ?? null,
286
308
  exchange: data.exchange ?? null,
287
309
  currency,
310
+ valueCurrency,
311
+ ...(data.isDepositaryReceipt != null ? { isDepositaryReceipt: data.isDepositaryReceipt } : {}),
312
+ ...(data.shareBasis ? { shareBasis: data.shareBasis } : {}),
313
+ ...(data.adrRatio != null ? { adrRatio: data.adrRatio } : {}),
288
314
  asOf: data.asOf ?? null,
289
315
  summary: data.summary ?? null,
290
316
  shown: rows.length,
@@ -294,7 +320,7 @@ export function createHoldersHeadless(
294
320
  valueBasis,
295
321
  changeReported,
296
322
  positionsBasis,
297
- notices: [header, basis, positionsBasis].filter((line): line is string => !!line),
323
+ notices: [header, basis, positionsBasis, shareBasisNote].filter((line): line is string => !!line),
298
324
  },
299
325
  };
300
326
  },
@@ -26,8 +26,11 @@ import {
26
26
  formatMaybePercent,
27
27
  formatMoneyCompact,
28
28
  formatSignedCompact,
29
+ holderStakeMarketCap,
30
+ holderValueCurrency,
29
31
  resolveHolderOwnershipPercent,
30
32
  } from "./format";
33
+ import { holderShareBasisMarker } from "./report-header";
31
34
  import {
32
35
  buildColumns,
33
36
  hasHolderChanges,
@@ -40,14 +43,15 @@ import {
40
43
  import { loadHolderData } from "./client";
41
44
  import { HoldersTreemap } from "./treemap";
42
45
  import { BeneficialOwnersView } from "./beneficial-view";
46
+ import { useIssuerListing } from "../shared/ticker-request";
43
47
  import type { HolderColumn, HolderColumnId, HolderRow, SortPreference, ViewMode } from "./types";
44
48
  import { loadHolder13FMatches, type Holder13FMatch } from "./thirteenf-match";
45
49
  import { useSampledValue, useTickerQuoteStream } from "../../../state/hooks/live-ticker-financials";
46
50
 
47
51
  const HOLDER_MARKET_CAP_SAMPLE_MS = 5_000;
48
52
 
49
- /** The "13F" badge: the label and its padding. */
50
- const FUND_BADGE_WIDTH = 5;
53
+ /** The width of a three-letter badge ("13F", "ADR"): the label and its padding. */
54
+ const BADGE_WIDTH = 5;
51
55
 
52
56
  export function HoldersView({ focused, width, height }: { focused: boolean; width: number; height: number }) {
53
57
  const { nativePaneChrome } = useUiCapabilities();
@@ -62,6 +66,7 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
62
66
  const fundMatchAbortRef = useRef<AbortController | null>(null);
63
67
 
64
68
  const exchange = ticker?.metadata.exchange ?? "";
69
+ const issuerListing = useIssuerListing(ticker);
65
70
  const loadHolders = useMemo(() => symbol ? async (forceRefresh: boolean) => {
66
71
  if (!dataProvider) throw new Error("Holder data unavailable");
67
72
  return loadHolderData(dataProvider, symbol, exchange, forceRefresh ? { cacheMode: "refresh" } : undefined);
@@ -70,13 +75,14 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
70
75
  // refresh keeps the last holders; the failure goes to the footer.
71
76
  const { data, loading, error, reload } = useAsyncResource<HolderData>(loadHolders, { keepPreviousData: true });
72
77
 
73
- const currency = data?.currency || ticker?.metadata.currency || "USD";
78
+ // The values' own currency: dollars from the 13F filings on a London line, not its pence.
79
+ const currency = holderValueCurrency(data, ticker?.metadata.currency || "USD")!;
74
80
  // A stake without a reported percentage is the holding's value over the
75
- // market cap. The stream keeps the cap current, about once a second; the
76
- // stakes follow it at most every few seconds so ticks do not re-sort them.
81
+ // market cap, when both are in one currency. The stream keeps the cap
82
+ // current, about once a second; the stakes follow it at most every few
83
+ // seconds so ticks do not re-sort them.
77
84
  useTickerQuoteStream(ticker ? symbol : null, ticker, { surface: "detail", visible: false, weight: 30 });
78
- const quoteMarketCap = financials?.quote?.marketCap;
79
- const liveMarketCap = financials?.quote?.currency && financials.quote.currency !== currency ? undefined : quoteMarketCap;
85
+ const liveMarketCap = holderStakeMarketCap(financials?.quote?.marketCap, financials?.quote?.currency, currency);
80
86
  const marketCap = useSampledValue(liveMarketCap, HOLDER_MARKET_CAP_SAMPLE_MS, `${symbol ?? ""}:${currency}`);
81
87
  const rows = useMemo(() => buildRows(data), [data]);
82
88
  const sortedRows = useMemo(() => sortRows(rows, sortPreference, marketCap), [marketCap, rows, sortPreference]);
@@ -216,17 +222,22 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
216
222
  switch (column.id) {
217
223
  case "holder": {
218
224
  const color = rowState.selected ? colors.selectedText : colors.textBright;
219
- if (!fundMatches.has(row.id)) return { text: row.name, color, attributes: TextAttributes.BOLD };
220
- // A fund with a 13F opens on Enter or o. The name gives way first, so
221
- // the marker survives a narrow column.
225
+ // ADR: a position held as receipts on a home line's list. 13F: a fund
226
+ // whose filing opens on Enter or o.
227
+ const receipts = holderShareBasisMarker(row);
228
+ const fund = fundMatches.has(row.id);
229
+ if (!receipts && !fund) return { text: row.name, color, attributes: TextAttributes.BOLD };
230
+ const badges = (receipts ? 1 : 0) + (fund ? 1 : 0);
231
+ // The name gives way first, so the markers survive a narrow column.
222
232
  return {
223
- text: `${row.name} 13F`,
233
+ text: [row.name, receipts, fund ? "13F" : ""].filter(Boolean).join(" "),
224
234
  content: (
225
235
  <Box flexDirection="row" gap={1} width={column.width} overflow="hidden">
226
236
  <Text fg={color} attributes={TextAttributes.BOLD}>
227
- {clipToDisplayWidth(row.name, Math.max(1, column.width - FUND_BADGE_WIDTH - 1))}
237
+ {clipToDisplayWidth(row.name, Math.max(1, column.width - badges * (BADGE_WIDTH + 1)))}
228
238
  </Text>
229
- <Badge label="13F" tone="accent" />
239
+ {receipts ? <Badge label={receipts} /> : null}
240
+ {fund ? <Badge label="13F" tone="accent" /> : null}
230
241
  </Box>
231
242
  ),
232
243
  };
@@ -328,6 +339,7 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
328
339
  focused={focused}
329
340
  width={width}
330
341
  symbol={symbol ?? null}
342
+ listing={issuerListing}
331
343
  holderRows={rows}
332
344
  fundMatches={fundMatches}
333
345
  onCycleView={toggleView}
@@ -1,4 +1,6 @@
1
+ import type { HolderData } from "../../../types/financials";
1
2
  import { currencyUnitLabel, resolveCurrencyUnit } from "../../../utils/currency-units";
3
+ import { adrRatioText } from "../../../utils/depositary-receipt";
2
4
  import { canonicalExchange, isUsListingExchange, resolveExchangeTimeZone } from "../../../utils/exchanges";
3
5
 
4
6
  /**
@@ -12,7 +14,10 @@ export interface HolderReportFacts {
12
14
  /** Left out when the report title already names it. */
13
15
  symbol?: string | null;
14
16
  exchange?: string | null;
17
+ /** The unit the values are in. */
15
18
  currency?: string | null;
19
+ /** The listing's price currency, when the values are in another one. */
20
+ listingCurrency?: string | null;
16
21
  /** Left out when the report's closing line already carries it. */
17
22
  asOf?: string | null;
18
23
  /** Rows printed. */
@@ -46,10 +51,13 @@ function venueLabel(exchange?: string | null): string {
46
51
 
47
52
  /** What the list is, after the listing is named: its unit, date and how much of it is shown. */
48
53
  export function holderListFacts(
49
- facts: Pick<HolderReportFacts, "currency" | "asOf" | "shown" | "reported" | "total">,
54
+ facts: Pick<HolderReportFacts, "currency" | "listingCurrency" | "asOf" | "shown" | "reported" | "total">,
50
55
  ): string[] {
56
+ const unit = currencyUnitLabel(facts.currency);
57
+ // Dollars on a London line: say they are the values' unit, not the listing's.
58
+ const otherUnit = unit && facts.listingCurrency?.trim() && facts.listingCurrency.trim() !== facts.currency?.trim();
51
59
  return [
52
- currencyUnitLabel(facts.currency),
60
+ otherUnit ? `values in ${unit}` : unit,
53
61
  facts.asOf ? `as of ${facts.asOf}` : "",
54
62
  holderCountText(facts.shown, facts.reported, facts.total),
55
63
  ].filter(Boolean);
@@ -69,9 +77,42 @@ export function sharedReportDate(rows: ReadonlyArray<{ reportDate?: unknown }>):
69
77
  return dates.size === 1 && only ? only : null;
70
78
  }
71
79
 
72
- /** How a holder value was reached: reported shares times a current price, not the filing's own value. */
73
- export function holderValueBasis(reportDate: string | null): string {
74
- return `shares reported${reportDate ? ` at ${reportDate}` : ""} x latest price`;
80
+ /**
81
+ * How a holder value was reached: reported shares times the latest price, or,
82
+ * where Gloom Cloud priced the filings themselves, times the security's price
83
+ * at the report date in the values' currency.
84
+ */
85
+ export function holderValueBasis(
86
+ reportDate: string | null,
87
+ basis?: HolderData["valueBasis"] | null,
88
+ valueCurrency?: string | null,
89
+ ): string {
90
+ const shares = `shares reported${reportDate ? ` at ${reportDate}` : ""}`;
91
+ if (basis === "period_end_price") {
92
+ const unit = valueCurrency?.trim();
93
+ return `${shares} x period-end price${unit ? ` (${unit})` : ""}`;
94
+ }
95
+ return `${shares} x latest price`;
96
+ }
97
+
98
+ /** The short marker a holder row carries when its position is held as depositary receipts. */
99
+ export function holderShareBasisMarker(row: { shareBasis?: unknown }): string {
100
+ return row.shareBasis === "depositary_receipt" ? "ADR" : "";
101
+ }
102
+
103
+ /**
104
+ * What the share counts are, when Gloom Cloud says: which rows of a home
105
+ * line are positions in the receipts, or that a receipt line's rows all are.
106
+ */
107
+ export function holderShareBasisNote(
108
+ data: Pick<HolderData, "shareBasis" | "adrRatio">,
109
+ rows: ReadonlyArray<{ shareBasis?: unknown }>,
110
+ ): string | null {
111
+ const ratio = adrRatioText(data.adrRatio);
112
+ const each = ratio ? ` (${ratio})` : "";
113
+ if (rows.some((row) => holderShareBasisMarker(row))) return `ADR: shares held as depositary receipts${each}.`;
114
+ if (rows.length > 0 && data.shareBasis === "depositary_receipt") return `Shares are depositary receipts${each}.`;
115
+ return null;
75
116
  }
76
117
 
77
118
  /** A listing outside the US, by its venue, or by a currency other than the dollar. */
@@ -22,7 +22,7 @@ import {
22
22
  type StatItem,
23
23
  } from "../../../components";
24
24
  import { useDebouncedPluginPaneState, usePluginPaneState } from "../../runtime";
25
- import { isUsEquityTicker } from "../../../utils/sec";
25
+ import { mayBeUsEquityTicker } from "../../../utils/sec";
26
26
  import { formatCompact } from "../../../utils/format";
27
27
  import { nextHeaderSort } from "../../../utils/sort-values";
28
28
  import { formatShortDate } from "../../../utils/datetime-format";
@@ -130,7 +130,7 @@ function InsiderView({ width, height, focused }: { width: number; height: number
130
130
  [setOpenItemIdState],
131
131
  );
132
132
  const [sort, setSort] = useState<StackSortPreference<InsiderColumnId>>({ columnId: "date", direction: "desc" });
133
- const eligibleTicker = isUsEquityTicker(ticker);
133
+ const eligibleTicker = mayBeUsEquityTicker(ticker);
134
134
  const instrument = instrumentFromTicker(ticker, ticker?.metadata.ticker ?? null);
135
135
 
136
136
  const filingsEntry = useSecFilingsQuery(
@@ -458,7 +458,7 @@ export const insiderModule: PluginModule = {
458
458
  description: "Insider transaction activity for the selected ticker.",
459
459
  keywords: ["insider", "form 4", "ownership", "transactions", "ins"],
460
460
  shortcut: "INS",
461
- canCreate: (_context, options) => !options?.ticker || isUsEquityTicker(options.ticker),
461
+ canCreate: (_context, options) => !options?.ticker || mayBeUsEquityTicker(options.ticker),
462
462
  }),
463
463
  headless: insiderHeadless,
464
464
  },
@@ -471,7 +471,7 @@ export const insiderModule: PluginModule = {
471
471
  order: 47,
472
472
  component: InsiderView,
473
473
  instruments: ["equity"],
474
- isVisible: ({ ticker }) => isUsEquityTicker(ticker),
474
+ isVisible: ({ ticker }) => mayBeUsEquityTicker(ticker),
475
475
  });
476
476
  },
477
477
  };
@@ -3,7 +3,8 @@ import { CompositeChart } from "../../../components";
3
3
  import { resolveChartPalette } from "../../../components/chart/core/palette";
4
4
  import { formatCompositeSeriesValue } from "../../../components/chart/composite/format";
5
5
  import { staticSeries } from "../../../components/chart/static/series";
6
- import { useThemeColors } from "../../../theme/theme-context";
6
+ import { useThemeColors, useThemeId } from "../../../theme/theme-context";
7
+ import { themeSeriesColors } from "../../../theme/series-colors";
7
8
  import type { ResolvedSeries } from "../../../time-series/types";
8
9
  import { formatPoints } from "./format";
9
10
  import type { DatedValue, IvHistoryModel } from "./model";
@@ -11,8 +12,15 @@ import type { DatedValue, IvHistoryModel } from "./model";
11
12
  const PANELS = [{ id: "vol", height: 3 }, { id: "spread", height: 1 }];
12
13
  /** The chart drawing blue: IV90 must read apart from amber IV30 in every theme. */
13
14
  const IV90_COLOR = "#4c9aff";
14
- /** The live capture point must stand apart from the amber IV30 line it continues. */
15
- const LIVE_COLOR = "#c084fc";
15
+ /** The quote capture point must stand apart from the amber IV30 line it continues. */
16
+ const QUOTE_COLOR = "#c084fc";
17
+ /** IV1Y: pink, apart from amber IV30, blue IV90, green HV and the violet quote points. */
18
+ const IV365_COLOR = "#f783ac";
19
+ /**
20
+ * Palette slots for a theme with its own series colours (the colour-blind
21
+ * ones), where the up colour HV takes elsewhere is the same blue as IV90.
22
+ */
23
+ const THEMED_SLOTS = { iv30: 1, iv90: 0, hv: 2, quote: 5, iv365: 4 } as const;
16
24
 
17
25
  function volSeries(id: string, label: string, color: string, points: readonly DatedValue[], panelId: string,
18
26
  style: ResolvedSeries["style"] = "line"): ResolvedSeries {
@@ -27,22 +35,28 @@ function formatLegendValue(value: number, series: ResolvedSeries): string {
27
35
  return series.id === "spread" ? `${formatPoints(value / 100)} pts` : formatCompositeSeriesValue(value, series);
28
36
  }
29
37
 
30
- function ivHistorySeries(model: IvHistoryModel, colors: { iv30: string; iv90: string; hv: string; quote: string; spread: string },
38
+ function ivHistorySeries(model: IvHistoryModel, colors: { iv30: string; iv90: string; iv365: string; hv: string; quote: string; spread: string },
31
39
  hvLabel: string): ResolvedSeries[] {
32
40
  return [
33
41
  volSeries("iv30", "IV 30d", colors.iv30, model.iv30, "vol"),
34
42
  volSeries("iv90", "IV 90d", colors.iv90, model.iv90, "vol"),
43
+ // Drawn only once there is a 1Y close, so an empty legend entry never suggests one.
44
+ ...(model.iv365.length ? [volSeries("iv365", "IV 1Y", colors.iv365, model.iv365, "vol")] : []),
35
45
  volSeries("hv", hvLabel, colors.hv, model.hv, "vol"),
36
- ...(model.quoteIv30.length ? [volSeries("iv30-quote", "IV 30d live", colors.quote, model.quoteIv30, "vol", "points")] : []),
46
+ ...(model.quoteIv30.length ? [volSeries("iv30-quote", "IV 30d quote", colors.quote, model.quoteIv30, "vol", "points")] : []),
37
47
  { ...volSeries("spread", `IV 30d - ${hvLabel}`, colors.spread, model.spread, "spread", "columns"), ...SPREAD_UNIT },
38
48
  ];
39
49
  }
40
50
 
41
51
  export function IvHistoryChart({ model, width, height, hvLabel, focused = false }: { model: IvHistoryModel; width: number; height: number; hvLabel: string; focused?: boolean }) {
42
52
  const colors = useThemeColors();
53
+ const themed = themeSeriesColors(useThemeId());
43
54
  const palette = resolveChartPalette(colors);
44
- const series = useMemo(() => ivHistorySeries(model, { iv30: colors.warning, iv90: IV90_COLOR, hv: colors.positive,
45
- quote: LIVE_COLOR, spread: colors.textDim }, hvLabel), [model, colors, hvLabel]);
55
+ const series = useMemo(() => ivHistorySeries(model, themed
56
+ ? { iv30: themed[THEMED_SLOTS.iv30]!, iv90: themed[THEMED_SLOTS.iv90]!, iv365: themed[THEMED_SLOTS.iv365]!, hv: themed[THEMED_SLOTS.hv]!,
57
+ quote: themed[THEMED_SLOTS.quote]!, spread: colors.textDim }
58
+ : { iv30: colors.warning, iv90: IV90_COLOR, iv365: IV365_COLOR, hv: colors.positive, quote: QUOTE_COLOR, spread: colors.textDim },
59
+ hvLabel), [model, colors, themed, hvLabel]);
46
60
  return <CompositeChart series={series} panels={PANELS} width={width} height={height} focused={focused} navigable={false} showLegend showTimeAxis formatValue={formatLegendValue}
47
61
  remoteKind="implied-volatility-history"
48
62
  colors={{ background: palette.bgColor, grid: palette.gridColor, crosshair: palette.crosshairColor, text: colors.text,
@@ -33,6 +33,10 @@ export interface IvStats {
33
33
  high: number | null;
34
34
  samples: number;
35
35
  windowStart: string | null;
36
+ /** Prior sessions the rank needs: 120, or 60 for IV1Y. Absent from older servers. */
37
+ minSamples?: number;
38
+ /** Why rank or percentile is empty, when one is. */
39
+ rankNote?: string | null;
36
40
  }
37
41
  interface IvReading {
38
42
  date: string;
@@ -51,8 +55,12 @@ export interface IvHistoryPayload {
51
55
  symbol: string;
52
56
  asOf: string;
53
57
  status: IvCoverageStatus;
54
- coverage: { source: "seed" | "demand"; addedAt: string; backfilledThrough: string | null; since: string | null } | null;
55
- stats: { iv30: IvStats | null; iv90: IvStats | null };
58
+ coverage: {
59
+ source: "seed" | "demand"; addedAt: string; backfilledThrough: string | null; since: string | null;
60
+ /** First trade close with a one-year reading; the 1Y series starts later and is sparser. */
61
+ iv365Since?: string | null;
62
+ } | null;
63
+ stats: { iv30: IvStats | null; iv90: IvStats | null; iv365?: IvStats | null };
56
64
  latest: IvReading | null;
57
65
  series: IvPoint[];
58
66
  warnings: string[];
@@ -62,6 +70,8 @@ export interface IvScreenRow {
62
70
  status: "ready" | "queued";
63
71
  iv30: IvStats | null;
64
72
  iv90: IvStats | null;
73
+ iv365?: IvStats | null;
74
+ iv365Since?: string | null;
65
75
  latest: IvReading | null;
66
76
  skew: { date: string; put25: number; call25: number; skew: number } | null;
67
77
  }
@@ -1,4 +1,16 @@
1
- import type { IvStatRow, IvStatUnit, RichCheap } from "./model";
1
+ import type { IvMethod } from "./client";
2
+ import type { IvStatRow, IvStatUnit, RichCheap, RichCheapDates } from "./model";
3
+
4
+ /**
5
+ * VCA's sign conventions, and what VIX is beside an at-the-money IV30, one
6
+ * line each: under the table, in the shot and in the report's notes. The skew
7
+ * is the stored 25-delta put and call IVs at a 30-day constant maturity, from
8
+ * the latest quote capture; both differences are decimal IV shown in points.
9
+ */
10
+ export const VCA_DEFINITIONS = [
11
+ "25D skew: 30-day 25-delta put IV minus 25-delta call IV, in vol points; positive means puts are richer. 30-90: IV30 minus IV90.",
12
+ "VIX is Cboe's 30-day S&P 500 implied vol from the whole SPX strike strip, skew included; IV30 here is at the money, so VIX usually reads higher.",
13
+ ] as const;
2
14
 
3
15
  export const formatVol = (value: number | null | undefined) => value == null || !Number.isFinite(value) ? "--" : `${(value * 100).toFixed(1)}%`;
4
16
  export const formatPoints = (value: number | null | undefined) =>
@@ -13,6 +25,25 @@ export function formatStat(value: number | null | undefined, unit: IvStatUnit):
13
25
  if (value == null || !Number.isFinite(value)) return "--";
14
26
  return unit === "vol" ? formatVol(value) : unit === "points" ? formatPoints(value) : value.toFixed(2);
15
27
  }
28
+ /**
29
+ * How a stored reading was taken. A quote capture is the option quote mids
30
+ * from the minutes before the close, taken once a session and never updated:
31
+ * it is not a live feed, so it is not called live.
32
+ */
33
+ const readingSource = (method: IvMethod | null | undefined) => method === "quote-mid" ? "quote" : "close";
16
34
  export function statSource(row: Pick<IvStatRow, "method">): string {
17
- return row.method === "quote-mid" ? "live" : row.method === "prices" ? "closes" : row.method === "trade-close" ? "close" : "";
35
+ return row.method === "prices" ? "closes" : row.method ? readingSource(row.method) : "";
36
+ }
37
+ /** "2026-10-09 quote": the session a reading belongs to and how it was taken. */
38
+ export const readingLabel = (date: string, method: IvMethod | null | undefined, short = false) =>
39
+ `${short ? shortDate(date) : date} ${readingSource(method)}`;
40
+ /**
41
+ * The dates every row agrees on, said once: the reading's, and the trade close
42
+ * its rank is measured on. `short` is for a footer with little room: month and
43
+ * day, and no window.
44
+ */
45
+ export function sharedDates({ reading, rank }: Pick<RichCheapDates, "reading" | "rank">, short = false): string[] {
46
+ if (reading?.method === "trade-close" && reading.date === rank) return [readingLabel(reading.date, reading.method, short)];
47
+ return [reading && `IV30 ${readingLabel(reading.date, reading.method, short)}`,
48
+ rank && (short ? `IVR/IVP ${shortDate(rank)} close` : `IVR/IVP vs 52 weeks to ${rank} close`)].filter((part): part is string => !!part);
18
49
  }
@@ -2,8 +2,8 @@ import type { HeadlessPaneDefinition } from "../../../types/plugin";
2
2
  import { resolveHeadlessInstrument } from "../shared/headless-market-data";
3
3
  import { createRealizedVolatilityDependencies, loadRealizedVolatilityHistory } from "../realized-vol/client";
4
4
  import { createHvDependencies, loadIvHistory, loadIvScreen, loadRealizedVolatilities } from "./client";
5
- import { formatPoints, formatRank, formatStat, formatVol, verdictLabel } from "./format";
6
- import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, sharedReading, VCA_LIMIT, VCA_PRESETS } from "./model";
5
+ import { formatPoints, formatRank, formatStat, formatVol, readingLabel, sharedDates, VCA_DEFINITIONS, verdictLabel } from "./format";
6
+ import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, type RichCheapRow, richCheapDates, VCA_LIMIT, VCA_PRESETS } from "./model";
7
7
  import { vcaUniverse } from "./universe";
8
8
 
9
9
  const METHODOLOGY = "docs/research-data.md#implied-volatility-history";
@@ -14,7 +14,8 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
14
14
  freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date", oldest: null },
15
15
  describe: (args) => `HIVG ${args.symbols[0] ?? ""}`,
16
16
  discovery: { aliases: ["HIVG"], screenshotReadiness: "live-dom", dataRequirements: ["Cloud stored implied volatility", "Daily price history"],
17
- limitations: ["History starts February 2024 (OPRA daily trade closes)", "Rank and percentile use trade-close readings only"] },
17
+ limitations: ["History starts February 2024 (daily option trade closes)", "Rank and percentile use trade-close readings only",
18
+ "IV1Y needs long-dated trades: it can start later, has gaps, and ranks from 60 prior sessions"] },
18
19
  options: [
19
20
  { key: "lookback", type: "enum", values: [{ value: "1Y" }, { value: "2Y" }, { value: "ALL" }], defaultValue: "1Y", description: "Visible history" },
20
21
  { key: "hvWindow", type: "enum", values: HV_WINDOWS.map((window) => ({ value: String(window) })), defaultValue: "20", description: "Realized volatility window in sessions" },
@@ -28,8 +29,10 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
28
29
  const model = projectIvHistory(payload, prices.history, { lookback: String(args.options.lookback) as IvLookback,
29
30
  hvWindow: (Number(args.options.hvWindow) === 30 ? 30 : 20) as HvWindow });
30
31
  const hvByDay = new Map(model.hv.map((point) => [point.date.getTime(), point.value]));
32
+ const iv365ByDay = new Map(model.iv365.map((point) => [point.date.getTime(), point.value]));
31
33
  const rows = model.iv30.map((point, index) => ({ date: point.date.toISOString().slice(0, 10), iv30: point.value,
32
34
  iv90: model.iv90.find((entry) => entry.date.getTime() === point.date.getTime())?.value ?? null,
35
+ iv365: iv365ByDay.get(point.date.getTime()) ?? null,
33
36
  hv: hvByDay.get(point.date.getTime()) ?? null, spread: model.spread[index]?.value ?? null })).reverse();
34
37
  const errors = [prices.error, ...model.warnings].filter((value): value is string => !!value);
35
38
  return {
@@ -45,11 +48,12 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
45
48
  { key: "samples", header: "Sessions" },
46
49
  ], rows: model.stats.map((row) => ({ ...row })) },
47
50
  { title: "Daily history", columns: [{ key: "date", header: "Session" },
48
- ...["iv30", "iv90", "hv"].map((key) => ({ key, header: key.toUpperCase(), format: percent })),
51
+ ...[["iv30", "IV30"], ["iv90", "IV90"], ["iv365", "IV1Y"], ["hv", "HV"]].map(([key, header]) => ({ key: key!, header: header!, format: percent })),
49
52
  { key: "spread", header: "IV-HV", format: (value: unknown) => typeof value === "number" ? formatPoints(value) : "--" }], rows },
50
53
  ],
51
54
  complete: payload.status === "ready" && errors.length === 0,
52
55
  unavailableSymbols: model.iv30.length ? [] : [instrument.symbol], errors,
56
+ ...(model.oneYearNote ? { notes: [model.oneYearNote] } : {}),
53
57
  metadata: { status: payload.status, coverage: payload.coverage, latest: payload.latest, stats: payload.stats,
54
58
  unit: "decimal annualized volatility", methodology: METHODOLOGY },
55
59
  };
@@ -58,7 +62,8 @@ export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
58
62
 
59
63
  export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
60
64
  shape: "bundle",
61
- freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily" },
65
+ // Dated by the readings themselves; the response's own date is the day it was fetched.
66
+ freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date" },
62
67
  argument: { kind: "symbol-list", optional: true, maximum: VCA_LIMIT, description: "US option underlyings; defaults to index and sector ETFs." },
63
68
  options: [{ key: "preset", type: "enum", values: [{ value: "etfs" }, { value: "megacaps" }], defaultValue: "etfs", description: "Preset when no symbols are given" }],
64
69
  describe: "Volatility rich/cheap",
@@ -73,24 +78,31 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
73
78
  ]);
74
79
  const rows = projectRichCheap(payload.rows, hv).sort((a, b) => (b.percentile ?? -1) - (a.percentile ?? -1));
75
80
  const queued = rows.filter((row) => row.status === "queued").map((row) => row.symbol);
76
- const shared = sharedReading(rows);
81
+ const dates = richCheapDates(rows);
77
82
  const hasSkew = rows.some((row) => row.skew != null);
83
+ const hasIv1y = rows.some((row) => row.iv1y != null), hasIv1yRank = rows.some((row) => row.iv1yPercentile != null);
84
+ const dated = (value: unknown) => typeof value === "string" ? value : "--";
78
85
  return {
79
- sections: [{ title: `Rich/cheap · ${universe.label}${shared ? ` · ${shared.date} ${shared.method === "quote-mid" ? "live" : "close"}` : ""}`, columns: [
86
+ sections: [{ title: ["Rich/cheap", universe.label, ...sharedDates(dates)].join(" · "), columns: [
80
87
  { key: "symbol", header: "Symbol" }, { key: "iv30", header: "IV30", format: percent },
81
- ...(shared ? [] : [{ key: "date", header: "As of" }]),
88
+ ...(dates.reading ? [] : [{ key: "date", header: "As of", format: (value: unknown, row?: unknown) =>
89
+ typeof value === "string" ? readingLabel(value, (row as RichCheapRow).method) : "--" }]),
90
+ ...(dates.rankApart ? [{ key: "rankDate", header: "Rank as of", format: (value: unknown) => typeof value === "string" ? `${value} close` : "--" }] : []),
82
91
  { key: "rank", header: "IVR", format: (value: unknown) => formatRank(value as number | null) },
83
92
  { key: "percentile", header: "IVP", format: (value: unknown) => formatRank(value as number | null) },
84
93
  { key: "verdict", header: "Rich/Cheap", format: (value: unknown) => verdictLabel(value as never) },
94
+ ...(hasIv1y ? [{ key: "iv1y", header: "IV1Y", format: percent }] : []),
95
+ ...(hasIv1yRank ? [{ key: "iv1yPercentile", header: "IVP1Y", format: (value: unknown) => formatRank(value as number | null) }] : []),
85
96
  { key: "termSlope", header: "30-90", format: (value: unknown) => formatPoints(value as number | null) },
86
97
  ...(hasSkew ? [{ key: "skew", header: "25D skew", format: (value: unknown) => formatPoints(value as number | null) }] : []),
98
+ ...(hasSkew && dates.skewApart ? [{ key: "skewDate", header: "Skew as of", format: dated }] : []),
87
99
  { key: "hv", header: "HV20", format: percent },
88
100
  { key: "ivHv", header: "IV/HV", format: (value: unknown) => typeof value === "number" ? value.toFixed(2) : "--" },
89
101
  ], rows: rows.map((row) => ({ ...row })) }],
90
102
  complete: !queued.length && !universe.error, unavailableSymbols: queued,
91
103
  errors: [universe.error].filter((value): value is string => !!value),
92
- ...(queued.length ? { notes: [`Queued for backfill: ${queued.join(", ")}.`] } : {}),
93
- metadata: { asOf: payload.asOf, presets: VCA_PRESETS, methodology: METHODOLOGY },
104
+ notes: [...queued.length ? [`Queued for backfill: ${queued.join(", ")}.`] : [], ...VCA_DEFINITIONS],
105
+ metadata: { presets: VCA_PRESETS, methodology: METHODOLOGY },
94
106
  };
95
107
  },
96
108
  };
@@ -26,12 +26,12 @@ export const ivHistoryModule: PluginModule = {
26
26
  paneTemplates: [
27
27
  { ...createTickerSurfacePaneTemplate({
28
28
  id: "iv-history-pane", paneId: "iv-history", label: "Implied Volatility History",
29
- description: "30 and 90-day ATM implied volatility since 2024 against realized, with IV rank and percentile.",
30
- keywords: ["hivg", "implied", "volatility", "history", "iv rank", "ivr", "percentile", "vrp"], shortcut: "HIVG", publicShare: true,
29
+ description: "30-day, 90-day and one-year ATM implied volatility since 2024 against realized, with IV rank and percentile.",
30
+ keywords: ["hivg", "implied", "volatility", "history", "iv rank", "ivr", "percentile", "vrp", "iv1y", "leaps"], shortcut: "HIVG", publicShare: true,
31
31
  }), headless: ivHistoryHeadless },
32
32
  {
33
33
  id: "iv-screen-pane", paneId: "iv-screen", label: "Volatility Rich/Cheap",
34
- description: "IV rank, percentile, term slope, skew and IV/HV across a list, ranked rich to cheap.",
34
+ description: "IV rank, percentile, one-year IV, term slope, skew and IV/HV across a list, ranked rich to cheap.",
35
35
  keywords: ["vca", "rich", "cheap", "iv rank", "implied volatility", "screen"],
36
36
  shortcut: { prefix: "VCA", argKind: "ticker-list" as const, argOptional: true }, headless: ivScreenHeadless,
37
37
  createInstance: (_context, options) => ({