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,46 +1,64 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { httpGet } from "../../infra/http-client.js";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { httpGet, httpPost } from "../../infra/http-client.js";
4
+ import { rateLimiter } from "../../infra/rate-limiter.js";
4
5
 
5
6
  const params = Type.Object({
6
7
  query: Type.String({
7
- description: "Search query — company name, ticker symbol, or crypto name (e.g. 'apple', 'AAPL', 'ethereum', 'bitcoin')",
8
+ description:
9
+ "Search query — company name, ticker symbol, or crypto name (e.g. 'apple', 'AAPL', 'ethereum', 'bitcoin')",
8
10
  }),
9
11
  });
10
12
 
11
13
  interface YahooSearchResponse {
12
- quotes: Array<{
13
- symbol: string;
14
- shortname?: string;
15
- longname?: string;
16
- quoteType: string;
17
- exchange: string;
18
- score: number;
14
+ quotes?: YahooSearchQuote[];
15
+ }
16
+
17
+ interface YahooSearchQuote {
18
+ symbol: string;
19
+ shortname?: string;
20
+ longname?: string;
21
+ quoteType: string;
22
+ exchange: string;
23
+ score: number;
24
+ }
25
+
26
+ interface TradingViewSearchResponse {
27
+ fields?: string[];
28
+ symbols?: Array<{
29
+ s: string;
30
+ f: unknown[];
19
31
  }>;
20
32
  }
21
33
 
34
+ const FALLBACK_SEARCH_MAX_RETRY_AFTER_MS = 1_000;
35
+
22
36
  export const searchTickerTool: AgentTool<typeof params> = {
23
37
  name: "search_ticker",
24
38
  label: "Search Ticker",
25
39
  description:
26
40
  "Search for any ticker symbol — stocks, crypto, ETFs, indices, forex. Returns matching symbols with names and exchange info. Use this when you don't know the exact ticker for an asset.",
27
41
  parameters: params,
28
- async execute(toolCallId, args) {
29
- const url = `https://query1.finance.yahoo.com/v1/finance/search?q=${encodeURIComponent(args.query)}&quotesCount=10&newsCount=0`;
30
- const data = await httpGet<YahooSearchResponse>(url, {
31
- headers: { "User-Agent": "OpenCandle/1.0" },
32
- });
42
+ async execute(_toolCallId, args) {
43
+ const yahoo = await searchYahoo(args.query);
44
+ const fallback = yahoo.quotes.length > 0 ? { quotes: [] } : await searchTradingView(args.query);
45
+ const quotes = yahoo.quotes.length > 0 ? yahoo.quotes : fallback.quotes;
33
46
 
34
- const quotes = data.quotes ?? [];
35
47
  if (quotes.length === 0) {
48
+ const fallbackError = "error" in fallback ? fallback.error : undefined;
36
49
  return {
37
- content: [{ type: "text", text: `No results found for "${args.query}"` }],
50
+ content: [
51
+ {
52
+ type: "text",
53
+ text: formatNoResultsText(args.query, yahoo.error, fallbackError),
54
+ },
55
+ ],
38
56
  details: quotes,
39
57
  };
40
58
  }
41
59
 
42
60
  const lines = [
43
- `**Search results for "${args.query}"** — ${quotes.length} matches`,
61
+ `**Search results for "${args.query}"** — ${quotes.length} matches${yahoo.quotes.length === 0 ? " (TradingView fallback)" : ""}`,
44
62
  "",
45
63
  ...quotes.map(
46
64
  (q) =>
@@ -51,3 +69,186 @@ export const searchTickerTool: AgentTool<typeof params> = {
51
69
  return { content: [{ type: "text", text: lines.join("\n") }], details: quotes };
52
70
  },
53
71
  };
72
+
73
+ function formatNoResultsText(
74
+ query: string,
75
+ yahooError?: string,
76
+ tradingViewError?: string,
77
+ ): string {
78
+ if (yahooError && tradingViewError) {
79
+ return `No results found for "${query}". Yahoo search was unavailable (${yahooError}), and the TradingView fallback was unavailable (${tradingViewError}).`;
80
+ }
81
+ if (yahooError) {
82
+ return `No results found for "${query}". Yahoo search was unavailable (${yahooError}), and the TradingView fallback found no equity matches.`;
83
+ }
84
+ if (tradingViewError) {
85
+ return `No results found for "${query}". TradingView fallback was unavailable (${tradingViewError}).`;
86
+ }
87
+ return `No results found for "${query}"`;
88
+ }
89
+
90
+ async function searchYahoo(query: string): Promise<{ quotes: YahooSearchQuote[]; error?: string }> {
91
+ const url = `https://query1.finance.yahoo.com/v1/finance/search?q=${encodeURIComponent(query)}&quotesCount=10&newsCount=0`;
92
+ try {
93
+ await rateLimiter.acquire("yahoo");
94
+ const data = await httpGet<YahooSearchResponse>(url, {
95
+ headers: { "User-Agent": "OpenCandle/1.0" },
96
+ maxRetryAfterMs: FALLBACK_SEARCH_MAX_RETRY_AFTER_MS,
97
+ });
98
+ return { quotes: data.quotes ?? [] };
99
+ } catch (error) {
100
+ return {
101
+ quotes: [],
102
+ error: error instanceof Error ? error.message : "unknown_error",
103
+ };
104
+ }
105
+ }
106
+
107
+ async function searchTradingView(
108
+ query: string,
109
+ ): Promise<{ quotes: YahooSearchQuote[]; error?: string }> {
110
+ const normalized = query.trim();
111
+ if (!normalized) return { quotes: [] };
112
+
113
+ try {
114
+ const exactTicker = normalizeTickerQuery(normalized);
115
+ if (exactTicker) {
116
+ const exact = await fetchTradingViewSearch(
117
+ buildTradingViewSearchBody({
118
+ query: normalized,
119
+ ticker: exactTicker,
120
+ mode: "ticker",
121
+ }),
122
+ );
123
+ const exactQuotes = decodeTradingViewSearch(exact, normalized).slice(0, 10);
124
+ if (exactQuotes.length > 0) return { quotes: exactQuotes };
125
+ }
126
+
127
+ const matched = await fetchTradingViewSearch(
128
+ buildTradingViewSearchBody({
129
+ query: normalized,
130
+ mode: "description",
131
+ }),
132
+ );
133
+ return { quotes: decodeTradingViewSearch(matched, normalized).slice(0, 10) };
134
+ } catch (error) {
135
+ return {
136
+ quotes: [],
137
+ error: error instanceof Error ? error.message : "unknown_error",
138
+ };
139
+ }
140
+ }
141
+
142
+ async function fetchTradingViewSearch(
143
+ body: ReturnType<typeof buildTradingViewSearchBody>,
144
+ ): Promise<TradingViewSearchResponse> {
145
+ await rateLimiter.acquire("tradingview");
146
+ return await httpPost<TradingViewSearchResponse>(
147
+ "https://scanner.tradingview.com/america/scan2?label-product=screener-stock",
148
+ body,
149
+ {
150
+ headers: {
151
+ Origin: "https://www.tradingview.com",
152
+ Referer: "https://www.tradingview.com/",
153
+ "User-Agent":
154
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
155
+ },
156
+ },
157
+ );
158
+ }
159
+
160
+ function buildTradingViewSearchBody(options: {
161
+ query: string;
162
+ mode: "ticker" | "description";
163
+ ticker?: string;
164
+ }) {
165
+ const columns = ["name", "description", "exchange", "type", "typespecs"] as const;
166
+ const filter =
167
+ options.mode === "ticker" && options.ticker
168
+ ? [
169
+ { left: "name", operation: "equal", right: options.ticker },
170
+ { left: "is_primary", operation: "equal", right: true },
171
+ { left: "type", operation: "in_range", right: ["stock", "fund", "dr"] },
172
+ ]
173
+ : [
174
+ { left: "description", operation: "match", right: options.query },
175
+ { left: "type", operation: "in_range", right: ["stock", "fund", "dr"] },
176
+ ];
177
+
178
+ return {
179
+ markets: ["america"],
180
+ columns,
181
+ filter,
182
+ sort: { sortBy: "market_cap_basic", sortOrder: "desc" },
183
+ range: [0, 10],
184
+ options: { lang: "en" },
185
+ };
186
+ }
187
+
188
+ function decodeTradingViewSearch(
189
+ response: TradingViewSearchResponse,
190
+ query: string,
191
+ ): YahooSearchQuote[] {
192
+ const fields = response.fields ?? [];
193
+ const queryTicker = normalizeTickerQuery(query);
194
+ return (response.symbols ?? [])
195
+ .map((row, index) => {
196
+ const values = Object.fromEntries(
197
+ fields.map((field, fieldIndex) => [field, row.f[fieldIndex]]),
198
+ );
199
+ const symbol = stringValue(values.name) ?? symbolFromTvSymbol(row.s);
200
+ const longname = stringValue(values.description);
201
+ const exchange = stringValue(values.exchange) ?? exchangeFromTvSymbol(row.s);
202
+ return {
203
+ symbol,
204
+ shortname: longname,
205
+ longname,
206
+ quoteType: quoteTypeFromTradingView(values.type, values.typespecs),
207
+ exchange,
208
+ score: scoreTradingViewResult({ symbol, exchange, index, queryTicker }),
209
+ };
210
+ })
211
+ .sort((a, b) => b.score - a.score || a.symbol.localeCompare(b.symbol));
212
+ }
213
+
214
+ function scoreTradingViewResult(opts: {
215
+ symbol: string;
216
+ exchange: string;
217
+ index: number;
218
+ queryTicker?: string;
219
+ }): number {
220
+ let score = 50_000 - opts.index;
221
+ if (opts.queryTicker && opts.symbol.toUpperCase() === opts.queryTicker) score += 50_000;
222
+ const exchange = opts.exchange.toUpperCase();
223
+ if (exchange === "NASDAQ" || exchange === "NYSE") score += 1_000;
224
+ if (exchange === "AMEX") score += 500;
225
+ return score;
226
+ }
227
+
228
+ function quoteTypeFromTradingView(type: unknown, typespecs: unknown): string {
229
+ const normalizedType = stringValue(type)?.toLowerCase();
230
+ const normalizedSpecs = Array.isArray(typespecs)
231
+ ? typespecs.map((value) => String(value).toLowerCase())
232
+ : [];
233
+ if (normalizedType === "fund" && normalizedSpecs.includes("etf")) return "ETF";
234
+ if (normalizedType === "fund") return "MUTUALFUND";
235
+ if (normalizedType === "dr") return "EQUITY";
236
+ return "EQUITY";
237
+ }
238
+
239
+ function normalizeTickerQuery(query: string): string | undefined {
240
+ const normalized = query.trim().toUpperCase();
241
+ return /^[A-Z0-9][A-Z0-9.-]{0,9}$/.test(normalized) ? normalized : undefined;
242
+ }
243
+
244
+ function symbolFromTvSymbol(tvSymbol: string): string {
245
+ return tvSymbol.includes(":") ? tvSymbol.split(":").at(-1)! : tvSymbol;
246
+ }
247
+
248
+ function exchangeFromTvSymbol(tvSymbol: string): string {
249
+ return tvSymbol.includes(":") ? tvSymbol.split(":")[0] : "";
250
+ }
251
+
252
+ function stringValue(value: unknown): string | undefined {
253
+ return typeof value === "string" ? value : undefined;
254
+ }
@@ -1,26 +1,46 @@
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";
3
+ import { getConfig } from "../../config.js";
4
4
  import { getDailyHistory } from "../../providers/alpha-vantage.js";
5
- import { wrapProvider } from "../../providers/wrap-provider.js";
6
5
  import { withFallback } from "../../providers/with-fallback.js";
7
- import { getConfig } from "../../config.js";
8
- import type { OHLCV } from "../../types/market.js";
6
+ import { wrapProvider } from "../../providers/wrap-provider.js";
7
+ import { getHistory } from "../../providers/yahoo-finance.js";
9
8
  import type { ProviderResult } from "../../runtime/evidence.js";
9
+ import type { OHLCV } from "../../types/market.js";
10
10
 
11
11
  const DAILY_INTERVALS = new Set(["1d", "1wk", "1mo"]);
12
+ const HISTORY_RANGES = [
13
+ "1d",
14
+ "5d",
15
+ "1mo",
16
+ "3mo",
17
+ "6mo",
18
+ "ytd",
19
+ "1y",
20
+ "2y",
21
+ "5y",
22
+ "10y",
23
+ "max",
24
+ ] as const;
25
+ const HISTORY_INTERVALS = ["1m", "5m", "15m", "1h", "1d", "1wk", "1mo"] as const;
12
26
 
13
27
  const params = Type.Object({
14
28
  symbol: Type.String({ description: "Stock ticker symbol (e.g. AAPL, MSFT)" }),
15
29
  range: Type.Optional(
16
- Type.String({
17
- description: "Time range: 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, max. Default: 6mo",
18
- }),
30
+ Type.Union(
31
+ HISTORY_RANGES.map((range) => Type.Literal(range)),
32
+ {
33
+ description: "Time range: 1d, 5d, 1mo, 3mo, 6mo, ytd, 1y, 2y, 5y, 10y, max. Default: 6mo",
34
+ },
35
+ ),
19
36
  ),
20
37
  interval: Type.Optional(
21
- Type.String({
22
- description: "Data interval: 1m, 5m, 15m, 1h, 1d, 1wk, 1mo. Default: 1d",
23
- }),
38
+ Type.Union(
39
+ HISTORY_INTERVALS.map((interval) => Type.Literal(interval)),
40
+ {
41
+ description: "Data interval: 1m, 5m, 15m, 1h, 1d, 1wk, 1mo. Default: 1d",
42
+ },
43
+ ),
24
44
  ),
25
45
  });
26
46
 
@@ -29,7 +49,7 @@ export const stockHistoryTool: AgentTool<typeof params, OHLCV[]> = {
29
49
  label: "Stock History",
30
50
  description: "Get historical OHLCV (open, high, low, close, volume) data for a stock",
31
51
  parameters: params,
32
- async execute(toolCallId, args) {
52
+ async execute(_toolCallId, args) {
33
53
  const symbol = args.symbol.toUpperCase();
34
54
  const range = args.range ?? "6mo";
35
55
  const interval = args.interval ?? "1d";
@@ -53,7 +73,12 @@ export const stockHistoryTool: AgentTool<typeof params, OHLCV[]> = {
53
73
  ? ` No alternate source for ${interval} data.`
54
74
  : "";
55
75
  return {
56
- content: [{ type: "text", text: `⚠ Stock history unavailable for ${symbol} (${result.reason}).${intradayNote}` }],
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: `⚠ Stock history unavailable for ${symbol} (${result.reason}).${intradayNote}`,
80
+ },
81
+ ],
57
82
  details: [],
58
83
  };
59
84
  }
@@ -1,9 +1,9 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getQuote } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { getConfig } from "../../config.js";
4
4
  import { getGlobalQuote } from "../../providers/alpha-vantage.js";
5
5
  import { withFallback } from "../../providers/with-fallback.js";
6
- import { getConfig } from "../../config.js";
6
+ import { getQuote } from "../../providers/yahoo-finance.js";
7
7
  import type { StockQuote } from "../../types/market.js";
8
8
 
9
9
  const params = Type.Object({
@@ -16,7 +16,7 @@ export const stockQuoteTool: AgentTool<typeof params, StockQuote> = {
16
16
  description:
17
17
  "Get real-time stock price, volume, market cap, and 52-week range for a ticker symbol",
18
18
  parameters: params,
19
- async execute(toolCallId, args) {
19
+ async execute(_toolCallId, args) {
20
20
  const symbol = args.symbol.toUpperCase();
21
21
  const apiKey = getConfig().alphaVantageApiKey;
22
22
 
@@ -30,16 +30,19 @@ export const stockQuoteTool: AgentTool<typeof params, StockQuote> = {
30
30
  const result = await withFallback(entries);
31
31
  if (result.status === "unavailable") {
32
32
  return {
33
- content: [{ type: "text", text: `⚠ Stock quote unavailable for ${symbol} (${result.reason}).` }],
33
+ content: [
34
+ { type: "text", text: `⚠ Stock quote unavailable for ${symbol} (${result.reason}).` },
35
+ ],
34
36
  details: null as any,
35
37
  };
36
38
  }
37
39
  const quote = result.data;
38
40
  const sign = quote.changePercent >= 0 ? "+" : "";
39
41
 
40
- const week52 = quote.week52High > 0 && quote.week52Low > 0
41
- ? `$${quote.week52Low.toFixed(2)} - $${quote.week52High.toFixed(2)}`
42
- : "N/A";
42
+ const week52 =
43
+ quote.week52High > 0 && quote.week52Low > 0
44
+ ? `$${quote.week52Low.toFixed(2)} - $${quote.week52High.toFixed(2)}`
45
+ : "N/A";
43
46
  const marketCapStr = quote.marketCap > 0 ? `$${formatLargeNumber(quote.marketCap)}` : "N/A";
44
47
 
45
48
  const text = [
@@ -2,10 +2,10 @@ import type { Greeks } from "../../types/options.js";
2
2
 
3
3
  interface GreeksInput {
4
4
  type: "call" | "put";
5
- spot: number; // underlying price
6
- strike: number; // strike price
7
- timeYears: number; // time to expiration in years
8
- iv: number; // implied volatility (e.g. 0.30 for 30%)
5
+ spot: number; // underlying price
6
+ strike: number; // strike price
7
+ timeYears: number; // time to expiration in years
8
+ iv: number; // implied volatility (e.g. 0.30 for 30%)
9
9
  riskFreeRate: number; // risk-free rate (e.g. 0.05 for 5%)
10
10
  }
11
11
 
@@ -47,7 +47,6 @@ export function computeGreeks(input: GreeksInput): Greeks {
47
47
  rho: (strike * timeYears * expRT * nd2) / 100, // per 1% change in rate
48
48
  };
49
49
  } else {
50
- const nMinusD1 = cdf(-d1);
51
50
  const nMinusD2 = cdf(-d2);
52
51
  return {
53
52
  delta: nd1 - 1,
@@ -72,7 +71,7 @@ function cdf(x: number): number {
72
71
  const sign = x < 0 ? -1 : 1;
73
72
  const absX = Math.abs(x);
74
73
  const t = 1 / (1 + p * absX);
75
- const y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-absX * absX / 2);
74
+ const y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp((-absX * absX) / 2);
76
75
  return 0.5 * (1 + sign * y);
77
76
  }
78
77
 
@@ -1,21 +1,24 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getOptionsChain } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
4
3
  import { wrapProvider } from "../../providers/wrap-provider.js";
5
- import type { OptionsChain, OptionContract } from "../../types/options.js";
4
+ import { getOptionsChain } from "../../providers/yahoo-finance.js";
5
+ import type { OptionContract, OptionsChain } from "../../types/options.js";
6
6
 
7
7
  const params = Type.Object({
8
8
  symbol: Type.String({ description: "Stock ticker symbol (e.g. AAPL, TSLA, SPY, MSFT)" }),
9
9
  expiration: Type.Optional(
10
10
  Type.String({
11
- description:
12
- "Expiration date as YYYY-MM-DD. If omitted, uses the nearest expiration.",
11
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
12
+ description: "Expiration date as YYYY-MM-DD. If omitted, uses the nearest expiration.",
13
13
  }),
14
14
  ),
15
15
  type: Type.Optional(
16
- Type.Union([Type.Literal("call"), Type.Literal("put"), Type.Literal("CALL"), Type.Literal("PUT")], {
17
- description: "Filter by option type. Omit for both calls and puts.",
18
- }),
16
+ Type.Union(
17
+ [Type.Literal("call"), Type.Literal("put"), Type.Literal("CALL"), Type.Literal("PUT")],
18
+ {
19
+ description: "Filter by option type. Omit for both calls and puts.",
20
+ },
21
+ ),
19
22
  ),
20
23
  });
21
24
 
@@ -25,17 +28,17 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
25
28
  description:
26
29
  "Get the full options chain for a stock with strikes, bids, asks, volume, open interest, implied volatility, and computed Greeks (Delta, Gamma, Theta, Vega, Rho via Black-Scholes). No API key required.",
27
30
  parameters: params,
28
- async execute(toolCallId, args) {
31
+ async execute(_toolCallId, args) {
29
32
  const symbol = args.symbol.toUpperCase();
30
33
  const normalizedType = args.type?.toLowerCase();
31
- const expirationTs = args.expiration
32
- ? Math.floor(new Date(args.expiration).getTime() / 1000)
33
- : undefined;
34
+ const expirationTs = args.expiration ? parseExpiration(args.expiration) : undefined;
34
35
 
35
36
  const result = await wrapProvider("yahoo", () => getOptionsChain(symbol, expirationTs));
36
37
  if (result.status === "unavailable") {
37
38
  return {
38
- content: [{ type: "text", text: `⚠ Options chain unavailable for ${symbol} (${result.reason}).` }],
39
+ content: [
40
+ { type: "text", text: `⚠ Options chain unavailable for ${symbol} (${result.reason}).` },
41
+ ],
39
42
  details: null as any,
40
43
  };
41
44
  }
@@ -44,6 +47,13 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
44
47
  const lines: string[] = [
45
48
  `**${chain.symbol} Options Chain** — Expiry: ${chain.expirationDate}`,
46
49
  `Underlying: $${chain.underlyingPrice.toFixed(2)}`,
50
+ `Quote status: ${chain.quoteStatus.marketSession} / ${chain.quoteStatus.bidAskState}`,
51
+ "Option bid/ask and last prices are quoted per share; multiply by 100 for one standard contract premium.",
52
+ ...(chain.quoteStatus.warning
53
+ ? [
54
+ `⚠ ${chain.quoteStatus.warning} do not treat zero bid/ask as confirmed live illiquidity without broker verification; do not stop at the stale quote caveat. Disclose the gap, avoid naming tradable live premiums, and finish the strategy explanation with mechanics, assignment outcomes, labeled hypotheticals, and what live broker quotes would change.`,
55
+ ]
56
+ : []),
47
57
  `Available expirations: ${formatAvailableExpirations(chain.expirationDates)}`,
48
58
  "",
49
59
  ];
@@ -52,8 +62,12 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
52
62
  const showPuts = !normalizedType || normalizedType === "put";
53
63
 
54
64
  if (showCalls && chain.calls.length > 0) {
55
- lines.push(`**CALLS** (${chain.calls.length} contracts, volume: ${chain.totalCallVolume.toLocaleString()})`);
56
- lines.push("Strike | Bid/Ask | Last | Vol | OI | IV | Delta | Theta");
65
+ lines.push(
66
+ `**CALLS** (${chain.calls.length} contracts, volume: ${chain.totalCallVolume.toLocaleString()})`,
67
+ );
68
+ lines.push(
69
+ "Strike | Bid/Ask (per share) | Last (per share) | Vol | OI | IV | Delta | Gamma | Theta | Vega | Rho",
70
+ );
57
71
  const topCalls = sortByVolume(chain.calls).slice(0, 10);
58
72
  for (const c of topCalls) {
59
73
  lines.push(formatContract(c));
@@ -62,8 +76,12 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
62
76
  }
63
77
 
64
78
  if (showPuts && chain.puts.length > 0) {
65
- lines.push(`**PUTS** (${chain.puts.length} contracts, volume: ${chain.totalPutVolume.toLocaleString()})`);
66
- lines.push("Strike | Bid/Ask | Last | Vol | OI | IV | Delta | Theta");
79
+ lines.push(
80
+ `**PUTS** (${chain.puts.length} contracts, volume: ${chain.totalPutVolume.toLocaleString()})`,
81
+ );
82
+ lines.push(
83
+ "Strike | Bid/Ask (per share) | Last (per share) | Vol | OI | IV | Delta | Gamma | Theta | Vega | Rho",
84
+ );
67
85
  const topPuts = sortByVolume(chain.puts).slice(0, 10);
68
86
  for (const c of topPuts) {
69
87
  lines.push(formatContract(c));
@@ -77,6 +95,17 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
77
95
  },
78
96
  };
79
97
 
98
+ function parseExpiration(expiration: string): number {
99
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(expiration)) {
100
+ throw new Error("expiration must be a valid YYYY-MM-DD date.");
101
+ }
102
+ const parsed = new Date(`${expiration}T00:00:00.000Z`);
103
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== expiration) {
104
+ throw new Error("expiration must be a valid YYYY-MM-DD date.");
105
+ }
106
+ return Math.floor(parsed.getTime() / 1000);
107
+ }
108
+
80
109
  function sortByVolume(contracts: OptionContract[]): OptionContract[] {
81
110
  return [...contracts].sort((a, b) => b.volume - a.volume);
82
111
  }
@@ -87,5 +116,5 @@ function formatAvailableExpirations(expirationDates: string[]): string {
87
116
 
88
117
  function formatContract(c: OptionContract): string {
89
118
  const itm = c.inTheMoney ? "*" : " ";
90
- return `${itm}$${c.strike.toFixed(2)} | $${c.bid.toFixed(2)}/$${c.ask.toFixed(2)} | $${c.lastPrice.toFixed(2)} | ${c.volume} | ${c.openInterest} | ${(c.impliedVolatility * 100).toFixed(1)}% | ${c.greeks.delta.toFixed(3)} | ${c.greeks.theta.toFixed(3)}`;
119
+ return `${itm}$${c.strike.toFixed(2)} | $${c.bid.toFixed(2)}/$${c.ask.toFixed(2)} | $${c.lastPrice.toFixed(2)} | ${c.volume} | ${c.openInterest} | ${(c.impliedVolatility * 100).toFixed(1)}% | ${c.greeks.delta.toFixed(3)} | ${c.greeks.gamma.toFixed(3)} | ${c.greeks.theta.toFixed(3)} | ${c.greeks.vega.toFixed(3)} | ${c.greeks.rho.toFixed(3)}`;
91
120
  }