gloomberb 0.11.2 → 0.12.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 (531) hide show
  1. package/README.md +7 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +108 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +134 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +220 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/capabilities/chart-series.ts +3 -2
  20. package/src/cli/commands/market.ts +2 -2
  21. package/src/cli/commands/plugins.ts +39 -11
  22. package/src/cli/desktop-pane-shot.ts +285 -16
  23. package/src/cli/index.ts +1 -1
  24. package/src/cli/pane-functions/capabilities.ts +61 -31
  25. package/src/cli/pane-functions/catalog.ts +16 -4
  26. package/src/cli/pane-functions/discovery.ts +1 -0
  27. package/src/cli/pane-functions/headless.test.ts +247 -0
  28. package/src/cli/pane-functions/headless.ts +423 -0
  29. package/src/cli/pane-functions/index.test.ts +9 -0
  30. package/src/cli/pane-functions/index.ts +6 -2
  31. package/src/cli/pane-functions/options.ts +27 -2
  32. package/src/cli/pane-functions/report.ts +4 -0
  33. package/src/cli/pane-functions/resolver.ts +26 -8
  34. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  35. package/src/cli/pane-functions/screenshot.ts +236 -20
  36. package/src/cli/registry.ts +0 -22
  37. package/src/cli/restore-plugins.ts +34 -0
  38. package/src/cli/result.ts +7 -10
  39. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  40. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  41. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  42. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  43. package/src/components/chart/composite/composite-chart.tsx +301 -204
  44. package/src/components/chart/composite/format.ts +8 -2
  45. package/src/components/chart/composite/interactions.test.ts +68 -102
  46. package/src/components/chart/composite/interactions.ts +214 -264
  47. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  48. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  49. package/src/components/chart/composite/scene.ts +57 -3
  50. package/src/components/chart/composite/text-renderer.ts +3 -2
  51. package/src/components/chart/composite/time-scale.ts +138 -57
  52. package/src/components/chart/composite/types.ts +25 -1
  53. package/src/components/chart/core/data.ts +9 -13
  54. package/src/components/chart/core/date-window.ts +7 -1
  55. package/src/components/chart/core/pointer.ts +3 -0
  56. package/src/components/chart/core/types.ts +0 -45
  57. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  58. package/src/components/chart/native/kitty/index.test.ts +0 -188
  59. package/src/components/chart/native/raster/crosshair.ts +6 -5
  60. package/src/components/chart/native/raster/primitives.ts +0 -23
  61. package/src/components/chart/native/renderer-selection.ts +81 -31
  62. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  63. package/src/components/chart/static/chart-surface.tsx +150 -0
  64. package/src/components/chart/static/index.ts +8 -0
  65. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  66. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  67. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  68. package/src/components/chart/static/series.ts +49 -0
  69. package/src/components/command-bar/action-runtime.ts +1 -3
  70. package/src/components/command-bar/assist/model.test.ts +17 -2
  71. package/src/components/command-bar/assist/model.ts +40 -9
  72. package/src/components/command-bar/commands/direct/index.ts +1 -1
  73. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  74. package/src/components/command-bar/commands/registry.ts +0 -11
  75. package/src/components/command-bar/helpers.test.ts +2 -2
  76. package/src/components/command-bar/helpers.ts +2 -6
  77. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  78. package/src/components/command-bar/list/badge.test.ts +54 -0
  79. package/src/components/command-bar/list/badge.ts +62 -0
  80. package/src/components/command-bar/list/model.test.ts +103 -0
  81. package/src/components/command-bar/list/model.ts +61 -4
  82. package/src/components/command-bar/list/view.test.tsx +113 -0
  83. package/src/components/command-bar/list/view.tsx +147 -131
  84. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  85. package/src/components/command-bar/panel/body.tsx +51 -65
  86. package/src/components/command-bar/panel/index.tsx +48 -13
  87. package/src/components/command-bar/panel/layout.test.ts +139 -0
  88. package/src/components/command-bar/panel/layout.ts +65 -28
  89. package/src/components/command-bar/panel/native-surface.ts +24 -0
  90. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  91. package/src/components/command-bar/panel/runtime.ts +6 -3
  92. package/src/components/command-bar/panel/state.ts +41 -7
  93. package/src/components/command-bar/panel/types.ts +7 -1
  94. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  95. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  96. package/src/components/command-bar/routes/root/results.ts +30 -28
  97. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  98. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  99. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  100. package/src/components/command-bar/routes/root/selection.ts +1 -12
  101. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  102. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  103. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  104. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  105. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  106. package/src/components/command-bar/routing/actions.ts +1 -81
  107. package/src/components/command-bar/routing/launch-request.ts +1 -1
  108. package/src/components/command-bar/routing/list-state.ts +6 -21
  109. package/src/components/command-bar/selection-runtime.ts +1 -1
  110. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  111. package/src/components/command-bar/surface/environment.ts +8 -1
  112. package/src/components/command-bar/surface/index.test.tsx +84 -68
  113. package/src/components/command-bar/surface/index.tsx +33 -36
  114. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  115. package/src/components/command-bar/theme-picker.tsx +43 -15
  116. package/src/components/command-bar/view-model.test.ts +53 -8
  117. package/src/components/command-bar/view-model.ts +43 -14
  118. package/src/components/command-bar/workflow/tickers.ts +1 -0
  119. package/src/components/command-bar/workflow/types.ts +1 -1
  120. package/src/components/data-table/export.test.ts +21 -0
  121. package/src/components/data-table/export.ts +21 -0
  122. package/src/components/data-table/view.test.tsx +45 -6
  123. package/src/components/data-table/view.tsx +55 -9
  124. package/src/components/index.ts +10 -2
  125. package/src/components/input-search-bar.test.tsx +36 -2
  126. package/src/components/input-search-bar.tsx +18 -1
  127. package/src/components/layout/header.test.tsx +57 -10
  128. package/src/components/layout/header.tsx +292 -149
  129. package/src/components/layout/market-summary.test.ts +81 -0
  130. package/src/components/layout/market-summary.ts +109 -0
  131. package/src/components/layout/shell/chrome.ts +96 -1
  132. package/src/components/layout/shell/index.tsx +11 -3
  133. package/src/components/layout/shell/menu.ts +9 -0
  134. package/src/components/layout/shell/pane/actions.ts +29 -0
  135. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  136. package/src/components/layout/shell/shortcuts.ts +3 -0
  137. package/src/components/layout/status-bar.test.tsx +9 -8
  138. package/src/components/layout/status-bar.tsx +85 -54
  139. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  140. package/src/components/price-sparkline/index.ts +61 -34
  141. package/src/components/price-sparkline/view.tsx +1 -1
  142. package/src/components/sign-in-gate.test.tsx +128 -0
  143. package/src/components/sign-in-gate.tsx +149 -0
  144. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  145. package/src/components/ticker/badge/list.tsx +17 -3
  146. package/src/components/ticker/list-table-view.tsx +6 -3
  147. package/src/components/ui/data-table/index.tsx +22 -4
  148. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  149. package/src/components/ui/frame.tsx +28 -0
  150. package/src/components/ui/index.ts +3 -0
  151. package/src/components/ui/native-select.tsx +20 -10
  152. package/src/components/ui/select-button.tsx +138 -0
  153. package/src/components/ui/status.tsx +2 -2
  154. package/src/components/ui/table-layout.ts +1 -1
  155. package/src/components/use-double-click-activation.ts +7 -3
  156. package/src/core/app-services.ts +3 -3
  157. package/src/data/config/chart-settings.ts +4 -4
  158. package/src/data/config/layout.ts +12 -6
  159. package/src/data/config/store/index.test.ts +97 -0
  160. package/src/data/config/store/migrations.ts +41 -0
  161. package/src/data/config/store/normalize.ts +5 -3
  162. package/src/i18n/es.ts +6 -2
  163. package/src/i18n/ja.ts +6 -2
  164. package/src/i18n/ko.ts +6 -2
  165. package/src/i18n/zh-cn.ts +6 -2
  166. package/src/i18n/zh-tw.ts +6 -2
  167. package/src/layout-marketplace/gallery-terminal.tsx +126 -96
  168. package/src/layout-marketplace/gallery.test.tsx +65 -1
  169. package/src/layout-marketplace/pane-imagery.ts +1 -0
  170. package/src/layout-marketplace/payload.test.ts +17 -0
  171. package/src/layout-marketplace/payload.ts +40 -5
  172. package/src/market-data/coordinator/index.ts +3 -2
  173. package/src/market-data/coordinator/quotes.ts +173 -66
  174. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  175. package/src/pane-settings.ts +1 -1
  176. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  177. package/src/plugins/builtin/analytics/view.tsx +1 -1
  178. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  179. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  180. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  181. package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
  182. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  183. package/src/plugins/builtin/chart-composer/settings.ts +2 -13
  184. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  185. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  186. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  188. package/src/plugins/builtin/composite-plugins.ts +16 -1
  189. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  190. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  191. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  192. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  193. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  194. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  195. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  196. package/src/plugins/builtin/earnings/index.tsx +1 -0
  197. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  198. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  199. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  200. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  201. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  202. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  203. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  204. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  205. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  206. package/src/plugins/builtin/econ/index.tsx +3 -1
  207. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  208. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  209. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  210. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  211. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  212. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  213. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  214. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  215. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  216. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  217. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  218. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  219. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  220. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  221. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  222. package/src/plugins/builtin/futures/index.tsx +1 -0
  223. package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
  224. package/src/plugins/builtin/help/index.tsx +4 -0
  225. package/src/plugins/builtin/holders/index.tsx +1 -0
  226. package/src/plugins/builtin/insider/index.tsx +1 -0
  227. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
  228. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  229. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  230. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  231. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  232. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  233. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  234. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  235. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  236. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  237. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  238. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  239. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  240. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  241. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  242. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  243. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  244. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  245. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  246. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  247. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  248. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  249. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  250. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  251. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  252. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  253. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  254. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  255. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  256. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  257. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  258. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  259. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  260. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  261. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  262. package/src/plugins/builtin/options/analytics.test.ts +83 -0
  263. package/src/plugins/builtin/options/analytics.ts +110 -0
  264. package/src/plugins/builtin/options/index.tsx +18 -2
  265. package/src/plugins/builtin/options/table.ts +121 -51
  266. package/src/plugins/builtin/options/types.ts +22 -15
  267. package/src/plugins/builtin/options/view.test.tsx +53 -1
  268. package/src/plugins/builtin/options/view.tsx +92 -41
  269. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  270. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  271. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  272. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  273. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  274. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  275. package/src/plugins/builtin/plugin-module.test.ts +2 -4
  276. package/src/plugins/builtin/plugin-module.ts +9 -8
  277. package/src/plugins/builtin/portfolio-list/index.tsx +1 -0
  278. package/src/plugins/builtin/research/index.tsx +4 -0
  279. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  280. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  281. package/src/plugins/builtin/research-search/data.ts +109 -0
  282. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  283. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  284. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  285. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  286. package/src/plugins/builtin/research-search/index.tsx +85 -0
  287. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  288. package/src/plugins/builtin/research-search/model.ts +291 -0
  289. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  290. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  291. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  292. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  293. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  294. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  295. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  296. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  297. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  298. package/src/plugins/builtin/scanner/index.ts +2 -0
  299. package/src/plugins/builtin/sec/index.tsx +1 -0
  300. package/src/plugins/builtin/sectors/index.tsx +1 -0
  301. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  302. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  303. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  304. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  305. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +3 -5
  306. package/src/plugins/builtin/ticker-detail/index.tsx +2 -14
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  308. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  309. package/src/plugins/builtin/volatility/index.tsx +1 -1
  310. package/src/plugins/builtin/world-indices/index.tsx +1 -0
  311. package/src/plugins/builtin/world-venue-map/index.ts +27 -0
  312. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  313. package/src/plugins/builtin/world-venue-map/map.tsx +481 -0
  314. package/src/plugins/builtin/world-venue-map/model.test.ts +85 -0
  315. package/src/plugins/builtin/world-venue-map/model.ts +268 -0
  316. package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
  317. package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
  318. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  319. package/src/plugins/bundle.test.ts +109 -0
  320. package/src/plugins/bundle.ts +139 -0
  321. package/src/plugins/catalog-backend.ts +6 -44
  322. package/src/plugins/catalog-browser.test.ts +1 -0
  323. package/src/plugins/catalog-browser.ts +12 -0
  324. package/src/plugins/catalog-manifest.test.ts +43 -0
  325. package/src/plugins/catalog-manifest.ts +92 -0
  326. package/src/plugins/catalog-ui.ts +20 -4
  327. package/src/plugins/catalog.ts +9 -2
  328. package/src/plugins/current-target.ts +28 -0
  329. package/src/plugins/host-contract.ts +37 -0
  330. package/src/plugins/host-link.ts +158 -0
  331. package/src/plugins/host-modules.ts +61 -0
  332. package/src/plugins/loader.ts +41 -22
  333. package/src/plugins/ownership.ts +1 -0
  334. package/src/plugins/prediction-markets/index.tsx +1 -0
  335. package/src/plugins/registry/context.ts +3 -0
  336. package/src/plugins/registry/contributions.ts +27 -0
  337. package/src/plugins/registry/index.test.ts +25 -0
  338. package/src/plugins/registry/index.ts +8 -0
  339. package/src/plugins/registry/pane-settings.test.ts +53 -0
  340. package/src/plugins/registry/pane-settings.ts +29 -3
  341. package/src/plugins/seed.test.ts +83 -0
  342. package/src/plugins/seed.ts +80 -0
  343. package/src/public/broker.ts +31 -0
  344. package/src/public/market-data.ts +9 -0
  345. package/src/public/pane-hooks.ts +57 -0
  346. package/src/public/public-api.test.ts +285 -0
  347. package/src/public/react.ts +62 -0
  348. package/src/public/test-support.ts +23 -0
  349. package/src/public/time-series.ts +8 -0
  350. package/src/public/utils.ts +77 -0
  351. package/src/remote/layout-helpers.ts +6 -8
  352. package/src/remote/resources.ts +2 -0
  353. package/src/remote/schema.ts +1 -0
  354. package/src/remote/semantic-tree.test.tsx +105 -0
  355. package/src/remote/semantic-tree.tsx +53 -35
  356. package/src/renderers/browser/main.tsx +6 -0
  357. package/src/renderers/browser/ui-host.tsx +11 -0
  358. package/src/renderers/cloudflare/worker.test.ts +19 -0
  359. package/src/renderers/cloudflare/worker.ts +1 -1
  360. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
  361. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  362. package/src/renderers/electrobun/bun/index.ts +6 -2
  363. package/src/renderers/electrobun/shared/protocol.ts +25 -0
  364. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  365. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  366. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  367. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  368. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  369. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  370. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  371. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  372. package/src/renderers/electrobun/view/main.tsx +27 -2
  373. package/src/renderers/electrobun/view/styles.css +5 -0
  374. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  375. package/src/renderers/electrobun/view/ui-host.tsx +3 -0
  376. package/src/renderers/opentui/chart-surface.tsx +5 -1
  377. package/src/renderers/opentui/host.tsx +20 -2
  378. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  379. package/src/renderers/opentui/interaction-performance.ts +188 -0
  380. package/src/renderers/opentui/start.tsx +26 -1
  381. package/src/renderers/opentui/test-utils.tsx +13 -0
  382. package/src/renderers/opentui/toast-host.tsx +26 -2
  383. package/src/renderers/opentui/ui-host.tsx +1 -0
  384. package/src/sources/provider-router/search.test.ts +191 -0
  385. package/src/sources/provider-router/search.ts +92 -39
  386. package/src/state/app/context/index.tsx +2 -0
  387. package/src/state/app/context/selector.test.tsx +33 -1
  388. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  389. package/src/state/hooks/quote-streaming.ts +106 -30
  390. package/src/state/pane-table-export-registry.ts +50 -0
  391. package/src/theme/color-utils.ts +1 -1
  392. package/src/theme/colors.ts +50 -0
  393. package/src/theme/themes.test.ts +15 -2
  394. package/src/theme/themes.ts +19 -1
  395. package/src/tickers/search/index.test.ts +26 -0
  396. package/src/tickers/search/index.ts +42 -17
  397. package/src/tickers/search/ranking.ts +14 -0
  398. package/src/time-series/chart-data.test.ts +216 -0
  399. package/src/time-series/range.ts +12 -0
  400. package/src/time-series/resolution.test.ts +25 -26
  401. package/src/time-series/resolution.ts +44 -25
  402. package/src/time-series/resolve.ts +7 -0
  403. package/src/time-series/spec.ts +5 -5
  404. package/src/time-series/types.ts +3 -1
  405. package/src/time-series/use-chart-resolution.ts +5 -0
  406. package/src/types/config.ts +123 -3
  407. package/src/types/data-provider.ts +11 -0
  408. package/src/types/headless.ts +161 -0
  409. package/src/types/plugin.ts +104 -20
  410. package/src/ui/host.tsx +19 -1
  411. package/src/ui/index.tsx +1 -0
  412. package/src/ui/toast.tsx +8 -4
  413. package/src/utils/csv.test.ts +13 -0
  414. package/src/utils/csv.ts +37 -0
  415. package/src/utils/format.ts +35 -0
  416. package/src/utils/fuzzy-search.test.ts +94 -0
  417. package/src/utils/fuzzy-search.ts +81 -22
  418. package/src/utils/save-text-file.test.ts +24 -0
  419. package/src/utils/save-text-file.ts +36 -0
  420. package/src/utils/shortcut-labels.ts +14 -0
  421. package/src/components/chart/core/axis-format.ts +0 -157
  422. package/src/components/chart/core/index.test.ts +0 -1075
  423. package/src/components/chart/core/projection.ts +0 -357
  424. package/src/components/chart/core/render-utils.ts +0 -4
  425. package/src/components/chart/core/renderer.ts +0 -244
  426. package/src/components/chart/core/scene.ts +0 -180
  427. package/src/components/chart/core/time-axis.ts +0 -330
  428. package/src/components/chart/cursor-motion.ts +0 -47
  429. package/src/components/chart/indicators/types.ts +0 -26
  430. package/src/components/chart/market-session.ts +0 -176
  431. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  432. package/src/components/chart/multi-line/renderer.ts +0 -161
  433. package/src/components/chart/multi-line/scene.ts +0 -188
  434. package/src/components/chart/native/raster/layout.ts +0 -129
  435. package/src/components/chart/native/raster/price-chart.ts +0 -244
  436. package/src/components/chart/rendering/chart-draw.ts +0 -270
  437. package/src/components/chart/rendering/geometry.ts +0 -95
  438. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  439. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  440. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  441. package/src/components/chart/static/chart/surface.tsx +0 -358
  442. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  443. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  444. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  445. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  446. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  447. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  448. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  449. package/src/plugins/builtin/substack/api/store.ts +0 -186
  450. package/src/plugins/builtin/substack/api/types.ts +0 -42
  451. package/src/plugins/builtin/substack/api.test.ts +0 -298
  452. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  453. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  454. package/src/plugins/builtin/substack/content.ts +0 -478
  455. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  456. package/src/plugins/builtin/substack/index.tsx +0 -49
  457. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  458. package/src/plugins/builtin/substack/model.test.ts +0 -155
  459. package/src/plugins/builtin/substack/normalize.ts +0 -343
  460. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  461. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  462. package/src/plugins/builtin/substack/pane.tsx +0 -547
  463. package/src/plugins/builtin/substack/read-state.ts +0 -30
  464. package/src/plugins/builtin/substack/table.ts +0 -111
  465. package/src/plugins/builtin/substack/types.ts +0 -71
  466. package/src/plugins/builtin/substack/utils.ts +0 -131
  467. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  468. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  469. package/src/plugins/ibkr/account-cache.ts +0 -24
  470. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  471. package/src/plugins/ibkr/config.test.ts +0 -79
  472. package/src/plugins/ibkr/config.ts +0 -250
  473. package/src/plugins/ibkr/dialogs.tsx +0 -40
  474. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  475. package/src/plugins/ibkr/flex/index.ts +0 -367
  476. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  477. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  478. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  479. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  480. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  481. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  482. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  483. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  484. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  485. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  486. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  487. package/src/plugins/ibkr/gateway/history.ts +0 -120
  488. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  489. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  490. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  491. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  492. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  493. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  494. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  495. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  496. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  497. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  498. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  499. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  500. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  501. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  502. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  503. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  504. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  505. package/src/plugins/ibkr/gateway/types.ts +0 -27
  506. package/src/plugins/ibkr/index.test.tsx +0 -162
  507. package/src/plugins/ibkr/index.tsx +0 -159
  508. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  509. package/src/plugins/ibkr/instance-selection.ts +0 -36
  510. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  511. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  512. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  513. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  514. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  515. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  516. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  517. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  518. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  519. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  520. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  521. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  522. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  523. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  524. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  525. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  526. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  527. package/src/plugins/ibkr/trade/utils.ts +0 -144
  528. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  529. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  530. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  531. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -1,235 +0,0 @@
1
- import { httpFetch } from "../../../utils/http-transport";
2
- import type { FlexQueryConfig } from "../config";
3
- import { IBKR_STATEMENT_URL } from "../config";
4
-
5
- const IBKR_STATEMENT_GET_URL = "https://ndcdyn.interactivebrokers.com/AccountManagement/FlexWebService/GetStatement";
6
- const FLEX_USER_AGENT = "Gloomberb/1.0 Flex";
7
- const FLEX_STATEMENT_CACHE_MS = 15 * 60_000;
8
- const FLEX_STATEMENT_INITIAL_WAIT_MS = 3_000;
9
- const FLEX_STATEMENT_POLL_DELAY_MS = 5_000;
10
- const FLEX_STATEMENT_MAX_DOWNLOAD_ATTEMPTS = 60;
11
- const flexStatementCache = new Map<string, { createdAt: number; promise: Promise<string> }>();
12
-
13
- type FlexRequestPhase = "request" | "download";
14
-
15
- interface FlexErrorContext {
16
- phase: FlexRequestPhase;
17
- endpoint?: string;
18
- queryId?: string;
19
- token?: string;
20
- referenceCode?: string;
21
- httpStatus?: number;
22
- }
23
-
24
- function decodeXmlText(value: string): string {
25
- return value
26
- .replace(/&amp;/g, "&")
27
- .replace(/&lt;/g, "<")
28
- .replace(/&gt;/g, ">")
29
- .replace(/&quot;/g, "\"")
30
- .replace(/&apos;/g, "'");
31
- }
32
-
33
- function extractXmlTag(text: string, tagName: string): string | null {
34
- const match = text.match(new RegExp(`<${tagName}>\\s*([^<]*?)\\s*<\\/${tagName}>`, "i"));
35
- return match?.[1] ? decodeXmlText(match[1].trim()) : null;
36
- }
37
-
38
- function extractFlexErrorMessage(text: string): string | null {
39
- const message = extractXmlTag(text, "ErrorMessage");
40
- const code = extractXmlTag(text, "ErrorCode");
41
- if (message && code) return `IBKR error ${code}: ${message}`;
42
- return message;
43
- }
44
-
45
- function buildFlexUrl(endpoint: string, params: Record<string, string>): string {
46
- const url = new URL(endpoint);
47
- for (const [key, value] of Object.entries(params)) {
48
- url.searchParams.set(key, value);
49
- }
50
- return url.toString();
51
- }
52
-
53
- function resolveFlexGetEndpoint(sendEndpoint: string | undefined): string {
54
- if (!sendEndpoint || sendEndpoint === IBKR_STATEMENT_URL) return IBKR_STATEMENT_GET_URL;
55
-
56
- try {
57
- const url = new URL(sendEndpoint);
58
- url.search = "";
59
- if (url.pathname.endsWith("/SendRequest")) {
60
- url.pathname = url.pathname.replace(/\/SendRequest$/, "/GetStatement");
61
- return url.toString();
62
- }
63
- if (url.pathname.endsWith("FlexStatementService.SendRequest")) {
64
- url.pathname = url.pathname.replace(/FlexStatementService\.SendRequest$/, "FlexStatementService.GetStatement");
65
- return url.toString();
66
- }
67
- } catch {
68
- return IBKR_STATEMENT_GET_URL;
69
- }
70
-
71
- return IBKR_STATEMENT_GET_URL;
72
- }
73
-
74
- function flexRequestInit(): RequestInit {
75
- return {
76
- headers: {
77
- "User-Agent": FLEX_USER_AGENT,
78
- },
79
- signal: AbortSignal.timeout(30_000),
80
- };
81
- }
82
-
83
- function endpointName(endpoint: string | undefined): string {
84
- if (!endpoint) return "default";
85
- try {
86
- const url = new URL(endpoint);
87
- return url.pathname.split("/").filter(Boolean).at(-1) || url.hostname;
88
- } catch {
89
- return endpoint.split("?")[0]?.split("/").filter(Boolean).at(-1) || endpoint;
90
- }
91
- }
92
-
93
- function buildFlexErrorMessage(providerMessage: string, context: FlexErrorContext): string {
94
- const phase = context.phase === "request"
95
- ? "requesting the statement"
96
- : "downloading the generated statement";
97
- const details = [
98
- `Endpoint ${endpointName(context.endpoint)}`,
99
- context.queryId ? `query ID ${context.queryId}` : null,
100
- context.referenceCode ? `reference code ${context.referenceCode}` : null,
101
- context.token ? "token configured" : "token missing",
102
- context.httpStatus && context.httpStatus >= 400 ? `HTTP ${context.httpStatus}` : null,
103
- ].filter(Boolean).join(", ");
104
- const advice = context.phase === "request"
105
- ? "Check that Flex Web Service is enabled, the token is active, and the query ID belongs to that token."
106
- : "IBKR accepted the request but did not return the statement; retry shortly or check that the Flex query can generate a statement.";
107
-
108
- return `IBKR Flex request failed while ${phase}: ${providerMessage}. ${details}. ${advice}`;
109
- }
110
-
111
- function flexError(providerMessage: string, context: FlexErrorContext): Error {
112
- return new Error(buildFlexErrorMessage(providerMessage, context));
113
- }
114
-
115
- export async function requestFlexStatement(config: FlexQueryConfig): Promise<string> {
116
- const endpoint = config.endpoint || IBKR_STATEMENT_URL;
117
- const url = buildFlexUrl(endpoint, {
118
- t: config.token,
119
- q: config.queryId,
120
- v: "3",
121
- });
122
- let resp: Response;
123
- let text: string;
124
- try {
125
- resp = await httpFetch(url, flexRequestInit());
126
- text = await resp.text();
127
- } catch (error) {
128
- throw flexError(error instanceof Error ? error.message : "Network request failed", {
129
- phase: "request",
130
- endpoint,
131
- queryId: config.queryId,
132
- token: config.token,
133
- });
134
- }
135
-
136
- const referenceCode = extractXmlTag(text, "ReferenceCode");
137
- if (!referenceCode) {
138
- throw flexError(extractFlexErrorMessage(text) || "No reference code returned", {
139
- phase: "request",
140
- endpoint,
141
- queryId: config.queryId,
142
- token: config.token,
143
- httpStatus: resp.status,
144
- });
145
- }
146
-
147
- return referenceCode;
148
- }
149
-
150
- async function getFlexStatement(
151
- token: string,
152
- referenceCode: string,
153
- context: Partial<Pick<FlexQueryConfig, "endpoint" | "queryId">> = {},
154
- ): Promise<string> {
155
- await new Promise((resolve) => setTimeout(resolve, FLEX_STATEMENT_INITIAL_WAIT_MS));
156
-
157
- const endpoint = resolveFlexGetEndpoint(context.endpoint);
158
- const url = buildFlexUrl(endpoint, {
159
- t: token,
160
- q: referenceCode,
161
- v: "3",
162
- });
163
- for (let attempt = 0; attempt < FLEX_STATEMENT_MAX_DOWNLOAD_ATTEMPTS; attempt += 1) {
164
- let resp: Response;
165
- let text: string;
166
- try {
167
- resp = await httpFetch(url, flexRequestInit());
168
- text = await resp.text();
169
- } catch (error) {
170
- throw flexError(error instanceof Error ? error.message : "Network request failed", {
171
- phase: "download",
172
- endpoint,
173
- queryId: context.queryId,
174
- token,
175
- referenceCode,
176
- });
177
- }
178
-
179
- if (text.includes("<FlexQueryResponse") || text.includes("<FlexStatements")) {
180
- return text;
181
- }
182
-
183
- if (text.includes("Statement generation in progress")) {
184
- await new Promise((resolve) => setTimeout(resolve, FLEX_STATEMENT_POLL_DELAY_MS));
185
- continue;
186
- }
187
-
188
- const providerMessage = extractFlexErrorMessage(text);
189
- if (providerMessage) {
190
- throw flexError(providerMessage, {
191
- phase: "download",
192
- endpoint,
193
- queryId: context.queryId,
194
- token,
195
- referenceCode,
196
- httpStatus: resp.status,
197
- });
198
- }
199
- }
200
-
201
- throw flexError("statement generation timed out", {
202
- phase: "download",
203
- endpoint,
204
- queryId: context.queryId,
205
- token,
206
- referenceCode,
207
- });
208
- }
209
-
210
- export async function loadFlexStatement(config: FlexQueryConfig): Promise<string> {
211
- const cacheKey = `${config.endpoint || IBKR_STATEMENT_URL}|${config.token}|${config.queryId}`;
212
- const existing = flexStatementCache.get(cacheKey);
213
- if (existing && Date.now() - existing.createdAt < FLEX_STATEMENT_CACHE_MS) {
214
- return existing.promise;
215
- }
216
-
217
- const promise = (async () => {
218
- const referenceCode = await requestFlexStatement(config);
219
- return getFlexStatement(config.token, referenceCode, config);
220
- })();
221
- flexStatementCache.set(cacheKey, { createdAt: Date.now(), promise });
222
- promise.catch(() => {
223
- const cached = flexStatementCache.get(cacheKey);
224
- if (cached?.promise === promise) {
225
- flexStatementCache.delete(cacheKey);
226
- }
227
- });
228
- setTimeout(() => {
229
- const cached = flexStatementCache.get(cacheKey);
230
- if (cached?.promise === promise) {
231
- flexStatementCache.delete(cacheKey);
232
- }
233
- }, FLEX_STATEMENT_CACHE_MS);
234
- return promise;
235
- }
@@ -1,149 +0,0 @@
1
- import type { Fundamentals, FinancialStatement } from "../../types/financials";
2
-
3
- /**
4
- * Parse IBKR ReportSnapshot XML (from getFundamentalData "ReportSnapshot")
5
- * into a Fundamentals object. Uses <Ratio FieldName="...">value</Ratio> tags.
6
- */
7
- export function parseReportSnapshot(xml: string): Fundamentals {
8
- const ratios = new Map<string, number>();
9
- const ratioRegex = /<Ratio\s+FieldName="([^"]+)"[^>]*>([^<]+)<\/Ratio>/gi;
10
- let match: RegExpExecArray | null;
11
- while ((match = ratioRegex.exec(xml)) !== null) {
12
- const value = parseFloat(match[2]!);
13
- if (Number.isFinite(value)) {
14
- ratios.set(match[1]!.toUpperCase(), value);
15
- }
16
- }
17
-
18
- const get = (key: string): number | undefined => ratios.get(key.toUpperCase());
19
-
20
- const fundamentals: Fundamentals = {};
21
-
22
- const trailingPE = get("PEEXCLXOR");
23
- if (trailingPE != null) fundamentals.trailingPE = trailingPE;
24
-
25
- const forwardPE = get("APENORM");
26
- if (forwardPE != null) fundamentals.forwardPE = forwardPE;
27
-
28
- const pegRatio = get("PEGR");
29
- if (pegRatio != null) fundamentals.pegRatio = pegRatio;
30
-
31
- const eps = get("TTMEPSXCLX");
32
- if (eps != null) fundamentals.eps = eps;
33
-
34
- const dividendYield = get("YIELD");
35
- if (dividendYield != null) fundamentals.dividendYield = dividendYield / 100;
36
-
37
- const revenue = get("TTMREV");
38
- if (revenue != null) fundamentals.revenue = revenue * 1_000_000;
39
-
40
- const netIncome = get("TTMNIAC");
41
- if (netIncome != null) fundamentals.netIncome = netIncome * 1_000_000;
42
-
43
- const freeCashFlow = get("TTMFCF");
44
- if (freeCashFlow != null) fundamentals.freeCashFlow = freeCashFlow * 1_000_000;
45
-
46
- const operatingMargin = get("TTMOPMGN");
47
- if (operatingMargin != null) fundamentals.operatingMargin = operatingMargin / 100;
48
-
49
- const profitMargin = get("TTMNPMGN");
50
- if (profitMargin != null) fundamentals.profitMargin = profitMargin / 100;
51
-
52
- const enterpriseValue = get("ENTRVAL");
53
- if (enterpriseValue != null) fundamentals.enterpriseValue = enterpriseValue * 1_000_000;
54
-
55
- const revenueGrowth = get("REVCHNGYR");
56
- if (revenueGrowth != null) fundamentals.revenueGrowth = revenueGrowth / 100;
57
-
58
- const sharesOutstanding = get("SHAESSION");
59
- if (sharesOutstanding != null) fundamentals.sharesOutstanding = sharesOutstanding;
60
-
61
- return fundamentals;
62
- }
63
-
64
- /** COA code → FinancialStatement field mapping */
65
- const COA_MAP: Record<string, keyof FinancialStatement> = {
66
- SREV: "totalRevenue",
67
- SCOR: "costOfRevenue",
68
- SGRP: "grossProfit",
69
- SSGA: "sellingGeneralAndAdministration",
70
- ERAD: "researchAndDevelopment",
71
- ETOE: "operatingExpense",
72
- SOPI: "operatingIncome",
73
- STIE: "interestExpense",
74
- TTAX: "taxProvision",
75
- NINC: "netIncome",
76
- SDAJ: "eps",
77
- SDBF: "basicEps",
78
- SDWS: "dilutedShares",
79
- ATOT: "totalAssets",
80
- ATCA: "currentAssets",
81
- ACAE: "cashAndCashEquivalents",
82
- LTLL: "totalLiabilities",
83
- LTCL: "currentLiabilities",
84
- LTTD: "longTermDebt",
85
- STLD: "totalDebt",
86
- QTLE: "totalEquity",
87
- QRED: "retainedEarnings",
88
- OTLO: "operatingCashFlow",
89
- SCEX: "capitalExpenditure",
90
- ITLI: "investingCashFlow",
91
- FTLF: "financingCashFlow",
92
- FPRD: "issuanceOfDebt",
93
- FPSS: "repurchaseOfCapitalStock",
94
- FCDP: "cashDividendsPaid",
95
- };
96
-
97
- function parsePeriods(xml: string, sectionTag: string): FinancialStatement[] {
98
- const sectionRegex = new RegExp(`<${sectionTag}[^>]*>([\\s\\S]*?)<\\/${sectionTag}>`, "i");
99
- const sectionMatch = sectionRegex.exec(xml);
100
- if (!sectionMatch) return [];
101
-
102
- const section = sectionMatch[1]!;
103
- const statements: FinancialStatement[] = [];
104
-
105
- // Match each FiscalPeriod
106
- const periodRegex = /<FiscalPeriod[^>]*EndDate="([^"]+)"[^>]*>([\s\S]*?)<\/FiscalPeriod>/gi;
107
- let periodMatch: RegExpExecArray | null;
108
-
109
- while ((periodMatch = periodRegex.exec(section)) !== null) {
110
- const endDate = periodMatch[1]!;
111
- const periodContent = periodMatch[2]!;
112
- const stmt: FinancialStatement = { date: endDate };
113
-
114
- // Match all lineItems across all Statement types
115
- const lineItemRegex = /<lineItem\s+[^>]*coaCode="([^"]+)"[^>]*>([^<]+)<\/lineItem>/gi;
116
- let lineMatch: RegExpExecArray | null;
117
-
118
- while ((lineMatch = lineItemRegex.exec(periodContent)) !== null) {
119
- const coaCode = lineMatch[1]!.toUpperCase();
120
- const field = COA_MAP[coaCode];
121
- if (field && field !== "date") {
122
- const value = parseFloat(lineMatch[2]!);
123
- if (Number.isFinite(value)) {
124
- (stmt as any)[field] = value;
125
- }
126
- }
127
- }
128
-
129
- // Compute freeCashFlow if not directly available
130
- if (stmt.freeCashFlow == null && stmt.operatingCashFlow != null && stmt.capitalExpenditure != null) {
131
- stmt.freeCashFlow = stmt.operatingCashFlow - Math.abs(stmt.capitalExpenditure);
132
- }
133
-
134
- statements.push(stmt);
135
- }
136
-
137
- return statements;
138
- }
139
-
140
- /**
141
- * Parse IBKR ReportsFinStatements XML (from getFundamentalData "ReportsFinStatements")
142
- * into annual and quarterly FinancialStatement arrays.
143
- */
144
- export function parseFinStatements(xml: string): { annual: FinancialStatement[]; quarterly: FinancialStatement[] } {
145
- return {
146
- annual: parsePeriods(xml, "AnnualPeriods"),
147
- quarterly: parsePeriods(xml, "InterimPeriods"),
148
- };
149
- }
@@ -1,266 +0,0 @@
1
- import {
2
- EventName,
3
- type Contract,
4
- type IBApiNext,
5
- } from "@stoqey/ib";
6
- import { firstValueFrom, filter, take, timeout } from "rxjs";
7
- import type { BrokerPosition } from "../../../types/broker";
8
- import type {
9
- BrokerAccount,
10
- BrokerExecution,
11
- BrokerOrder,
12
- } from "../../../types/trading";
13
- import { debugLog } from "../../../utils/debug-log";
14
- import {
15
- finiteNumber,
16
- summarizeBrokerAccount,
17
- type AccountPnlSnapshot,
18
- type AccountSummaryTags,
19
- } from "./account-summary";
20
- import { contractToBrokerRef } from "./contracts";
21
- import { openIbkrOrderToBrokerOrder, type OpenIbkrOrder } from "./orders";
22
- import { getIbkrPriceDivisor, normalizeIbkrPriceValue } from "./price-normalization";
23
- import {
24
- IBKR_DATA_TIMEOUT,
25
- IBKR_PNL_TIMEOUT,
26
- withTimeout,
27
- } from "./timeouts";
28
-
29
- interface AccountPortfolioSnapshotPosition {
30
- contract: Contract;
31
- avgCost?: number;
32
- marketPrice?: number;
33
- marketValue?: number;
34
- unrealizedPNL?: number;
35
- }
36
-
37
- interface GatewayAccountLoadOptions {
38
- api: IBApiNext;
39
- rawApi: any;
40
- instanceId?: string;
41
- cachedAccountIds: string[];
42
- setCachedAccountIds(accountIds: string[]): void;
43
- }
44
-
45
- interface GatewayApiOptions {
46
- api: IBApiNext;
47
- instanceId?: string;
48
- }
49
-
50
- const gatewayLog = debugLog.createLogger("ibkr-gateway");
51
-
52
- function buildPortfolioPositionKey(accountId: string, contract: Contract): string {
53
- return [
54
- accountId.trim(),
55
- contract.conId ?? "",
56
- contract.localSymbol ?? "",
57
- contract.symbol ?? "",
58
- contract.secType ?? "",
59
- ].join("|");
60
- }
61
-
62
- export async function loadIbkrAccounts({
63
- api,
64
- rawApi,
65
- instanceId,
66
- cachedAccountIds,
67
- setCachedAccountIds,
68
- }: GatewayAccountLoadOptions): Promise<BrokerAccount[]> {
69
- let managedAccounts: string[];
70
- try {
71
- managedAccounts = await withTimeout(api.getManagedAccounts(), IBKR_DATA_TIMEOUT, "getManagedAccounts");
72
- } catch {
73
- // getManagedAccounts() can hang after reconnects or when another client with the same
74
- // clientId is connected. Fall back to requesting via the raw event API, or use cached IDs.
75
- if (cachedAccountIds.length > 0) {
76
- managedAccounts = cachedAccountIds;
77
- } else {
78
- managedAccounts = await withTimeout(
79
- new Promise<string[]>((resolve) => {
80
- const handler = (accountsList: string) => {
81
- rawApi.off(EventName.managedAccounts, handler);
82
- resolve(accountsList.split(",").map((s: string) => s.trim()).filter(Boolean));
83
- };
84
- rawApi.on(EventName.managedAccounts, handler);
85
- rawApi.reqManagedAccts();
86
- }),
87
- IBKR_DATA_TIMEOUT,
88
- "getManagedAccounts-fallback",
89
- );
90
- setCachedAccountIds(managedAccounts);
91
- }
92
- }
93
- if (!managedAccounts.length) return [];
94
- let summary: ReadonlyMap<string, AccountSummaryTags> | undefined;
95
- try {
96
- const result = await firstValueFrom(
97
- api.getAccountSummary(
98
- "All",
99
- "NetLiquidation,GrossPositionValue,TotalCashValue,SettledCash,AvailableFunds,BuyingPower,ExcessLiquidity,InitMarginReq,MaintMarginReq,$LEDGER:ALL",
100
- )
101
- .pipe(take(1), timeout(10_000)),
102
- );
103
- summary = result.all;
104
- } catch {
105
- // Account summary may fail; return accounts with basic info only.
106
- }
107
-
108
- const updatedAt = Date.now();
109
- const aggregateTags = summary?.get("All");
110
- const allowAggregateCashBalances = managedAccounts.length === 1;
111
- const pnlByAccount = await loadAccountPnlForAccounts(api, instanceId, managedAccounts);
112
- return managedAccounts.map((accountId) => summarizeBrokerAccount(
113
- accountId,
114
- summary?.get(accountId),
115
- updatedAt,
116
- aggregateTags,
117
- allowAggregateCashBalances,
118
- pnlByAccount.get(accountId),
119
- ));
120
- }
121
-
122
- export async function loadIbkrPositions({
123
- api,
124
- instanceId,
125
- }: GatewayApiOptions): Promise<BrokerPosition[]> {
126
- const update = await firstValueFrom(api.getPositions().pipe(take(1), timeout(10_000)));
127
- const portfolioSnapshots = await loadPortfolioSnapshotsForAccounts(api, instanceId, [...update.all.keys()]);
128
- const positions: BrokerPosition[] = [];
129
- for (const [accountId, accountPositions] of update.all) {
130
- for (const position of accountPositions) {
131
- if (!position.contract.symbol) continue;
132
- const portfolioSnapshot = portfolioSnapshots.get(buildPortfolioPositionKey(accountId, position.contract));
133
- const priceDivisor = getIbkrPriceDivisor(position.contract);
134
- positions.push({
135
- ticker: position.contract.localSymbol || position.contract.symbol,
136
- exchange: position.contract.primaryExch || position.contract.exchange || "",
137
- shares: Math.abs(position.pos),
138
- avgCost: portfolioSnapshot?.avgCost ?? position.avgCost,
139
- currency: position.contract.currency || "USD",
140
- accountId,
141
- name: position.contract.description || position.contract.localSymbol || position.contract.symbol,
142
- assetCategory: position.contract.secType,
143
- markPrice: normalizeIbkrPriceValue(portfolioSnapshot?.marketPrice ?? position.marketPrice, priceDivisor),
144
- marketValue: portfolioSnapshot?.marketValue ?? position.marketValue,
145
- unrealizedPnl: portfolioSnapshot?.unrealizedPNL ?? position.unrealizedPNL,
146
- side: position.pos < 0 ? "short" : "long",
147
- multiplier: position.contract.multiplier,
148
- brokerContract: contractToBrokerRef(position.contract, instanceId),
149
- });
150
- }
151
- }
152
- return positions;
153
- }
154
-
155
- export async function loadIbkrOpenOrders({
156
- api,
157
- instanceId,
158
- }: GatewayApiOptions): Promise<BrokerOrder[]> {
159
- const orders = await withTimeout(api.getAllOpenOrders(), IBKR_DATA_TIMEOUT, "getAllOpenOrders");
160
- return orders.map((order) => openIbkrOrderToBrokerOrder(
161
- order as OpenIbkrOrder,
162
- instanceId,
163
- (contract) => contractToBrokerRef(contract, instanceId),
164
- ));
165
- }
166
-
167
- export async function loadIbkrExecutions({
168
- api,
169
- instanceId,
170
- }: GatewayApiOptions): Promise<BrokerExecution[]> {
171
- const executions = await withTimeout(api.getExecutionDetails({}), IBKR_DATA_TIMEOUT, "getExecutionDetails");
172
- return executions.map((detail) => ({
173
- execId: detail.execution.execId || `${detail.execution.orderId ?? "exec"}-${detail.execution.time ?? Date.now()}`,
174
- brokerInstanceId: instanceId,
175
- orderId: detail.execution.orderId,
176
- accountId: detail.execution.acctNumber,
177
- side: detail.execution.side || "",
178
- shares: detail.execution.shares ?? 0,
179
- price: detail.execution.price ?? 0,
180
- time: detail.execution.time ? Date.parse(detail.execution.time) : Date.now(),
181
- exchange: detail.execution.exchange,
182
- contract: contractToBrokerRef(detail.contract, instanceId),
183
- }));
184
- }
185
-
186
- async function loadAccountPnlForAccounts(
187
- api: IBApiNext,
188
- instanceId: string | undefined,
189
- accountIds: string[],
190
- ): Promise<Map<string, AccountPnlSnapshot>> {
191
- const entries = await Promise.all(accountIds.map(async (accountId) => {
192
- const pnl = await loadAccountPnl(api, instanceId, accountId);
193
- return pnl ? ([accountId, pnl] as const) : null;
194
- }));
195
- return new Map(entries.filter((entry): entry is readonly [string, AccountPnlSnapshot] => entry != null));
196
- }
197
-
198
- async function loadAccountPnl(
199
- api: IBApiNext,
200
- instanceId: string | undefined,
201
- accountId: string,
202
- ): Promise<AccountPnlSnapshot | null> {
203
- if (typeof api.getPnL !== "function") return null;
204
-
205
- try {
206
- const pnl = await firstValueFrom(
207
- api.getPnL(accountId).pipe(
208
- take(1),
209
- timeout(IBKR_PNL_TIMEOUT),
210
- ),
211
- );
212
- return {
213
- dailyPnl: finiteNumber(pnl.dailyPnL),
214
- unrealizedPnl: finiteNumber(pnl.unrealizedPnL),
215
- realizedPnl: finiteNumber(pnl.realizedPnL),
216
- };
217
- } catch (error: any) {
218
- gatewayLog.warn("Failed to load IBKR account P&L", {
219
- instanceId,
220
- accountId,
221
- error: error?.message || String(error || ""),
222
- });
223
- return null;
224
- }
225
- }
226
-
227
- async function loadPortfolioSnapshotsForAccounts(
228
- api: IBApiNext,
229
- instanceId: string | undefined,
230
- accountIds: string[],
231
- ): Promise<Map<string, AccountPortfolioSnapshotPosition>> {
232
- const snapshots = new Map<string, AccountPortfolioSnapshotPosition>();
233
- for (const accountId of accountIds) {
234
- const positions = await loadPortfolioSnapshotForAccount(api, instanceId, accountId);
235
- for (const position of positions) {
236
- snapshots.set(buildPortfolioPositionKey(accountId, position.contract), position);
237
- }
238
- }
239
- return snapshots;
240
- }
241
-
242
- async function loadPortfolioSnapshotForAccount(
243
- api: IBApiNext,
244
- instanceId: string | undefined,
245
- accountId: string,
246
- ): Promise<AccountPortfolioSnapshotPosition[]> {
247
- if (!accountId) return [];
248
-
249
- try {
250
- const update = await firstValueFrom(
251
- api.getAccountUpdates(accountId).pipe(
252
- filter((value) => value.changed == null && value.added == null && value.removed == null),
253
- take(1),
254
- timeout(IBKR_DATA_TIMEOUT),
255
- ),
256
- );
257
- return (update.all.portfolio?.get(accountId) ?? []) as AccountPortfolioSnapshotPosition[];
258
- } catch (error: any) {
259
- gatewayLog.warn("Failed to load IBKR portfolio snapshot", {
260
- instanceId,
261
- accountId,
262
- error: error?.message || String(error || ""),
263
- });
264
- return [];
265
- }
266
- }