gloomberb 0.16.4 → 0.16.5

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 (554) hide show
  1. package/README.md +14 -91
  2. package/package.json +4 -1
  3. package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
  4. package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
  5. package/src/account/billing.ts +57 -0
  6. package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
  7. package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
  8. package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
  9. package/src/account/session.ts +16 -0
  10. package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
  11. package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
  12. package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
  13. package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
  14. package/src/api-client/data.ts +24 -2
  15. package/src/api-client/earnings.ts +6 -0
  16. package/src/api-client/futures-curve.ts +56 -0
  17. package/src/api-client/index.ts +4 -1
  18. package/src/api-client/market-discovery.ts +10 -1
  19. package/src/api-client/paths.ts +7 -0
  20. package/src/api-client/research-activity.ts +15 -10
  21. package/src/api-client/types.ts +17 -1
  22. package/src/api-client/yield-curves.ts +8 -0
  23. package/src/app/global-shortcuts.ts +15 -1
  24. package/src/app/pane-runtime/ticker-open-runtime.ts +7 -3
  25. package/src/architecture/core-builtin-allowlist.json +4 -13
  26. package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
  27. package/src/capabilities/chart-series.ts +11 -1
  28. package/src/cli/commands/crypto-hints.ts +7 -0
  29. package/src/cli/commands/fred-series.ts +61 -0
  30. package/src/cli/commands/market.ts +407 -104
  31. package/src/cli/commands/news-hints.ts +62 -0
  32. package/src/cli/commands/news-search.ts +53 -0
  33. package/src/cli/commands/options-leaps.ts +5 -2
  34. package/src/cli/commands/overview.ts +93 -20
  35. package/src/cli/commands/remote.ts +2 -1
  36. package/src/cli/commands/search.ts +82 -9
  37. package/src/cli/commands/system.ts +55 -8
  38. package/src/cli/commands/ticker.ts +88 -11
  39. package/src/cli/context.ts +45 -14
  40. package/src/cli/desktop-pane-shot.ts +59 -7
  41. package/src/cli/earnings-calendar.ts +71 -0
  42. package/src/cli/freshness.ts +18 -7
  43. package/src/cli/glossary.ts +429 -0
  44. package/src/cli/history-rows.ts +48 -7
  45. package/src/cli/index-roots.ts +7 -0
  46. package/src/cli/index.ts +21 -7
  47. package/src/cli/known-gaps.ts +27 -0
  48. package/src/cli/listing-arg.ts +417 -56
  49. package/src/cli/not-a-ticker.ts +12 -5
  50. package/src/cli/pane-functions/access-gate.ts +5 -0
  51. package/src/cli/pane-functions/capabilities.ts +3 -2
  52. package/src/cli/pane-functions/catalog.ts +35 -5
  53. package/src/cli/pane-functions/data.ts +12 -8
  54. package/src/cli/pane-functions/discovery.ts +1 -0
  55. package/src/cli/pane-functions/dom.ts +20 -6
  56. package/src/cli/pane-functions/freshness.ts +363 -43
  57. package/src/cli/pane-functions/function-help.ts +12 -5
  58. package/src/cli/pane-functions/headless.ts +45 -17
  59. package/src/cli/pane-functions/index.ts +63 -10
  60. package/src/cli/pane-functions/market-session.ts +22 -8
  61. package/src/cli/pane-functions/options.ts +63 -22
  62. package/src/cli/pane-functions/report-notices.ts +25 -7
  63. package/src/cli/pane-functions/report.ts +15 -8
  64. package/src/cli/pane-functions/resolver.ts +49 -12
  65. package/src/cli/pane-functions/screenshot.ts +110 -23
  66. package/src/cli/report-tables.ts +3 -0
  67. package/src/cli/result.ts +4 -1
  68. package/src/cli/staged-tickers.ts +52 -0
  69. package/src/cli/symbol-args.ts +9 -0
  70. package/src/cli/symbol-list.ts +172 -0
  71. package/src/cli/ticker-resolution.ts +223 -30
  72. package/src/cli/types.ts +5 -0
  73. package/src/components/chart/composite/axis-overlays.tsx +6 -2
  74. package/src/components/chart/composite/composite-chart.tsx +40 -6
  75. package/src/components/chart/composite/legend.tsx +49 -2
  76. package/src/components/chart/composite/panel-surface.tsx +44 -17
  77. package/src/components/chart/composite/price-axis-labels.tsx +3 -1
  78. package/src/components/command-bar/panel/index.tsx +11 -3
  79. package/src/components/command-bar/panel/runtime.ts +3 -0
  80. package/src/components/command-bar/panel/types.ts +2 -0
  81. package/src/components/command-bar/routes/root/provider-search.ts +4 -3
  82. package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
  83. package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
  84. package/src/components/command-bar/workflow/tickers.ts +23 -4
  85. package/src/components/layout/detached-pane-shell.tsx +9 -0
  86. package/src/components/layout/floating-pane.tsx +3 -0
  87. package/src/components/layout/header.tsx +20 -5
  88. package/src/components/layout/pane/content.tsx +7 -13
  89. package/src/components/layout/pane/footer/index.tsx +6 -2
  90. package/src/components/layout/pane/footer/model.ts +35 -1
  91. package/src/components/layout/pane/header.tsx +24 -1
  92. package/src/components/layout/pane/index.tsx +12 -2
  93. package/src/components/layout/pane/sidebar.tsx +78 -10
  94. package/src/components/layout/shell/active-drag.ts +21 -5
  95. package/src/components/layout/shell/drag/index.ts +39 -9
  96. package/src/components/layout/shell/drag/live.ts +61 -51
  97. package/src/components/layout/shell/drag/runtime.ts +54 -12
  98. package/src/components/layout/shell/index.tsx +25 -7
  99. package/src/components/layout/shell/menu.ts +5 -0
  100. package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
  101. package/src/components/layout/shell/pane/layers.tsx +192 -45
  102. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
  103. package/src/components/markdown-text.tsx +62 -20
  104. package/src/components/metric-treemap/heat-surface.tsx +10 -1
  105. package/src/components/onboarding/account-step/form-panel.tsx +1 -1
  106. package/src/components/onboarding/account-step/index.tsx +1 -1
  107. package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
  108. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  109. package/src/components/onboarding/onboarding-keyboard.ts +1 -1
  110. package/src/components/onboarding/onboarding-wizard.tsx +2 -2
  111. package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
  112. package/src/components/onboarding/wizard-account.ts +2 -2
  113. package/src/components/pane-template-wizard.tsx +9 -1
  114. package/src/components/price-selector-dialog.tsx +11 -2
  115. package/src/components/sign-in-gate.tsx +3 -3
  116. package/src/components/ticker/list-table-view.tsx +24 -1
  117. package/src/components/ui/action-row.tsx +2 -1
  118. package/src/components/ui/button.tsx +7 -1
  119. package/src/components/ui/choice-dialog.tsx +6 -4
  120. package/src/components/ui/confirm-dialog.tsx +28 -18
  121. package/src/components/ui/data-table/index.tsx +17 -6
  122. package/src/components/ui/data-table/opentui/index.tsx +37 -6
  123. package/src/components/ui/data-table/types.ts +12 -0
  124. package/src/components/ui/dialog-actions.tsx +64 -0
  125. package/src/components/ui/display.tsx +17 -3
  126. package/src/components/ui/frame.tsx +35 -2
  127. package/src/components/ui/icon.tsx +2 -0
  128. package/src/components/ui/text-prompt-dialog.tsx +11 -9
  129. package/src/data/cached-query.ts +2 -0
  130. package/src/data/config/store/normalize.ts +7 -2
  131. package/src/data/ticker-repository.ts +5 -0
  132. package/src/data/ticker-store.ts +15 -0
  133. package/src/docs/content.ts +215 -0
  134. package/src/docs/load.ts +139 -0
  135. package/src/docs/requests.ts +39 -0
  136. package/src/docs/snapshot.chunk.ts +8 -0
  137. package/src/docs/snapshot.json +7416 -0
  138. package/src/docs/topics.ts +216 -0
  139. package/src/i18n/es.ts +61 -4
  140. package/src/i18n/ja.ts +61 -4
  141. package/src/i18n/ko.ts +61 -4
  142. package/src/i18n/zh-cn.ts +61 -4
  143. package/src/i18n/zh-tw.ts +61 -4
  144. package/src/layout/desks.ts +1 -1
  145. package/src/layout/pane-manager/dock-tree.ts +18 -0
  146. package/src/layout/pane-manager/index.ts +1 -0
  147. package/src/market-data/coordinator/chart.ts +10 -4
  148. package/src/market-data/coordinator/index.ts +6 -3
  149. package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
  150. package/src/market-data/futures-contract.ts +151 -0
  151. package/src/market-data/hooks.tsx +10 -2
  152. package/src/market-data/market/format.ts +39 -21
  153. package/src/market-data/market/freshness.ts +81 -18
  154. package/src/market-data/market/status.ts +19 -23
  155. package/src/market-data/published-apac-sessions.ts +23 -1
  156. package/src/market-data/quotes/contributions.ts +3 -0
  157. package/src/market-data/quotes/metadata.ts +7 -24
  158. package/src/market-data/quotes/resolution.ts +2 -0
  159. package/src/market-data/quotes/screener-live-quotes.ts +5 -4
  160. package/src/market-data/result-types.ts +2 -0
  161. package/src/plugins/builtin/account-management/model.ts +1 -56
  162. package/src/plugins/builtin/account-management/pane.tsx +6 -4
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
  164. package/src/plugins/builtin/alerts/cli.ts +313 -47
  165. package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
  166. package/src/plugins/builtin/alerts/events.ts +23 -2
  167. package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
  168. package/src/plugins/builtin/alerts/history.ts +2 -0
  169. package/src/plugins/builtin/alerts/index.tsx +41 -9
  170. package/src/plugins/builtin/alerts/pane.tsx +8 -0
  171. package/src/plugins/builtin/alerts/research-builder.ts +8 -3
  172. package/src/plugins/builtin/alerts/research-rules.ts +18 -2
  173. package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
  174. package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
  175. package/src/plugins/builtin/alerts/scheduled.ts +254 -0
  176. package/src/plugins/builtin/alerts/targets.ts +92 -0
  177. package/src/plugins/builtin/analytics/index.tsx +4 -2
  178. package/src/plugins/builtin/analytics/risk-headless.ts +30 -12
  179. package/src/plugins/builtin/analytics/risk-math.ts +91 -27
  180. package/src/plugins/builtin/analytics/risk-model.ts +35 -5
  181. package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
  182. package/src/plugins/builtin/attention/index.tsx +2 -0
  183. package/src/plugins/builtin/attention/pane.tsx +3 -1
  184. package/src/plugins/builtin/attention/series.ts +26 -0
  185. package/src/plugins/builtin/awards/index.tsx +2 -0
  186. package/src/plugins/builtin/awards/pane.tsx +3 -1
  187. package/src/plugins/builtin/awards/series.ts +29 -0
  188. package/src/plugins/builtin/bond-calculator/model.ts +2 -1
  189. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  190. package/src/plugins/builtin/cds/client.ts +70 -13
  191. package/src/plugins/builtin/cds/headless.ts +116 -19
  192. package/src/plugins/builtin/cds/index.tsx +3 -1
  193. package/src/plugins/builtin/cds/model.ts +193 -31
  194. package/src/plugins/builtin/cds/pane.tsx +56 -14
  195. package/src/plugins/builtin/cds/series.ts +19 -0
  196. package/src/plugins/builtin/central-bank-rates/index.tsx +2 -0
  197. package/src/plugins/builtin/central-bank-rates/series.ts +42 -0
  198. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +192 -255
  199. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
  200. package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
  201. package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
  202. package/src/plugins/builtin/chart-composer/headless.ts +8 -3
  203. package/src/plugins/builtin/chart-composer/index.tsx +9 -2
  204. package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
  205. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
  206. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
  207. package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
  208. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
  209. package/src/plugins/builtin/chat/controller/index.ts +2 -0
  210. package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
  211. package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
  212. package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
  213. package/src/plugins/builtin/cloud/askg/model.ts +27 -8
  214. package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
  215. package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
  216. package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
  217. package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
  218. package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
  219. package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
  220. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  221. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
  222. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
  223. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
  224. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
  225. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
  226. package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
  227. package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
  228. package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
  229. package/src/plugins/builtin/company-attention/headless.ts +19 -5
  230. package/src/plugins/builtin/company-attention/index.tsx +2 -0
  231. package/src/plugins/builtin/company-attention/model.ts +26 -7
  232. package/src/plugins/builtin/company-attention/pane.tsx +12 -9
  233. package/src/plugins/builtin/company-attention/series.ts +52 -0
  234. package/src/plugins/builtin/company-kpis/pane.tsx +4 -2
  235. package/src/plugins/builtin/composite-plugins.ts +19 -13
  236. package/src/plugins/builtin/cot/index.tsx +3 -1
  237. package/src/plugins/builtin/cot/model.ts +40 -11
  238. package/src/plugins/builtin/cot/series.ts +27 -0
  239. package/src/plugins/builtin/credit-boards/index.tsx +2 -0
  240. package/src/plugins/builtin/credit-boards/series.ts +66 -0
  241. package/src/plugins/builtin/credit-conditions/index.tsx +2 -0
  242. package/src/plugins/builtin/credit-conditions/series.ts +26 -0
  243. package/src/plugins/builtin/credit-documents/headless.ts +31 -1
  244. package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
  245. package/src/plugins/builtin/crypto-board/headless.ts +2 -3
  246. package/src/plugins/builtin/crypto-board/index.tsx +2 -0
  247. package/src/plugins/builtin/crypto-board/model.ts +12 -0
  248. package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
  249. package/src/plugins/builtin/crypto-board/series.ts +48 -0
  250. package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
  251. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  252. package/src/plugins/builtin/debt-maturities/series.ts +45 -0
  253. package/src/plugins/builtin/dividend-yield/headless.ts +2 -2
  254. package/src/plugins/builtin/dividend-yield/index.tsx +2 -0
  255. package/src/plugins/builtin/dividend-yield/series.ts +20 -0
  256. package/src/plugins/builtin/docs/blocks.tsx +181 -0
  257. package/src/plugins/builtin/docs/index.tsx +68 -0
  258. package/src/plugins/builtin/docs/pane.tsx +426 -0
  259. package/src/plugins/builtin/docs/targets.ts +72 -0
  260. package/src/plugins/builtin/docs/use-docs.ts +60 -0
  261. package/src/plugins/builtin/doe/index.tsx +2 -0
  262. package/src/plugins/builtin/doe/series.ts +62 -0
  263. package/src/plugins/builtin/earnings/client.ts +3 -2
  264. package/src/plugins/builtin/earnings/format.ts +1 -5
  265. package/src/plugins/builtin/earnings/headless.ts +58 -6
  266. package/src/plugins/builtin/earnings/history-model.ts +15 -1
  267. package/src/plugins/builtin/earnings/history-table.ts +13 -2
  268. package/src/plugins/builtin/earnings/history.tsx +7 -3
  269. package/src/plugins/builtin/econ/fred-catalog.ts +189 -0
  270. package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
  271. package/src/plugins/builtin/econ/index.tsx +2 -0
  272. package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
  273. package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
  274. package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
  276. package/src/plugins/builtin/exposure/holdings.ts +1 -1
  277. package/src/plugins/builtin/exposure/pane.tsx +3 -1
  278. package/src/plugins/builtin/fear-greed/index.tsx +4 -0
  279. package/src/plugins/builtin/fear-greed/series.ts +52 -0
  280. package/src/plugins/builtin/futures/index.tsx +43 -3
  281. package/src/plugins/builtin/futures/model.ts +1 -1
  282. package/src/plugins/builtin/futures/returns.ts +1 -1
  283. package/src/plugins/builtin/futures/series.ts +13 -0
  284. package/src/plugins/builtin/futures/table.tsx +1 -1
  285. package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
  286. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  287. package/src/plugins/builtin/futures-curve/headless.ts +70 -9
  288. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  289. package/src/plugins/builtin/futures-curve/model.ts +66 -11
  290. package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
  291. package/src/plugins/builtin/fx-matrix/index.tsx +1 -1
  292. package/src/plugins/builtin/gpu/board.tsx +13 -10
  293. package/src/plugins/builtin/gpu/changes.tsx +4 -4
  294. package/src/plugins/builtin/gpu/client.ts +19 -1
  295. package/src/plugins/builtin/gpu/equities.tsx +14 -10
  296. package/src/plugins/builtin/gpu/headless.ts +56 -25
  297. package/src/plugins/builtin/gpu/history.tsx +3 -3
  298. package/src/plugins/builtin/gpu/index.tsx +2 -0
  299. package/src/plugins/builtin/gpu/model.ts +101 -16
  300. package/src/plugins/builtin/gpu/pane.tsx +28 -12
  301. package/src/plugins/builtin/gpu/series.ts +38 -0
  302. package/src/plugins/builtin/help/function-card.tsx +25 -6
  303. package/src/plugins/builtin/help/index.tsx +17 -8
  304. package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
  305. package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
  306. package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
  307. package/src/plugins/builtin/holders/client.ts +22 -3
  308. package/src/plugins/builtin/holders/format.ts +18 -3
  309. package/src/plugins/builtin/holders/headless.ts +36 -133
  310. package/src/plugins/builtin/holders/pane.tsx +11 -4
  311. package/src/plugins/builtin/holders/report-header.ts +40 -5
  312. package/src/plugins/builtin/holders/report-rows.ts +240 -0
  313. package/src/plugins/builtin/insider/client.ts +35 -20
  314. package/src/plugins/builtin/insider/headless.ts +41 -11
  315. package/src/plugins/builtin/insider/index.tsx +26 -9
  316. package/src/plugins/builtin/insider/insider-data.ts +10 -0
  317. package/src/plugins/builtin/insider/issuer.ts +49 -0
  318. package/src/plugins/builtin/insider/model.ts +3 -0
  319. package/src/plugins/builtin/iv-history/client.ts +14 -4
  320. package/src/plugins/builtin/iv-history/headless.ts +7 -4
  321. package/src/plugins/builtin/iv-history/index.tsx +2 -0
  322. package/src/plugins/builtin/iv-history/model.ts +111 -7
  323. package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
  324. package/src/plugins/builtin/iv-history/series.ts +29 -0
  325. package/src/plugins/builtin/jobs/headless.ts +28 -3
  326. package/src/plugins/builtin/jobs/index.tsx +3 -1
  327. package/src/plugins/builtin/jobs/series.ts +19 -0
  328. package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
  329. package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
  330. package/src/plugins/builtin/macro-day/client.ts +44 -3
  331. package/src/plugins/builtin/macro-day/headless.ts +13 -10
  332. package/src/plugins/builtin/macro-day/model.ts +56 -10
  333. package/src/plugins/builtin/macro-day/pane.tsx +30 -20
  334. package/src/plugins/builtin/market-heatmap/model.ts +19 -11
  335. package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
  336. package/src/plugins/builtin/market-movers/client.ts +7 -5
  337. package/src/plugins/builtin/market-movers/index.tsx +3 -2
  338. package/src/plugins/builtin/market-movers/model.ts +30 -0
  339. package/src/plugins/builtin/market-movers/screener.ts +31 -0
  340. package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
  341. package/src/plugins/builtin/market-valuation/index.tsx +3 -3
  342. package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
  343. package/src/plugins/builtin/members/headless.ts +7 -2
  344. package/src/plugins/builtin/members/model.ts +31 -3
  345. package/src/plugins/builtin/members/pane.tsx +3 -6
  346. package/src/plugins/builtin/mna/headless.ts +15 -3
  347. package/src/plugins/builtin/money-markets/index.tsx +2 -0
  348. package/src/plugins/builtin/money-markets/series.ts +41 -0
  349. package/src/plugins/builtin/notes/cli.ts +12 -7
  350. package/src/plugins/builtin/options/view.tsx +1 -1
  351. package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
  352. package/src/plugins/builtin/options-positioning/client.ts +7 -3
  353. package/src/plugins/builtin/options-positioning/headless.ts +1 -0
  354. package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
  355. package/src/plugins/builtin/options-scenario/client.ts +2 -1
  356. package/src/plugins/builtin/options-scenario/headless.ts +1 -0
  357. package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
  358. package/src/plugins/builtin/perps/client.ts +40 -6
  359. package/src/plugins/builtin/perps/equity-row.tsx +33 -11
  360. package/src/plugins/builtin/perps/freshness.ts +66 -0
  361. package/src/plugins/builtin/perps/headless.ts +45 -30
  362. package/src/plugins/builtin/perps/index.tsx +18 -1
  363. package/src/plugins/builtin/perps/model.ts +20 -5
  364. package/src/plugins/builtin/perps/pane.tsx +76 -20
  365. package/src/plugins/builtin/perps/series.ts +76 -0
  366. package/src/plugins/builtin/plugin-marketplace/pane.tsx +67 -61
  367. package/src/plugins/builtin/plugin-module.ts +3 -0
  368. package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
  369. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +273 -0
  370. package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
  371. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +331 -65
  372. package/src/plugins/builtin/portfolio-list/cli/render.ts +200 -31
  373. package/src/plugins/builtin/portfolio-list/cli/shared.ts +24 -0
  374. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -47
  375. package/src/plugins/builtin/portfolio-list/column-values.ts +1 -1
  376. package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
  377. package/src/plugins/builtin/portfolio-list/headless.ts +34 -4
  378. package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
  379. package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
  380. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -1
  381. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +17 -1
  382. package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
  383. package/src/plugins/builtin/portfolio-list/table.tsx +7 -1
  384. package/src/plugins/builtin/power/client.ts +16 -0
  385. package/src/plugins/builtin/power/evidence.ts +6 -2
  386. package/src/plugins/builtin/power/headless.ts +71 -7
  387. package/src/plugins/builtin/power/index.tsx +4 -1
  388. package/src/plugins/builtin/power/model.ts +72 -1
  389. package/src/plugins/builtin/power/pane.tsx +32 -9
  390. package/src/plugins/builtin/rate-path/headless.ts +11 -3
  391. package/src/plugins/builtin/realized-vol/headless.ts +5 -0
  392. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
  393. package/src/plugins/builtin/research/event-model.ts +68 -24
  394. package/src/plugins/builtin/research/events-headless.ts +2 -1
  395. package/src/plugins/builtin/research/headless.ts +2 -1
  396. package/src/plugins/builtin/seasonality/client.ts +15 -7
  397. package/src/plugins/builtin/seasonality/headless.ts +36 -12
  398. package/src/plugins/builtin/seasonality/index.tsx +2 -2
  399. package/src/plugins/builtin/seasonality/model.ts +119 -0
  400. package/src/plugins/builtin/seasonality/pane.tsx +108 -37
  401. package/src/plugins/builtin/series-inventory.ts +91 -0
  402. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
  403. package/src/plugins/builtin/shared/command-tab.ts +30 -12
  404. package/src/plugins/builtin/shared/earnings-report.ts +55 -0
  405. package/src/plugins/builtin/shared/fred-points.ts +20 -0
  406. package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
  407. package/src/plugins/builtin/shared/open-description.ts +29 -0
  408. package/src/plugins/builtin/shared/report-freshness.ts +9 -0
  409. package/src/plugins/builtin/shared/series-warning.ts +5 -0
  410. package/src/plugins/builtin/short-interest/headless.ts +1 -0
  411. package/src/plugins/builtin/short-interest/index.tsx +2 -0
  412. package/src/plugins/builtin/short-interest/series.ts +36 -0
  413. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  414. package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
  415. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  416. package/src/plugins/builtin/short-volume/index.tsx +2 -0
  417. package/src/plugins/builtin/short-volume/series.ts +23 -0
  418. package/src/plugins/builtin/social-mentions/index.tsx +2 -0
  419. package/src/plugins/builtin/social-mentions/series.ts +23 -0
  420. package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
  421. package/src/plugins/builtin/supply-chain/pane.tsx +3 -1
  422. package/src/plugins/builtin/thirteenf/api.ts +1 -0
  423. package/src/plugins/builtin/thirteenf/data.ts +6 -4
  424. package/src/plugins/builtin/thirteenf/headless.ts +217 -36
  425. package/src/plugins/builtin/thirteenf/model.ts +39 -1
  426. package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
  427. package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
  428. package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
  429. package/src/plugins/builtin/thirteenf/types.ts +2 -0
  430. package/src/plugins/builtin/ticker-detail/index.tsx +3 -0
  431. package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
  432. package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
  433. package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
  434. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
  435. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +48 -4
  436. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
  437. package/src/plugins/builtin/time-sales/headless.ts +1 -0
  438. package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
  439. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  440. package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
  441. package/src/plugins/builtin/vol-surface/headless.ts +1 -0
  442. package/src/plugins/builtin/volatility/index.tsx +2 -0
  443. package/src/plugins/builtin/volatility/series.ts +84 -0
  444. package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
  445. package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
  446. package/src/plugins/builtin/world-venue-map/index.ts +2 -1
  447. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +8 -4
  448. package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
  449. package/src/plugins/builtin/yield-curve/chart.ts +2 -2
  450. package/src/plugins/builtin/yield-curve/curves.ts +21 -8
  451. package/src/plugins/builtin/yield-curve/headless.ts +71 -18
  452. package/src/plugins/builtin/yield-curve/index.tsx +3 -1
  453. package/src/plugins/builtin/yield-curve/series.ts +86 -0
  454. package/src/plugins/catalog-browser.ts +2 -1
  455. package/src/plugins/registry/index.ts +89 -4
  456. package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
  457. package/src/portfolio/position-value.ts +40 -0
  458. package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
  459. package/src/public/time-series.ts +17 -0
  460. package/src/react/auto-refresh.ts +13 -3
  461. package/src/remote/app-host.tsx +20 -4
  462. package/src/remote/confirm-change.tsx +85 -0
  463. package/src/remote/controller-utils.ts +8 -1
  464. package/src/remote/controller.ts +123 -6
  465. package/src/remote/pane-screenshot.ts +47 -0
  466. package/src/remote/schema.ts +62 -0
  467. package/src/remote/types.ts +30 -0
  468. package/src/remote/watchlist.ts +306 -0
  469. package/src/renderers/browser/main.tsx +3 -2
  470. package/src/renderers/cloudflare/worker.ts +1 -1
  471. package/src/renderers/dom/data-table/frozen-column.ts +3 -1
  472. package/src/renderers/dom/data-table/index.tsx +33 -6
  473. package/src/renderers/dom/data-table/row.tsx +27 -1
  474. package/src/renderers/dom/desktop/control-styles.ts +63 -1
  475. package/src/renderers/dom/desktop/controls.tsx +30 -23
  476. package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
  477. package/src/renderers/dom/desktop/icons.tsx +7 -0
  478. package/src/renderers/dom/dom-ui-host.tsx +3 -0
  479. package/src/renderers/dom/host/box.tsx +3 -0
  480. package/src/renderers/dom/host/chart-surface.tsx +6 -2
  481. package/src/renderers/dom/host/live-frame.ts +6 -2
  482. package/src/renderers/dom/host/overflow-fade.ts +14 -1
  483. package/src/renderers/dom/host/size-observer.ts +26 -0
  484. package/src/renderers/dom/host/surface-3d.tsx +7 -2
  485. package/src/renderers/dom/host/text.tsx +1 -1
  486. package/src/renderers/dom/styles.css +61 -0
  487. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +17 -2
  488. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +19 -11
  489. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
  490. package/src/renderers/electrobun/view/main.tsx +2 -0
  491. package/src/series/capability.ts +75 -0
  492. package/src/series/csv.ts +160 -0
  493. package/src/series/files.ts +107 -0
  494. package/src/series/registry.ts +198 -0
  495. package/src/series/resolve.ts +107 -0
  496. package/src/series/standalone.ts +48 -0
  497. package/src/series/types.ts +145 -0
  498. package/src/series/validate.ts +171 -0
  499. package/src/sources/gloomberb-cloud/index.ts +7 -7
  500. package/src/sources/gloomberb-cloud/normalizers.ts +1 -0
  501. package/src/sources/history-result.ts +1 -0
  502. package/src/sources/listing-symbols.ts +4 -1
  503. package/src/sources/provider-router/batches.ts +4 -1
  504. package/src/sources/provider-router/cache.ts +4 -2
  505. package/src/sources/provider-router/cached-routes.ts +1 -1
  506. package/src/sources/provider-router/financial-routes.ts +1 -0
  507. package/src/sources/provider-router/financials.ts +13 -0
  508. package/src/sources/provider-router/history.ts +43 -25
  509. package/src/sources/provider-router/search.ts +1 -0
  510. package/src/sync/core-contributors.ts +1 -0
  511. package/src/tickers/company-name.ts +75 -0
  512. package/src/tickers/instrument-kind.ts +25 -0
  513. package/src/tickers/open-target.ts +3 -2
  514. package/src/tickers/search/index.ts +45 -7
  515. package/src/tickers/search/ranking.ts +33 -17
  516. package/src/tickers/search/types.ts +7 -1
  517. package/src/tickers/us-listing.ts +34 -0
  518. package/src/time-series/hooks.ts +9 -0
  519. package/src/time-series/resolve.ts +95 -16
  520. package/src/time-series/types.ts +4 -2
  521. package/src/time-series/use-chart-resolution.ts +32 -0
  522. package/src/types/config.ts +6 -0
  523. package/src/types/data-provider.ts +5 -0
  524. package/src/types/financials.ts +52 -1
  525. package/src/types/headless.ts +61 -0
  526. package/src/types/plugin.ts +19 -0
  527. package/src/types/price-history.ts +7 -0
  528. package/src/types/ticker.ts +14 -0
  529. package/src/ui/host.tsx +18 -0
  530. package/src/ui/live-resize.ts +81 -0
  531. package/src/ui/live-stretch.ts +121 -0
  532. package/src/ui/measured-cells.ts +58 -0
  533. package/src/ui/pane-fill.ts +11 -0
  534. package/src/utils/basis-points.ts +17 -0
  535. package/src/utils/cli-output.ts +7 -1
  536. package/src/utils/concurrency.ts +25 -0
  537. package/src/utils/crypto-pair.ts +20 -0
  538. package/src/utils/currency-pair.ts +19 -0
  539. package/src/utils/decimal-comma.ts +19 -0
  540. package/src/utils/dom-screenshot-css.ts +265 -0
  541. package/src/utils/dom-screenshot-rasters.ts +276 -0
  542. package/src/utils/dom-screenshot.ts +437 -52
  543. package/src/utils/exchange-rate-snapshot.ts +6 -2
  544. package/src/utils/exchanges.ts +107 -1
  545. package/src/utils/format.ts +16 -3
  546. package/src/utils/fundamentals.ts +66 -0
  547. package/src/utils/futures-generic.ts +6 -12
  548. package/src/utils/fx-market-hours.ts +15 -5
  549. package/src/utils/price-history.ts +99 -42
  550. package/src/utils/sec-eps-basis.ts +24 -11
  551. package/src/utils/sec.ts +61 -7
  552. package/src/utils/utc-time.ts +51 -4
  553. package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
  554. /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
@@ -1,16 +1,19 @@
1
- import type { DataProvider, SecFilingItem } from "../../../types/data-provider";
1
+ import type { DataProvider } from "../../../types/data-provider";
2
2
  import { parseInsiderFiling, type ParsedInsiderFiling } from "./model";
3
3
  import { isInsiderForm } from "./insider-data";
4
+ import { filesAsForeignPrivateIssuer } from "./issuer";
4
5
 
5
- async function loadInsiderFilings(
6
- provider: DataProvider,
7
- symbol: string,
8
- count: number,
9
- exchange = "",
10
- ): Promise<SecFilingItem[]> {
11
- if (!provider.getSecFilings) throw new Error("Insider filing data unavailable");
12
- const filings = await provider.getSecFilings(symbol, count, exchange);
13
- return filings.filter((filing) => isInsiderForm(filing.form));
6
+ // Recent EDGAR dumps cap at 1,000 mixed forms; older archives are read until
7
+ // this many filings or the company's history ends.
8
+ const SEC_FILING_SCAN_LIMIT = 20_000;
9
+
10
+ export interface InsiderFilingsLoad {
11
+ /** The rows of the company's own Form 4 filings, newest first. */
12
+ parsed: ParsedInsiderFiling[];
13
+ /** Every Form 4 listed was read, none is the company's own, and it reports as a foreign private issuer. */
14
+ foreignPrivateIssuer: boolean;
15
+ /** The SEC lists no filing of any form for the company. */
16
+ noFilings: boolean;
14
17
  }
15
18
 
16
19
  function throwIfAborted(signal: AbortSignal | undefined): void {
@@ -18,29 +21,41 @@ function throwIfAborted(signal: AbortSignal | undefined): void {
18
21
  throw signal.reason instanceof Error ? signal.reason : new Error("Insider filing load was aborted.");
19
22
  }
20
23
 
24
+ /**
25
+ * Reads the company's Form 4 filings newest first, one at a time, until
26
+ * `limit` of them are its own. A form it filed as an owner of another
27
+ * company's shares is read and left out, so a list of only those is read to
28
+ * the end before it can say there are none.
29
+ */
21
30
  export async function loadParsedInsiderFilings(
22
31
  provider: DataProvider,
23
32
  symbol: string,
24
33
  options: { limit: number; exchange?: string; signal?: AbortSignal },
25
- ): Promise<ParsedInsiderFiling[]> {
34
+ ): Promise<InsiderFilingsLoad> {
35
+ if (!provider.getSecFilings) throw new Error("Insider filing data unavailable");
26
36
  if (!provider.getSecFilingContent) throw new Error("Insider filing content unavailable");
27
- const filings = (await loadInsiderFilings(
28
- provider,
29
- symbol,
30
- 20_000,
31
- options.exchange,
32
- )).slice(0, options.limit);
37
+ const filings = await provider.getSecFilings(symbol, SEC_FILING_SCAN_LIMIT, options.exchange ?? "");
33
38
 
34
39
  const parsed: ParsedInsiderFiling[] = [];
40
+ let own = 0;
35
41
  for (const filing of filings) {
42
+ if (own >= options.limit) break;
43
+ if (!isInsiderForm(filing.form)) continue;
36
44
  throwIfAborted(options.signal);
45
+ let content: string | null;
37
46
  try {
38
- const content = await provider.getSecFilingContent(filing);
39
- parsed.push(...parseInsiderFiling(filing, content));
47
+ content = await provider.getSecFilingContent(filing);
40
48
  } catch {
41
49
  throwIfAborted(options.signal);
50
+ // An unread filing may be the company's own; it stays as a row that says so.
42
51
  parsed.push({ filing, transaction: null, isLoading: false });
52
+ own += 1;
53
+ continue;
43
54
  }
55
+ const entries = parseInsiderFiling(filing, content);
56
+ if (entries.length === 0) continue;
57
+ parsed.push(...entries);
58
+ own += 1;
44
59
  }
45
- return parsed;
60
+ return { parsed, foreignPrivateIssuer: own === 0 && filesAsForeignPrivateIssuer(filings), noFilings: filings.length === 0 };
46
61
  }
@@ -3,9 +3,11 @@ import type {
3
3
  HeadlessPaneContext,
4
4
  HeadlessPaneDefinition,
5
5
  HeadlessPaneLoadArgs,
6
+ HeadlessPaneRow,
6
7
  } from "../../../types/plugin";
7
8
  import { formatCompact, formatCurrency } from "../../../utils/format";
8
- import { loadParsedInsiderFilings } from "./client";
9
+ import { loadParsedInsiderFilings, type InsiderFilingsLoad } from "./client";
10
+ import { insiderEmptyNotice } from "./issuer";
9
11
  import {
10
12
  buildInsiderRows,
11
13
  buildInsiderSummary,
@@ -15,9 +17,12 @@ import {
15
17
  matchesInsiderTypeFilter,
16
18
  type ParsedInsiderFiling,
17
19
  } from "./model";
18
- import { relevantInsiderAmendments } from "./amendments";
20
+ import { isAmendedInsiderFiling, relevantInsiderAmendments } from "./amendments";
19
21
  import { formatInsiderName } from "./display";
20
22
  import { SEC_FILINGS } from "../shared/report-freshness";
23
+ import { resolveHeadlessIssuerListing } from "../shared/headless-market-data";
24
+ import { noSecReportsNotice } from "../../../utils/sec";
25
+ import { parsePublicTickerKey } from "../../../utils/exchanges";
21
26
 
22
27
  const INSIDER_COLUMNS: HeadlessPaneColumn[] = [
23
28
  { key: "form", header: "Form" },
@@ -62,15 +67,23 @@ const INSIDER_COLUMNS: HeadlessPaneColumn[] = [
62
67
  align: "right",
63
68
  format: (value) => value == null ? "-" : `$${formatCompact(Number(value))}`,
64
69
  },
70
+ // An amendment's original date, remarks and footnotes, under the amendment's own row.
71
+ { key: "note", header: "Note", underRow: true, format: (value) => typeof value === "string" ? value : "" },
65
72
  ];
66
73
 
74
+ /** What a Form 4/A says about itself, for its last row in the report. */
75
+ function amendmentNote(entry: ParsedInsiderFiling): string {
76
+ const original = entry.disclosure?.originalFilingDate;
77
+ return [original ? `Original filed ${original}` : "", ...buildInsiderDisclosureText(entry).split("\n")].filter(Boolean).join("\n");
78
+ }
79
+
67
80
  export interface InsiderHeadlessDependencies {
68
81
  loadParsed(
69
82
  symbol: string,
70
83
  limit: number,
71
84
  args: HeadlessPaneLoadArgs,
72
85
  ctx: HeadlessPaneContext,
73
- ): Promise<ParsedInsiderFiling[]>;
86
+ ): Promise<InsiderFilingsLoad>;
74
87
  }
75
88
 
76
89
  const defaultDependencies: InsiderHeadlessDependencies = {
@@ -80,6 +93,17 @@ const defaultDependencies: InsiderHeadlessDependencies = {
80
93
  }),
81
94
  };
82
95
 
96
+ /**
97
+ * "CAR Group (ASX) files no SEC reports." for a listing abroad the SEC lists
98
+ * nothing for, as `filings` says it; null for a US listing or a symbol with no
99
+ * venue, whose empty list is only no Form 4.
100
+ */
101
+ async function noSecReportsEmpty(ctx: HeadlessPaneContext, key: string): Promise<string | null> {
102
+ const listing = await resolveHeadlessIssuerListing(ctx, key);
103
+ if (!listing.exchange) return null;
104
+ return noSecReportsNotice({ symbol: parsePublicTickerKey(key).symbol, exchange: listing.exchange, name: listing.name ?? null });
105
+ }
106
+
83
107
  export function createInsiderHeadless(
84
108
  dependencies: InsiderHeadlessDependencies = defaultDependencies,
85
109
  ): HeadlessPaneDefinition<"rows"> {
@@ -113,7 +137,7 @@ export function createInsiderHeadless(
113
137
  {
114
138
  key: "limit",
115
139
  aliases: ["count", "rows"],
116
- description: "Maximum Form 4 and Form 4/A filings to parse.",
140
+ description: "Maximum of the company's own Form 4 and Form 4/A filings to parse.",
117
141
  type: "integer",
118
142
  defaultValue: 20,
119
143
  minimum: 1,
@@ -125,7 +149,9 @@ export function createInsiderHeadless(
125
149
  async load(args, ctx) {
126
150
  const symbol = args.symbols[0]!;
127
151
  const limit = Number(args.options.limit);
128
- const parsed = await dependencies.loadParsed(symbol, limit, args, ctx);
152
+ const { parsed, foreignPrivateIssuer, noFilings } = await dependencies.loadParsed(symbol, limit, args, ctx);
153
+ const empty = parsed.length > 0 ? null
154
+ : (noFilings ? await noSecReportsEmpty(ctx, symbol) : null) ?? insiderEmptyNotice(foreignPrivateIssuer);
129
155
  const name = String(args.options.name ?? "").trim().toLocaleLowerCase();
130
156
  const type = isInsiderTypeFilter(args.options.type) ? args.options.type : "all";
131
157
  const owned = name
@@ -136,8 +162,16 @@ export function createInsiderHeadless(
136
162
  const incomplete = [...new Set(buildInsiderRows(parsed)
137
163
  .filter((row) => row.status !== "parsed" && row.status !== "disclosure")
138
164
  .map((row) => row.accessionNumber))];
165
+ // A filing's lines sit together, so its note goes once, under the last of them.
166
+ const rows: HeadlessPaneRow[] = buildInsiderRows(filtered, parsed).map((row, index) => {
167
+ const entry = filtered[index]!;
168
+ const last = filtered[index + 1]?.filing.accessionNumber !== entry.filing.accessionNumber;
169
+ return isAmendedInsiderFiling(entry) && last ? { ...row, note: amendmentNote(entry) || null } : row;
170
+ });
139
171
  return {
140
- rows: buildInsiderRows(filtered, parsed),
172
+ rows,
173
+ // Nothing at all: the company has no Form 4 of its own, which is the whole answer.
174
+ ...(empty ? { empty, unavailableSymbols: [] } : {}),
141
175
  errors: [
142
176
  ...incomplete.map((accessionNumber) => `${accessionNumber}: Form 4 transactions are unavailable or incomplete.`),
143
177
  ...amendments.map((scope) => `${scope.accessionNumber}: Form 4/A is unreconciled; affected transaction totals are unavailable.`),
@@ -146,11 +180,7 @@ export function createInsiderHeadless(
146
180
  symbol,
147
181
  summary: buildInsiderSummary(owned, Date.now(), parsed),
148
182
  amendments,
149
- notices: amendments.map((scope) => {
150
- const entry = parsed.find(({ filing }) => filing.accessionNumber === scope.accessionNumber)!;
151
- return [`Form 4/A ${scope.accessionNumber}${scope.originalFilingDate ? ` | Original filed ${scope.originalFilingDate}` : ""}`,
152
- buildInsiderDisclosureText(entry)].filter(Boolean).join("\n");
153
- }),
183
+ foreignPrivateIssuer,
154
184
  parsed: new Set(parsed.filter(({ transaction }) => transaction != null).map(({ filing }) => filing.accessionNumber)).size,
155
185
  transactions: filtered.filter(({ transaction }) => transaction != null).length,
156
186
  requested: limit,
@@ -22,7 +22,8 @@ import {
22
22
  type StatItem,
23
23
  } from "../../../components";
24
24
  import { useDebouncedPluginPaneState, usePluginPaneState } from "../../runtime";
25
- import { mayBeUsEquityTicker } from "../../../utils/sec";
25
+ import { mayBeSecEquityListing, mayBeUsEquityTicker, noSecReportsNotice, nonUsSecListingVenue } from "../../../utils/sec";
26
+ import { parsePublicTickerKey } from "../../../utils/exchanges";
26
27
  import { formatCompact } from "../../../utils/format";
27
28
  import { nextHeaderSort } from "../../../utils/sort-values";
28
29
  import { formatShortDate } from "../../../utils/datetime-format";
@@ -48,6 +49,7 @@ import {
48
49
  import { formatInsiderName } from "./display";
49
50
  import { insiderHeadless } from "./headless";
50
51
  import { isInsiderForm } from "./insider-data";
52
+ import { filesAsForeignPrivateIssuer, insiderEmptyNotice, isOtherIssuerFiling } from "./issuer";
51
53
  import { relevantInsiderAmendments } from "./amendments";
52
54
  import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
53
55
  import {
@@ -130,7 +132,9 @@ function InsiderView({ width, height, focused }: { width: number; height: number
130
132
  [setOpenItemIdState],
131
133
  );
132
134
  const [sort, setSort] = useState<StackSortPreference<InsiderColumnId>>({ columnId: "date", direction: "desc" });
133
- const eligibleTicker = mayBeUsEquityTicker(ticker);
135
+ const eligibleTicker = mayBeSecEquityListing(ticker);
136
+ // A listing abroad is looked up by its company; an empty list means it files nothing with the SEC.
137
+ const abroadVenue = ticker ? nonUsSecListingVenue(ticker.metadata.ticker, ticker.metadata.exchange) : null;
134
138
  const instrument = instrumentFromTicker(ticker, ticker?.metadata.ticker ?? null);
135
139
 
136
140
  const filingsEntry = useSecFilingsQuery(
@@ -185,6 +189,17 @@ function InsiderView({ width, height, focused }: { width: number; height: number
185
189
  scopeKey: `${ticker?.metadata.ticker ?? "none"}:${ticker?.metadata.exchange ?? ""}`,
186
190
  targets: visibleForm4Filings,
187
191
  });
192
+ // The list also holds the Form 4s the company filed as an owner of another
193
+ // company's shares, which only their documents tell apart. Until one of its
194
+ // own is read, the pages are read on to the end, so a list of none says so.
195
+ const ownerFilingsShown = useMemo(() => visibleForm4Filings.some((filing) => (
196
+ isOtherIssuerFiling(filing, contentMap.get(filing.accessionNumber))
197
+ )), [contentMap, visibleForm4Filings]);
198
+ const ownFilingShown = useMemo(() => visibleForm4Filings.some((filing) => (
199
+ contentMap.has(filing.accessionNumber) && !isOtherIssuerFiling(filing, contentMap.get(filing.accessionNumber))
200
+ )), [contentMap, visibleForm4Filings]);
201
+ const searchingOwn = !ownFilingShown && pendingCount === 0 && shownCount < form4Filings.length;
202
+ const noOwnFilings = !loading && !ownFilingShown && pendingCount === 0 && shownCount >= form4Filings.length;
188
203
 
189
204
  const allParsed: ParsedFiling[] = useMemo(() => visibleForm4Filings.flatMap((filing) => {
190
205
  const hasContent = contentMap.has(filing.accessionNumber);
@@ -223,13 +238,13 @@ function InsiderView({ width, height, focused }: { width: number; height: number
223
238
  // load here until it fills a page, back to a year at most.
224
239
  const oldestShown = visibleForm4Filings[visibleForm4Filings.length - 1]?.filingDate;
225
240
  const oldestShownTime = oldestShown ? new Date(oldestShown).getTime() : Number.NaN;
226
- const wantsOlderFilings = (typeFilter !== "all" || nameFilter != null)
241
+ const wantsOlderFilings = (typeFilter !== "all" || nameFilter != null || ownerFilingsShown)
227
242
  && shown.length < Math.max(FORM4_PAGE_SIZE, height)
228
243
  && shownCount < Math.min(form4Filings.length, AUTO_FILL_FILING_CAP)
229
244
  && oldestShownTime >= Date.now() - AUTO_FILL_DAYS * DAY_MS;
230
245
  useEffect(() => {
231
- if (wantsOlderFilings && pendingCount === 0) loadNextPage();
232
- }, [loadNextPage, pendingCount, shownCount, wantsOlderFilings]);
246
+ if (searchingOwn || (wantsOlderFilings && pendingCount === 0)) loadNextPage();
247
+ }, [loadNextPage, pendingCount, searchingOwn, shownCount, wantsOlderFilings]);
233
248
 
234
249
  const summary = useMemo(() => buildInsiderSummaryFigures(parsed, Date.now(), allParsed), [parsed, allParsed]);
235
250
  // A page loading in on scroll clears the totals until it is read; the last
@@ -353,15 +368,17 @@ function InsiderView({ width, height, focused }: { width: number; height: number
353
368
  if (authWall) return <SignInWall placement="insider-signin" action="view insider transactions" needsVerification={cloudSession.needsVerification} />;
354
369
  if (loading && allFilings.length === 0) return <Spinner label="Loading insider filings..." />;
355
370
  if (error && allFilings.length === 0) return <EmptyState title="Insider filings unavailable." message={error} />;
356
- if (!loading && form4Filings.length === 0) {
357
- return renderFilingNotice(`No Form 4 filings found for ${ticker.metadata.ticker}.`, width);
371
+ if (noOwnFilings) {
372
+ return renderFilingNotice(abroadVenue && allFilings.length === 0
373
+ ? noSecReportsNotice({ symbol: parsePublicTickerKey(ticker.metadata.ticker).symbol, exchange: abroadVenue, name: ticker.metadata.name ?? null })
374
+ : insiderEmptyNotice(filesAsForeignPrivateIssuer(allFilings)), width);
358
375
  }
359
376
 
360
377
  // Until a filing is read there is nothing to say; after that the empty
361
378
  // list says how far back the filings it has read go, while older ones load.
362
379
  const oldestRead = allParsed.findLast((entry) => !entry.isLoading)?.filing.filingDate;
363
380
  const since = oldestRead ? ` since ${formatShortDate(oldestRead, { utc: true })}` : "";
364
- const emptyTitle = typeCounts.all === 0 && (pendingCount > 0 || wantsOlderFilings)
381
+ const emptyTitle = typeCounts.all === 0 && (pendingCount > 0 || wantsOlderFilings || searchingOwn)
365
382
  ? "Loading Form 4 transactions..."
366
383
  : typeFilter === "trades"
367
384
  ? `No open-market buys or sells${nameFilter ? " by this insider" : ""}${since}.`
@@ -458,7 +475,7 @@ export const insiderModule: PluginModule = {
458
475
  description: "Insider transaction activity for the selected ticker.",
459
476
  keywords: ["insider", "form 4", "ownership", "transactions", "ins"],
460
477
  shortcut: "INS",
461
- canCreate: (_context, options) => !options?.ticker || mayBeUsEquityTicker(options.ticker),
478
+ canCreate: (_context, options) => !options?.ticker || mayBeSecEquityListing(options.ticker),
462
479
  }),
463
480
  headless: insiderHeadless,
464
481
  },
@@ -104,6 +104,16 @@ function disclosureText(xml: string): string {
104
104
  return decodeHtmlEntities(xml.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim());
105
105
  }
106
106
 
107
+ /**
108
+ * The CIK of the company whose shares the form reports, without leading
109
+ * zeros, or null when the document is not a Form 4 or names none.
110
+ */
111
+ export function parseForm4IssuerCik(xml: string): string | null {
112
+ if (!/<(?:[\w.-]+:)?ownershipDocument(?:\s|>)/i.test(xml)) return null;
113
+ const cik = tagText(tagContent(xml, "issuer") ?? "", "issuerCik").replace(/^0+/, "");
114
+ return /^\d+$/.test(cik) ? cik : null;
115
+ }
116
+
107
117
  export function parseForm4Disclosure(xml: string): InsiderFilingDisclosure | null {
108
118
  if (!/<(?:[\w.-]+:)?ownershipDocument(?:\s|>)/i.test(xml)) return null;
109
119
  const original = tagText(xml, "dateOfOriginalSubmission");
@@ -0,0 +1,49 @@
1
+ import type { SecFilingItem } from "../../../types/data-provider";
2
+ import { parseForm4IssuerCik } from "./insider-data";
3
+
4
+ /**
5
+ * A company's SEC filing list holds every Form 4 it is named on: its insiders'
6
+ * trades in its shares, and the ones it filed itself as an owner of another
7
+ * company's shares (Banco Santander buying Santander Consumer, Sanofi
8
+ * converting Regeneron notes). Only the form's issuer tells them apart, so a
9
+ * filing is the company's own until its document names another issuer.
10
+ */
11
+ export function isOtherIssuerFiling(filing: SecFilingItem, content: string | null | undefined): boolean {
12
+ if (!content) return false;
13
+ const issuer = parseForm4IssuerCik(content);
14
+ const company = filing.cik.trim().replace(/^0+/, "");
15
+ return issuer != null && /^\d+$/.test(company) && issuer !== company;
16
+ }
17
+
18
+ const FOREIGN_PRIVATE_ISSUER_FORM = /^(?:20-F|40-F|6-K)(?:\/A)?$/i;
19
+ const DOMESTIC_REPORT_FORM = /^(?:10-KT?|10-QT?|8-K)(?:\/A)?$/i;
20
+
21
+ function newestFiled(filings: readonly SecFilingItem[], form: RegExp): number {
22
+ let newest = Number.NEGATIVE_INFINITY;
23
+ for (const filing of filings) {
24
+ if (!form.test(filing.form.trim())) continue;
25
+ const time = new Date(filing.filingDate).getTime();
26
+ if (Number.isFinite(time) && time > newest) newest = time;
27
+ }
28
+ return newest;
29
+ }
30
+
31
+ /**
32
+ * The company reports as a foreign private issuer: its newest report is a
33
+ * 20-F, 40-F or 6-K rather than a 10-K, 10-Q or 8-K. Such an issuer's insiders
34
+ * file no Form 4, so its list holds only the forms it filed as an owner.
35
+ */
36
+ export function filesAsForeignPrivateIssuer(filings: readonly SecFilingItem[]): boolean {
37
+ const foreign = newestFiled(filings, FOREIGN_PRIVATE_ISSUER_FORM);
38
+ return Number.isFinite(foreign) && foreign > newestFiled(filings, DOMESTIC_REPORT_FORM);
39
+ }
40
+
41
+ /**
42
+ * What the pane, `fn INS` and `insider` say when a company has no Form 4 of
43
+ * its own. Each already names the company above it.
44
+ */
45
+ export function insiderEmptyNotice(foreignPrivateIssuer: boolean): string {
46
+ return foreignPrivateIssuer
47
+ ? "No Form 4 insiders: foreign private issuers file none."
48
+ : "No Form 4 insider filings.";
49
+ }
@@ -3,6 +3,7 @@ import { formatCompact, formatCurrency } from "../../../utils/format";
3
3
  import { parseForm4Xml, parseForm4Disclosure, transactionTypeLabel, type InsiderTransaction, type InsiderFilingDisclosure, type InsiderReportingOwner } from "./insider-data";
4
4
  import { affectingInsiderAmendments, buildInsiderAmendmentScopes, isAmendedInsiderFiling } from "./amendments";
5
5
  import { formatInsiderName, isOpenMarketTrade, shortInsiderRole } from "./display";
6
+ import { isOtherIssuerFiling } from "./issuer";
6
7
 
7
8
  const NINETY_DAYS_MS = 90 * 24 * 60 * 60 * 1000;
8
9
 
@@ -13,7 +14,9 @@ export interface ParsedInsiderFiling {
13
14
  disclosure?: InsiderFilingDisclosure | null;
14
15
  }
15
16
 
17
+ /** The rows of one filing; none for a Form 4 the company filed as an owner of another issuer. */
16
18
  export function parseInsiderFiling(filing: SecFilingItem, content: string | null, isLoading = false): ParsedInsiderFiling[] {
19
+ if (isOtherIssuerFiling(filing, content)) return [];
17
20
  const transactions = content ? parseForm4Xml(content) : [];
18
21
  const disclosure = content ? parseForm4Disclosure(content) : null;
19
22
  return transactions.length
@@ -1,5 +1,5 @@
1
1
  import { apiClient } from "../../../api-client";
2
- import { parsePublicTickerKey } from "../../../utils/exchanges";
2
+ import { listingAbroadVenue, usListingSymbol } from "../../../utils/sec";
3
3
  import { getSharedMarketDataCoordinator, MarketDataCoordinator, resolveEntryValue } from "../../../market-data/coordinator";
4
4
  import type { ChartRequest, InstrumentRef } from "../../../market-data/request-types";
5
5
  import type { QueryEntry } from "../../../market-data/result-types";
@@ -8,7 +8,7 @@ import type { PricePoint } from "../../../types/financials";
8
8
  import { realizedVolatility, realizedVolatilityCadenceIssue } from "../shared/volatility";
9
9
 
10
10
  export type IvMethod = "quote-mid" | "trade-close";
11
- type IvCoverageStatus = "ready" | "backfilling" | "queued" | "unavailable";
11
+ export type IvCoverageStatus = "ready" | "backfilling" | "queued" | "unavailable";
12
12
  export interface IvPoint {
13
13
  sessionDate: string;
14
14
  method: IvMethod;
@@ -67,7 +67,14 @@ export interface IvHistoryPayload {
67
67
  }
68
68
  export interface IvScreenRow {
69
69
  symbol: string;
70
+ /** Has readings, or none yet. All an older server says; `coverageStatus` is exact. */
70
71
  status: "ready" | "queued";
72
+ /** Where the symbol's history stands, as in `IvHistoryPayload.status`. Absent from older servers. */
73
+ coverageStatus?: IvCoverageStatus;
74
+ /** When the symbol was queued and how far its backfill has got; null when it is not covered. */
75
+ coverage?: { source: "seed" | "demand"; addedAt: string; backfilledThrough: string | null } | null;
76
+ /** Why it is not covered yet, such as demand coverage being full. */
77
+ warnings?: string[];
71
78
  iv30: IvStats | null;
72
79
  iv90: IvStats | null;
73
80
  iv365?: IvStats | null;
@@ -88,14 +95,17 @@ export interface StoredSurfacePayload {
88
95
  export type ImpliedVolatilityApi = Pick<typeof apiClient, "impliedVolatility">;
89
96
  const query = (params: Record<string, string>) => new URLSearchParams(params).toString();
90
97
  /** Stored IV is keyed by the bare US symbol; a listing key ("SPY:ARCX") drops its exchange. */
91
- export const ivSymbol = (symbol: string) => parsePublicTickerKey(symbol.trim()).symbol.toUpperCase();
98
+ /** A US listing's symbol; a listing abroad is refused, since its bare symbol's options are another company's. */
99
+ export const ivSymbol = (symbol: string) => usListingSymbol(symbol, "Implied volatility");
92
100
 
93
101
  export function loadIvHistory(symbol: string, options: { signal?: AbortSignal; days?: number } = {}, api: ImpliedVolatilityApi = apiClient) {
94
102
  return api.impliedVolatility<IvHistoryPayload>(`history?${query({ symbol: ivSymbol(symbol), days: String(options.days ?? 1100) })}`,
95
103
  { signal: options.signal });
96
104
  }
105
+ /** A listing abroad has no row: its bare symbol's options are another company's. */
97
106
  export function loadIvScreen(symbols: readonly string[], options: { signal?: AbortSignal } = {}, api: ImpliedVolatilityApi = apiClient) {
98
- return api.impliedVolatility<IvScreenPayload>(`screen?${query({ symbols: symbols.map(ivSymbol).join(",") })}`, { signal: options.signal });
107
+ const us = symbols.filter((symbol) => listingAbroadVenue(symbol) == null).map(ivSymbol);
108
+ return api.impliedVolatility<IvScreenPayload>(`screen?${query({ symbols: us.join(",") })}`, { signal: options.signal });
99
109
  }
100
110
  export function loadSurfaceDates(symbol: string, options: { signal?: AbortSignal } = {}, api: ImpliedVolatilityApi = apiClient) {
101
111
  return api.impliedVolatility<{ version: 1; symbol: string; dates: string[] }>(`surface-dates?${query({ symbol: ivSymbol(symbol) })}`,
@@ -3,7 +3,7 @@ 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
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";
6
+ import { HV_WINDOWS, type HvWindow, type IvLookback, type IvStatRow, projectIvHistory, projectRichCheap, type RichCheapRow, richCheapDates, screenCoverage, 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";
@@ -11,6 +11,7 @@ const percent = (value: unknown) => typeof value === "number" ? formatVol(value)
11
11
 
12
12
  export const ivHistoryHeadless: HeadlessPaneDefinition<"bundle"> = {
13
13
  shape: "bundle", argument: { kind: "ticker", description: "US option underlying" },
14
+ usListingsOnly: "Implied volatility history",
14
15
  freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date", oldest: null },
15
16
  describe: (args) => `HIVG ${args.symbols[0] ?? ""}`,
16
17
  discovery: { aliases: ["HIVG"], screenshotReadiness: "live-dom", dataRequirements: ["Cloud stored implied volatility", "Daily price history"],
@@ -65,6 +66,7 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
65
66
  // Dated by the readings themselves; the response's own date is the day it was fetched.
66
67
  freshness: { status: "not-a-feed", basis: "daily implied volatility", cadence: "daily", observedKey: "date" },
67
68
  argument: { kind: "symbol-list", optional: true, maximum: VCA_LIMIT, description: "US option underlyings; defaults to index and sector ETFs." },
69
+ usListingsOnly: "Implied volatility",
68
70
  options: [{ key: "preset", type: "enum", values: [{ value: "etfs" }, { value: "megacaps" }], defaultValue: "etfs", description: "Preset when no symbols are given" }],
69
71
  describe: "Volatility rich/cheap",
70
72
  discovery: { aliases: ["VCA"], screenshotReadiness: "live-dom", dataRequirements: ["Cloud stored implied volatility", "Daily price history"],
@@ -77,7 +79,7 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
77
79
  loadRealizedVolatilities(universe.instruments, 20, { signal: ctx.signal }, createHvDependencies(ctx.marketData)),
78
80
  ]);
79
81
  const rows = projectRichCheap(payload.rows, hv).sort((a, b) => (b.percentile ?? -1) - (a.percentile ?? -1));
80
- const queued = rows.filter((row) => row.status === "queued").map((row) => row.symbol);
82
+ const coverage = screenCoverage(rows);
81
83
  const dates = richCheapDates(rows);
82
84
  const hasSkew = rows.some((row) => row.skew != null);
83
85
  const hasIv1y = rows.some((row) => row.iv1y != null), hasIv1yRank = rows.some((row) => row.iv1yPercentile != null);
@@ -99,9 +101,10 @@ export const ivScreenHeadless: HeadlessPaneDefinition<"bundle"> = {
99
101
  { key: "hv", header: "HV20", format: percent },
100
102
  { key: "ivHv", header: "IV/HV", format: (value: unknown) => typeof value === "number" ? value.toFixed(2) : "--" },
101
103
  ], rows: rows.map((row) => ({ ...row })) }],
102
- complete: !queued.length && !universe.error, unavailableSymbols: queued,
104
+ // A symbol with no options history is final; only history still to come leaves the screen incomplete.
105
+ complete: !coverage.pending.length && !universe.error, unavailableSymbols: coverage.missing,
103
106
  errors: [universe.error].filter((value): value is string => !!value),
104
- notes: [...queued.length ? [`Queued for backfill: ${queued.join(", ")}.`] : [], ...VCA_DEFINITIONS],
107
+ notes: [...coverage.notes, ...VCA_DEFINITIONS],
105
108
  metadata: { presets: VCA_PRESETS, methodology: METHODOLOGY },
106
109
  };
107
110
  },
@@ -1,4 +1,5 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
+ import { IV_SERIES } from "./series";
2
3
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
3
4
  import { ivHistoryHeadless, ivScreenHeadless } from "./headless";
4
5
  import { IvHistoryPane } from "./pane";
@@ -6,6 +7,7 @@ import { IvScreenPane, VCA_SCOPE_OPTIONS } from "./screen-pane";
6
7
 
7
8
 
8
9
  export const ivHistoryModule: PluginModule = {
10
+ series: IV_SERIES,
9
11
  panes: [{
10
12
  id: "iv-history", name: "Implied Volatility History", icon: "V", component: IvHistoryPane,
11
13
  defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 118, height: 36 },