@gloom-sh/terminal 0.18.5 → 0.19.0

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 (436) hide show
  1. package/package.json +1 -1
  2. package/patches/electrobun@1.18.1.patch +140 -7
  3. package/src/api-client/cot.ts +6 -2
  4. package/src/api-client/credit-documents.ts +6 -0
  5. package/src/api-client/data.ts +17 -6
  6. package/src/api-client/equity-screener.ts +24 -0
  7. package/src/api-client/fund-facts.ts +45 -4
  8. package/src/api-client/index.ts +11 -0
  9. package/src/api-client/market-discovery.ts +10 -0
  10. package/src/api-client/market-movers.ts +7 -0
  11. package/src/api-client/members.ts +1 -1
  12. package/src/api-client/perps.ts +43 -0
  13. package/src/api-client/research-activity.ts +3 -2
  14. package/src/api-client/types.ts +70 -2
  15. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  16. package/src/app/runtime/broker-import.ts +0 -2
  17. package/src/app/runtime/plugin-bindings.ts +73 -56
  18. package/src/architecture/pane-tabs-allowlist.json +3 -0
  19. package/src/architecture/ttl-caches-allowlist.json +19 -0
  20. package/src/brokers/sync-broker-instance.ts +2 -8
  21. package/src/capabilities/chart-series.ts +1 -1
  22. package/src/cli/commands/crypto-hints.ts +40 -24
  23. package/src/cli/commands/market.ts +72 -26
  24. package/src/cli/commands/options-chain.ts +48 -7
  25. package/src/cli/commands/options-leaps.ts +9 -3
  26. package/src/cli/commands/overview.ts +61 -12
  27. package/src/cli/commands/search.ts +21 -1
  28. package/src/cli/commands/themes.ts +11 -2
  29. package/src/cli/commands/ticker.ts +55 -34
  30. package/src/cli/company-name-hint.ts +41 -0
  31. package/src/cli/earnings-calendar.ts +1 -1
  32. package/src/cli/errors.ts +8 -0
  33. package/src/cli/glossary.ts +25 -2
  34. package/src/cli/history-rows.ts +57 -9
  35. package/src/cli/index.ts +2 -0
  36. package/src/cli/listing-arg.ts +180 -52
  37. package/src/cli/output-file.ts +68 -0
  38. package/src/cli/pane-functions/capabilities.ts +3 -0
  39. package/src/cli/pane-functions/catalog.ts +80 -7
  40. package/src/cli/pane-functions/dom.ts +13 -3
  41. package/src/cli/pane-functions/freshness.ts +86 -3
  42. package/src/cli/pane-functions/function-help.ts +13 -10
  43. package/src/cli/pane-functions/headless.ts +17 -7
  44. package/src/cli/pane-functions/index.ts +49 -14
  45. package/src/cli/pane-functions/market-session.ts +66 -4
  46. package/src/cli/pane-functions/options.ts +19 -1
  47. package/src/cli/pane-functions/resolver.ts +19 -8
  48. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  49. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  50. package/src/cli/quote-answer.ts +22 -11
  51. package/src/cli/ticker-resolution.ts +3 -2
  52. package/src/components/chart/composite/composite-chart.tsx +4 -0
  53. package/src/components/chart/composite/interactions.ts +6 -0
  54. package/src/components/chart/composite/legend.tsx +45 -7
  55. package/src/components/chart/composite/panel-gestures.ts +33 -0
  56. package/src/components/chart/composite/panel-surface.tsx +52 -3
  57. package/src/components/chart/composite/rasterizer.ts +24 -13
  58. package/src/components/chart/composite/scene.ts +39 -4
  59. package/src/components/chart/composite/series-cues.ts +14 -0
  60. package/src/components/chart/composite/text-renderer.ts +9 -2
  61. package/src/components/chart/composite/tool-catalog.ts +36 -0
  62. package/src/components/chart/composite/tools.ts +203 -44
  63. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  64. package/src/components/command-bar/assist/inventory.ts +2 -1
  65. package/src/components/command-bar/commands/direct/index.ts +28 -13
  66. package/src/components/command-bar/pane-templates/items.ts +3 -2
  67. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  68. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  69. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  70. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  71. package/src/components/onboarding/wizard-positions.ts +3 -3
  72. package/src/components/ticker/list-table-view.tsx +4 -1
  73. package/src/components/ui/choice-dialog.tsx +23 -18
  74. package/src/components/ui/choice-groups.ts +18 -0
  75. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  76. package/src/components/ui/data-table/opentui/model.ts +26 -1
  77. package/src/components/ui/data-table/types.ts +2 -0
  78. package/src/components/ui/list-view.tsx +8 -4
  79. package/src/components/ui/query-bar-keys.tsx +2 -0
  80. package/src/components/ui/query-bar.tsx +1 -0
  81. package/src/components/ui/select-button.tsx +5 -1
  82. package/src/components/ui/select-field.tsx +1 -0
  83. package/src/components/ui/table-layout.ts +8 -5
  84. package/src/core/app-runtime.ts +2 -0
  85. package/src/core/app-services.ts +2 -2
  86. package/src/data/config/chart-settings.ts +1 -1
  87. package/src/data/json-ticker-repository.ts +17 -8
  88. package/src/data/ticker-repository.ts +16 -9
  89. package/src/data/ticker-writes.ts +81 -0
  90. package/src/data/ttl-cache.ts +165 -0
  91. package/src/docs/snapshot.json +71 -137
  92. package/src/i18n/es.ts +18 -2
  93. package/src/i18n/ja.ts +18 -2
  94. package/src/i18n/ko.ts +18 -2
  95. package/src/i18n/zh-cn.ts +18 -2
  96. package/src/i18n/zh-tw.ts +18 -2
  97. package/src/market-data/depositary-pair.ts +2 -2
  98. package/src/market-data/fund-facts.ts +50 -0
  99. package/src/market-data/market/change-basis.ts +16 -3
  100. package/src/market-data/market/format.ts +25 -0
  101. package/src/market-data/market/freshness.ts +2 -1
  102. package/src/market-data/options-alternatives.ts +81 -4
  103. package/src/news/aggregator.ts +113 -9
  104. package/src/news/cloud-session.ts +21 -0
  105. package/src/news/hooks.ts +1 -0
  106. package/src/news/news-model.ts +1 -0
  107. package/src/news/types.ts +11 -0
  108. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  109. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  110. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  111. package/src/plugins/builtin/alerts/cli.ts +336 -77
  112. package/src/plugins/builtin/alerts/command.ts +25 -1
  113. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  114. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  115. package/src/plugins/builtin/alerts/format.ts +75 -5
  116. package/src/plugins/builtin/alerts/headless.ts +24 -6
  117. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  118. package/src/plugins/builtin/alerts/history.ts +1 -1
  119. package/src/plugins/builtin/alerts/index.tsx +79 -16
  120. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  121. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  122. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  123. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  124. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  125. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  126. package/src/plugins/builtin/alerts/storage.ts +12 -8
  127. package/src/plugins/builtin/alerts/types.ts +4 -0
  128. package/src/plugins/builtin/analytics/index.tsx +2 -1
  129. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  130. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  131. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  132. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  133. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  134. package/src/plugins/builtin/attention/index.tsx +2 -1
  135. package/src/plugins/builtin/attention/listing.ts +14 -0
  136. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  137. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  138. package/src/plugins/builtin/buildout/format.ts +38 -0
  139. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  140. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  141. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  142. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  143. package/src/plugins/builtin/changelog/index.tsx +9 -6
  144. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  145. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  146. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  147. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  148. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  149. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  150. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  151. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  152. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  153. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  154. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  155. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  156. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  157. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  158. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  159. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  160. package/src/plugins/builtin/company-attention/client.ts +8 -9
  161. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  162. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  163. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  164. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  165. package/src/plugins/builtin/correlation/headless.ts +13 -5
  166. package/src/plugins/builtin/correlation/history.ts +22 -0
  167. package/src/plugins/builtin/cot/client.ts +22 -18
  168. package/src/plugins/builtin/cot/headless.ts +15 -10
  169. package/src/plugins/builtin/cot/model.ts +14 -1
  170. package/src/plugins/builtin/cot/pane.tsx +22 -14
  171. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  172. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  173. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  174. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  175. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  176. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  177. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  178. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  179. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  180. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  181. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  182. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  183. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  184. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  185. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  186. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  187. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  188. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  189. package/src/plugins/builtin/docs/targets.ts +5 -1
  190. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  191. package/src/plugins/builtin/earnings/board.tsx +62 -71
  192. package/src/plugins/builtin/earnings/headless.ts +40 -4
  193. package/src/plugins/builtin/earnings/scope.ts +31 -43
  194. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  195. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  196. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  197. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  198. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  199. package/src/plugins/builtin/econ/types.ts +2 -0
  200. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  201. package/src/plugins/builtin/econ-statistics/headless.ts +6 -4
  202. package/src/plugins/builtin/econ-statistics/stats.ts +36 -9
  203. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  204. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  205. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  206. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  207. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  208. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  209. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  210. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  211. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  212. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  213. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  214. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  215. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  216. package/src/plugins/builtin/gpu/headless.ts +8 -2
  217. package/src/plugins/builtin/help/function-index.ts +4 -1
  218. package/src/plugins/builtin/help/index.tsx +1 -0
  219. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  220. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  221. package/src/plugins/builtin/holders/headless.ts +18 -1
  222. package/src/plugins/builtin/iv-history/client.ts +52 -11
  223. package/src/plugins/builtin/iv-history/format.ts +36 -23
  224. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  225. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  226. package/src/plugins/builtin/iv-history/model.ts +163 -27
  227. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  228. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  229. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  230. package/src/plugins/builtin/jobs/client.ts +25 -29
  231. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  232. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  233. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  234. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  235. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  236. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  237. package/src/plugins/builtin/market-movers/client.ts +25 -7
  238. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  239. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  240. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  241. package/src/plugins/builtin/market-movers/model.ts +4 -1
  242. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  243. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  244. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  245. package/src/plugins/builtin/market-movers/table.ts +17 -5
  246. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  247. package/src/plugins/builtin/news/index.tsx +1 -0
  248. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  249. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  250. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  251. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  252. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  253. package/src/plugins/builtin/openfda/client.ts +14 -2
  254. package/src/plugins/builtin/openfda/index.tsx +3 -2
  255. package/src/plugins/builtin/openfda/model.ts +9 -0
  256. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  257. package/src/plugins/builtin/options/analytics.ts +29 -5
  258. package/src/plugins/builtin/options/enrichment.ts +17 -5
  259. package/src/plugins/builtin/options/table.ts +5 -2
  260. package/src/plugins/builtin/options/types.ts +2 -0
  261. package/src/plugins/builtin/options/view.tsx +17 -4
  262. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  263. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  264. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  265. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  266. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  267. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  268. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  269. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  270. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  271. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  272. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  273. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  274. package/src/plugins/builtin/pe-band/model.ts +12 -49
  275. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  276. package/src/plugins/builtin/perps/cascade.ts +236 -0
  277. package/src/plugins/builtin/perps/client.ts +56 -2
  278. package/src/plugins/builtin/perps/headless.ts +65 -9
  279. package/src/plugins/builtin/perps/index.tsx +7 -5
  280. package/src/plugins/builtin/perps/model.ts +18 -4
  281. package/src/plugins/builtin/perps/pane.tsx +19 -22
  282. package/src/plugins/builtin/perps/status-line.ts +25 -0
  283. package/src/plugins/builtin/plugin-module.ts +5 -1
  284. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  285. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  286. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  287. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  288. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  289. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  290. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  291. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  292. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  293. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  294. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  295. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  296. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  297. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  298. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  299. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  300. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  301. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  302. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  303. package/src/plugins/builtin/research/event-model.ts +10 -8
  304. package/src/plugins/builtin/research/headless.ts +1 -1
  305. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  306. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  307. package/src/plugins/builtin/research/relative-valuation-pane.tsx +7 -5
  308. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  309. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  310. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  311. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  312. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  313. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  314. package/src/plugins/builtin/scanner/headless.ts +204 -35
  315. package/src/plugins/builtin/scanner/index.ts +16 -5
  316. package/src/plugins/builtin/scanner/live-headless.ts +27 -3
  317. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  318. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  319. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  320. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  321. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  322. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  323. package/src/plugins/builtin/sec/headless.ts +2 -1
  324. package/src/plugins/builtin/series-inventory.ts +2 -0
  325. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  326. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  327. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  328. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  329. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  330. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  331. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  332. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  333. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  334. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  335. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  336. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  337. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  338. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  339. package/src/plugins/builtin/themes/model.ts +16 -7
  340. package/src/plugins/builtin/themes/pane.tsx +5 -5
  341. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  342. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  343. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  344. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  345. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  346. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  347. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  348. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  349. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  350. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  351. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  352. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  353. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  354. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  355. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  356. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  357. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  358. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  359. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  360. package/src/plugins/builtin/volatility/index.tsx +2 -2
  361. package/src/plugins/builtin/volatility/pane.tsx +2 -2
  362. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  363. package/src/plugins/catalog-manifest.ts +2 -1
  364. package/src/plugins/marketplace/model.ts +18 -5
  365. package/src/portfolio/futures-positions.ts +6 -3
  366. package/src/portfolio/position-quantity.ts +15 -9
  367. package/src/remote/resources.ts +2 -1
  368. package/src/renderers/dom/data-table/row.tsx +26 -0
  369. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  370. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  371. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  372. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  373. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  374. package/src/renderers/electrobun/bun/desktop/backend.ts +12 -0
  375. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  376. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  377. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  378. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  379. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  380. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  381. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  382. package/src/renderers/electrobun/view/main.tsx +24 -1
  383. package/src/renderers/electrobun/view/news-source.ts +3 -1
  384. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  385. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  386. package/src/renderers/opentui/start.tsx +5 -0
  387. package/src/series/registry.ts +1 -0
  388. package/src/series/types.ts +6 -0
  389. package/src/series/validate.ts +1 -0
  390. package/src/sources/gloomberb-cloud/index.ts +1 -0
  391. package/src/sources/gloomberb-cloud/news.ts +47 -7
  392. package/src/sources/provider-router/financials.ts +15 -3
  393. package/src/sources/provider-router/index.ts +8 -5
  394. package/src/state/ticker-cleanup.ts +50 -0
  395. package/src/sync/controller.ts +4 -3
  396. package/src/sync/core-contributors.ts +90 -31
  397. package/src/telemetry/stall-reports-dom.ts +38 -0
  398. package/src/telemetry/stall-reports-node.ts +75 -0
  399. package/src/telemetry/stall-reports.ts +214 -0
  400. package/src/test-support/preload.ts +14 -0
  401. package/src/theme/themes.ts +10 -0
  402. package/src/tickers/company-name.ts +44 -3
  403. package/src/tickers/listing-venues.ts +15 -3
  404. package/src/tickers/terminal-codes.ts +19 -0
  405. package/src/time-series/alignment.ts +1 -1
  406. package/src/time-series/economic.ts +18 -4
  407. package/src/time-series/field-catalog.ts +1 -1
  408. package/src/time-series/filed-trailing-eps.ts +197 -0
  409. package/src/time-series/fundamentals.ts +44 -1
  410. package/src/time-series/ice-credit.ts +3 -0
  411. package/src/time-series/price-comparison.ts +30 -2
  412. package/src/time-series/resolve-context.ts +3 -1
  413. package/src/time-series/resolve.ts +56 -4
  414. package/src/time-series/session-bar.ts +11 -0
  415. package/src/time-series/spec.ts +3 -2
  416. package/src/time-series/studies.ts +8 -2
  417. package/src/time-series/total-return.ts +160 -0
  418. package/src/time-series/transforms.ts +2 -3
  419. package/src/time-series/types.ts +22 -1
  420. package/src/types/financials.ts +32 -1
  421. package/src/types/headless.ts +21 -3
  422. package/src/types/news-source.ts +1 -0
  423. package/src/types/plugin.ts +9 -0
  424. package/src/ui/host.tsx +3 -0
  425. package/src/updater/failure.ts +4 -1
  426. package/src/utils/cli-output.ts +42 -1
  427. package/src/utils/crypto-pair.ts +44 -0
  428. package/src/utils/forward-dividend.ts +2 -0
  429. package/src/utils/fundamentals.ts +24 -3
  430. package/src/utils/fx-market-hours.ts +26 -3
  431. package/src/utils/main-thread-stall.ts +35 -2
  432. package/src/utils/market-capitalization.ts +24 -1
  433. package/src/utils/price-history.ts +7 -1
  434. package/src/utils/shell-word.ts +8 -0
  435. package/src/utils/trailing-dividend.ts +53 -4
  436. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
@@ -23,6 +23,7 @@ import {
23
23
  type MoverRankBasis,
24
24
  } from "../../plugins/builtin/market-movers/model";
25
25
  import { MOVER_CANDIDATES } from "../../plugins/builtin/market-movers/client";
26
+ import { hasPartialFloat, meetsMinFloat, minFloatShares, normalizeMinFloat, PARTIAL_FLOAT_NOTE } from "../../plugins/builtin/market-movers/min-float";
26
27
  import { fetchEconCalendarFrom } from "../../plugins/builtin/econ/calendar-source";
27
28
  import { computedFredSeriesNote, seriesNotOnFred } from "../../sources/gloomberb-cloud/fred-series";
28
29
  import { EXTENDED_SESSION_LABELS, getQuoteSessionFields, type ExtendedSession } from "../../market-data/market/status";
@@ -41,7 +42,7 @@ import {
41
42
  type CountryFilter,
42
43
  type ImpactFilter,
43
44
  } from "../../plugins/builtin/econ/calendar-model";
44
- import { hasNoReleaseTime, hasNoStatedTime } from "../../plugins/builtin/econ/types";
45
+ import { hasNoReleaseTime, hasNoStatedTime, type EconEvent } from "../../plugins/builtin/econ/types";
45
46
  import { econRowFigures } from "../../plugins/builtin/econ/figures";
46
47
  import { isoDate, rejectExtraArgs, requireArg, requireOneArg, takeFlag, takeOption } from "./command-utils";
47
48
  import { buildCorrelationSeries } from "../../plugins/builtin/correlation/matrix/model";
@@ -141,7 +142,7 @@ const MOVER_LISTS: Record<string, ScreenerCategory | "trending"> = {
141
142
  "most-active": "most_actives",
142
143
  trending: "trending",
143
144
  };
144
- const MOVERS_USAGE = "movers [gainers|losers|active|trending] [--session regular|extended]";
145
+ const MOVERS_USAGE = "movers [gainers|losers|active|trending] [--session regular|extended] [--min-float <shares>]";
145
146
  /** The most rows the movers list serves in one request. */
146
147
  const MOVER_REQUEST_CAP = 100;
147
148
  /** A one-shot list waits this long for its rows' average volumes and sizes, as `fn MOST` does. */
@@ -154,6 +155,16 @@ function moverRankBasis(value: string | undefined, ctx: Parameters<CliCommandDef
154
155
  return basis;
155
156
  }
156
157
 
158
+ /** `--min-float` in shares, null for none; fails on a value that is not a share count. */
159
+ function moverMinFloat(value: string | undefined, ctx: Parameters<CliCommandDef["execute"]>[1]): number | null {
160
+ if (value == null) return null;
161
+ try {
162
+ return minFloatShares(normalizeMinFloat(value));
163
+ } catch (error) {
164
+ ctx.fail(error instanceof Error ? error.message : String(error));
165
+ }
166
+ }
167
+
157
168
  function screenerCategory(args: readonly string[], ctx: Parameters<CliCommandDef["execute"]>[1]): ScreenerCategory | "trending" {
158
169
  rejectExtraArgs(args, 1, { usage: MOVERS_USAGE, takes: "one list" }, ctx);
159
170
  const list = args[0]?.toLowerCase() ?? "gainers";
@@ -199,12 +210,14 @@ async function runMoverCommand(rawArgs: string[], ctx: Parameters<CliCommandDef[
199
210
  const args = [...rawArgs];
200
211
  const sessionOption = takeOption(args, "--session");
201
212
  const basis = moverRankBasis(sessionOption, ctx);
213
+ const minFloat = moverMinFloat(takeOption(args, "--min-float"), ctx);
202
214
  const category = screenerCategory(args, ctx);
203
215
  const limit = ctx.cliOptions.limit ?? 25;
204
216
  if (sessionOption != null && (category === "trending" || category === "most_actives")) {
205
217
  ctx.fail("--session ranks Gainers and Losers.", category === "trending"
206
218
  ? "Trending keeps its source's order." : "Most active ranks by volume.");
207
219
  }
220
+ if (minFloat != null && category === "trending") ctx.fail("--min-float filters Gainers, Losers and Most active.", "Trending carries no float.");
208
221
  if (category === "trending") {
209
222
  await withCliServices(ctx, async (services) => {
210
223
  const trending = await fetchTrending(limit, undefined, { forceRefresh: ctx.cliOptions.refresh });
@@ -220,8 +233,8 @@ async function runMoverCommand(rawArgs: string[], ctx: Parameters<CliCommandDef[
220
233
  }
221
234
 
222
235
  // After the close a price list keeps only the rows moving its way on the basis
223
- // asked for, so it reads more candidates than it shows.
224
- const count = category === "most_actives" ? limit : Math.min(MOVER_REQUEST_CAP, Math.max(limit, MOVER_CANDIDATES));
236
+ // asked for, and a minimum float drops rows, so they read more candidates than they show.
237
+ const count = category === "most_actives" && minFloat == null ? limit : Math.min(MOVER_REQUEST_CAP, Math.max(limit, MOVER_CANDIDATES));
225
238
  // With the saved session, as MOST reads it: a Pro account gets the real-time lists, anyone else the delayed ones.
226
239
  const movers = await withMarketData(ctx, () =>
227
240
  fetchPreferredMarketMovers(category, count, { forceRefresh: ctx.cliOptions.refresh, session: basis, metadataWaitMs: MOVER_METADATA_WAIT_MS }));
@@ -230,7 +243,7 @@ async function runMoverCommand(rawArgs: string[], ctx: Parameters<CliCommandDef[
230
243
  // the regular session's move (its close's after the close), or with
231
244
  // --session extended by the extended print's move from that close.
232
245
  const { quotes, rankedBy } = rankMoverQuotes(category, movers.quotes.map(moverRegularSession), basis);
233
- const rows = quotes.slice(0, limit).map((quote) => {
246
+ const rows = quotes.filter((quote) => meetsMinFloat(quote.floatShares, minFloat)).slice(0, limit).map((quote) => {
234
247
  const { extended, ...row } = quote;
235
248
  return {
236
249
  ...row,
@@ -244,16 +257,18 @@ async function runMoverCommand(rawArgs: string[], ctx: Parameters<CliCommandDef[
244
257
  // As the MOST report reads it: real-time for a Pro account, delayed otherwise, each row dated by its own last price.
245
258
  ctx.printResult({
246
259
  data: rows,
247
- metadata: { category, rankedBy },
260
+ metadata: { category, rankedBy, ...(minFloat != null ? { minFloat } : {}) },
248
261
  ...(fellBack ? { warnings: ["No pre-market or after-hours prints now, so the list is ranked by the regular-session move."] } : {}),
249
262
  freshness: rowsFreshness(rows, { status: movers.source === "cloud" ? "live" : "delayed" }, { stale: movers.stale }),
250
263
  }, {
251
264
  heading: `Ranked by ${MOVER_RANKING_LABELS[rankedBy]}`,
265
+ ...(minFloat != null && hasPartialFloat(rows) ? { footnote: PARTIAL_FLOAT_NOTE } : {}),
252
266
  columns: [
253
267
  ...MOVER_COLUMNS.slice(0, 4),
254
268
  ...EXTENDED_COLUMNS,
255
269
  { key: "volume", header: "Volume", align: "right", value: (row) => formatCompact(Number(row.volume)) },
256
270
  { ...MARKET_CAP_CELL, value: (row) => row.marketCap == null ? "" : formatCompact(Number(row.marketCap)) },
271
+ ...(minFloat != null ? [{ key: "floatShares", header: "Float", align: "right" as const, value: (row: Record<string, unknown>) => formatCompact(Number(row.floatShares)) }] : []),
257
272
  ],
258
273
  });
259
274
  }
@@ -325,6 +340,19 @@ function econHeadColumns(viewerZone: string | null): CliResultColumn[] {
325
340
  ];
326
341
  }
327
342
 
343
+ /**
344
+ * The text table's columns up to the event. Plain output keeps every column a
345
+ * query has, so a column that is empty by construction has to be left out
346
+ * here, not hidden by the table: the viewer's clock only exists with --tz, and
347
+ * the publisher's own clock only on rows a bank or statistics office times
348
+ * itself. Exports keep both, so a script's header never moves.
349
+ */
350
+ function econTextHeadColumns(viewerZone: string | null, publisherTimed: boolean): CliResultColumn[] {
351
+ return econHeadColumns(viewerZone).filter((column) => (
352
+ column.key === "viewerTime" ? !!viewerZone : column.key === "localTime" ? publisherTimed : true
353
+ ));
354
+ }
355
+
328
356
  /**
329
357
  * Actual, Forecast and Prior: as the text draws them (`30.3K`, `-4.1M bbl`) in the table, as
330
358
  * numbers in the exports (30300, -4100000), where Unit and Scale say what they count and how they were drawn.
@@ -349,6 +377,9 @@ const econExportColumns: CliResultColumn[] = [
349
377
  { key: "actualText", header: "actual_text" },
350
378
  { key: "forecastText", header: "forecast_text" },
351
379
  { key: "priorText", header: "prior_text" },
380
+ // Which feed the forecast was read from and when, so a consensus can be told from another read of it.
381
+ { key: "consensusFeed", header: "consensus_feed" },
382
+ { key: "consensusReadAt", header: "consensus_read_at" },
352
383
  ];
353
384
 
354
385
  /**
@@ -409,6 +440,16 @@ function blankTimeNotes(rows: ReadonlyArray<{ event: string; country: string; ti
409
440
  return [...byNote].map(([note, events]) => `No time for ${events.join(", ")}: ${note.charAt(0).toLowerCase()}${note.slice(1)}.`);
410
441
  }
411
442
 
443
+ /**
444
+ * Whether a row has come out: it has an actual, or it has no figure at all (a
445
+ * speech, a meeting, a holiday) and its time has passed. A row with a forecast
446
+ * or a prior and no actual is still to come, whatever its time.
447
+ */
448
+ function cameOut(event: Pick<EconEvent, "actual" | "forecast" | "prior" | "date">, now: number): boolean {
449
+ if (event.actual?.trim()) return true;
450
+ return !event.forecast?.trim() && !event.prior?.trim() && event.date.getTime() <= now;
451
+ }
452
+
412
453
  /**
413
454
  * A `--from` the stored calendar does not reach back to, which Gloom Cloud refuses
414
455
  * with "The calendar reaches back 400 days": an input error naming the earliest
@@ -437,7 +478,8 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
437
478
  }
438
479
  rejectArgs(rawArgs, ECON_USAGE, ctx, "Filter with --country and --impact.");
439
480
  await withCliServices(ctx, async () => {
440
- const todayStart = Math.floor(Date.now() / DAY_MS) * DAY_MS;
481
+ const now = Date.now();
482
+ const todayStart = Math.floor(now / DAY_MS) * DAY_MS;
441
483
  // An earlier day reads the stored calendar, which keeps past events after the week's listing drops them.
442
484
  // A server without it, a server error or no network lists the current calendar (cached when offline) instead.
443
485
  const stored = fromOption != null && fromOption < isoDate(new Date(todayStart))
@@ -497,11 +539,15 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
497
539
  // Who publishes the figure (Freddie Mac for the 30-year mortgage rate), the period it covers and its page.
498
540
  publisher: event.source?.name ?? null,
499
541
  referencePeriod: event.referencePeriod ?? null,
500
- // When the figure came out: the row's time once it has an actual (its day alone when it states no time), null before.
501
- released: event.actual?.trim() ? (hasNoReleaseTime(event) ? isoDate(event.date).slice(0, 10) : isoDate(event.date)) : null,
542
+ // When the figure came out: the row's time once it has an actual, and for a row with no figures (a speech, a
543
+ // meeting) once its time has passed; its day alone when it states no time, null before.
544
+ released: cameOut(event, now) ? (hasNoReleaseTime(event) ? isoDate(event.date).slice(0, 10) : isoDate(event.date)) : null,
502
545
  sourceUrl: event.source?.url ?? null,
503
- // The same indicator from one release to the next; empty until Gloom Cloud names it.
546
+ // The same indicator from one release to the next, as Gloom Cloud names it.
504
547
  indicatorId: event.indicatorId ?? null,
548
+ // The feed the forecast was read from and when, for a row with a forecast.
549
+ consensusFeed: event.consensus?.source ?? null,
550
+ consensusReadAt: event.consensus?.readAt ?? null,
505
551
  };
506
552
  });
507
553
  // A filter of countries only, none of which has anything in the window, says so as its empty list.
@@ -535,7 +581,7 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
535
581
  }),
536
582
  // The exports add what a script needs to read a row; the text table keeps the columns it had.
537
583
  columns: [...econHeadColumns(viewerZone), ...econFigureColumns(false), ...econExportColumns],
538
- textColumns: [...econHeadColumns(viewerZone), ...econFigureColumns(true), ECON_PUBLISHER_COLUMN],
584
+ textColumns: [...econTextHeadColumns(viewerZone, rows.some((row) => !!publisherClock(row.localTime, row.timeZone))), ...econFigureColumns(true), ECON_PUBLISHER_COLUMN],
539
585
  });
540
586
  });
541
587
  }
@@ -826,8 +872,11 @@ export const overviewCliCommands: CliCommandDef[] = [
826
872
  options: [{
827
873
  flags: "--session <basis>",
828
874
  description: "Rank gainers and losers by the regular-session move (regular, the default) or by the pre-market or after-hours move from the close (extended)",
875
+ }, {
876
+ flags: "--min-float <shares>",
877
+ description: "Keep the gainers, losers or most active whose float (the one EQS serves) is at least this many shares, such as 10M or 500k; a row with no float is left out",
829
878
  }],
830
- examples: ["movers", "movers losers --limit 10", "movers gainers --session extended", "movers trending"],
879
+ examples: ["movers", "movers losers --limit 10", "movers gainers --session extended", "movers gainers --min-float 10M", "movers trending"],
831
880
  },
832
881
  execute: runMoverCommand,
833
882
  },
@@ -133,7 +133,7 @@ export async function searchCandidatesForCli({
133
133
  const quote = BARE_SYMBOL.test(symbol) && candidates.some((candidate) => candidate.symbol.toUpperCase() === symbol)
134
134
  ? await (quoted ?? loadSearchedQuote(symbol, savedExchange, dataProvider))
135
135
  : null;
136
- return withQuotedNames(symbol, withHomeListingPrimary(trimmedQuery, withPrimaryListingFirst(symbol, candidates, quote)), quote)
136
+ return withQuotedNames(symbol, withUsLineOfPrimary(withHomeListingPrimary(trimmedQuery, withPrimaryListingFirst(symbol, candidates, quote))), quote)
137
137
  .slice(0, totalLimit);
138
138
  } catch (error) {
139
139
  // Not "No matches found": a script or a reader would take that for a name nothing lists.
@@ -299,6 +299,26 @@ function withHomeListingPrimary(query: string, candidates: TickerSearchCandidate
299
299
  return [...rest.slice(0, at), { ...home, category: "Primary Listing" as const }, { ...primary, category: "Other Listings" as const }, ...rest.slice(at + 1)];
300
300
  }
301
301
 
302
+ /**
303
+ * A company whose primary listing is abroad lists the US line of the same
304
+ * symbol right after it: the line `quote SHEL` reads (a bare symbol is its US
305
+ * listing), which a name search ("shell") would otherwise rank below the
306
+ * company's lines on other venues and cut. Any other list keeps its order.
307
+ */
308
+ function withUsLineOfPrimary(candidates: TickerSearchCandidate[]): TickerSearchCandidate[] {
309
+ const primary = candidates.find((candidate) => candidate.category === "Primary Listing");
310
+ if (!primary || primary.kind !== "search" || isUsListingExchange(candidateExchange(primary))) return candidates;
311
+ const symbol = primary.symbol.toUpperCase();
312
+ const company = resolveSearchName(primary);
313
+ const us = candidates.find((candidate) => candidate !== primary && candidate.kind === "search" && !candidate.contractKey
314
+ && candidate.category !== "Funds & Derivatives" && candidate.symbol.toUpperCase() === symbol
315
+ && isUsListingExchange(candidateExchange(candidate)) && sameCompany(resolveSearchName(candidate), company));
316
+ if (!us) return candidates;
317
+ const rest = candidates.filter((candidate) => candidate !== us);
318
+ const at = rest.indexOf(primary) + 1;
319
+ return [...rest.slice(0, at), { ...us, category: "Other Listings" as const }, ...rest.slice(at)];
320
+ }
321
+
302
322
  /** The candidate on the venue the quote prices; the US exchanges share one symbol, so a quote on any of them is its one US line. */
303
323
  function quotedCandidate(candidates: readonly TickerSearchCandidate[], quote: Quote | null): TickerSearchCandidate | undefined {
304
324
  const quoted = canonicalExchange(quote?.listingExchangeName || quote?.exchangeName);
@@ -16,14 +16,23 @@ function themeChoicesText(): string {
16
16
  return `Themes: ${choices}. gloom config themes lists them with notes.`;
17
17
  }
18
18
 
19
+ /** `Light themes: paper (Paper), github-light (GitHub Light), ...`, for `light` or `dark` typed as a theme; null for anything else. */
20
+ function appearanceHint(input: string): string | null {
21
+ const appearance = input.trim().toLowerCase();
22
+ if (appearance !== "light" && appearance !== "dark") return null;
23
+ const matching = listThemes().filter((theme) => theme.appearance === appearance).map(({ id, name }) => `${id} (${name})`);
24
+ return matching.length > 0 ? ` ${appearance === "light" ? "Light" : "Dark"} themes: ${matching.join(", ")}.` : null;
25
+ }
26
+
19
27
  /**
20
28
  * The id `input` names, by id or display name in any case. Anything else
21
- * fails with the closest id when there is one, and every id with its name.
29
+ * fails with the closest id when there is one, and every id with its name;
30
+ * `light` or `dark` with the themes of that look.
22
31
  */
23
32
  export function requireThemeId(input: string): string {
24
33
  const lookup = resolveThemeId(input);
25
34
  if (lookup.ok) return lookup.id;
26
- const hint = lookup.suggestion ? ` Did you mean ${describeThemeId(lookup.suggestion)}?` : "";
35
+ const hint = appearanceHint(input) ?? (lookup.suggestion ? ` Did you mean ${describeThemeId(lookup.suggestion)}?` : "");
27
36
  return fail(`Unknown theme "${input.trim()}".${hint}`, themeChoicesText());
28
37
  }
29
38
 
@@ -3,12 +3,12 @@ import { formatShortDate, parseDisplayDate } from "../../utils/datetime-format";
3
3
  import { formatReportedMoney } from "../../utils/reported-money";
4
4
  import {
5
5
  appTtmRate, computedEnterpriseValueNote, convertedPriceEarningsNote, enterpriseValueLabel, enterpriseValueReconciliationNote, epsConversionNote,
6
- fundamentalsCurrency, reportedEnterpriseValue, ttmAtAppRate, ttmConversionNote,
6
+ fundamentalsCurrency, reportedEnterpriseValue, trailingEpsSourceNote, ttmAtAppRate, ttmConversionNote,
7
7
  } from "../../utils/fundamentals";
8
8
  import { latestFinancialPeriod } from "../../utils/latest-financial-period";
9
9
  import { burnsCash, cashRunway, SHORT_RUNWAY_HISTORY, formatRunwayMonths, offeringNote, offeringsAfter, runwayMethod, type CashRunway, type OfferingFiling, type RunwayEstimate } from "../../utils/cash-runway";
10
10
  import { exportedFundamentals, formatPriceEarnings, NOT_MEANINGFUL, priceEarningsOnEarnings } from "../../utils/price-earnings";
11
- import { describeFundamentalMarketCap, marketCapBasisNote, selectMarketCapitalization, viewMarketCapitalization } from "../../utils/market-capitalization";
11
+ import { describeFundamentalMarketCap, marketCapBasisNote, selectMarketCapitalization, viewMarketCapitalization, withheldMarketCap } from "../../utils/market-capitalization";
12
12
  import {
13
13
  formatCompact,
14
14
  formatCurrency,
@@ -76,7 +76,7 @@ import { isKnownNonUsListing, secFilingItemCodes, US_FUND_FACTS_ONLY } from "../
76
76
  import { currentReportItemsLabel, hiddenFilingsNote, loadLatestFilings, type HiddenFilings } from "../../utils/sec-filings";
77
77
  import { canonicalExchange, exchangeLabel, isKnownExchangeCode, isUsListingExchange } from "../../utils/exchanges";
78
78
  import {
79
- failIfNotTraded, failMovedSymbol, ListingArgError, listingChoiceNotes, listingFateLoader, listingIdentity, loadBareVenues,
79
+ coinListingNotes, failIfNotTraded, failMovedSymbol, ListingArgError, listingChoiceNotes, listingFateLoader, listingIdentity, loadBareVenues,
80
80
  resolveCliListing, type CliListing, type ListingDataRequest,
81
81
  } from "../listing-arg";
82
82
  import { movedAwayChange } from "../../tickers/ticker-changes";
@@ -92,12 +92,12 @@ import { fundamentalsFreshness, quotesFreshness } from "../freshness";
92
92
  import { distributionHistory, forwardDividendBasis, forwardDividendMayHaveLapsed, lapsedDividend, noCurrentDividend, TRAILING_DIVIDEND_BASIS, type DistributionHistory, type DividendLapse } from "../../utils/forward-dividend";
93
93
  import { declaredDividends } from "../../utils/declared-dividends";
94
94
  import { historyTrailingDividendYield, measuresTrailingYield } from "../../market-data/quotes/live-valuation";
95
- import { notTheCoinNote } from "./crypto-hints";
96
95
  import { loadOfferingFilings, loadStatementFilings, resultsReleaseText, statementPeriodText, type StatementFilingsReport } from "../statement-filings";
97
96
  import type { BankFiguresPayload } from "../../api-client/bank-figures";
98
97
  import { bankSection } from "../bank-section";
99
- import type { FundBondFacts } from "../../api-client/fund-facts";
100
- import { bondFigures, fetchFundBondFacts, NOT_SEC_YIELD } from "../../market-data/bond-funds";
98
+ import type { FundBondFacts, FundFactsPayload } from "../../api-client/fund-facts";
99
+ import { bondFigures, NOT_SEC_YIELD } from "../../market-data/bond-funds";
100
+ import { readFundFacts } from "../../market-data/fund-facts";
101
101
  import { taxEquivalentLines, type TaxEquivalentAnalysis } from "../../market-data/tax-equivalent";
102
102
  import { isRoundTheClockCoin } from "../../utils/crypto-pair";
103
103
  import { coinNewsFallbackNotice } from "../../sources/gloomberb-cloud/news";
@@ -667,21 +667,23 @@ function isYieldMetric(label: string): boolean {
667
667
  }
668
668
 
669
669
  /**
670
- * A US-listed fund's bond facts for the yield lines; null for any other
671
- * listing, and when they cannot be read, which leaves the yield as it was.
670
+ * A US-listed fund's facts for the report: the bond figures its yield lines
671
+ * read, and whether `fn ETF` has figures to point to. Null for any other
672
+ * listing, and when they cannot be read, which leaves the yield as it was
673
+ * and points nowhere.
672
674
  */
673
- export async function loadReportBondFacts(
675
+ export async function loadReportFundFacts(
674
676
  symbol: string,
675
677
  exchange: string | undefined,
676
678
  financials: Pick<TickerFinancials, "quote" | "quoteMetadata">,
677
679
  tickerFile: TickerRecord | null = null,
678
- load: (symbol: string) => Promise<FundBondFacts | null> = fetchFundBondFacts,
679
- ): Promise<FundBondFacts | null> {
680
+ load: (symbol: string) => Promise<FundFactsPayload> = (ticker) => readFundFacts(ticker),
681
+ ): Promise<FundFactsPayload | null> {
680
682
  if (resolveTickerInstrumentKind(tickerFile, financials) !== "fund" || (exchange && !isUsListingExchange(exchange))) return null;
681
683
  try {
682
684
  return await load(symbol);
683
685
  } catch (error) {
684
- logFailure("cli.commands-ticker", "Read bond fund facts failed")(error);
686
+ logFailure("cli.commands-ticker", "Read fund facts failed")(error);
685
687
  return null;
686
688
  }
687
689
  }
@@ -712,7 +714,8 @@ function fundamentalsMetrics(
712
714
  { label: "P/E (TTM)", ...multipleFigure(priceEarningsOnEarnings(fundamentals?.trailingPE, fundamentals?.eps)) },
713
715
  { label: "Forward P/E", ...multipleFigure(priceEarningsOnEarnings(fundamentals?.forwardPE, fundamentals?.forwardEps)) },
714
716
  { label: "PEG", ...multipleFigure(fundamentals?.pegRatio) },
715
- // The flows and EPS are one trailing-twelve-month block, the same twelve months for each line.
717
+ // EPS (TTM) is the last four filed quarters' (filed-trailing-eps), so its twelve months can end before those of
718
+ // the flows below, Gloom Cloud's trailing block; Gloom Cloud's EPS standing in is noted (trailingEpsSourceNote).
716
719
  { label: "EPS (TTM)", ...reportedMoney(fundamentals?.eps, fundamentals?.epsCurrency?.trim() || reportingCurrency, true, codes) },
717
720
  {
718
721
  label: dividendYieldLabel(fundamentals?.dividendYieldBasis, bond),
@@ -797,6 +800,7 @@ function fundamentalsNotes(
797
800
  : undefined;
798
801
  const conversion = [...TRAILING_MONEY_METRICS].some(shown) ? ttmConversionNote(fundamentals) : undefined;
799
802
  const eps = shown("EPS (TTM)") ? epsConversionNote(fundamentals) : undefined;
803
+ const epsSource = trailingEpsSourceNote(fundamentals, { eps: shown("EPS (TTM)"), pe: shown("P/E (TTM)") });
800
804
  const priceEarnings = metrics.some((metric) => metric.label === "P/E (TTM)" && typeof metric.value === "number")
801
805
  ? convertedPriceEarningsNote(fundamentals, quoteCurrency)
802
806
  : undefined;
@@ -804,7 +808,9 @@ function fundamentalsNotes(
804
808
  const enterpriseValue = shown("Enterprise Value")
805
809
  ? enterpriseValueReconciliationNote(fundamentals) ?? computedEnterpriseValueNote(fundamentals)
806
810
  : undefined;
807
- return [shareBasis, enterpriseValue, conversion, eps, priceEarnings].filter((note): note is string => !!note);
811
+ // A cap Gloom Cloud withheld says why, as RV and EQS do.
812
+ const withheld = withheldMarketCap(fundamentals)?.note;
813
+ return [withheld, shareBasis, enterpriseValue, conversion, eps, epsSource, priceEarnings].filter((note): note is string => !!note);
808
814
  }
809
815
 
810
816
  // The share count is the market cap's basis: the cap is the count times the price, or a note says why not.
@@ -814,8 +820,10 @@ type FundamentalsReportData = TickerFinancials & {
814
820
  symbol: string;
815
821
  exchange?: string;
816
822
  distributions?: DividendDistributions | null;
817
- /** A bond fund's SEC yield, yield to worst and duration (loadReportBondFacts). */
823
+ /** A bond fund's SEC yield, yield to worst and duration (loadReportFundFacts). */
818
824
  bondFund?: FundBondFacts | null;
825
+ /** A US-listed fund's facts (loadReportFundFacts): the note points to `fn ETF` only when they cover it. */
826
+ fundFacts?: FundFactsPayload | null;
819
827
  /** `fundamentals --tax-rate`: the tax-equivalent yield and muni/Treasury ratio. */
820
828
  taxEquivalent?: TaxEquivalentAnalysis | null;
821
829
  /** The filings behind the latest statements, and a newer quarter's results release. */
@@ -839,20 +847,25 @@ function statementFilingsNotes(report: StatementFilingsReport | null | undefined
839
847
  }
840
848
 
841
849
  /**
842
- * Where a fund's facts are: for a US listing, the ETF function (fee, NAV,
843
- * premium, net assets); a fund listed elsewhere has none yet. Only for a
844
- * fund: a stock's report says nothing of it.
850
+ * Where a fund's facts are: for a US listing, the ETF function, naming the
851
+ * figures it has (fee, NAV, premium, net assets), and only when it has some:
852
+ * a fund it has none for, or whose facts could not be read, gets no pointer
853
+ * to a pane that would only say why. A fund listed elsewhere has none yet.
854
+ * Only for a fund: a stock's report says nothing of it.
845
855
  */
846
856
  function fundFactsNote(
847
857
  symbol: string,
848
858
  exchange: string | undefined,
849
859
  financials: Pick<TickerFinancials, "quote" | "quoteMetadata">,
850
860
  tickerFile: TickerRecord | null = null,
861
+ facts: FundFactsPayload | null = null,
851
862
  ): string | null {
852
863
  if (resolveTickerInstrumentKind(tickerFile, financials) !== "fund") return null;
853
- return !exchange || isUsListingExchange(exchange)
854
- ? `Fund facts (fee, NAV, premium, net assets): fn ETF ${symbol}.`
855
- : US_FUND_FACTS_ONLY;
864
+ if (exchange && !isUsListingExchange(exchange)) return US_FUND_FACTS_ONLY;
865
+ if (!facts?.covered) return null;
866
+ const figures = [facts.expenseRatio && "fee", facts.nav && "NAV", facts.premiumDiscount && "premium", facts.netAssets && "net assets"]
867
+ .filter((figure): figure is string => !!figure);
868
+ return `Fund facts${figures.length ? ` (${figures.join(", ")})` : ""}: fn ETF ${symbol}.`;
856
869
  }
857
870
 
858
871
  /**
@@ -947,7 +960,7 @@ export function renderFundamentalsReport(
947
960
  const before = lines.length;
948
961
  const metrics = fundamentalsReportMetrics(financials, view);
949
962
  appendMetricSection(lines, view === "valuation" ? "Valuation" : "Fundamentals", metricLines(metrics));
950
- const fund = fundFactsNote(symbol, financials.exchange || quote?.exchangeName, financials);
963
+ const fund = fundFactsNote(symbol, financials.exchange || quote?.exchangeName, financials, null, financials.fundFacts);
951
964
  if (lines.length === before) lines.push("", cliStyles.muted([`No ${view} reported for ${financials.symbol}.`, fund].filter(Boolean).join(" ")));
952
965
  else if (fund) lines.push(cliStyles.muted(fund));
953
966
  for (const note of fundamentalsNotes(atRegularSessionPrice(financials), metrics, quote?.currency)) lines.push(cliStyles.muted(note));
@@ -987,7 +1000,8 @@ export function fundamentalsReportTables(
987
1000
  freshness: ReportFreshness,
988
1001
  ): CliReportTables {
989
1002
  const metrics = fundamentalsReportMetrics(financials, view);
990
- const fund = fundFactsNote(financials.quote?.symbol ?? financials.symbol, financials.exchange || financials.quote?.exchangeName, financials);
1003
+ const fund = fundFactsNote(financials.quote?.symbol ?? financials.symbol, financials.exchange || financials.quote?.exchangeName, financials, null,
1004
+ financials.fundFacts);
991
1005
  const tables = [exportEntriesTable(
992
1006
  view === "valuation" ? "Valuation" : "Fundamentals",
993
1007
  metrics.map((metric) => ({ label: metric.label, value: metric.value, formatted: metric.text, unit: metric.unit })),
@@ -1064,6 +1078,7 @@ export async function buildTickerReport({
1064
1078
  distributions = null,
1065
1079
  statementFilings = null,
1066
1080
  bondFund = null,
1081
+ fundFacts = null,
1067
1082
  memberships,
1068
1083
  offerings = [],
1069
1084
  }: {
@@ -1074,8 +1089,10 @@ export async function buildTickerReport({
1074
1089
  statementFilings?: StatementFilingsReport | null;
1075
1090
  /** The company's latest offering prospectuses (424B5, 424B4); those after the latest balance sheet get a note. */
1076
1091
  offerings?: readonly OfferingFiling[];
1077
- /** A bond fund's SEC yield, yield to worst and duration (loadReportBondFacts). */
1092
+ /** A bond fund's SEC yield, yield to worst and duration (loadReportFundFacts). */
1078
1093
  bondFund?: FundBondFacts | null;
1094
+ /** A US-listed fund's facts (loadReportFundFacts): the report points to `fn ETF` only when they cover it. */
1095
+ fundFacts?: FundFactsPayload | null;
1079
1096
  /** The distributions a dividend yield is read with (loadDividendDistributions). */
1080
1097
  distributions?: DividendDistributions | null;
1081
1098
  /** The exchange the command named, as a canonical code. */
@@ -1091,7 +1108,7 @@ export async function buildTickerReport({
1091
1108
  notes?: string;
1092
1109
  /** Why there is no quote, when a source said; "Quote unavailable." otherwise. */
1093
1110
  quoteNote?: string;
1094
- /** "CAR = Avis Budget Group, NASDAQ (also XETRA, ASX)", for a bare symbol several companies list. */
1111
+ /** "CAR = Avis Budget Group, NASDAQ (different companies on XETRA, ASX)", for a bare symbol several companies list. */
1095
1112
  listingNote?: string;
1096
1113
  recentNews?: NewsArticle[];
1097
1114
  recentSecFilings?: SecFilingItem[];
@@ -1254,7 +1271,8 @@ export async function buildTickerReport({
1254
1271
  if (lines.length > beforeFundamentals) for (const note of conversionNotes(toBase, convertedCurrencies, rates)) lines.push(cliStyles.muted(note));
1255
1272
  // Where a fund's facts would go: under its fundamentals, or in their place.
1256
1273
  const fund = fundFactsNote(quote?.symbol ?? symbol,
1257
- quote?.tradingExchangeName || listingExchange || quote?.listingExchangeName || quote?.exchangeName || tickerFile?.metadata.exchange, financials, tickerFile);
1274
+ quote?.tradingExchangeName || listingExchange || quote?.listingExchangeName || quote?.exchangeName || tickerFile?.metadata.exchange, financials, tickerFile,
1275
+ fundFacts);
1258
1276
  if (fund) lines.push(...lines.length === beforeFundamentals ? [""] : [], cliStyles.muted(fund));
1259
1277
 
1260
1278
  if (capitalization?.provenance.kind === "fundamentals") {
@@ -1535,10 +1553,11 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
1535
1553
  // The yield's distributions, read beside the rest when a trailing yield, a forward basis or its lapse needs them; they never fail the report.
1536
1554
  const distributions = loadDividendDistributions(dataProvider, { symbol: requestSymbol, exchange }, resolvedFinancials,
1537
1555
  { declared: !dependencies.printResult });
1538
- // A bond fund's SEC yield, yield to worst and duration, beside its distribution yield.
1539
- const bondFund = loadReportBondFacts(requestSymbol,
1556
+ // A fund's facts: its bond figures beside its distribution yield, and whether fn ETF has any to point to.
1557
+ const fundFacts = loadReportFundFacts(requestSymbol,
1540
1558
  quote?.tradingExchangeName || exchange || quote?.listingExchangeName || quote?.exchangeName || tickerFile?.metadata.exchange,
1541
1559
  resolvedFinancials, tickerFile);
1560
+ const bondFund = fundFacts.then((facts) => facts?.bond ?? null);
1542
1561
  const statementFilings = loadStatementFilings(statementSources, { symbol: requestSymbol, exchange }, {
1543
1562
  quarter: latestFinancialPeriod(resolvedFinancials.quarterlyStatements, (row) => row.date),
1544
1563
  annual: latestFinancialPeriod(resolvedFinancials.annualStatements, (row) => row.date),
@@ -1589,17 +1608,18 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
1589
1608
  // The quote is the feed in this report; without one, the fundamentals date it.
1590
1609
  const freshness = quote ? quotesFreshness([quote]) : fundamentalsFreshness(resolvedFinancials);
1591
1610
  const quoteNote = quote ? undefined : await quoteUnavailableNote(dataProvider, requestSymbol, exchange);
1592
- const [choiceNote] = await listingChoiceNotes(listing, { store, dataProvider }, quote, venues);
1593
- // A bare coin ticker read as a fund says where the coin is, as `quote` does; a named listing was meant.
1594
- const coinNote = listing.exchange ? null
1595
- : notTheCoinNote(listing.symbol, quote ?? (tickerFile ? { name: tickerFile.metadata.name, instrumentType: tickerFile.metadata.assetCategory } : null));
1596
- const listingNote = [choiceNote, coinNote].filter((note): note is string => !!note).join("\n") || undefined;
1611
+ // A bare coin ticker read as another listing says where the coin is, as `quote` does: from the quote, else from the saved listing.
1612
+ const choiceNotes = [
1613
+ ...await listingChoiceNotes(listing, { store, dataProvider }, quote, venues),
1614
+ ...quote || !tickerFile ? [] : coinListingNotes(listing, { name: tickerFile.metadata.name, instrumentType: tickerFile.metadata.assetCategory }),
1615
+ ];
1616
+ const listingNote = choiceNotes.join("\n") || undefined;
1597
1617
  const memberships = await membershipsLoad;
1598
1618
  // A converted TTM block at the rate the rest of the report converts at, the one `fx` reports.
1599
1619
  const shownFinancials = await atReportTtmRate(resolvedFinancials, toBase);
1600
1620
  if (dependencies.printResult) {
1601
1621
  const newsNote = recentNews[0]?.searchedFor ? coinNewsFallbackNotice(requestSymbol, recentNews[0].searchedFor) : null;
1602
- const warnings = [choiceNote, coinNote, quoteNote, newsNote].filter((note): note is string => !!note);
1622
+ const warnings = [...choiceNotes, quoteNote, newsNote].filter((note): note is string => !!note);
1603
1623
  dependencies.printResult({
1604
1624
  freshness,
1605
1625
  warnings: warnings.length > 0 ? warnings : undefined,
@@ -1643,6 +1663,7 @@ export async function ticker(symbol: string, dependencies: TickerCommandDependen
1643
1663
  distributions: await distributions,
1644
1664
  statementFilings: await statementFilings,
1645
1665
  bondFund: await bondFund,
1666
+ fundFacts: await fundFacts,
1646
1667
  memberships,
1647
1668
  offerings: await offerings,
1648
1669
  }));
@@ -0,0 +1,41 @@
1
+ import type { CliListing } from "./listing-arg";
2
+ import { companyNameSuggestion, isUnsettledNoProvider, type ListingDeps } from "./listing-arg";
3
+ import { describeSuggestion } from "./listing-suggestion";
4
+ import { isNotATickerMessage, withListingSuggestion } from "./not-a-ticker";
5
+
6
+ type SearchDeps = Pick<ListingDeps, "dataProvider">;
7
+
8
+ /**
9
+ * "Not a ticker: PALANTIR TECHNOLOGIES. Did you mean PLTR (Palantir
10
+ * Technologies Inc.)?" for words typed where one ticker goes, with the
11
+ * company search names for them. Without one the sentence ends at the
12
+ * symbol, and the error printer adds the search to try.
13
+ */
14
+ export async function notATickerWords(typed: string, deps: SearchDeps): Promise<string> {
15
+ const symbol = typed.trim().replace(/^\$/, "").toUpperCase();
16
+ const message = `Not a ticker: ${symbol}.`;
17
+ const suggestion = await companyNameSuggestion(typed, deps);
18
+ return suggestion ? withListingSuggestion(message, describeSuggestion(suggestion)) : message;
19
+ }
20
+
21
+ /**
22
+ * Why a command stops on a listing whose own quote failed with `error`, when
23
+ * the word typed for it is no ticker, or null when it is not known to be: the
24
+ * source's "Not a ticker: PALANTIR.", and a word a source could not answer for
25
+ * (a timeout, a rate limit) that search resolves to a company all the same, as
26
+ * `quote` says it of a name. Both name the company beside it. A real ticker a
27
+ * source failed to quote is not stopped here: it is not a company's name.
28
+ */
29
+ export async function notATickerStop(
30
+ error: unknown,
31
+ listing: Pick<CliListing, "symbol" | "exchange" | "typed">,
32
+ deps: SearchDeps,
33
+ ): Promise<string | null> {
34
+ const message = error instanceof Error ? error.message : null;
35
+ const notATicker = isNotATickerMessage(message);
36
+ if (!notATicker && !isUnsettledNoProvider(error)) return null;
37
+ if (listing.exchange || !listing.typed) return notATicker ? message : null;
38
+ const suggestion = await companyNameSuggestion(listing.typed, deps);
39
+ if (!suggestion) return notATicker ? message : null;
40
+ return withListingSuggestion(notATicker ? message! : `Not a ticker: ${listing.symbol}.`, describeSuggestion(suggestion));
41
+ }
@@ -43,7 +43,7 @@ export async function noListingCalendarNote(
43
43
  }
44
44
 
45
45
  /**
46
- * "SAN = Banco Santander, NYSE (also EPA, LSE)" for a bare symbol whose
46
+ * "SAN = Banco Santander, NYSE (different companies on EPA, LSE)" for a bare symbol whose
47
47
  * calendar date is its US line's, when several companies list the symbol:
48
48
  * the event names the company the date is for.
49
49
  */
package/src/cli/errors.ts CHANGED
@@ -10,6 +10,8 @@ import { DATA_SERVICE_UNREACHABLE_MESSAGE, isNetworkFailure } from "../utils/net
10
10
  const USAGE_PREFIX = "Usage: ";
11
11
  /** The code of every error a command fails with: bad input, nothing found, an option it does not take. */
12
12
  export const CLI_ERROR_CODE = "cli_error";
13
+ /** The code of a command that ran, and the data service has nothing for what it asked. */
14
+ export const NO_DATA_CODE = "no_data";
13
15
  /** The code of a command whose requests never reached the data service. */
14
16
  export const SERVICE_UNREACHABLE_CODE = "service_unreachable";
15
17
 
@@ -61,6 +63,12 @@ export function failWithFields(message: string, code: string, fields: Record<str
61
63
  throw new CliFailure(message, undefined, code, undefined, fields);
62
64
  }
63
65
 
66
+ /** Fails as `error` says: its code, message, details and whether to retry, and any field it carries beside them. */
67
+ export function failWithError(error: CliErrorObject): never {
68
+ const { code, message, details, retryable, ...fields } = error;
69
+ throw new CliFailure(message, details, code, retryable, Object.keys(fields).length > 0 ? fields : undefined);
70
+ }
71
+
64
72
  /** Fails with what asking again shortly may get past (a busy service): `--json` marks it `retryable`. */
65
73
  export function failRetryable(message: string, code: string): never {
66
74
  throw new CliFailure(message, undefined, code, true);
@@ -714,7 +714,30 @@ export const GLOSSARY: readonly GlossaryEntry[] = [
714
714
  },
715
715
  {
716
716
  term: "Other reportables", aliases: ["other reportable", "other reportable traders"], functions: ["COT"], shown: ["Other Reportables"],
717
- definition: "The disaggregated report's large traders that fit none of its other classes, such as proprietary traders and family offices.",
717
+ definition: "The disaggregated and TFF reports' large traders that fit none of their other classes, such as proprietary traders and family offices.",
718
+ },
719
+ // The Traders in Financial Futures report's classes, and the options it can count.
720
+ {
721
+ term: "Traders in Financial Futures", short: "TFF", aliases: ["tff report", "financial futures report", "financial traders report"], functions: ["COT"], shown: ["Financial (TFF)"],
722
+ definition: "The CFTC's report on financial futures, such as rates, currencies, stock indexes and VIX. "
723
+ + "It splits large traders into dealers, asset managers, leveraged funds and other reportables.",
724
+ },
725
+ {
726
+ term: "Leveraged funds", aliases: ["leveraged fund", "leveraged money", "lev funds"], functions: ["COT"], shown: ["Leveraged Funds"],
727
+ definition: "The TFF report's hedge funds, CTAs and other money managers trading for clients or with borrowed money: the speculators most watched in rates, currencies and indexes.",
728
+ },
729
+ {
730
+ term: "Asset manager/institutional", aliases: ["asset manager", "asset managers", "institutional", "real money"], functions: ["COT"], shown: ["Asset Manager/Institutional"],
731
+ definition: "The TFF report's pension funds, insurers, mutual funds and endowments, investing for the long run; usually net long stock index futures.",
732
+ },
733
+ {
734
+ term: "Dealer/intermediary", aliases: ["dealer intermediary", "intermediary", "intermediaries"], functions: ["COT"], shown: ["Dealer/Intermediary"],
735
+ definition: "The TFF report's banks and dealers on the sell side, taking the other side of their clients' trades and hedging it.",
736
+ },
737
+ {
738
+ term: "Futures and options combined", aliases: ["futures + options", "futures and options", "combined report", "combined positions"], functions: ["COT"], shown: ["Futures + options"],
739
+ definition: "A COT report that adds each group's options to its futures, delta-adjusted: an option counts as the futures it moves like. "
740
+ + "Futures only, the default, counts futures alone.",
718
741
  },
719
742
  // Futures basis and curve shape (CTM, FUT, PERP).
720
743
  {
@@ -800,7 +823,7 @@ export const GLOSSARY: readonly GlossaryEntry[] = [
800
823
  term: "Risk reversal", short: "RR", aliases: ["25d risk reversal", "25-delta risk reversal", "25 delta risk reversal", "rr25", "risk reversals"],
801
824
  functions: ["OVDV"], shown: ["RR"],
802
825
  definition: "The 25-delta call's implied volatility minus the 25-delta put's, in vol points, for one expiry. Negative means puts cost more than calls "
803
- + "as far from the forward (downside protection is bid), positive that upside calls are. OVDV signs it call minus put.",
826
+ + "as far from the forward (downside protection is bid), positive that upside calls are. OVDV, VCA and HIVG's rr25d all sign it call minus put.",
804
827
  },
805
828
  {
806
829
  term: "Butterfly", short: "BF", aliases: ["25d butterfly", "25-delta butterfly", "25 delta butterfly", "bf25", "smile curvature", "fly"],