opencandle 0.5.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 (527) hide show
  1. package/README.md +164 -187
  2. package/dist/analysts/contracts.d.ts +1 -3
  3. package/dist/analysts/contracts.js +1 -11
  4. package/dist/analysts/contracts.js.map +1 -1
  5. package/dist/analysts/orchestrator.d.ts +1 -3
  6. package/dist/analysts/orchestrator.js +1 -26
  7. package/dist/analysts/orchestrator.js.map +1 -1
  8. package/dist/cli.js +30 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +3 -3
  11. package/dist/config.js +12 -5
  12. package/dist/config.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/infra/browser.js +3 -1
  17. package/dist/infra/browser.js.map +1 -1
  18. package/dist/infra/cache.d.ts +8 -11
  19. package/dist/infra/cache.js +17 -15
  20. package/dist/infra/cache.js.map +1 -1
  21. package/dist/infra/http-client.d.ts +4 -1
  22. package/dist/infra/http-client.js +59 -6
  23. package/dist/infra/http-client.js.map +1 -1
  24. package/dist/infra/index.d.ts +3 -3
  25. package/dist/infra/index.js +3 -3
  26. package/dist/infra/index.js.map +1 -1
  27. package/dist/infra/native-dependencies.js +2 -2
  28. package/dist/infra/native-dependencies.js.map +1 -1
  29. package/dist/infra/node-version.js.map +1 -1
  30. package/dist/infra/opencandle-paths.d.ts +0 -3
  31. package/dist/infra/opencandle-paths.js +4 -11
  32. package/dist/infra/opencandle-paths.js.map +1 -1
  33. package/dist/infra/rate-limiter.js +12 -9
  34. package/dist/infra/rate-limiter.js.map +1 -1
  35. package/dist/market-state/alert-conditions.d.ts +34 -0
  36. package/dist/market-state/alert-conditions.js +23 -0
  37. package/dist/market-state/alert-conditions.js.map +1 -0
  38. package/dist/market-state/alert-runner.d.ts +55 -0
  39. package/dist/market-state/alert-runner.js +634 -0
  40. package/dist/market-state/alert-runner.js.map +1 -0
  41. package/dist/market-state/daily-report.d.ts +26 -0
  42. package/dist/market-state/daily-report.js +179 -0
  43. package/dist/market-state/daily-report.js.map +1 -0
  44. package/dist/market-state/local-automation-service.d.ts +25 -0
  45. package/dist/market-state/local-automation-service.js +119 -0
  46. package/dist/market-state/local-automation-service.js.map +1 -0
  47. package/dist/market-state/notification-delivery.d.ts +14 -0
  48. package/dist/market-state/notification-delivery.js +139 -0
  49. package/dist/market-state/notification-delivery.js.map +1 -0
  50. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  51. package/dist/market-state/resolve-for-mutation.js +15 -0
  52. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  53. package/dist/market-state/resolve.d.ts +14 -0
  54. package/dist/market-state/resolve.js +89 -0
  55. package/dist/market-state/resolve.js.map +1 -0
  56. package/dist/market-state/service.d.ts +527 -0
  57. package/dist/market-state/service.js +1099 -0
  58. package/dist/market-state/service.js.map +1 -0
  59. package/dist/memory/index.d.ts +7 -7
  60. package/dist/memory/index.js +6 -6
  61. package/dist/memory/index.js.map +1 -1
  62. package/dist/memory/manager.js +11 -11
  63. package/dist/memory/manager.js.map +1 -1
  64. package/dist/memory/retrieval.js +7 -4
  65. package/dist/memory/retrieval.js.map +1 -1
  66. package/dist/memory/sqlite.js +385 -3
  67. package/dist/memory/sqlite.js.map +1 -1
  68. package/dist/memory/storage.js +1 -2
  69. package/dist/memory/storage.js.map +1 -1
  70. package/dist/memory/tool-defaults.js +64 -28
  71. package/dist/memory/tool-defaults.js.map +1 -1
  72. package/dist/memory/types.js.map +1 -1
  73. package/dist/monitor.d.ts +2 -0
  74. package/dist/monitor.js +104 -0
  75. package/dist/monitor.js.map +1 -0
  76. package/dist/onboarding/connect.js +4 -6
  77. package/dist/onboarding/connect.js.map +1 -1
  78. package/dist/onboarding/credential-interceptor.js +1 -1
  79. package/dist/onboarding/credential-interceptor.js.map +1 -1
  80. package/dist/onboarding/degradation-accumulator.js +1 -3
  81. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  82. package/dist/onboarding/providers.js +3 -16
  83. package/dist/onboarding/providers.js.map +1 -1
  84. package/dist/onboarding/state.js.map +1 -1
  85. package/dist/onboarding/tool-helpers.js +1 -1
  86. package/dist/onboarding/tool-helpers.js.map +1 -1
  87. package/dist/onboarding/tool-tags.js +6 -4
  88. package/dist/onboarding/tool-tags.js.map +1 -1
  89. package/dist/onboarding/validation.js +1 -1
  90. package/dist/onboarding/validation.js.map +1 -1
  91. package/dist/pi/opencandle-extension.d.ts +8 -0
  92. package/dist/pi/opencandle-extension.js +412 -28
  93. package/dist/pi/opencandle-extension.js.map +1 -1
  94. package/dist/pi/session.d.ts +1 -1
  95. package/dist/pi/session.js +3 -1
  96. package/dist/pi/session.js.map +1 -1
  97. package/dist/pi/setup.js +8 -3
  98. package/dist/pi/setup.js.map +1 -1
  99. package/dist/pi/tool-adapter.js +5 -2
  100. package/dist/pi/tool-adapter.js.map +1 -1
  101. package/dist/prompts/context-builder.d.ts +1 -1
  102. package/dist/prompts/context-builder.js +19 -6
  103. package/dist/prompts/context-builder.js.map +1 -1
  104. package/dist/prompts/policy-cards.d.ts +1 -1
  105. package/dist/prompts/policy-cards.js +1 -1
  106. package/dist/prompts/policy-cards.js.map +1 -1
  107. package/dist/prompts/sections.d.ts +1 -1
  108. package/dist/prompts/symbol-preflight.d.ts +20 -0
  109. package/dist/prompts/symbol-preflight.js +49 -0
  110. package/dist/prompts/symbol-preflight.js.map +1 -0
  111. package/dist/prompts/workflow-prompts.d.ts +1 -1
  112. package/dist/prompts/workflow-prompts.js +54 -16
  113. package/dist/prompts/workflow-prompts.js.map +1 -1
  114. package/dist/providers/alpha-vantage.d.ts +1 -1
  115. package/dist/providers/alpha-vantage.js +26 -7
  116. package/dist/providers/alpha-vantage.js.map +1 -1
  117. package/dist/providers/coingecko.js +1 -1
  118. package/dist/providers/coingecko.js.map +1 -1
  119. package/dist/providers/errors.d.ts +5 -0
  120. package/dist/providers/errors.js +11 -0
  121. package/dist/providers/errors.js.map +1 -0
  122. package/dist/providers/exa-search.d.ts +2 -2
  123. package/dist/providers/exa-search.js +19 -11
  124. package/dist/providers/exa-search.js.map +1 -1
  125. package/dist/providers/fear-greed.js +1 -1
  126. package/dist/providers/fear-greed.js.map +1 -1
  127. package/dist/providers/finnhub.js +3 -5
  128. package/dist/providers/finnhub.js.map +1 -1
  129. package/dist/providers/fred.js +2 -2
  130. package/dist/providers/fred.js.map +1 -1
  131. package/dist/providers/index.d.ts +7 -6
  132. package/dist/providers/index.js +6 -5
  133. package/dist/providers/index.js.map +1 -1
  134. package/dist/providers/reddit.js +2 -2
  135. package/dist/providers/reddit.js.map +1 -1
  136. package/dist/providers/sec-edgar.d.ts +1 -0
  137. package/dist/providers/sec-edgar.js +12 -4
  138. package/dist/providers/sec-edgar.js.map +1 -1
  139. package/dist/providers/tradingview.d.ts +47 -0
  140. package/dist/providers/tradingview.js +275 -0
  141. package/dist/providers/tradingview.js.map +1 -0
  142. package/dist/providers/twitter.js +6 -8
  143. package/dist/providers/twitter.js.map +1 -1
  144. package/dist/providers/web-search.js +26 -12
  145. package/dist/providers/web-search.js.map +1 -1
  146. package/dist/providers/with-fallback.js +4 -2
  147. package/dist/providers/with-fallback.js.map +1 -1
  148. package/dist/providers/wrap-provider.d.ts +2 -3
  149. package/dist/providers/wrap-provider.js +14 -8
  150. package/dist/providers/wrap-provider.js.map +1 -1
  151. package/dist/providers/yahoo-finance.d.ts +1 -1
  152. package/dist/providers/yahoo-finance.js +101 -17
  153. package/dist/providers/yahoo-finance.js.map +1 -1
  154. package/dist/routing/classify-intent.d.ts +6 -0
  155. package/dist/routing/classify-intent.js +78 -7
  156. package/dist/routing/classify-intent.js.map +1 -1
  157. package/dist/routing/defaults.d.ts +1 -1
  158. package/dist/routing/entity-extractor.d.ts +1 -0
  159. package/dist/routing/entity-extractor.js +234 -29
  160. package/dist/routing/entity-extractor.js.map +1 -1
  161. package/dist/routing/fund-symbols.d.ts +2 -0
  162. package/dist/routing/fund-symbols.js +55 -0
  163. package/dist/routing/fund-symbols.js.map +1 -0
  164. package/dist/routing/horizon.d.ts +1 -0
  165. package/dist/routing/horizon.js +10 -0
  166. package/dist/routing/horizon.js.map +1 -0
  167. package/dist/routing/index.d.ts +10 -10
  168. package/dist/routing/index.js +6 -6
  169. package/dist/routing/index.js.map +1 -1
  170. package/dist/routing/planning.d.ts +1 -1
  171. package/dist/routing/planning.js +65 -34
  172. package/dist/routing/planning.js.map +1 -1
  173. package/dist/routing/route-manifest.d.ts +2 -2
  174. package/dist/routing/route-manifest.js +25 -4
  175. package/dist/routing/route-manifest.js.map +1 -1
  176. package/dist/routing/router-llm-client.js.map +1 -1
  177. package/dist/routing/router-prompt.js +7 -9
  178. package/dist/routing/router-prompt.js.map +1 -1
  179. package/dist/routing/router-types.d.ts +1 -0
  180. package/dist/routing/router.js +137 -22
  181. package/dist/routing/router.js.map +1 -1
  182. package/dist/routing/slot-resolver.d.ts +1 -1
  183. package/dist/routing/slot-resolver.js +2 -4
  184. package/dist/routing/slot-resolver.js.map +1 -1
  185. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  186. package/dist/routing/symbol-disambiguator.js +52 -0
  187. package/dist/routing/symbol-disambiguator.js.map +1 -0
  188. package/dist/routing/turn-context.d.ts +1 -1
  189. package/dist/routing/turn-context.js +1 -1
  190. package/dist/routing/turn-context.js.map +1 -1
  191. package/dist/routing/types.d.ts +2 -0
  192. package/dist/runtime/answer-contracts.js +36 -8
  193. package/dist/runtime/answer-contracts.js.map +1 -1
  194. package/dist/runtime/artifact-contracts.js.map +1 -1
  195. package/dist/runtime/planning-evidence.js +47 -26
  196. package/dist/runtime/planning-evidence.js.map +1 -1
  197. package/dist/runtime/prompt-step.d.ts +1 -9
  198. package/dist/runtime/prompt-step.js +0 -10
  199. package/dist/runtime/prompt-step.js.map +1 -1
  200. package/dist/runtime/run-context.d.ts +5 -2
  201. package/dist/runtime/run-context.js +8 -1
  202. package/dist/runtime/run-context.js.map +1 -1
  203. package/dist/runtime/session-coordinator.d.ts +13 -5
  204. package/dist/runtime/session-coordinator.js +160 -20
  205. package/dist/runtime/session-coordinator.js.map +1 -1
  206. package/dist/runtime/session-title.d.ts +14 -0
  207. package/dist/runtime/session-title.js +50 -0
  208. package/dist/runtime/session-title.js.map +1 -0
  209. package/dist/runtime/tool-defaults-wrapper.js +1 -3
  210. package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
  211. package/dist/runtime/validation.js.map +1 -1
  212. package/dist/runtime/workflow-events.js.map +1 -1
  213. package/dist/runtime/workflow-runner.d.ts +3 -3
  214. package/dist/runtime/workflow-runner.js +1 -1
  215. package/dist/runtime/workflow-runner.js.map +1 -1
  216. package/dist/sentiment/adapters/finnhub.d.ts +1 -1
  217. package/dist/sentiment/adapters/finnhub.js +6 -1
  218. package/dist/sentiment/adapters/finnhub.js.map +1 -1
  219. package/dist/sentiment/adapters/reddit.d.ts +2 -2
  220. package/dist/sentiment/adapters/twitter.d.ts +1 -1
  221. package/dist/sentiment/adapters/web.d.ts +1 -1
  222. package/dist/sentiment/index.d.ts +9 -11
  223. package/dist/sentiment/index.js +9 -20
  224. package/dist/sentiment/index.js.map +1 -1
  225. package/dist/sentiment/keywords.js +26 -4
  226. package/dist/sentiment/keywords.js.map +1 -1
  227. package/dist/sentiment/pipeline.d.ts +2 -2
  228. package/dist/sentiment/pipeline.js +1 -1
  229. package/dist/sentiment/pipeline.js.map +1 -1
  230. package/dist/sentiment/scorer.js +1 -1
  231. package/dist/sentiment/store.d.ts +1 -1
  232. package/dist/sentiment/store.js +1 -1
  233. package/dist/sentiment/store.js.map +1 -1
  234. package/dist/sentiment/trends.d.ts +1 -1
  235. package/dist/sentiment/trends.js.map +1 -1
  236. package/dist/sentiment/types.js.map +1 -1
  237. package/dist/system-prompt.js +3 -2
  238. package/dist/system-prompt.js.map +1 -1
  239. package/dist/tool-kit.d.ts +7 -7
  240. package/dist/tool-kit.js +4 -4
  241. package/dist/tool-kit.js.map +1 -1
  242. package/dist/tools/fundamentals/company-overview.js +11 -6
  243. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  244. package/dist/tools/fundamentals/comps.js +18 -9
  245. package/dist/tools/fundamentals/comps.js.map +1 -1
  246. package/dist/tools/fundamentals/dcf.js +23 -11
  247. package/dist/tools/fundamentals/dcf.js.map +1 -1
  248. package/dist/tools/fundamentals/earnings.js +8 -3
  249. package/dist/tools/fundamentals/earnings.js.map +1 -1
  250. package/dist/tools/fundamentals/financials.js +8 -3
  251. package/dist/tools/fundamentals/financials.js.map +1 -1
  252. package/dist/tools/fundamentals/sec-filings.js +21 -6
  253. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  254. package/dist/tools/index.d.ts +23 -19
  255. package/dist/tools/index.js +51 -39
  256. package/dist/tools/index.js.map +1 -1
  257. package/dist/tools/interaction/ask-user.js +15 -3
  258. package/dist/tools/interaction/ask-user.js.map +1 -1
  259. package/dist/tools/interaction/twitter-login.js +13 -3
  260. package/dist/tools/interaction/twitter-login.js.map +1 -1
  261. package/dist/tools/macro/fear-greed.js.map +1 -1
  262. package/dist/tools/macro/fred-data.d.ts +1 -1
  263. package/dist/tools/macro/fred-data.js +17 -6
  264. package/dist/tools/macro/fred-data.js.map +1 -1
  265. package/dist/tools/market/crypto-history.js +3 -1
  266. package/dist/tools/market/crypto-history.js.map +1 -1
  267. package/dist/tools/market/crypto-price.js +3 -1
  268. package/dist/tools/market/crypto-price.js.map +1 -1
  269. package/dist/tools/market/screen-stocks.d.ts +18 -0
  270. package/dist/tools/market/screen-stocks.js +252 -0
  271. package/dist/tools/market/screen-stocks.js.map +1 -0
  272. package/dist/tools/market/search-ticker.js +160 -8
  273. package/dist/tools/market/search-ticker.js.map +1 -1
  274. package/dist/tools/market/stock-history.d.ts +2 -2
  275. package/dist/tools/market/stock-history.js +26 -7
  276. package/dist/tools/market/stock-history.js.map +1 -1
  277. package/dist/tools/market/stock-quote.js +5 -3
  278. package/dist/tools/market/stock-quote.js.map +1 -1
  279. package/dist/tools/options/greeks.js +1 -1
  280. package/dist/tools/options/greeks.js.map +1 -1
  281. package/dist/tools/options/option-chain.js +19 -6
  282. package/dist/tools/options/option-chain.js.map +1 -1
  283. package/dist/tools/portfolio/alerts.d.ts +15 -0
  284. package/dist/tools/portfolio/alerts.js +357 -0
  285. package/dist/tools/portfolio/alerts.js.map +1 -0
  286. package/dist/tools/portfolio/correlation.d.ts +1 -1
  287. package/dist/tools/portfolio/correlation.js +33 -13
  288. package/dist/tools/portfolio/correlation.js.map +1 -1
  289. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  290. package/dist/tools/portfolio/daily-report.js +83 -0
  291. package/dist/tools/portfolio/daily-report.js.map +1 -0
  292. package/dist/tools/portfolio/holdings-overlap.js +10 -3
  293. package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
  294. package/dist/tools/portfolio/notifications.d.ts +7 -0
  295. package/dist/tools/portfolio/notifications.js +43 -0
  296. package/dist/tools/portfolio/notifications.js.map +1 -0
  297. package/dist/tools/portfolio/predictions.d.ts +12 -6
  298. package/dist/tools/portfolio/predictions.js +337 -87
  299. package/dist/tools/portfolio/predictions.js.map +1 -1
  300. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  301. package/dist/tools/portfolio/risk-analysis.js +45 -6
  302. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  303. package/dist/tools/portfolio/tracker.d.ts +4 -3
  304. package/dist/tools/portfolio/tracker.js +246 -101
  305. package/dist/tools/portfolio/tracker.js.map +1 -1
  306. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  307. package/dist/tools/portfolio/watchlist.js +208 -108
  308. package/dist/tools/portfolio/watchlist.js.map +1 -1
  309. package/dist/tools/sentiment/reddit-sentiment.js +23 -10
  310. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  311. package/dist/tools/sentiment/sentiment-summary.js +15 -13
  312. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  313. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  314. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  315. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  316. package/dist/tools/sentiment/twitter-sentiment.js +12 -5
  317. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  318. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  319. package/dist/tools/sentiment/untrusted-text.js +17 -0
  320. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  321. package/dist/tools/sentiment/web-search.js +9 -13
  322. package/dist/tools/sentiment/web-search.js.map +1 -1
  323. package/dist/tools/sentiment/web-sentiment.js +15 -3
  324. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  325. package/dist/tools/technical/backtest.d.ts +1 -1
  326. package/dist/tools/technical/backtest.js +27 -20
  327. package/dist/tools/technical/backtest.js.map +1 -1
  328. package/dist/tools/technical/indicators.js +23 -5
  329. package/dist/tools/technical/indicators.js.map +1 -1
  330. package/dist/types/index.d.ts +3 -3
  331. package/dist/types/index.js.map +1 -1
  332. package/dist/types/market.d.ts +1 -0
  333. package/dist/types/portfolio.d.ts +14 -4
  334. package/dist/workflows/compare-assets.d.ts +0 -3
  335. package/dist/workflows/compare-assets.js +20 -11
  336. package/dist/workflows/compare-assets.js.map +1 -1
  337. package/dist/workflows/index.d.ts +3 -4
  338. package/dist/workflows/index.js +3 -3
  339. package/dist/workflows/index.js.map +1 -1
  340. package/dist/workflows/options-screener.d.ts +0 -3
  341. package/dist/workflows/options-screener.js +4 -11
  342. package/dist/workflows/options-screener.js.map +1 -1
  343. package/dist/workflows/portfolio-builder.d.ts +0 -3
  344. package/dist/workflows/portfolio-builder.js +0 -8
  345. package/dist/workflows/portfolio-builder.js.map +1 -1
  346. package/gui/server/ask-user-bridge.ts +1 -1
  347. package/gui/server/automation-heartbeat.ts +97 -0
  348. package/gui/server/background-quotes.ts +97 -1
  349. package/gui/server/chat-event-adapter.ts +32 -10
  350. package/gui/server/chat-run-session.ts +16 -0
  351. package/gui/server/invoke-tool.ts +144 -1
  352. package/gui/server/live-chat-event-adapter.ts +21 -6
  353. package/gui/server/market-state-api.ts +315 -0
  354. package/gui/server/model-setup.ts +149 -2
  355. package/gui/server/private-api-access.ts +62 -0
  356. package/gui/server/projector.ts +12 -7
  357. package/gui/server/prompt-observation.ts +4 -7
  358. package/gui/server/quote-snapshot-store.ts +50 -0
  359. package/gui/server/server.ts +200 -451
  360. package/gui/server/session-actions.ts +186 -1
  361. package/gui/server/shutdown.ts +47 -0
  362. package/gui/server/tool-invoke-ack.ts +49 -0
  363. package/gui/server/tool-metadata.ts +23 -10
  364. package/gui/server/websocket.ts +13 -3
  365. package/gui/server/writer-lock.ts +6 -2
  366. package/gui/server/ws-hub.ts +292 -0
  367. package/gui/shared/chat-events.ts +16 -1
  368. package/gui/shared/event-reducer.ts +24 -6
  369. package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
  370. package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
  371. package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
  372. package/gui/web/dist/index.html +2 -2
  373. package/package.json +5 -1
  374. package/src/analysts/contracts.ts +10 -23
  375. package/src/analysts/orchestrator.ts +8 -43
  376. package/src/cli.ts +35 -12
  377. package/src/config.ts +17 -9
  378. package/src/index.ts +1 -1
  379. package/src/infra/browser.ts +3 -1
  380. package/src/infra/cache.ts +41 -30
  381. package/src/infra/http-client.ts +72 -6
  382. package/src/infra/index.ts +7 -10
  383. package/src/infra/native-dependencies.ts +8 -3
  384. package/src/infra/node-version.ts +3 -1
  385. package/src/infra/opencandle-paths.ts +3 -14
  386. package/src/infra/rate-limiter.ts +22 -19
  387. package/src/market-state/alert-conditions.ts +82 -0
  388. package/src/market-state/alert-runner.ts +863 -0
  389. package/src/market-state/daily-report.ts +247 -0
  390. package/src/market-state/local-automation-service.ts +162 -0
  391. package/src/market-state/notification-delivery.ts +158 -0
  392. package/src/market-state/resolve-for-mutation.ts +24 -0
  393. package/src/market-state/resolve.ts +112 -0
  394. package/src/market-state/service.ts +2344 -0
  395. package/src/memory/index.ts +7 -7
  396. package/src/memory/manager.ts +14 -16
  397. package/src/memory/retrieval.ts +8 -7
  398. package/src/memory/sqlite.ts +407 -6
  399. package/src/memory/storage.ts +5 -15
  400. package/src/memory/tool-defaults.ts +60 -39
  401. package/src/memory/types.ts +3 -3
  402. package/src/monitor.ts +121 -0
  403. package/src/onboarding/connect.ts +10 -33
  404. package/src/onboarding/credential-interceptor.ts +3 -15
  405. package/src/onboarding/degradation-accumulator.ts +1 -3
  406. package/src/onboarding/providers.ts +9 -40
  407. package/src/onboarding/state.ts +4 -15
  408. package/src/onboarding/tool-helpers.ts +2 -9
  409. package/src/onboarding/tool-tags.ts +6 -6
  410. package/src/onboarding/validation.ts +14 -20
  411. package/src/pi/opencandle-extension.ts +529 -85
  412. package/src/pi/session.ts +7 -5
  413. package/src/pi/setup.ts +61 -43
  414. package/src/pi/tool-adapter.ts +5 -2
  415. package/src/prompts/context-builder.ts +23 -12
  416. package/src/prompts/policy-cards.ts +2 -2
  417. package/src/prompts/sections.ts +1 -1
  418. package/src/prompts/symbol-preflight.ts +80 -0
  419. package/src/prompts/workflow-prompts.ts +77 -28
  420. package/src/providers/alpha-vantage.ts +58 -39
  421. package/src/providers/coingecko.ts +2 -5
  422. package/src/providers/errors.ts +9 -0
  423. package/src/providers/exa-search.ts +24 -22
  424. package/src/providers/fear-greed.ts +1 -1
  425. package/src/providers/finnhub.ts +7 -6
  426. package/src/providers/fred.ts +3 -3
  427. package/src/providers/index.ts +14 -6
  428. package/src/providers/reddit.ts +17 -6
  429. package/src/providers/sec-edgar.ts +20 -6
  430. package/src/providers/tradingview.ts +399 -0
  431. package/src/providers/twitter.ts +6 -8
  432. package/src/providers/web-search.ts +30 -20
  433. package/src/providers/with-fallback.ts +8 -7
  434. package/src/providers/wrap-provider.ts +15 -10
  435. package/src/providers/yahoo-finance.ts +140 -35
  436. package/src/routing/classify-intent.ts +101 -10
  437. package/src/routing/defaults.ts +1 -1
  438. package/src/routing/entity-extractor.ts +287 -38
  439. package/src/routing/fund-symbols.ts +58 -0
  440. package/src/routing/horizon.ts +7 -0
  441. package/src/routing/index.ts +48 -48
  442. package/src/routing/planning.ts +144 -53
  443. package/src/routing/route-manifest.ts +37 -15
  444. package/src/routing/router-llm-client.ts +4 -4
  445. package/src/routing/router-prompt.ts +15 -19
  446. package/src/routing/router-types.ts +2 -5
  447. package/src/routing/router.ts +251 -53
  448. package/src/routing/slot-resolver.ts +34 -11
  449. package/src/routing/symbol-disambiguator.ts +72 -0
  450. package/src/routing/turn-context.ts +6 -9
  451. package/src/routing/types.ts +2 -0
  452. package/src/runtime/answer-contracts.ts +82 -43
  453. package/src/runtime/artifact-contracts.ts +2 -1
  454. package/src/runtime/planning-evidence.ts +157 -66
  455. package/src/runtime/prompt-step.ts +1 -16
  456. package/src/runtime/run-context.ts +12 -2
  457. package/src/runtime/session-coordinator.ts +238 -63
  458. package/src/runtime/session-title.ts +60 -0
  459. package/src/runtime/tool-defaults-wrapper.ts +1 -3
  460. package/src/runtime/validation.ts +1 -4
  461. package/src/runtime/workflow-events.ts +7 -7
  462. package/src/runtime/workflow-runner.ts +5 -11
  463. package/src/sentiment/adapters/finnhub.ts +7 -2
  464. package/src/sentiment/adapters/reddit.ts +2 -2
  465. package/src/sentiment/adapters/twitter.ts +1 -1
  466. package/src/sentiment/adapters/web.ts +1 -1
  467. package/src/sentiment/index.ts +16 -26
  468. package/src/sentiment/keywords.ts +26 -4
  469. package/src/sentiment/pipeline.ts +15 -4
  470. package/src/sentiment/scorer.ts +1 -1
  471. package/src/sentiment/store.ts +2 -2
  472. package/src/sentiment/trends.ts +9 -3
  473. package/src/sentiment/types.ts +5 -4
  474. package/src/system-prompt.ts +3 -2
  475. package/src/tool-kit.ts +10 -9
  476. package/src/tools/fundamentals/company-overview.ts +19 -9
  477. package/src/tools/fundamentals/comps.ts +68 -55
  478. package/src/tools/fundamentals/dcf.ts +145 -95
  479. package/src/tools/fundamentals/earnings.ts +16 -6
  480. package/src/tools/fundamentals/financials.ts +16 -7
  481. package/src/tools/fundamentals/sec-filings.ts +37 -16
  482. package/src/tools/index.ts +51 -39
  483. package/src/tools/interaction/ask-user.ts +22 -10
  484. package/src/tools/interaction/twitter-login.ts +17 -5
  485. package/src/tools/macro/fear-greed.ts +1 -1
  486. package/src/tools/macro/fred-data.ts +58 -46
  487. package/src/tools/market/crypto-history.ts +8 -3
  488. package/src/tools/market/crypto-price.ts +6 -6
  489. package/src/tools/market/screen-stocks.ts +279 -0
  490. package/src/tools/market/search-ticker.ts +218 -17
  491. package/src/tools/market/stock-history.ts +37 -12
  492. package/src/tools/market/stock-quote.ts +10 -7
  493. package/src/tools/options/greeks.ts +5 -5
  494. package/src/tools/options/option-chain.ts +41 -17
  495. package/src/tools/portfolio/alerts.ts +457 -0
  496. package/src/tools/portfolio/correlation.ts +47 -20
  497. package/src/tools/portfolio/daily-report.ts +101 -0
  498. package/src/tools/portfolio/holdings-overlap.ts +31 -15
  499. package/src/tools/portfolio/notifications.ts +45 -0
  500. package/src/tools/portfolio/predictions.ts +406 -106
  501. package/src/tools/portfolio/risk-analysis.ts +46 -7
  502. package/src/tools/portfolio/tracker.ts +270 -109
  503. package/src/tools/portfolio/watchlist.ts +250 -121
  504. package/src/tools/sentiment/reddit-sentiment.ts +50 -24
  505. package/src/tools/sentiment/sentiment-summary.ts +62 -41
  506. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  507. package/src/tools/sentiment/twitter-sentiment.ts +22 -15
  508. package/src/tools/sentiment/untrusted-text.ts +21 -0
  509. package/src/tools/sentiment/web-search.ts +21 -18
  510. package/src/tools/sentiment/web-sentiment.ts +26 -10
  511. package/src/tools/technical/backtest.ts +32 -22
  512. package/src/tools/technical/indicators.ts +39 -14
  513. package/src/types/index.ts +8 -3
  514. package/src/types/market.ts +1 -0
  515. package/src/types/portfolio.ts +14 -4
  516. package/src/types/sentiment.ts +2 -2
  517. package/src/workflows/compare-assets.ts +33 -21
  518. package/src/workflows/index.ts +3 -4
  519. package/src/workflows/options-screener.ts +27 -29
  520. package/src/workflows/portfolio-builder.ts +34 -27
  521. package/dist/workflows/types.d.ts +0 -4
  522. package/dist/workflows/types.js +0 -2
  523. package/dist/workflows/types.js.map +0 -1
  524. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
  525. package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
  526. package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
  527. package/src/workflows/types.ts +0 -4
@@ -1,24 +1,38 @@
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",
@@ -26,21 +40,25 @@ export const searchTickerTool: AgentTool<typeof params> = {
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
42
  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
- });
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
 
@@ -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({
@@ -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
 
@@ -71,7 +71,7 @@ function cdf(x: number): number {
71
71
  const sign = x < 0 ? -1 : 1;
72
72
  const absX = Math.abs(x);
73
73
  const t = 1 / (1 + p * absX);
74
- 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);
75
75
  return 0.5 * (1 + sign * y);
76
76
  }
77
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
 
@@ -28,14 +31,14 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
28
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
  }
@@ -47,7 +50,9 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
47
50
  `Quote status: ${chain.quoteStatus.marketSession} / ${chain.quoteStatus.bidAskState}`,
48
51
  "Option bid/ask and last prices are quoted per share; multiply by 100 for one standard contract premium.",
49
52
  ...(chain.quoteStatus.warning
50
- ? [`⚠ ${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.`]
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
+ ]
51
56
  : []),
52
57
  `Available expirations: ${formatAvailableExpirations(chain.expirationDates)}`,
53
58
  "",
@@ -57,8 +62,12 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
57
62
  const showPuts = !normalizedType || normalizedType === "put";
58
63
 
59
64
  if (showCalls && chain.calls.length > 0) {
60
- lines.push(`**CALLS** (${chain.calls.length} contracts, volume: ${chain.totalCallVolume.toLocaleString()})`);
61
- lines.push("Strike | Bid/Ask (per share) | Last (per share) | Vol | OI | IV | Delta | Gamma | Theta | Vega | Rho");
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
+ );
62
71
  const topCalls = sortByVolume(chain.calls).slice(0, 10);
63
72
  for (const c of topCalls) {
64
73
  lines.push(formatContract(c));
@@ -67,8 +76,12 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
67
76
  }
68
77
 
69
78
  if (showPuts && chain.puts.length > 0) {
70
- lines.push(`**PUTS** (${chain.puts.length} contracts, volume: ${chain.totalPutVolume.toLocaleString()})`);
71
- lines.push("Strike | Bid/Ask (per share) | Last (per share) | Vol | OI | IV | Delta | Gamma | Theta | Vega | Rho");
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
+ );
72
85
  const topPuts = sortByVolume(chain.puts).slice(0, 10);
73
86
  for (const c of topPuts) {
74
87
  lines.push(formatContract(c));
@@ -82,6 +95,17 @@ export const optionChainTool: AgentTool<typeof params, OptionsChain> = {
82
95
  },
83
96
  };
84
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
+
85
109
  function sortByVolume(contracts: OptionContract[]): OptionContract[] {
86
110
  return [...contracts].sort((a, b) => b.volume - a.volume);
87
111
  }