gloomberb 0.11.3 → 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 (489) hide show
  1. package/README.md +6 -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 +103 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +129 -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 +196 -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/cli/commands/plugins.ts +39 -11
  20. package/src/cli/desktop-pane-shot.ts +285 -16
  21. package/src/cli/index.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +58 -28
  23. package/src/cli/pane-functions/catalog.ts +16 -4
  24. package/src/cli/pane-functions/discovery.ts +1 -0
  25. package/src/cli/pane-functions/headless.test.ts +247 -0
  26. package/src/cli/pane-functions/headless.ts +423 -0
  27. package/src/cli/pane-functions/index.test.ts +9 -0
  28. package/src/cli/pane-functions/index.ts +6 -2
  29. package/src/cli/pane-functions/options.ts +27 -2
  30. package/src/cli/pane-functions/report.ts +4 -0
  31. package/src/cli/pane-functions/resolver.ts +26 -8
  32. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  33. package/src/cli/pane-functions/screenshot.ts +236 -20
  34. package/src/cli/restore-plugins.ts +34 -0
  35. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  36. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  37. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  38. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  39. package/src/components/chart/composite/composite-chart.tsx +301 -204
  40. package/src/components/chart/composite/format.ts +8 -2
  41. package/src/components/chart/composite/interactions.test.ts +68 -102
  42. package/src/components/chart/composite/interactions.ts +214 -264
  43. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  44. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  45. package/src/components/chart/composite/scene.ts +57 -3
  46. package/src/components/chart/composite/text-renderer.ts +3 -2
  47. package/src/components/chart/composite/time-scale.ts +138 -57
  48. package/src/components/chart/composite/types.ts +25 -1
  49. package/src/components/chart/core/data.ts +9 -13
  50. package/src/components/chart/core/date-window.ts +7 -1
  51. package/src/components/chart/core/pointer.ts +3 -0
  52. package/src/components/chart/core/types.ts +0 -45
  53. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  54. package/src/components/chart/native/kitty/index.test.ts +0 -188
  55. package/src/components/chart/native/raster/crosshair.ts +6 -5
  56. package/src/components/chart/native/raster/primitives.ts +0 -23
  57. package/src/components/chart/native/renderer-selection.ts +81 -31
  58. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  59. package/src/components/chart/static/chart-surface.tsx +150 -0
  60. package/src/components/chart/static/index.ts +8 -0
  61. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  62. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  63. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  64. package/src/components/chart/static/series.ts +49 -0
  65. package/src/components/command-bar/action-runtime.ts +1 -3
  66. package/src/components/command-bar/assist/model.test.ts +17 -2
  67. package/src/components/command-bar/assist/model.ts +40 -9
  68. package/src/components/command-bar/commands/direct/index.ts +1 -1
  69. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  70. package/src/components/command-bar/commands/registry.ts +0 -11
  71. package/src/components/command-bar/helpers.test.ts +2 -2
  72. package/src/components/command-bar/helpers.ts +2 -6
  73. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  74. package/src/components/command-bar/list/badge.test.ts +54 -0
  75. package/src/components/command-bar/list/badge.ts +62 -0
  76. package/src/components/command-bar/list/model.test.ts +103 -0
  77. package/src/components/command-bar/list/model.ts +61 -4
  78. package/src/components/command-bar/list/view.test.tsx +113 -0
  79. package/src/components/command-bar/list/view.tsx +147 -131
  80. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  81. package/src/components/command-bar/panel/body.tsx +51 -65
  82. package/src/components/command-bar/panel/index.tsx +48 -13
  83. package/src/components/command-bar/panel/layout.test.ts +139 -0
  84. package/src/components/command-bar/panel/layout.ts +65 -28
  85. package/src/components/command-bar/panel/native-surface.ts +24 -0
  86. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  87. package/src/components/command-bar/panel/runtime.ts +6 -3
  88. package/src/components/command-bar/panel/state.ts +41 -7
  89. package/src/components/command-bar/panel/types.ts +7 -1
  90. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  91. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  92. package/src/components/command-bar/routes/root/results.ts +30 -28
  93. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  94. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  95. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  96. package/src/components/command-bar/routes/root/selection.ts +1 -12
  97. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  98. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  99. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  100. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  101. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  102. package/src/components/command-bar/routing/actions.ts +1 -81
  103. package/src/components/command-bar/routing/launch-request.ts +1 -1
  104. package/src/components/command-bar/routing/list-state.ts +6 -21
  105. package/src/components/command-bar/selection-runtime.ts +1 -1
  106. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  107. package/src/components/command-bar/surface/environment.ts +8 -1
  108. package/src/components/command-bar/surface/index.test.tsx +84 -68
  109. package/src/components/command-bar/surface/index.tsx +33 -36
  110. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  111. package/src/components/command-bar/theme-picker.tsx +43 -15
  112. package/src/components/command-bar/view-model.test.ts +53 -8
  113. package/src/components/command-bar/view-model.ts +43 -14
  114. package/src/components/command-bar/workflow/tickers.ts +1 -0
  115. package/src/components/command-bar/workflow/types.ts +1 -1
  116. package/src/components/data-table/view.test.tsx +45 -6
  117. package/src/components/data-table/view.tsx +55 -9
  118. package/src/components/index.ts +10 -2
  119. package/src/components/input-search-bar.test.tsx +36 -2
  120. package/src/components/input-search-bar.tsx +18 -1
  121. package/src/components/layout/header.test.tsx +57 -10
  122. package/src/components/layout/header.tsx +292 -149
  123. package/src/components/layout/market-summary.test.ts +81 -0
  124. package/src/components/layout/market-summary.ts +109 -0
  125. package/src/components/layout/shell/chrome.ts +96 -1
  126. package/src/components/layout/shell/index.tsx +11 -3
  127. package/src/components/layout/shell/menu.ts +9 -0
  128. package/src/components/layout/shell/pane/actions.ts +29 -0
  129. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  130. package/src/components/layout/shell/shortcuts.ts +3 -0
  131. package/src/components/layout/status-bar.test.tsx +9 -8
  132. package/src/components/layout/status-bar.tsx +85 -54
  133. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  134. package/src/components/price-sparkline/index.ts +61 -34
  135. package/src/components/price-sparkline/view.tsx +1 -1
  136. package/src/components/sign-in-gate.test.tsx +128 -0
  137. package/src/components/sign-in-gate.tsx +149 -0
  138. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  139. package/src/components/ticker/badge/list.tsx +17 -3
  140. package/src/components/ticker/list-table-view.tsx +6 -3
  141. package/src/components/ui/data-table/index.tsx +3 -2
  142. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  143. package/src/components/ui/frame.tsx +28 -0
  144. package/src/components/ui/index.ts +3 -0
  145. package/src/components/ui/native-select.tsx +20 -10
  146. package/src/components/ui/select-button.tsx +138 -0
  147. package/src/components/ui/status.tsx +2 -2
  148. package/src/components/ui/table-layout.ts +1 -1
  149. package/src/components/use-double-click-activation.ts +7 -3
  150. package/src/core/app-services.ts +3 -3
  151. package/src/data/config/layout.ts +12 -6
  152. package/src/data/config/store/index.test.ts +97 -0
  153. package/src/data/config/store/migrations.ts +41 -0
  154. package/src/data/config/store/normalize.ts +5 -3
  155. package/src/i18n/es.ts +6 -2
  156. package/src/i18n/ja.ts +6 -2
  157. package/src/i18n/ko.ts +6 -2
  158. package/src/i18n/zh-cn.ts +6 -2
  159. package/src/i18n/zh-tw.ts +6 -2
  160. package/src/layout-marketplace/pane-imagery.ts +1 -0
  161. package/src/layout-marketplace/payload.test.ts +17 -0
  162. package/src/layout-marketplace/payload.ts +40 -5
  163. package/src/market-data/coordinator/index.ts +3 -2
  164. package/src/market-data/coordinator/quotes.ts +173 -66
  165. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  166. package/src/pane-settings.ts +1 -1
  167. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  168. package/src/plugins/builtin/analytics/view.tsx +1 -1
  169. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  170. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  171. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  172. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  173. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  174. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  175. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  176. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  177. package/src/plugins/builtin/composite-plugins.ts +14 -1
  178. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  179. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  180. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  181. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  182. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  183. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  184. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  185. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  186. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  187. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  188. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  189. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  190. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  191. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  192. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  193. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  194. package/src/plugins/builtin/econ/index.tsx +2 -1
  195. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  196. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  197. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  198. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  199. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  200. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  201. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  202. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  203. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  204. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  205. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  206. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  207. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  208. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  209. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  210. package/src/plugins/builtin/help/index.tsx +4 -0
  211. package/src/plugins/builtin/holders/index.tsx +1 -0
  212. package/src/plugins/builtin/insider/index.tsx +1 -0
  213. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  214. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  215. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  216. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  217. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  218. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  219. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  220. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  221. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  222. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  223. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  224. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  225. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  226. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  227. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  228. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  229. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  230. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  231. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  232. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  233. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  234. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  235. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  236. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  237. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  238. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  239. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  240. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  241. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  242. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  243. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  244. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  245. package/src/plugins/builtin/options/index.tsx +1 -0
  246. package/src/plugins/builtin/options/view.test.tsx +29 -1
  247. package/src/plugins/builtin/options/view.tsx +1 -22
  248. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  249. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  250. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  251. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  252. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  253. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  254. package/src/plugins/builtin/plugin-module.ts +9 -1
  255. package/src/plugins/builtin/research/index.tsx +4 -0
  256. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  257. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  258. package/src/plugins/builtin/research-search/data.ts +109 -0
  259. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  260. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  261. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  262. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  263. package/src/plugins/builtin/research-search/index.tsx +85 -0
  264. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  265. package/src/plugins/builtin/research-search/model.ts +291 -0
  266. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  267. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  268. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  269. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  270. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  271. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  272. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  273. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  274. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  275. package/src/plugins/builtin/sec/index.tsx +1 -0
  276. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  277. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  278. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  279. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  280. package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
  281. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  282. package/src/plugins/builtin/volatility/index.tsx +1 -1
  283. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  284. package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
  285. package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
  286. package/src/plugins/builtin/world-venue-map/model.ts +134 -13
  287. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  288. package/src/plugins/bundle.test.ts +109 -0
  289. package/src/plugins/bundle.ts +139 -0
  290. package/src/plugins/catalog-browser.test.ts +1 -0
  291. package/src/plugins/catalog-browser.ts +10 -0
  292. package/src/plugins/catalog-manifest.test.ts +43 -0
  293. package/src/plugins/catalog-manifest.ts +92 -0
  294. package/src/plugins/catalog-ui.ts +20 -4
  295. package/src/plugins/catalog.ts +9 -2
  296. package/src/plugins/current-target.ts +28 -0
  297. package/src/plugins/host-contract.ts +37 -0
  298. package/src/plugins/host-link.ts +158 -0
  299. package/src/plugins/host-modules.ts +61 -0
  300. package/src/plugins/loader.ts +41 -22
  301. package/src/plugins/ownership.ts +1 -0
  302. package/src/plugins/prediction-markets/index.tsx +1 -0
  303. package/src/plugins/registry/context.ts +3 -0
  304. package/src/plugins/registry/contributions.ts +27 -0
  305. package/src/plugins/registry/index.test.ts +25 -0
  306. package/src/plugins/registry/index.ts +8 -0
  307. package/src/plugins/registry/pane-settings.ts +6 -13
  308. package/src/plugins/seed.test.ts +83 -0
  309. package/src/plugins/seed.ts +80 -0
  310. package/src/public/broker.ts +31 -0
  311. package/src/public/market-data.ts +9 -0
  312. package/src/public/pane-hooks.ts +57 -0
  313. package/src/public/public-api.test.ts +285 -0
  314. package/src/public/react.ts +62 -0
  315. package/src/public/test-support.ts +23 -0
  316. package/src/public/time-series.ts +8 -0
  317. package/src/public/utils.ts +77 -0
  318. package/src/remote/layout-helpers.ts +6 -8
  319. package/src/remote/resources.ts +2 -0
  320. package/src/remote/schema.ts +1 -0
  321. package/src/remote/semantic-tree.test.tsx +105 -0
  322. package/src/remote/semantic-tree.tsx +53 -35
  323. package/src/renderers/browser/main.tsx +6 -0
  324. package/src/renderers/cloudflare/worker.test.ts +19 -0
  325. package/src/renderers/cloudflare/worker.ts +1 -1
  326. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  327. package/src/renderers/electrobun/bun/index.ts +5 -2
  328. package/src/renderers/electrobun/shared/protocol.ts +21 -0
  329. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  330. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  331. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  332. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  333. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  334. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  335. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  336. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  337. package/src/renderers/electrobun/view/main.tsx +27 -2
  338. package/src/renderers/electrobun/view/styles.css +5 -0
  339. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  340. package/src/renderers/opentui/chart-surface.tsx +5 -1
  341. package/src/renderers/opentui/host.tsx +16 -2
  342. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  343. package/src/renderers/opentui/interaction-performance.ts +188 -0
  344. package/src/renderers/opentui/start.tsx +26 -1
  345. package/src/renderers/opentui/test-utils.tsx +13 -0
  346. package/src/renderers/opentui/toast-host.tsx +26 -2
  347. package/src/renderers/opentui/ui-host.tsx +1 -0
  348. package/src/sources/provider-router/search.test.ts +191 -0
  349. package/src/sources/provider-router/search.ts +92 -39
  350. package/src/state/app/context/index.tsx +2 -0
  351. package/src/state/app/context/selector.test.tsx +33 -1
  352. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  353. package/src/state/hooks/quote-streaming.ts +106 -30
  354. package/src/state/pane-table-export-registry.ts +19 -0
  355. package/src/theme/color-utils.ts +1 -1
  356. package/src/theme/colors.ts +50 -0
  357. package/src/theme/themes.test.ts +15 -2
  358. package/src/theme/themes.ts +19 -1
  359. package/src/tickers/search/index.test.ts +26 -0
  360. package/src/tickers/search/index.ts +42 -17
  361. package/src/tickers/search/ranking.ts +14 -0
  362. package/src/time-series/chart-data.test.ts +216 -0
  363. package/src/time-series/resolution.test.ts +25 -26
  364. package/src/time-series/resolution.ts +41 -10
  365. package/src/time-series/resolve.ts +7 -0
  366. package/src/time-series/types.ts +3 -1
  367. package/src/time-series/use-chart-resolution.ts +5 -0
  368. package/src/types/config.ts +123 -3
  369. package/src/types/data-provider.ts +11 -0
  370. package/src/types/headless.ts +161 -0
  371. package/src/types/plugin.ts +102 -0
  372. package/src/ui/host.tsx +12 -1
  373. package/src/ui/index.tsx +1 -0
  374. package/src/ui/toast.tsx +8 -4
  375. package/src/utils/format.ts +35 -0
  376. package/src/utils/fuzzy-search.test.ts +94 -0
  377. package/src/utils/fuzzy-search.ts +81 -22
  378. package/src/utils/shortcut-labels.ts +14 -0
  379. package/src/components/chart/core/axis-format.ts +0 -157
  380. package/src/components/chart/core/index.test.ts +0 -1075
  381. package/src/components/chart/core/projection.ts +0 -357
  382. package/src/components/chart/core/render-utils.ts +0 -4
  383. package/src/components/chart/core/renderer.ts +0 -244
  384. package/src/components/chart/core/scene.ts +0 -180
  385. package/src/components/chart/core/time-axis.ts +0 -330
  386. package/src/components/chart/cursor-motion.ts +0 -47
  387. package/src/components/chart/indicators/types.ts +0 -26
  388. package/src/components/chart/market-session.ts +0 -176
  389. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  390. package/src/components/chart/multi-line/renderer.ts +0 -161
  391. package/src/components/chart/multi-line/scene.ts +0 -188
  392. package/src/components/chart/native/raster/layout.ts +0 -129
  393. package/src/components/chart/native/raster/price-chart.ts +0 -244
  394. package/src/components/chart/rendering/chart-draw.ts +0 -270
  395. package/src/components/chart/rendering/geometry.ts +0 -95
  396. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  397. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  398. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  399. package/src/components/chart/static/chart/surface.tsx +0 -358
  400. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  401. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  402. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  403. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  404. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  405. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  406. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  407. package/src/plugins/builtin/substack/api/store.ts +0 -186
  408. package/src/plugins/builtin/substack/api/types.ts +0 -42
  409. package/src/plugins/builtin/substack/api.test.ts +0 -298
  410. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  411. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  412. package/src/plugins/builtin/substack/content.ts +0 -478
  413. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  414. package/src/plugins/builtin/substack/index.tsx +0 -49
  415. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  416. package/src/plugins/builtin/substack/model.test.ts +0 -155
  417. package/src/plugins/builtin/substack/normalize.ts +0 -343
  418. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  419. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  420. package/src/plugins/builtin/substack/pane.tsx +0 -547
  421. package/src/plugins/builtin/substack/read-state.ts +0 -30
  422. package/src/plugins/builtin/substack/table.ts +0 -111
  423. package/src/plugins/builtin/substack/types.ts +0 -71
  424. package/src/plugins/builtin/substack/utils.ts +0 -131
  425. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  426. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  427. package/src/plugins/ibkr/account-cache.ts +0 -24
  428. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  429. package/src/plugins/ibkr/config.test.ts +0 -79
  430. package/src/plugins/ibkr/config.ts +0 -250
  431. package/src/plugins/ibkr/dialogs.tsx +0 -40
  432. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  433. package/src/plugins/ibkr/flex/index.ts +0 -367
  434. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  435. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  436. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  437. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  438. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  439. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  440. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  441. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  442. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  443. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  444. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  445. package/src/plugins/ibkr/gateway/history.ts +0 -120
  446. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  447. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  448. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  449. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  450. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  451. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  452. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  453. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  454. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  455. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  456. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  457. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  458. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  459. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  460. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  461. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  462. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  463. package/src/plugins/ibkr/gateway/types.ts +0 -27
  464. package/src/plugins/ibkr/index.test.tsx +0 -162
  465. package/src/plugins/ibkr/index.tsx +0 -159
  466. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  467. package/src/plugins/ibkr/instance-selection.ts +0 -36
  468. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  469. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  470. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  471. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  472. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  473. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  474. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  475. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  476. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  477. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  478. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  479. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  480. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  481. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  482. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  483. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  484. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  485. package/src/plugins/ibkr/trade/utils.ts +0 -144
  486. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  487. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  488. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  489. 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
- }