opencandle 0.5.0 → 0.7.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 (574) hide show
  1. package/README.md +170 -186
  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 +66 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +13 -3
  11. package/dist/config.js +25 -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/cache.d.ts +8 -11
  17. package/dist/infra/cache.js +17 -15
  18. package/dist/infra/cache.js.map +1 -1
  19. package/dist/infra/http-client.d.ts +4 -1
  20. package/dist/infra/http-client.js +59 -6
  21. package/dist/infra/http-client.js.map +1 -1
  22. package/dist/infra/index.d.ts +2 -3
  23. package/dist/infra/index.js +2 -3
  24. package/dist/infra/index.js.map +1 -1
  25. package/dist/infra/native-dependencies.js +2 -2
  26. package/dist/infra/native-dependencies.js.map +1 -1
  27. package/dist/infra/node-version.js.map +1 -1
  28. package/dist/infra/opencandle-paths.d.ts +0 -3
  29. package/dist/infra/opencandle-paths.js +4 -11
  30. package/dist/infra/opencandle-paths.js.map +1 -1
  31. package/dist/infra/rate-limiter.js +12 -9
  32. package/dist/infra/rate-limiter.js.map +1 -1
  33. package/dist/market-state/alert-conditions.d.ts +34 -0
  34. package/dist/market-state/alert-conditions.js +23 -0
  35. package/dist/market-state/alert-conditions.js.map +1 -0
  36. package/dist/market-state/alert-runner.d.ts +55 -0
  37. package/dist/market-state/alert-runner.js +634 -0
  38. package/dist/market-state/alert-runner.js.map +1 -0
  39. package/dist/market-state/daily-report.d.ts +26 -0
  40. package/dist/market-state/daily-report.js +179 -0
  41. package/dist/market-state/daily-report.js.map +1 -0
  42. package/dist/market-state/local-automation-service.d.ts +25 -0
  43. package/dist/market-state/local-automation-service.js +119 -0
  44. package/dist/market-state/local-automation-service.js.map +1 -0
  45. package/dist/market-state/notification-delivery.d.ts +14 -0
  46. package/dist/market-state/notification-delivery.js +139 -0
  47. package/dist/market-state/notification-delivery.js.map +1 -0
  48. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  49. package/dist/market-state/resolve-for-mutation.js +15 -0
  50. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  51. package/dist/market-state/resolve.d.ts +14 -0
  52. package/dist/market-state/resolve.js +89 -0
  53. package/dist/market-state/resolve.js.map +1 -0
  54. package/dist/market-state/service.d.ts +527 -0
  55. package/dist/market-state/service.js +1099 -0
  56. package/dist/market-state/service.js.map +1 -0
  57. package/dist/memory/index.d.ts +7 -7
  58. package/dist/memory/index.js +6 -6
  59. package/dist/memory/index.js.map +1 -1
  60. package/dist/memory/manager.js +11 -11
  61. package/dist/memory/manager.js.map +1 -1
  62. package/dist/memory/retrieval.js +7 -4
  63. package/dist/memory/retrieval.js.map +1 -1
  64. package/dist/memory/sqlite.js +385 -3
  65. package/dist/memory/sqlite.js.map +1 -1
  66. package/dist/memory/storage.js +1 -2
  67. package/dist/memory/storage.js.map +1 -1
  68. package/dist/memory/tool-defaults.js +64 -28
  69. package/dist/memory/tool-defaults.js.map +1 -1
  70. package/dist/memory/types.js.map +1 -1
  71. package/dist/monitor.d.ts +2 -0
  72. package/dist/monitor.js +104 -0
  73. package/dist/monitor.js.map +1 -0
  74. package/dist/onboarding/connect.d.ts +2 -2
  75. package/dist/onboarding/connect.js +13 -8
  76. package/dist/onboarding/connect.js.map +1 -1
  77. package/dist/onboarding/credential-interceptor.js +1 -1
  78. package/dist/onboarding/credential-interceptor.js.map +1 -1
  79. package/dist/onboarding/degradation-accumulator.js +1 -3
  80. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  81. package/dist/onboarding/provider-status.d.ts +48 -0
  82. package/dist/onboarding/provider-status.js +285 -0
  83. package/dist/onboarding/provider-status.js.map +1 -0
  84. package/dist/onboarding/providers.d.ts +85 -8
  85. package/dist/onboarding/providers.js +83 -18
  86. package/dist/onboarding/providers.js.map +1 -1
  87. package/dist/onboarding/state.d.ts +1 -0
  88. package/dist/onboarding/state.js +5 -0
  89. package/dist/onboarding/state.js.map +1 -1
  90. package/dist/onboarding/tool-helpers.js +1 -1
  91. package/dist/onboarding/tool-helpers.js.map +1 -1
  92. package/dist/onboarding/tool-tags.d.ts +12 -1
  93. package/dist/onboarding/tool-tags.js +37 -5
  94. package/dist/onboarding/tool-tags.js.map +1 -1
  95. package/dist/onboarding/validation.d.ts +2 -2
  96. package/dist/onboarding/validation.js +1 -1
  97. package/dist/onboarding/validation.js.map +1 -1
  98. package/dist/pi/opencandle-extension.d.ts +8 -0
  99. package/dist/pi/opencandle-extension.js +502 -42
  100. package/dist/pi/opencandle-extension.js.map +1 -1
  101. package/dist/pi/session.d.ts +1 -1
  102. package/dist/pi/session.js +3 -1
  103. package/dist/pi/session.js.map +1 -1
  104. package/dist/pi/setup.js +8 -3
  105. package/dist/pi/setup.js.map +1 -1
  106. package/dist/pi/tool-adapter.d.ts +4 -1
  107. package/dist/pi/tool-adapter.js +10 -6
  108. package/dist/pi/tool-adapter.js.map +1 -1
  109. package/dist/prompts/context-builder.d.ts +1 -1
  110. package/dist/prompts/context-builder.js +20 -7
  111. package/dist/prompts/context-builder.js.map +1 -1
  112. package/dist/prompts/policy-cards.d.ts +1 -1
  113. package/dist/prompts/policy-cards.js +2 -2
  114. package/dist/prompts/policy-cards.js.map +1 -1
  115. package/dist/prompts/sections.d.ts +1 -1
  116. package/dist/prompts/symbol-preflight.d.ts +20 -0
  117. package/dist/prompts/symbol-preflight.js +49 -0
  118. package/dist/prompts/symbol-preflight.js.map +1 -0
  119. package/dist/prompts/workflow-prompts.d.ts +1 -1
  120. package/dist/prompts/workflow-prompts.js +54 -16
  121. package/dist/prompts/workflow-prompts.js.map +1 -1
  122. package/dist/providers/alpha-vantage.d.ts +1 -1
  123. package/dist/providers/alpha-vantage.js +26 -7
  124. package/dist/providers/alpha-vantage.js.map +1 -1
  125. package/dist/providers/coingecko.js +1 -1
  126. package/dist/providers/coingecko.js.map +1 -1
  127. package/dist/providers/errors.d.ts +5 -0
  128. package/dist/providers/errors.js +11 -0
  129. package/dist/providers/errors.js.map +1 -0
  130. package/dist/providers/exa-search.d.ts +2 -2
  131. package/dist/providers/exa-search.js +19 -11
  132. package/dist/providers/exa-search.js.map +1 -1
  133. package/dist/providers/external-tool-error.d.ts +10 -0
  134. package/dist/providers/external-tool-error.js +21 -0
  135. package/dist/providers/external-tool-error.js.map +1 -0
  136. package/dist/providers/fear-greed.js +1 -1
  137. package/dist/providers/fear-greed.js.map +1 -1
  138. package/dist/providers/finnhub.js +3 -5
  139. package/dist/providers/finnhub.js.map +1 -1
  140. package/dist/providers/fred.js +2 -2
  141. package/dist/providers/fred.js.map +1 -1
  142. package/dist/providers/index.d.ts +7 -6
  143. package/dist/providers/index.js +6 -5
  144. package/dist/providers/index.js.map +1 -1
  145. package/dist/providers/reddit-cli.d.ts +36 -0
  146. package/dist/providers/reddit-cli.js +201 -0
  147. package/dist/providers/reddit-cli.js.map +1 -0
  148. package/dist/providers/reddit.d.ts +1 -1
  149. package/dist/providers/reddit.js +9 -37
  150. package/dist/providers/reddit.js.map +1 -1
  151. package/dist/providers/sec-edgar.d.ts +1 -0
  152. package/dist/providers/sec-edgar.js +12 -4
  153. package/dist/providers/sec-edgar.js.map +1 -1
  154. package/dist/providers/tradingview.d.ts +47 -0
  155. package/dist/providers/tradingview.js +275 -0
  156. package/dist/providers/tradingview.js.map +1 -0
  157. package/dist/providers/twitter-cli.d.ts +40 -0
  158. package/dist/providers/twitter-cli.js +153 -0
  159. package/dist/providers/twitter-cli.js.map +1 -0
  160. package/dist/providers/twitter.d.ts +0 -8
  161. package/dist/providers/twitter.js +8 -60
  162. package/dist/providers/twitter.js.map +1 -1
  163. package/dist/providers/web-search.js +26 -12
  164. package/dist/providers/web-search.js.map +1 -1
  165. package/dist/providers/with-fallback.js +4 -2
  166. package/dist/providers/with-fallback.js.map +1 -1
  167. package/dist/providers/wrap-provider.d.ts +2 -3
  168. package/dist/providers/wrap-provider.js +44 -8
  169. package/dist/providers/wrap-provider.js.map +1 -1
  170. package/dist/providers/yahoo-finance.d.ts +1 -1
  171. package/dist/providers/yahoo-finance.js +153 -48
  172. package/dist/providers/yahoo-finance.js.map +1 -1
  173. package/dist/routing/classify-intent.d.ts +6 -0
  174. package/dist/routing/classify-intent.js +78 -7
  175. package/dist/routing/classify-intent.js.map +1 -1
  176. package/dist/routing/defaults.d.ts +1 -1
  177. package/dist/routing/entity-extractor.d.ts +1 -0
  178. package/dist/routing/entity-extractor.js +234 -29
  179. package/dist/routing/entity-extractor.js.map +1 -1
  180. package/dist/routing/fund-symbols.d.ts +2 -0
  181. package/dist/routing/fund-symbols.js +55 -0
  182. package/dist/routing/fund-symbols.js.map +1 -0
  183. package/dist/routing/horizon.d.ts +1 -0
  184. package/dist/routing/horizon.js +10 -0
  185. package/dist/routing/horizon.js.map +1 -0
  186. package/dist/routing/index.d.ts +10 -10
  187. package/dist/routing/index.js +6 -6
  188. package/dist/routing/index.js.map +1 -1
  189. package/dist/routing/planning.d.ts +2 -2
  190. package/dist/routing/planning.js +65 -34
  191. package/dist/routing/planning.js.map +1 -1
  192. package/dist/routing/route-manifest.d.ts +2 -2
  193. package/dist/routing/route-manifest.js +25 -4
  194. package/dist/routing/route-manifest.js.map +1 -1
  195. package/dist/routing/router-llm-client.js.map +1 -1
  196. package/dist/routing/router-prompt.js +7 -9
  197. package/dist/routing/router-prompt.js.map +1 -1
  198. package/dist/routing/router-types.d.ts +1 -0
  199. package/dist/routing/router.js +137 -22
  200. package/dist/routing/router.js.map +1 -1
  201. package/dist/routing/slot-resolver.d.ts +1 -1
  202. package/dist/routing/slot-resolver.js +2 -4
  203. package/dist/routing/slot-resolver.js.map +1 -1
  204. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  205. package/dist/routing/symbol-disambiguator.js +52 -0
  206. package/dist/routing/symbol-disambiguator.js.map +1 -0
  207. package/dist/routing/turn-context.d.ts +1 -1
  208. package/dist/routing/turn-context.js +1 -1
  209. package/dist/routing/turn-context.js.map +1 -1
  210. package/dist/routing/types.d.ts +2 -0
  211. package/dist/runtime/answer-contracts.d.ts +1 -1
  212. package/dist/runtime/answer-contracts.js +48 -9
  213. package/dist/runtime/answer-contracts.js.map +1 -1
  214. package/dist/runtime/artifact-contracts.js.map +1 -1
  215. package/dist/runtime/planning-evidence.js +47 -26
  216. package/dist/runtime/planning-evidence.js.map +1 -1
  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 +13 -5
  224. package/dist/runtime/session-coordinator.js +160 -20
  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 +7 -5
  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 +10 -11
  243. package/dist/sentiment/index.js +10 -20
  244. package/dist/sentiment/index.js.map +1 -1
  245. package/dist/sentiment/insights.d.ts +17 -0
  246. package/dist/sentiment/insights.js +206 -0
  247. package/dist/sentiment/insights.js.map +1 -0
  248. package/dist/sentiment/keywords.js +26 -4
  249. package/dist/sentiment/keywords.js.map +1 -1
  250. package/dist/sentiment/pipeline.d.ts +2 -2
  251. package/dist/sentiment/pipeline.js +14 -2
  252. package/dist/sentiment/pipeline.js.map +1 -1
  253. package/dist/sentiment/scorer.d.ts +2 -0
  254. package/dist/sentiment/scorer.js +11 -2
  255. package/dist/sentiment/scorer.js.map +1 -1
  256. package/dist/sentiment/store.d.ts +1 -1
  257. package/dist/sentiment/store.js +1 -1
  258. package/dist/sentiment/store.js.map +1 -1
  259. package/dist/sentiment/trends.d.ts +1 -1
  260. package/dist/sentiment/trends.js.map +1 -1
  261. package/dist/sentiment/types.d.ts +2 -0
  262. package/dist/sentiment/types.js.map +1 -1
  263. package/dist/system-prompt.js +6 -9
  264. package/dist/system-prompt.js.map +1 -1
  265. package/dist/tool-kit.d.ts +7 -7
  266. package/dist/tool-kit.js +4 -4
  267. package/dist/tool-kit.js.map +1 -1
  268. package/dist/tools/fundamentals/company-overview.js +11 -6
  269. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  270. package/dist/tools/fundamentals/comps.js +18 -9
  271. package/dist/tools/fundamentals/comps.js.map +1 -1
  272. package/dist/tools/fundamentals/dcf.js +23 -11
  273. package/dist/tools/fundamentals/dcf.js.map +1 -1
  274. package/dist/tools/fundamentals/earnings.js +8 -3
  275. package/dist/tools/fundamentals/earnings.js.map +1 -1
  276. package/dist/tools/fundamentals/financials.js +8 -3
  277. package/dist/tools/fundamentals/financials.js.map +1 -1
  278. package/dist/tools/fundamentals/sec-filings.js +21 -6
  279. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  280. package/dist/tools/index.d.ts +27 -20
  281. package/dist/tools/index.js +55 -43
  282. package/dist/tools/index.js.map +1 -1
  283. package/dist/tools/interaction/ask-user.js +15 -3
  284. package/dist/tools/interaction/ask-user.js.map +1 -1
  285. package/dist/tools/macro/fear-greed.js.map +1 -1
  286. package/dist/tools/macro/fred-data.d.ts +1 -1
  287. package/dist/tools/macro/fred-data.js +17 -6
  288. package/dist/tools/macro/fred-data.js.map +1 -1
  289. package/dist/tools/market/crypto-history.js +3 -1
  290. package/dist/tools/market/crypto-history.js.map +1 -1
  291. package/dist/tools/market/crypto-price.js +3 -1
  292. package/dist/tools/market/crypto-price.js.map +1 -1
  293. package/dist/tools/market/screen-stocks.d.ts +18 -0
  294. package/dist/tools/market/screen-stocks.js +252 -0
  295. package/dist/tools/market/screen-stocks.js.map +1 -0
  296. package/dist/tools/market/search-ticker.js +160 -8
  297. package/dist/tools/market/search-ticker.js.map +1 -1
  298. package/dist/tools/market/stock-history.d.ts +2 -2
  299. package/dist/tools/market/stock-history.js +26 -7
  300. package/dist/tools/market/stock-history.js.map +1 -1
  301. package/dist/tools/market/stock-quote.js +5 -3
  302. package/dist/tools/market/stock-quote.js.map +1 -1
  303. package/dist/tools/options/greeks.js +1 -1
  304. package/dist/tools/options/greeks.js.map +1 -1
  305. package/dist/tools/options/option-chain.js +19 -6
  306. package/dist/tools/options/option-chain.js.map +1 -1
  307. package/dist/tools/portfolio/alerts.d.ts +15 -0
  308. package/dist/tools/portfolio/alerts.js +357 -0
  309. package/dist/tools/portfolio/alerts.js.map +1 -0
  310. package/dist/tools/portfolio/correlation.d.ts +1 -1
  311. package/dist/tools/portfolio/correlation.js +33 -13
  312. package/dist/tools/portfolio/correlation.js.map +1 -1
  313. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  314. package/dist/tools/portfolio/daily-report.js +83 -0
  315. package/dist/tools/portfolio/daily-report.js.map +1 -0
  316. package/dist/tools/portfolio/holdings-overlap.js +10 -3
  317. package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
  318. package/dist/tools/portfolio/notifications.d.ts +7 -0
  319. package/dist/tools/portfolio/notifications.js +43 -0
  320. package/dist/tools/portfolio/notifications.js.map +1 -0
  321. package/dist/tools/portfolio/predictions.d.ts +12 -6
  322. package/dist/tools/portfolio/predictions.js +337 -87
  323. package/dist/tools/portfolio/predictions.js.map +1 -1
  324. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  325. package/dist/tools/portfolio/risk-analysis.js +45 -6
  326. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  327. package/dist/tools/portfolio/tracker.d.ts +4 -3
  328. package/dist/tools/portfolio/tracker.js +246 -101
  329. package/dist/tools/portfolio/tracker.js.map +1 -1
  330. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  331. package/dist/tools/portfolio/watchlist.js +208 -108
  332. package/dist/tools/portfolio/watchlist.js.map +1 -1
  333. package/dist/tools/sentiment/insight-format.d.ts +2 -0
  334. package/dist/tools/sentiment/insight-format.js +36 -0
  335. package/dist/tools/sentiment/insight-format.js.map +1 -0
  336. package/dist/tools/sentiment/query-match.d.ts +3 -0
  337. package/dist/tools/sentiment/query-match.js +113 -0
  338. package/dist/tools/sentiment/query-match.js.map +1 -0
  339. package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
  340. package/dist/tools/sentiment/reddit-sentiment.js +266 -107
  341. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  342. package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
  343. package/dist/tools/sentiment/sentiment-summary.js +223 -205
  344. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  345. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  346. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  347. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  348. package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
  349. package/dist/tools/sentiment/twitter-sentiment.js +188 -58
  350. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  351. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  352. package/dist/tools/sentiment/untrusted-text.js +17 -0
  353. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  354. package/dist/tools/sentiment/web-search.js +9 -13
  355. package/dist/tools/sentiment/web-search.js.map +1 -1
  356. package/dist/tools/sentiment/web-sentiment.js +19 -3
  357. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  358. package/dist/tools/technical/backtest.d.ts +1 -1
  359. package/dist/tools/technical/backtest.js +27 -20
  360. package/dist/tools/technical/backtest.js.map +1 -1
  361. package/dist/tools/technical/indicators.js +23 -5
  362. package/dist/tools/technical/indicators.js.map +1 -1
  363. package/dist/types/index.d.ts +3 -3
  364. package/dist/types/index.js.map +1 -1
  365. package/dist/types/market.d.ts +1 -0
  366. package/dist/types/portfolio.d.ts +14 -4
  367. package/dist/types/sentiment.d.ts +52 -0
  368. package/dist/workflows/compare-assets.d.ts +0 -3
  369. package/dist/workflows/compare-assets.js +20 -11
  370. package/dist/workflows/compare-assets.js.map +1 -1
  371. package/dist/workflows/index.d.ts +3 -4
  372. package/dist/workflows/index.js +3 -3
  373. package/dist/workflows/index.js.map +1 -1
  374. package/dist/workflows/options-screener.d.ts +0 -3
  375. package/dist/workflows/options-screener.js +4 -11
  376. package/dist/workflows/options-screener.js.map +1 -1
  377. package/dist/workflows/portfolio-builder.d.ts +0 -3
  378. package/dist/workflows/portfolio-builder.js +0 -8
  379. package/dist/workflows/portfolio-builder.js.map +1 -1
  380. package/gui/server/ask-user-bridge.ts +1 -1
  381. package/gui/server/automation-heartbeat.ts +97 -0
  382. package/gui/server/background-quotes.ts +97 -1
  383. package/gui/server/chat-event-adapter.ts +32 -10
  384. package/gui/server/chat-run-session.ts +16 -0
  385. package/gui/server/invoke-tool.ts +160 -3
  386. package/gui/server/live-chat-event-adapter.ts +21 -6
  387. package/gui/server/market-state-api.ts +315 -0
  388. package/gui/server/model-setup.ts +156 -2
  389. package/gui/server/private-api-access.ts +62 -0
  390. package/gui/server/projector.ts +18 -9
  391. package/gui/server/prompt-observation.ts +4 -7
  392. package/gui/server/quote-snapshot-store.ts +50 -0
  393. package/gui/server/server.ts +218 -451
  394. package/gui/server/session-actions.ts +186 -1
  395. package/gui/server/shutdown.ts +47 -0
  396. package/gui/server/tool-invoke-ack.ts +49 -0
  397. package/gui/server/tool-metadata.ts +101 -24
  398. package/gui/server/websocket.ts +13 -3
  399. package/gui/server/writer-lock.ts +6 -2
  400. package/gui/server/ws-hub.ts +311 -0
  401. package/gui/shared/chat-events.ts +16 -1
  402. package/gui/shared/event-reducer.ts +24 -6
  403. package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
  404. package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
  405. package/gui/web/dist/assets/index-hwbx24a5.css +1 -0
  406. package/gui/web/dist/index.html +2 -2
  407. package/package.json +9 -6
  408. package/src/analysts/contracts.ts +10 -23
  409. package/src/analysts/orchestrator.ts +8 -43
  410. package/src/cli.ts +76 -12
  411. package/src/config.ts +44 -9
  412. package/src/index.ts +1 -1
  413. package/src/infra/cache.ts +41 -30
  414. package/src/infra/http-client.ts +72 -6
  415. package/src/infra/index.ts +6 -10
  416. package/src/infra/native-dependencies.ts +8 -3
  417. package/src/infra/node-version.ts +3 -1
  418. package/src/infra/opencandle-paths.ts +3 -14
  419. package/src/infra/rate-limiter.ts +22 -19
  420. package/src/market-state/alert-conditions.ts +82 -0
  421. package/src/market-state/alert-runner.ts +863 -0
  422. package/src/market-state/daily-report.ts +247 -0
  423. package/src/market-state/local-automation-service.ts +162 -0
  424. package/src/market-state/notification-delivery.ts +158 -0
  425. package/src/market-state/resolve-for-mutation.ts +24 -0
  426. package/src/market-state/resolve.ts +112 -0
  427. package/src/market-state/service.ts +2344 -0
  428. package/src/memory/index.ts +7 -7
  429. package/src/memory/manager.ts +14 -16
  430. package/src/memory/retrieval.ts +8 -7
  431. package/src/memory/sqlite.ts +407 -6
  432. package/src/memory/storage.ts +5 -15
  433. package/src/memory/tool-defaults.ts +60 -39
  434. package/src/memory/types.ts +3 -3
  435. package/src/monitor.ts +121 -0
  436. package/src/onboarding/connect.ts +24 -31
  437. package/src/onboarding/credential-interceptor.ts +3 -15
  438. package/src/onboarding/degradation-accumulator.ts +1 -3
  439. package/src/onboarding/provider-status.ts +410 -0
  440. package/src/onboarding/providers.ts +144 -45
  441. package/src/onboarding/state.ts +13 -15
  442. package/src/onboarding/tool-helpers.ts +2 -9
  443. package/src/onboarding/tool-tags.ts +51 -8
  444. package/src/onboarding/validation.ts +16 -22
  445. package/src/pi/opencandle-extension.ts +643 -101
  446. package/src/pi/session.ts +7 -5
  447. package/src/pi/setup.ts +61 -43
  448. package/src/pi/tool-adapter.ts +19 -6
  449. package/src/prompts/context-builder.ts +24 -13
  450. package/src/prompts/policy-cards.ts +3 -3
  451. package/src/prompts/sections.ts +1 -1
  452. package/src/prompts/symbol-preflight.ts +80 -0
  453. package/src/prompts/workflow-prompts.ts +77 -28
  454. package/src/providers/alpha-vantage.ts +58 -39
  455. package/src/providers/coingecko.ts +2 -5
  456. package/src/providers/errors.ts +9 -0
  457. package/src/providers/exa-search.ts +24 -22
  458. package/src/providers/external-tool-error.ts +20 -0
  459. package/src/providers/fear-greed.ts +1 -1
  460. package/src/providers/finnhub.ts +7 -6
  461. package/src/providers/fred.ts +3 -3
  462. package/src/providers/index.ts +14 -6
  463. package/src/providers/reddit-cli.ts +317 -0
  464. package/src/providers/reddit.ts +14 -59
  465. package/src/providers/sec-edgar.ts +20 -6
  466. package/src/providers/tradingview.ts +399 -0
  467. package/src/providers/twitter-cli.ts +233 -0
  468. package/src/providers/twitter.ts +8 -79
  469. package/src/providers/web-search.ts +30 -20
  470. package/src/providers/with-fallback.ts +8 -7
  471. package/src/providers/wrap-provider.ts +49 -10
  472. package/src/providers/yahoo-finance.ts +204 -66
  473. package/src/routing/classify-intent.ts +101 -10
  474. package/src/routing/defaults.ts +1 -1
  475. package/src/routing/entity-extractor.ts +287 -38
  476. package/src/routing/fund-symbols.ts +58 -0
  477. package/src/routing/horizon.ts +7 -0
  478. package/src/routing/index.ts +48 -48
  479. package/src/routing/planning.ts +145 -53
  480. package/src/routing/route-manifest.ts +37 -15
  481. package/src/routing/router-llm-client.ts +4 -4
  482. package/src/routing/router-prompt.ts +15 -19
  483. package/src/routing/router-types.ts +2 -5
  484. package/src/routing/router.ts +251 -53
  485. package/src/routing/slot-resolver.ts +34 -11
  486. package/src/routing/symbol-disambiguator.ts +72 -0
  487. package/src/routing/turn-context.ts +6 -9
  488. package/src/routing/types.ts +2 -0
  489. package/src/runtime/answer-contracts.ts +105 -45
  490. package/src/runtime/artifact-contracts.ts +2 -1
  491. package/src/runtime/planning-evidence.ts +157 -66
  492. package/src/runtime/prompt-step.ts +1 -16
  493. package/src/runtime/run-context.ts +12 -2
  494. package/src/runtime/session-coordinator.ts +238 -63
  495. package/src/runtime/session-title.ts +60 -0
  496. package/src/runtime/tool-defaults-wrapper.ts +13 -5
  497. package/src/runtime/validation.ts +1 -4
  498. package/src/runtime/workflow-events.ts +7 -7
  499. package/src/runtime/workflow-runner.ts +5 -11
  500. package/src/sentiment/adapters/finnhub.ts +7 -2
  501. package/src/sentiment/adapters/reddit.ts +2 -2
  502. package/src/sentiment/adapters/twitter.ts +1 -1
  503. package/src/sentiment/adapters/web.ts +1 -1
  504. package/src/sentiment/index.ts +17 -26
  505. package/src/sentiment/insights.ts +269 -0
  506. package/src/sentiment/keywords.ts +26 -4
  507. package/src/sentiment/pipeline.ts +28 -5
  508. package/src/sentiment/scorer.ts +13 -2
  509. package/src/sentiment/store.ts +2 -2
  510. package/src/sentiment/trends.ts +9 -3
  511. package/src/sentiment/types.ts +8 -4
  512. package/src/system-prompt.ts +6 -9
  513. package/src/tool-kit.ts +10 -9
  514. package/src/tools/fundamentals/company-overview.ts +19 -9
  515. package/src/tools/fundamentals/comps.ts +68 -55
  516. package/src/tools/fundamentals/dcf.ts +145 -95
  517. package/src/tools/fundamentals/earnings.ts +16 -6
  518. package/src/tools/fundamentals/financials.ts +16 -7
  519. package/src/tools/fundamentals/sec-filings.ts +37 -16
  520. package/src/tools/index.ts +56 -43
  521. package/src/tools/interaction/ask-user.ts +22 -10
  522. package/src/tools/macro/fear-greed.ts +1 -1
  523. package/src/tools/macro/fred-data.ts +58 -46
  524. package/src/tools/market/crypto-history.ts +8 -3
  525. package/src/tools/market/crypto-price.ts +6 -6
  526. package/src/tools/market/screen-stocks.ts +279 -0
  527. package/src/tools/market/search-ticker.ts +218 -17
  528. package/src/tools/market/stock-history.ts +37 -12
  529. package/src/tools/market/stock-quote.ts +10 -7
  530. package/src/tools/options/greeks.ts +5 -5
  531. package/src/tools/options/option-chain.ts +41 -17
  532. package/src/tools/portfolio/alerts.ts +457 -0
  533. package/src/tools/portfolio/correlation.ts +47 -20
  534. package/src/tools/portfolio/daily-report.ts +101 -0
  535. package/src/tools/portfolio/holdings-overlap.ts +31 -15
  536. package/src/tools/portfolio/notifications.ts +45 -0
  537. package/src/tools/portfolio/predictions.ts +406 -106
  538. package/src/tools/portfolio/risk-analysis.ts +46 -7
  539. package/src/tools/portfolio/tracker.ts +270 -109
  540. package/src/tools/portfolio/watchlist.ts +250 -121
  541. package/src/tools/sentiment/insight-format.ts +50 -0
  542. package/src/tools/sentiment/query-match.ts +117 -0
  543. package/src/tools/sentiment/reddit-sentiment.ts +360 -121
  544. package/src/tools/sentiment/sentiment-summary.ts +302 -235
  545. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  546. package/src/tools/sentiment/twitter-sentiment.ts +264 -73
  547. package/src/tools/sentiment/untrusted-text.ts +21 -0
  548. package/src/tools/sentiment/web-search.ts +21 -18
  549. package/src/tools/sentiment/web-sentiment.ts +30 -10
  550. package/src/tools/technical/backtest.ts +32 -22
  551. package/src/tools/technical/indicators.ts +39 -14
  552. package/src/types/index.ts +8 -3
  553. package/src/types/market.ts +1 -0
  554. package/src/types/portfolio.ts +14 -4
  555. package/src/types/sentiment.ts +61 -2
  556. package/src/workflows/compare-assets.ts +33 -21
  557. package/src/workflows/index.ts +3 -4
  558. package/src/workflows/options-screener.ts +27 -29
  559. package/src/workflows/portfolio-builder.ts +34 -27
  560. package/dist/infra/browser.d.ts +0 -35
  561. package/dist/infra/browser.js +0 -103
  562. package/dist/infra/browser.js.map +0 -1
  563. package/dist/tools/interaction/twitter-login.d.ts +0 -8
  564. package/dist/tools/interaction/twitter-login.js +0 -77
  565. package/dist/tools/interaction/twitter-login.js.map +0 -1
  566. package/dist/workflows/types.d.ts +0 -4
  567. package/dist/workflows/types.js +0 -2
  568. package/dist/workflows/types.js.map +0 -1
  569. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
  570. package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
  571. package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
  572. package/src/infra/browser.ts +0 -111
  573. package/src/tools/interaction/twitter-login.ts +0 -93
  574. package/src/workflows/types.ts +0 -4
@@ -1,3 +1,5 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
1
3
  interface CacheEntry<T> {
2
4
  value: T;
3
5
  expiresAt: number;
@@ -10,10 +12,26 @@ export interface StaleResult<T> {
10
12
  cachedAt: number;
11
13
  }
12
14
 
15
+ interface StaleMetadata {
16
+ stale: boolean;
17
+ cachedAt: number;
18
+ }
19
+
20
+ const staleMetadataStorage = new AsyncLocalStorage<StaleMetadata>();
21
+
22
+ export async function runWithStaleMetadata<T>(
23
+ fn: () => Promise<T>,
24
+ ): Promise<{ value: T; stale?: { cachedAt: number } }> {
25
+ const metadata: StaleMetadata = { stale: false, cachedAt: 0 };
26
+ const value = await staleMetadataStorage.run(metadata, fn);
27
+ return {
28
+ value,
29
+ stale: metadata.stale ? { cachedAt: metadata.cachedAt } : undefined,
30
+ };
31
+ }
32
+
13
33
  export class Cache {
14
34
  private store = new Map<string, CacheEntry<unknown>>();
15
- private lastStaleHit = false;
16
- private lastStaleCachedAt = 0;
17
35
 
18
36
  get<T>(key: string): T | undefined {
19
37
  const entry = this.store.get(key);
@@ -36,23 +54,14 @@ export class Cache {
36
54
  return undefined;
37
55
  }
38
56
 
39
- this.lastStaleHit = true;
40
- this.lastStaleCachedAt = entry.cachedAt;
57
+ const metadata = staleMetadataStorage.getStore();
58
+ if (metadata) {
59
+ metadata.stale = true;
60
+ metadata.cachedAt = entry.cachedAt;
61
+ }
41
62
  return { value: entry.value as T, stale: true, cachedAt: entry.cachedAt };
42
63
  }
43
64
 
44
- /**
45
- * Consume the stale flag set by the last getStale() hit.
46
- * Returns { stale: true, cachedAt } if the last getStale() found data,
47
- * then resets the flag. Used by wrapProvider to propagate stale metadata.
48
- */
49
- consumeStaleFlag(): { stale: boolean; cachedAt: number } {
50
- const result = { stale: this.lastStaleHit, cachedAt: this.lastStaleCachedAt };
51
- this.lastStaleHit = false;
52
- this.lastStaleCachedAt = 0;
53
- return result;
54
- }
55
-
56
65
  set<T>(key: string, value: T, ttlMs: number): void {
57
66
  this.store.set(key, { value, expiresAt: Date.now() + ttlMs, cachedAt: Date.now() });
58
67
  }
@@ -79,25 +88,27 @@ export const cache = new Cache();
79
88
 
80
89
  // Default TTLs
81
90
  export const TTL = {
82
- QUOTE: 60_000, // 1 minute
83
- HISTORY: 3_600_000, // 1 hour
91
+ QUOTE: 60_000, // 1 minute
92
+ HISTORY: 3_600_000, // 1 hour
84
93
  FUNDAMENTALS: 86_400_000, // 24 hours
85
- MACRO: 3_600_000, // 1 hour
86
- SENTIMENT: 300_000, // 5 minutes
94
+ MACRO: 3_600_000, // 1 hour
95
+ SENTIMENT: 300_000, // 5 minutes
87
96
  OPTIONS_CHAIN: 120_000, // 2 minutes
88
- CRUMB: 900_000, // 15 minutes
89
- WEB_SEARCH: 300_000, // 5 minutes
97
+ SCREENER: 60_000, // 1 minute
98
+ CRUMB: 900_000, // 15 minutes
99
+ WEB_SEARCH: 300_000, // 5 minutes
90
100
  FINNHUB_NEWS: 300_000, // 5 minutes
91
101
  } as const;
92
102
 
93
103
  // Stale limits — how long past TTL expiry a cached value is still useful as fallback
94
104
  export const STALE_LIMIT = {
95
- QUOTE: 15 * 60_000, // 15 minutes
96
- HISTORY: 24 * 3_600_000, // 24 hours
97
- FUNDAMENTALS: 7 * 86_400_000, // 7 days
98
- MACRO: 24 * 3_600_000, // 24 hours
99
- SENTIMENT: 3_600_000, // 1 hour
100
- OPTIONS_CHAIN: 30 * 60_000, // 30 minutes
101
- WEB_SEARCH: 3_600_000, // 1 hour
102
- FINNHUB_NEWS: 3_600_000, // 1 hour
105
+ QUOTE: 15 * 60_000, // 15 minutes
106
+ HISTORY: 24 * 3_600_000, // 24 hours
107
+ FUNDAMENTALS: 7 * 86_400_000, // 7 days
108
+ MACRO: 24 * 3_600_000, // 24 hours
109
+ SENTIMENT: 3_600_000, // 1 hour
110
+ OPTIONS_CHAIN: 30 * 60_000, // 30 minutes
111
+ SCREENER: 15 * 60_000, // 15 minutes
112
+ WEB_SEARCH: 3_600_000, // 1 hour
113
+ FINNHUB_NEWS: 3_600_000, // 1 hour
103
114
  } as const;
@@ -2,6 +2,7 @@ export interface HttpClientOptions {
2
2
  timeoutMs?: number;
3
3
  maxRetries?: number;
4
4
  retryDelayMs?: number;
5
+ maxRetryAfterMs?: number;
5
6
  headers?: Record<string, string>;
6
7
  }
7
8
 
@@ -9,6 +10,7 @@ const DEFAULT_OPTIONS: Required<HttpClientOptions> = {
9
10
  timeoutMs: 10_000,
10
11
  maxRetries: 2,
11
12
  retryDelayMs: 1_000,
13
+ maxRetryAfterMs: 5_000,
12
14
  headers: {},
13
15
  };
14
16
 
@@ -17,52 +19,116 @@ export class HttpError extends Error {
17
19
  public readonly status: number,
18
20
  public readonly statusText: string,
19
21
  public readonly body: string,
22
+ public readonly retryAfterMs?: number,
20
23
  ) {
21
24
  super(`HTTP ${status} ${statusText}`);
22
25
  this.name = "HttpError";
23
26
  }
24
27
  }
25
28
 
26
- export async function httpGet<T>(
29
+ export async function httpGet<T>(url: string, options: HttpClientOptions = {}): Promise<T> {
30
+ return httpRequest<T>(url, { ...options, method: "GET" });
31
+ }
32
+
33
+ export async function httpPost<T>(
27
34
  url: string,
35
+ body: unknown,
28
36
  options: HttpClientOptions = {},
29
37
  ): Promise<T> {
38
+ return httpRequest<T>(url, {
39
+ ...options,
40
+ method: "POST",
41
+ body: JSON.stringify(body),
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ ...options.headers,
45
+ },
46
+ });
47
+ }
48
+
49
+ interface HttpRequestOptions extends HttpClientOptions {
50
+ method: "GET" | "POST";
51
+ body?: string;
52
+ }
53
+
54
+ async function httpRequest<T>(url: string, options: HttpRequestOptions): Promise<T> {
30
55
  const opts = { ...DEFAULT_OPTIONS, ...options };
31
56
  let lastError: Error | undefined;
32
57
 
33
58
  for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
34
- if (attempt > 0) {
35
- await sleep(opts.retryDelayMs * attempt);
36
- }
59
+ let retryDelayMs: number | undefined;
37
60
 
38
61
  const controller = new AbortController();
39
62
  const timeout = setTimeout(() => controller.abort(), opts.timeoutMs);
40
63
 
41
64
  try {
42
65
  const response = await fetch(url, {
66
+ method: opts.method,
43
67
  signal: controller.signal,
44
68
  headers: opts.headers,
69
+ ...(opts.body !== undefined && { body: opts.body }),
45
70
  });
46
71
 
47
72
  if (!response.ok) {
48
73
  const body = await response.text().catch(() => "");
49
- throw new HttpError(response.status, response.statusText, body);
74
+ throw new HttpError(
75
+ response.status,
76
+ response.statusText,
77
+ body,
78
+ parseRetryAfterMs(response.headers?.get?.("retry-after") ?? null),
79
+ );
50
80
  }
51
81
 
52
82
  return (await response.json()) as T;
53
83
  } catch (error) {
54
84
  lastError = error as Error;
55
- if (error instanceof HttpError && error.status >= 400 && error.status < 500) {
85
+ if (!isRetryableError(error)) {
56
86
  throw error; // Don't retry client errors
57
87
  }
88
+ if (attempt < opts.maxRetries) {
89
+ retryDelayMs =
90
+ error instanceof HttpError && error.status === 429 && error.retryAfterMs !== undefined
91
+ ? capRetryAfterMs(error.retryAfterMs, opts.maxRetryAfterMs)
92
+ : opts.retryDelayMs * (attempt + 1);
93
+ }
58
94
  } finally {
59
95
  clearTimeout(timeout);
60
96
  }
97
+
98
+ if (retryDelayMs !== undefined) {
99
+ await sleep(retryDelayMs);
100
+ }
61
101
  }
62
102
 
63
103
  throw lastError!;
64
104
  }
65
105
 
106
+ function isRetryableError(error: unknown): boolean {
107
+ if (!(error instanceof HttpError)) return true;
108
+ if (error.status === 429) return true;
109
+ return error.status < 400 || error.status >= 500;
110
+ }
111
+
112
+ function parseRetryAfterMs(value: string | null): number | undefined {
113
+ if (!value) return undefined;
114
+ const trimmed = value.trim();
115
+ if (trimmed.length === 0) return undefined;
116
+
117
+ const seconds = Number(trimmed);
118
+ if (Number.isFinite(seconds) && seconds >= 0) {
119
+ return seconds * 1000;
120
+ }
121
+
122
+ const dateMs = Date.parse(trimmed);
123
+ if (Number.isNaN(dateMs)) return undefined;
124
+ return Math.max(0, dateMs - Date.now());
125
+ }
126
+
127
+ function capRetryAfterMs(retryAfterMs: number, maxRetryAfterMs: number | undefined): number {
128
+ if (maxRetryAfterMs === undefined) return retryAfterMs;
129
+ return Math.min(retryAfterMs, Math.max(0, maxRetryAfterMs));
130
+ }
131
+
66
132
  function sleep(ms: number): Promise<void> {
67
133
  return new Promise((resolve) => setTimeout(resolve, ms));
68
134
  }
@@ -1,18 +1,14 @@
1
1
  export { Cache, cache, TTL } from "./cache.js";
2
- export { RateLimiter, rateLimiter } from "./rate-limiter.js";
3
- export { httpGet, HttpError, type HttpClientOptions } from "./http-client.js";
4
- export { StealthBrowser } from "./browser.js";
2
+ export { type HttpClientOptions, HttpError, httpGet } from "./http-client.js";
5
3
  export {
6
- getOpenCandleHomeDir,
7
4
  ensureOpenCandleHomeDir,
8
- resolveOpenCandlePath,
9
5
  ensureParentDir,
10
- getWatchlistPath,
11
- getPortfolioPath,
12
- getPredictionsPath,
6
+ getBrowserProfileDir,
13
7
  getConfigPath,
8
+ getLogsDir,
14
9
  getOnboardingPath,
10
+ getOpenCandleHomeDir,
15
11
  getStateDbPath,
16
- getLogsDir,
17
- getBrowserProfileDir,
12
+ resolveOpenCandlePath,
18
13
  } from "./opencandle-paths.js";
14
+ export { RateLimiter, rateLimiter } from "./rate-limiter.js";
@@ -1,4 +1,7 @@
1
- export function getNativeDependencyErrorMessage(error: unknown, dependencyName: string): string | null {
1
+ export function getNativeDependencyErrorMessage(
2
+ error: unknown,
3
+ dependencyName: string,
4
+ ): string | null {
2
5
  const message = error instanceof Error ? error.message : String(error);
3
6
  if (
4
7
  !message.includes("NODE_MODULE_VERSION") &&
@@ -7,6 +10,8 @@ export function getNativeDependencyErrorMessage(error: unknown, dependencyName:
7
10
  return null;
8
11
  }
9
12
 
10
- return `${dependencyName} native binding was built for a different Node ABI than the active Node ${process.versions.node}. ` +
11
- `Run \`npm rebuild ${dependencyName}\` or reinstall dependencies under the active Node with \`npm install\`.`;
13
+ return (
14
+ `${dependencyName} native binding was built for a different Node ABI than the active Node ${process.versions.node}. ` +
15
+ `Run \`npm rebuild ${dependencyName}\` or reinstall dependencies under the active Node with \`npm install\`.`
16
+ );
12
17
  }
@@ -10,7 +10,9 @@ function isSupportedNodeVersion(version: string): boolean {
10
10
  return major >= 24 && major < 27;
11
11
  }
12
12
 
13
- export function getUnsupportedNodeVersionMessage(version: string = process.versions.node): string | null {
13
+ export function getUnsupportedNodeVersionMessage(
14
+ version: string = process.versions.node,
15
+ ): string | null {
14
16
  if (isSupportedNodeVersion(version)) return null;
15
17
 
16
18
  return `OpenCandle supports Node ${SUPPORTED_NODE_RANGE}. Current Node is ${version}. Use Node ${SUPPORTED_NODE_RANGE}; the repo default is Node 22.22.0 via \`nvm use\`. After switching Node versions, reinstall dependencies under the active Node with \`npm install\` or rebuild native modules with \`npm rebuild better-sqlite3\`.`;
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync } from "node:fs";
1
+ import { chmodSync, existsSync, mkdirSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
 
@@ -6,7 +6,7 @@ const OPENCANDLE_HOME_ENV = "OPENCANDLE_HOME";
6
6
 
7
7
  function ensureDir(path: string): void {
8
8
  if (!existsSync(path)) {
9
- mkdirSync(path, { recursive: true });
9
+ mkdirSync(path, { recursive: true, mode: 0o700 });
10
10
  }
11
11
  }
12
12
 
@@ -18,6 +18,7 @@ export function getOpenCandleHomeDir(): string {
18
18
  export function ensureOpenCandleHomeDir(): string {
19
19
  const home = getOpenCandleHomeDir();
20
20
  ensureDir(home);
21
+ if (process.platform !== "win32") chmodSync(home, 0o700);
21
22
  return home;
22
23
  }
23
24
 
@@ -31,18 +32,6 @@ export function ensureParentDir(path: string): string {
31
32
  return parent;
32
33
  }
33
34
 
34
- export function getWatchlistPath(): string {
35
- return resolveOpenCandlePath("watchlist.json");
36
- }
37
-
38
- export function getPortfolioPath(): string {
39
- return resolveOpenCandlePath("portfolio.json");
40
- }
41
-
42
- export function getPredictionsPath(): string {
43
- return resolveOpenCandlePath("predictions.json");
44
- }
45
-
46
35
  export function getConfigPath(): string {
47
36
  return resolveOpenCandlePath("config.json");
48
37
  }
@@ -29,18 +29,17 @@ export class RateLimiter {
29
29
  const bucket = this.buckets.get(provider);
30
30
  if (!bucket) return; // No limit configured
31
31
 
32
- this.refill(bucket);
32
+ while (true) {
33
+ this.refill(bucket);
33
34
 
34
- if (bucket.tokens >= 1) {
35
- bucket.tokens -= 1;
36
- return;
37
- }
35
+ if (bucket.tokens >= 1) {
36
+ bucket.tokens -= 1;
37
+ return;
38
+ }
38
39
 
39
- // Wait until a token is available
40
- const waitMs = ((1 - bucket.tokens) / bucket.config.refillRate) * 1000;
41
- await new Promise((resolve) => setTimeout(resolve, Math.ceil(waitMs)));
42
- this.refill(bucket);
43
- bucket.tokens -= 1;
40
+ const waitMs = ((1 - bucket.tokens) / bucket.config.refillRate) * 1000;
41
+ await new Promise((resolve) => setTimeout(resolve, Math.ceil(waitMs)));
42
+ }
44
43
  }
45
44
 
46
45
  private refill(bucket: Bucket): void {
@@ -56,18 +55,22 @@ export class RateLimiter {
56
55
 
57
56
  // Shared instance with default provider limits
58
57
  export const rateLimiter = new RateLimiter();
59
- rateLimiter.configure("yahoo", 5, 5); // 5 req/s
60
- rateLimiter.configure("coingecko", 10, 0.167); // 10 req/min
58
+ rateLimiter.configure("yahoo", 5, 5); // 5 req/s
59
+ rateLimiter.configure("coingecko", 10, 0.167); // 10 req/min
61
60
  rateLimiter.configure(
62
61
  "alphavantage",
63
62
  ALPHA_VANTAGE_RATE_LIMIT.maxTokens,
64
63
  ALPHA_VANTAGE_RATE_LIMIT.refillRate,
65
64
  ); // 5 req/min (free tier)
66
- rateLimiter.configure("fred", 120, 2); // 120 req/min
67
- rateLimiter.configure("twitter", 5, 0.167); // 5 req, ~10 req/min
68
- rateLimiter.configure("reddit", 5, 0.167); // 5 req, ~10 req/min
65
+ rateLimiter.configure("fred", 120, 2); // 120 req/min
66
+ rateLimiter.configure("twitter", 5, 0.167); // 5 req, ~10 req/min
67
+ rateLimiter.configure("reddit", 5, 0.167); // 5 req, ~10 req/min
69
68
  rateLimiter.configure("reddit_comments", 10, 0.5); // 10 req, ~30 req/min
70
- rateLimiter.configure("ddg", 3, 0.1); // 3 req, ~6 req/min
71
- rateLimiter.configure("brave_search", 5, 0.083); // 5 req, ~5 req/min
72
- rateLimiter.configure("exa", 5, 0.1); // 5 req, ~6 req/min
73
- rateLimiter.configure("finnhub", 60, 1); // 60 req/min (free tier)
69
+ rateLimiter.configure("ddg", 3, 0.1); // 3 req, ~6 req/min
70
+ rateLimiter.configure("brave_search", 5, 0.083); // 5 req, ~5 req/min
71
+ rateLimiter.configure("exa", 5, 0.1); // 5 req, ~6 req/min
72
+ rateLimiter.configure("finnhub", 60, 1); // 60 req/min (free tier)
73
+ // TradingView scanner is undocumented; keep usage batch-first and paced.
74
+ rateLimiter.configure("tradingview", 5, 1); // 5 burst, 1 req/s sustained
75
+ // SEC EDGAR fair-access guideline is 10 req/s; stay below it.
76
+ rateLimiter.configure("sec_edgar", 5, 5); // 5 burst, 5 req/s sustained
@@ -0,0 +1,82 @@
1
+ export const ALERT_CONDITION_VERSION = 1;
2
+
3
+ export type AlertCondition =
4
+ | ReturnType<typeof priceCrossesAbove>
5
+ | ReturnType<typeof priceCrossesBelow>
6
+ | ReturnType<typeof percentMove>
7
+ | ReturnType<typeof priceCrossesSma>
8
+ | ReturnType<typeof smaCross>
9
+ | ReturnType<typeof rsiThreshold>
10
+ | ReturnType<typeof volumeSpike>;
11
+
12
+ export function priceCrossesAbove(threshold: number): {
13
+ threshold: number;
14
+ field: "last_price";
15
+ } {
16
+ return { threshold, field: "last_price" };
17
+ }
18
+
19
+ export function priceCrossesBelow(threshold: number): {
20
+ threshold: number;
21
+ field: "last_price";
22
+ } {
23
+ return { threshold, field: "last_price" };
24
+ }
25
+
26
+ export function percentMove(
27
+ direction: "up" | "down",
28
+ percent: number,
29
+ window = "1d",
30
+ ): {
31
+ direction: "up" | "down";
32
+ percent: number;
33
+ window: string;
34
+ } {
35
+ return { direction, percent, window };
36
+ }
37
+
38
+ export function priceCrossesSma(
39
+ period: number,
40
+ direction: "above" | "below",
41
+ priceField: "close" | "last_price" = "close",
42
+ ): {
43
+ period: number;
44
+ direction: "above" | "below";
45
+ price_field: "close" | "last_price";
46
+ } {
47
+ return { period, direction, price_field: priceField };
48
+ }
49
+
50
+ export function smaCross(
51
+ fastPeriod: number,
52
+ slowPeriod: number,
53
+ direction: "above" | "below",
54
+ ): {
55
+ fast_period: number;
56
+ slow_period: number;
57
+ direction: "above" | "below";
58
+ } {
59
+ return { fast_period: fastPeriod, slow_period: slowPeriod, direction };
60
+ }
61
+
62
+ export function rsiThreshold(
63
+ period: number,
64
+ threshold: number,
65
+ direction: "above" | "below",
66
+ ): {
67
+ period: number;
68
+ threshold: number;
69
+ direction: "above" | "below";
70
+ } {
71
+ return { period, threshold, direction };
72
+ }
73
+
74
+ export function volumeSpike(
75
+ lookbackPeriod: number,
76
+ multiplier: number,
77
+ ): {
78
+ lookback_period: number;
79
+ multiplier: number;
80
+ } {
81
+ return { lookback_period: lookbackPeriod, multiplier };
82
+ }