opencandle 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +186 -117
- package/dist/analysts/contracts.d.ts +1 -3
- package/dist/analysts/contracts.js +1 -11
- package/dist/analysts/contracts.js.map +1 -1
- package/dist/analysts/orchestrator.d.ts +1 -3
- package/dist/analysts/orchestrator.js +1 -26
- package/dist/analysts/orchestrator.js.map +1 -1
- package/dist/cli.js +32 -8
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +19 -3
- package/dist/config.js +69 -3
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/browser.d.ts +1 -3
- package/dist/infra/browser.js +4 -2
- package/dist/infra/browser.js.map +1 -1
- package/dist/infra/cache.d.ts +8 -11
- package/dist/infra/cache.js +17 -15
- package/dist/infra/cache.js.map +1 -1
- package/dist/infra/http-client.d.ts +4 -1
- package/dist/infra/http-client.js +59 -6
- package/dist/infra/http-client.js.map +1 -1
- package/dist/infra/index.d.ts +3 -3
- package/dist/infra/index.js +3 -3
- package/dist/infra/index.js.map +1 -1
- package/dist/infra/native-dependencies.js +2 -2
- package/dist/infra/native-dependencies.js.map +1 -1
- package/dist/infra/node-version.js.map +1 -1
- package/dist/infra/opencandle-paths.d.ts +0 -3
- package/dist/infra/opencandle-paths.js +4 -11
- package/dist/infra/opencandle-paths.js.map +1 -1
- package/dist/infra/rate-limiter.d.ts +4 -0
- package/dist/infra/rate-limiter.js +17 -10
- package/dist/infra/rate-limiter.js.map +1 -1
- package/dist/market-state/alert-conditions.d.ts +34 -0
- package/dist/market-state/alert-conditions.js +23 -0
- package/dist/market-state/alert-conditions.js.map +1 -0
- package/dist/market-state/alert-runner.d.ts +55 -0
- package/dist/market-state/alert-runner.js +634 -0
- package/dist/market-state/alert-runner.js.map +1 -0
- package/dist/market-state/daily-report.d.ts +26 -0
- package/dist/market-state/daily-report.js +179 -0
- package/dist/market-state/daily-report.js.map +1 -0
- package/dist/market-state/local-automation-service.d.ts +25 -0
- package/dist/market-state/local-automation-service.js +119 -0
- package/dist/market-state/local-automation-service.js.map +1 -0
- package/dist/market-state/notification-delivery.d.ts +14 -0
- package/dist/market-state/notification-delivery.js +139 -0
- package/dist/market-state/notification-delivery.js.map +1 -0
- package/dist/market-state/resolve-for-mutation.d.ts +10 -0
- package/dist/market-state/resolve-for-mutation.js +15 -0
- package/dist/market-state/resolve-for-mutation.js.map +1 -0
- package/dist/market-state/resolve.d.ts +14 -0
- package/dist/market-state/resolve.js +89 -0
- package/dist/market-state/resolve.js.map +1 -0
- package/dist/market-state/service.d.ts +527 -0
- package/dist/market-state/service.js +1099 -0
- package/dist/market-state/service.js.map +1 -0
- package/dist/memory/index.d.ts +7 -7
- package/dist/memory/index.js +6 -6
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manager.d.ts +9 -0
- package/dist/memory/manager.js +39 -22
- package/dist/memory/manager.js.map +1 -1
- package/dist/memory/retrieval.js +7 -4
- package/dist/memory/retrieval.js.map +1 -1
- package/dist/memory/sqlite.js +385 -3
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/storage.d.ts +3 -2
- package/dist/memory/storage.js +1 -2
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/tool-defaults.js +64 -28
- package/dist/memory/tool-defaults.js.map +1 -1
- package/dist/memory/types.js +4 -0
- package/dist/memory/types.js.map +1 -1
- package/dist/monitor.d.ts +2 -0
- package/dist/monitor.js +104 -0
- package/dist/monitor.js.map +1 -0
- package/dist/onboarding/connect.js +4 -6
- package/dist/onboarding/connect.js.map +1 -1
- package/dist/onboarding/credential-interceptor.js +1 -1
- package/dist/onboarding/credential-interceptor.js.map +1 -1
- package/dist/onboarding/degradation-accumulator.js +1 -3
- package/dist/onboarding/degradation-accumulator.js.map +1 -1
- package/dist/onboarding/providers.js +3 -16
- package/dist/onboarding/providers.js.map +1 -1
- package/dist/onboarding/state.js.map +1 -1
- package/dist/onboarding/tool-helpers.js +1 -1
- package/dist/onboarding/tool-helpers.js.map +1 -1
- package/dist/onboarding/tool-tags.js +6 -4
- package/dist/onboarding/tool-tags.js.map +1 -1
- package/dist/onboarding/validation.js +1 -1
- package/dist/onboarding/validation.js.map +1 -1
- package/dist/pi/opencandle-extension.d.ts +8 -0
- package/dist/pi/opencandle-extension.js +637 -59
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/session.d.ts +1 -1
- package/dist/pi/session.js +3 -1
- package/dist/pi/session.js.map +1 -1
- package/dist/pi/setup.js +17 -2
- package/dist/pi/setup.js.map +1 -1
- package/dist/pi/tool-adapter.js +5 -2
- package/dist/pi/tool-adapter.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +18 -3
- package/dist/prompts/context-builder.js +117 -18
- package/dist/prompts/context-builder.js.map +1 -1
- package/dist/prompts/disclaimer.js +1 -1
- package/dist/prompts/disclaimer.js.map +1 -1
- package/dist/prompts/policy-cards.d.ts +13 -0
- package/dist/prompts/policy-cards.js +197 -0
- package/dist/prompts/policy-cards.js.map +1 -0
- package/dist/prompts/sections.d.ts +1 -1
- package/dist/prompts/sections.js +3 -3
- package/dist/prompts/sections.js.map +1 -1
- package/dist/prompts/symbol-preflight.d.ts +20 -0
- package/dist/prompts/symbol-preflight.js +49 -0
- package/dist/prompts/symbol-preflight.js.map +1 -0
- package/dist/prompts/workflow-prompts.d.ts +1 -1
- package/dist/prompts/workflow-prompts.js +209 -19
- package/dist/prompts/workflow-prompts.js.map +1 -1
- package/dist/providers/alpha-vantage.d.ts +1 -1
- package/dist/providers/alpha-vantage.js +49 -8
- package/dist/providers/alpha-vantage.js.map +1 -1
- package/dist/providers/coingecko.js +1 -1
- package/dist/providers/coingecko.js.map +1 -1
- package/dist/providers/errors.d.ts +5 -0
- package/dist/providers/errors.js +11 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/exa-search.d.ts +2 -2
- package/dist/providers/exa-search.js +19 -11
- package/dist/providers/exa-search.js.map +1 -1
- package/dist/providers/fear-greed.js +1 -1
- package/dist/providers/fear-greed.js.map +1 -1
- package/dist/providers/finnhub.js +3 -5
- package/dist/providers/finnhub.js.map +1 -1
- package/dist/providers/fred.js +2 -2
- package/dist/providers/fred.js.map +1 -1
- package/dist/providers/index.d.ts +7 -6
- package/dist/providers/index.js +6 -5
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/reddit.js +2 -2
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/sec-edgar.d.ts +9 -1
- package/dist/providers/sec-edgar.js +181 -6
- package/dist/providers/sec-edgar.js.map +1 -1
- package/dist/providers/tradingview.d.ts +47 -0
- package/dist/providers/tradingview.js +275 -0
- package/dist/providers/tradingview.js.map +1 -0
- package/dist/providers/twitter.js +6 -8
- package/dist/providers/twitter.js.map +1 -1
- package/dist/providers/web-search.js +26 -12
- package/dist/providers/web-search.js.map +1 -1
- package/dist/providers/with-fallback.js +4 -2
- package/dist/providers/with-fallback.js.map +1 -1
- package/dist/providers/wrap-provider.d.ts +2 -3
- package/dist/providers/wrap-provider.js +14 -8
- package/dist/providers/wrap-provider.js.map +1 -1
- package/dist/providers/yahoo-finance.d.ts +3 -1
- package/dist/providers/yahoo-finance.js +226 -11
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.d.ts +9 -0
- package/dist/routing/classify-intent.js +153 -3
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/routing/defaults.d.ts +1 -1
- package/dist/routing/defaults.js +3 -3
- package/dist/routing/defaults.js.map +1 -1
- package/dist/routing/entity-extractor.d.ts +2 -0
- package/dist/routing/entity-extractor.js +377 -26
- package/dist/routing/entity-extractor.js.map +1 -1
- package/dist/routing/fund-symbols.d.ts +2 -0
- package/dist/routing/fund-symbols.js +55 -0
- package/dist/routing/fund-symbols.js.map +1 -0
- package/dist/routing/horizon.d.ts +1 -0
- package/dist/routing/horizon.js +10 -0
- package/dist/routing/horizon.js.map +1 -0
- package/dist/routing/index.d.ts +12 -6
- package/dist/routing/index.js +8 -4
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/legacy-rule-router.d.ts +9 -0
- package/dist/routing/legacy-rule-router.js +12 -0
- package/dist/routing/legacy-rule-router.js.map +1 -0
- package/dist/routing/planning.d.ts +54 -0
- package/dist/routing/planning.js +562 -0
- package/dist/routing/planning.js.map +1 -0
- package/dist/routing/route-manifest.d.ts +35 -0
- package/dist/routing/route-manifest.js +242 -0
- package/dist/routing/route-manifest.js.map +1 -0
- package/dist/routing/router-llm-client.js.map +1 -1
- package/dist/routing/router-prompt.js +46 -45
- package/dist/routing/router-prompt.js.map +1 -1
- package/dist/routing/router-types.d.ts +10 -0
- package/dist/routing/router.d.ts +1 -0
- package/dist/routing/router.js +572 -13
- package/dist/routing/router.js.map +1 -1
- package/dist/routing/slot-resolver.d.ts +1 -1
- package/dist/routing/slot-resolver.js +45 -7
- package/dist/routing/slot-resolver.js.map +1 -1
- package/dist/routing/symbol-disambiguator.d.ts +11 -0
- package/dist/routing/symbol-disambiguator.js +52 -0
- package/dist/routing/symbol-disambiguator.js.map +1 -0
- package/dist/routing/turn-context.d.ts +44 -0
- package/dist/routing/turn-context.js +45 -0
- package/dist/routing/turn-context.js.map +1 -0
- package/dist/routing/types.d.ts +15 -1
- package/dist/runtime/answer-contracts.d.ts +82 -0
- package/dist/runtime/answer-contracts.js +442 -0
- package/dist/runtime/answer-contracts.js.map +1 -0
- package/dist/runtime/artifact-contracts.d.ts +14 -0
- package/dist/runtime/artifact-contracts.js +57 -0
- package/dist/runtime/artifact-contracts.js.map +1 -0
- package/dist/runtime/planning-evidence.d.ts +99 -0
- package/dist/runtime/planning-evidence.js +466 -0
- package/dist/runtime/planning-evidence.js.map +1 -0
- package/dist/runtime/prompt-step.d.ts +1 -9
- package/dist/runtime/prompt-step.js +0 -10
- package/dist/runtime/prompt-step.js.map +1 -1
- package/dist/runtime/run-context.d.ts +5 -2
- package/dist/runtime/run-context.js +8 -1
- package/dist/runtime/run-context.js.map +1 -1
- package/dist/runtime/session-coordinator.d.ts +29 -3
- package/dist/runtime/session-coordinator.js +204 -31
- package/dist/runtime/session-coordinator.js.map +1 -1
- package/dist/runtime/session-title.d.ts +14 -0
- package/dist/runtime/session-title.js +50 -0
- package/dist/runtime/session-title.js.map +1 -0
- package/dist/runtime/tool-defaults-wrapper.js +1 -3
- package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
- package/dist/runtime/validation.js.map +1 -1
- package/dist/runtime/workflow-events.js.map +1 -1
- package/dist/runtime/workflow-runner.d.ts +3 -3
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/runtime/workflow-runner.js.map +1 -1
- package/dist/sentiment/adapters/finnhub.d.ts +1 -1
- package/dist/sentiment/adapters/finnhub.js +6 -1
- package/dist/sentiment/adapters/finnhub.js.map +1 -1
- package/dist/sentiment/adapters/reddit.d.ts +2 -2
- package/dist/sentiment/adapters/twitter.d.ts +1 -1
- package/dist/sentiment/adapters/web.d.ts +1 -1
- package/dist/sentiment/index.d.ts +9 -11
- package/dist/sentiment/index.js +9 -20
- package/dist/sentiment/index.js.map +1 -1
- package/dist/sentiment/keywords.js +26 -4
- package/dist/sentiment/keywords.js.map +1 -1
- package/dist/sentiment/pipeline.d.ts +2 -2
- package/dist/sentiment/pipeline.js +1 -1
- package/dist/sentiment/pipeline.js.map +1 -1
- package/dist/sentiment/scorer.js +1 -1
- package/dist/sentiment/store.d.ts +1 -1
- package/dist/sentiment/store.js +1 -1
- package/dist/sentiment/store.js.map +1 -1
- package/dist/sentiment/trends.d.ts +1 -1
- package/dist/sentiment/trends.js.map +1 -1
- package/dist/sentiment/types.js.map +1 -1
- package/dist/system-prompt.js +7 -3
- package/dist/system-prompt.js.map +1 -1
- package/dist/tool-kit.d.ts +7 -7
- package/dist/tool-kit.js +4 -4
- package/dist/tool-kit.js.map +1 -1
- package/dist/tools/fundamentals/company-overview.js +12 -7
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +19 -10
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +24 -12
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +9 -4
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +9 -4
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.d.ts +1 -0
- package/dist/tools/fundamentals/sec-filings.js +36 -4
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.d.ts +23 -18
- package/dist/tools/index.js +53 -38
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interaction/ask-user.js +15 -3
- package/dist/tools/interaction/ask-user.js.map +1 -1
- package/dist/tools/interaction/twitter-login.js +13 -3
- package/dist/tools/interaction/twitter-login.js.map +1 -1
- package/dist/tools/macro/fear-greed.js +1 -1
- package/dist/tools/macro/fear-greed.js.map +1 -1
- package/dist/tools/macro/fred-data.d.ts +1 -1
- package/dist/tools/macro/fred-data.js +44 -9
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +21 -3
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +4 -2
- package/dist/tools/market/crypto-price.js.map +1 -1
- package/dist/tools/market/screen-stocks.d.ts +18 -0
- package/dist/tools/market/screen-stocks.js +252 -0
- package/dist/tools/market/screen-stocks.js.map +1 -0
- package/dist/tools/market/search-ticker.js +161 -9
- package/dist/tools/market/search-ticker.js.map +1 -1
- package/dist/tools/market/stock-history.d.ts +2 -2
- package/dist/tools/market/stock-history.js +27 -8
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +6 -4
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/greeks.js +1 -2
- package/dist/tools/options/greeks.js.map +1 -1
- package/dist/tools/options/option-chain.js +27 -9
- package/dist/tools/options/option-chain.js.map +1 -1
- package/dist/tools/portfolio/alerts.d.ts +15 -0
- package/dist/tools/portfolio/alerts.js +357 -0
- package/dist/tools/portfolio/alerts.js.map +1 -0
- package/dist/tools/portfolio/correlation.d.ts +1 -1
- package/dist/tools/portfolio/correlation.js +34 -14
- package/dist/tools/portfolio/correlation.js.map +1 -1
- package/dist/tools/portfolio/daily-report.d.ts +8 -0
- package/dist/tools/portfolio/daily-report.js +83 -0
- package/dist/tools/portfolio/daily-report.js.map +1 -0
- package/dist/tools/portfolio/holdings-overlap.d.ts +8 -0
- package/dist/tools/portfolio/holdings-overlap.js +112 -0
- package/dist/tools/portfolio/holdings-overlap.js.map +1 -0
- package/dist/tools/portfolio/notifications.d.ts +7 -0
- package/dist/tools/portfolio/notifications.js +43 -0
- package/dist/tools/portfolio/notifications.js.map +1 -0
- package/dist/tools/portfolio/predictions.d.ts +12 -6
- package/dist/tools/portfolio/predictions.js +338 -88
- package/dist/tools/portfolio/predictions.js.map +1 -1
- package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
- package/dist/tools/portfolio/risk-analysis.js +46 -7
- package/dist/tools/portfolio/risk-analysis.js.map +1 -1
- package/dist/tools/portfolio/tracker.d.ts +4 -3
- package/dist/tools/portfolio/tracker.js +247 -102
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.d.ts +6 -4
- package/dist/tools/portfolio/watchlist.js +209 -101
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/reddit-sentiment.js +24 -11
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.js +71 -14
- package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
- package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
- package/dist/tools/sentiment/sentiment-trend.js +12 -2
- package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
- package/dist/tools/sentiment/twitter-sentiment.js +13 -6
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
- package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
- package/dist/tools/sentiment/untrusted-text.js +17 -0
- package/dist/tools/sentiment/untrusted-text.js.map +1 -0
- package/dist/tools/sentiment/web-search.js +37 -12
- package/dist/tools/sentiment/web-search.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +16 -4
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/tools/technical/backtest.d.ts +3 -3
- package/dist/tools/technical/backtest.js +65 -44
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +24 -8
- package/dist/tools/technical/indicators.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/market.d.ts +1 -0
- package/dist/types/options.d.ts +10 -0
- package/dist/types/portfolio.d.ts +41 -4
- package/dist/workflows/compare-assets.d.ts +0 -3
- package/dist/workflows/compare-assets.js +55 -10
- package/dist/workflows/compare-assets.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -4
- package/dist/workflows/index.js +3 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/options-screener.d.ts +0 -3
- package/dist/workflows/options-screener.js +88 -14
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +0 -3
- package/dist/workflows/portfolio-builder.js +7 -11
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/gui/server/ask-user-bridge.ts +82 -0
- package/gui/server/automation-heartbeat.ts +97 -0
- package/gui/server/background-quotes.ts +97 -1
- package/gui/server/chat-event-adapter.ts +32 -10
- package/gui/server/chat-run-session.ts +16 -0
- package/gui/server/gui-session-manager.ts +5 -0
- package/gui/server/invoke-tool.ts +144 -1
- package/gui/server/live-chat-event-adapter.ts +21 -6
- package/gui/server/market-state-api.ts +315 -0
- package/gui/server/model-setup.ts +149 -2
- package/gui/server/private-api-access.ts +62 -0
- package/gui/server/projector.ts +58 -11
- package/gui/server/prompt-observation.ts +58 -0
- package/gui/server/quote-snapshot-store.ts +50 -0
- package/gui/server/server.ts +236 -376
- package/gui/server/session-actions.ts +186 -1
- package/gui/server/session-entry-wait.ts +81 -0
- package/gui/server/shutdown.ts +47 -0
- package/gui/server/tool-invoke-ack.ts +49 -0
- package/gui/server/tool-metadata.ts +23 -10
- package/gui/server/websocket.ts +13 -3
- package/gui/server/writer-lock.ts +6 -2
- package/gui/server/ws-hub.ts +292 -0
- package/gui/shared/chat-events.ts +16 -1
- package/gui/shared/event-reducer.ts +24 -6
- package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
- package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
- package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
- package/gui/web/dist/index.html +2 -2
- package/package.json +22 -12
- package/src/analysts/contracts.ts +10 -23
- package/src/analysts/orchestrator.ts +8 -43
- package/src/cli.ts +37 -13
- package/src/config.ts +99 -7
- package/src/index.ts +1 -1
- package/src/infra/browser.ts +4 -2
- package/src/infra/cache.ts +41 -30
- package/src/infra/http-client.ts +72 -6
- package/src/infra/index.ts +7 -10
- package/src/infra/native-dependencies.ts +8 -3
- package/src/infra/node-version.ts +3 -1
- package/src/infra/opencandle-paths.ts +3 -14
- package/src/infra/rate-limiter.ts +32 -20
- package/src/market-state/alert-conditions.ts +82 -0
- package/src/market-state/alert-runner.ts +863 -0
- package/src/market-state/daily-report.ts +247 -0
- package/src/market-state/local-automation-service.ts +162 -0
- package/src/market-state/notification-delivery.ts +158 -0
- package/src/market-state/resolve-for-mutation.ts +24 -0
- package/src/market-state/resolve.ts +112 -0
- package/src/market-state/service.ts +2344 -0
- package/src/memory/index.ts +7 -7
- package/src/memory/manager.ts +57 -26
- package/src/memory/retrieval.ts +8 -7
- package/src/memory/sqlite.ts +407 -6
- package/src/memory/storage.ts +8 -17
- package/src/memory/tool-defaults.ts +60 -39
- package/src/memory/types.ts +7 -3
- package/src/monitor.ts +121 -0
- package/src/onboarding/connect.ts +10 -33
- package/src/onboarding/credential-interceptor.ts +3 -15
- package/src/onboarding/degradation-accumulator.ts +1 -3
- package/src/onboarding/providers.ts +9 -40
- package/src/onboarding/state.ts +4 -15
- package/src/onboarding/tool-helpers.ts +2 -9
- package/src/onboarding/tool-tags.ts +6 -6
- package/src/onboarding/validation.ts +14 -20
- package/src/pi/opencandle-extension.ts +795 -120
- package/src/pi/session.ts +7 -5
- package/src/pi/setup.ts +61 -33
- package/src/pi/tool-adapter.ts +5 -2
- package/src/prompts/context-builder.ts +143 -21
- package/src/prompts/disclaimer.ts +1 -1
- package/src/prompts/policy-cards.ts +220 -0
- package/src/prompts/sections.ts +4 -4
- package/src/prompts/symbol-preflight.ts +80 -0
- package/src/prompts/workflow-prompts.ts +231 -28
- package/src/providers/alpha-vantage.ts +82 -40
- package/src/providers/coingecko.ts +2 -5
- package/src/providers/errors.ts +9 -0
- package/src/providers/exa-search.ts +24 -22
- package/src/providers/fear-greed.ts +1 -1
- package/src/providers/finnhub.ts +7 -6
- package/src/providers/fred.ts +3 -3
- package/src/providers/index.ts +14 -6
- package/src/providers/reddit.ts +17 -6
- package/src/providers/sec-edgar.ts +235 -5
- package/src/providers/tradingview.ts +399 -0
- package/src/providers/twitter.ts +6 -8
- package/src/providers/web-search.ts +30 -20
- package/src/providers/with-fallback.ts +8 -7
- package/src/providers/wrap-provider.ts +15 -10
- package/src/providers/yahoo-finance.ts +292 -20
- package/src/routing/classify-intent.ts +186 -4
- package/src/routing/defaults.ts +4 -4
- package/src/routing/entity-extractor.ts +428 -28
- package/src/routing/fund-symbols.ts +58 -0
- package/src/routing/horizon.ts +7 -0
- package/src/routing/index.ts +60 -16
- package/src/routing/legacy-rule-router.ts +13 -0
- package/src/routing/planning.ts +823 -0
- package/src/routing/route-manifest.ts +309 -0
- package/src/routing/router-llm-client.ts +4 -4
- package/src/routing/router-prompt.ts +52 -52
- package/src/routing/router-types.ts +18 -0
- package/src/routing/router.ts +717 -20
- package/src/routing/slot-resolver.ts +75 -14
- package/src/routing/symbol-disambiguator.ts +72 -0
- package/src/routing/turn-context.ts +108 -0
- package/src/routing/types.ts +15 -1
- package/src/runtime/answer-contracts.ts +672 -0
- package/src/runtime/artifact-contracts.ts +77 -0
- package/src/runtime/planning-evidence.ts +682 -0
- package/src/runtime/prompt-step.ts +1 -16
- package/src/runtime/run-context.ts +12 -2
- package/src/runtime/session-coordinator.ts +297 -56
- package/src/runtime/session-title.ts +60 -0
- package/src/runtime/tool-defaults-wrapper.ts +1 -3
- package/src/runtime/validation.ts +1 -4
- package/src/runtime/workflow-events.ts +7 -7
- package/src/runtime/workflow-runner.ts +5 -11
- package/src/sentiment/adapters/finnhub.ts +7 -2
- package/src/sentiment/adapters/reddit.ts +2 -2
- package/src/sentiment/adapters/twitter.ts +1 -1
- package/src/sentiment/adapters/web.ts +1 -1
- package/src/sentiment/index.ts +16 -26
- package/src/sentiment/keywords.ts +26 -4
- package/src/sentiment/pipeline.ts +15 -4
- package/src/sentiment/scorer.ts +1 -1
- package/src/sentiment/store.ts +2 -2
- package/src/sentiment/trends.ts +9 -3
- package/src/sentiment/types.ts +5 -4
- package/src/system-prompt.ts +7 -3
- package/src/tool-kit.ts +10 -9
- package/src/tools/fundamentals/company-overview.ts +20 -10
- package/src/tools/fundamentals/comps.ts +69 -56
- package/src/tools/fundamentals/dcf.ts +146 -96
- package/src/tools/fundamentals/earnings.ts +17 -7
- package/src/tools/fundamentals/financials.ts +17 -8
- package/src/tools/fundamentals/sec-filings.ts +52 -8
- package/src/tools/index.ts +53 -38
- package/src/tools/interaction/ask-user.ts +22 -10
- package/src/tools/interaction/twitter-login.ts +17 -5
- package/src/tools/macro/fear-greed.ts +2 -2
- package/src/tools/macro/fred-data.ts +80 -42
- package/src/tools/market/crypto-history.ts +25 -4
- package/src/tools/market/crypto-price.ts +7 -7
- package/src/tools/market/screen-stocks.ts +279 -0
- package/src/tools/market/search-ticker.ts +219 -18
- package/src/tools/market/stock-history.ts +38 -13
- package/src/tools/market/stock-quote.ts +11 -8
- package/src/tools/options/greeks.ts +5 -6
- package/src/tools/options/option-chain.ts +47 -18
- package/src/tools/portfolio/alerts.ts +457 -0
- package/src/tools/portfolio/correlation.ts +48 -21
- package/src/tools/portfolio/daily-report.ts +101 -0
- package/src/tools/portfolio/holdings-overlap.ts +139 -0
- package/src/tools/portfolio/notifications.ts +45 -0
- package/src/tools/portfolio/predictions.ts +407 -107
- package/src/tools/portfolio/risk-analysis.ts +47 -8
- package/src/tools/portfolio/tracker.ts +271 -110
- package/src/tools/portfolio/watchlist.ts +251 -116
- package/src/tools/sentiment/reddit-sentiment.ts +51 -25
- package/src/tools/sentiment/sentiment-summary.ts +116 -35
- package/src/tools/sentiment/sentiment-trend.ts +24 -7
- package/src/tools/sentiment/twitter-sentiment.ts +23 -16
- package/src/tools/sentiment/untrusted-text.ts +21 -0
- package/src/tools/sentiment/web-search.ts +52 -16
- package/src/tools/sentiment/web-sentiment.ts +27 -11
- package/src/tools/technical/backtest.ts +78 -47
- package/src/tools/technical/indicators.ts +40 -17
- package/src/types/index.ts +8 -3
- package/src/types/market.ts +1 -0
- package/src/types/options.ts +17 -0
- package/src/types/portfolio.ts +46 -4
- package/src/types/sentiment.ts +2 -2
- package/src/workflows/compare-assets.ts +67 -19
- package/src/workflows/index.ts +3 -4
- package/src/workflows/options-screener.ts +98 -22
- package/src/workflows/portfolio-builder.ts +40 -29
- package/dist/runtime/index.d.ts +0 -16
- package/dist/runtime/index.js +0 -10
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/provider-ids.d.ts +0 -14
- package/dist/runtime/provider-ids.js +0 -14
- package/dist/runtime/provider-ids.js.map +0 -1
- package/dist/workflows/types.d.ts +0 -4
- package/dist/workflows/types.js +0 -2
- package/dist/workflows/types.js.map +0 -1
- package/gui/web/dist/assets/CatalogOverlay-D1ImSJTe.js +0 -1
- package/gui/web/dist/assets/index-DBrWq43L.css +0 -1
- package/gui/web/dist/assets/index-RflHaj0y.js +0 -67
- package/src/runtime/index.ts +0 -55
- package/src/runtime/provider-ids.ts +0 -15
- package/src/workflows/types.ts +0 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
|
|
2
|
-
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
3
1
|
import { getConfig } from "../config.js";
|
|
2
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
3
|
+
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
4
|
+
import type { WebSearchEnvelope, WebSearchResult } from "../types/sentiment.js";
|
|
4
5
|
import { ProviderCredentialError } from "./provider-credential-error.js";
|
|
5
|
-
import type { WebSearchResult, WebSearchEnvelope } from "../types/sentiment.js";
|
|
6
6
|
import type { WebSearchOpts } from "./web-search.js";
|
|
7
7
|
|
|
8
8
|
const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
|
|
@@ -19,19 +19,27 @@ let requestIdCounter = 0;
|
|
|
19
19
|
|
|
20
20
|
function freshnessToMs(freshness: WebSearchOpts["freshness"]): number {
|
|
21
21
|
switch (freshness) {
|
|
22
|
-
case "hours":
|
|
23
|
-
|
|
24
|
-
case "
|
|
25
|
-
|
|
22
|
+
case "hours":
|
|
23
|
+
return 60 * 60 * 1000;
|
|
24
|
+
case "day":
|
|
25
|
+
return 24 * 60 * 60 * 1000;
|
|
26
|
+
case "week":
|
|
27
|
+
return 7 * 24 * 60 * 60 * 1000;
|
|
28
|
+
case "month":
|
|
29
|
+
return 30 * 24 * 60 * 60 * 1000;
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
function enrichQueryForMcp(query: string, freshness: WebSearchOpts["freshness"]): string {
|
|
30
34
|
switch (freshness) {
|
|
31
|
-
case "hours":
|
|
32
|
-
|
|
33
|
-
case "
|
|
34
|
-
|
|
35
|
+
case "hours":
|
|
36
|
+
return `${query} past hour`;
|
|
37
|
+
case "day":
|
|
38
|
+
return `${query} past 24 hours`;
|
|
39
|
+
case "week":
|
|
40
|
+
return `${query} past week`;
|
|
41
|
+
case "month":
|
|
42
|
+
return `${query} past month`;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
45
|
|
|
@@ -192,10 +200,7 @@ function exaCacheKey(query: string, opts: WebSearchOpts): string {
|
|
|
192
200
|
// MCP path
|
|
193
201
|
// ---------------------------------------------------------------------------
|
|
194
202
|
|
|
195
|
-
async function exaMcpSearch(
|
|
196
|
-
query: string,
|
|
197
|
-
opts: WebSearchOpts,
|
|
198
|
-
): Promise<WebSearchEnvelope> {
|
|
203
|
+
async function exaMcpSearch(query: string, opts: WebSearchOpts): Promise<WebSearchEnvelope> {
|
|
199
204
|
const enrichedQuery = enrichQueryForMcp(query, opts.freshness);
|
|
200
205
|
|
|
201
206
|
const response = await fetch(EXA_MCP_URL, {
|
|
@@ -330,10 +335,7 @@ async function exaApiSearch(
|
|
|
330
335
|
// Public entry point
|
|
331
336
|
// ---------------------------------------------------------------------------
|
|
332
337
|
|
|
333
|
-
export async function exaSearch(
|
|
334
|
-
query: string,
|
|
335
|
-
opts: WebSearchOpts,
|
|
336
|
-
): Promise<WebSearchEnvelope> {
|
|
338
|
+
export async function exaSearch(query: string, opts: WebSearchOpts): Promise<WebSearchEnvelope> {
|
|
337
339
|
const key = exaCacheKey(query, opts);
|
|
338
340
|
const cached = cache.get<WebSearchEnvelope>(key);
|
|
339
341
|
if (cached) return cached;
|
|
@@ -362,12 +364,12 @@ export async function exaSearch(
|
|
|
362
364
|
}
|
|
363
365
|
}
|
|
364
366
|
|
|
367
|
+
export type { ExaApiResponse, McpRpcResponse, ParsedResult };
|
|
365
368
|
// Exported for testing
|
|
366
369
|
export {
|
|
367
|
-
parseMcpResultBlocks,
|
|
368
|
-
extractJsonRpcPayload,
|
|
369
370
|
enrichQueryForMcp,
|
|
371
|
+
extractJsonRpcPayload,
|
|
370
372
|
filterByFreshness,
|
|
371
373
|
mapApiResults,
|
|
374
|
+
parseMcpResultBlocks,
|
|
372
375
|
};
|
|
373
|
-
export type { ParsedResult, McpRpcResponse, ExaApiResponse };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
1
2
|
import { httpGet } from "../infra/http-client.js";
|
|
2
|
-
import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
|
|
3
3
|
import type { FearGreedData } from "../types/sentiment.js";
|
|
4
4
|
|
|
5
5
|
// alternative.me provides a free crypto Fear & Greed index
|
package/src/providers/finnhub.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
2
|
+
import { HttpError, httpGet } from "../infra/http-client.js";
|
|
3
3
|
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
4
4
|
import { ProviderCredentialError } from "./provider-credential-error.js";
|
|
5
5
|
|
|
@@ -36,7 +36,10 @@ const TICKER_NAMES: Record<string, string> = {
|
|
|
36
36
|
ORCL: "oracle",
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export function finnhubDateRange(freshness: "hours" | "day" | "week" | "month"): {
|
|
39
|
+
export function finnhubDateRange(freshness: "hours" | "day" | "week" | "month"): {
|
|
40
|
+
from: string;
|
|
41
|
+
to: string;
|
|
42
|
+
} {
|
|
40
43
|
const now = new Date();
|
|
41
44
|
const to = formatDate(now);
|
|
42
45
|
|
|
@@ -86,9 +89,7 @@ export function filterByRelevance(
|
|
|
86
89
|
});
|
|
87
90
|
|
|
88
91
|
// Most recent first, capped
|
|
89
|
-
return filtered
|
|
90
|
-
.sort((a, b) => b.datetime - a.datetime)
|
|
91
|
-
.slice(0, limit);
|
|
92
|
+
return filtered.sort((a, b) => b.datetime - a.datetime).slice(0, limit);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export async function getCompanyNews(
|
package/src/providers/fred.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
2
|
+
import { HttpError, httpGet } from "../infra/http-client.js";
|
|
3
3
|
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
4
|
+
import type { FredObservation, FredSeries } from "../types/macro.js";
|
|
4
5
|
import { ProviderCredentialError } from "./provider-credential-error.js";
|
|
5
|
-
import type { FredSeries, FredObservation } from "../types/macro.js";
|
|
6
6
|
|
|
7
7
|
const BASE_URL = "https://api.stlouisfed.org/fred";
|
|
8
8
|
|
package/src/providers/index.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { getEarnings, getFinancials, getOverview } from "./alpha-vantage.js";
|
|
2
|
+
export { getCryptoHistory, getCryptoPrice } from "./coingecko.js";
|
|
3
|
+
export { getFearGreedIndex } from "./fear-greed.js";
|
|
3
4
|
export { getSeries } from "./fred.js";
|
|
4
|
-
export { getCryptoPrice, getCryptoHistory } from "./coingecko.js";
|
|
5
5
|
export { getSubredditPosts, scoreSentiment } from "./reddit.js";
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export { searchWeb, ddgSearch, braveSearch, normalizeFinancialQuery } from "./web-search.js";
|
|
6
|
+
export { type SECFiling, searchFilings } from "./sec-edgar.js";
|
|
7
|
+
export { getQuotes, screenStocks } from "./tradingview.js";
|
|
9
8
|
export type { WebSearchOpts } from "./web-search.js";
|
|
9
|
+
export { braveSearch, ddgSearch, normalizeFinancialQuery, searchWeb } from "./web-search.js";
|
|
10
|
+
export {
|
|
11
|
+
clearCrumbCache,
|
|
12
|
+
computeTimeToExpiry,
|
|
13
|
+
getHistory,
|
|
14
|
+
getOptionsChain,
|
|
15
|
+
getQuote,
|
|
16
|
+
getYahooCrumb,
|
|
17
|
+
} from "./yahoo-finance.js";
|
package/src/providers/reddit.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
1
2
|
import { httpGet } from "../infra/http-client.js";
|
|
2
|
-
import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
|
|
3
3
|
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
4
|
+
import { BEARISH_TERMS, BULLISH_TERMS } from "../sentiment/keywords.js";
|
|
4
5
|
import type { RedditSentimentResult } from "../types/sentiment.js";
|
|
5
|
-
import { BULLISH_TERMS, BEARISH_TERMS } from "../sentiment/keywords.js";
|
|
6
6
|
|
|
7
7
|
interface RedditListingResponse {
|
|
8
8
|
data: {
|
|
@@ -108,7 +108,16 @@ export async function getPostComments(
|
|
|
108
108
|
|
|
109
109
|
await rateLimiter.acquire("reddit_comments");
|
|
110
110
|
const url = `https://www.reddit.com/r/${encodeURIComponent(subreddit)}/comments/${postId}.json`;
|
|
111
|
-
const data = await httpGet<
|
|
111
|
+
const data = await httpGet<
|
|
112
|
+
Array<{
|
|
113
|
+
data: {
|
|
114
|
+
children: Array<{
|
|
115
|
+
kind: string;
|
|
116
|
+
data: { id: string; body?: string; author?: string; score?: number; permalink?: string };
|
|
117
|
+
}>;
|
|
118
|
+
};
|
|
119
|
+
}>
|
|
120
|
+
>(url, {
|
|
112
121
|
headers: REDDIT_HEADERS,
|
|
113
122
|
});
|
|
114
123
|
|
|
@@ -132,9 +141,11 @@ export async function getPostComments(
|
|
|
132
141
|
|
|
133
142
|
// ── Sentiment scoring ───────────────────────────────────
|
|
134
143
|
|
|
135
|
-
export function scoreSentiment(
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
export function scoreSentiment(posts: Array<{ title: string }>): {
|
|
145
|
+
score: number;
|
|
146
|
+
bullish: number;
|
|
147
|
+
bearish: number;
|
|
148
|
+
} {
|
|
138
149
|
let bullish = 0;
|
|
139
150
|
let bearish = 0;
|
|
140
151
|
for (const post of posts) {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { httpGet } from "../infra/http-client.js";
|
|
2
1
|
import { cache, TTL } from "../infra/cache.js";
|
|
2
|
+
import { httpGet } from "../infra/http-client.js";
|
|
3
|
+
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
3
4
|
|
|
4
5
|
const EFTS_BASE = "https://efts.sec.gov/LATEST/search-index";
|
|
6
|
+
const COMPANY_TICKERS_URL = "https://www.sec.gov/files/company_tickers.json";
|
|
7
|
+
const SUBMISSIONS_BASE = "https://data.sec.gov/submissions";
|
|
8
|
+
const SEC_DOCUMENT_FETCH_TIMEOUT_MS = 10_000;
|
|
5
9
|
|
|
6
10
|
export interface SECFiling {
|
|
7
11
|
formType: string;
|
|
@@ -10,6 +14,15 @@ export interface SECFiling {
|
|
|
10
14
|
entityName: string;
|
|
11
15
|
accessionNumber: string;
|
|
12
16
|
url: string;
|
|
17
|
+
primaryDocumentUrl?: string;
|
|
18
|
+
items?: string[];
|
|
19
|
+
evidenceSnippets?: string[];
|
|
20
|
+
evidenceWarning?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SearchFilingsOptions {
|
|
24
|
+
includeSnippets?: boolean;
|
|
25
|
+
snippetLimitPerFiling?: number;
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
interface EFTSResponse {
|
|
@@ -23,20 +36,54 @@ interface EFTSResponse {
|
|
|
23
36
|
display_names: string[];
|
|
24
37
|
period_ending: string;
|
|
25
38
|
ciks: string[];
|
|
39
|
+
items?: string[];
|
|
26
40
|
};
|
|
27
41
|
}>;
|
|
28
42
|
};
|
|
29
43
|
}
|
|
30
44
|
|
|
45
|
+
interface CompanyTickerEntry {
|
|
46
|
+
cik_str: number;
|
|
47
|
+
ticker: string;
|
|
48
|
+
title: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface SubmissionsResponse {
|
|
52
|
+
name: string;
|
|
53
|
+
tickers?: string[];
|
|
54
|
+
filings: {
|
|
55
|
+
recent: {
|
|
56
|
+
accessionNumber: string[];
|
|
57
|
+
filingDate: string[];
|
|
58
|
+
reportDate: string[];
|
|
59
|
+
form: string[];
|
|
60
|
+
primaryDocument: string[];
|
|
61
|
+
items?: string[];
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
31
66
|
export async function searchFilings(
|
|
32
67
|
ticker: string,
|
|
33
68
|
formTypes: string[] = ["10-K", "10-Q", "8-K"],
|
|
34
69
|
limit: number = 10,
|
|
70
|
+
options: SearchFilingsOptions = {},
|
|
35
71
|
): Promise<SECFiling[]> {
|
|
36
|
-
const cacheKey = `sec:${ticker}:${formTypes.join(",")}:${limit}`;
|
|
72
|
+
const cacheKey = `sec:${ticker}:${formTypes.join(",")}:${limit}:${options.includeSnippets ? "snippets" : "metadata"}`;
|
|
37
73
|
const cached = cache.get<SECFiling[]>(cacheKey);
|
|
38
74
|
if (cached) return cached;
|
|
39
75
|
|
|
76
|
+
if (options.includeSnippets) {
|
|
77
|
+
const submissions = await searchFilingsFromCompanySubmissions(ticker, formTypes, limit).catch(
|
|
78
|
+
() => [],
|
|
79
|
+
);
|
|
80
|
+
if (submissions.length > 0) {
|
|
81
|
+
await enrichWithEvidenceSnippets(submissions, options.snippetLimitPerFiling ?? 3);
|
|
82
|
+
cache.set(cacheKey, submissions, TTL.FUNDAMENTALS);
|
|
83
|
+
return submissions;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
40
87
|
const params = new URLSearchParams({
|
|
41
88
|
q: ticker,
|
|
42
89
|
forms: formTypes.join(","),
|
|
@@ -60,12 +107,15 @@ export async function searchFilings(
|
|
|
60
107
|
const src = hit._source;
|
|
61
108
|
const accession = src.adsh;
|
|
62
109
|
if (!accession || seen.has(accession)) continue;
|
|
110
|
+
if (!matchesTicker(src.display_names, ticker)) continue;
|
|
63
111
|
seen.add(accession);
|
|
64
112
|
|
|
65
113
|
const cik = src.ciks?.[0] ?? "";
|
|
66
114
|
const displayName = src.display_names?.[0] ?? "";
|
|
67
115
|
// Extract entity name from display format: "APPLE INC (AAPL) (CIK 0000320193)"
|
|
68
116
|
const entityName = displayName.split("(")[0]?.trim() ?? displayName;
|
|
117
|
+
const archiveDir = buildEdgarArchiveDir(cik, accession);
|
|
118
|
+
const primaryDocumentUrl = buildPrimaryDocumentUrl(archiveDir, hit._id);
|
|
69
119
|
|
|
70
120
|
filings.push({
|
|
71
121
|
formType: src.form ?? "",
|
|
@@ -73,20 +123,105 @@ export async function searchFilings(
|
|
|
73
123
|
periodOfReport: src.period_ending ?? "",
|
|
74
124
|
entityName,
|
|
75
125
|
accessionNumber: accession,
|
|
76
|
-
url:
|
|
126
|
+
url: `${archiveDir}/${accession}-index.htm`,
|
|
127
|
+
primaryDocumentUrl,
|
|
128
|
+
items: src.items ?? [],
|
|
77
129
|
});
|
|
78
130
|
|
|
79
131
|
if (filings.length >= limit) break;
|
|
80
132
|
}
|
|
81
133
|
|
|
134
|
+
if (options.includeSnippets) {
|
|
135
|
+
await enrichWithEvidenceSnippets(filings, options.snippetLimitPerFiling ?? 3);
|
|
136
|
+
}
|
|
137
|
+
|
|
82
138
|
cache.set(cacheKey, filings, TTL.FUNDAMENTALS);
|
|
83
139
|
return filings;
|
|
84
140
|
}
|
|
85
141
|
|
|
86
|
-
function
|
|
142
|
+
async function searchFilingsFromCompanySubmissions(
|
|
143
|
+
ticker: string,
|
|
144
|
+
formTypes: string[],
|
|
145
|
+
limit: number,
|
|
146
|
+
): Promise<SECFiling[]> {
|
|
147
|
+
const company = await resolveCompanyTicker(ticker);
|
|
148
|
+
if (!company) return [];
|
|
149
|
+
|
|
150
|
+
const cik = String(company.cik_str).padStart(10, "0");
|
|
151
|
+
const data = await httpGet<SubmissionsResponse>(`${SUBMISSIONS_BASE}/CIK${cik}.json`, {
|
|
152
|
+
headers: { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" },
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const filings: SECFiling[] = [];
|
|
156
|
+
const recent = data.filings.recent;
|
|
157
|
+
for (let i = 0; i < recent.form.length && filings.length < limit; i++) {
|
|
158
|
+
const formType = recent.form[i] ?? "";
|
|
159
|
+
if (!formTypes.includes(formType)) continue;
|
|
160
|
+
const accession = recent.accessionNumber[i];
|
|
161
|
+
if (!accession) continue;
|
|
162
|
+
const archiveDir = buildEdgarArchiveDir(cik, accession);
|
|
163
|
+
const primaryDocument = recent.primaryDocument[i];
|
|
164
|
+
filings.push({
|
|
165
|
+
formType,
|
|
166
|
+
filedDate: recent.filingDate[i] ?? "",
|
|
167
|
+
periodOfReport: recent.reportDate[i] ?? "",
|
|
168
|
+
entityName: data.name || company.title,
|
|
169
|
+
accessionNumber: accession,
|
|
170
|
+
url: `${archiveDir}/${accession}-index.htm`,
|
|
171
|
+
primaryDocumentUrl: primaryDocument ? `${archiveDir}/${primaryDocument}` : undefined,
|
|
172
|
+
items: splitFilingItems(recent.items?.[i]),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return filings;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function resolveCompanyTicker(ticker: string): Promise<CompanyTickerEntry | undefined> {
|
|
179
|
+
const cacheKey = "sec:company-tickers";
|
|
180
|
+
let companies = cache.get<CompanyTickerEntry[]>(cacheKey);
|
|
181
|
+
if (!companies) {
|
|
182
|
+
const data = await httpGet<Record<string, CompanyTickerEntry>>(COMPANY_TICKERS_URL, {
|
|
183
|
+
headers: { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" },
|
|
184
|
+
});
|
|
185
|
+
companies = Object.values(data);
|
|
186
|
+
cache.set(cacheKey, companies, TTL.FUNDAMENTALS);
|
|
187
|
+
}
|
|
188
|
+
const normalized = ticker.toUpperCase();
|
|
189
|
+
return companies.find((company) => company.ticker.toUpperCase() === normalized);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function splitFilingItems(raw: string | undefined): string[] {
|
|
193
|
+
return raw
|
|
194
|
+
? raw
|
|
195
|
+
.split(",")
|
|
196
|
+
.map((item) => item.trim())
|
|
197
|
+
.filter(Boolean)
|
|
198
|
+
: [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function matchesTicker(displayNames: string[] | undefined, ticker: string): boolean {
|
|
202
|
+
const normalized = ticker.toUpperCase();
|
|
203
|
+
return (displayNames ?? []).some((name) => {
|
|
204
|
+
const tickerGroups = name.match(/\(([^)]*)\)/g) ?? [];
|
|
205
|
+
return tickerGroups.some((group) =>
|
|
206
|
+
group
|
|
207
|
+
.slice(1, -1)
|
|
208
|
+
.split(",")
|
|
209
|
+
.map((part) => part.trim().toUpperCase())
|
|
210
|
+
.includes(normalized),
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buildEdgarArchiveDir(cik: string, accession: string): string {
|
|
87
216
|
const cikNum = cik.replace(/^0+/, "");
|
|
88
217
|
const accessionNoDash = accession.replace(/-/g, "");
|
|
89
|
-
return `https://www.sec.gov/Archives/edgar/data/${cikNum}/${accessionNoDash}
|
|
218
|
+
return `https://www.sec.gov/Archives/edgar/data/${cikNum}/${accessionNoDash}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildPrimaryDocumentUrl(archiveDir: string, hitId: string): string | undefined {
|
|
222
|
+
const fileName = hitId.split(":")[1];
|
|
223
|
+
if (!fileName || !/\.(?:htm|html|txt)$/i.test(fileName)) return undefined;
|
|
224
|
+
return `${archiveDir}/${fileName}`;
|
|
90
225
|
}
|
|
91
226
|
|
|
92
227
|
function getDateYearsAgo(years: number): string {
|
|
@@ -94,3 +229,98 @@ function getDateYearsAgo(years: number): string {
|
|
|
94
229
|
d.setFullYear(d.getFullYear() - years);
|
|
95
230
|
return d.toISOString().split("T")[0];
|
|
96
231
|
}
|
|
232
|
+
|
|
233
|
+
async function enrichWithEvidenceSnippets(
|
|
234
|
+
filings: SECFiling[],
|
|
235
|
+
limitPerFiling: number,
|
|
236
|
+
): Promise<void> {
|
|
237
|
+
await Promise.all(
|
|
238
|
+
filings.map(async (filing) => {
|
|
239
|
+
if (!filing.primaryDocumentUrl) return;
|
|
240
|
+
try {
|
|
241
|
+
const raw = await fetchText(filing.primaryDocumentUrl);
|
|
242
|
+
filing.evidenceSnippets = extractEvidenceSnippets(raw, limitPerFiling);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
filing.evidenceSnippets = [];
|
|
245
|
+
filing.evidenceWarning = `Evidence fetch failed for primary document: ${error instanceof Error ? error.message : String(error)}`;
|
|
246
|
+
}
|
|
247
|
+
}),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function fetchText(url: string): Promise<string> {
|
|
252
|
+
await rateLimiter.acquire("sec_edgar");
|
|
253
|
+
const response = await fetch(url, {
|
|
254
|
+
headers: { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" },
|
|
255
|
+
signal: AbortSignal.timeout(SEC_DOCUMENT_FETCH_TIMEOUT_MS),
|
|
256
|
+
});
|
|
257
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
258
|
+
return response.text();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const EVIDENCE_KEYWORDS = [
|
|
262
|
+
"risk factor",
|
|
263
|
+
"legal proceedings",
|
|
264
|
+
"litigation",
|
|
265
|
+
"regulatory",
|
|
266
|
+
"regulation",
|
|
267
|
+
"revenue",
|
|
268
|
+
"concentration",
|
|
269
|
+
"management's discussion",
|
|
270
|
+
"management discussion",
|
|
271
|
+
"liquidity",
|
|
272
|
+
"outlook",
|
|
273
|
+
"guidance",
|
|
274
|
+
"material weakness",
|
|
275
|
+
"going concern",
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
function extractEvidenceSnippets(raw: string, limit: number): string[] {
|
|
279
|
+
const text = stripFilingMarkup(raw);
|
|
280
|
+
const snippets: string[] = [];
|
|
281
|
+
const lower = text.toLowerCase();
|
|
282
|
+
|
|
283
|
+
for (const keyword of EVIDENCE_KEYWORDS) {
|
|
284
|
+
let searchFrom = 0;
|
|
285
|
+
while (searchFrom < lower.length) {
|
|
286
|
+
const index = lower.indexOf(keyword, searchFrom);
|
|
287
|
+
if (index === -1) break;
|
|
288
|
+
searchFrom = index + keyword.length;
|
|
289
|
+
|
|
290
|
+
const start = Math.max(0, index - 220);
|
|
291
|
+
const end = Math.min(text.length, index + keyword.length + 420);
|
|
292
|
+
const snippet = text.slice(start, end).replace(/\s+/g, " ").trim();
|
|
293
|
+
if (
|
|
294
|
+
snippet &&
|
|
295
|
+
!isLikelyTableOfContentsSnippet(snippet) &&
|
|
296
|
+
!snippets.some((existing) => existing.includes(snippet.slice(0, 80)))
|
|
297
|
+
) {
|
|
298
|
+
snippets.push(snippet);
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (snippets.length >= limit) break;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return snippets;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function stripFilingMarkup(raw: string): string {
|
|
309
|
+
return raw
|
|
310
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ")
|
|
311
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ")
|
|
312
|
+
.replace(/<[^>]+>/g, " ")
|
|
313
|
+
.replace(/ /gi, " ")
|
|
314
|
+
.replace(/&/gi, "&")
|
|
315
|
+
.replace(/ /g, " ")
|
|
316
|
+
.replace(/’/g, "'")
|
|
317
|
+
.replace(/“|”/g, '"')
|
|
318
|
+
.replace(/\s+/g, " ")
|
|
319
|
+
.trim();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function isLikelyTableOfContentsSnippet(snippet: string): boolean {
|
|
323
|
+
const lower = snippet.toLowerCase();
|
|
324
|
+
const itemHeadingCount = lower.match(/\bitem\s+\d+[a-z]?\b/g)?.length ?? 0;
|
|
325
|
+
return lower.includes("table of contents") && itemHeadingCount >= 2;
|
|
326
|
+
}
|