opencandle 0.4.0 → 0.6.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 (564) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +186 -117
  3. package/dist/analysts/contracts.d.ts +1 -3
  4. package/dist/analysts/contracts.js +1 -11
  5. package/dist/analysts/contracts.js.map +1 -1
  6. package/dist/analysts/orchestrator.d.ts +1 -3
  7. package/dist/analysts/orchestrator.js +1 -26
  8. package/dist/analysts/orchestrator.js.map +1 -1
  9. package/dist/cli.js +32 -8
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts +19 -3
  12. package/dist/config.js +69 -3
  13. package/dist/config.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/infra/browser.d.ts +1 -3
  18. package/dist/infra/browser.js +4 -2
  19. package/dist/infra/browser.js.map +1 -1
  20. package/dist/infra/cache.d.ts +8 -11
  21. package/dist/infra/cache.js +17 -15
  22. package/dist/infra/cache.js.map +1 -1
  23. package/dist/infra/http-client.d.ts +4 -1
  24. package/dist/infra/http-client.js +59 -6
  25. package/dist/infra/http-client.js.map +1 -1
  26. package/dist/infra/index.d.ts +3 -3
  27. package/dist/infra/index.js +3 -3
  28. package/dist/infra/index.js.map +1 -1
  29. package/dist/infra/native-dependencies.js +2 -2
  30. package/dist/infra/native-dependencies.js.map +1 -1
  31. package/dist/infra/node-version.js.map +1 -1
  32. package/dist/infra/opencandle-paths.d.ts +0 -3
  33. package/dist/infra/opencandle-paths.js +4 -11
  34. package/dist/infra/opencandle-paths.js.map +1 -1
  35. package/dist/infra/rate-limiter.d.ts +4 -0
  36. package/dist/infra/rate-limiter.js +17 -10
  37. package/dist/infra/rate-limiter.js.map +1 -1
  38. package/dist/market-state/alert-conditions.d.ts +34 -0
  39. package/dist/market-state/alert-conditions.js +23 -0
  40. package/dist/market-state/alert-conditions.js.map +1 -0
  41. package/dist/market-state/alert-runner.d.ts +55 -0
  42. package/dist/market-state/alert-runner.js +634 -0
  43. package/dist/market-state/alert-runner.js.map +1 -0
  44. package/dist/market-state/daily-report.d.ts +26 -0
  45. package/dist/market-state/daily-report.js +179 -0
  46. package/dist/market-state/daily-report.js.map +1 -0
  47. package/dist/market-state/local-automation-service.d.ts +25 -0
  48. package/dist/market-state/local-automation-service.js +119 -0
  49. package/dist/market-state/local-automation-service.js.map +1 -0
  50. package/dist/market-state/notification-delivery.d.ts +14 -0
  51. package/dist/market-state/notification-delivery.js +139 -0
  52. package/dist/market-state/notification-delivery.js.map +1 -0
  53. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  54. package/dist/market-state/resolve-for-mutation.js +15 -0
  55. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  56. package/dist/market-state/resolve.d.ts +14 -0
  57. package/dist/market-state/resolve.js +89 -0
  58. package/dist/market-state/resolve.js.map +1 -0
  59. package/dist/market-state/service.d.ts +527 -0
  60. package/dist/market-state/service.js +1099 -0
  61. package/dist/market-state/service.js.map +1 -0
  62. package/dist/memory/index.d.ts +7 -7
  63. package/dist/memory/index.js +6 -6
  64. package/dist/memory/index.js.map +1 -1
  65. package/dist/memory/manager.d.ts +9 -0
  66. package/dist/memory/manager.js +39 -22
  67. package/dist/memory/manager.js.map +1 -1
  68. package/dist/memory/retrieval.js +7 -4
  69. package/dist/memory/retrieval.js.map +1 -1
  70. package/dist/memory/sqlite.js +385 -3
  71. package/dist/memory/sqlite.js.map +1 -1
  72. package/dist/memory/storage.d.ts +3 -2
  73. package/dist/memory/storage.js +1 -2
  74. package/dist/memory/storage.js.map +1 -1
  75. package/dist/memory/tool-defaults.js +64 -28
  76. package/dist/memory/tool-defaults.js.map +1 -1
  77. package/dist/memory/types.js +4 -0
  78. package/dist/memory/types.js.map +1 -1
  79. package/dist/monitor.d.ts +2 -0
  80. package/dist/monitor.js +104 -0
  81. package/dist/monitor.js.map +1 -0
  82. package/dist/onboarding/connect.js +4 -6
  83. package/dist/onboarding/connect.js.map +1 -1
  84. package/dist/onboarding/credential-interceptor.js +1 -1
  85. package/dist/onboarding/credential-interceptor.js.map +1 -1
  86. package/dist/onboarding/degradation-accumulator.js +1 -3
  87. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  88. package/dist/onboarding/providers.js +3 -16
  89. package/dist/onboarding/providers.js.map +1 -1
  90. package/dist/onboarding/state.js.map +1 -1
  91. package/dist/onboarding/tool-helpers.js +1 -1
  92. package/dist/onboarding/tool-helpers.js.map +1 -1
  93. package/dist/onboarding/tool-tags.js +6 -4
  94. package/dist/onboarding/tool-tags.js.map +1 -1
  95. package/dist/onboarding/validation.js +1 -1
  96. package/dist/onboarding/validation.js.map +1 -1
  97. package/dist/pi/opencandle-extension.d.ts +8 -0
  98. package/dist/pi/opencandle-extension.js +637 -59
  99. package/dist/pi/opencandle-extension.js.map +1 -1
  100. package/dist/pi/session.d.ts +1 -1
  101. package/dist/pi/session.js +3 -1
  102. package/dist/pi/session.js.map +1 -1
  103. package/dist/pi/setup.js +17 -2
  104. package/dist/pi/setup.js.map +1 -1
  105. package/dist/pi/tool-adapter.js +5 -2
  106. package/dist/pi/tool-adapter.js.map +1 -1
  107. package/dist/prompts/context-builder.d.ts +18 -3
  108. package/dist/prompts/context-builder.js +117 -18
  109. package/dist/prompts/context-builder.js.map +1 -1
  110. package/dist/prompts/disclaimer.js +1 -1
  111. package/dist/prompts/disclaimer.js.map +1 -1
  112. package/dist/prompts/policy-cards.d.ts +13 -0
  113. package/dist/prompts/policy-cards.js +197 -0
  114. package/dist/prompts/policy-cards.js.map +1 -0
  115. package/dist/prompts/sections.d.ts +1 -1
  116. package/dist/prompts/sections.js +3 -3
  117. package/dist/prompts/sections.js.map +1 -1
  118. package/dist/prompts/symbol-preflight.d.ts +20 -0
  119. package/dist/prompts/symbol-preflight.js +49 -0
  120. package/dist/prompts/symbol-preflight.js.map +1 -0
  121. package/dist/prompts/workflow-prompts.d.ts +1 -1
  122. package/dist/prompts/workflow-prompts.js +209 -19
  123. package/dist/prompts/workflow-prompts.js.map +1 -1
  124. package/dist/providers/alpha-vantage.d.ts +1 -1
  125. package/dist/providers/alpha-vantage.js +49 -8
  126. package/dist/providers/alpha-vantage.js.map +1 -1
  127. package/dist/providers/coingecko.js +1 -1
  128. package/dist/providers/coingecko.js.map +1 -1
  129. package/dist/providers/errors.d.ts +5 -0
  130. package/dist/providers/errors.js +11 -0
  131. package/dist/providers/errors.js.map +1 -0
  132. package/dist/providers/exa-search.d.ts +2 -2
  133. package/dist/providers/exa-search.js +19 -11
  134. package/dist/providers/exa-search.js.map +1 -1
  135. package/dist/providers/fear-greed.js +1 -1
  136. package/dist/providers/fear-greed.js.map +1 -1
  137. package/dist/providers/finnhub.js +3 -5
  138. package/dist/providers/finnhub.js.map +1 -1
  139. package/dist/providers/fred.js +2 -2
  140. package/dist/providers/fred.js.map +1 -1
  141. package/dist/providers/index.d.ts +7 -6
  142. package/dist/providers/index.js +6 -5
  143. package/dist/providers/index.js.map +1 -1
  144. package/dist/providers/reddit.js +2 -2
  145. package/dist/providers/reddit.js.map +1 -1
  146. package/dist/providers/sec-edgar.d.ts +9 -1
  147. package/dist/providers/sec-edgar.js +181 -6
  148. package/dist/providers/sec-edgar.js.map +1 -1
  149. package/dist/providers/tradingview.d.ts +47 -0
  150. package/dist/providers/tradingview.js +275 -0
  151. package/dist/providers/tradingview.js.map +1 -0
  152. package/dist/providers/twitter.js +6 -8
  153. package/dist/providers/twitter.js.map +1 -1
  154. package/dist/providers/web-search.js +26 -12
  155. package/dist/providers/web-search.js.map +1 -1
  156. package/dist/providers/with-fallback.js +4 -2
  157. package/dist/providers/with-fallback.js.map +1 -1
  158. package/dist/providers/wrap-provider.d.ts +2 -3
  159. package/dist/providers/wrap-provider.js +14 -8
  160. package/dist/providers/wrap-provider.js.map +1 -1
  161. package/dist/providers/yahoo-finance.d.ts +3 -1
  162. package/dist/providers/yahoo-finance.js +226 -11
  163. package/dist/providers/yahoo-finance.js.map +1 -1
  164. package/dist/routing/classify-intent.d.ts +9 -0
  165. package/dist/routing/classify-intent.js +153 -3
  166. package/dist/routing/classify-intent.js.map +1 -1
  167. package/dist/routing/defaults.d.ts +1 -1
  168. package/dist/routing/defaults.js +3 -3
  169. package/dist/routing/defaults.js.map +1 -1
  170. package/dist/routing/entity-extractor.d.ts +2 -0
  171. package/dist/routing/entity-extractor.js +377 -26
  172. package/dist/routing/entity-extractor.js.map +1 -1
  173. package/dist/routing/fund-symbols.d.ts +2 -0
  174. package/dist/routing/fund-symbols.js +55 -0
  175. package/dist/routing/fund-symbols.js.map +1 -0
  176. package/dist/routing/horizon.d.ts +1 -0
  177. package/dist/routing/horizon.js +10 -0
  178. package/dist/routing/horizon.js.map +1 -0
  179. package/dist/routing/index.d.ts +12 -6
  180. package/dist/routing/index.js +8 -4
  181. package/dist/routing/index.js.map +1 -1
  182. package/dist/routing/legacy-rule-router.d.ts +9 -0
  183. package/dist/routing/legacy-rule-router.js +12 -0
  184. package/dist/routing/legacy-rule-router.js.map +1 -0
  185. package/dist/routing/planning.d.ts +54 -0
  186. package/dist/routing/planning.js +562 -0
  187. package/dist/routing/planning.js.map +1 -0
  188. package/dist/routing/route-manifest.d.ts +35 -0
  189. package/dist/routing/route-manifest.js +242 -0
  190. package/dist/routing/route-manifest.js.map +1 -0
  191. package/dist/routing/router-llm-client.js.map +1 -1
  192. package/dist/routing/router-prompt.js +46 -45
  193. package/dist/routing/router-prompt.js.map +1 -1
  194. package/dist/routing/router-types.d.ts +10 -0
  195. package/dist/routing/router.d.ts +1 -0
  196. package/dist/routing/router.js +572 -13
  197. package/dist/routing/router.js.map +1 -1
  198. package/dist/routing/slot-resolver.d.ts +1 -1
  199. package/dist/routing/slot-resolver.js +45 -7
  200. package/dist/routing/slot-resolver.js.map +1 -1
  201. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  202. package/dist/routing/symbol-disambiguator.js +52 -0
  203. package/dist/routing/symbol-disambiguator.js.map +1 -0
  204. package/dist/routing/turn-context.d.ts +44 -0
  205. package/dist/routing/turn-context.js +45 -0
  206. package/dist/routing/turn-context.js.map +1 -0
  207. package/dist/routing/types.d.ts +15 -1
  208. package/dist/runtime/answer-contracts.d.ts +82 -0
  209. package/dist/runtime/answer-contracts.js +442 -0
  210. package/dist/runtime/answer-contracts.js.map +1 -0
  211. package/dist/runtime/artifact-contracts.d.ts +14 -0
  212. package/dist/runtime/artifact-contracts.js +57 -0
  213. package/dist/runtime/artifact-contracts.js.map +1 -0
  214. package/dist/runtime/planning-evidence.d.ts +99 -0
  215. package/dist/runtime/planning-evidence.js +466 -0
  216. package/dist/runtime/planning-evidence.js.map +1 -0
  217. package/dist/runtime/prompt-step.d.ts +1 -9
  218. package/dist/runtime/prompt-step.js +0 -10
  219. package/dist/runtime/prompt-step.js.map +1 -1
  220. package/dist/runtime/run-context.d.ts +5 -2
  221. package/dist/runtime/run-context.js +8 -1
  222. package/dist/runtime/run-context.js.map +1 -1
  223. package/dist/runtime/session-coordinator.d.ts +29 -3
  224. package/dist/runtime/session-coordinator.js +204 -31
  225. package/dist/runtime/session-coordinator.js.map +1 -1
  226. package/dist/runtime/session-title.d.ts +14 -0
  227. package/dist/runtime/session-title.js +50 -0
  228. package/dist/runtime/session-title.js.map +1 -0
  229. package/dist/runtime/tool-defaults-wrapper.js +1 -3
  230. package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
  231. package/dist/runtime/validation.js.map +1 -1
  232. package/dist/runtime/workflow-events.js.map +1 -1
  233. package/dist/runtime/workflow-runner.d.ts +3 -3
  234. package/dist/runtime/workflow-runner.js +1 -1
  235. package/dist/runtime/workflow-runner.js.map +1 -1
  236. package/dist/sentiment/adapters/finnhub.d.ts +1 -1
  237. package/dist/sentiment/adapters/finnhub.js +6 -1
  238. package/dist/sentiment/adapters/finnhub.js.map +1 -1
  239. package/dist/sentiment/adapters/reddit.d.ts +2 -2
  240. package/dist/sentiment/adapters/twitter.d.ts +1 -1
  241. package/dist/sentiment/adapters/web.d.ts +1 -1
  242. package/dist/sentiment/index.d.ts +9 -11
  243. package/dist/sentiment/index.js +9 -20
  244. package/dist/sentiment/index.js.map +1 -1
  245. package/dist/sentiment/keywords.js +26 -4
  246. package/dist/sentiment/keywords.js.map +1 -1
  247. package/dist/sentiment/pipeline.d.ts +2 -2
  248. package/dist/sentiment/pipeline.js +1 -1
  249. package/dist/sentiment/pipeline.js.map +1 -1
  250. package/dist/sentiment/scorer.js +1 -1
  251. package/dist/sentiment/store.d.ts +1 -1
  252. package/dist/sentiment/store.js +1 -1
  253. package/dist/sentiment/store.js.map +1 -1
  254. package/dist/sentiment/trends.d.ts +1 -1
  255. package/dist/sentiment/trends.js.map +1 -1
  256. package/dist/sentiment/types.js.map +1 -1
  257. package/dist/system-prompt.js +7 -3
  258. package/dist/system-prompt.js.map +1 -1
  259. package/dist/tool-kit.d.ts +7 -7
  260. package/dist/tool-kit.js +4 -4
  261. package/dist/tool-kit.js.map +1 -1
  262. package/dist/tools/fundamentals/company-overview.js +12 -7
  263. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  264. package/dist/tools/fundamentals/comps.js +19 -10
  265. package/dist/tools/fundamentals/comps.js.map +1 -1
  266. package/dist/tools/fundamentals/dcf.js +24 -12
  267. package/dist/tools/fundamentals/dcf.js.map +1 -1
  268. package/dist/tools/fundamentals/earnings.js +9 -4
  269. package/dist/tools/fundamentals/earnings.js.map +1 -1
  270. package/dist/tools/fundamentals/financials.js +9 -4
  271. package/dist/tools/fundamentals/financials.js.map +1 -1
  272. package/dist/tools/fundamentals/sec-filings.d.ts +1 -0
  273. package/dist/tools/fundamentals/sec-filings.js +36 -4
  274. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  275. package/dist/tools/index.d.ts +23 -18
  276. package/dist/tools/index.js +53 -38
  277. package/dist/tools/index.js.map +1 -1
  278. package/dist/tools/interaction/ask-user.js +15 -3
  279. package/dist/tools/interaction/ask-user.js.map +1 -1
  280. package/dist/tools/interaction/twitter-login.js +13 -3
  281. package/dist/tools/interaction/twitter-login.js.map +1 -1
  282. package/dist/tools/macro/fear-greed.js +1 -1
  283. package/dist/tools/macro/fear-greed.js.map +1 -1
  284. package/dist/tools/macro/fred-data.d.ts +1 -1
  285. package/dist/tools/macro/fred-data.js +44 -9
  286. package/dist/tools/macro/fred-data.js.map +1 -1
  287. package/dist/tools/market/crypto-history.js +21 -3
  288. package/dist/tools/market/crypto-history.js.map +1 -1
  289. package/dist/tools/market/crypto-price.js +4 -2
  290. package/dist/tools/market/crypto-price.js.map +1 -1
  291. package/dist/tools/market/screen-stocks.d.ts +18 -0
  292. package/dist/tools/market/screen-stocks.js +252 -0
  293. package/dist/tools/market/screen-stocks.js.map +1 -0
  294. package/dist/tools/market/search-ticker.js +161 -9
  295. package/dist/tools/market/search-ticker.js.map +1 -1
  296. package/dist/tools/market/stock-history.d.ts +2 -2
  297. package/dist/tools/market/stock-history.js +27 -8
  298. package/dist/tools/market/stock-history.js.map +1 -1
  299. package/dist/tools/market/stock-quote.js +6 -4
  300. package/dist/tools/market/stock-quote.js.map +1 -1
  301. package/dist/tools/options/greeks.js +1 -2
  302. package/dist/tools/options/greeks.js.map +1 -1
  303. package/dist/tools/options/option-chain.js +27 -9
  304. package/dist/tools/options/option-chain.js.map +1 -1
  305. package/dist/tools/portfolio/alerts.d.ts +15 -0
  306. package/dist/tools/portfolio/alerts.js +357 -0
  307. package/dist/tools/portfolio/alerts.js.map +1 -0
  308. package/dist/tools/portfolio/correlation.d.ts +1 -1
  309. package/dist/tools/portfolio/correlation.js +34 -14
  310. package/dist/tools/portfolio/correlation.js.map +1 -1
  311. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  312. package/dist/tools/portfolio/daily-report.js +83 -0
  313. package/dist/tools/portfolio/daily-report.js.map +1 -0
  314. package/dist/tools/portfolio/holdings-overlap.d.ts +8 -0
  315. package/dist/tools/portfolio/holdings-overlap.js +112 -0
  316. package/dist/tools/portfolio/holdings-overlap.js.map +1 -0
  317. package/dist/tools/portfolio/notifications.d.ts +7 -0
  318. package/dist/tools/portfolio/notifications.js +43 -0
  319. package/dist/tools/portfolio/notifications.js.map +1 -0
  320. package/dist/tools/portfolio/predictions.d.ts +12 -6
  321. package/dist/tools/portfolio/predictions.js +338 -88
  322. package/dist/tools/portfolio/predictions.js.map +1 -1
  323. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  324. package/dist/tools/portfolio/risk-analysis.js +46 -7
  325. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  326. package/dist/tools/portfolio/tracker.d.ts +4 -3
  327. package/dist/tools/portfolio/tracker.js +247 -102
  328. package/dist/tools/portfolio/tracker.js.map +1 -1
  329. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  330. package/dist/tools/portfolio/watchlist.js +209 -101
  331. package/dist/tools/portfolio/watchlist.js.map +1 -1
  332. package/dist/tools/sentiment/reddit-sentiment.js +24 -11
  333. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  334. package/dist/tools/sentiment/sentiment-summary.js +71 -14
  335. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  336. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  337. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  338. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  339. package/dist/tools/sentiment/twitter-sentiment.js +13 -6
  340. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  341. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  342. package/dist/tools/sentiment/untrusted-text.js +17 -0
  343. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  344. package/dist/tools/sentiment/web-search.js +37 -12
  345. package/dist/tools/sentiment/web-search.js.map +1 -1
  346. package/dist/tools/sentiment/web-sentiment.js +16 -4
  347. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  348. package/dist/tools/technical/backtest.d.ts +3 -3
  349. package/dist/tools/technical/backtest.js +65 -44
  350. package/dist/tools/technical/backtest.js.map +1 -1
  351. package/dist/tools/technical/indicators.js +24 -8
  352. package/dist/tools/technical/indicators.js.map +1 -1
  353. package/dist/types/index.d.ts +3 -3
  354. package/dist/types/index.js.map +1 -1
  355. package/dist/types/market.d.ts +1 -0
  356. package/dist/types/options.d.ts +10 -0
  357. package/dist/types/portfolio.d.ts +41 -4
  358. package/dist/workflows/compare-assets.d.ts +0 -3
  359. package/dist/workflows/compare-assets.js +55 -10
  360. package/dist/workflows/compare-assets.js.map +1 -1
  361. package/dist/workflows/index.d.ts +3 -4
  362. package/dist/workflows/index.js +3 -3
  363. package/dist/workflows/index.js.map +1 -1
  364. package/dist/workflows/options-screener.d.ts +0 -3
  365. package/dist/workflows/options-screener.js +88 -14
  366. package/dist/workflows/options-screener.js.map +1 -1
  367. package/dist/workflows/portfolio-builder.d.ts +0 -3
  368. package/dist/workflows/portfolio-builder.js +7 -11
  369. package/dist/workflows/portfolio-builder.js.map +1 -1
  370. package/gui/server/ask-user-bridge.ts +82 -0
  371. package/gui/server/automation-heartbeat.ts +97 -0
  372. package/gui/server/background-quotes.ts +97 -1
  373. package/gui/server/chat-event-adapter.ts +32 -10
  374. package/gui/server/chat-run-session.ts +16 -0
  375. package/gui/server/gui-session-manager.ts +5 -0
  376. package/gui/server/invoke-tool.ts +144 -1
  377. package/gui/server/live-chat-event-adapter.ts +21 -6
  378. package/gui/server/market-state-api.ts +315 -0
  379. package/gui/server/model-setup.ts +149 -2
  380. package/gui/server/private-api-access.ts +62 -0
  381. package/gui/server/projector.ts +58 -11
  382. package/gui/server/prompt-observation.ts +58 -0
  383. package/gui/server/quote-snapshot-store.ts +50 -0
  384. package/gui/server/server.ts +236 -376
  385. package/gui/server/session-actions.ts +186 -1
  386. package/gui/server/session-entry-wait.ts +81 -0
  387. package/gui/server/shutdown.ts +47 -0
  388. package/gui/server/tool-invoke-ack.ts +49 -0
  389. package/gui/server/tool-metadata.ts +23 -10
  390. package/gui/server/websocket.ts +13 -3
  391. package/gui/server/writer-lock.ts +6 -2
  392. package/gui/server/ws-hub.ts +292 -0
  393. package/gui/shared/chat-events.ts +16 -1
  394. package/gui/shared/event-reducer.ts +24 -6
  395. package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
  396. package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
  397. package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
  398. package/gui/web/dist/index.html +2 -2
  399. package/package.json +22 -12
  400. package/src/analysts/contracts.ts +10 -23
  401. package/src/analysts/orchestrator.ts +8 -43
  402. package/src/cli.ts +37 -13
  403. package/src/config.ts +99 -7
  404. package/src/index.ts +1 -1
  405. package/src/infra/browser.ts +4 -2
  406. package/src/infra/cache.ts +41 -30
  407. package/src/infra/http-client.ts +72 -6
  408. package/src/infra/index.ts +7 -10
  409. package/src/infra/native-dependencies.ts +8 -3
  410. package/src/infra/node-version.ts +3 -1
  411. package/src/infra/opencandle-paths.ts +3 -14
  412. package/src/infra/rate-limiter.ts +32 -20
  413. package/src/market-state/alert-conditions.ts +82 -0
  414. package/src/market-state/alert-runner.ts +863 -0
  415. package/src/market-state/daily-report.ts +247 -0
  416. package/src/market-state/local-automation-service.ts +162 -0
  417. package/src/market-state/notification-delivery.ts +158 -0
  418. package/src/market-state/resolve-for-mutation.ts +24 -0
  419. package/src/market-state/resolve.ts +112 -0
  420. package/src/market-state/service.ts +2344 -0
  421. package/src/memory/index.ts +7 -7
  422. package/src/memory/manager.ts +57 -26
  423. package/src/memory/retrieval.ts +8 -7
  424. package/src/memory/sqlite.ts +407 -6
  425. package/src/memory/storage.ts +8 -17
  426. package/src/memory/tool-defaults.ts +60 -39
  427. package/src/memory/types.ts +7 -3
  428. package/src/monitor.ts +121 -0
  429. package/src/onboarding/connect.ts +10 -33
  430. package/src/onboarding/credential-interceptor.ts +3 -15
  431. package/src/onboarding/degradation-accumulator.ts +1 -3
  432. package/src/onboarding/providers.ts +9 -40
  433. package/src/onboarding/state.ts +4 -15
  434. package/src/onboarding/tool-helpers.ts +2 -9
  435. package/src/onboarding/tool-tags.ts +6 -6
  436. package/src/onboarding/validation.ts +14 -20
  437. package/src/pi/opencandle-extension.ts +795 -120
  438. package/src/pi/session.ts +7 -5
  439. package/src/pi/setup.ts +61 -33
  440. package/src/pi/tool-adapter.ts +5 -2
  441. package/src/prompts/context-builder.ts +143 -21
  442. package/src/prompts/disclaimer.ts +1 -1
  443. package/src/prompts/policy-cards.ts +220 -0
  444. package/src/prompts/sections.ts +4 -4
  445. package/src/prompts/symbol-preflight.ts +80 -0
  446. package/src/prompts/workflow-prompts.ts +231 -28
  447. package/src/providers/alpha-vantage.ts +82 -40
  448. package/src/providers/coingecko.ts +2 -5
  449. package/src/providers/errors.ts +9 -0
  450. package/src/providers/exa-search.ts +24 -22
  451. package/src/providers/fear-greed.ts +1 -1
  452. package/src/providers/finnhub.ts +7 -6
  453. package/src/providers/fred.ts +3 -3
  454. package/src/providers/index.ts +14 -6
  455. package/src/providers/reddit.ts +17 -6
  456. package/src/providers/sec-edgar.ts +235 -5
  457. package/src/providers/tradingview.ts +399 -0
  458. package/src/providers/twitter.ts +6 -8
  459. package/src/providers/web-search.ts +30 -20
  460. package/src/providers/with-fallback.ts +8 -7
  461. package/src/providers/wrap-provider.ts +15 -10
  462. package/src/providers/yahoo-finance.ts +292 -20
  463. package/src/routing/classify-intent.ts +186 -4
  464. package/src/routing/defaults.ts +4 -4
  465. package/src/routing/entity-extractor.ts +428 -28
  466. package/src/routing/fund-symbols.ts +58 -0
  467. package/src/routing/horizon.ts +7 -0
  468. package/src/routing/index.ts +60 -16
  469. package/src/routing/legacy-rule-router.ts +13 -0
  470. package/src/routing/planning.ts +823 -0
  471. package/src/routing/route-manifest.ts +309 -0
  472. package/src/routing/router-llm-client.ts +4 -4
  473. package/src/routing/router-prompt.ts +52 -52
  474. package/src/routing/router-types.ts +18 -0
  475. package/src/routing/router.ts +717 -20
  476. package/src/routing/slot-resolver.ts +75 -14
  477. package/src/routing/symbol-disambiguator.ts +72 -0
  478. package/src/routing/turn-context.ts +108 -0
  479. package/src/routing/types.ts +15 -1
  480. package/src/runtime/answer-contracts.ts +672 -0
  481. package/src/runtime/artifact-contracts.ts +77 -0
  482. package/src/runtime/planning-evidence.ts +682 -0
  483. package/src/runtime/prompt-step.ts +1 -16
  484. package/src/runtime/run-context.ts +12 -2
  485. package/src/runtime/session-coordinator.ts +297 -56
  486. package/src/runtime/session-title.ts +60 -0
  487. package/src/runtime/tool-defaults-wrapper.ts +1 -3
  488. package/src/runtime/validation.ts +1 -4
  489. package/src/runtime/workflow-events.ts +7 -7
  490. package/src/runtime/workflow-runner.ts +5 -11
  491. package/src/sentiment/adapters/finnhub.ts +7 -2
  492. package/src/sentiment/adapters/reddit.ts +2 -2
  493. package/src/sentiment/adapters/twitter.ts +1 -1
  494. package/src/sentiment/adapters/web.ts +1 -1
  495. package/src/sentiment/index.ts +16 -26
  496. package/src/sentiment/keywords.ts +26 -4
  497. package/src/sentiment/pipeline.ts +15 -4
  498. package/src/sentiment/scorer.ts +1 -1
  499. package/src/sentiment/store.ts +2 -2
  500. package/src/sentiment/trends.ts +9 -3
  501. package/src/sentiment/types.ts +5 -4
  502. package/src/system-prompt.ts +7 -3
  503. package/src/tool-kit.ts +10 -9
  504. package/src/tools/fundamentals/company-overview.ts +20 -10
  505. package/src/tools/fundamentals/comps.ts +69 -56
  506. package/src/tools/fundamentals/dcf.ts +146 -96
  507. package/src/tools/fundamentals/earnings.ts +17 -7
  508. package/src/tools/fundamentals/financials.ts +17 -8
  509. package/src/tools/fundamentals/sec-filings.ts +52 -8
  510. package/src/tools/index.ts +53 -38
  511. package/src/tools/interaction/ask-user.ts +22 -10
  512. package/src/tools/interaction/twitter-login.ts +17 -5
  513. package/src/tools/macro/fear-greed.ts +2 -2
  514. package/src/tools/macro/fred-data.ts +80 -42
  515. package/src/tools/market/crypto-history.ts +25 -4
  516. package/src/tools/market/crypto-price.ts +7 -7
  517. package/src/tools/market/screen-stocks.ts +279 -0
  518. package/src/tools/market/search-ticker.ts +219 -18
  519. package/src/tools/market/stock-history.ts +38 -13
  520. package/src/tools/market/stock-quote.ts +11 -8
  521. package/src/tools/options/greeks.ts +5 -6
  522. package/src/tools/options/option-chain.ts +47 -18
  523. package/src/tools/portfolio/alerts.ts +457 -0
  524. package/src/tools/portfolio/correlation.ts +48 -21
  525. package/src/tools/portfolio/daily-report.ts +101 -0
  526. package/src/tools/portfolio/holdings-overlap.ts +139 -0
  527. package/src/tools/portfolio/notifications.ts +45 -0
  528. package/src/tools/portfolio/predictions.ts +407 -107
  529. package/src/tools/portfolio/risk-analysis.ts +47 -8
  530. package/src/tools/portfolio/tracker.ts +271 -110
  531. package/src/tools/portfolio/watchlist.ts +251 -116
  532. package/src/tools/sentiment/reddit-sentiment.ts +51 -25
  533. package/src/tools/sentiment/sentiment-summary.ts +116 -35
  534. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  535. package/src/tools/sentiment/twitter-sentiment.ts +23 -16
  536. package/src/tools/sentiment/untrusted-text.ts +21 -0
  537. package/src/tools/sentiment/web-search.ts +52 -16
  538. package/src/tools/sentiment/web-sentiment.ts +27 -11
  539. package/src/tools/technical/backtest.ts +78 -47
  540. package/src/tools/technical/indicators.ts +40 -17
  541. package/src/types/index.ts +8 -3
  542. package/src/types/market.ts +1 -0
  543. package/src/types/options.ts +17 -0
  544. package/src/types/portfolio.ts +46 -4
  545. package/src/types/sentiment.ts +2 -2
  546. package/src/workflows/compare-assets.ts +67 -19
  547. package/src/workflows/index.ts +3 -4
  548. package/src/workflows/options-screener.ts +98 -22
  549. package/src/workflows/portfolio-builder.ts +40 -29
  550. package/dist/runtime/index.d.ts +0 -16
  551. package/dist/runtime/index.js +0 -10
  552. package/dist/runtime/index.js.map +0 -1
  553. package/dist/runtime/provider-ids.d.ts +0 -14
  554. package/dist/runtime/provider-ids.js +0 -14
  555. package/dist/runtime/provider-ids.js.map +0 -1
  556. package/dist/workflows/types.d.ts +0 -4
  557. package/dist/workflows/types.js +0 -2
  558. package/dist/workflows/types.js.map +0 -1
  559. package/gui/web/dist/assets/CatalogOverlay-D1ImSJTe.js +0 -1
  560. package/gui/web/dist/assets/index-DBrWq43L.css +0 -1
  561. package/gui/web/dist/assets/index-RflHaj0y.js +0 -67
  562. package/src/runtime/index.ts +0 -55
  563. package/src/runtime/provider-ids.ts +0 -15
  564. package/src/workflows/types.ts +0 -4
@@ -1,11 +1,11 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getHistory } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
4
3
  import { wrapProvider } from "../../providers/wrap-provider.js";
5
- import { computeSMA, computeRSI } from "./indicators.js";
4
+ import { getHistory } from "../../providers/yahoo-finance.js";
6
5
  import type { OHLCV } from "../../types/market.js";
6
+ import { computeRSI, computeSMA } from "./indicators.js";
7
7
 
8
- export type Strategy = "sma_crossover" | "rsi_mean_reversion";
8
+ export type Strategy = "sma_crossover" | "sma_50_200_crossover" | "rsi_mean_reversion";
9
9
 
10
10
  export interface BacktestResult {
11
11
  strategy: string;
@@ -20,28 +20,32 @@ export interface BacktestResult {
20
20
 
21
21
  export function runBacktest(bars: OHLCV[], strategy: Strategy): BacktestResult {
22
22
  const closes = bars.map((b) => b.close);
23
- const buyAndHoldReturn = closes.length > 1
24
- ? (closes[closes.length - 1] - closes[0]) / closes[0]
25
- : 0;
26
23
 
27
24
  if (strategy === "sma_crossover") {
28
- return backtestSMACrossover(bars, closes);
25
+ return backtestSMACrossover(bars, closes, 20, 50, strategy);
26
+ }
27
+ if (strategy === "sma_50_200_crossover") {
28
+ return backtestSMACrossover(bars, closes, 50, 200, strategy);
29
29
  }
30
30
  return backtestRSIMeanReversion(bars, closes);
31
31
  }
32
32
 
33
- function backtestSMACrossover(bars: OHLCV[], closes: number[]): BacktestResult {
34
- const sma20 = computeSMA(closes, 20);
35
- const sma50 = computeSMA(closes, 50);
33
+ function backtestSMACrossover(
34
+ bars: OHLCV[],
35
+ closes: number[],
36
+ shortWindow: number,
37
+ longWindow: number,
38
+ strategyName: Strategy,
39
+ ): BacktestResult {
40
+ const shortSma = computeSMA(closes, shortWindow);
41
+ const longSma = computeSMA(closes, longWindow);
36
42
 
37
- if (sma50.length === 0) {
38
- return emptyResult("sma_crossover", closes);
43
+ if (longSma.length === 0) {
44
+ return emptyResult(strategyName, closes);
39
45
  }
40
46
 
41
- // Align: SMA(20) starts at index 19, SMA(50) at index 49
42
- // sma20[i] corresponds to closes[i + 19], sma50[i] to closes[i + 49]
43
- const offset20 = 19;
44
- const offset50 = 49;
47
+ const shortOffset = shortWindow - 1;
48
+ const longOffset = longWindow - 1;
45
49
 
46
50
  let position = false;
47
51
  let entryPrice = 0;
@@ -50,19 +54,19 @@ function backtestSMACrossover(bars: OHLCV[], closes: number[]): BacktestResult {
50
54
  let peak = 1.0;
51
55
  let maxDd = 0;
52
56
 
53
- for (let i = 0; i < sma50.length; i++) {
54
- const barIdx = i + offset50;
55
- const sma20Idx = i + (offset50 - offset20);
56
- const s20 = sma20[sma20Idx];
57
- const s50 = sma50[i];
57
+ for (let i = 0; i < longSma.length; i++) {
58
+ const barIdx = i + longOffset;
59
+ const shortSmaIdx = i + (longOffset - shortOffset);
60
+ const sShort = shortSma[shortSmaIdx];
61
+ const sLong = longSma[i];
58
62
  const price = closes[barIdx];
59
63
 
60
- if (!position && s20 > s50) {
64
+ if (!position && sShort > sLong && price > 0) {
61
65
  // Buy signal
62
66
  position = true;
63
67
  entryPrice = price;
64
68
  tradeLog.push({ type: "buy", date: bars[barIdx].date, price });
65
- } else if (position && s20 < s50) {
69
+ } else if (position && sShort < sLong) {
66
70
  // Sell signal
67
71
  const pnl = (price - entryPrice) / entryPrice;
68
72
  equity *= 1 + pnl;
@@ -71,9 +75,7 @@ function backtestSMACrossover(bars: OHLCV[], closes: number[]): BacktestResult {
71
75
  }
72
76
 
73
77
  // Track mark-to-market equity for accurate drawdown
74
- const currentEquity = position
75
- ? equity * (1 + (price - entryPrice) / entryPrice)
76
- : equity;
78
+ const currentEquity = position ? equity * (1 + (price - entryPrice) / entryPrice) : equity;
77
79
  if (currentEquity > peak) peak = currentEquity;
78
80
  const dd = (peak - currentEquity) / peak;
79
81
  if (dd > maxDd) maxDd = dd;
@@ -87,7 +89,7 @@ function backtestSMACrossover(bars: OHLCV[], closes: number[]): BacktestResult {
87
89
  tradeLog.push({ type: "sell", date: bars[bars.length - 1].date, price: lastPrice, pnl });
88
90
  }
89
91
 
90
- return buildResult("sma_crossover", equity - 1, closes, tradeLog, maxDd);
92
+ return buildResult(strategyName, equity - 1, closes, tradeLog, maxDd);
91
93
  }
92
94
 
93
95
  function backtestRSIMeanReversion(bars: OHLCV[], closes: number[]): BacktestResult {
@@ -111,7 +113,7 @@ function backtestRSIMeanReversion(bars: OHLCV[], closes: number[]): BacktestResu
111
113
  const r = rsi[i];
112
114
  const price = closes[barIdx];
113
115
 
114
- if (!position && r < 30) {
116
+ if (!position && r < 30 && price > 0) {
115
117
  // RSI oversold → buy
116
118
  position = true;
117
119
  entryPrice = price;
@@ -125,9 +127,7 @@ function backtestRSIMeanReversion(bars: OHLCV[], closes: number[]): BacktestResu
125
127
  }
126
128
 
127
129
  // Track mark-to-market equity for accurate drawdown
128
- const currentEquity = position
129
- ? equity * (1 + (price - entryPrice) / entryPrice)
130
- : equity;
130
+ const currentEquity = position ? equity * (1 + (price - entryPrice) / entryPrice) : equity;
131
131
  if (currentEquity > peak) peak = currentEquity;
132
132
  const dd = (peak - currentEquity) / peak;
133
133
  if (dd > maxDd) maxDd = dd;
@@ -153,9 +153,8 @@ function buildResult(
153
153
  ): BacktestResult {
154
154
  const sellTrades = tradeLog.filter((t) => t.type === "sell" && t.pnl != null);
155
155
  const wins = sellTrades.filter((t) => t.pnl! > 0).length;
156
- const buyAndHoldReturn = closes.length > 1
157
- ? (closes[closes.length - 1] - closes[0]) / closes[0]
158
- : 0;
156
+ const buyAndHoldReturn =
157
+ closes.length > 1 && closes[0] > 0 ? (closes[closes.length - 1] - closes[0]) / closes[0] : 0;
159
158
 
160
159
  return {
161
160
  strategy,
@@ -173,9 +172,8 @@ function emptyResult(strategy: string, closes: number[]): BacktestResult {
173
172
  return {
174
173
  strategy,
175
174
  totalReturn: 0,
176
- buyAndHoldReturn: closes.length > 1
177
- ? (closes[closes.length - 1] - closes[0]) / closes[0]
178
- : 0,
175
+ buyAndHoldReturn:
176
+ closes.length > 1 && closes[0] > 0 ? (closes[closes.length - 1] - closes[0]) / closes[0] : 0,
179
177
  trades: 0,
180
178
  wins: 0,
181
179
  winRate: 0,
@@ -187,11 +185,20 @@ function emptyResult(strategy: string, closes: number[]): BacktestResult {
187
185
  const params = Type.Object({
188
186
  symbol: Type.String({ description: "Stock ticker symbol (e.g. AAPL, MSFT, SPY)" }),
189
187
  strategy: Type.Union(
190
- [Type.Literal("sma_crossover"), Type.Literal("rsi_mean_reversion")],
191
- { description: "Strategy: sma_crossover (buy when SMA20 > SMA50, sell on reverse) or rsi_mean_reversion (buy when RSI < 30, sell when RSI > 70)" },
188
+ [
189
+ Type.Literal("sma_crossover"),
190
+ Type.Literal("sma_50_200_crossover"),
191
+ Type.Literal("rsi_mean_reversion"),
192
+ ],
193
+ {
194
+ description:
195
+ "Strategy: sma_crossover (buy when SMA20 > SMA50, sell on reverse), sma_50_200_crossover (buy when SMA50 > SMA200, sell on reverse), or rsi_mean_reversion (buy when RSI < 30, sell when RSI > 70)",
196
+ },
192
197
  ),
193
198
  period: Type.Optional(
194
- Type.String({ description: "Historical period to backtest: 1y, 2y, 5y. Default: 2y" }),
199
+ Type.Union([Type.Literal("1y"), Type.Literal("2y"), Type.Literal("5y")], {
200
+ description: "Historical period to backtest: 1y, 2y, 5y. Default: 2y",
201
+ }),
195
202
  ),
196
203
  });
197
204
 
@@ -199,23 +206,31 @@ export const backtestTool: AgentTool<typeof params> = {
199
206
  name: "backtest_strategy",
200
207
  label: "Backtest Strategy",
201
208
  description:
202
- "Backtest a simple trading strategy against historical data. Supported strategies: SMA crossover (SMA20/SMA50) and RSI mean-reversion (buy <30, sell >70). Returns total return, win rate, max drawdown, and comparison to buy-and-hold.",
209
+ "Backtest a simple trading strategy against historical data. Supported strategies: SMA crossover (SMA20/SMA50), standard long-term SMA crossover (SMA50/SMA200), and RSI mean-reversion (buy <30, sell >70). Returns total return, win rate, max drawdown, and comparison to buy-and-hold.",
203
210
  parameters: params,
204
- async execute(toolCallId, args) {
211
+ async execute(_toolCallId, args) {
205
212
  const symbol = args.symbol.toUpperCase();
206
213
  const period = args.period ?? "2y";
207
214
  const historyResult = await wrapProvider("yahoo", () => getHistory(symbol, period, "1d"));
208
215
  if (historyResult.status === "unavailable") {
209
216
  return {
210
- content: [{ type: "text", text: `⚠ Backtest unavailable for ${symbol} (${historyResult.reason}).` }],
217
+ content: [
218
+ { type: "text", text: `⚠ Backtest unavailable for ${symbol} (${historyResult.reason}).` },
219
+ ],
211
220
  details: null as any,
212
221
  };
213
222
  }
214
223
  const bars = historyResult.data;
215
224
 
216
- if (bars.length < 60) {
225
+ const minBars = requiredBarsForStrategy(args.strategy);
226
+ if (bars.length < minBars) {
217
227
  return {
218
- content: [{ type: "text", text: `Insufficient data for backtesting ${symbol} (need 60+ days, got ${bars.length})` }],
228
+ content: [
229
+ {
230
+ type: "text",
231
+ text: `Insufficient data for backtesting ${symbol} (need ${minBars}+ days, got ${bars.length})`,
232
+ },
233
+ ],
219
234
  details: null,
220
235
  };
221
236
  }
@@ -224,7 +239,7 @@ export const backtestTool: AgentTool<typeof params> = {
224
239
 
225
240
  const outperformance = result.totalReturn - result.buyAndHoldReturn;
226
241
  const lines = [
227
- `**${symbol} Backtest: ${args.strategy}** (${bars[0].date} to ${bars[bars.length - 1].date}, ${bars.length} days)`,
242
+ `**${symbol} Backtest: ${strategyLabel(args.strategy)}** (${bars[0].date} to ${bars[bars.length - 1].date}, ${bars.length} days)`,
228
243
  ``,
229
244
  `Strategy Return: ${(result.totalReturn * 100).toFixed(2)}%`,
230
245
  `Buy & Hold Return: ${(result.buyAndHoldReturn * 100).toFixed(2)}%`,
@@ -244,3 +259,19 @@ export const backtestTool: AgentTool<typeof params> = {
244
259
  };
245
260
  },
246
261
  };
262
+
263
+ function requiredBarsForStrategy(strategy: Strategy): number {
264
+ if (strategy === "sma_50_200_crossover") return 200;
265
+ return 60;
266
+ }
267
+
268
+ function strategyLabel(strategy: Strategy): string {
269
+ switch (strategy) {
270
+ case "sma_crossover":
271
+ return "SMA 20/50 Crossover";
272
+ case "sma_50_200_crossover":
273
+ return "SMA 50/200 Crossover";
274
+ case "rsi_mean_reversion":
275
+ return "RSI Mean Reversion";
276
+ }
277
+ }
@@ -1,7 +1,7 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getHistory } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
4
3
  import { wrapProvider } from "../../providers/wrap-provider.js";
4
+ import { getHistory } from "../../providers/yahoo-finance.js";
5
5
  import type { OHLCV } from "../../types/market.js";
6
6
 
7
7
  // --- Volume-based indicators ---
@@ -46,13 +46,18 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
46
46
  description:
47
47
  "Compute technical indicators (SMA, EMA, RSI, MACD, Bollinger Bands) from historical price data. All computed locally — no API dependency.",
48
48
  parameters: params,
49
- async execute(toolCallId, args) {
49
+ async execute(_toolCallId, args) {
50
50
  const symbol = args.symbol.toUpperCase();
51
51
  const range = args.range ?? "1y";
52
52
  const result = await wrapProvider("yahoo", () => getHistory(symbol, range, "1d"));
53
53
  if (result.status === "unavailable") {
54
54
  return {
55
- content: [{ type: "text", text: `⚠ Technical indicators unavailable for ${symbol} (${result.reason}).` }],
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: `⚠ Technical indicators unavailable for ${symbol} (${result.reason}).`,
59
+ },
60
+ ],
56
61
  details: null as any,
57
62
  };
58
63
  }
@@ -61,15 +66,18 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
61
66
 
62
67
  if (closes.length < 26) {
63
68
  return {
64
- content: [{ type: "text", text: `Insufficient data for ${symbol} (need 26+ bars, got ${closes.length})` }],
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: `Insufficient data for ${symbol} (need 26+ bars, got ${closes.length})`,
73
+ },
74
+ ],
65
75
  details: null,
66
76
  };
67
77
  }
68
78
 
69
79
  const sma20 = computeSMA(closes, 20);
70
80
  const sma50 = computeSMA(closes, 50);
71
- const ema12 = computeEMA(closes, 12);
72
- const ema26 = computeEMA(closes, 26);
73
81
  const rsi = computeRSI(closes, 14);
74
82
  const macd = computeMACD(closes);
75
83
  const bb = computeBollingerBands(closes, 20, 2);
@@ -81,9 +89,12 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
81
89
  const latestMacd = macd[macd.length - 1];
82
90
  const latestBB = bb[bb.length - 1];
83
91
  const latestVwap = vwap[vwap.length - 1];
84
- const obvTrend = obv.length >= 20
85
- ? (obv[obv.length - 1] > obv[obv.length - 20] ? "Rising" : "Falling")
86
- : "N/A";
92
+ const obvTrend =
93
+ obv.length >= 20
94
+ ? obv[obv.length - 1] > obv[obv.length - 20]
95
+ ? "Rising"
96
+ : "Falling"
97
+ : "N/A";
87
98
 
88
99
  const lines = [
89
100
  `**${symbol} Technical Analysis** (${bars[0].date} to ${bars[bars.length - 1].date})`,
@@ -105,7 +116,13 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
105
116
  range,
106
117
  prices: closes,
107
118
  dates: bars.map((b) => b.date),
108
- sma20, sma50, rsi, macd, bb, obv, vwap,
119
+ sma20,
120
+ sma50,
121
+ rsi,
122
+ macd,
123
+ bb,
124
+ obv,
125
+ vwap,
109
126
  },
110
127
  };
111
128
  },
@@ -243,16 +260,22 @@ function trendSummary(
243
260
 
244
261
  if (latestSma20 && price > latestSma20) signals.push("Price above SMA(20) — short-term bullish");
245
262
  if (latestSma20 && price < latestSma20) signals.push("Price below SMA(20) — short-term bearish");
246
- if (latestSma20 && latestSma50 && latestSma20 > latestSma50) signals.push("Golden cross pattern (SMA20 > SMA50)");
247
- if (latestSma20 && latestSma50 && latestSma20 < latestSma50) signals.push("Death cross pattern (SMA20 < SMA50)");
263
+ if (latestSma20 && latestSma50 && latestSma20 > latestSma50)
264
+ signals.push("Golden cross pattern (SMA20 > SMA50)");
265
+ if (latestSma20 && latestSma50 && latestSma20 < latestSma50)
266
+ signals.push("Death cross pattern (SMA20 < SMA50)");
248
267
  if (rsi != null && rsi >= 70) signals.push("RSI overbought — potential reversal");
249
268
  if (rsi != null && rsi <= 30) signals.push("RSI oversold — potential bounce");
250
269
  if (macd && macd.histogram > 0) signals.push("MACD bullish (histogram positive)");
251
270
  if (macd && macd.histogram < 0) signals.push("MACD bearish (histogram negative)");
252
- if (obvTrend === "Rising" && price > (latestSma20 ?? 0)) signals.push("Volume confirming price advance (OBV rising)");
253
- if (obvTrend === "Falling" && price < (latestSma20 ?? Infinity)) signals.push("Volume confirming price decline (OBV falling)");
254
- if (vwap != null && price > vwap) signals.push("Price above cumulative VWAP — bullish volume-weighted bias");
255
- if (vwap != null && price < vwap) signals.push("Price below cumulative VWAP bearish volume-weighted bias");
271
+ if (obvTrend === "Rising" && price > (latestSma20 ?? 0))
272
+ signals.push("Volume confirming price advance (OBV rising)");
273
+ if (obvTrend === "Falling" && price < (latestSma20 ?? Infinity))
274
+ signals.push("Volume confirming price decline (OBV falling)");
275
+ if (vwap != null && price > vwap)
276
+ signals.push("Price above cumulative VWAP — bullish volume-weighted bias");
277
+ if (vwap != null && price < vwap)
278
+ signals.push("Price below cumulative VWAP — bearish volume-weighted bias");
256
279
 
257
280
  return signals.length > 0 ? "Signals: " + signals.join(" | ") : "No strong signals";
258
281
  }
@@ -1,10 +1,15 @@
1
- export type { StockQuote, OHLCV, CryptoPrice } from "./market.js";
2
1
  export type { CompanyOverview, EarningsData, FinancialStatement } from "./fundamentals.js";
3
2
  export type { FredObservation, FredSeries } from "./macro.js";
4
3
  export { FRED_SERIES } from "./macro.js";
4
+ export type { CryptoPrice, OHLCV, StockQuote } from "./market.js";
5
5
  export type { Greeks, OptionContract, OptionsChain } from "./options.js";
6
- export type { Position, PortfolioSummary, RiskMetrics, TechnicalIndicators } from "./portfolio.js";
7
- export type { FearGreedData, RedditSentimentResult, WebSearchResult, WebSearchEnvelope } from "./sentiment.js";
6
+ export type { PortfolioSummary, Position, RiskMetrics, TechnicalIndicators } from "./portfolio.js";
7
+ export type {
8
+ FearGreedData,
9
+ RedditSentimentResult,
10
+ WebSearchEnvelope,
11
+ WebSearchResult,
12
+ } from "./sentiment.js";
8
13
 
9
14
  /**
10
15
  * Handler for `ask_user` tool invocations in non-UI contexts (e.g. test harness).
@@ -13,6 +13,7 @@ export interface StockQuote {
13
13
  week52High: number;
14
14
  week52Low: number;
15
15
  timestamp: number;
16
+ currency?: string | null;
16
17
  }
17
18
 
18
19
  export interface OHLCV {
@@ -21,6 +21,22 @@ export interface OptionContract {
21
21
  greeks: Greeks;
22
22
  }
23
23
 
24
+ export type OptionsMarketSession = "pre_market" | "regular" | "after_hours" | "closed";
25
+
26
+ export type OptionsBidAskState =
27
+ | "live_quotes"
28
+ | "closed_market_or_stale_quotes"
29
+ | "live_zero_bid_ask"
30
+ | "mixed_or_unknown";
31
+
32
+ export interface OptionsQuoteStatus {
33
+ marketSession: OptionsMarketSession;
34
+ bidAskState: OptionsBidAskState;
35
+ zeroBidAskContracts: number;
36
+ totalContracts: number;
37
+ warning?: string;
38
+ }
39
+
24
40
  export interface OptionsChain {
25
41
  symbol: string;
26
42
  underlyingPrice: number;
@@ -31,5 +47,6 @@ export interface OptionsChain {
31
47
  totalCallVolume: number;
32
48
  totalPutVolume: number;
33
49
  putCallRatio: number;
50
+ quoteStatus: OptionsQuoteStatus;
34
51
  fetchedAt: string;
35
52
  }
@@ -2,23 +2,33 @@ export interface Position {
2
2
  symbol: string;
3
3
  shares: number;
4
4
  avgCost: number;
5
+ currency: string;
5
6
  addedAt: string;
6
7
  }
7
8
 
8
9
  export interface PortfolioSummary {
9
10
  positions: Array<
10
11
  Position & {
11
- currentPrice: number;
12
- marketValue: number;
12
+ currentPrice: number | null;
13
+ marketValue: number | null;
13
14
  totalCost: number;
14
- pnl: number;
15
- pnlPercent: number;
15
+ pnl: number | null;
16
+ pnlPercent: number | null;
17
+ includedInTotals: boolean;
18
+ quoteStatus?: "ok" | "unavailable";
19
+ exclusionReason?: string;
16
20
  }
17
21
  >;
22
+ baseCurrency: string;
18
23
  totalValue: number;
19
24
  totalCost: number;
20
25
  totalPnl: number;
21
26
  totalPnlPercent: number;
27
+ excludedFromTotals: Array<{
28
+ symbol: string;
29
+ currency: string;
30
+ reason: string;
31
+ }>;
22
32
  }
23
33
 
24
34
  export interface RiskMetrics {
@@ -30,6 +40,38 @@ export interface RiskMetrics {
30
40
  var95: number; // 95% Value at Risk (daily)
31
41
  }
32
42
 
43
+ export interface FundHolding {
44
+ symbol: string;
45
+ name: string;
46
+ weight: number;
47
+ }
48
+
49
+ export interface FundHoldings {
50
+ symbol: string;
51
+ name?: string;
52
+ provider: string;
53
+ holdings: FundHolding[];
54
+ sectorWeights?: Record<string, number>;
55
+ }
56
+
57
+ export interface SharedFundHolding {
58
+ symbol: string;
59
+ name: string;
60
+ weights: Record<string, number>;
61
+ overlapWeight: number;
62
+ }
63
+
64
+ export interface FundOverlapPair {
65
+ symbols: [string, string];
66
+ overlapWeight: number;
67
+ sharedHoldings: SharedFundHolding[];
68
+ }
69
+
70
+ export interface FundHoldingsOverlap {
71
+ funds: FundHoldings[];
72
+ pairs: FundOverlapPair[];
73
+ }
74
+
33
75
  export interface TechnicalIndicators {
34
76
  symbol: string;
35
77
  period: string;
@@ -23,7 +23,7 @@ export interface TwitterSentimentResult {
23
23
  query: string;
24
24
  tweetCount: number;
25
25
  tweets: TwitterTweet[];
26
- sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
26
+ sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
27
27
  bullishCount: number;
28
28
  bearishCount: number;
29
29
  topMentions: string[];
@@ -63,7 +63,7 @@ export interface RedditSentimentResult {
63
63
  created: string;
64
64
  }>;
65
65
  topMentions: string[];
66
- sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
66
+ sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
67
67
  bullishCount: number;
68
68
  bearishCount: number;
69
69
  fetchedAt: string;
@@ -1,6 +1,7 @@
1
- import type { CompareAssetsSlots, SlotResolution } from "../routing/types.js";
2
1
  import { buildCompareAssetsPrompt } from "../prompts/workflow-prompts.js";
3
- import type { WorkflowPlan } from "./types.js";
2
+ import { areLikelyFundOrIndexSymbols, isFundOrIndexAssetScope } from "../routing/fund-symbols.js";
3
+ import { isLongInvestmentHorizon } from "../routing/horizon.js";
4
+ import type { CompareAssetsSlots, SlotResolution } from "../routing/types.js";
4
5
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
5
6
  import { promptStep } from "../runtime/prompt-step.js";
6
7
 
@@ -8,6 +9,59 @@ export function buildCompareAssetsWorkflowDefinition(
8
9
  resolution: SlotResolution<CompareAssetsSlots>,
9
10
  ): WorkflowDefinition {
10
11
  const symbols = resolution.resolved.symbols.join(", ");
12
+ const timeHorizon = resolution.resolved.timeHorizon;
13
+ const isMacroHedge = resolution.resolved.metrics?.includes("macro_hedge") ?? false;
14
+ const isInterestRateSensitive = resolution.resolved.metrics?.includes("interest_rates") ?? false;
15
+ const isOverlapComparison = resolution.resolved.metrics?.includes("overlap") ?? false;
16
+ const hasFundContext =
17
+ isFundOrIndexAssetScope(resolution.resolved.assetScope) ||
18
+ areLikelyFundOrIndexSymbols(resolution.resolved.symbols);
19
+ const shouldProbeFundOverlap =
20
+ !isOverlapComparison && isLongInvestmentHorizon(timeHorizon) && hasFundContext;
21
+ const evidenceList = resolution.resolved.metrics?.includes("sentiment")
22
+ ? "price, technical, risk, and sentiment data"
23
+ : isOverlapComparison
24
+ ? "quote, holdings-overlap, and correlation data"
25
+ : shouldProbeFundOverlap
26
+ ? "price, technical, risk, correlation, and holdings-overlap data when applicable"
27
+ : "price, technical, and risk data";
28
+ const horizonGuidance = timeHorizon
29
+ ? `
30
+ - Start by directly answering whether these assets are reasonable to compare for a ${timeHorizon} horizon.
31
+ - Rank the evidence for that horizon: near-term catalysts, earnings/guidance, estimate revisions, forward-looking valuation evidence, sentiment, macro sensitivity, and company-specific risks before long-term historical metrics.
32
+ - Treat unavailable forward-looking evidence as a caveat instead of replacing it with unrelated historical certainty.`
33
+ : "";
34
+ const macroHedgeGuidance = isMacroHedge
35
+ ? `
36
+ - Treat this as a macro hedge decision: explain each asset's hedge role, correlation regime, volatility/drawdown profile, and sensitivity to real yields, USD/liquidity, geopolitical shocks, and risk-off drawdowns.
37
+ - Do not let missing asset-specific metrics turn into a shallow default winner. Explain what each missing metric would have shown and how that lowers confidence.
38
+ - Give actionable conditional guidance: conditions under which each asset is the better capital-preservation hedge, diversifier, inflation hedge, duration hedge, liquidity sleeve, or higher-volatility asymmetric-upside sleeve.`
39
+ : "";
40
+ const interestRateGuidance = isInterestRateSensitive
41
+ ? `
42
+ - For the rate-sensitive premise, separate benign falling rates from recessionary cuts and sticky-inflation/rate-reversal scenarios before giving the final tilt.
43
+ - Discuss concentration or sector-exposure risk when one asset is narrower or more growth-heavy than the other.
44
+ - Say whether the rate evidence you used is historical/current data or forward-looking market-pricing evidence; do not treat historical Fed funds data as a forecast.`
45
+ : "";
46
+ const overlapGuidance = isOverlapComparison
47
+ ? `
48
+ - For ETF overlap prompts, synthesize the holdings-overlap and shared-exposure evidence first, with correlation only as supporting diversification context.
49
+ - State the diversification implication directly: deliberate factor tilt, accidental duplication, or genuinely differentiated exposure.
50
+ - If provider holdings coverage was partial or unavailable, say that before giving the practical next step.`
51
+ : shouldProbeFundOverlap
52
+ ? `
53
+ - If these assets are ETFs, funds, or index products, synthesize holdings-overlap and shared-exposure evidence before correlation when that provider evidence is available.
54
+ - Compare fund role, broad style or sector tilt, dividend/income versus growth tradeoffs, taxable-account dividend drag, and any fetched expense ratio, yield, or AUM evidence.
55
+ - If expense ratio, yield, AUM, or constituent detail is unavailable, name that verification gap instead of filling it with approximate fund facts.
56
+ - If provider holdings coverage was partial or unavailable, say that before giving the practical next step.`
57
+ : "";
58
+ const verdictInstruction = isOverlapComparison
59
+ ? "End with a concise verdict on whether the added fund improves diversification or mostly duplicates existing exposure."
60
+ : shouldProbeFundOverlap
61
+ ? "End with a concise verdict tied to the user's horizon, fund roles, and diversification needs rather than short-term timing."
62
+ : timeHorizon
63
+ ? `End with a concise verdict on which asset best fits the ${timeHorizon} horizon and why.`
64
+ : "End with a concise verdict on which asset looks strongest right now and why.";
11
65
 
12
66
  return {
13
67
  workflowType: "compare_assets",
@@ -16,24 +70,18 @@ export function buildCompareAssetsWorkflowDefinition(
16
70
  requiredInputs: ["symbols"],
17
71
  expectedOutputs: ["asset_data"],
18
72
  }),
19
- promptStep("compare_and_present", "Present side-by-side comparison", `Now present the side-by-side comparison for ${symbols}:
73
+ promptStep(
74
+ "compare_and_present",
75
+ "Present side-by-side comparison",
76
+ `Now present the side-by-side comparison for ${symbols}:
20
77
  - Keep any unavailable fundamentals marked as unavailable instead of retrying the same failed provider calls.
21
- - Use the price, technical, and risk data you already fetched to finish the comparison even if some fundamentals are missing.
22
- - End with a concise verdict on which asset looks strongest right now and why.`, {
23
- requiredInputs: ["asset_data"],
24
- expectedOutputs: ["comparison_summary"],
25
- }),
78
+ - Use the ${evidenceList} you already fetched to finish the comparison even if some fundamentals are missing.
79
+ - ${verdictInstruction}${horizonGuidance}${macroHedgeGuidance}${interestRateGuidance}${overlapGuidance}`,
80
+ {
81
+ requiredInputs: ["asset_data"],
82
+ expectedOutputs: ["comparison_summary"],
83
+ },
84
+ ),
26
85
  ],
27
86
  };
28
87
  }
29
-
30
- /** @deprecated Use buildCompareAssetsWorkflowDefinition instead */
31
- export function buildCompareAssetsWorkflow(
32
- resolution: SlotResolution<CompareAssetsSlots>,
33
- ): WorkflowPlan {
34
- const def = buildCompareAssetsWorkflowDefinition(resolution);
35
- return {
36
- initialPrompt: def.steps[0].prompt,
37
- followUps: def.steps.slice(1).map((s) => s.prompt),
38
- };
39
- }
@@ -1,4 +1,3 @@
1
- export { buildPortfolioWorkflow, buildPortfolioWorkflowDefinition } from "./portfolio-builder.js";
2
- export { buildCompareAssetsWorkflow, buildCompareAssetsWorkflowDefinition } from "./compare-assets.js";
3
- export { buildOptionsScreenerWorkflow, buildOptionsScreenerWorkflowDefinition } from "./options-screener.js";
4
- export type { WorkflowPlan } from "./types.js";
1
+ export { buildCompareAssetsWorkflowDefinition } from "./compare-assets.js";
2
+ export { buildOptionsScreenerWorkflowDefinition } from "./options-screener.js";
3
+ export { buildPortfolioWorkflowDefinition } from "./portfolio-builder.js";