opencandle 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -187
- 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 +30 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -3
- package/dist/config.js +12 -5
- 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.js +3 -1
- 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.js +12 -9
- 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.js +11 -11
- 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.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.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 +412 -28
- 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 +8 -3
- 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 +1 -1
- package/dist/prompts/context-builder.js +19 -6
- package/dist/prompts/context-builder.js.map +1 -1
- package/dist/prompts/policy-cards.d.ts +1 -1
- package/dist/prompts/policy-cards.js +1 -1
- package/dist/prompts/policy-cards.js.map +1 -1
- package/dist/prompts/sections.d.ts +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 +54 -16
- package/dist/prompts/workflow-prompts.js.map +1 -1
- package/dist/providers/alpha-vantage.d.ts +1 -1
- package/dist/providers/alpha-vantage.js +26 -7
- 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 +1 -0
- package/dist/providers/sec-edgar.js +12 -4
- 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 +1 -1
- package/dist/providers/yahoo-finance.js +101 -17
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.d.ts +6 -0
- package/dist/routing/classify-intent.js +78 -7
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/routing/defaults.d.ts +1 -1
- package/dist/routing/entity-extractor.d.ts +1 -0
- package/dist/routing/entity-extractor.js +234 -29
- 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 +10 -10
- package/dist/routing/index.js +6 -6
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/planning.d.ts +1 -1
- package/dist/routing/planning.js +65 -34
- package/dist/routing/planning.js.map +1 -1
- package/dist/routing/route-manifest.d.ts +2 -2
- package/dist/routing/route-manifest.js +25 -4
- package/dist/routing/route-manifest.js.map +1 -1
- package/dist/routing/router-llm-client.js.map +1 -1
- package/dist/routing/router-prompt.js +7 -9
- package/dist/routing/router-prompt.js.map +1 -1
- package/dist/routing/router-types.d.ts +1 -0
- package/dist/routing/router.js +137 -22
- package/dist/routing/router.js.map +1 -1
- package/dist/routing/slot-resolver.d.ts +1 -1
- package/dist/routing/slot-resolver.js +2 -4
- 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 +1 -1
- package/dist/routing/turn-context.js +1 -1
- package/dist/routing/turn-context.js.map +1 -1
- package/dist/routing/types.d.ts +2 -0
- package/dist/runtime/answer-contracts.js +36 -8
- package/dist/runtime/answer-contracts.js.map +1 -1
- package/dist/runtime/artifact-contracts.js.map +1 -1
- package/dist/runtime/planning-evidence.js +47 -26
- package/dist/runtime/planning-evidence.js.map +1 -1
- 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 +13 -5
- package/dist/runtime/session-coordinator.js +160 -20
- 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 +3 -2
- 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 +11 -6
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +18 -9
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +23 -11
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +8 -3
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +8 -3
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.js +21 -6
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.d.ts +23 -19
- package/dist/tools/index.js +51 -39
- 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.map +1 -1
- package/dist/tools/macro/fred-data.d.ts +1 -1
- package/dist/tools/macro/fred-data.js +17 -6
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +3 -1
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +3 -1
- 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 +160 -8
- 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 +26 -7
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +5 -3
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/greeks.js +1 -1
- package/dist/tools/options/greeks.js.map +1 -1
- package/dist/tools/options/option-chain.js +19 -6
- 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 +33 -13
- 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.js +10 -3
- package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
- 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 +337 -87
- 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 +45 -6
- 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 +246 -101
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.d.ts +6 -4
- package/dist/tools/portfolio/watchlist.js +208 -108
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/reddit-sentiment.js +23 -10
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.js +15 -13
- 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 +12 -5
- 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 +9 -13
- package/dist/tools/sentiment/web-search.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +15 -3
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/tools/technical/backtest.d.ts +1 -1
- package/dist/tools/technical/backtest.js +27 -20
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +23 -5
- 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/portfolio.d.ts +14 -4
- package/dist/workflows/compare-assets.d.ts +0 -3
- package/dist/workflows/compare-assets.js +20 -11
- 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 +4 -11
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +0 -3
- package/dist/workflows/portfolio-builder.js +0 -8
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/gui/server/ask-user-bridge.ts +1 -1
- 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/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 +12 -7
- package/gui/server/prompt-observation.ts +4 -7
- package/gui/server/quote-snapshot-store.ts +50 -0
- package/gui/server/server.ts +200 -451
- package/gui/server/session-actions.ts +186 -1
- 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 +5 -1
- package/src/analysts/contracts.ts +10 -23
- package/src/analysts/orchestrator.ts +8 -43
- package/src/cli.ts +35 -12
- package/src/config.ts +17 -9
- package/src/index.ts +1 -1
- package/src/infra/browser.ts +3 -1
- 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 +22 -19
- 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 +14 -16
- package/src/memory/retrieval.ts +8 -7
- package/src/memory/sqlite.ts +407 -6
- package/src/memory/storage.ts +5 -15
- package/src/memory/tool-defaults.ts +60 -39
- package/src/memory/types.ts +3 -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 +529 -85
- package/src/pi/session.ts +7 -5
- package/src/pi/setup.ts +61 -43
- package/src/pi/tool-adapter.ts +5 -2
- package/src/prompts/context-builder.ts +23 -12
- package/src/prompts/policy-cards.ts +2 -2
- package/src/prompts/sections.ts +1 -1
- package/src/prompts/symbol-preflight.ts +80 -0
- package/src/prompts/workflow-prompts.ts +77 -28
- package/src/providers/alpha-vantage.ts +58 -39
- 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 +20 -6
- 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 +140 -35
- package/src/routing/classify-intent.ts +101 -10
- package/src/routing/defaults.ts +1 -1
- package/src/routing/entity-extractor.ts +287 -38
- package/src/routing/fund-symbols.ts +58 -0
- package/src/routing/horizon.ts +7 -0
- package/src/routing/index.ts +48 -48
- package/src/routing/planning.ts +144 -53
- package/src/routing/route-manifest.ts +37 -15
- package/src/routing/router-llm-client.ts +4 -4
- package/src/routing/router-prompt.ts +15 -19
- package/src/routing/router-types.ts +2 -5
- package/src/routing/router.ts +251 -53
- package/src/routing/slot-resolver.ts +34 -11
- package/src/routing/symbol-disambiguator.ts +72 -0
- package/src/routing/turn-context.ts +6 -9
- package/src/routing/types.ts +2 -0
- package/src/runtime/answer-contracts.ts +82 -43
- package/src/runtime/artifact-contracts.ts +2 -1
- package/src/runtime/planning-evidence.ts +157 -66
- package/src/runtime/prompt-step.ts +1 -16
- package/src/runtime/run-context.ts +12 -2
- package/src/runtime/session-coordinator.ts +238 -63
- 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 +3 -2
- package/src/tool-kit.ts +10 -9
- package/src/tools/fundamentals/company-overview.ts +19 -9
- package/src/tools/fundamentals/comps.ts +68 -55
- package/src/tools/fundamentals/dcf.ts +145 -95
- package/src/tools/fundamentals/earnings.ts +16 -6
- package/src/tools/fundamentals/financials.ts +16 -7
- package/src/tools/fundamentals/sec-filings.ts +37 -16
- package/src/tools/index.ts +51 -39
- 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 +1 -1
- package/src/tools/macro/fred-data.ts +58 -46
- package/src/tools/market/crypto-history.ts +8 -3
- package/src/tools/market/crypto-price.ts +6 -6
- package/src/tools/market/screen-stocks.ts +279 -0
- package/src/tools/market/search-ticker.ts +218 -17
- package/src/tools/market/stock-history.ts +37 -12
- package/src/tools/market/stock-quote.ts +10 -7
- package/src/tools/options/greeks.ts +5 -5
- package/src/tools/options/option-chain.ts +41 -17
- package/src/tools/portfolio/alerts.ts +457 -0
- package/src/tools/portfolio/correlation.ts +47 -20
- package/src/tools/portfolio/daily-report.ts +101 -0
- package/src/tools/portfolio/holdings-overlap.ts +31 -15
- package/src/tools/portfolio/notifications.ts +45 -0
- package/src/tools/portfolio/predictions.ts +406 -106
- package/src/tools/portfolio/risk-analysis.ts +46 -7
- package/src/tools/portfolio/tracker.ts +270 -109
- package/src/tools/portfolio/watchlist.ts +250 -121
- package/src/tools/sentiment/reddit-sentiment.ts +50 -24
- package/src/tools/sentiment/sentiment-summary.ts +62 -41
- package/src/tools/sentiment/sentiment-trend.ts +24 -7
- package/src/tools/sentiment/twitter-sentiment.ts +22 -15
- package/src/tools/sentiment/untrusted-text.ts +21 -0
- package/src/tools/sentiment/web-search.ts +21 -18
- package/src/tools/sentiment/web-sentiment.ts +26 -10
- package/src/tools/technical/backtest.ts +32 -22
- package/src/tools/technical/indicators.ts +39 -14
- package/src/types/index.ts +8 -3
- package/src/types/market.ts +1 -0
- package/src/types/portfolio.ts +14 -4
- package/src/types/sentiment.ts +2 -2
- package/src/workflows/compare-assets.ts +33 -21
- package/src/workflows/index.ts +3 -4
- package/src/workflows/options-screener.ts +27 -29
- package/src/workflows/portfolio-builder.ts +34 -27
- 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-Bmp6Knu7.js +0 -1
- package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
- package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
- package/src/workflows/types.ts +0 -4
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { httpGet } from "../infra/http-client.js";
|
|
2
|
-
import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
|
|
3
|
-
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
4
1
|
import { StealthBrowser } from "../infra/browser.js";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
2
|
+
import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
|
|
3
|
+
import { HttpError, httpGet } from "../infra/http-client.js";
|
|
4
|
+
import { rateLimiter } from "../infra/rate-limiter.js";
|
|
8
5
|
import { computeGreeks } from "../tools/options/greeks.js";
|
|
6
|
+
import type { OHLCV, StockQuote } from "../types/market.js";
|
|
7
|
+
import type {
|
|
8
|
+
OptionContract,
|
|
9
|
+
OptionsChain,
|
|
10
|
+
OptionsMarketSession,
|
|
11
|
+
OptionsQuoteStatus,
|
|
12
|
+
} from "../types/options.js";
|
|
13
|
+
import type { FundHoldings } from "../types/portfolio.js";
|
|
14
|
+
import { InvalidSymbolError } from "./errors.js";
|
|
9
15
|
|
|
10
16
|
const BASE_URL = "https://query1.finance.yahoo.com/v8/finance/chart";
|
|
11
17
|
const QUOTE_SUMMARY_URL = "https://query1.finance.yahoo.com/v10/finance/quoteSummary";
|
|
18
|
+
const STALE_QUOTE_MAX_RETRY_AFTER_MS = 1_000;
|
|
19
|
+
|
|
20
|
+
type YahooNumber = number | { raw?: number; fmt?: string };
|
|
12
21
|
|
|
13
22
|
interface YahooChartResponse {
|
|
14
23
|
chart: {
|
|
@@ -42,10 +51,10 @@ interface YahooQuoteSummaryResponse {
|
|
|
42
51
|
holdings?: Array<{
|
|
43
52
|
symbol?: string;
|
|
44
53
|
holdingName?: string;
|
|
45
|
-
holdingPercent?:
|
|
54
|
+
holdingPercent?: YahooNumber;
|
|
46
55
|
}>;
|
|
47
56
|
equityHoldings?: {
|
|
48
|
-
sectorWeightings?: Array<Record<string,
|
|
57
|
+
sectorWeightings?: Array<Record<string, YahooNumber>>;
|
|
49
58
|
};
|
|
50
59
|
};
|
|
51
60
|
}>;
|
|
@@ -64,6 +73,7 @@ export async function getQuote(symbol: string): Promise<StockQuote> {
|
|
|
64
73
|
const url = `${BASE_URL}/${encodeURIComponent(symbol)}?interval=1d&range=1d`;
|
|
65
74
|
const data = await httpGet<YahooChartResponse>(url, {
|
|
66
75
|
headers: { "User-Agent": "OpenCandle/1.0" },
|
|
76
|
+
maxRetryAfterMs: STALE_QUOTE_MAX_RETRY_AFTER_MS,
|
|
67
77
|
});
|
|
68
78
|
|
|
69
79
|
if (data.chart.error) {
|
|
@@ -97,8 +107,16 @@ export async function getQuote(symbol: string): Promise<StockQuote> {
|
|
|
97
107
|
week52High: meta.fiftyTwoWeekHigh ?? 0,
|
|
98
108
|
week52Low: meta.fiftyTwoWeekLow ?? 0,
|
|
99
109
|
timestamp: Date.now(),
|
|
110
|
+
currency:
|
|
111
|
+
typeof meta.currency === "string" && meta.currency.trim() !== ""
|
|
112
|
+
? meta.currency.trim().toUpperCase()
|
|
113
|
+
: null,
|
|
100
114
|
};
|
|
101
115
|
|
|
116
|
+
if (isZeroResultQuote(quote)) {
|
|
117
|
+
throw new InvalidSymbolError(symbol.toUpperCase(), "yahoo");
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
cache.set(cacheKey, quote, TTL.QUOTE);
|
|
103
121
|
return quote;
|
|
104
122
|
} catch (error) {
|
|
@@ -108,6 +126,16 @@ export async function getQuote(symbol: string): Promise<StockQuote> {
|
|
|
108
126
|
}
|
|
109
127
|
}
|
|
110
128
|
|
|
129
|
+
function isZeroResultQuote(quote: StockQuote): boolean {
|
|
130
|
+
return (
|
|
131
|
+
quote.price === 0 &&
|
|
132
|
+
quote.volume === 0 &&
|
|
133
|
+
quote.week52High === 0 &&
|
|
134
|
+
quote.week52Low === 0 &&
|
|
135
|
+
quote.marketCap === 0
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
111
139
|
export async function getHistory(
|
|
112
140
|
symbol: string,
|
|
113
141
|
range: string = "6mo",
|
|
@@ -162,14 +190,12 @@ export async function getFundHoldings(symbol: string): Promise<FundHoldings> {
|
|
|
162
190
|
try {
|
|
163
191
|
await rateLimiter.acquire("yahoo");
|
|
164
192
|
|
|
165
|
-
const
|
|
166
|
-
const url = `${QUOTE_SUMMARY_URL}/${encodeURIComponent(normalizedSymbol)}?modules=${modules}`;
|
|
167
|
-
const data = await httpGet<YahooQuoteSummaryResponse>(url, {
|
|
168
|
-
headers: { "User-Agent": "OpenCandle/1.0" },
|
|
169
|
-
});
|
|
193
|
+
const data = await getFundHoldingsSummary(normalizedSymbol);
|
|
170
194
|
const result = data.quoteSummary.result?.[0];
|
|
171
195
|
if (data.quoteSummary.error) {
|
|
172
|
-
throw new Error(
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Yahoo Finance: ${data.quoteSummary.error.description ?? data.quoteSummary.error.code ?? "quoteSummary error"}`,
|
|
198
|
+
);
|
|
173
199
|
}
|
|
174
200
|
if (!result?.topHoldings?.holdings?.length) {
|
|
175
201
|
throw new Error(`Yahoo Finance: no fund holdings returned for ${normalizedSymbol}`);
|
|
@@ -183,11 +209,13 @@ export async function getFundHoldings(symbol: string): Promise<FundHoldings> {
|
|
|
183
209
|
const holdingSymbol = holding.symbol?.trim().toUpperCase();
|
|
184
210
|
const weight = normalizeHoldingWeight(holding.holdingPercent);
|
|
185
211
|
if (!holdingSymbol || weight === undefined) return [];
|
|
186
|
-
return [
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
212
|
+
return [
|
|
213
|
+
{
|
|
214
|
+
symbol: holdingSymbol,
|
|
215
|
+
name: holding.holdingName?.trim() || holdingSymbol,
|
|
216
|
+
weight,
|
|
217
|
+
},
|
|
218
|
+
];
|
|
191
219
|
}),
|
|
192
220
|
sectorWeights: normalizeSectorWeights(result.topHoldings.equityHoldings?.sectorWeightings),
|
|
193
221
|
};
|
|
@@ -204,13 +232,56 @@ export async function getFundHoldings(symbol: string): Promise<FundHoldings> {
|
|
|
204
232
|
}
|
|
205
233
|
}
|
|
206
234
|
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
|
|
235
|
+
async function getFundHoldingsSummary(symbol: string): Promise<YahooQuoteSummaryResponse> {
|
|
236
|
+
try {
|
|
237
|
+
return await fetchFundHoldingsSummary(symbol);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (!isYahooAuthError(error)) throw error;
|
|
240
|
+
return fetchFundHoldingsSummaryWithCrumb(symbol);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function fetchFundHoldingsSummary(symbol: string): Promise<YahooQuoteSummaryResponse> {
|
|
245
|
+
const modules = encodeURIComponent("price,topHoldings");
|
|
246
|
+
const url = `${QUOTE_SUMMARY_URL}/${encodeURIComponent(symbol)}?modules=${modules}`;
|
|
247
|
+
return httpGet<YahooQuoteSummaryResponse>(url, {
|
|
248
|
+
headers: { "User-Agent": "OpenCandle/1.0" },
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function fetchFundHoldingsSummaryWithCrumb(
|
|
253
|
+
symbol: string,
|
|
254
|
+
): Promise<YahooQuoteSummaryResponse> {
|
|
255
|
+
const modules = encodeURIComponent("price,topHoldings");
|
|
256
|
+
const { crumb, cookie } = await getYahooCrumb();
|
|
257
|
+
const url = `${QUOTE_SUMMARY_URL}/${encodeURIComponent(symbol)}?modules=${modules}&crumb=${encodeURIComponent(crumb)}`;
|
|
258
|
+
try {
|
|
259
|
+
return await httpGet<YahooQuoteSummaryResponse>(url, {
|
|
260
|
+
headers: { "User-Agent": BROWSER_UA, Cookie: cookie },
|
|
261
|
+
});
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (!isYahooAuthError(error)) throw error;
|
|
264
|
+
clearCrumbCache();
|
|
265
|
+
const fresh = await getYahooCrumb();
|
|
266
|
+
const retryUrl = `${QUOTE_SUMMARY_URL}/${encodeURIComponent(symbol)}?modules=${modules}&crumb=${encodeURIComponent(fresh.crumb)}`;
|
|
267
|
+
return httpGet<YahooQuoteSummaryResponse>(retryUrl, {
|
|
268
|
+
headers: { "User-Agent": BROWSER_UA, Cookie: fresh.cookie },
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isYahooAuthError(error: unknown): boolean {
|
|
274
|
+
return error instanceof HttpError && (error.status === 401 || error.status === 429);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function normalizeHoldingWeight(value: YahooNumber | undefined): number | undefined {
|
|
278
|
+
const numeric = typeof value === "number" ? value : value?.raw;
|
|
279
|
+
if (numeric === undefined || !Number.isFinite(numeric) || numeric <= 0) return undefined;
|
|
280
|
+
return numeric > 1 ? roundWeight(numeric / 100) : roundWeight(numeric);
|
|
210
281
|
}
|
|
211
282
|
|
|
212
283
|
function normalizeSectorWeights(
|
|
213
|
-
sectors: Array<Record<string,
|
|
284
|
+
sectors: Array<Record<string, YahooNumber>> | undefined,
|
|
214
285
|
): Record<string, number> | undefined {
|
|
215
286
|
if (!sectors?.length) return undefined;
|
|
216
287
|
const weights: Record<string, number> = {};
|
|
@@ -231,6 +302,7 @@ function roundWeight(value: number): number {
|
|
|
231
302
|
|
|
232
303
|
const BROWSER_UA =
|
|
233
304
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
305
|
+
const YAHOO_RAW_FETCH_TIMEOUT_MS = 10_000;
|
|
234
306
|
|
|
235
307
|
let cachedCrumb: { crumb: string; cookie: string; expiresAt: number } | null = null;
|
|
236
308
|
|
|
@@ -246,15 +318,30 @@ export async function getYahooCrumb(): Promise<{ crumb: string; cookie: string }
|
|
|
246
318
|
// Step 1: Hit fc.yahoo.com to get a session cookie
|
|
247
319
|
const cookieRes = await fetch("https://fc.yahoo.com/t", {
|
|
248
320
|
headers: { "User-Agent": BROWSER_UA },
|
|
321
|
+
signal: yahooRawFetchSignal(),
|
|
249
322
|
});
|
|
250
323
|
const setCookie = cookieRes.headers.get("set-cookie") ?? "";
|
|
251
324
|
const cookie = setCookie.split(";")[0]; // Extract just the cookie value
|
|
325
|
+
if (!cookie) {
|
|
326
|
+
if (!cookieRes.ok) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
`Yahoo crumb cookie request failed: HTTP ${cookieRes.status} ${cookieRes.statusText}`.trim(),
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
throw new Error("Yahoo crumb cookie request did not return a session cookie");
|
|
332
|
+
}
|
|
252
333
|
|
|
253
334
|
// Step 2: Use the cookie to get a crumb
|
|
254
335
|
const crumbRes = await fetch("https://query2.finance.yahoo.com/v1/test/getcrumb", {
|
|
255
336
|
headers: { "User-Agent": BROWSER_UA, Cookie: cookie },
|
|
337
|
+
signal: yahooRawFetchSignal(),
|
|
256
338
|
});
|
|
257
|
-
|
|
339
|
+
if (!crumbRes.ok) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Yahoo crumb request failed: HTTP ${crumbRes.status} ${crumbRes.statusText}`.trim(),
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
const crumb = (await crumbRes.text()).trim();
|
|
258
345
|
|
|
259
346
|
if (!crumb || crumb.includes("Unauthorized")) {
|
|
260
347
|
throw new Error("Failed to acquire Yahoo Finance crumb");
|
|
@@ -281,25 +368,23 @@ interface YahooOptionsResponse {
|
|
|
281
368
|
};
|
|
282
369
|
}
|
|
283
370
|
|
|
284
|
-
export async function getOptionsChain(
|
|
285
|
-
symbol: string,
|
|
286
|
-
expiration?: number,
|
|
287
|
-
): Promise<OptionsChain> {
|
|
371
|
+
export async function getOptionsChain(symbol: string, expiration?: number): Promise<OptionsChain> {
|
|
288
372
|
const cacheKey = `yahoo:options:${symbol}:${expiration ?? "nearest"}`;
|
|
289
373
|
const cached = cache.get<OptionsChain>(cacheKey);
|
|
290
374
|
if (cached) return cached;
|
|
291
375
|
|
|
292
376
|
await rateLimiter.acquire("yahoo");
|
|
293
377
|
|
|
294
|
-
const { crumb, cookie } = await getYahooCrumb();
|
|
295
378
|
const dateParam = expiration ? `&date=${expiration}` : "";
|
|
296
|
-
const url = `https://query1.finance.yahoo.com/v7/finance/options/${encodeURIComponent(symbol)}?crumb=${encodeURIComponent(crumb)}${dateParam}`;
|
|
297
379
|
|
|
298
380
|
let res: Response | null = null;
|
|
299
381
|
let fetchError: unknown;
|
|
300
382
|
try {
|
|
383
|
+
const { crumb, cookie } = await getYahooCrumb();
|
|
384
|
+
const url = `https://query1.finance.yahoo.com/v7/finance/options/${encodeURIComponent(symbol)}?crumb=${encodeURIComponent(crumb)}${dateParam}`;
|
|
301
385
|
res = await fetch(url, {
|
|
302
386
|
headers: { "User-Agent": BROWSER_UA, Cookie: cookie },
|
|
387
|
+
signal: yahooRawFetchSignal(),
|
|
303
388
|
});
|
|
304
389
|
} catch (error) {
|
|
305
390
|
fetchError = error;
|
|
@@ -313,6 +398,7 @@ export async function getOptionsChain(
|
|
|
313
398
|
const retryUrl = `https://query1.finance.yahoo.com/v7/finance/options/${encodeURIComponent(symbol)}?crumb=${encodeURIComponent(fresh.crumb)}${dateParam}`;
|
|
314
399
|
res = await fetch(retryUrl, {
|
|
315
400
|
headers: { "User-Agent": BROWSER_UA, Cookie: fresh.cookie },
|
|
401
|
+
signal: yahooRawFetchSignal(),
|
|
316
402
|
});
|
|
317
403
|
} catch (error) {
|
|
318
404
|
fetchError = error;
|
|
@@ -344,10 +430,13 @@ export async function getOptionsChain(
|
|
|
344
430
|
throw new Error(message);
|
|
345
431
|
}
|
|
346
432
|
if (browserError instanceof Error) {
|
|
347
|
-
const message =
|
|
433
|
+
const message =
|
|
434
|
+
fetchError instanceof Error ? fetchError.message : "Yahoo Finance options: fetch failed";
|
|
348
435
|
throw new Error(`${message}; browser fallback failed: ${browserError.message}`);
|
|
349
436
|
}
|
|
350
|
-
throw fetchError instanceof Error
|
|
437
|
+
throw fetchError instanceof Error
|
|
438
|
+
? fetchError
|
|
439
|
+
: new Error("Yahoo Finance options: fetch failed");
|
|
351
440
|
}
|
|
352
441
|
|
|
353
442
|
const data: YahooOptionsResponse = await res.json();
|
|
@@ -356,6 +445,10 @@ export async function getOptionsChain(
|
|
|
356
445
|
return chain;
|
|
357
446
|
}
|
|
358
447
|
|
|
448
|
+
function yahooRawFetchSignal(): AbortSignal {
|
|
449
|
+
return AbortSignal.timeout(YAHOO_RAW_FETCH_TIMEOUT_MS);
|
|
450
|
+
}
|
|
451
|
+
|
|
359
452
|
/**
|
|
360
453
|
* Compute time to expiry in years from a Yahoo expiration timestamp (midnight UTC).
|
|
361
454
|
* US equity options expire at 4:00 PM ET. During EDT that is 20:00 UTC.
|
|
@@ -364,7 +457,7 @@ export async function getOptionsChain(
|
|
|
364
457
|
*/
|
|
365
458
|
export function computeTimeToExpiry(expirationTs: number, nowMs: number = Date.now()): number {
|
|
366
459
|
const MARKET_CLOSE_OFFSET_S = 21 * 3600; // 21:00 UTC ≈ 4 PM ET
|
|
367
|
-
const MIN_TIME_YEARS = 1 / (365 * 24);
|
|
460
|
+
const MIN_TIME_YEARS = 1 / (365 * 24); // ~1 hour floor
|
|
368
461
|
const SECONDS_PER_YEAR = 365 * 24 * 3600;
|
|
369
462
|
|
|
370
463
|
const expiryCloseTs = expirationTs + MARKET_CLOSE_OFFSET_S;
|
|
@@ -449,6 +542,9 @@ function parseOptionsResponse(data: YahooOptionsResponse): OptionsChain {
|
|
|
449
542
|
const quote = result.quote;
|
|
450
543
|
const underlyingPrice = quote.regularMarketPrice ?? 0;
|
|
451
544
|
const opts = result.options[0];
|
|
545
|
+
if (!opts && underlyingPrice === 0) {
|
|
546
|
+
throw new InvalidSymbolError(result.underlyingSymbol, "yahoo");
|
|
547
|
+
}
|
|
452
548
|
const riskFreeRate = 0.05;
|
|
453
549
|
|
|
454
550
|
const expirationTs = opts.expirationDate;
|
|
@@ -458,7 +554,14 @@ function parseOptionsResponse(data: YahooOptionsResponse): OptionsChain {
|
|
|
458
554
|
const mapContract = (c: any, type: "call" | "put"): OptionContract => {
|
|
459
555
|
const strike = c.strike ?? c.strike?.raw ?? 0;
|
|
460
556
|
const iv = c.impliedVolatility ?? c.impliedVolatility?.raw ?? 0;
|
|
461
|
-
const greeks = computeGreeks({
|
|
557
|
+
const greeks = computeGreeks({
|
|
558
|
+
type,
|
|
559
|
+
spot: underlyingPrice,
|
|
560
|
+
strike,
|
|
561
|
+
timeYears,
|
|
562
|
+
iv,
|
|
563
|
+
riskFreeRate,
|
|
564
|
+
});
|
|
462
565
|
return {
|
|
463
566
|
contractSymbol: c.contractSymbol ?? "",
|
|
464
567
|
type,
|
|
@@ -485,7 +588,9 @@ function parseOptionsResponse(data: YahooOptionsResponse): OptionsChain {
|
|
|
485
588
|
symbol: result.underlyingSymbol,
|
|
486
589
|
underlyingPrice,
|
|
487
590
|
expirationDate,
|
|
488
|
-
expirationDates: result.expirationDates.map(
|
|
591
|
+
expirationDates: result.expirationDates.map(
|
|
592
|
+
(ts) => new Date(ts * 1000).toISOString().split("T")[0],
|
|
593
|
+
),
|
|
489
594
|
calls,
|
|
490
595
|
puts,
|
|
491
596
|
totalCallVolume,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ClassificationResult, WorkflowType, ExtractedEntities } from "./types.js";
|
|
2
1
|
import { extractEntities } from "./entity-extractor.js";
|
|
2
|
+
import type { ClassificationResult, ExtractedEntities, WorkflowType } from "./types.js";
|
|
3
3
|
|
|
4
4
|
interface Rule {
|
|
5
5
|
workflow: WorkflowType;
|
|
@@ -43,7 +43,9 @@ const RULES: Rule[] = [
|
|
|
43
43
|
return (
|
|
44
44
|
entities.symbols.length >= 1 &&
|
|
45
45
|
(/\bi\s+own\b/.test(lower) || /\bmy\s+holdings\b/.test(lower)) &&
|
|
46
|
-
(/\bportfolio\s+risk\b/.test(lower) ||
|
|
46
|
+
(/\bportfolio\s+risk\b/.test(lower) ||
|
|
47
|
+
/\bbiggest\s+risk\b/.test(lower) ||
|
|
48
|
+
/\bconcentration\b/.test(lower))
|
|
47
49
|
);
|
|
48
50
|
},
|
|
49
51
|
},
|
|
@@ -91,9 +93,7 @@ const RULES: Rule[] = [
|
|
|
91
93
|
if (hasCompareKeywords && entities.symbols.length >= 2) return false;
|
|
92
94
|
|
|
93
95
|
return (
|
|
94
|
-
/\bbacktest\b/.test(lower) ||
|
|
95
|
-
/\bsentiment\b/.test(lower) ||
|
|
96
|
-
/\brate\s+cuts?\b/.test(lower)
|
|
96
|
+
/\bbacktest\b/.test(lower) || /\bsentiment\b/.test(lower) || /\brate\s+cuts?\b/.test(lower)
|
|
97
97
|
);
|
|
98
98
|
},
|
|
99
99
|
},
|
|
@@ -145,6 +145,14 @@ const RULES: Rule[] = [
|
|
|
145
145
|
return hasOptionKeywords && entities.symbols.length >= 1;
|
|
146
146
|
},
|
|
147
147
|
},
|
|
148
|
+
// Stateful portfolio/watchlist/alert/prediction mutations must not be
|
|
149
|
+
// mistaken for compare or portfolio-construction workflows just because a
|
|
150
|
+
// cost basis, target, or currency token is present.
|
|
151
|
+
{
|
|
152
|
+
workflow: "watchlist_or_tracking",
|
|
153
|
+
confidence: 0.95,
|
|
154
|
+
test: (input) => isStatefulTrackingRequest(input),
|
|
155
|
+
},
|
|
148
156
|
// Compare: keyword + 2+ symbols (uppercase)
|
|
149
157
|
{
|
|
150
158
|
workflow: "compare_assets",
|
|
@@ -165,8 +173,10 @@ const RULES: Rule[] = [
|
|
|
165
173
|
confidence: 0.85,
|
|
166
174
|
test: (input, entities) => {
|
|
167
175
|
const lower = input.toLowerCase();
|
|
168
|
-
return
|
|
169
|
-
|
|
176
|
+
return (
|
|
177
|
+
entities.symbols.length >= 2 &&
|
|
178
|
+
/\bcompare\s+[a-z]{1,5}\b(?:\s*,?\s*(?:and\s+)?[a-z]{1,5}\b)+/.test(lower)
|
|
179
|
+
);
|
|
170
180
|
},
|
|
171
181
|
},
|
|
172
182
|
// Compare: 2+ uppercase symbols without explicit keyword
|
|
@@ -275,11 +285,92 @@ export function classifyIntent(input: string): ClassificationResult {
|
|
|
275
285
|
function isPortfolioEvaluationRequest(input: string): boolean {
|
|
276
286
|
const lower = input.toLowerCase();
|
|
277
287
|
const hasEvaluationIntent =
|
|
278
|
-
/\b(?:evaluat(?:e|ion)|review|assess|analy[sz]e|prospects?|risks?|opportunities?|mitigat(?:e|ion)|adjustment)\b/.test(
|
|
288
|
+
/\b(?:evaluat(?:e|ion)|review|assess|analy[sz]e|prospects?|risks?|opportunities?|mitigat(?:e|ion)|adjustment)\b/.test(
|
|
289
|
+
lower,
|
|
290
|
+
);
|
|
279
291
|
const hasPortfolioObject =
|
|
280
|
-
/\b(?:portfolio|allocation|asset\s+allocation|60\/40|equity|fixed\s+income|bonds?)\b/.test(
|
|
292
|
+
/\b(?:portfolio|allocation|asset\s+allocation|60\/40|equity|fixed\s+income|bonds?)\b/.test(
|
|
293
|
+
lower,
|
|
294
|
+
);
|
|
281
295
|
const hasConstructionIntent =
|
|
282
296
|
/\b(?:build|create|construct|put\s+together|invest|allocate)\b/.test(lower) &&
|
|
283
|
-
|
|
297
|
+
/\$\s*\d|\b\d+(?:\.\d+)?\s*k\b|\bbudget\b|\bcapital\b/.test(lower);
|
|
284
298
|
return hasEvaluationIntent && hasPortfolioObject && !hasConstructionIntent;
|
|
285
299
|
}
|
|
300
|
+
|
|
301
|
+
function isStatefulTrackingRequest(input: string): boolean {
|
|
302
|
+
const lower = input.toLowerCase();
|
|
303
|
+
const hasPortfolioConstructionIntent =
|
|
304
|
+
/\b(?:build|create|construct|put\s+together)\b/.test(lower) &&
|
|
305
|
+
/\bportfolio\b/.test(lower) &&
|
|
306
|
+
/\$\s*\d|\b\d+(?:\.\d+)?\s*k\b|\bbudget\b|\bcapital\b/.test(lower);
|
|
307
|
+
const hasStateVerb =
|
|
308
|
+
/\b(?:add|remove|update|record|track|create|configure|check|show|list|view|cancel)\b/.test(
|
|
309
|
+
lower,
|
|
310
|
+
);
|
|
311
|
+
const hasStateObject =
|
|
312
|
+
/\b(?:watchlist|portfolio|holding|holdings|position|positions|prediction|predictions|alert|alerts|daily\s+report|watchlist\s+report|report\s+history)\b/.test(
|
|
313
|
+
lower,
|
|
314
|
+
);
|
|
315
|
+
const hasPortfolioLotShape =
|
|
316
|
+
/\b(?:add|record|track)\b/.test(lower) &&
|
|
317
|
+
/\b\d+(?:\.\d+)?\s+shares?\b/.test(lower) &&
|
|
318
|
+
/\b(?:portfolio|holding|holdings|position|positions)\b/.test(lower);
|
|
319
|
+
if (hasPortfolioConstructionIntent) return false;
|
|
320
|
+
return (hasStateVerb && hasStateObject) || hasPortfolioLotShape;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const FINANCE_SIGNAL_TERMS = [
|
|
324
|
+
"stock",
|
|
325
|
+
"stocks",
|
|
326
|
+
"shares",
|
|
327
|
+
"ticker",
|
|
328
|
+
"tickers",
|
|
329
|
+
"etf",
|
|
330
|
+
"etfs",
|
|
331
|
+
"ipo",
|
|
332
|
+
"earnings",
|
|
333
|
+
"dividend",
|
|
334
|
+
"dividends",
|
|
335
|
+
"valuation",
|
|
336
|
+
"stock market",
|
|
337
|
+
"invest",
|
|
338
|
+
"investing",
|
|
339
|
+
"investment",
|
|
340
|
+
"portfolio",
|
|
341
|
+
"watchlist",
|
|
342
|
+
"bond",
|
|
343
|
+
"bonds",
|
|
344
|
+
"bond yield",
|
|
345
|
+
"treasury",
|
|
346
|
+
"the fed",
|
|
347
|
+
"inflation",
|
|
348
|
+
"interest rates",
|
|
349
|
+
"crypto",
|
|
350
|
+
"bitcoin",
|
|
351
|
+
"ethereum",
|
|
352
|
+
"options chain",
|
|
353
|
+
"covered call",
|
|
354
|
+
"puts",
|
|
355
|
+
"bullish",
|
|
356
|
+
"bearish",
|
|
357
|
+
"hedge",
|
|
358
|
+
"price target",
|
|
359
|
+
"cost basis",
|
|
360
|
+
"nasdaq",
|
|
361
|
+
"s&p",
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
const FINANCE_SIGNAL_PATTERN = new RegExp(
|
|
365
|
+
`\\b(?:${FINANCE_SIGNAL_TERMS.map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\b`,
|
|
366
|
+
"i",
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Deterministic finance-vocabulary check for rules-mode fallback turns whose
|
|
371
|
+
* intent did not match a workflow and whose entities carry no symbols (for
|
|
372
|
+
* example theme prompts about private companies or sectors).
|
|
373
|
+
*/
|
|
374
|
+
export function hasFinanceSignals(input: string): boolean {
|
|
375
|
+
return FINANCE_SIGNAL_PATTERN.test(input);
|
|
376
|
+
}
|
package/src/routing/defaults.ts
CHANGED